github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/verifiable/presentation_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  	_ "embed"
    10  	"encoding/json"
    11  	"testing"
    12  
    13  	jsonld "github.com/piprate/json-gold/ld"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/hyperledger/aries-framework-go/pkg/doc/ldcontext"
    17  	jsonldsig "github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld"
    18  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite"
    19  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/ed25519signature2018"
    20  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/verifier"
    21  	"github.com/hyperledger/aries-framework-go/pkg/internal/ldtestutil"
    22  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    23  )
    24  
    25  const validPresentation = `
    26  {
    27    "@context": [
    28      "https://www.w3.org/2018/credentials/v1",
    29      "https://www.w3.org/2018/credentials/examples/v1",
    30      "https://trustbloc.github.io/context/vc/examples-v1.jsonld"
    31    ],
    32    "id": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5",
    33    "type": "VerifiablePresentation",
    34    "verifiableCredential": [
    35      {
    36        "@context": [
    37          "https://www.w3.org/2018/credentials/v1",
    38          "https://www.w3.org/2018/credentials/examples/v1"
    39        ],
    40        "id": "http://example.edu/credentials/58473",
    41        "type": ["VerifiableCredential", "UniversityDegreeCredential"],
    42        "issuer": "https://example.edu/issuers/14",
    43        "issuanceDate": "2010-01-01T19:23:24Z",
    44        "credentialSubject": {
    45          "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
    46          "alumniOf": "Example University"
    47        },
    48        "proof": {
    49          "type": "RsaSignature2018"
    50        }
    51      }
    52    ],
    53    "holder": "did:example:ebfeb1f712ebc6f1c276e12ec21"
    54  }
    55  `
    56  
    57  const presentationWithoutCredentials = `
    58  {
    59    "@context": [
    60      "https://www.w3.org/2018/credentials/v1",
    61      "https://www.w3.org/2018/credentials/examples/v1",
    62      "https://trustbloc.github.io/context/vc/examples-v1.jsonld"
    63    ],
    64    "id": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5",
    65    "type": "VerifiablePresentation",
    66    "holder": "did:example:ebfeb1f712ebc6f1c276e12ec21"
    67  }
    68  `
    69  
    70  const validPresentationWithCustomFields = `
    71  {
    72    "@context": [
    73      "https://www.w3.org/2018/credentials/v1",
    74  	"https://trustbloc.github.io/context/vc/presentation-exchange-submission-v1.jsonld"
    75    ],
    76    "id": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5",
    77     "type": [
    78          "VerifiablePresentation",
    79          "PresentationSubmission"
    80      ],
    81      "presentation_submission": {
    82          "descriptor_map": [
    83              {
    84                  "id": "degree_input_1",
    85                  "path": "$.verifiableCredential.[0]"
    86              },
    87              {
    88                  "id": "citizenship_input_1",
    89                  "path": "$.verifiableCredential.[1]"
    90              }
    91          ]
    92      },
    93    "verifiableCredential": [
    94      {
    95        "@context": [
    96          "https://www.w3.org/2018/credentials/v1",
    97          "https://www.w3.org/2018/credentials/examples/v1"
    98        ],
    99        "id": "http://example.edu/credentials/58473",
   100        "type": ["VerifiableCredential", "UniversityDegreeCredential"],
   101        "issuer": "https://example.edu/issuers/14",
   102        "issuanceDate": "2010-01-01T19:23:24Z",
   103        "credentialSubject": {
   104          "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
   105          "alumniOf": "Example University"
   106        },
   107        "proof": {
   108          "type": "RsaSignature2018"
   109        }
   110      }
   111    ],
   112    "holder": "did:example:ebfeb1f712ebc6f1c276e12ec21"
   113  }
   114  `
   115  
   116  //go:embed testdata/validPresentationWithJWTVC.jsonld
   117  var validPresentationWithJWTVC []byte //nolint:gochecknoglobals
   118  
   119  //go:embed testdata/context/presentation_submission_v1.jsonld
   120  var presentationSubmissionV1 []byte //nolint:gochecknoglobals
   121  
   122  func TestParsePresentation(t *testing.T) {
   123  	t.Run("creates a new Verifiable Presentation from JSON with valid structure", func(t *testing.T) {
   124  		vp, err := newTestPresentation(t, []byte(validPresentation), WithPresStrictValidation())
   125  		require.NoError(t, err)
   126  		require.NotNil(t, vp)
   127  
   128  		// validate @context
   129  		require.Equal(t, []string{
   130  			"https://www.w3.org/2018/credentials/v1",
   131  			"https://www.w3.org/2018/credentials/examples/v1",
   132  			"https://trustbloc.github.io/context/vc/examples-v1.jsonld",
   133  		}, vp.Context)
   134  
   135  		// check id
   136  		require.Equal(t, "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", vp.ID)
   137  
   138  		// check type
   139  		require.Equal(t, []string{"VerifiablePresentation"}, vp.Type)
   140  
   141  		// check verifiableCredentials
   142  		require.NotNil(t, vp.Credentials())
   143  		require.Len(t, vp.Credentials(), 1)
   144  
   145  		// check holder
   146  		require.Equal(t, "did:example:ebfeb1f712ebc6f1c276e12ec21", vp.Holder)
   147  	})
   148  
   149  	t.Run("creates a new Verifiable Presentation from valid JSON without credentials", func(t *testing.T) {
   150  		vp, err := newTestPresentation(t, []byte(presentationWithoutCredentials), WithPresStrictValidation())
   151  		require.NoError(t, err)
   152  		require.NotNil(t, vp)
   153  
   154  		// validate @context
   155  		require.Equal(t, []string{
   156  			"https://www.w3.org/2018/credentials/v1",
   157  			"https://www.w3.org/2018/credentials/examples/v1",
   158  			"https://trustbloc.github.io/context/vc/examples-v1.jsonld",
   159  		}, vp.Context)
   160  
   161  		// check id
   162  		require.Equal(t, "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", vp.ID)
   163  
   164  		// check type
   165  		require.Equal(t, []string{"VerifiablePresentation"}, vp.Type)
   166  
   167  		// check verifiableCredentials
   168  		require.Nil(t, vp.Credentials())
   169  		require.Empty(t, vp.Credentials())
   170  
   171  		// check holder
   172  		require.Equal(t, "did:example:ebfeb1f712ebc6f1c276e12ec21", vp.Holder)
   173  
   174  		// check rawPresentation
   175  		rp, err := vp.raw()
   176  		require.NoError(t, err)
   177  
   178  		require.IsType(t, nil, rp.Credential)
   179  	})
   180  
   181  	t.Run("creates a new Verifiable Presentation with custom/additional fields", func(t *testing.T) {
   182  		verify := func(t *testing.T, vp *Presentation) {
   183  			require.Len(t, vp.CustomFields, 1)
   184  			require.Len(t, vp.CustomFields["presentation_submission"], 1)
   185  			submission, ok := vp.CustomFields["presentation_submission"].(map[string]interface{})
   186  			require.True(t, ok)
   187  			require.Len(t, submission, 1)
   188  			descrMap, ok := submission["descriptor_map"].([]interface{})
   189  			require.True(t, ok)
   190  			require.Len(t, descrMap, 2)
   191  		}
   192  
   193  		loader := createTestDocumentLoader(t, ldcontext.Document{
   194  			URL:     "https://trustbloc.github.io/context/vc/presentation-exchange-submission-v1.jsonld",
   195  			Content: presentationSubmissionV1,
   196  		})
   197  
   198  		vp, err := ParsePresentation([]byte(validPresentationWithCustomFields), WithPresJSONLDDocumentLoader(loader))
   199  		require.NoError(t, err)
   200  		require.NotNil(t, vp)
   201  		verify(t, vp)
   202  
   203  		b, e := vp.MarshalJSON()
   204  		require.NoError(t, e)
   205  		require.NotEmpty(t, b)
   206  
   207  		vp, err = ParsePresentation(b, WithPresStrictValidation(), WithPresJSONLDDocumentLoader(loader))
   208  		require.NoError(t, err)
   209  		require.NotNil(t, vp)
   210  		verify(t, vp)
   211  	})
   212  
   213  	t.Run("creates a new Verifiable Presentation from JSON with invalid structure", func(t *testing.T) {
   214  		emptyJSONDoc := "{}"
   215  		vp, err := newTestPresentation(t, []byte(emptyJSONDoc))
   216  		require.Error(t, err)
   217  		require.Nil(t, vp)
   218  	})
   219  
   220  	t.Run("fails to create a new Verifiable Presentation from non-JSON doc", func(t *testing.T) {
   221  		vp, err := newTestPresentation(t, []byte("non json"))
   222  		require.Error(t, err)
   223  		require.Contains(t, err.Error(), "JSON unmarshalling of verifiable presentation")
   224  		require.Nil(t, vp)
   225  	})
   226  
   227  	t.Run("strict VP validation fails because of invalid field in VP", func(t *testing.T) {
   228  		var vpMap map[string]interface{}
   229  
   230  		err := json.Unmarshal([]byte(validPresentation), &vpMap)
   231  		require.NoError(t, err)
   232  
   233  		// add invalid field
   234  		vpMap["foo1"] = "bar1"
   235  
   236  		vpBytes, err := json.Marshal(vpMap)
   237  		require.NoError(t, err)
   238  
   239  		vp, err := newTestPresentation(t, vpBytes, WithPresStrictValidation())
   240  		require.Error(t, err)
   241  		require.EqualError(t, err, "JSON-LD doc has different structure after compaction")
   242  		require.Nil(t, vp)
   243  	})
   244  
   245  	t.Run("strict VP validation fails because of invalid field in VP proof", func(t *testing.T) {
   246  		vp, err := newTestPresentation(t, []byte(validPresentation))
   247  		require.NoError(t, err)
   248  
   249  		signer, err := newCryptoSigner(kms.ED25519Type)
   250  		require.NoError(t, err)
   251  
   252  		ss := ed25519signature2018.New(suite.WithSigner(signer),
   253  			suite.WithVerifier(ed25519signature2018.NewPublicKeyVerifier()))
   254  
   255  		ldpContext := &LinkedDataProofContext{
   256  			SignatureType:           "Ed25519Signature2018",
   257  			SignatureRepresentation: SignatureJWS,
   258  			Suite:                   ss,
   259  			VerificationMethod:      "did:example:123456#key1",
   260  		}
   261  
   262  		err = vp.AddLinkedDataProof(ldpContext, jsonldsig.WithDocumentLoader(createTestDocumentLoader(t)))
   263  		require.NoError(t, err)
   264  
   265  		proof := vp.Proofs[0]
   266  		proof["foo2"] = "bar2"
   267  
   268  		vpBytes, err := json.Marshal(vp)
   269  		require.NoError(t, err)
   270  
   271  		vp, err = newTestPresentation(t, vpBytes,
   272  			WithPresStrictValidation(),
   273  			WithPresPublicKeyFetcher(SingleKey(signer.PublicKeyBytes(), kms.ED25519)))
   274  		require.Error(t, err)
   275  		require.EqualError(t, err, "JSON-LD doc has different structure after compaction")
   276  		require.Nil(t, vp)
   277  	})
   278  
   279  	t.Run("strict VP validation fails because of invalid field in VC of VP", func(t *testing.T) {
   280  		vp, err := newTestPresentation(t, []byte(validPresentation))
   281  		require.NoError(t, err)
   282  
   283  		vc := vp.Credentials()[0]
   284  		require.NotNil(t, vc)
   285  
   286  		vcMap, ok := vc.(map[string]interface{})
   287  		require.True(t, ok)
   288  
   289  		vcMap["foo3"] = "bar3"
   290  
   291  		vpBytes, err := json.Marshal(vp)
   292  		require.NoError(t, err)
   293  
   294  		vp, err = newTestPresentation(t, vpBytes, WithPresStrictValidation())
   295  		require.Error(t, err)
   296  		require.EqualError(t, err, "JSON-LD doc has different structure after compaction")
   297  		require.Nil(t, vp)
   298  	})
   299  
   300  	t.Run("parsing VP with a JWT VC succeeds", func(t *testing.T) {
   301  		loader := createTestDocumentLoader(t, ldcontext.Document{
   302  			URL:     "https://trustbloc.github.io/context/vc/presentation-exchange-submission-v1.jsonld",
   303  			Content: presentationSubmissionV1,
   304  		})
   305  
   306  		vp, err := ParsePresentation(validPresentationWithJWTVC, WithPresDisabledProofCheck(),
   307  			WithPresJSONLDDocumentLoader(loader))
   308  		require.NoError(t, err)
   309  		require.NotNil(t, vp)
   310  	})
   311  
   312  	t.Run("parsing VP with a JWT VC with required JSON-LD checks succeeds", func(t *testing.T) {
   313  		vp, err := ParsePresentation(validPresentationWithJWTVC, WithPresDisabledProofCheck(),
   314  			WithDisabledJSONLDChecks())
   315  		require.NoError(t, err)
   316  		require.NotNil(t, vp)
   317  	})
   318  }
   319  
   320  func TestValidateVP_Context(t *testing.T) {
   321  	t.Run("rejects verifiable presentation with empty context", func(t *testing.T) {
   322  		raw := &rawPresentation{}
   323  		require.NoError(t, json.Unmarshal([]byte(validPresentation), &raw))
   324  		raw.Context = nil
   325  		bytes, err := json.Marshal(raw)
   326  		require.NoError(t, err)
   327  		vp, err := newTestPresentation(t, bytes)
   328  		require.Error(t, err)
   329  		require.Contains(t, err.Error(), "@context is required")
   330  		require.Nil(t, vp)
   331  	})
   332  
   333  	t.Run("rejects verifiable presentation with invalid context", func(t *testing.T) {
   334  		raw := &rawPresentation{}
   335  		require.NoError(t, json.Unmarshal([]byte(validPresentation), &raw))
   336  		raw.Context = []string{
   337  			"https://www.w3.org/2018/credentials/v2",
   338  			"https://www.w3.org/2018/credentials/examples/v1",
   339  		}
   340  		bytes, err := json.Marshal(raw)
   341  		require.NoError(t, err)
   342  		vp, err := newTestPresentation(t, bytes)
   343  		require.Error(t, err)
   344  		require.Contains(t, err.Error(), "does not match: \"https://www.w3.org/2018/credentials/v1\"")
   345  		require.Nil(t, vp)
   346  	})
   347  
   348  	t.Run("generate verifiable presentation with valid string context", func(t *testing.T) {
   349  		raw := &rawPresentation{}
   350  		require.NoError(t, json.Unmarshal([]byte(validPresentation), &raw))
   351  		raw.Context = "https://www.w3.org/2018/credentials/v1"
   352  		bytes, err := json.Marshal(raw)
   353  		require.NoError(t, err)
   354  		vp, err := newTestPresentation(t, bytes)
   355  		require.NoError(t, err)
   356  		require.NotNil(t, vp)
   357  	})
   358  
   359  	t.Run("rejects verifiable presentation with invalid string context", func(t *testing.T) {
   360  		raw := &rawPresentation{}
   361  		require.NoError(t, json.Unmarshal([]byte(validPresentation), &raw))
   362  		raw.Context = "https://www.w3.org/2018/credentials/v2"
   363  		bytes, err := json.Marshal(raw)
   364  		require.NoError(t, err)
   365  		vp, err := newTestPresentation(t, bytes)
   366  		require.Error(t, err)
   367  		require.Contains(t, err.Error(), "does not match: \"https://www.w3.org/2018/credentials/v1\"")
   368  		require.Nil(t, vp)
   369  	})
   370  }
   371  
   372  func TestValidateVP_ID(t *testing.T) {
   373  	t.Run("accept verifiable presentation with string ID", func(t *testing.T) {
   374  		raw := &rawPresentation{}
   375  		require.NoError(t, json.Unmarshal([]byte(validPresentation), &raw))
   376  		raw.ID = "id"
   377  		bytes, err := json.Marshal(raw)
   378  		require.NoError(t, err)
   379  		_, err = newTestPresentation(t, bytes)
   380  		require.NoError(t, err)
   381  	})
   382  }
   383  
   384  func TestValidateVP_Type(t *testing.T) {
   385  	t.Run("accepts verifiable presentation with single VerifiablePresentation type", func(t *testing.T) {
   386  		raw := &rawPresentation{}
   387  		require.NoError(t, json.Unmarshal([]byte(validPresentation), &raw))
   388  		raw.Type = "VerifiablePresentation"
   389  		bytes, err := json.Marshal(raw)
   390  		require.NoError(t, err)
   391  		_, err = newTestPresentation(t, bytes)
   392  		require.NoError(t, err)
   393  	})
   394  
   395  	t.Run("accepts verifiable presentation with multiple types where VerifiablePresentation is a first type",
   396  		func(t *testing.T) {
   397  			raw := &rawPresentation{}
   398  			require.NoError(t, json.Unmarshal([]byte(validPresentation), &raw))
   399  			raw.Type = []string{"VerifiablePresentation", "CredentialManagerPresentation"}
   400  			bytes, err := json.Marshal(raw)
   401  			require.NoError(t, err)
   402  			_, err = newTestPresentation(t, bytes)
   403  			require.NoError(t, err)
   404  		})
   405  
   406  	t.Run("accepts verifiable presentation with multiple types where VerifiablePresentation is not a first type",
   407  		func(t *testing.T) {
   408  			raw := &rawPresentation{}
   409  			require.NoError(t, json.Unmarshal([]byte(validPresentation), &raw))
   410  			raw.Type = []string{"CredentialManagerPresentation", "VerifiablePresentation"}
   411  			bytes, err := json.Marshal(raw)
   412  			require.NoError(t, err)
   413  			_, err = newTestPresentation(t, bytes)
   414  			require.NoError(t, err)
   415  		})
   416  
   417  	t.Run("rejects verifiable presentation with no type defined", func(t *testing.T) {
   418  		raw := &rawPresentation{}
   419  		require.NoError(t, json.Unmarshal([]byte(validPresentation), &raw))
   420  		raw.Type = nil
   421  		bytes, err := json.Marshal(raw)
   422  		require.NoError(t, err)
   423  		vp, err := newTestPresentation(t, bytes)
   424  		require.Error(t, err)
   425  		require.Contains(t, err.Error(), "type is required")
   426  		require.Nil(t, vp)
   427  	})
   428  
   429  	t.Run("rejects verifiable presentation where single type is not VerifiablePresentation", func(t *testing.T) {
   430  		raw := &rawPresentation{}
   431  		require.NoError(t, json.Unmarshal([]byte(validPresentation), &raw))
   432  		raw.Type = "CredentialManagerPresentation"
   433  		bytes, err := json.Marshal(raw)
   434  		require.NoError(t, err)
   435  		vp, err := newTestPresentation(t, bytes)
   436  		require.Error(t, err)
   437  		require.Contains(t, err.Error(), "Does not match pattern '^VerifiablePresentation$'")
   438  		require.Nil(t, vp)
   439  	})
   440  }
   441  
   442  func TestValidateVP_Holder(t *testing.T) {
   443  	t.Run("rejects verifiable presentation with non-url holder", func(t *testing.T) {
   444  		raw := &rawPresentation{}
   445  		require.NoError(t, json.Unmarshal([]byte(validPresentation), &raw))
   446  		raw.Holder = "not valid presentation Holder URL"
   447  		bytes, err := json.Marshal(raw)
   448  		require.NoError(t, err)
   449  		vp, err := newTestPresentation(t, bytes)
   450  		require.Error(t, err)
   451  		require.Contains(t, err.Error(), "holder: Does not match format 'uri'")
   452  		require.Nil(t, vp)
   453  	})
   454  }
   455  
   456  func TestPresentation_MarshalJSON(t *testing.T) {
   457  	vp, err := newTestPresentation(t, []byte(validPresentation))
   458  	require.NoError(t, err)
   459  	require.NotEmpty(t, vp)
   460  
   461  	// convert verifiable credential to json byte data
   462  	vpData, err := vp.MarshalJSON()
   463  	require.NoError(t, err)
   464  	require.NotEmpty(t, vpData)
   465  
   466  	// convert json byte data back to verifiable presentation
   467  	vp2, err := newTestPresentation(t, vpData)
   468  	require.NoError(t, err)
   469  	require.NotEmpty(t, vp2)
   470  
   471  	// verify that verifiable presentations created by ParsePresentation() and MarshalJSON() matches
   472  	require.Equal(t, vp, vp2)
   473  }
   474  
   475  func TestNewPresentation(t *testing.T) {
   476  	r := require.New(t)
   477  
   478  	vc, err := ParseCredential([]byte(validCredential),
   479  		WithJSONLDDocumentLoader(createTestDocumentLoader(t)),
   480  		WithDisabledProofCheck())
   481  	r.NoError(err)
   482  
   483  	// Pass Credential struct pointer
   484  	vp, err := NewPresentation(WithCredentials(vc))
   485  	r.NoError(err)
   486  	r.Len(vp.credentials, 1)
   487  	r.Equal(vc, vp.credentials[0])
   488  
   489  	vp.AddCredentials(&Credential{})
   490  	r.Len(vp.credentials, 2)
   491  
   492  	// Pass VC marshalled into unsecured JWT
   493  	jwtClaims, err := vc.JWTClaims(true)
   494  	r.NoError(err)
   495  
   496  	jwt, err := jwtClaims.MarshalUnsecuredJWT()
   497  	r.NoError(err)
   498  
   499  	vp, err = NewPresentation(WithJWTCredentials(jwt))
   500  	r.NoError(err)
   501  	r.Len(vp.credentials, 1)
   502  	// VC JWT is NOT converted to vc struct, it's kept as is
   503  	r.Equal(jwt, vp.credentials[0])
   504  
   505  	// set multiple credentials
   506  	vp, err = NewPresentation(WithCredentials(vc, vc), WithJWTCredentials(jwt), WithCredentials(vc))
   507  	r.NoError(err)
   508  	r.Len(vp.credentials, 4)
   509  	r.Equal(vc, vp.credentials[0])
   510  	r.Equal(vc, vp.credentials[1])
   511  	r.Equal(jwt, vp.credentials[2])
   512  	r.Equal(vc, vp.credentials[3])
   513  
   514  	// Error - pass unsupported type
   515  	_, err = NewPresentation(WithJWTCredentials("notajwt"))
   516  	r.Error(err)
   517  	r.EqualError(err, "credential is not base64url encoded JWT")
   518  }
   519  
   520  func TestPresentation_decodeCredentials(t *testing.T) {
   521  	r := require.New(t)
   522  
   523  	signer, err := newCryptoSigner(kms.ED25519Type)
   524  	r.NoError(err)
   525  
   526  	vc, err := parseTestCredential(t, []byte(validCredential))
   527  	r.NoError(err)
   528  
   529  	jwtClaims, err := vc.JWTClaims(false)
   530  	r.NoError(err)
   531  
   532  	jws, err := jwtClaims.MarshalJWS(EdDSA, signer, "did:123#k1")
   533  	r.NoError(err)
   534  
   535  	// single credential - JWS
   536  	opts := defaultPresentationOpts()
   537  	opts.jsonldCredentialOpts.jsonldDocumentLoader = createTestDocumentLoader(t)
   538  	opts.publicKeyFetcher = SingleKey(signer.PublicKeyBytes(), kms.ED25519)
   539  	dCreds, err := decodeCredentials(jws, opts)
   540  	r.NoError(err)
   541  	r.Len(dCreds, 1)
   542  
   543  	// no credential
   544  	dCreds, err = decodeCredentials(nil, opts)
   545  	r.NoError(err)
   546  	r.Len(dCreds, 0)
   547  	dCreds, err = decodeCredentials([]interface{}{}, opts)
   548  	r.NoError(err)
   549  	r.Len(dCreds, 0)
   550  
   551  	// single credential - JWS decoding failed (e.g. to no public key fetcher available)
   552  	opts.publicKeyFetcher = nil
   553  	_, err = decodeCredentials(jws, opts)
   554  	r.Error(err)
   555  }
   556  
   557  func TestWithPresPublicKeyFetcher(t *testing.T) {
   558  	vpOpt := WithPresPublicKeyFetcher(SingleKey([]byte("test pubKey"), kms.ED25519))
   559  	require.NotNil(t, vpOpt)
   560  
   561  	opts := &presentationOpts{}
   562  	vpOpt(opts)
   563  	require.NotNil(t, opts.publicKeyFetcher)
   564  }
   565  
   566  func TestWithPresEmbeddedSignatureSuites(t *testing.T) {
   567  	ss := ed25519signature2018.New()
   568  
   569  	vpOpt := WithPresEmbeddedSignatureSuites(ss)
   570  	require.NotNil(t, vpOpt)
   571  
   572  	opts := &presentationOpts{}
   573  	vpOpt(opts)
   574  	require.Equal(t, []verifier.SignatureSuite{ss}, opts.ldpSuites)
   575  }
   576  
   577  func TestWithPresJSONLDDocumentLoader(t *testing.T) {
   578  	documentLoader := jsonld.NewDefaultDocumentLoader(nil)
   579  	presentationOpt := WithPresJSONLDDocumentLoader(documentLoader)
   580  	require.NotNil(t, presentationOpt)
   581  
   582  	opts := &presentationOpts{}
   583  	presentationOpt(opts)
   584  	require.Equal(t, documentLoader, opts.jsonldDocumentLoader)
   585  }
   586  
   587  func TestParseUnverifiedPresentation(t *testing.T) {
   588  	loader, err := ldtestutil.DocumentLoader()
   589  	require.NoError(t, err)
   590  
   591  	// happy path
   592  	vp, err := ParsePresentation([]byte(validPresentation), WithPresDisabledProofCheck(),
   593  		WithPresJSONLDDocumentLoader(loader))
   594  	require.NoError(t, err)
   595  	require.NotNil(t, vp)
   596  
   597  	// delete the embedded proof and check the VP decoding once again
   598  	var vpJSON map[string]interface{}
   599  
   600  	err = json.Unmarshal([]byte(validPresentation), &vpJSON)
   601  	require.NoError(t, err)
   602  	delete(vpJSON, "proof")
   603  
   604  	vpWithoutProofBytes, err := json.Marshal(vpJSON)
   605  	require.NoError(t, err)
   606  
   607  	vp, err = ParsePresentation(vpWithoutProofBytes, WithPresDisabledProofCheck(),
   608  		WithPresJSONLDDocumentLoader(loader))
   609  	require.NoError(t, err)
   610  	require.NotNil(t, vp)
   611  
   612  	// VP decoding error
   613  	vp, err = ParsePresentation([]byte("invalid"), WithPresDisabledProofCheck(),
   614  		WithPresJSONLDDocumentLoader(loader))
   615  	require.Error(t, err)
   616  	require.Nil(t, vp)
   617  }