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