github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/sdjwt/example_test.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package sdjwt 8 9 import ( 10 "crypto/ed25519" 11 "crypto/rand" 12 "encoding/json" 13 "fmt" 14 "time" 15 16 "github.com/go-jose/go-jose/v3/jwt" 17 18 "github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk" 19 "github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk/jwksupport" 20 afjwt "github.com/hyperledger/aries-framework-go/pkg/doc/jwt" 21 "github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/holder" 22 "github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/issuer" 23 "github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/verifier" 24 ) 25 26 func ExampleSimpleClaims() { //nolint:govet 27 signer, signatureVerifier, err := setUp() 28 if err != nil { 29 fmt.Println("failed to set-up test: %w", err.Error()) 30 } 31 32 claims := map[string]interface{}{ 33 "given_name": "Albert", 34 "last_name": "Smith", 35 } 36 37 // Issuer will issue SD-JWT for specified claims. 38 token, err := issuer.New(testIssuer, claims, nil, signer) 39 if err != nil { 40 fmt.Println("failed to issue SD-JWT: %w", err.Error()) 41 } 42 43 combinedFormatForIssuance, err := token.Serialize(false) 44 if err != nil { 45 fmt.Println("failed to issue SD-JWT: %w", err.Error()) 46 } 47 48 // Holder will parse combined format for issuance and hold on to that 49 // combined format for issuance and the claims that can be selected. 50 holderClaims, err := holder.Parse(combinedFormatForIssuance, holder.WithSignatureVerifier(signatureVerifier)) 51 if err != nil { 52 fmt.Println("holder failed to parse SD-JWT: %w", err.Error()) 53 } 54 55 // The Holder will only select given_name 56 selectedDisclosures := getDisclosuresFromClaimNames([]string{"given_name"}, holderClaims) 57 58 // Holder will disclose only sub-set of claims to verifier. 59 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, selectedDisclosures) 60 if err != nil { 61 fmt.Println("holder failed to create presentation: %w", err.Error()) 62 } 63 64 // Verifier will validate combined format for presentation and create verified claims. 65 verifiedClaims, err := verifier.Parse(combinedFormatForPresentation, 66 verifier.WithSignatureVerifier(signatureVerifier)) 67 if err != nil { 68 fmt.Println("verifier failed to parse holder presentation: %w", err.Error()) 69 } 70 71 verifiedClaimsJSON, err := marshalObj(verifiedClaims) 72 if err != nil { 73 fmt.Println("verifier failed to marshal verified claims: %w", err.Error()) 74 } 75 76 fmt.Println(verifiedClaimsJSON) 77 78 // Output: { 79 // "given_name": "Albert", 80 // "iss": "https://example.com/issuer" 81 //} 82 } 83 84 func ExampleComplexClaimsWithHolderBinding() { //nolint:govet 85 signer, signatureVerifier, err := setUp() 86 if err != nil { 87 fmt.Println("failed to set-up test: %w", err.Error()) 88 } 89 90 holderSigner, holderJWK, err := setUpHolderBinding() 91 if err != nil { 92 fmt.Println("failed to set-up test: %w", err.Error()) 93 } 94 95 claims := map[string]interface{}{ 96 "sub": "john_doe_42", 97 "given_name": "John", 98 "family_name": "Doe", 99 "email": "johndoe@example.com", 100 "phone_number": "+1-202-555-0101", 101 "birthdate": "1940-01-01", 102 "address": map[string]interface{}{ 103 "street_address": "123 Main St", 104 "locality": "Anytown", 105 "region": "Anystate", 106 "country": "US", 107 }, 108 } 109 110 // Issuer will issue SD-JWT for specified claims. Structured claims not selected as an option hence complex object 111 // address will be treated as an object not as a set of properties. Holder public key is provided therefore it will 112 // be added as "cnf" claim. 113 token, err := issuer.New(testIssuer, claims, nil, signer, 114 issuer.WithHolderPublicKey(holderJWK), 115 ) 116 if err != nil { 117 fmt.Println("failed to issue SD-JWT: %w", err.Error()) 118 } 119 120 combinedFormatForIssuance, err := token.Serialize(false) 121 if err != nil { 122 fmt.Println("failed to issue SD-JWT: %w", err.Error()) 123 } 124 125 // Holder will parse combined format for issuance and hold on to that 126 // combined format for issuance and the claims that can be selected. 127 holderClaims, err := holder.Parse(combinedFormatForIssuance, holder.WithSignatureVerifier(signatureVerifier)) 128 if err != nil { 129 fmt.Println("holder failed to parse SD-JWT: %w", err.Error()) 130 } 131 132 // The Holder will only select given_name, address 133 selectedDisclosures := getDisclosuresFromClaimNames([]string{"given_name", "address"}, holderClaims) 134 135 // Holder will disclose only sub-set of claims to verifier. 136 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, selectedDisclosures, 137 holder.WithHolderBinding(&holder.BindingInfo{ 138 Payload: holder.BindingPayload{ 139 Nonce: "nonce", 140 Audience: "https://test.com/verifier", 141 IssuedAt: jwt.NewNumericDate(time.Now()), 142 }, 143 Signer: holderSigner, 144 })) 145 if err != nil { 146 fmt.Println("holder failed to create presentation: %w", err.Error()) 147 } 148 149 // Verifier will validate combined format for presentation and create verified claims. 150 verifiedClaims, err := verifier.Parse(combinedFormatForPresentation, 151 verifier.WithSignatureVerifier(signatureVerifier)) 152 if err != nil { 153 fmt.Println("verifier failed to parse holder presentation: %w", err.Error()) 154 } 155 156 addressClaimsJSON, err := marshalObj(verifiedClaims["address"]) 157 if err != nil { 158 fmt.Println("verifier failed to marshal verified claims: %w", err.Error()) 159 } 160 161 fmt.Println(addressClaimsJSON) 162 163 // Output: { 164 // "country": "US", 165 // "locality": "Anytown", 166 // "region": "Anystate", 167 // "street_address": "123 Main St" 168 //} 169 } 170 171 func ExampleComplexObjectWithStructuredClaims() { //nolint:govet 172 signer, signatureVerifier, err := setUp() 173 if err != nil { 174 fmt.Println("failed to set-up test: %w", err.Error()) 175 } 176 177 claims := map[string]interface{}{ 178 "sub": "john_doe_42", 179 "given_name": "John", 180 "family_name": "Doe", 181 "email": "johndoe@example.com", 182 "phone_number": "+1-202-555-0101", 183 "birthdate": "1940-01-01", 184 "address": map[string]interface{}{ 185 "street_address": "123 Main St", 186 "locality": "Anytown", 187 "region": "Anystate", 188 "country": "US", 189 }, 190 } 191 192 // Issuer will issue SD-JWT for specified claims. 193 token, err := issuer.New(testIssuer, claims, nil, signer, 194 issuer.WithStructuredClaims(true), 195 issuer.WithNonSelectivelyDisclosableClaims([]string{"address.country"}), 196 ) 197 if err != nil { 198 fmt.Println("failed to issue SD-JWT: %w", err.Error()) 199 } 200 201 combinedFormatForIssuance, err := token.Serialize(false) 202 if err != nil { 203 fmt.Println("failed to issue SD-JWT: %w", err.Error()) 204 } 205 206 // Holder will parse combined format for issuance and hold on to that 207 // combined format for issuance and the claims that can be selected. 208 holderClaims, err := holder.Parse(combinedFormatForIssuance, holder.WithSignatureVerifier(signatureVerifier)) 209 if err != nil { 210 fmt.Println("holder failed to parse SD-JWT: %w", err.Error()) 211 } 212 213 // The Holder will only select given_name, street_address 214 selectedDisclosures := getDisclosuresFromClaimNames([]string{"given_name", "street_address"}, holderClaims) 215 216 // Holder will disclose only sub-set of claims to verifier. 217 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, selectedDisclosures) 218 if err != nil { 219 fmt.Println("holder failed to create presentation: %w", err.Error()) 220 } 221 222 // Verifier will validate combined format for presentation and create verified claims. 223 verifiedClaims, err := verifier.Parse(combinedFormatForPresentation, 224 verifier.WithSignatureVerifier(signatureVerifier)) 225 if err != nil { 226 fmt.Println("verifier failed to parse holder presentation: %w", err.Error()) 227 } 228 229 verifiedClaimsJSON, err := marshalObj(verifiedClaims) 230 if err != nil { 231 fmt.Println("verifier failed to marshal verified claims: %w", err.Error()) 232 } 233 234 fmt.Println(verifiedClaimsJSON) 235 236 // Output: { 237 // "address": { 238 // "country": "US", 239 // "street_address": "123 Main St" 240 // }, 241 // "given_name": "John", 242 // "iss": "https://example.com/issuer" 243 //} 244 } 245 func setUp() (*afjwt.JoseED25519Signer, *afjwt.JoseEd25519Verifier, error) { 246 issuerPublicKey, issuerPrivateKey, err := ed25519.GenerateKey(rand.Reader) 247 if err != nil { 248 return nil, nil, err 249 } 250 251 signer := afjwt.NewEd25519Signer(issuerPrivateKey) 252 253 signatureVerifier, err := afjwt.NewEd25519Verifier(issuerPublicKey) 254 if err != nil { 255 return nil, nil, err 256 } 257 258 return signer, signatureVerifier, nil 259 } 260 261 func setUpHolderBinding() (*afjwt.JoseED25519Signer, *jwk.JWK, error) { 262 holderPublicKey, holderPrivateKey, err := ed25519.GenerateKey(rand.Reader) 263 if err != nil { 264 return nil, nil, err 265 } 266 267 holderPublicJWK, err := jwksupport.JWKFromKey(holderPublicKey) 268 if err != nil { 269 return nil, nil, err 270 } 271 272 holderSigner := afjwt.NewEd25519Signer(holderPrivateKey) 273 274 return holderSigner, holderPublicJWK, nil 275 } 276 277 func marshalObj(obj interface{}) (string, error) { 278 objBytes, err := json.Marshal(obj) 279 if err != nil { 280 fmt.Println("failed to marshal object: %w", err.Error()) 281 } 282 283 return prettyPrint(objBytes) 284 }