github.com/hyperledger/aries-framework-go@v0.3.2/pkg/client/issuecredential/rfc0593/event_test.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package rfc0593_test
     8  
     9  import (
    10  	"encoding/json"
    11  	"errors"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/google/uuid"
    16  	"github.com/piprate/json-gold/ld"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/hyperledger/aries-framework-go/component/storageutil/mem"
    20  	"github.com/hyperledger/aries-framework-go/pkg/client/issuecredential/rfc0593"
    21  	"github.com/hyperledger/aries-framework-go/pkg/crypto"
    22  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
    23  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator"
    24  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/didexchange"
    25  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/issuecredential"
    26  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld"
    27  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite"
    28  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/ed25519signature2018"
    29  	"github.com/hyperledger/aries-framework-go/pkg/doc/util"
    30  	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
    31  	"github.com/hyperledger/aries-framework-go/pkg/framework/aries"
    32  	"github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
    33  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    34  	mockcrypto "github.com/hyperledger/aries-framework-go/pkg/mock/crypto"
    35  	mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage"
    36  	"github.com/hyperledger/aries-framework-go/pkg/vdr/fingerprint"
    37  	"github.com/hyperledger/aries-framework-go/spi/storage"
    38  )
    39  
    40  func TestAutoExecute(t *testing.T) { // nolint:gocyclo
    41  	t.Run("auto-executes RFC0593", func(t *testing.T) {
    42  		provider := agent(t)
    43  		events := make(chan service.DIDCommAction)
    44  		go rfc0593.AutoExecute(provider, nil)(events)
    45  
    46  		testCases := []struct {
    47  			label string
    48  			msg   interface{}
    49  			check func(md *issuecredential.MetaData)
    50  		}{
    51  			{
    52  				label: "handles proposals",
    53  				check: func(md *issuecredential.MetaData) { require.NotEmpty(t, md.OfferCredentialV2()) },
    54  				msg: &issuecredential.ProposeCredentialV2{
    55  					Type: issuecredential.ProposeCredentialMsgTypeV2,
    56  					Formats: []issuecredential.Format{{
    57  						AttachID: "123",
    58  						Format:   rfc0593.ProofVCDetailFormat,
    59  					}},
    60  					FiltersAttach: []decorator.Attachment{{
    61  						ID: "123",
    62  						Data: decorator.AttachmentData{
    63  							JSON: randomCredSpec(t),
    64  						},
    65  					}},
    66  				},
    67  			},
    68  			{
    69  				label: "handles offers",
    70  				check: func(md *issuecredential.MetaData) { require.NotEmpty(t, md.RequestCredentialV2()) },
    71  				msg: &issuecredential.OfferCredentialV2{
    72  					Type: issuecredential.OfferCredentialMsgTypeV2,
    73  					Formats: []issuecredential.Format{{
    74  						AttachID: "123",
    75  						Format:   rfc0593.ProofVCDetailFormat,
    76  					}},
    77  					OffersAttach: []decorator.Attachment{{
    78  						ID: "123",
    79  						Data: decorator.AttachmentData{
    80  							JSON: randomCredSpec(t),
    81  						},
    82  					}},
    83  				},
    84  			},
    85  			{
    86  				label: "handles requests",
    87  				check: func(md *issuecredential.MetaData) { require.NotEmpty(t, md.IssueCredentialV2()) },
    88  				msg: &issuecredential.RequestCredentialV2{
    89  					Type: issuecredential.RequestCredentialMsgTypeV2,
    90  					Formats: []issuecredential.Format{{
    91  						AttachID: "123",
    92  						Format:   rfc0593.ProofVCDetailFormat,
    93  					}},
    94  					RequestsAttach: []decorator.Attachment{{
    95  						ID: "123",
    96  						Data: decorator.AttachmentData{
    97  							JSON: randomCredSpec(t),
    98  						},
    99  					}},
   100  				},
   101  			},
   102  		}
   103  
   104  		for i := range testCases {
   105  			tc := testCases[i]
   106  
   107  			t.Run(tc.label, func(t *testing.T) {
   108  				var (
   109  					arg interface{}
   110  					err error
   111  				)
   112  
   113  				ready := make(chan struct{})
   114  				msg := service.NewDIDCommMsgMap(tc.msg)
   115  				msg.SetID(uuid.New().String())
   116  
   117  				go func() {
   118  					events <- service.DIDCommAction{
   119  						ProtocolName: issuecredential.Name,
   120  						Message:      msg,
   121  						Continue: func(a interface{}) {
   122  							arg = a
   123  							ready <- struct{}{}
   124  						},
   125  						Stop: func(e error) {
   126  							err = e
   127  							ready <- struct{}{}
   128  						},
   129  					}
   130  				}()
   131  
   132  				select {
   133  				case <-ready:
   134  				case <-time.After(time.Second):
   135  					require.Fail(t, "timeout")
   136  				}
   137  
   138  				require.NoError(t, err)
   139  				opt, ok := arg.(issuecredential.Opt)
   140  				require.True(t, ok)
   141  
   142  				md := &issuecredential.MetaData{}
   143  				opt(md)
   144  
   145  				tc.check(md)
   146  			})
   147  		}
   148  	})
   149  
   150  	t.Run("forwards events from other protocols", func(t *testing.T) {
   151  		events := make(chan service.DIDCommAction)
   152  		next := make(chan service.DIDCommAction)
   153  
   154  		go rfc0593.AutoExecute(agent(t), next)(events)
   155  
   156  		expected := &didexchange.Complete{
   157  			ID:   uuid.New().String(),
   158  			Type: didexchange.CompleteMsgType,
   159  		}
   160  
   161  		go func() {
   162  			events <- service.DIDCommAction{
   163  				Message: service.NewDIDCommMsgMap(expected),
   164  			}
   165  		}()
   166  
   167  		select {
   168  		case <-time.After(time.Second):
   169  			require.Fail(t, "timeout")
   170  		case event := <-next:
   171  			result := &didexchange.Complete{}
   172  
   173  			err := event.Message.Decode(result)
   174  			require.NoError(t, err)
   175  
   176  			require.Equal(t, expected, result)
   177  		}
   178  	})
   179  
   180  	t.Run("forwards issue-credential events not related to RFC0593", func(t *testing.T) {
   181  		events := make(chan service.DIDCommAction)
   182  		next := make(chan service.DIDCommAction)
   183  
   184  		go rfc0593.AutoExecute(agent(t), next)(events)
   185  
   186  		expected := &issuecredential.RequestCredentialV2{
   187  			Type:    issuecredential.RequestCredentialMsgTypeV2,
   188  			Comment: uuid.New().String(),
   189  			RequestsAttach: []decorator.Attachment{{
   190  				ID: uuid.New().String(),
   191  				Data: decorator.AttachmentData{
   192  					JSON: map[string]interface{}{},
   193  				},
   194  			}},
   195  		}
   196  
   197  		go func() {
   198  			events <- service.DIDCommAction{
   199  				Message: service.NewDIDCommMsgMap(expected),
   200  			}
   201  		}()
   202  
   203  		select {
   204  		case <-time.After(time.Second):
   205  			require.Fail(t, "timeout")
   206  		case event := <-next:
   207  			result := &issuecredential.RequestCredentialV2{}
   208  
   209  			err := event.Message.Decode(result)
   210  			require.NoError(t, err)
   211  			require.Equal(t, expected, result)
   212  		}
   213  	})
   214  
   215  	t.Run("stops the protocol in the event of an error", func(t *testing.T) {
   216  		events := make(chan service.DIDCommAction)
   217  
   218  		go rfc0593.AutoExecute(agent(t), nil)(events)
   219  
   220  		ready := make(chan struct{})
   221  
   222  		go func() {
   223  			events <- service.DIDCommAction{
   224  				Message: service.NewDIDCommMsgMap(&issuecredential.RequestCredentialV2{
   225  					Type:    issuecredential.RequestCredentialMsgTypeV2,
   226  					Comment: uuid.New().String(),
   227  					Formats: []issuecredential.Format{{
   228  						AttachID: "123",
   229  						Format:   rfc0593.ProofVCDetailFormat,
   230  					}},
   231  					RequestsAttach: []decorator.Attachment{{
   232  						ID: "123",
   233  						Data: decorator.AttachmentData{
   234  							JSON: "INVALID",
   235  						},
   236  					}},
   237  				}),
   238  				Continue: func(interface{}) {
   239  					require.Fail(t, "protocol was continued")
   240  				},
   241  				Stop: func(e error) {
   242  					require.Error(t, e)
   243  					require.Contains(t, e.Error(), "failed to unmarshal attachment contents")
   244  					ready <- struct{}{}
   245  				},
   246  			}
   247  		}()
   248  
   249  		select {
   250  		case <-ready:
   251  		case <-time.After(time.Second):
   252  			require.Fail(t, "timeout")
   253  		}
   254  	})
   255  
   256  	t.Run("stops the protocol if the DB cannot be opened", func(t *testing.T) {
   257  		events := make(chan service.DIDCommAction)
   258  
   259  		agent := agent(t, withProtoStateStorageProvider(&mockstorage.MockStoreProvider{
   260  			Store:         &mockstorage.MockStore{Store: make(map[string]mockstorage.DBEntry)},
   261  			FailNamespace: rfc0593.StoreName,
   262  		}))
   263  
   264  		go rfc0593.AutoExecute(agent, nil)(events)
   265  
   266  		ready := make(chan struct{})
   267  
   268  		go func() {
   269  			events <- service.DIDCommAction{
   270  				Message: service.NewDIDCommMsgMap(&issuecredential.RequestCredentialV2{
   271  					Type:    issuecredential.RequestCredentialMsgTypeV2,
   272  					Comment: uuid.New().String(),
   273  					Formats: []issuecredential.Format{{
   274  						AttachID: "123",
   275  						Format:   rfc0593.ProofVCDetailFormat,
   276  					}},
   277  					RequestsAttach: []decorator.Attachment{{
   278  						ID: "123",
   279  						Data: decorator.AttachmentData{
   280  							JSON: randomCredSpec(t),
   281  						},
   282  					}},
   283  				}),
   284  				Continue: func(interface{}) {
   285  					require.Fail(t, "protocol was continued")
   286  				},
   287  				Stop: func(e error) {
   288  					require.Error(t, e)
   289  					require.Contains(t, e.Error(), "failed to open store")
   290  					ready <- struct{}{}
   291  				},
   292  			}
   293  		}()
   294  
   295  		select {
   296  		case <-ready:
   297  		case <-time.After(time.Second):
   298  			require.Fail(t, "timeout")
   299  		}
   300  	})
   301  }
   302  
   303  func TestReplayProposal(t *testing.T) {
   304  	t.Run("replays the proposed spec as an offer", func(t *testing.T) {
   305  		expected := &rfc0593.CredentialSpec{
   306  			Template: marshal(t, newVC(t)),
   307  			Options: &rfc0593.CredentialSpecOptions{
   308  				ProofPurpose: "assertionMethod",
   309  				Created:      time.Now().Format(time.RFC3339),
   310  				Domain:       uuid.New().String(),
   311  				Challenge:    uuid.New().String(),
   312  				ProofType:    "Ed25519Signature2018",
   313  				Status:       &rfc0593.CredentialStatus{Type: "test"},
   314  			},
   315  		}
   316  
   317  		arg, options, err := rfc0593.ReplayProposal(agent(t), service.NewDIDCommMsgMap(&issuecredential.ProposeCredentialV2{
   318  			Type: issuecredential.ProposeCredentialMsgTypeV2,
   319  			Formats: []issuecredential.Format{{
   320  				AttachID: "123",
   321  				Format:   rfc0593.ProofVCDetailFormat,
   322  			}},
   323  			FiltersAttach: []decorator.Attachment{{
   324  				ID: "123",
   325  				Data: decorator.AttachmentData{
   326  					JSON: expected,
   327  				},
   328  			}},
   329  		}))
   330  		require.NoError(t, err)
   331  		require.Equal(t, expected.Options, options)
   332  
   333  		opt, ok := arg.(issuecredential.Opt)
   334  		require.True(t, ok)
   335  
   336  		md := &issuecredential.MetaData{}
   337  		opt(md)
   338  
   339  		require.NotEmpty(t, md.OfferCredentialV2())
   340  		require.Equal(t,
   341  			expected,
   342  			extractPayload(t,
   343  				rfc0593.ProofVCDetailFormat, md.OfferCredentialV2().Formats, md.OfferCredentialV2().OffersAttach),
   344  		)
   345  	})
   346  
   347  	t.Run("error if VC is malformed", func(t *testing.T) {
   348  		spec := randomCredSpec(t)
   349  		spec.Template = nil
   350  
   351  		_, _, err := rfc0593.ReplayProposal(agent(t), service.NewDIDCommMsgMap(&issuecredential.ProposeCredentialV2{
   352  			Type: issuecredential.ProposeCredentialMsgTypeV2,
   353  			Formats: []issuecredential.Format{{
   354  				AttachID: "123",
   355  				Format:   rfc0593.ProofVCDetailFormat,
   356  			}},
   357  			FiltersAttach: []decorator.Attachment{{
   358  				ID: "123",
   359  				Data: decorator.AttachmentData{
   360  					JSON: spec,
   361  				},
   362  			}},
   363  		}))
   364  		require.Error(t, err)
   365  		require.Contains(t, err.Error(), "unable to parse vc")
   366  	})
   367  }
   368  
   369  func TestReplayOffer(t *testing.T) {
   370  	t.Run("replays the offered spec as a request", func(t *testing.T) {
   371  		expected := &rfc0593.CredentialSpec{
   372  			Template: marshal(t, newVC(t)),
   373  			Options: &rfc0593.CredentialSpecOptions{
   374  				ProofPurpose: "assertionMethod",
   375  				Created:      time.Now().Format(time.RFC3339),
   376  				Domain:       uuid.New().String(),
   377  				Challenge:    uuid.New().String(),
   378  				ProofType:    "Ed25519Signature2018",
   379  				Status:       &rfc0593.CredentialStatus{Type: "test"},
   380  			},
   381  		}
   382  
   383  		arg, options, err := rfc0593.ReplayOffer(agent(t), service.NewDIDCommMsgMap(&issuecredential.OfferCredentialV2{
   384  			Type: issuecredential.OfferCredentialMsgTypeV2,
   385  			Formats: []issuecredential.Format{{
   386  				AttachID: "123",
   387  				Format:   rfc0593.ProofVCDetailFormat,
   388  			}},
   389  			OffersAttach: []decorator.Attachment{{
   390  				ID: "123",
   391  				Data: decorator.AttachmentData{
   392  					JSON: expected,
   393  				},
   394  			}},
   395  		}))
   396  		require.NoError(t, err)
   397  		require.Equal(t, expected.Options, options)
   398  
   399  		opt, ok := arg.(issuecredential.Opt)
   400  		require.True(t, ok)
   401  
   402  		md := &issuecredential.MetaData{}
   403  		opt(md)
   404  
   405  		require.NotEmpty(t, md.RequestCredentialV2())
   406  		require.Equal(t,
   407  			expected,
   408  			extractPayload(t,
   409  				rfc0593.ProofVCDetailFormat, md.RequestCredentialV2().Formats, md.RequestCredentialV2().RequestsAttach),
   410  		)
   411  	})
   412  
   413  	t.Run("error if VC is malformed", func(t *testing.T) {
   414  		spec := randomCredSpec(t)
   415  		spec.Template = nil
   416  
   417  		_, _, err := rfc0593.ReplayOffer(agent(t), service.NewDIDCommMsgMap(&issuecredential.OfferCredentialV2{
   418  			Type: issuecredential.OfferCredentialMsgTypeV2,
   419  			Formats: []issuecredential.Format{{
   420  				AttachID: "123",
   421  				Format:   rfc0593.ProofVCDetailFormat,
   422  			}},
   423  			OffersAttach: []decorator.Attachment{{
   424  				ID: "123",
   425  				Data: decorator.AttachmentData{
   426  					JSON: spec,
   427  				},
   428  			}},
   429  		}))
   430  		require.Error(t, err)
   431  		require.Contains(t, err.Error(), "unable to parse vc")
   432  	})
   433  }
   434  
   435  func TestIssueCredential(t *testing.T) {
   436  	t.Run("attaches LD proof and produces a request-credential message with the VC attached", func(t *testing.T) {
   437  		t.Run("Ed25519", func(t *testing.T) {
   438  			agent := agent(t)
   439  			unverifiedCredential := newVC(t)
   440  			spec := &rfc0593.CredentialSpec{
   441  				Template: marshal(t, unverifiedCredential),
   442  				Options: &rfc0593.CredentialSpecOptions{
   443  					ProofPurpose: "assertionMethod",
   444  					Created:      time.Now().Format(time.RFC3339),
   445  					Domain:       uuid.New().String(),
   446  					Challenge:    uuid.New().String(),
   447  					ProofType:    "Ed25519Signature2018",
   448  					Status:       &rfc0593.CredentialStatus{Type: "test"},
   449  				},
   450  			}
   451  
   452  			arg, options, err := rfc0593.IssueCredential(agent, service.NewDIDCommMsgMap(&issuecredential.RequestCredentialV2{
   453  				Type: issuecredential.RequestCredentialMsgTypeV2,
   454  				Formats: []issuecredential.Format{{
   455  					AttachID: "123",
   456  					Format:   rfc0593.ProofVCDetailFormat,
   457  				}},
   458  				RequestsAttach: []decorator.Attachment{{
   459  					ID: "123",
   460  					Data: decorator.AttachmentData{
   461  						JSON: spec,
   462  					},
   463  				}},
   464  			}))
   465  			require.NoError(t, err)
   466  			require.Equal(t, spec.Options, options)
   467  
   468  			opt, ok := arg.(issuecredential.Opt)
   469  			require.True(t, ok)
   470  
   471  			md := &issuecredential.MetaData{}
   472  			opt(md)
   473  
   474  			require.NotEmpty(t, md.IssueCredentialV2())
   475  			require.NotEmpty(t, md.IssueCredentialV2().CredentialsAttach)
   476  
   477  			raw, err := md.IssueCredentialV2().CredentialsAttach[0].Data.Fetch()
   478  			require.NoError(t, err)
   479  
   480  			verifiableCredential, err := verifiable.ParseCredential(
   481  				raw,
   482  				verifiable.WithPublicKeyFetcher(verifiable.NewVDRKeyResolver(agent.VDRegistry()).PublicKeyFetcher()),
   483  				verifiable.WithJSONLDDocumentLoader(agent.JSONLDDocumentLoader()),
   484  			)
   485  			require.NoError(t, err)
   486  
   487  			require.Equal(t, unverifiedCredential.ID, verifiableCredential.ID)
   488  			require.NotEmpty(t, verifiableCredential.Proofs)
   489  			require.Equal(t, spec.Options.Challenge, verifiableCredential.Proofs[0]["challenge"])
   490  			require.Equal(t, spec.Options.Domain, verifiableCredential.Proofs[0]["domain"])
   491  		})
   492  
   493  		t.Run("BbsBlsSignature2020", func(t *testing.T) {
   494  			agent := agent(t)
   495  			unverifiedCredential := newVC(t)
   496  
   497  			spec := &rfc0593.CredentialSpec{
   498  				Template: marshal(t, unverifiedCredential),
   499  				Options: &rfc0593.CredentialSpecOptions{
   500  					ProofPurpose: "assertionMethod",
   501  					Created:      time.Now().Format(time.RFC3339),
   502  					Domain:       uuid.New().String(),
   503  					Challenge:    uuid.New().String(),
   504  					ProofType:    "BbsBlsSignature2020",
   505  					Status:       &rfc0593.CredentialStatus{Type: "test"},
   506  				},
   507  			}
   508  
   509  			arg, options, err := rfc0593.IssueCredential(agent, service.NewDIDCommMsgMap(&issuecredential.RequestCredentialV2{
   510  				Type: issuecredential.RequestCredentialMsgTypeV2,
   511  				Formats: []issuecredential.Format{{
   512  					AttachID: "123",
   513  					Format:   rfc0593.ProofVCDetailFormat,
   514  				}},
   515  				RequestsAttach: []decorator.Attachment{{
   516  					ID: "123",
   517  					Data: decorator.AttachmentData{
   518  						JSON: spec,
   519  					},
   520  				}},
   521  			}))
   522  			require.NoError(t, err)
   523  			require.Equal(t, spec.Options, options)
   524  
   525  			opt, ok := arg.(issuecredential.Opt)
   526  			require.True(t, ok)
   527  
   528  			md := &issuecredential.MetaData{}
   529  			opt(md)
   530  
   531  			require.NotEmpty(t, md.IssueCredentialV2())
   532  			require.NotEmpty(t, md.IssueCredentialV2().CredentialsAttach)
   533  
   534  			raw, err := md.IssueCredentialV2().CredentialsAttach[0].Data.Fetch()
   535  			require.NoError(t, err)
   536  
   537  			verifiableCredential, err := verifiable.ParseCredential(
   538  				raw,
   539  				verifiable.WithPublicKeyFetcher(verifiable.NewVDRKeyResolver(agent.VDRegistry()).PublicKeyFetcher()),
   540  				verifiable.WithJSONLDDocumentLoader(agent.JSONLDDocumentLoader()),
   541  			)
   542  			require.NoError(t, err)
   543  
   544  			require.Equal(t, unverifiedCredential.ID, verifiableCredential.ID)
   545  			require.NotEmpty(t, verifiableCredential.Proofs)
   546  			require.Equal(t, spec.Options.Challenge, verifiableCredential.Proofs[0]["challenge"])
   547  			require.Equal(t, spec.Options.Domain, verifiableCredential.Proofs[0]["domain"])
   548  		})
   549  	})
   550  
   551  	t.Run("error if VC is malformed", func(t *testing.T) {
   552  		spec := randomCredSpec(t)
   553  		spec.Template = nil
   554  
   555  		_, _, err := rfc0593.IssueCredential(agent(t), service.NewDIDCommMsgMap(&issuecredential.RequestCredentialV2{
   556  			Type: issuecredential.RequestCredentialMsgTypeV2,
   557  			Formats: []issuecredential.Format{{
   558  				AttachID: "123",
   559  				Format:   rfc0593.ProofVCDetailFormat,
   560  			}},
   561  			RequestsAttach: []decorator.Attachment{{
   562  				ID: "123",
   563  				Data: decorator.AttachmentData{
   564  					JSON: spec,
   565  				},
   566  			}},
   567  		}))
   568  		require.Error(t, err)
   569  		require.Contains(t, err.Error(), "unable to parse vc")
   570  	})
   571  
   572  	t.Run("error on unsupported proofType", func(t *testing.T) {
   573  		spec := randomCredSpec(t)
   574  		spec.Options.ProofType = "UNSUPPORTED"
   575  
   576  		_, _, err := rfc0593.IssueCredential(agent(t), service.NewDIDCommMsgMap(&issuecredential.RequestCredentialV2{
   577  			Type: issuecredential.RequestCredentialMsgTypeV2,
   578  			Formats: []issuecredential.Format{{
   579  				AttachID: "123",
   580  				Format:   rfc0593.ProofVCDetailFormat,
   581  			}},
   582  			RequestsAttach: []decorator.Attachment{{
   583  				ID: "123",
   584  				Data: decorator.AttachmentData{
   585  					JSON: spec,
   586  				},
   587  			}},
   588  		}))
   589  		require.Error(t, err)
   590  		require.Contains(t, err.Error(), "unsupported proof type")
   591  	})
   592  
   593  	t.Run("error if cannot add LD proof", func(t *testing.T) {
   594  		expected := errors.New("test")
   595  		spec := randomCredSpec(t)
   596  		ctx := agent(t)
   597  		provider := &mockProvider{
   598  			loader: ctx.JSONLDDocumentLoader(),
   599  			km:     ctx.KMS(),
   600  			cr:     &mockcrypto.Crypto{SignErr: expected},
   601  			sp:     mem.NewProvider(),
   602  		}
   603  
   604  		_, _, err := rfc0593.IssueCredential(provider, service.NewDIDCommMsgMap(&issuecredential.RequestCredentialV2{
   605  			Type: issuecredential.RequestCredentialMsgTypeV2,
   606  			Formats: []issuecredential.Format{{
   607  				AttachID: "123",
   608  				Format:   rfc0593.ProofVCDetailFormat,
   609  			}},
   610  			RequestsAttach: []decorator.Attachment{{
   611  				ID: "123",
   612  				Data: decorator.AttachmentData{
   613  					JSON: spec,
   614  				},
   615  			}},
   616  		}))
   617  		require.ErrorIs(t, err, expected)
   618  	})
   619  }
   620  
   621  func TestVerifyCredential(t *testing.T) {
   622  	t.Run("verifies the credential", func(t *testing.T) {
   623  		agent := agent(t)
   624  		spec := randomCredSpec(t)
   625  		name := uuid.New().String()
   626  		attachID := uuid.New().String()
   627  		msg := service.NewDIDCommMsgMap(&issuecredential.IssueCredentialV2{
   628  			Type: issuecredential.IssueCredentialMsgTypeV2,
   629  			Formats: []issuecredential.Format{{
   630  				AttachID: attachID,
   631  				Format:   rfc0593.ProofVCFormat,
   632  			}},
   633  			CredentialsAttach: []decorator.Attachment{{
   634  				ID: attachID,
   635  				Data: decorator.AttachmentData{
   636  					JSON: newVCWithProof(t, agent, spec),
   637  				},
   638  			}},
   639  		})
   640  		msg.SetID(uuid.New().String())
   641  
   642  		arg, err := rfc0593.VerifyCredential(agent, spec.Options, name, msg)
   643  		require.NoError(t, err)
   644  
   645  		opt, ok := arg.(issuecredential.Opt)
   646  		require.True(t, ok)
   647  
   648  		md := &issuecredential.MetaData{}
   649  		opt(md)
   650  
   651  		require.Len(t, md.CredentialNames(), 1)
   652  		require.Equal(t, name, md.CredentialNames()[0])
   653  	})
   654  
   655  	t.Run("fails if credential has no proof", func(t *testing.T) {
   656  		// TODO - enable when ParseCredential is fixed: https://github.com/hyperledger/aries-framework-go/issues/2799
   657  		t.Skip()
   658  		agent := agent(t)
   659  		attachID := uuid.New().String()
   660  		msg := service.NewDIDCommMsgMap(&issuecredential.IssueCredentialV2{
   661  			Type: issuecredential.IssueCredentialMsgTypeV2,
   662  			Formats: []issuecredential.Format{{
   663  				AttachID: attachID,
   664  				Format:   rfc0593.ProofVCFormat,
   665  			}},
   666  			CredentialsAttach: []decorator.Attachment{{
   667  				ID: attachID,
   668  				Data: decorator.AttachmentData{
   669  					JSON: newVC(t),
   670  				},
   671  			}},
   672  		})
   673  		msg.SetID(uuid.New().String())
   674  
   675  		_, err := rfc0593.VerifyCredential(agent, nil, uuid.New().String(), msg)
   676  		require.NoError(t, err)
   677  	})
   678  }
   679  
   680  func marshal(t *testing.T, v interface{}) []byte {
   681  	t.Helper()
   682  
   683  	raw, err := json.Marshal(v)
   684  	require.NoError(t, err)
   685  
   686  	return raw
   687  }
   688  
   689  type options struct {
   690  	protoStateStorageProvider storage.Provider
   691  }
   692  
   693  type option func(*options)
   694  
   695  func withProtoStateStorageProvider(p storage.Provider) option {
   696  	return func(o *options) {
   697  		o.protoStateStorageProvider = p
   698  	}
   699  }
   700  
   701  func agent(t *testing.T, o ...option) rfc0593.Provider {
   702  	t.Helper()
   703  
   704  	opts := &options{
   705  		protoStateStorageProvider: mem.NewProvider(),
   706  	}
   707  
   708  	for i := range o {
   709  		o[i](opts)
   710  	}
   711  
   712  	a, err := aries.New(
   713  		aries.WithStoreProvider(mem.NewProvider()),
   714  		aries.WithProtocolStateStoreProvider(opts.protoStateStorageProvider),
   715  	)
   716  	require.NoError(t, err)
   717  
   718  	t.Cleanup(func() {
   719  		err = a.Close()
   720  		require.NoError(t, err)
   721  	})
   722  
   723  	ctx, err := a.Context()
   724  	require.NoError(t, err)
   725  
   726  	return ctx
   727  }
   728  
   729  func extractPayload(t *testing.T,
   730  	formatID string, formats []issuecredential.Format, attachments []decorator.Attachment) *rfc0593.CredentialSpec {
   731  	t.Helper()
   732  
   733  	var attachID string
   734  
   735  	for i := range formats {
   736  		if formats[i].Format == formatID {
   737  			attachID = formats[i].AttachID
   738  
   739  			break
   740  		}
   741  	}
   742  
   743  	require.NotEmpty(t, attachID)
   744  
   745  	var a *decorator.Attachment
   746  
   747  	for i := range attachments {
   748  		if attachments[i].ID == attachID {
   749  			a = &attachments[i]
   750  
   751  			break
   752  		}
   753  	}
   754  
   755  	require.NotNil(t, a)
   756  	require.NotZero(t, a.Data)
   757  
   758  	raw, err := a.Data.Fetch()
   759  	require.NoError(t, err)
   760  
   761  	spec := &rfc0593.CredentialSpec{}
   762  
   763  	err = json.Unmarshal(raw, spec)
   764  	require.NoError(t, err)
   765  
   766  	return spec
   767  }
   768  
   769  func newVC(t *testing.T) *verifiable.Credential {
   770  	t.Helper()
   771  
   772  	return &verifiable.Credential{
   773  		Context: []string{verifiable.ContextURI, "https://w3id.org/security/bbs/v1"},
   774  		Types:   []string{verifiable.VCType},
   775  		ID:      uuid.New().URN(),
   776  		Subject: verifiable.Subject{
   777  			ID: uuid.New().URN(),
   778  		},
   779  		Issuer: verifiable.Issuer{
   780  			ID: uuid.New().URN(),
   781  		},
   782  		Issued: util.NewTime(time.Now()),
   783  	}
   784  }
   785  
   786  func newVCWithProof(t *testing.T, agent rfc0593.Provider, spec *rfc0593.CredentialSpec) *verifiable.Credential {
   787  	t.Helper()
   788  
   789  	keyID, kh, err := agent.KMS().Create(kms.ED25519Type)
   790  	require.NoError(t, err)
   791  
   792  	keyBytes, kt, err := agent.KMS().ExportPubKeyBytes(keyID)
   793  	require.NoError(t, err)
   794  	require.Equal(t, kms.ED25519Type, kt)
   795  
   796  	_, verificationMethod := fingerprint.CreateDIDKeyByCode(fingerprint.ED25519PubKeyMultiCodec, keyBytes)
   797  
   798  	suiteSigner := suite.NewCryptoSigner(agent.Crypto(), kh)
   799  
   800  	vc, err := verifiable.ParseCredential(
   801  		spec.Template,
   802  		verifiable.WithDisabledProofCheck(),
   803  		verifiable.WithJSONLDDocumentLoader(agent.JSONLDDocumentLoader()),
   804  	)
   805  	require.NoError(t, err)
   806  
   807  	created, err := time.Parse(time.RFC3339, spec.Options.Created)
   808  	require.NoError(t, err)
   809  
   810  	err = vc.AddLinkedDataProof(&verifiable.LinkedDataProofContext{
   811  		SignatureType:           spec.Options.ProofType,
   812  		Suite:                   ed25519signature2018.New(suite.WithSigner(suiteSigner)),
   813  		SignatureRepresentation: verifiable.SignatureJWS,
   814  		Created:                 &created,
   815  		VerificationMethod:      verificationMethod,
   816  		Challenge:               spec.Options.Challenge,
   817  		Domain:                  spec.Options.Domain,
   818  		Purpose:                 spec.Options.ProofPurpose,
   819  	}, jsonld.WithDocumentLoader(agent.JSONLDDocumentLoader()))
   820  	require.NoError(t, err)
   821  
   822  	return vc
   823  }
   824  
   825  func randomCredSpec(t *testing.T) *rfc0593.CredentialSpec {
   826  	t.Helper()
   827  
   828  	return &rfc0593.CredentialSpec{
   829  		Template: marshal(t, newVC(t)),
   830  		Options: &rfc0593.CredentialSpecOptions{
   831  			ProofPurpose: "assertionMethod",
   832  			Created:      time.Now().Format(time.RFC3339),
   833  			Domain:       uuid.New().String(),
   834  			Challenge:    uuid.New().String(),
   835  			ProofType:    ed25519signature2018.SignatureType,
   836  		},
   837  	}
   838  }
   839  
   840  type mockProvider struct {
   841  	loader ld.DocumentLoader
   842  	km     kms.KeyManager
   843  	cr     crypto.Crypto
   844  	sp     storage.Provider
   845  	vdr    vdr.Registry
   846  }
   847  
   848  func (m *mockProvider) JSONLDDocumentLoader() ld.DocumentLoader {
   849  	return m.loader
   850  }
   851  
   852  func (m *mockProvider) KMS() kms.KeyManager {
   853  	return m.km
   854  }
   855  
   856  func (m *mockProvider) Crypto() crypto.Crypto {
   857  	return m.cr
   858  }
   859  
   860  func (m *mockProvider) ProtocolStateStorageProvider() storage.Provider {
   861  	return m.sp
   862  }
   863  
   864  func (m *mockProvider) VDRegistry() vdr.Registry {
   865  	return m.vdr
   866  }