github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/verifiable/credential_jwt_proof_test.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  SPDX-License-Identifier: Apache-2.0
     4  */
     5  
     6  package verifiable
     7  
     8  import (
     9  	"crypto/ed25519"
    10  	"errors"
    11  	"fmt"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/stretchr/testify/require"
    16  
    17  	"github.com/hyperledger/aries-framework-go/component/models/did/endpoint"
    18  	"github.com/hyperledger/aries-framework-go/pkg/doc/did"
    19  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/verifier"
    20  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    21  	mockvdr "github.com/hyperledger/aries-framework-go/pkg/mock/vdr"
    22  )
    23  
    24  const jwtTestCredential = `
    25  {
    26  	"@context": [
    27  	  "https://www.w3.org/2018/credentials/v1",
    28  	  "https://www.w3.org/2018/credentials/examples/v1"
    29  	],
    30  	"type": ["VerifiableCredential", "UniversityDegreeCredential"],
    31  	"credentialSubject": {
    32  	  "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
    33  	  "degree": {
    34  		"type": "BachelorDegree",
    35  		"university": "MIT"
    36  	  }
    37  	},
    38    "issuer": {
    39      "id": "did:example:76e12ec712ebc6f1c221ebfeb1f",
    40      "name": "Example University"
    41    },
    42    "issuanceDate": "2010-01-01T19:23:24Z",
    43    "expirationDate": "2020-01-01T19:23:24Z"
    44  }
    45  `
    46  
    47  const keyID = "1"
    48  
    49  func TestParseCredentialFromJWS(t *testing.T) {
    50  	testCred := []byte(jwtTestCredential)
    51  
    52  	ed25519Signer, err := newCryptoSigner(kms.ED25519Type)
    53  	require.NoError(t, err)
    54  
    55  	ed25519KeyFetcher := createDIDKeyFetcher(t, ed25519Signer.PublicKeyBytes(), "76e12ec712ebc6f1c221ebfeb1f")
    56  
    57  	rs256Signer, err := newCryptoSigner(kms.RSARS256Type)
    58  	require.NoError(t, err)
    59  
    60  	t.Run("Decoding credential from JWS", func(t *testing.T) {
    61  		vcFromJWT, err := parseTestCredential(t,
    62  			createEdDSAJWS(t, testCred, ed25519Signer, false),
    63  			WithPublicKeyFetcher(ed25519KeyFetcher))
    64  
    65  		require.NoError(t, err)
    66  
    67  		vc, err := parseTestCredential(t, testCred)
    68  		require.NoError(t, err)
    69  
    70  		require.NotEqual(t, "", vcFromJWT.JWT)
    71  		vcFromJWT.JWT = ""
    72  
    73  		require.Equal(t, vc, vcFromJWT)
    74  	})
    75  
    76  	t.Run("Decoding credential from JWS with minimized fields of \"vc\" claim", func(t *testing.T) {
    77  		vcFromJWT, err := parseTestCredential(t,
    78  			createEdDSAJWS(t, testCred, ed25519Signer, true),
    79  			WithPublicKeyFetcher(ed25519KeyFetcher))
    80  
    81  		require.NoError(t, err)
    82  
    83  		vc, err := parseTestCredential(t, testCred)
    84  		require.NoError(t, err)
    85  
    86  		require.NotEqual(t, "", vcFromJWT.JWT)
    87  		vcFromJWT.JWT = ""
    88  
    89  		require.Equal(t, vc, vcFromJWT)
    90  	})
    91  
    92  	t.Run("Failed JWT signature verification of credential", func(t *testing.T) {
    93  		vc, err := parseTestCredential(t,
    94  			createRS256JWS(t, testCred, rs256Signer, true),
    95  			// passing holder's key, while expecting issuer one
    96  			WithPublicKeyFetcher(func(issuerID, keyID string) (*verifier.PublicKey, error) {
    97  				holderSigner, err := newCryptoSigner(kms.RSARS256Type)
    98  				require.NoError(t, err)
    99  
   100  				return &verifier.PublicKey{
   101  					Type:  kms.RSARS256,
   102  					Value: holderSigner.PublicKeyBytes(),
   103  				}, nil
   104  			}))
   105  
   106  		require.Error(t, err)
   107  		require.Contains(t, err.Error(), "JWS decoding: unmarshal VC JWT claims")
   108  		require.Nil(t, vc)
   109  	})
   110  
   111  	t.Run("Failed public key fetching", func(t *testing.T) {
   112  		vc, err := parseTestCredential(t,
   113  			createRS256JWS(t, testCred, rs256Signer, true),
   114  
   115  			WithPublicKeyFetcher(func(issuerID, keyID string) (*verifier.PublicKey, error) {
   116  				return nil, errors.New("test: public key is not found")
   117  			}))
   118  
   119  		require.Error(t, err)
   120  		require.Nil(t, vc)
   121  	})
   122  
   123  	t.Run("Not defined public key fetcher", func(t *testing.T) {
   124  		vc, err := parseTestCredential(t, createRS256JWS(t, testCred, rs256Signer, true))
   125  
   126  		require.Error(t, err)
   127  		require.Contains(t, err.Error(), "public key fetcher is not defined")
   128  		require.Nil(t, vc)
   129  	})
   130  }
   131  
   132  func TestParseCredentialFromJWS_EdDSA(t *testing.T) {
   133  	vcBytes := []byte(jwtTestCredential)
   134  
   135  	signer, err := newCryptoSigner(kms.ED25519Type)
   136  	require.NoError(t, err)
   137  
   138  	vc, err := parseTestCredential(t, vcBytes)
   139  	require.NoError(t, err)
   140  
   141  	vcJWSStr := createEdDSAJWS(t, vcBytes, signer, false)
   142  
   143  	// unmarshal credential from JWS
   144  	vcFromJWS, err := parseTestCredential(t,
   145  		vcJWSStr,
   146  		WithPublicKeyFetcher(SingleKey(signer.PublicKeyBytes(), kms.ED25519)))
   147  	require.NoError(t, err)
   148  
   149  	require.NotEqual(t, "", vcFromJWS.JWT)
   150  	vcFromJWS.JWT = ""
   151  
   152  	// unmarshalled credential must be the same as original one
   153  	require.Equal(t, vc, vcFromJWS)
   154  }
   155  
   156  func TestParseCredentialFromUnsecuredJWT(t *testing.T) {
   157  	testCred := []byte(jwtTestCredential)
   158  
   159  	t.Run("Unsecured JWT decoding with no fields minimization", func(t *testing.T) {
   160  		vcFromJWT, err := parseTestCredential(t, createUnsecuredJWT(t, testCred, false))
   161  
   162  		require.NoError(t, err)
   163  
   164  		vc, err := parseTestCredential(t, testCred)
   165  		require.NoError(t, err)
   166  
   167  		require.Equal(t, vc, vcFromJWT)
   168  	})
   169  
   170  	t.Run("Unsecured JWT decoding with minimized fields", func(t *testing.T) {
   171  		vcFromJWT, err := parseTestCredential(t, createUnsecuredJWT(t, testCred, true))
   172  
   173  		require.NoError(t, err)
   174  
   175  		vc, err := parseTestCredential(t, testCred)
   176  		require.NoError(t, err)
   177  
   178  		require.Equal(t, vc, vcFromJWT)
   179  	})
   180  }
   181  
   182  func TestJwtWithExtension(t *testing.T) {
   183  	signer, err := newCryptoSigner(kms.RSARS256Type)
   184  	require.NoError(t, err)
   185  
   186  	keyFetcher := WithPublicKeyFetcher(func(issuerID, keyID string) (*verifier.PublicKey, error) {
   187  		return &verifier.PublicKey{
   188  			Type:  kms.RSARS256,
   189  			Value: signer.PublicKeyBytes(),
   190  		}, nil
   191  	})
   192  
   193  	vcJWS := createRS256JWS(t, []byte(jwtTestCredential), signer, true)
   194  
   195  	// Decode to base credential.
   196  	cred, err := parseTestCredential(t, vcJWS, keyFetcher)
   197  	require.NoError(t, err)
   198  	require.NotNil(t, cred)
   199  
   200  	// Decode to the Credential extension.
   201  	udc, err := NewUniversityDegreeCredential(t, vcJWS, keyFetcher)
   202  	require.NoError(t, err)
   203  	require.NotNil(t, udc)
   204  
   205  	// Compare that base credentials are the same.
   206  	require.Equal(t, udc.Base, *cred)
   207  }
   208  
   209  func TestRefineVcIssuerFromJwtClaims(t *testing.T) {
   210  	t.Run("refine verifiable credential issuer defined by plain id", func(t *testing.T) {
   211  		vcMap := map[string]interface{}{
   212  			"issuer": "id to override",
   213  		}
   214  		refineVCIssuerFromJWTClaims(vcMap, "did:example:76e12ec712ebc6f1c221ebfeb1f")
   215  		require.Equal(t, "did:example:76e12ec712ebc6f1c221ebfeb1f", vcMap["issuer"])
   216  	})
   217  
   218  	t.Run("refine verifiable credential issuer defined by structure", func(t *testing.T) {
   219  		issuerMap := map[string]interface{}{"id": "id to override", "name": "Example University"}
   220  		vcMap := map[string]interface{}{
   221  			"issuer": issuerMap,
   222  		}
   223  		refineVCIssuerFromJWTClaims(vcMap, "did:example:76e12ec712ebc6f1c221ebfeb1f")
   224  		// issuer id is refined
   225  		require.Equal(t, "did:example:76e12ec712ebc6f1c221ebfeb1f", issuerMap["id"])
   226  		// issuer name remains the same (i.e. not erased)
   227  		require.Equal(t, "Example University", issuerMap["name"])
   228  	})
   229  
   230  	t.Run("refine not defined verifiable credential issuer", func(t *testing.T) {
   231  		vcMap := make(map[string]interface{})
   232  		refineVCIssuerFromJWTClaims(vcMap, "did:example:76e12ec712ebc6f1c221ebfeb1f")
   233  		require.Equal(t, "did:example:76e12ec712ebc6f1c221ebfeb1f", vcMap["issuer"])
   234  	})
   235  }
   236  
   237  func createDIDKeyFetcher(t *testing.T, pub ed25519.PublicKey, didID string) PublicKeyFetcher {
   238  	const (
   239  		didFormat    = "did:%s:%s"
   240  		didPKID      = "%s#keys-%d"
   241  		didServiceID = "%s#endpoint-%d"
   242  		method       = "example"
   243  	)
   244  
   245  	id := fmt.Sprintf(didFormat, method, didID)
   246  	pubKeyID := fmt.Sprintf(didPKID, id, 1)
   247  	pubKey := did.NewVerificationMethodFromBytes(pubKeyID, "Ed25519VerificationKey2018", id, pub)
   248  	services := []did.Service{
   249  		{
   250  			ID:              fmt.Sprintf(didServiceID, id, 1),
   251  			Type:            "did-communication",
   252  			ServiceEndpoint: endpoint.NewDIDCommV1Endpoint("http://localhost:47582"),
   253  			Priority:        0,
   254  			RecipientKeys:   []string{pubKeyID},
   255  		},
   256  	}
   257  	createdTime := time.Now()
   258  	didDoc := &did.Doc{
   259  		Context:            []string{did.ContextV1},
   260  		ID:                 id,
   261  		VerificationMethod: []did.VerificationMethod{*pubKey},
   262  		Service:            services,
   263  		Created:            &createdTime,
   264  		Updated:            &createdTime,
   265  	}
   266  
   267  	v := &mockvdr.MockVDRegistry{
   268  		ResolveValue: didDoc,
   269  	}
   270  
   271  	resolver := NewVDRKeyResolver(v)
   272  	require.NotNil(t, resolver)
   273  
   274  	return resolver.resolvePublicKey
   275  }
   276  
   277  func createRS256JWS(t *testing.T, cred []byte, signer Signer, minimize bool) []byte {
   278  	vc, err := parseTestCredential(t, cred)
   279  	require.NoError(t, err)
   280  
   281  	jwtClaims, err := vc.JWTClaims(minimize)
   282  	require.NoError(t, err)
   283  	vcJWT, err := jwtClaims.MarshalJWS(RS256, signer, vc.Issuer.ID+"#keys-"+keyID)
   284  	require.NoError(t, err)
   285  
   286  	return []byte(vcJWT)
   287  }
   288  
   289  func createEdDSAJWS(t *testing.T, cred []byte, signer Signer, minimize bool) []byte {
   290  	vc, err := parseTestCredential(t, cred)
   291  	require.NoError(t, err)
   292  
   293  	jwtClaims, err := vc.JWTClaims(minimize)
   294  	require.NoError(t, err)
   295  	vcJWT, err := jwtClaims.MarshalJWS(EdDSA, signer, vc.Issuer.ID+"#keys-"+keyID)
   296  	require.NoError(t, err)
   297  
   298  	return []byte(vcJWT)
   299  }
   300  
   301  func createUnsecuredJWT(t *testing.T, cred []byte, minimize bool) []byte {
   302  	vc, err := parseTestCredential(t, cred)
   303  	require.NoError(t, err)
   304  
   305  	jwtClaims, err := vc.JWTClaims(minimize)
   306  	require.NoError(t, err)
   307  	vcJWT, err := jwtClaims.MarshalUnsecuredJWT()
   308  	require.NoError(t, err)
   309  
   310  	return []byte(vcJWT)
   311  }