github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/sdjwt/verifier/verifier_test.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package verifier 8 9 import ( 10 "bytes" 11 "crypto/ed25519" 12 "crypto/rand" 13 "crypto/rsa" 14 "encoding/base64" 15 "encoding/json" 16 "fmt" 17 "strings" 18 "testing" 19 "time" 20 21 "github.com/go-jose/go-jose/v3/jwt" 22 "github.com/stretchr/testify/require" 23 24 afjose "github.com/hyperledger/aries-framework-go/pkg/doc/jose" 25 "github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk/jwksupport" 26 afjwt "github.com/hyperledger/aries-framework-go/pkg/doc/jwt" 27 "github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/common" 28 "github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/holder" 29 "github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/issuer" 30 ) 31 32 const ( 33 testIssuer = "https://example.com/issuer" 34 35 testAudience = "https://test.com/verifier" 36 testNonce = "nonce" 37 testSDAlg = "sha-256" 38 39 year = 365 * 24 * 60 * time.Minute 40 ) 41 42 func TestParse(t *testing.T) { 43 r := require.New(t) 44 45 pubKey, privKey, e := ed25519.GenerateKey(rand.Reader) 46 r.NoError(e) 47 48 signer := afjwt.NewEd25519Signer(privKey) 49 selectiveClaims := map[string]interface{}{"given_name": "Albert"} 50 51 now := time.Now() 52 53 var timeOpts []issuer.NewOpt 54 timeOpts = append(timeOpts, 55 issuer.WithNotBefore(jwt.NewNumericDate(now)), 56 issuer.WithIssuedAt(jwt.NewNumericDate(now)), 57 issuer.WithExpiry(jwt.NewNumericDate(now.Add(year)))) 58 59 token, e := issuer.New(testIssuer, selectiveClaims, nil, signer, timeOpts...) 60 r.NoError(e) 61 combinedFormatForIssuance, e := token.Serialize(false) 62 r.NoError(e) 63 64 combinedFormatForPresentation := combinedFormatForIssuance + common.CombinedFormatSeparator 65 66 verifier, e := afjwt.NewEd25519Verifier(pubKey) 67 r.NoError(e) 68 69 t.Run("success - EdDSA signing algorithm", func(t *testing.T) { 70 claims, err := Parse(combinedFormatForPresentation, WithSignatureVerifier(verifier)) 71 r.NoError(err) 72 require.NotNil(t, claims) 73 74 // expected claims iss, exp, iat, nbf, given_name 75 require.Equal(t, 5, len(claims)) 76 }) 77 78 t.Run("success - VC sample", func(t *testing.T) { 79 token, _, err := afjwt.Parse(vcSDJWT, afjwt.WithSignatureVerifier(&holder.NoopSignatureVerifier{})) 80 r.NoError(err) 81 82 var payload map[string]interface{} 83 err = token.DecodeClaims(&payload) 84 r.NoError(err) 85 86 printObject(t, "SD-JWT Payload with VC", payload) 87 88 vcCombinedFormatForPresentation := vcCombinedFormatForIssuance + common.CombinedFormatSeparator 89 claims, err := Parse(vcCombinedFormatForPresentation, WithSignatureVerifier(&holder.NoopSignatureVerifier{})) 90 r.NoError(err) 91 92 printObject(t, "Disclosed Claims with VC", claims) 93 94 // expected claims iat, iss, jti, nbf, sub, vc 95 require.Equal(t, 6, len(claims)) 96 }) 97 98 t.Run("success - RS256 signing algorithm", func(t *testing.T) { 99 privKey, err := rsa.GenerateKey(rand.Reader, 2048) 100 r.NoError(err) 101 102 pubKey := &privKey.PublicKey 103 104 v := afjwt.NewRS256Verifier(pubKey) 105 106 rsaToken, err := issuer.New(testIssuer, selectiveClaims, nil, afjwt.NewRS256Signer(privKey, nil)) 107 r.NoError(err) 108 rsaCombinedFormatForIssuance, err := rsaToken.Serialize(false) 109 require.NoError(t, err) 110 111 cfp := fmt.Sprintf("%s%s", rsaCombinedFormatForIssuance, common.CombinedFormatSeparator) 112 113 claims, err := Parse(cfp, WithSignatureVerifier(v)) 114 r.NoError(err) 115 116 // expected claims iss, given_name 117 require.Equal(t, 2, len(claims)) 118 printObject(t, "claims", claims) 119 }) 120 121 t.Run("success - valid SD-JWT times", func(t *testing.T) { 122 now := time.Now() 123 oneHourInThePast := now.Add(-time.Hour) 124 oneHourInTheFuture := now.Add(time.Hour) 125 126 tokenWithTimes, e := issuer.New(testIssuer, selectiveClaims, nil, signer, 127 issuer.WithIssuedAt(jwt.NewNumericDate(oneHourInThePast)), 128 issuer.WithNotBefore(jwt.NewNumericDate(oneHourInThePast)), 129 issuer.WithExpiry(jwt.NewNumericDate(oneHourInTheFuture))) 130 r.NoError(e) 131 cfIssuance, e := tokenWithTimes.Serialize(false) 132 r.NoError(e) 133 134 cfPresentation := fmt.Sprintf("%s%s", cfIssuance, common.CombinedFormatSeparator) 135 136 claims, err := Parse(cfPresentation, WithSignatureVerifier(verifier)) 137 r.NoError(err) 138 r.NotNil(claims) 139 }) 140 141 t.Run("error - signing algorithm not supported", func(t *testing.T) { 142 claims, err := Parse(combinedFormatForPresentation, 143 WithSignatureVerifier(verifier), 144 WithIssuerSigningAlgorithms([]string{})) 145 r.Error(err) 146 require.Nil(t, claims) 147 require.Equal(t, err.Error(), "failed to verify issuer signing algorithm: alg 'EdDSA' is not in the allowed list") 148 }) 149 150 t.Run("error - additional disclosure", func(t *testing.T) { 151 claims, err := Parse(fmt.Sprintf("%s~%s~", combinedFormatForIssuance, additionalDisclosure), 152 WithSignatureVerifier(verifier)) 153 r.Error(err) 154 r.Nil(claims) 155 r.Contains(err.Error(), 156 "disclosure digest 'qqvcqnczAMgYx7EykI6wwtspyvyvK790ge7MBbQ-Nus' not found in SD-JWT disclosure digests") 157 }) 158 159 t.Run("error - duplicate disclosure", func(t *testing.T) { 160 claims, err := Parse(fmt.Sprintf("%s~%s~%s~", combinedFormatForIssuance, additionalDisclosure, additionalDisclosure), 161 WithSignatureVerifier(verifier)) 162 r.Error(err) 163 r.Nil(claims) 164 r.Contains(err.Error(), 165 "check disclosures: duplicate values found [WyIzanFjYjY3ejl3a3MwOHp3aUs3RXlRIiwgImdpdmVuX25hbWUiLCAiSm9obiJd]") 166 }) 167 168 t.Run("success - with detached payload", func(t *testing.T) { 169 jwsParts := strings.Split(combinedFormatForPresentation, ".") 170 jwsDetached := fmt.Sprintf("%s..%s", jwsParts[0], jwsParts[2]) 171 172 jwsPayload, err := base64.RawURLEncoding.DecodeString(jwsParts[1]) 173 require.NoError(t, err) 174 175 claims, err := Parse(jwsDetached, 176 WithSignatureVerifier(verifier), WithJWTDetachedPayload(jwsPayload)) 177 r.NoError(err) 178 r.NotNil(r, claims) 179 }) 180 181 t.Run("error - invalid claims format", func(t *testing.T) { 182 // claims is not JSON 183 sdJWTSerialized, err := buildJWS(signer, "not JSON") 184 r.NoError(err) 185 186 claims, err := Parse(sdJWTSerialized, WithSignatureVerifier(verifier)) 187 r.Error(err) 188 r.Contains(err.Error(), "read JWT claims from JWS payload") 189 r.Nil(claims) 190 }) 191 192 t.Run("error - invalid claims(iat)", func(t *testing.T) { 193 now := time.Now() 194 oneHourInTheFuture := now.Add(time.Hour) 195 196 tokenWithTimes, e := issuer.New(testIssuer, selectiveClaims, nil, signer, 197 issuer.WithIssuedAt(jwt.NewNumericDate(oneHourInTheFuture))) 198 r.NoError(e) 199 cfi, e := tokenWithTimes.Serialize(false) 200 r.NoError(e) 201 202 claims, err := Parse(cfi, WithSignatureVerifier(verifier)) 203 r.Error(err) 204 r.Contains(err.Error(), 205 "invalid JWT time values: go-jose/go-jose/jwt: validation field, token issued in the future (iat)") 206 r.Nil(claims) 207 }) 208 209 t.Run("error - invalid claims(nbf)", func(t *testing.T) { 210 now := time.Now() 211 oneHourInTheFuture := now.Add(time.Hour) 212 213 tokenWithTimes, e := issuer.New(testIssuer, selectiveClaims, nil, signer, 214 issuer.WithNotBefore(jwt.NewNumericDate(oneHourInTheFuture))) 215 r.NoError(e) 216 cfIssuance, e := tokenWithTimes.Serialize(false) 217 r.NoError(e) 218 219 cfPresentation := fmt.Sprintf("%s%s", cfIssuance, common.CombinedFormatSeparator) 220 221 claims, err := Parse(cfPresentation, WithSignatureVerifier(verifier)) 222 r.Error(err) 223 r.Contains(err.Error(), 224 "invalid JWT time values: go-jose/go-jose/jwt: validation failed, token not valid yet (nbf)") 225 r.Nil(claims) 226 }) 227 228 t.Run("error - invalid claims(expiry)", func(t *testing.T) { 229 now := time.Now() 230 oneHourInThePast := now.Add(-time.Hour) 231 232 tokenWithTimes, e := issuer.New(testIssuer, selectiveClaims, nil, signer, 233 issuer.WithExpiry(jwt.NewNumericDate(oneHourInThePast))) 234 r.NoError(e) 235 cfIssuance, e := tokenWithTimes.Serialize(false) 236 r.NoError(e) 237 238 cfPresentation := fmt.Sprintf("%s%s", cfIssuance, common.CombinedFormatSeparator) 239 240 claims, err := Parse(cfPresentation, WithSignatureVerifier(verifier)) 241 r.Error(err) 242 r.Contains(err.Error(), 243 "invalid JWT time values: go-jose/go-jose/jwt: validation failed, token is expired (exp)") 244 r.Nil(claims) 245 }) 246 } 247 248 func TestHolderBinding(t *testing.T) { 249 r := require.New(t) 250 251 issuerPubKey, issuerPrivateKey, e := ed25519.GenerateKey(rand.Reader) 252 r.NoError(e) 253 254 signer := afjwt.NewEd25519Signer(issuerPrivateKey) 255 256 signatureVerifier, e := afjwt.NewEd25519Verifier(issuerPubKey) 257 r.NoError(e) 258 259 claims := map[string]interface{}{ 260 "given_name": "Albert", 261 "last_name": "Smith", 262 } 263 264 holderPubKey, holderPrivKey, e := ed25519.GenerateKey(rand.Reader) 265 r.NoError(e) 266 267 holderPublicJWK, e := jwksupport.JWKFromKey(holderPubKey) 268 require.NoError(t, e) 269 270 token, e := issuer.New(testIssuer, claims, nil, signer, 271 issuer.WithHolderPublicKey(holderPublicJWK)) 272 r.NoError(e) 273 274 combinedFormatForIssuance, e := token.Serialize(false) 275 r.NoError(e) 276 277 _, e = holder.Parse(combinedFormatForIssuance, holder.WithSignatureVerifier(signatureVerifier)) 278 r.NoError(e) 279 280 holderSigner := afjwt.NewEd25519Signer(holderPrivKey) 281 282 cfi := common.ParseCombinedFormatForIssuance(combinedFormatForIssuance) 283 284 claimsToDisclose := []string{cfi.Disclosures[0]} 285 286 t.Run("success - with holder binding", func(t *testing.T) { 287 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 288 holder.WithHolderBinding(&holder.BindingInfo{ 289 Payload: holder.BindingPayload{ 290 Nonce: testNonce, 291 Audience: testAudience, 292 IssuedAt: jwt.NewNumericDate(time.Now()), 293 }, 294 Signer: holderSigner, 295 })) 296 r.NoError(err) 297 298 verifiedClaims, err := Parse(combinedFormatForPresentation, 299 WithSignatureVerifier(signatureVerifier), 300 WithExpectedAudienceForHolderBinding(testAudience), 301 WithExpectedNonceForHolderBinding(testNonce), 302 WithLeewayForClaimsValidation(time.Hour)) 303 r.NoError(err) 304 305 // expected claims cnf, iss, given_name; last_name was not disclosed 306 r.Equal(3, len(verifiedClaims)) 307 }) 308 309 t.Run("success - with holder binding; expected nonce and audience not specified", func(t *testing.T) { 310 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 311 holder.WithHolderBinding(&holder.BindingInfo{ 312 Payload: holder.BindingPayload{ 313 Nonce: testNonce, 314 Audience: testAudience, 315 IssuedAt: jwt.NewNumericDate(time.Now()), 316 }, 317 Signer: holderSigner, 318 })) 319 r.NoError(err) 320 321 verifiedClaims, err := Parse(combinedFormatForPresentation, 322 WithSignatureVerifier(signatureVerifier), 323 WithHolderBindingRequired(true)) 324 r.NoError(err) 325 326 // expected claims cnf, iss, given_name; last_name was not disclosed 327 r.Equal(3, len(verifiedClaims)) 328 }) 329 330 t.Run("success - with holder binding (required)", func(t *testing.T) { 331 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 332 holder.WithHolderBinding(&holder.BindingInfo{ 333 Payload: holder.BindingPayload{ 334 Nonce: testNonce, 335 Audience: testAudience, 336 IssuedAt: jwt.NewNumericDate(time.Now()), 337 }, 338 Signer: holderSigner, 339 })) 340 r.NoError(err) 341 342 // Verifier will validate combined format for presentation and create verified claims. 343 verifiedClaims, err := Parse(combinedFormatForPresentation, 344 WithSignatureVerifier(signatureVerifier), 345 WithHolderBindingRequired(true), 346 WithExpectedAudienceForHolderBinding(testAudience), 347 WithExpectedNonceForHolderBinding(testNonce)) 348 r.NoError(err) 349 350 // expected claims cnf, iss, given_name; last_name was not disclosed 351 r.Equal(3, len(verifiedClaims)) 352 }) 353 354 t.Run("error - holder binding required, however not provided by the holder", func(t *testing.T) { 355 // holder will not issue holder binding 356 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose) 357 r.NoError(err) 358 359 // Verifier will validate combined format for presentation and create verified claims. 360 verifiedClaims, err := Parse(combinedFormatForPresentation, 361 WithSignatureVerifier(signatureVerifier), 362 WithHolderBindingRequired(true), 363 WithExpectedAudienceForHolderBinding(testAudience), 364 WithExpectedNonceForHolderBinding(testNonce)) 365 r.Error(err) 366 r.Nil(verifiedClaims) 367 368 r.Contains(err.Error(), "failed to verify holder binding: holder binding is required") 369 }) 370 371 t.Run("error - holder signature is not matching holder public key in SD-JWT", func(t *testing.T) { 372 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 373 holder.WithHolderBinding(&holder.BindingInfo{ 374 Payload: holder.BindingPayload{ 375 Nonce: testNonce, 376 Audience: testAudience, 377 IssuedAt: jwt.NewNumericDate(time.Now()), 378 }, 379 Signer: signer, // should have been holder signer; on purpose sign holder binding with wrong signer 380 })) 381 r.NoError(err) 382 383 verifiedClaims, err := Parse(combinedFormatForPresentation, 384 WithSignatureVerifier(signatureVerifier), 385 WithExpectedAudienceForHolderBinding(testAudience), 386 WithExpectedNonceForHolderBinding(testNonce)) 387 r.Error(err) 388 r.Nil(verifiedClaims) 389 390 r.Contains(err.Error(), 391 "failed to verify holder binding: failed to parse holder binding: parse JWT from compact JWS: ed25519: invalid signature") // nolint:lll 392 }) 393 394 t.Run("error - invalid holder binding JWT provided by the holder", func(t *testing.T) { 395 // holder will not issue holder binding 396 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose) 397 r.NoError(err) 398 399 // add fake holder binding 400 combinedFormatForPresentation += "invalid-holder-jwt" 401 402 // Verifier will validate combined format for presentation and create verified claims. 403 verifiedClaims, err := Parse(combinedFormatForPresentation, 404 WithSignatureVerifier(signatureVerifier), 405 WithExpectedAudienceForHolderBinding(testAudience), 406 WithExpectedNonceForHolderBinding(testNonce)) 407 r.Error(err) 408 r.Nil(verifiedClaims) 409 410 r.Contains(err.Error(), 411 "failed to verify holder binding: failed to parse holder binding: JWT of compacted JWS form is supported only") 412 }) 413 414 t.Run("error - holder signature algorithm not supported", func(t *testing.T) { 415 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 416 holder.WithHolderBinding(&holder.BindingInfo{ 417 Payload: holder.BindingPayload{ 418 Nonce: testNonce, 419 Audience: testAudience, 420 IssuedAt: jwt.NewNumericDate(time.Now()), 421 }, 422 Signer: holderSigner, 423 })) 424 r.NoError(err) 425 426 // Verifier will validate combined format for presentation and create verified claims. 427 verifiedClaims, err := Parse(combinedFormatForPresentation, 428 WithSignatureVerifier(signatureVerifier), 429 WithHolderBindingRequired(true), 430 WithExpectedAudienceForHolderBinding(testAudience), 431 WithExpectedNonceForHolderBinding(testNonce), 432 WithHolderSigningAlgorithms([]string{})) 433 r.Error(err) 434 r.Nil(verifiedClaims) 435 436 r.Contains(err.Error(), 437 "failed to verify holder binding: failed to verify holder JWT: failed to verify holder signing algorithm: alg 'EdDSA'") //nolint:lll 438 }) 439 440 t.Run("error - invalid iat for holder binding", func(t *testing.T) { 441 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 442 holder.WithHolderBinding(&holder.BindingInfo{ 443 Payload: holder.BindingPayload{ 444 Nonce: "different", 445 Audience: testAudience, 446 IssuedAt: jwt.NewNumericDate(time.Now().AddDate(1, 0, 0)), // in future 447 }, 448 Signer: holderSigner, 449 })) 450 r.NoError(err) 451 452 verifiedClaims, err := Parse(combinedFormatForPresentation, 453 WithSignatureVerifier(signatureVerifier), 454 WithExpectedAudienceForHolderBinding(testAudience), 455 WithExpectedNonceForHolderBinding(testNonce)) 456 r.Error(err) 457 r.Nil(verifiedClaims) 458 459 r.Contains(err.Error(), 460 "failed to verify holder binding: failed to verify holder JWT: invalid JWT time values: go-jose/go-jose/jwt: validation field, token issued in the future (iat)") //nolint:lll 461 }) 462 463 t.Run("error - unexpected nonce for holder binding", func(t *testing.T) { 464 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 465 holder.WithHolderBinding(&holder.BindingInfo{ 466 Payload: holder.BindingPayload{ 467 Nonce: "different", 468 Audience: testAudience, 469 IssuedAt: jwt.NewNumericDate(time.Now()), 470 }, 471 Signer: holderSigner, 472 })) 473 r.NoError(err) 474 475 verifiedClaims, err := Parse(combinedFormatForPresentation, 476 WithSignatureVerifier(signatureVerifier), 477 WithExpectedAudienceForHolderBinding(testAudience), 478 WithExpectedNonceForHolderBinding(testNonce)) 479 r.Error(err) 480 r.Nil(verifiedClaims) 481 482 r.Contains(err.Error(), 483 "failed to verify holder binding: failed to verify holder JWT: nonce value 'different' does not match expected nonce value 'nonce'") //nolint:lll 484 }) 485 486 t.Run("error - unexpected audience for holder binding", func(t *testing.T) { 487 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 488 holder.WithHolderBinding(&holder.BindingInfo{ 489 Payload: holder.BindingPayload{ 490 Nonce: testNonce, 491 Audience: "different", 492 IssuedAt: jwt.NewNumericDate(time.Now()), 493 }, 494 Signer: holderSigner, 495 })) 496 r.NoError(err) 497 498 verifiedClaims, err := Parse(combinedFormatForPresentation, 499 WithSignatureVerifier(signatureVerifier), 500 WithExpectedAudienceForHolderBinding(testAudience), 501 WithExpectedNonceForHolderBinding(testNonce)) 502 r.Error(err) 503 r.Nil(verifiedClaims) 504 505 r.Contains(err.Error(), 506 "failed to verify holder binding: failed to verify holder JWT: audience value 'different' does not match expected audience value 'https://test.com/verifier'") //nolint:lll 507 }) 508 509 t.Run("error - holder binding provided, however cnf claim not in SD-JWT", func(t *testing.T) { 510 tokenWithoutHolderPublicKey, err := issuer.New(testIssuer, claims, nil, signer) 511 r.NoError(err) 512 513 cfiWithoutHolderPublicKey, err := tokenWithoutHolderPublicKey.Serialize(false) 514 r.NoError(err) 515 516 ctd := []string{common.ParseCombinedFormatForIssuance(cfiWithoutHolderPublicKey).Disclosures[0]} 517 518 _, err = holder.Parse(cfiWithoutHolderPublicKey, holder.WithSignatureVerifier(signatureVerifier)) 519 r.NoError(err) 520 521 combinedFormatForPresentation, err := holder.CreatePresentation(cfiWithoutHolderPublicKey, ctd, 522 holder.WithHolderBinding(&holder.BindingInfo{ 523 Payload: holder.BindingPayload{ 524 Nonce: testNonce, 525 Audience: testAudience, 526 IssuedAt: jwt.NewNumericDate(time.Now()), 527 }, 528 Signer: holderSigner, 529 })) 530 r.NoError(err) 531 532 verifiedClaims, err := Parse(combinedFormatForPresentation, 533 WithSignatureVerifier(signatureVerifier), 534 WithExpectedAudienceForHolderBinding(testAudience), 535 WithExpectedNonceForHolderBinding(testNonce)) 536 r.Error(err) 537 r.Nil(verifiedClaims) 538 539 r.Contains(err.Error(), 540 "failed to verify holder binding: failed to get signature verifier from presentation claims: cnf must be present in SD-JWT") //nolint:lll 541 }) 542 543 t.Run("error - holder binding provided, however cnf is not an object", func(t *testing.T) { 544 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 545 holder.WithHolderBinding(&holder.BindingInfo{ 546 Payload: holder.BindingPayload{ 547 Nonce: testNonce, 548 Audience: testAudience, 549 IssuedAt: jwt.NewNumericDate(time.Now()), 550 }, 551 Signer: holderSigner, 552 })) 553 r.NoError(err) 554 555 cfp := common.ParseCombinedFormatForPresentation(combinedFormatForPresentation) 556 557 claims := make(map[string]interface{}) 558 claims["cnf"] = "abc" 559 claims["_sd_alg"] = testSDAlg 560 561 sdJWT, err := buildJWS(signer, claims) 562 r.NoError(err) 563 564 cfpWithInvalidCNF := sdJWT + common.CombinedFormatSeparator + cfp.HolderBinding 565 566 verifiedClaims, err := Parse(cfpWithInvalidCNF, WithSignatureVerifier(signatureVerifier)) 567 r.Error(err) 568 r.Nil(verifiedClaims) 569 570 r.Contains(err.Error(), 571 "failed to verify holder binding: failed to get signature verifier from presentation claims: cnf must be an object") // nolint:lll 572 }) 573 574 t.Run("error - holder binding provided, cnf is missing jwk", func(t *testing.T) { 575 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 576 holder.WithHolderBinding(&holder.BindingInfo{ 577 Payload: holder.BindingPayload{ 578 Nonce: testNonce, 579 Audience: testAudience, 580 IssuedAt: jwt.NewNumericDate(time.Now()), 581 }, 582 Signer: holderSigner, 583 })) 584 r.NoError(err) 585 586 cfp := common.ParseCombinedFormatForPresentation(combinedFormatForPresentation) 587 588 cnf := make(map[string]interface{}) 589 cnf["test"] = "test" 590 591 claims := make(map[string]interface{}) 592 claims["cnf"] = cnf 593 claims["_sd_alg"] = testSDAlg 594 595 sdJWT, err := buildJWS(signer, claims) 596 r.NoError(err) 597 598 cfpWithInvalidCNF := sdJWT + common.CombinedFormatSeparator + cfp.HolderBinding 599 600 verifiedClaims, err := Parse(cfpWithInvalidCNF, WithSignatureVerifier(signatureVerifier)) 601 r.Error(err) 602 r.Nil(verifiedClaims) 603 604 r.Contains(err.Error(), 605 "failed to verify holder binding: failed to get signature verifier from presentation claims: jwk must be present in cnf") // nolint:lll 606 }) 607 608 t.Run("error - holder binding provided, invalid jwk in cnf", func(t *testing.T) { 609 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 610 holder.WithHolderBinding(&holder.BindingInfo{ 611 Payload: holder.BindingPayload{ 612 Nonce: testNonce, 613 Audience: testAudience, 614 IssuedAt: jwt.NewNumericDate(time.Now()), 615 }, 616 Signer: holderSigner, 617 })) 618 r.NoError(err) 619 620 cfp := common.ParseCombinedFormatForPresentation(combinedFormatForPresentation) 621 622 cnf := make(map[string]interface{}) 623 cnf["jwk"] = make(map[string]interface{}) 624 625 claims := make(map[string]interface{}) 626 claims["cnf"] = cnf 627 claims["_sd_alg"] = testSDAlg 628 629 sdJWT, err := buildJWS(signer, claims) 630 r.NoError(err) 631 632 cfpWithInvalidCNF := sdJWT + common.CombinedFormatSeparator + cfp.HolderBinding 633 634 verifiedClaims, err := Parse(cfpWithInvalidCNF, WithSignatureVerifier(signatureVerifier)) 635 r.Error(err) 636 r.Nil(verifiedClaims) 637 638 r.Contains(err.Error(), 639 "failed to verify holder binding: failed to get signature verifier from presentation claims: unmarshal jwk: unable to read jose JWK, go-jose/go-jose: unknown json web key type ''") // nolint:lll 640 }) 641 642 t.Run("error - holder binding provided, invalid jwk in cnf", func(t *testing.T) { 643 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 644 holder.WithHolderBinding(&holder.BindingInfo{ 645 Payload: holder.BindingPayload{ 646 Nonce: testNonce, 647 Audience: testAudience, 648 IssuedAt: jwt.NewNumericDate(time.Now()), 649 }, 650 Signer: holderSigner, 651 })) 652 r.NoError(err) 653 654 cfp := common.ParseCombinedFormatForPresentation(combinedFormatForPresentation) 655 656 cnf := make(map[string]interface{}) 657 cnf["jwk"] = make(map[string]interface{}) 658 659 claims := make(map[string]interface{}) 660 claims["cnf"] = cnf 661 claims["_sd_alg"] = testSDAlg 662 663 sdJWT, err := buildJWS(signer, claims) 664 r.NoError(err) 665 666 cfpWithInvalidCNF := sdJWT + common.CombinedFormatSeparator + cfp.HolderBinding 667 668 verifiedClaims, err := Parse(cfpWithInvalidCNF, WithSignatureVerifier(signatureVerifier)) 669 r.Error(err) 670 r.Nil(verifiedClaims) 671 672 r.Contains(err.Error(), 673 "failed to verify holder binding: failed to get signature verifier from presentation claims: unmarshal jwk: unable to read jose JWK, go-jose/go-jose: unknown json web key type ''") // nolint:lll 674 }) 675 676 t.Run("error - holder binding provided with EdDSA, jwk in cnf is RSA", func(t *testing.T) { 677 combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, claimsToDisclose, 678 holder.WithHolderBinding(&holder.BindingInfo{ 679 Payload: holder.BindingPayload{ 680 Nonce: testNonce, 681 Audience: testAudience, 682 IssuedAt: jwt.NewNumericDate(time.Now()), 683 }, 684 Signer: holderSigner, 685 })) 686 r.NoError(err) 687 688 cfp := common.ParseCombinedFormatForPresentation(combinedFormatForPresentation) 689 690 claims := make(map[string]interface{}) 691 claims["cnf"] = map[string]interface{}{ 692 "jwk": map[string]interface{}{ 693 "kty": "RSA", 694 "e": "AQAB", 695 "n": "pm4bOHBg-oYhAyPWzR56AWX3rUIXp11", 696 }, 697 } 698 699 claims["_sd_alg"] = testSDAlg 700 701 sdJWT, err := buildJWS(signer, claims) 702 r.NoError(err) 703 704 cfpWithInvalidCNF := sdJWT + common.CombinedFormatSeparator + cfp.HolderBinding 705 706 verifiedClaims, err := Parse(cfpWithInvalidCNF, WithSignatureVerifier(signatureVerifier)) 707 r.Error(err) 708 r.Nil(verifiedClaims) 709 710 r.Contains(err.Error(), 711 "failed to verify holder binding: failed to parse holder binding: parse JWT from compact JWS: no verifier found for EdDSA algorithm") // nolint:lll 712 }) 713 } 714 715 func TestVerifySigningAlgorithm(t *testing.T) { 716 r := require.New(t) 717 718 t.Run("success - EdDSA signing algorithm", func(t *testing.T) { 719 headers := make(afjose.Headers) 720 headers["alg"] = "EdDSA" 721 err := verifySigningAlg(headers, []string{"EdDSA"}) 722 r.NoError(err) 723 }) 724 725 t.Run("error - signing algorithm can not be empty", func(t *testing.T) { 726 headers := make(afjose.Headers) 727 err := verifySigningAlg(headers, []string{"RS256"}) 728 r.Error(err) 729 r.Contains(err.Error(), "missing alg") 730 }) 731 732 t.Run("success - EdDSA signing algorithm not in allowed list", func(t *testing.T) { 733 headers := make(afjose.Headers) 734 headers["alg"] = "EdDSA" 735 err := verifySigningAlg(headers, []string{"RS256"}) 736 r.Error(err) 737 r.Contains(err.Error(), "alg 'EdDSA' is not in the allowed list") 738 }) 739 740 t.Run("error - signing algorithm can not be none", func(t *testing.T) { 741 headers := make(afjose.Headers) 742 headers["alg"] = "none" 743 err := verifySigningAlg(headers, []string{"RS256"}) 744 r.Error(err) 745 r.Contains(err.Error(), "alg value cannot be 'none'") 746 }) 747 } 748 749 func TestGetVerifiedPayload(t *testing.T) { 750 r := require.New(t) 751 752 _, privKey, e := ed25519.GenerateKey(rand.Reader) 753 r.NoError(e) 754 755 signer := afjwt.NewEd25519Signer(privKey) 756 selectiveClaims := map[string]interface{}{"given_name": "Albert"} 757 758 now := time.Now() 759 760 var timeOpts []issuer.NewOpt 761 timeOpts = append(timeOpts, 762 issuer.WithNotBefore(jwt.NewNumericDate(now)), 763 issuer.WithIssuedAt(jwt.NewNumericDate(now)), 764 issuer.WithExpiry(jwt.NewNumericDate(now.Add(year)))) 765 766 token, e := issuer.New(testIssuer, selectiveClaims, nil, signer, timeOpts...) 767 r.NoError(e) 768 769 t.Run("success", func(t *testing.T) { 770 claims, err := getDisclosedClaims(token.Disclosures, token.SignedJWT) 771 r.NoError(err) 772 r.NotNil(claims) 773 r.Equal(5, len(claims)) 774 775 printObject(t, "Disclosed Claims", claims) 776 }) 777 778 t.Run("error - invalid disclosure(not encoded)", func(t *testing.T) { 779 claims, err := getDisclosedClaims([]string{"xyz"}, token.SignedJWT) 780 r.Error(err) 781 r.Nil(claims) 782 r.Contains(err.Error(), 783 "failed to get verified payload: failed to unmarshal disclosure array: invalid character") 784 }) 785 } 786 787 func TestWithJWTDetachedPayload(t *testing.T) { 788 detachedPayloadOpt := WithJWTDetachedPayload([]byte("payload")) 789 require.NotNil(t, detachedPayloadOpt) 790 791 opts := &parseOpts{} 792 detachedPayloadOpt(opts) 793 require.Equal(t, []byte("payload"), opts.detachedPayload) 794 } 795 796 func buildJWS(signer afjose.Signer, claims interface{}) (string, error) { 797 claimsBytes, err := json.Marshal(claims) 798 if err != nil { 799 return "", err 800 } 801 802 jws, err := afjose.NewJWS(nil, nil, claimsBytes, signer) 803 if err != nil { 804 return "", err 805 } 806 807 return jws.SerializeCompact(false) 808 } 809 810 func printObject(t *testing.T, name string, obj interface{}) { 811 t.Helper() 812 813 objBytes, err := json.Marshal(obj) 814 require.NoError(t, err) 815 816 prettyJSON, err := prettyPrint(objBytes) 817 require.NoError(t, err) 818 819 fmt.Println(name + ":") 820 fmt.Println(prettyJSON) 821 } 822 823 func prettyPrint(msg []byte) (string, error) { 824 var prettyJSON bytes.Buffer 825 826 err := json.Indent(&prettyJSON, msg, "", "\t") 827 if err != nil { 828 return "", err 829 } 830 831 return prettyJSON.String(), nil 832 } 833 834 const additionalDisclosure = `WyIzanFjYjY3ejl3a3MwOHp3aUs3RXlRIiwgImdpdmVuX25hbWUiLCAiSm9obiJd` 835 836 const vcCombinedFormatForIssuance = `eyJhbGciOiJFZERTQSJ9.eyJpYXQiOjEuNjczOTg3NTQ3ZSswOSwiaXNzIjoiZGlkOmV4YW1wbGU6NzZlMTJlYzcxMmViYzZmMWMyMjFlYmZlYjFmIiwianRpIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzE4NzIiLCJuYmYiOjEuNjczOTg3NTQ3ZSswOSwic3ViIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwiX3NkX2FsZyI6InNoYS0yNTYiLCJjbmYiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiZDlYemtRbVJMQncxSXpfeHVGUmVLMUItRmpCdTdjT0N3RTlOR2F1d251SSJ9fSwiY3JlZGVudGlhbFN1YmplY3QiOnsiX3NkIjpbInBBdjJUMU10YmRXNGttUUdxT1VVRUpjQmdTZi1mSFRHV2xQVUV4aWlIbVEiLCI2dDlBRUJCQnEzalZwckJ3bGljOGhFWnNNSmxXSXhRdUw5c3ExMzJZTnYwIl0sImRlZ3JlZSI6eyJfc2QiOlsibzZzV2h4RjcxWHBvZ1cxVUxCbU90bjR1SXFGdjJ3ODF6emRuelJXdlpqYyIsIi1yRklXbU1YR3ZXX0FIYVEtODhpMy11ZzRUVjhLUTg5TjdmZmtneFc2X2MiXX0sImlkIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIn0sImZpcnN0X25hbWUiOiJGaXJzdCBuYW1lIiwiaWQiOiJodHRwOi8vZXhhbXBsZS5lZHUvY3JlZGVudGlhbHMvMTg3MiIsImluZm8iOiJJbmZvIiwiaXNzdWFuY2VEYXRlIjoiMjAyMy0wMS0xN1QyMjozMjoyNy40NjgxMDk4MTcrMDI6MDAiLCJpc3N1ZXIiOiJkaWQ6ZXhhbXBsZTo3NmUxMmVjNzEyZWJjNmYxYzIyMWViZmViMWYiLCJsYXN0X25hbWUiOiJMYXN0IG5hbWUiLCJ0eXBlIjoiVmVyaWZpYWJsZUNyZWRlbnRpYWwifX0.GcfSA6NkONxdsm5Lxj9-988eWx1ZvMz5vJ1uh2x8UK1iKIeQLmhsWpA_34RbtAm2HnuoxW4_ZGeiHBzQ1GLTDQ~WyJFWkVDRVZ1YWVJOXhZWmlWb3VMQldBIiwidHlwZSIsIkJhY2hlbG9yRGVncmVlIl0~WyJyMno1UzZMa25FRTR3TWwteFB0VEx3IiwiZGVncmVlIiwiTUlUIl0~WyJ2VkhfaGhNQy1aSUt5WFdtdDUyOWpnIiwic3BvdXNlIiwiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIl0~WyJrVzh0WVVwbVl1VmRoZktFT050TnFnIiwibmFtZSIsIkpheWRlbiBEb2UiXQ` // nolint: lll 837 const vcSDJWT = `eyJhbGciOiJFZERTQSJ9.eyJpYXQiOjEuNjczOTg3NTQ3ZSswOSwiaXNzIjoiZGlkOmV4YW1wbGU6NzZlMTJlYzcxMmViYzZmMWMyMjFlYmZlYjFmIiwianRpIjoiaHR0cDovL2V4YW1wbGUuZWR1L2NyZWRlbnRpYWxzLzE4NzIiLCJuYmYiOjEuNjczOTg3NTQ3ZSswOSwic3ViIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwiX3NkX2FsZyI6InNoYS0yNTYiLCJjbmYiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiZDlYemtRbVJMQncxSXpfeHVGUmVLMUItRmpCdTdjT0N3RTlOR2F1d251SSJ9fSwiY3JlZGVudGlhbFN1YmplY3QiOnsiX3NkIjpbInBBdjJUMU10YmRXNGttUUdxT1VVRUpjQmdTZi1mSFRHV2xQVUV4aWlIbVEiLCI2dDlBRUJCQnEzalZwckJ3bGljOGhFWnNNSmxXSXhRdUw5c3ExMzJZTnYwIl0sImRlZ3JlZSI6eyJfc2QiOlsibzZzV2h4RjcxWHBvZ1cxVUxCbU90bjR1SXFGdjJ3ODF6emRuelJXdlpqYyIsIi1yRklXbU1YR3ZXX0FIYVEtODhpMy11ZzRUVjhLUTg5TjdmZmtneFc2X2MiXX0sImlkIjoiZGlkOmV4YW1wbGU6ZWJmZWIxZjcxMmViYzZmMWMyNzZlMTJlYzIxIn0sImZpcnN0X25hbWUiOiJGaXJzdCBuYW1lIiwiaWQiOiJodHRwOi8vZXhhbXBsZS5lZHUvY3JlZGVudGlhbHMvMTg3MiIsImluZm8iOiJJbmZvIiwiaXNzdWFuY2VEYXRlIjoiMjAyMy0wMS0xN1QyMjozMjoyNy40NjgxMDk4MTcrMDI6MDAiLCJpc3N1ZXIiOiJkaWQ6ZXhhbXBsZTo3NmUxMmVjNzEyZWJjNmYxYzIyMWViZmViMWYiLCJsYXN0X25hbWUiOiJMYXN0IG5hbWUiLCJ0eXBlIjoiVmVyaWZpYWJsZUNyZWRlbnRpYWwifX0.GcfSA6NkONxdsm5Lxj9-988eWx1ZvMz5vJ1uh2x8UK1iKIeQLmhsWpA_34RbtAm2HnuoxW4_ZGeiHBzQ1GLTDQ` // nolint:lll