@@ -4,26 +4,191 @@ import org.scalatest.matchers.must.Matchers.convertToAnyMustWrapper
4
4
import org .scalatest .wordspec .AnyWordSpec
5
5
import io .iohk .atala .prism .node .crypto .CryptoUtils .Sha256Hash
6
6
import io .iohk .atala .prism .node .utils .Base64Utils
7
+ import io .iohk .atala .prism .node .crypto .{CryptoUtils , CryptoTestUtils }
8
+ import io .iohk .atala .prism .protos .node_models ._
9
+ import org .scalatest .OptionValues ._
7
10
8
11
class PrismDidSpec extends AnyWordSpec {
9
12
10
- " PrismDid library" should {
13
+ val canonicalSuffixHex = " 9b5118411248d9663b6ab15128fba8106511230ff654e7514cdcc4ce919bde9b"
14
+ val canonicalSuffix = Sha256Hash .fromHex(canonicalSuffixHex)
15
+ val encodedStateUsedBase64 =
16
+ " Cj8KPRI7CgdtYXN0ZXIwEAFKLgoJc2VjcDI1NmsxEiEDHpf-yhIns-LP3tLvA8icC5FJ1ZlBwbllPtIdNZ3q0jU"
17
+ val encodedStateUsed = Base64Utils .decodeURL(encodedStateUsedBase64)
18
+
19
+ val short = PrismDid .buildCanonical(canonicalSuffix)
20
+ val long = PrismDid .buildLongForm(canonicalSuffix, encodedStateUsed)
21
+
22
+ private def createLongFormDidFromAtalaOperationUnsafely (atalaOperation : AtalaOperation ): LongFormPrismDid = {
23
+ val encodedState = atalaOperation.toByteArray
24
+ val encodedStateBase64 = Base64Utils .encodeURL(encodedState)
25
+ val stateHash = Sha256Hash .compute(encodedState)
11
26
12
- val canonicalSuffixHex = " 9b5118411248d9663b6ab15128fba8106511230ff654e7514cdcc4ce919bde9b"
13
- val canonicalSuffix = Sha256Hash .fromHex(canonicalSuffixHex)
14
- val encodedStateUsedBase64 =
15
- " Cj8KPRI7CgdtYXN0ZXIwEAFKLgoJc2VjcDI1NmsxEiEDHpf-yhIns-LP3tLvA8icC5FJ1ZlBwbllPtIdNZ3q0jU"
16
- val encodedStateUsed = Base64Utils .decodeURL(encodedStateUsedBase64)
27
+ val methodSpecificId = DidMethodSpecificId .fromSections(Array (stateHash.hexEncoded, encodedStateBase64))
28
+ val did = Did (PrismDid .PRISM_METHOD , methodSpecificId)
29
+ LongFormPrismDid (did, stateHash, atalaOperation)
30
+ }
17
31
18
- val short = PrismDid .buildCanonical(canonicalSuffix)
19
- val long = PrismDid .buildLongForm(canonicalSuffix, encodedStateUsed)
32
+ " PrismDid library" should {
20
33
21
34
// HASHING
22
35
" asCanonical should work for long and short form dids" in {
23
36
canonicalSuffixHex mustBe short.asCanonical().suffix
24
37
canonicalSuffixHex mustBe long.asCanonical().suffix
25
38
}
26
39
40
+ " values of canonical did should be a valid prism did with correct suffix" in {
41
+ short.asCanonical().value mustBe s " did:prism: $canonicalSuffixHex"
42
+ long.asCanonical().value mustBe s " did:prism: $canonicalSuffixHex"
43
+ }
44
+
45
+ " obtain did suffix correctly" in {
46
+ short.suffix mustBe canonicalSuffixHex
47
+ long.suffix mustBe s " $canonicalSuffixHex: $encodedStateUsedBase64"
48
+ }
49
+
50
+ " properly validate a long for DID" in {
51
+ // bytes extracted from a randomly generated key
52
+ val xBytes = Array [Byte ](
53
+ 30 , - 105 , - 2 , - 54 , 18 , 39 , - 77 , - 30 , - 49 , - 34 , - 46 , - 17 , 3 , - 56 , - 100 , 11 , - 111 , 73 , - 43 , - 103 , 65 , - 63 , - 71 ,
54
+ 101 , 62 , - 46 , 29 , 53 , - 99 , - 22 , - 46 , 53
55
+ )
56
+ val yBytes = Array [Byte ](
57
+ - 103 , 81 , - 25 , 85 , 91 , - 109 , - 113 , 111 , 106 , 7 , - 95 , 3 , 4 , 36 , 22 , - 11 , - 65 , 126 , - 4 , - 116 , - 42 , - 90 , - 72 , - 118 ,
58
+ 87 , - 120 , 17 , - 119 , 23 , - 77 , - 118 , 69
59
+ )
60
+ val masterKey = CryptoUtils .SecpPublicKey .unsafeFromByteCoordinates(xBytes, yBytes)
61
+
62
+ val expectedInitialState =
63
+ AtalaOperation (
64
+ operation = AtalaOperation .Operation .CreateDid (
65
+ CreateDIDOperation (
66
+ didData = Some (
67
+ CreateDIDOperation .DIDCreationData (
68
+ publicKeys = List (
69
+ PublicKey (
70
+ id = PrismDid .DEFAULT_MASTER_KEY_ID ,
71
+ usage = KeyUsage .MASTER_KEY ,
72
+ keyData = PublicKey .KeyData .CompressedEcKeyData (masterKey.toProto)
73
+ )
74
+ )
75
+ )
76
+ )
77
+ )
78
+ )
79
+ )
80
+
81
+ expectedInitialState mustBe long.initialState
82
+ }
83
+
84
+ " correctly generate suffix" in {
85
+ val hash = Sha256Hash .compute(Array [Byte ](0 ))
86
+ val input = PrismDid .buildCanonical(Sha256Hash .compute(Array [Byte ](0 )))
87
+
88
+ hash.hexEncoded mustBe input.suffix
89
+ }
90
+
91
+ " correctly build a valid did from empty canonical string" in {
92
+ val validDid = PrismDid .buildCanonical(Sha256Hash .compute(Array [Byte ](0 )))
93
+ val unsafeDid = PrismDid .fromString(validDid.value)
94
+
95
+ validDid mustBe unsafeDid
96
+ }
97
+
98
+ " correctly build from valid long form DID" in {
99
+ val longAsString = long.value
100
+ val unsafeDid = PrismDid .fromString(longAsString)
101
+
102
+ long mustBe unsafeDid
103
+ }
104
+
105
+ " correctly build from valid short form did" in {
106
+ val canonicalAsString = short.value
107
+ val unsafeDid = PrismDid .fromString(canonicalAsString)
108
+
109
+ short mustBe unsafeDid
110
+ }
111
+
112
+ " parse prism did into a long form did and extract keys from DID data" in {
113
+ val didString =
114
+ " did:prism:1e8777cf1e014563b123d6eed984ff35d235f64497e6736b7b9647649b6afe8f:CmIKYBJeCgdtYXN0ZXIwEAFCUQoJc2VjcDI1NmsxEiEAwCb_BYvKwhcOIAWiguHbdBfRgJWVO9EvBgWGHPKn9wYaIQDYr0B_6ZsLlfhdE9Nv8-_sZP-l-u8UeUCSbucNiDrrrg"
115
+ val prismDid = PrismDid .fromString(didString)
116
+ assert(prismDid.isInstanceOf [LongFormPrismDid ])
117
+
118
+ val prismState = prismDid.asInstanceOf [LongFormPrismDid ].initialState
119
+ val didData = prismState.operation.createDid.get.didData.value
120
+ val publicKey = didData.publicKeys.head
121
+
122
+ val xProtoBytes = publicKey.getEcKeyData.x.toByteArray
123
+ val yProtoBytes = publicKey.getEcKeyData.y.toByteArray
124
+ xProtoBytes.length mustBe 33
125
+ yProtoBytes.length mustBe 33
126
+ }
127
+
128
+ " catch failure when parsing invalid DID" in {
129
+ val caught = intercept[IllegalArgumentException ] {
130
+ PrismDid .fromString(" invalid-did" )
131
+ }
132
+
133
+ assert(caught.getMessage == " Invalid DID format: invalid-did" )
134
+ }
135
+
136
+ " create canonical from string correctly" in {
137
+ val shortAsString = short.value
138
+ val unsafeDid = PrismDid .canonicalFromString(shortAsString)
139
+
140
+ short mustBe unsafeDid
141
+ }
142
+
143
+ " fail when long form initial state is not CreateDid" in {
144
+ val mockAtalaOperation =
145
+ AtalaOperation (operation = AtalaOperation .Operation .UpdateDid (UpdateDIDOperation ()))
146
+ val updateDid = createLongFormDidFromAtalaOperationUnsafely(mockAtalaOperation)
147
+
148
+ val caught = intercept[CreateDidExpectedAsInitialState ] {
149
+ PrismDid .fromString(updateDid.did.toString())
150
+ }
151
+
152
+ caught.getMessage mustBe " Provided initial state of long form Prism DID is UpdateDIDOperation(<ByteString@18e36d14 size=0 contents=\"\" >,,Vector(),UnknownFieldSet(Map())), CreateDid Atala operation expected"
153
+
154
+ }
155
+
156
+ " fail for long form where master key is not present" in {
157
+ val issuingPublicKey = CryptoTestUtils .generateKeyPair().publicKey
158
+ val revocationPublicKey = CryptoTestUtils .generateKeyPair().publicKey
159
+
160
+ val issuingKeyPublicKey =
161
+ PublicKey (
162
+ id = PrismDid .DEFAULT_ISSUING_KEY_ID ,
163
+ usage = KeyUsage .ISSUING_KEY ,
164
+ keyData = PublicKey .KeyData .CompressedEcKeyData (issuingPublicKey.toProto)
165
+ )
166
+ val revocationKeyPublicKey =
167
+ PublicKey (
168
+ id = PrismDid .DEFAULT_REVOCATION_KEY_ID ,
169
+ usage = KeyUsage .REVOCATION_KEY ,
170
+ keyData = PublicKey .KeyData .CompressedEcKeyData (revocationPublicKey.toProto)
171
+ )
172
+
173
+ val createDidOp = CreateDIDOperation (
174
+ didData = Some (
175
+ CreateDIDOperation .DIDCreationData (
176
+ publicKeys = List (issuingKeyPublicKey, revocationKeyPublicKey)
177
+ )
178
+ )
179
+ )
180
+
181
+ val noMasterKeyCreateDid =
182
+ AtalaOperation (operation = AtalaOperation .Operation .CreateDid (createDidOp))
183
+ val longForm = createLongFormDidFromAtalaOperationUnsafely(noMasterKeyCreateDid)
184
+
185
+ val caught = intercept[IllegalArgumentException ] {
186
+ PrismDid .fromString(longForm.did.toString())
187
+ }
188
+
189
+ caught.getMessage mustBe " requirement failed: At least one public key with master role required"
190
+
191
+ }
27
192
28
193
}
29
194
}
0 commit comments