github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/verifiable/example_credential_test.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package verifiable_test 8 9 import ( 10 "crypto/ed25519" 11 "encoding/base64" 12 "encoding/json" 13 "errors" 14 "fmt" 15 "time" 16 17 "github.com/hyperledger/aries-framework-go/pkg/common/log" 18 "github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk/jwksupport" 19 "github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld" 20 "github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite" 21 "github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/bbsblssignature2020" 22 "github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/ed25519signature2018" 23 "github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/jsonwebsignature2020" 24 sigverifier "github.com/hyperledger/aries-framework-go/pkg/doc/signature/verifier" 25 "github.com/hyperledger/aries-framework-go/pkg/doc/util" 26 "github.com/hyperledger/aries-framework-go/pkg/doc/util/signature" 27 "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" 28 "github.com/hyperledger/aries-framework-go/pkg/kms" 29 spi "github.com/hyperledger/aries-framework-go/spi/log" 30 ) 31 32 //nolint:gochecknoglobals 33 var ( 34 // Private key generated by ed25519.GenerateKey(rand.Reader). 35 issuerPrivKey = ed25519.PrivateKey{72, 67, 163, 188, 235, 199, 239, 146, 129, 52, 228, 34, 44, 106, 23, 144, 189, 57, 115, 171, 4, 217, 54, 121, 41, 155, 251, 83, 1, 240, 238, 65, 234, 100, 192, 93, 251, 181, 198, 73, 122, 220, 27, 48, 93, 73, 166, 33, 152, 140, 168, 36, 9, 205, 59, 161, 137, 7, 164, 9, 176, 252, 1, 171} 36 issuerPubKey = ed25519.PublicKey{234, 100, 192, 93, 251, 181, 198, 73, 122, 220, 27, 48, 93, 73, 166, 33, 152, 140, 168, 36, 9, 205, 59, 161, 137, 7, 164, 9, 176, 252, 1, 171} 37 issued = time.Date(2010, time.January, 1, 19, 23, 24, 0, time.UTC) 38 expired = time.Date(2020, time.January, 1, 19, 23, 24, 0, time.UTC) 39 40 bbsPrivKeyB64 = "PcVroyzTlmnYIIq8In8QOZhpK72AdTjj3EitB9tSNrg" 41 bbsPubKeyB64 = "l0Wtf3gy5f140G5vCoCJw2420hwk6Xw65/DX3ycv1W7/eMky8DyExw+o1s2bmq3sEIJatkiN8f5D4k0766x0UvfbupFX+vVkeqnlOvT6o2cag2osQdMFbBQqAybOM4Gm" 42 ) 43 44 const vcJSON = ` 45 { 46 "@context": [ 47 "https://www.w3.org/2018/credentials/v1", 48 "https://www.w3.org/2018/credentials/examples/v1" 49 ], 50 "credentialSchema": [], 51 "credentialSubject": { 52 "degree": { 53 "type": "BachelorDegree", 54 "university": "MIT" 55 }, 56 "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", 57 "name": "Jayden Doe", 58 "spouse": "did:example:c276e12ec21ebfeb1f712ebc6f1" 59 }, 60 "expirationDate": "2020-01-01T19:23:24Z", 61 "id": "http://example.edu/credentials/1872", 62 "issuanceDate": "2009-01-01T19:23:24Z", 63 "issuer": { 64 "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 65 "name": "Example University" 66 }, 67 "referenceNumber": 83294849, 68 "type": [ 69 "VerifiableCredential", 70 "UniversityDegreeCredential" 71 ] 72 } 73 ` 74 75 func ExampleCredential_embedding() { 76 vc := &UniversityDegreeCredential{ 77 Credential: &verifiable.Credential{ 78 Context: []string{ 79 "https://www.w3.org/2018/credentials/v1", 80 "https://www.w3.org/2018/credentials/examples/v1", 81 }, 82 ID: "http://example.edu/credentials/1872", 83 Types: []string{ 84 "VerifiableCredential", 85 "UniversityDegreeCredential", 86 }, 87 Subject: UniversityDegreeSubject{ 88 ID: "did:example:ebfeb1f712ebc6f1c276e12ec21", 89 Name: "Jayden Doe", 90 Spouse: "did:example:c276e12ec21ebfeb1f712ebc6f1", 91 Degree: UniversityDegree{ 92 Type: "BachelorDegree", 93 University: "MIT", 94 }, 95 }, 96 Issuer: verifiable.Issuer{ 97 ID: "did:example:76e12ec712ebc6f1c221ebfeb1f", 98 CustomFields: verifiable.CustomFields{"name": "Example University"}, 99 }, 100 Issued: util.NewTime(issued), 101 Expired: util.NewTime(expired), 102 Schemas: []verifiable.TypedID{}, 103 }, 104 ReferenceNumber: 83294847, 105 } 106 107 // Marshal to JSON to verify the result of decoding. 108 vcBytes, err := json.Marshal(vc) 109 if err != nil { 110 panic("failed to marshal VC to JSON") 111 } 112 113 fmt.Println(string(vcBytes)) 114 115 // Marshal to JWS. 116 jwtClaims, err := vc.JWTClaims(true) 117 if err != nil { 118 panic(fmt.Errorf("failed to marshal JWT claims of VC: %w", err)) 119 } 120 121 signer := signature.GetEd25519Signer(issuerPrivKey, issuerPubKey) 122 123 jws, err := jwtClaims.MarshalJWS(verifiable.EdDSA, signer, "did:123#key1") 124 if err != nil { 125 panic(fmt.Errorf("failed to sign VC inside JWT: %w", err)) 126 } 127 128 fmt.Println(jws) 129 130 // Parse JWS and make sure it's coincide with JSON. 131 vcParsed, err := verifiable.ParseCredential( 132 []byte(jws), 133 verifiable.WithPublicKeyFetcher(verifiable.SingleKey(issuerPubKey, kms.ED25519)), 134 verifiable.WithJSONLDDocumentLoader(getJSONLDDocumentLoader())) 135 if err != nil { 136 panic(fmt.Errorf("failed to encode VC from JWS: %w", err)) 137 } 138 139 // When a Credential was parsed from JWS, it Marshals into a JSON string containing the original JWS. 140 141 vcBytesFromJWS, err := vcParsed.MarshalJSON() 142 if err != nil { 143 panic(fmt.Errorf("failed to marshal VC: %w", err)) 144 } 145 146 // todo missing referenceNumber here (https://github.com/hyperledger/aries-framework-go/issues/847) 147 fmt.Println(string(vcBytesFromJWS)) 148 149 // To marshal the Credential into JSON-LD form, clear the JWT field. 150 151 vcParsed.JWT = "" 152 153 vcBytesFromJWS, err = vcParsed.MarshalJSON() 154 if err != nil { 155 panic(fmt.Errorf("failed to marshal VC: %w", err)) 156 } 157 158 fmt.Println(string(vcBytesFromJWS)) 159 160 // Output: 161 // {"@context":["https://www.w3.org/2018/credentials/v1","https://www.w3.org/2018/credentials/examples/v1"],"credentialSubject":{"degree":{"type":"BachelorDegree","university":"MIT"},"id":"did:example:ebfeb1f712ebc6f1c276e12ec21","name":"Jayden Doe","spouse":"did:example:c276e12ec21ebfeb1f712ebc6f1"},"expirationDate":"2020-01-01T19:23:24Z","id":"http://example.edu/credentials/1872","issuanceDate":"2010-01-01T19:23:24Z","issuer":{"id":"did:example:76e12ec712ebc6f1c221ebfeb1f","name":"Example University"},"referenceNumber":83294847,"type":["VerifiableCredential","UniversityDegreeCredential"]} 162 // eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDoxMjMja2V5MSJ9.eyJleHAiOjE1Nzc5MDY2MDQsImlhdCI6MTI2MjM3MzgwNCwiaXNzIjoiZGlkOmV4YW1wbGU6NzZlMTJlYzcxMmViYzZmMWMyMjFlYmZlYjFmIiwianRpIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzE4NzIiLCJuYmYiOjEyNjIzNzM4MDQsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiXSwiY3JlZGVudGlhbFN1YmplY3QiOnsiZGVncmVlIjp7InR5cGUiOiJCYWNoZWxvckRlZ3JlZSIsInVuaXZlcnNpdHkiOiJNSVQifSwiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJuYW1lIjoiSmF5ZGVuIERvZSIsInNwb3VzZSI6ImRpZDpleGFtcGxlOmMyNzZlMTJlYzIxZWJmZWIxZjcxMmViYzZmMSJ9LCJpc3N1ZXIiOnsibmFtZSI6IkV4YW1wbGUgVW5pdmVyc2l0eSJ9LCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVW5pdmVyc2l0eURlZ3JlZUNyZWRlbnRpYWwiXX19.4VSZj1tPovsqtBihcWHxlGaE25bBNOqNdH85UoAz7XbV1rZ9hPYklwuV_sRRbcQOcEjTaRik2tzwsOPOz5EDAg 163 // "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDoxMjMja2V5MSJ9.eyJleHAiOjE1Nzc5MDY2MDQsImlhdCI6MTI2MjM3MzgwNCwiaXNzIjoiZGlkOmV4YW1wbGU6NzZlMTJlYzcxMmViYzZmMWMyMjFlYmZlYjFmIiwianRpIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzE4NzIiLCJuYmYiOjEyNjIzNzM4MDQsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiXSwiY3JlZGVudGlhbFN1YmplY3QiOnsiZGVncmVlIjp7InR5cGUiOiJCYWNoZWxvckRlZ3JlZSIsInVuaXZlcnNpdHkiOiJNSVQifSwiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJuYW1lIjoiSmF5ZGVuIERvZSIsInNwb3VzZSI6ImRpZDpleGFtcGxlOmMyNzZlMTJlYzIxZWJmZWIxZjcxMmViYzZmMSJ9LCJpc3N1ZXIiOnsibmFtZSI6IkV4YW1wbGUgVW5pdmVyc2l0eSJ9LCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVW5pdmVyc2l0eURlZ3JlZUNyZWRlbnRpYWwiXX19.4VSZj1tPovsqtBihcWHxlGaE25bBNOqNdH85UoAz7XbV1rZ9hPYklwuV_sRRbcQOcEjTaRik2tzwsOPOz5EDAg" 164 // {"@context":["https://www.w3.org/2018/credentials/v1","https://www.w3.org/2018/credentials/examples/v1"],"credentialSubject":{"degree":{"type":"BachelorDegree","university":"MIT"},"id":"did:example:ebfeb1f712ebc6f1c276e12ec21","name":"Jayden Doe","spouse":"did:example:c276e12ec21ebfeb1f712ebc6f1"},"expirationDate":"2020-01-01T19:23:24Z","id":"http://example.edu/credentials/1872","issuanceDate":"2010-01-01T19:23:24Z","issuer":{"id":"did:example:76e12ec712ebc6f1c221ebfeb1f","name":"Example University"},"type":["VerifiableCredential","UniversityDegreeCredential"]} 165 } 166 167 func ExampleCredential_extraFields() { 168 vc := &verifiable.Credential{ 169 Context: []string{ 170 "https://www.w3.org/2018/credentials/v1", 171 "https://www.w3.org/2018/credentials/examples/v1", 172 }, 173 ID: "http://example.edu/credentials/1872", 174 Types: []string{ 175 "VerifiableCredential", 176 "UniversityDegreeCredential", 177 }, 178 Subject: UniversityDegreeSubject{ 179 ID: "did:example:ebfeb1f712ebc6f1c276e12ec21", 180 Name: "Jayden Doe", 181 Spouse: "did:example:c276e12ec21ebfeb1f712ebc6f1", 182 Degree: UniversityDegree{ 183 Type: "BachelorDegree", 184 University: "MIT", 185 }, 186 }, 187 Issuer: verifiable.Issuer{ 188 ID: "did:example:76e12ec712ebc6f1c221ebfeb1f", 189 CustomFields: verifiable.CustomFields{"name": "Example University"}, 190 }, 191 Issued: util.NewTime(issued), 192 Expired: util.NewTime(expired), 193 Schemas: []verifiable.TypedID{}, 194 CustomFields: map[string]interface{}{ 195 "referenceNumber": 83294847, 196 }, 197 } 198 199 // Marshal to JSON. 200 vcBytes, err := json.Marshal(vc) 201 if err != nil { 202 panic("failed to marshal VC to JSON") 203 } 204 205 fmt.Println(string(vcBytes)) 206 207 // Marshal to JWS. 208 jwtClaims, err := vc.JWTClaims(true) 209 if err != nil { 210 panic(fmt.Errorf("failed to marshal JWT claims of VC: %w", err)) 211 } 212 213 signer := signature.GetEd25519Signer(issuerPrivKey, issuerPubKey) 214 215 jws, err := jwtClaims.MarshalJWS(verifiable.EdDSA, signer, "did:123#key1") 216 if err != nil { 217 panic(fmt.Errorf("failed to sign VC inside JWT: %w", err)) 218 } 219 220 fmt.Println(jws) 221 222 // Parse JWS and make sure it's coincide with JSON. 223 vcParsed, err := verifiable.ParseCredential( 224 []byte(jws), 225 verifiable.WithPublicKeyFetcher(verifiable.SingleKey(issuerPubKey, kms.ED25519)), 226 verifiable.WithJSONLDDocumentLoader(getJSONLDDocumentLoader())) 227 if err != nil { 228 panic(fmt.Errorf("failed to encode VC from JWS: %w", err)) 229 } 230 231 vcParsed.JWT = "" 232 233 vcBytesFromJWS, err := vcParsed.MarshalJSON() 234 if err != nil { 235 panic(fmt.Errorf("failed to marshal VC: %w", err)) 236 } 237 238 fmt.Println(string(vcBytesFromJWS)) 239 240 // Output: 241 // {"@context":["https://www.w3.org/2018/credentials/v1","https://www.w3.org/2018/credentials/examples/v1"],"credentialSubject":{"degree":{"type":"BachelorDegree","university":"MIT"},"id":"did:example:ebfeb1f712ebc6f1c276e12ec21","name":"Jayden Doe","spouse":"did:example:c276e12ec21ebfeb1f712ebc6f1"},"expirationDate":"2020-01-01T19:23:24Z","id":"http://example.edu/credentials/1872","issuanceDate":"2010-01-01T19:23:24Z","issuer":{"id":"did:example:76e12ec712ebc6f1c221ebfeb1f","name":"Example University"},"referenceNumber":83294847,"type":["VerifiableCredential","UniversityDegreeCredential"]} 242 // eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDoxMjMja2V5MSJ9.eyJleHAiOjE1Nzc5MDY2MDQsImlhdCI6MTI2MjM3MzgwNCwiaXNzIjoiZGlkOmV4YW1wbGU6NzZlMTJlYzcxMmViYzZmMWMyMjFlYmZlYjFmIiwianRpIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzE4NzIiLCJuYmYiOjEyNjIzNzM4MDQsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiXSwiY3JlZGVudGlhbFN1YmplY3QiOnsiZGVncmVlIjp7InR5cGUiOiJCYWNoZWxvckRlZ3JlZSIsInVuaXZlcnNpdHkiOiJNSVQifSwiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJuYW1lIjoiSmF5ZGVuIERvZSIsInNwb3VzZSI6ImRpZDpleGFtcGxlOmMyNzZlMTJlYzIxZWJmZWIxZjcxMmViYzZmMSJ9LCJpc3N1ZXIiOnsibmFtZSI6IkV4YW1wbGUgVW5pdmVyc2l0eSJ9LCJyZWZlcmVuY2VOdW1iZXIiOjguMzI5NDg0N2UrMDcsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCJdfX0.rmOsOJbKp68XeAw3SR93A67bgDYeOdLP3VDFIwbaNguE9eGQgdYjyAA2q07RbUD-uPoQMIpQDH6uhVAWYBDWCg 243 // {"@context":["https://www.w3.org/2018/credentials/v1","https://www.w3.org/2018/credentials/examples/v1"],"credentialSubject":{"degree":{"type":"BachelorDegree","university":"MIT"},"id":"did:example:ebfeb1f712ebc6f1c276e12ec21","name":"Jayden Doe","spouse":"did:example:c276e12ec21ebfeb1f712ebc6f1"},"expirationDate":"2020-01-01T19:23:24Z","id":"http://example.edu/credentials/1872","issuanceDate":"2010-01-01T19:23:24Z","issuer":{"id":"did:example:76e12ec712ebc6f1c221ebfeb1f","name":"Example University"},"referenceNumber":83294847,"type":["VerifiableCredential","UniversityDegreeCredential"]} 244 } 245 246 func ExampleParseCredential() { 247 // Issuer is about to issue the university degree credential for the Holder 248 vcEncoded := &verifiable.Credential{ 249 Context: []string{ 250 "https://www.w3.org/2018/credentials/v1", 251 "https://www.w3.org/2018/credentials/examples/v1", 252 }, 253 ID: "http://example.edu/credentials/1872", 254 Types: []string{ 255 "VerifiableCredential", 256 "UniversityDegreeCredential", 257 }, 258 Subject: UniversityDegreeSubject{ 259 ID: "did:example:ebfeb1f712ebc6f1c276e12ec21", 260 Name: "Jayden Doe", 261 Spouse: "did:example:c276e12ec21ebfeb1f712ebc6f1", 262 Degree: UniversityDegree{ 263 Type: "BachelorDegree", 264 University: "MIT", 265 }, 266 }, 267 Issuer: verifiable.Issuer{ 268 ID: "did:example:76e12ec712ebc6f1c221ebfeb1f", 269 CustomFields: verifiable.CustomFields{"name": "Example University"}, 270 }, 271 Issued: util.NewTime(issued), 272 Expired: util.NewTime(expired), 273 Schemas: []verifiable.TypedID{}, 274 CustomFields: map[string]interface{}{ 275 "referenceNumber": 83294847, 276 }, 277 } 278 279 // ... in JWS form. 280 jwtClaims, err := vcEncoded.JWTClaims(true) 281 if err != nil { 282 panic(fmt.Errorf("failed to marshal JWT claims of VC: %w", err)) 283 } 284 285 signer := signature.GetEd25519Signer(issuerPrivKey, issuerPubKey) 286 287 jws, err := jwtClaims.MarshalJWS(verifiable.EdDSA, signer, "did:123#key1") 288 if err != nil { 289 panic(fmt.Errorf("failed to sign VC inside JWT: %w", err)) 290 } 291 292 // The Holder receives JWS and decodes it. 293 vcParsed, err := verifiable.ParseCredential( 294 []byte(jws), 295 verifiable.WithPublicKeyFetcher(verifiable.SingleKey(issuerPubKey, kms.ED25519)), 296 verifiable.WithJSONLDDocumentLoader(getJSONLDDocumentLoader())) 297 if err != nil { 298 panic(fmt.Errorf("failed to decode VC JWS: %w", err)) 299 } 300 301 // When parsing a verifiable.Credential from JWS, Credential.JWT is set to the raw JWS value. 302 // This allows the user to save the Credential and verify it later. 303 304 // When Credential.JWT is set, the Credential Marshals into a JSON string containing the original JWS. 305 306 vcDecodedBytes, err := vcParsed.MarshalJSON() 307 if err != nil { 308 panic(fmt.Errorf("failed to marshal VC: %w", err)) 309 } 310 311 // The Holder then e.g. can save the credential to her personal verifiable credential wallet. 312 fmt.Println(string(vcDecodedBytes)) 313 314 // To marshal the Credential into JSON-LD form, clear the JWT field. 315 vcParsed.JWT = "" 316 317 vcDecodedBytes, err = vcParsed.MarshalJSON() 318 if err != nil { 319 panic(fmt.Errorf("failed to marshal VC: %w", err)) 320 } 321 322 // The Credential is now in JSON-LD form.. 323 fmt.Println(string(vcDecodedBytes)) 324 325 // Output: 326 // "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDoxMjMja2V5MSJ9.eyJleHAiOjE1Nzc5MDY2MDQsImlhdCI6MTI2MjM3MzgwNCwiaXNzIjoiZGlkOmV4YW1wbGU6NzZlMTJlYzcxMmViYzZmMWMyMjFlYmZlYjFmIiwianRpIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzE4NzIiLCJuYmYiOjEyNjIzNzM4MDQsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiXSwiY3JlZGVudGlhbFN1YmplY3QiOnsiZGVncmVlIjp7InR5cGUiOiJCYWNoZWxvckRlZ3JlZSIsInVuaXZlcnNpdHkiOiJNSVQifSwiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJuYW1lIjoiSmF5ZGVuIERvZSIsInNwb3VzZSI6ImRpZDpleGFtcGxlOmMyNzZlMTJlYzIxZWJmZWIxZjcxMmViYzZmMSJ9LCJpc3N1ZXIiOnsibmFtZSI6IkV4YW1wbGUgVW5pdmVyc2l0eSJ9LCJyZWZlcmVuY2VOdW1iZXIiOjguMzI5NDg0N2UrMDcsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCJdfX0.rmOsOJbKp68XeAw3SR93A67bgDYeOdLP3VDFIwbaNguE9eGQgdYjyAA2q07RbUD-uPoQMIpQDH6uhVAWYBDWCg" 327 // {"@context":["https://www.w3.org/2018/credentials/v1","https://www.w3.org/2018/credentials/examples/v1"],"credentialSubject":{"degree":{"type":"BachelorDegree","university":"MIT"},"id":"did:example:ebfeb1f712ebc6f1c276e12ec21","name":"Jayden Doe","spouse":"did:example:c276e12ec21ebfeb1f712ebc6f1"},"expirationDate":"2020-01-01T19:23:24Z","id":"http://example.edu/credentials/1872","issuanceDate":"2010-01-01T19:23:24Z","issuer":{"id":"did:example:76e12ec712ebc6f1c221ebfeb1f","name":"Example University"},"referenceNumber":83294847,"type":["VerifiableCredential","UniversityDegreeCredential"]} 328 } 329 330 func ExampleCredential_JWTClaims() { 331 // The Holder wants to send the credential to the Verifier in JWS. 332 vc, err := verifiable.ParseCredential([]byte(vcJSON), 333 verifiable.WithJSONLDDocumentLoader(getJSONLDDocumentLoader())) 334 if err != nil { 335 panic(fmt.Errorf("failed to decode VC JSON: %w", err)) 336 } 337 338 jwtClaims, err := vc.JWTClaims(true) 339 if err != nil { 340 panic(fmt.Errorf("failed to marshal JWT claims of VC: %w", err)) 341 } 342 343 signer := signature.GetEd25519Signer(issuerPrivKey, issuerPubKey) 344 345 jws, err := jwtClaims.MarshalJWS(verifiable.EdDSA, signer, "") 346 if err != nil { 347 panic(fmt.Errorf("failed to sign VC inside JWT: %w", err)) 348 } 349 350 // The Holder passes JWS to Verifier 351 fmt.Println(jws) 352 353 // Output: eyJhbGciOiJFZERTQSIsImtpZCI6IiJ9.eyJleHAiOjE1Nzc5MDY2MDQsImlhdCI6MTIzMDgzNzgwNCwiaXNzIjoiZGlkOmV4YW1wbGU6NzZlMTJlYzcxMmViYzZmMWMyMjFlYmZlYjFmIiwianRpIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzE4NzIiLCJuYmYiOjEyMzA4Mzc4MDQsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIiwiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiXSwiY3JlZGVudGlhbFN1YmplY3QiOnsiZGVncmVlIjp7InR5cGUiOiJCYWNoZWxvckRlZ3JlZSIsInVuaXZlcnNpdHkiOiJNSVQifSwiaWQiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxYzI3NmUxMmVjMjEiLCJuYW1lIjoiSmF5ZGVuIERvZSIsInNwb3VzZSI6ImRpZDpleGFtcGxlOmMyNzZlMTJlYzIxZWJmZWIxZjcxMmViYzZmMSJ9LCJpc3N1ZXIiOnsibmFtZSI6IkV4YW1wbGUgVW5pdmVyc2l0eSJ9LCJyZWZlcmVuY2VOdW1iZXIiOjguMzI5NDg0OWUrMDcsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCJdfX0.3rENm7FBKgw-04J5_RRKS_36UNdiD9DT2-zqdy2mJbpQvKalAt-r49LTSWU50XVxuCUepbo6K_SWDBErsIZ2Aw 354 } 355 356 func ExampleCredential_AddLinkedDataProof() { 357 vc, err := verifiable.ParseCredential([]byte(vcJSON), 358 verifiable.WithJSONLDDocumentLoader(getJSONLDDocumentLoader())) 359 if err != nil { 360 panic(fmt.Errorf("failed to decode VC JSON: %w", err)) 361 } 362 363 signer := signature.GetEd25519Signer(issuerPrivKey, issuerPubKey) 364 365 err = vc.AddLinkedDataProof(&verifiable.LinkedDataProofContext{ 366 Created: &issued, 367 SignatureType: "Ed25519Signature2018", 368 Suite: ed25519signature2018.New(suite.WithSigner(signer)), 369 SignatureRepresentation: verifiable.SignatureJWS, 370 VerificationMethod: "did:example:123456#key1", 371 }, jsonld.WithDocumentLoader(getJSONLDDocumentLoader())) 372 if err != nil { 373 panic(fmt.Errorf("failed to add linked data proof: %w", err)) 374 } 375 376 vcJSONWithProof, err := json.MarshalIndent(vc, "", "\t") 377 if err != nil { 378 panic(fmt.Errorf("failed to marshal VC to JSON: %w", err)) 379 } 380 381 fmt.Println(string(vcJSONWithProof)) 382 383 // Output: { 384 // "@context": [ 385 // "https://www.w3.org/2018/credentials/v1", 386 // "https://www.w3.org/2018/credentials/examples/v1" 387 // ], 388 // "credentialSubject": { 389 // "degree": { 390 // "type": "BachelorDegree", 391 // "university": "MIT" 392 // }, 393 // "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", 394 // "name": "Jayden Doe", 395 // "spouse": "did:example:c276e12ec21ebfeb1f712ebc6f1" 396 // }, 397 // "expirationDate": "2020-01-01T19:23:24Z", 398 // "id": "http://example.edu/credentials/1872", 399 // "issuanceDate": "2009-01-01T19:23:24Z", 400 // "issuer": { 401 // "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", 402 // "name": "Example University" 403 // }, 404 // "proof": { 405 // "created": "2010-01-01T19:23:24Z", 406 // "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..lrkhpRH4tWl6KzQKHlcyAwSm8qUTXIMSKmD3QASF_uI5QW8NWLxLebXmnQpIM8H7umhLA6dINSYVowcaPdpwBw", 407 // "proofPurpose": "assertionMethod", 408 // "type": "Ed25519Signature2018", 409 // "verificationMethod": "did:example:123456#key1" 410 // }, 411 // "referenceNumber": 83294849, 412 // "type": [ 413 // "VerifiableCredential", 414 // "UniversityDegreeCredential" 415 // ] 416 //} 417 } 418 419 //nolint:govet 420 func ExampleCredential_AddLinkedDataProofMultiProofs() { 421 log.SetLevel("aries-framework/json-ld-processor", spi.ERROR) 422 423 vc, err := verifiable.ParseCredential([]byte(vcJSON), 424 verifiable.WithJSONLDDocumentLoader(getJSONLDDocumentLoader())) 425 if err != nil { 426 panic(fmt.Errorf("failed to decode VC JSON: %w", err)) 427 } 428 429 ed25519Signer := signature.GetEd25519Signer(issuerPrivKey, issuerPubKey) 430 431 err = vc.AddLinkedDataProof(&verifiable.LinkedDataProofContext{ 432 Created: &issued, 433 SignatureType: "Ed25519Signature2018", 434 Suite: ed25519signature2018.New(suite.WithSigner(ed25519Signer)), 435 SignatureRepresentation: verifiable.SignatureJWS, 436 VerificationMethod: "did:example:123456#key1", 437 }, jsonld.WithDocumentLoader(getJSONLDDocumentLoader())) 438 if err != nil { 439 panic(err) 440 } 441 442 ecdsaSigner, err := signature.NewSigner(kms.ECDSASecp256k1TypeIEEEP1363) 443 if err != nil { 444 panic(err) 445 } 446 447 err = vc.AddLinkedDataProof(&verifiable.LinkedDataProofContext{ 448 Created: &issued, 449 SignatureType: "JsonWebSignature2020", 450 Suite: jsonwebsignature2020.New(suite.WithSigner(ecdsaSigner)), 451 SignatureRepresentation: verifiable.SignatureJWS, 452 VerificationMethod: "did:example:123456#key2", 453 }, jsonld.WithDocumentLoader(getJSONLDDocumentLoader())) 454 if err != nil { 455 panic(err) 456 } 457 458 vcBytes, err := json.Marshal(vc) 459 if err != nil { 460 panic(err) 461 } 462 463 // Verify the VC with two embedded proofs. 464 ed25519Suite := ed25519signature2018.New(suite.WithVerifier(ed25519signature2018.NewPublicKeyVerifier())) 465 jsonWebSignatureSuite := jsonwebsignature2020.New(suite.WithVerifier(jsonwebsignature2020.NewPublicKeyVerifier())) 466 467 j, err := jwksupport.JWKFromKey(ecdsaSigner.PublicKey()) 468 if err != nil { 469 panic(err) 470 } 471 472 _, err = verifiable.ParseCredential(vcBytes, 473 verifiable.WithEmbeddedSignatureSuites(ed25519Suite, jsonWebSignatureSuite), 474 verifiable.WithPublicKeyFetcher(func(issuerID, keyID string) (*sigverifier.PublicKey, error) { 475 switch keyID { 476 case "#key1": 477 return &sigverifier.PublicKey{ 478 Type: "Ed25519Signature2018", 479 Value: issuerPubKey, 480 }, nil 481 482 case "#key2": 483 return &sigverifier.PublicKey{ 484 Type: "JsonWebKey2020", 485 Value: ecdsaSigner.PublicKeyBytes(), 486 JWK: j, 487 }, nil 488 } 489 490 return nil, errors.New("unsupported keyID") 491 }), 492 verifiable.WithJSONLDOnlyValidRDF(), 493 verifiable.WithJSONLDDocumentLoader(getJSONLDDocumentLoader())) 494 if err != nil { 495 panic(err) 496 } 497 // Output: 498 } 499 500 //nolint:gocyclo 501 func ExampleCredential_GenerateBBSSelectiveDisclosure() { 502 log.SetLevel("aries-framework/json-ld-processor", spi.ERROR) 503 504 vcStr := ` 505 { 506 "@context": [ 507 "https://www.w3.org/2018/credentials/v1", 508 "https://w3id.org/citizenship/v1", 509 "https://w3id.org/security/bbs/v1" 510 ], 511 "id": "https://issuer.oidp.uscis.gov/credentials/83627465", 512 "type": [ 513 "VerifiableCredential", 514 "PermanentResidentCard" 515 ], 516 "issuer": "did:example:b34ca6cd37bbf23", 517 "identifier": "83627465", 518 "name": "Permanent Resident Card", 519 "description": "Government of Example Permanent Resident Card.", 520 "issuanceDate": "2019-12-03T12:19:52Z", 521 "expirationDate": "2029-12-03T12:19:52Z", 522 "credentialSubject": { 523 "id": "did:example:b34ca6cd37bbf23", 524 "type": [ 525 "PermanentResident", 526 "Person" 527 ], 528 "givenName": "JOHN", 529 "familyName": "SMITH", 530 "gender": "Male", 531 "image": "data:image/png;base64,iVBORw0KGgokJggg==", 532 "residentSince": "2015-01-01", 533 "lprCategory": "C09", 534 "lprNumber": "999-999-999", 535 "commuterClassification": "C1", 536 "birthCountry": "Bahamas", 537 "birthDate": "1958-07-17" 538 } 539 } 540 ` 541 542 vc, err := verifiable.ParseCredential([]byte(vcStr), 543 verifiable.WithJSONLDDocumentLoader(getJSONLDDocumentLoader()), 544 verifiable.WithDisabledProofCheck()) 545 if err != nil { 546 panic(fmt.Errorf("failed to decode VC JSON: %w", err)) 547 } 548 549 ed25519Signer := signature.GetEd25519Signer(issuerPrivKey, issuerPubKey) 550 551 err = vc.AddLinkedDataProof(&verifiable.LinkedDataProofContext{ 552 Created: &issued, 553 SignatureType: "Ed25519Signature2018", 554 Suite: ed25519signature2018.New(suite.WithSigner(ed25519Signer)), 555 SignatureRepresentation: verifiable.SignatureJWS, 556 VerificationMethod: "did:example:123456#key1", 557 }, jsonld.WithDocumentLoader(getJSONLDDocumentLoader())) 558 if err != nil { 559 panic(err) 560 } 561 562 pubKey, privKey, err := loadBBSKeyPair(bbsPubKeyB64, bbsPrivKeyB64) 563 if err != nil { 564 panic(err) 565 } 566 567 bbsSigner, err := newBBSSigner(privKey) 568 if err != nil { 569 panic(err) 570 } 571 572 err = vc.AddLinkedDataProof(&verifiable.LinkedDataProofContext{ 573 Created: &issued, 574 SignatureType: "BbsBlsSignature2020", 575 Suite: bbsblssignature2020.New(suite.WithSigner(bbsSigner)), 576 SignatureRepresentation: verifiable.SignatureProofValue, 577 VerificationMethod: "did:example:123456#key1", 578 }, jsonld.WithDocumentLoader(getJSONLDDocumentLoader())) 579 if err != nil { 580 panic(err) 581 } 582 583 // BBS+ signature is generated each time unique, that's why we substitute it with some constant value 584 // for a reason of keeping constant test output. 585 originalProofValue := hideProofValue(vc.Proofs[1], "dummy signature value") 586 587 vcJSONWithProof, err := json.MarshalIndent(vc, "", "\t") 588 if err != nil { 589 panic(fmt.Errorf("failed to marshal VC to JSON: %w", err)) 590 } 591 592 fmt.Println(string(vcJSONWithProof)) 593 594 restoreProofValue(vc.Proofs[1], originalProofValue) 595 596 // Create BBS+ selective disclosure. We explicitly state the fields we want to reveal in the output document. 597 // For example, "credentialSubject.birthDate" is not mentioned and thus will be hidden. 598 // To hide top-level VC fields, "@explicit": true is used on top level of reveal doc. 599 // For example, we can reveal "identifier" top-level VC field only. "issuer" and "issuanceDate" are mandatory 600 // and thus must be defined in reveal doc in case of hiding top-level VC fields. 601 revealDoc := ` 602 { 603 "@context": [ 604 "https://www.w3.org/2018/credentials/v1", 605 "https://w3id.org/citizenship/v1", 606 "https://w3id.org/security/bbs/v1" 607 ], 608 "type": ["VerifiableCredential", "PermanentResidentCard"], 609 "@explicit": true, 610 "identifier": {}, 611 "issuer": {}, 612 "issuanceDate": {}, 613 "credentialSubject": { 614 "@explicit": true, 615 "type": ["PermanentResident", "Person"], 616 "givenName": {}, 617 "familyName": {}, 618 "gender": {} 619 } 620 } 621 ` 622 623 var revealDocMap map[string]interface{} 624 625 err = json.Unmarshal([]byte(revealDoc), &revealDocMap) 626 if err != nil { 627 panic(err) 628 } 629 630 pubKeyBytes, err := pubKey.Marshal() 631 if err != nil { 632 panic(err) 633 } 634 635 vcWithSelectiveDisclosure, err := vc.GenerateBBSSelectiveDisclosure(revealDocMap, []byte("some nonce"), 636 verifiable.WithJSONLDDocumentLoader(getJSONLDDocumentLoader()), 637 verifiable.WithPublicKeyFetcher(verifiable.SingleKey(pubKeyBytes, "Bls12381G2Key2020"))) 638 if err != nil { 639 panic(err) 640 } 641 642 // Only BBS+ related proof left. 643 hideProofValue(vcWithSelectiveDisclosure.Proofs[0], "dummy signature proof value") 644 645 vcJSONWithProof, err = json.MarshalIndent(vcWithSelectiveDisclosure, "", "\t") 646 if err != nil { 647 panic(fmt.Errorf("failed to marshal VC to JSON: %w", err)) 648 } 649 650 fmt.Println() 651 fmt.Println(string(vcJSONWithProof)) 652 // Output:{ 653 // "@context": [ 654 // "https://www.w3.org/2018/credentials/v1", 655 // "https://w3id.org/citizenship/v1", 656 // "https://w3id.org/security/bbs/v1" 657 // ], 658 // "credentialSubject": { 659 // "birthCountry": "Bahamas", 660 // "birthDate": "1958-07-17", 661 // "commuterClassification": "C1", 662 // "familyName": "SMITH", 663 // "gender": "Male", 664 // "givenName": "JOHN", 665 // "id": "did:example:b34ca6cd37bbf23", 666 // "image": "data:image/png;base64,iVBORw0KGgokJggg==", 667 // "lprCategory": "C09", 668 // "lprNumber": "999-999-999", 669 // "residentSince": "2015-01-01", 670 // "type": [ 671 // "PermanentResident", 672 // "Person" 673 // ] 674 // }, 675 // "description": "Government of Example Permanent Resident Card.", 676 // "expirationDate": "2029-12-03T12:19:52Z", 677 // "id": "https://issuer.oidp.uscis.gov/credentials/83627465", 678 // "identifier": "83627465", 679 // "issuanceDate": "2019-12-03T12:19:52Z", 680 // "issuer": "did:example:b34ca6cd37bbf23", 681 // "name": "Permanent Resident Card", 682 // "proof": [ 683 // { 684 // "created": "2010-01-01T19:23:24Z", 685 // "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..HsBapUAZDdaZZy6hrn951768kJaRmNAwTWvVnTDM-Bp5k08eEnnxrii5n47AeWVLDJJo7P0dEPafyC_gMjFPAA", 686 // "proofPurpose": "assertionMethod", 687 // "type": "Ed25519Signature2018", 688 // "verificationMethod": "did:example:123456#key1" 689 // }, 690 // { 691 // "created": "2010-01-01T19:23:24Z", 692 // "proofPurpose": "assertionMethod", 693 // "proofValue": "ZHVtbXkgc2lnbmF0dXJlIHZhbHVl", 694 // "type": "BbsBlsSignature2020", 695 // "verificationMethod": "did:example:123456#key1" 696 // } 697 // ], 698 // "type": [ 699 // "VerifiableCredential", 700 // "PermanentResidentCard" 701 // ] 702 //} 703 // 704 //{ 705 // "@context": [ 706 // "https://www.w3.org/2018/credentials/v1", 707 // "https://w3id.org/citizenship/v1", 708 // "https://w3id.org/security/bbs/v1" 709 // ], 710 // "credentialSubject": { 711 // "familyName": "SMITH", 712 // "gender": "Male", 713 // "givenName": "JOHN", 714 // "id": "did:example:b34ca6cd37bbf23", 715 // "type": [ 716 // "Person", 717 // "PermanentResident" 718 // ] 719 // }, 720 // "id": "https://issuer.oidp.uscis.gov/credentials/83627465", 721 // "identifier": "83627465", 722 // "issuanceDate": "2019-12-03T12:19:52Z", 723 // "issuer": "did:example:b34ca6cd37bbf23", 724 // "proof": { 725 // "created": "2010-01-01T19:23:24Z", 726 // "nonce": "c29tZSBub25jZQ==", 727 // "proofPurpose": "assertionMethod", 728 // "proofValue": "ZHVtbXkgc2lnbmF0dXJlIHByb29mIHZhbHVl", 729 // "type": "BbsBlsSignatureProof2020", 730 // "verificationMethod": "did:example:123456#key1" 731 // }, 732 // "type": [ 733 // "PermanentResidentCard", 734 // "VerifiableCredential" 735 // ] 736 //} 737 } 738 739 func hideProofValue(proof verifiable.Proof, dummyValue string) interface{} { 740 oldProofValue := proof["proofValue"] 741 proof["proofValue"] = base64.StdEncoding.EncodeToString([]byte(dummyValue)) 742 743 return oldProofValue 744 } 745 746 func restoreProofValue(proof verifiable.Proof, proofValue interface{}) { 747 proof["proofValue"] = proofValue 748 }