github.com/hyperledger/aries-framework-go@v0.3.2/pkg/client/vcwallet/client_test.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package vcwallet
     8  
     9  import (
    10  	"crypto/sha256"
    11  	"encoding/base64"
    12  	"encoding/json"
    13  	"errors"
    14  	"fmt"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/google/uuid"
    20  	"github.com/stretchr/testify/require"
    21  
    22  	"github.com/hyperledger/aries-framework-go/internal/testdata"
    23  	"github.com/hyperledger/aries-framework-go/pkg/client/outofband"
    24  	"github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto"
    25  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/model"
    26  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
    27  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/didexchange"
    28  	issuecredentialsvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/issuecredential"
    29  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/mediator"
    30  	outofbandSvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/outofband"
    31  	oobv2 "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/outofbandv2"
    32  	presentproofSvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/presentproof"
    33  	"github.com/hyperledger/aries-framework-go/pkg/doc/did"
    34  	"github.com/hyperledger/aries-framework-go/pkg/doc/presexch"
    35  	"github.com/hyperledger/aries-framework-go/pkg/doc/util"
    36  	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
    37  	vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
    38  	mockoutofbandv2 "github.com/hyperledger/aries-framework-go/pkg/internal/gomocks/client/outofbandv2"
    39  	"github.com/hyperledger/aries-framework-go/pkg/internal/ldtestutil"
    40  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    41  	cryptomock "github.com/hyperledger/aries-framework-go/pkg/mock/crypto"
    42  	mockdidexchange "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/didexchange"
    43  	mockissuecredential "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/issuecredential"
    44  	mockmediator "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/mediator"
    45  	mockoutofband "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/outofband"
    46  	mockpresentproof "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/presentproof"
    47  	mockprovider "github.com/hyperledger/aries-framework-go/pkg/mock/provider"
    48  	"github.com/hyperledger/aries-framework-go/pkg/mock/secretlock"
    49  	mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage"
    50  	mockvdr "github.com/hyperledger/aries-framework-go/pkg/mock/vdr"
    51  	"github.com/hyperledger/aries-framework-go/pkg/secretlock/local/masterlock/pbkdf2"
    52  	"github.com/hyperledger/aries-framework-go/pkg/store/connection"
    53  	"github.com/hyperledger/aries-framework-go/pkg/vdr/key"
    54  	"github.com/hyperledger/aries-framework-go/pkg/wallet"
    55  	"github.com/hyperledger/aries-framework-go/spi/storage"
    56  )
    57  
    58  const (
    59  	samplePassPhrase        = "fakepassphrase"
    60  	sampleRemoteKMSAuth     = "sample-auth-token"
    61  	sampleKeyServerURL      = "sample/keyserver/test"
    62  	sampleUserID            = "sample-user01"
    63  	toBeImplementedErr      = "to be implemented"
    64  	sampleClientErr         = "sample client err"
    65  	sampleDIDKey            = "did:key:z6MknC1wwS6DEYwtGbZZo2QvjQjkh2qSBjb4GYmbye8dv4S5"
    66  	sampleDIDKey2           = "did:key:z6MkwFKUCsf8wvn6eSSu1WFAKatN1yexiDM7bf7pZLSFjdz6"
    67  	sampleInvalidDIDContent = `{"@context": ["https://w3id.org/did/v1"],"id": "did:example:sampleInvalidDIDContent"}`
    68  	webRedirectStatusKey    = "status"
    69  	webRedirectURLKey       = "url"
    70  	exampleWebRedirect      = "http://example.com/sample"
    71  	sampleMsgComment        = "sample mock msg"
    72  )
    73  
    74  func TestCreateProfile(t *testing.T) {
    75  	t.Run("test create new wallet client using local kms passphrase", func(t *testing.T) {
    76  		mockctx := newMockProvider(t)
    77  		err := CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
    78  		require.NoError(t, err)
    79  		require.NoError(t, ProfileExists(sampleUserID, mockctx))
    80  
    81  		vcWallet, err := New(sampleUserID, mockctx)
    82  		require.NoError(t, err)
    83  		require.NotEmpty(t, vcWallet)
    84  	})
    85  
    86  	t.Run("test create new wallet client using local kms secret lock service", func(t *testing.T) {
    87  		mockctx := newMockProvider(t)
    88  		err := CreateProfile(sampleUserID, mockctx, wallet.WithSecretLockService(&secretlock.MockSecretLock{}))
    89  		require.NoError(t, err)
    90  		require.NoError(t, ProfileExists(sampleUserID, mockctx))
    91  
    92  		vcWallet, err := New(sampleUserID, mockctx)
    93  		require.NoError(t, err)
    94  		require.NotEmpty(t, vcWallet)
    95  	})
    96  
    97  	t.Run("test create new wallet client using remote kms key server URL", func(t *testing.T) {
    98  		mockctx := newMockProvider(t)
    99  		err := CreateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   100  		require.NoError(t, err)
   101  		require.NoError(t, ProfileExists(sampleUserID, mockctx))
   102  
   103  		vcWallet, err := New(sampleUserID, mockctx)
   104  		require.NoError(t, err)
   105  		require.NotEmpty(t, vcWallet)
   106  	})
   107  
   108  	t.Run("test create new wallet failure", func(t *testing.T) {
   109  		mockctx := newMockProvider(t)
   110  		err := CreateProfile(sampleUserID, mockctx)
   111  		require.Error(t, err)
   112  		require.Contains(t, err.Error(), "invalid create profile options")
   113  		require.True(t, errors.Is(ProfileExists(sampleUserID, mockctx), wallet.ErrProfileNotFound))
   114  
   115  		vcWallet, err := New(sampleUserID, mockctx)
   116  		require.Error(t, err)
   117  		require.Empty(t, vcWallet)
   118  	})
   119  
   120  	t.Run("test create new wallet failure - create store error", func(t *testing.T) {
   121  		mockctx := newMockProvider(t)
   122  		mockctx.StorageProviderValue = &mockstorage.MockStoreProvider{
   123  			ErrOpenStoreHandle: fmt.Errorf(sampleClientErr),
   124  		}
   125  
   126  		err := CreateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   127  		require.Error(t, err)
   128  		require.Contains(t, err.Error(), sampleClientErr)
   129  
   130  		vcWallet, err := New(sampleUserID, mockctx)
   131  		require.Error(t, err)
   132  		require.Empty(t, vcWallet)
   133  
   134  		err = ProfileExists(sampleUserID, mockctx)
   135  		require.Error(t, err)
   136  		require.Contains(t, err.Error(), sampleClientErr)
   137  	})
   138  
   139  	t.Run("test create new wallet failure - save profile error", func(t *testing.T) {
   140  		mockctx := newMockProvider(t)
   141  		mockctx.StorageProviderValue = &mockstorage.MockStoreProvider{
   142  			Store: &mockstorage.MockStore{
   143  				ErrPut: fmt.Errorf(sampleClientErr),
   144  			},
   145  		}
   146  
   147  		err := CreateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   148  		require.Error(t, err)
   149  		require.Contains(t, err.Error(), sampleClientErr)
   150  
   151  		vcWallet, err := New(sampleUserID, mockctx)
   152  		require.Error(t, err)
   153  		require.Empty(t, vcWallet)
   154  	})
   155  
   156  	t.Run("test create new wallet failure - create content store error", func(t *testing.T) {
   157  		mockctx := newMockProvider(t)
   158  		mockctx.StorageProviderValue = &mockStorageProvider{
   159  			MockStoreProvider: mockstorage.NewMockStoreProvider(),
   160  			failure:           fmt.Errorf(sampleClientErr),
   161  		}
   162  
   163  		err := CreateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   164  		require.NoError(t, err)
   165  
   166  		vcWallet, err := New(sampleUserID, mockctx)
   167  		require.Error(t, err)
   168  		require.Contains(t, err.Error(), sampleClientErr)
   169  		require.Empty(t, vcWallet)
   170  	})
   171  }
   172  
   173  func TestUpdate(t *testing.T) {
   174  	t.Run("test update wallet client using local kms passphrase", func(t *testing.T) {
   175  		mockctx := newMockProvider(t)
   176  		createSampleProfile(t, mockctx)
   177  
   178  		err := UpdateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
   179  		require.NoError(t, err)
   180  
   181  		vcWallet, err := New(sampleUserID, mockctx)
   182  		require.NoError(t, err)
   183  		require.NotEmpty(t, vcWallet)
   184  	})
   185  
   186  	t.Run("test update wallet client using local kms secret lock service", func(t *testing.T) {
   187  		mockctx := newMockProvider(t)
   188  		createSampleProfile(t, mockctx)
   189  
   190  		err := UpdateProfile(sampleUserID, mockctx, wallet.WithSecretLockService(&secretlock.MockSecretLock{}))
   191  		require.NoError(t, err)
   192  
   193  		vcWallet, err := New(sampleUserID, mockctx)
   194  		require.NoError(t, err)
   195  		require.NotEmpty(t, vcWallet)
   196  	})
   197  
   198  	t.Run("test update wallet client using remote kms key server URL", func(t *testing.T) {
   199  		mockctx := newMockProvider(t)
   200  		createSampleProfile(t, mockctx)
   201  
   202  		err := UpdateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   203  		require.NoError(t, err)
   204  
   205  		vcWallet, err := New(sampleUserID, mockctx)
   206  		require.NoError(t, err)
   207  		require.NotEmpty(t, vcWallet)
   208  	})
   209  
   210  	t.Run("test update wallet failure", func(t *testing.T) {
   211  		mockctx := newMockProvider(t)
   212  		createSampleProfile(t, mockctx)
   213  
   214  		err := UpdateProfile(sampleUserID, mockctx)
   215  		require.Error(t, err)
   216  		require.Contains(t, err.Error(), "invalid create profile options")
   217  
   218  		vcWallet, err := New(sampleUserID, mockctx)
   219  		require.NoError(t, err)
   220  		require.NotEmpty(t, vcWallet)
   221  	})
   222  
   223  	t.Run("test update wallet failure - profile doesn't exists", func(t *testing.T) {
   224  		mockctx := newMockProvider(t)
   225  		err := UpdateProfile(sampleUserID, mockctx)
   226  		require.Error(t, err)
   227  		require.Contains(t, err.Error(), "profile does not exist")
   228  
   229  		vcWallet, err := New(sampleUserID, mockctx)
   230  		require.Error(t, err)
   231  		require.Empty(t, vcWallet)
   232  	})
   233  
   234  	t.Run("test update wallet failure - create store error", func(t *testing.T) {
   235  		mockctx := newMockProvider(t)
   236  		mockctx.StorageProviderValue = &mockstorage.MockStoreProvider{
   237  			ErrOpenStoreHandle: fmt.Errorf(sampleClientErr),
   238  		}
   239  
   240  		err := UpdateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   241  		require.Error(t, err)
   242  		require.Contains(t, err.Error(), sampleClientErr)
   243  
   244  		vcWallet, err := New(sampleUserID, mockctx)
   245  		require.Error(t, err)
   246  		require.Empty(t, vcWallet)
   247  	})
   248  
   249  	t.Run("test update wallet failure - save profile error", func(t *testing.T) {
   250  		mockctx := newMockProvider(t)
   251  		createSampleProfile(t, mockctx)
   252  
   253  		mockctx.StorageProviderValue.(*mockstorage.MockStoreProvider).Store.ErrPut = fmt.Errorf(sampleClientErr)
   254  
   255  		err := UpdateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   256  		require.Error(t, err)
   257  		require.Contains(t, err.Error(), sampleClientErr)
   258  
   259  		vcWallet, err := New(sampleUserID, mockctx)
   260  		require.NoError(t, err)
   261  		require.NotEmpty(t, vcWallet)
   262  	})
   263  }
   264  
   265  func TestNew(t *testing.T) {
   266  	t.Run("test get client", func(t *testing.T) {
   267  		mockctx := newMockProvider(t)
   268  		// create a wallet
   269  		err := CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
   270  		require.NoError(t, err)
   271  
   272  		vcWallet, err := New(sampleUserID, mockctx)
   273  		require.NoError(t, err)
   274  		require.NotEmpty(t, vcWallet)
   275  	})
   276  
   277  	t.Run("test get client unlocked", func(t *testing.T) {
   278  		mockctx := newMockProvider(t)
   279  		// create a wallet
   280  		err := CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
   281  		require.NoError(t, err)
   282  
   283  		vcWallet, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
   284  		require.NoError(t, err)
   285  		require.NotEmpty(t, vcWallet)
   286  
   287  		token, err := vcWallet.auth()
   288  		require.NoError(t, err)
   289  		require.NotEmpty(t, token)
   290  	})
   291  
   292  	t.Run("test get client unlock failure - wrong passphrase", func(t *testing.T) {
   293  		mockctx := newMockProvider(t)
   294  		// create a wallet
   295  		err := CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
   296  		require.NoError(t, err)
   297  
   298  		vcWallet, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase+"ss"))
   299  		require.Error(t, err)
   300  		require.Contains(t, err.Error(), "message authentication failed")
   301  		require.Empty(t, vcWallet)
   302  	})
   303  
   304  	t.Run("test get client by invalid userID", func(t *testing.T) {
   305  		mockctx := newMockProvider(t)
   306  		err := CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
   307  		require.NoError(t, err)
   308  
   309  		vcWallet, err := New(sampleUserID+"invalid", mockctx)
   310  		require.Empty(t, vcWallet)
   311  		require.Error(t, err)
   312  		require.Contains(t, err.Error(), "profile does not exist")
   313  	})
   314  
   315  	t.Run("test update wallet failure - save profile error", func(t *testing.T) {
   316  		mockctx := newMockProvider(t)
   317  		mockctx.StorageProviderValue = &mockstorage.MockStoreProvider{
   318  			ErrOpenStoreHandle: fmt.Errorf(sampleClientErr),
   319  		}
   320  
   321  		vcWallet, err := New(sampleUserID, mockctx)
   322  		require.Error(t, err)
   323  		require.Empty(t, vcWallet)
   324  		require.Contains(t, err.Error(), sampleClientErr)
   325  	})
   326  }
   327  
   328  func TestClient_OpenClose(t *testing.T) {
   329  	t.Run("test open & close wallet using local kms passphrase", func(t *testing.T) {
   330  		sampleUser := uuid.New().String()
   331  		mockctx := newMockProvider(t)
   332  
   333  		err := CreateProfile(sampleUser, mockctx, wallet.WithPassphrase(samplePassPhrase))
   334  		require.NoError(t, err)
   335  
   336  		vcWallet, err := New(sampleUser, mockctx)
   337  		require.NoError(t, err)
   338  		require.NotEmpty(t, vcWallet)
   339  
   340  		// get token
   341  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
   342  		require.NoError(t, err)
   343  		token, err := vcWallet.auth()
   344  		require.NoError(t, err)
   345  		require.NotEmpty(t, token)
   346  
   347  		defer vcWallet.Close()
   348  
   349  		// try again
   350  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
   351  		require.Error(t, err)
   352  		require.True(t, errors.Is(err, wallet.ErrAlreadyUnlocked))
   353  		token, err = vcWallet.auth()
   354  		require.NoError(t, err)
   355  		require.NotEmpty(t, token)
   356  
   357  		// close wallet
   358  		require.True(t, vcWallet.Close())
   359  		require.False(t, vcWallet.Close())
   360  
   361  		// try to open with wrong passphrase
   362  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase + "wrong"))
   363  		require.Error(t, err)
   364  		require.Contains(t, err.Error(), "message authentication failed")
   365  		token, err = vcWallet.auth()
   366  		require.Empty(t, token)
   367  		require.Error(t, err)
   368  		require.True(t, errors.Is(err, ErrWalletLocked))
   369  	})
   370  
   371  	t.Run("test open & close wallet using secret lock service", func(t *testing.T) {
   372  		sampleUser := uuid.New().String()
   373  		mockctx := newMockProvider(t)
   374  
   375  		masterLock, err := pbkdf2.NewMasterLock(samplePassPhrase, sha256.New, 0, nil)
   376  		require.NoError(t, err)
   377  
   378  		err = CreateProfile(sampleUser, mockctx, wallet.WithSecretLockService(masterLock))
   379  		require.NoError(t, err)
   380  
   381  		vcWallet, err := New(sampleUser, mockctx)
   382  		require.NoError(t, err)
   383  		require.NotEmpty(t, vcWallet)
   384  
   385  		// get token
   386  		err = vcWallet.Open(wallet.WithUnlockBySecretLockService(masterLock))
   387  		require.NoError(t, err)
   388  		token, err := vcWallet.auth()
   389  		require.NoError(t, err)
   390  		require.NotEmpty(t, token)
   391  
   392  		defer vcWallet.Close()
   393  
   394  		// try again
   395  		err = vcWallet.Open(wallet.WithUnlockBySecretLockService(masterLock))
   396  		require.Error(t, err)
   397  		require.True(t, errors.Is(err, wallet.ErrAlreadyUnlocked))
   398  		token, err = vcWallet.auth()
   399  		require.NoError(t, err)
   400  		require.NotEmpty(t, token)
   401  
   402  		// close wallet
   403  		require.True(t, vcWallet.Close())
   404  		require.False(t, vcWallet.Close())
   405  
   406  		// try to open with wrong secret lock service
   407  		badLock, err := pbkdf2.NewMasterLock(samplePassPhrase+"wrong", sha256.New, 0, nil)
   408  		require.NoError(t, err)
   409  
   410  		err = vcWallet.Open(wallet.WithUnlockBySecretLockService(badLock))
   411  		require.Error(t, err)
   412  		require.Contains(t, err.Error(), "message authentication failed")
   413  		token, err = vcWallet.auth()
   414  		require.Empty(t, token)
   415  		require.Error(t, err)
   416  		require.True(t, errors.Is(err, ErrWalletLocked))
   417  	})
   418  
   419  	t.Run("test open & close wallet using remote kms URL", func(t *testing.T) {
   420  		sampleUser := uuid.New().String()
   421  		mockctx := newMockProvider(t)
   422  
   423  		err := CreateProfile(sampleUser, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   424  		require.NoError(t, err)
   425  
   426  		vcWallet, err := New(sampleUser, mockctx)
   427  		require.NoError(t, err)
   428  		require.NotEmpty(t, vcWallet)
   429  
   430  		// get token
   431  		err = vcWallet.Open(wallet.WithUnlockByAuthorizationToken(sampleRemoteKMSAuth))
   432  		require.NoError(t, err)
   433  		token, err := vcWallet.auth()
   434  		require.NoError(t, err)
   435  		require.NotEmpty(t, token)
   436  
   437  		defer vcWallet.Close()
   438  
   439  		// try again
   440  		err = vcWallet.Open(wallet.WithUnlockByAuthorizationToken(sampleRemoteKMSAuth))
   441  		require.Error(t, err)
   442  		require.True(t, errors.Is(err, wallet.ErrAlreadyUnlocked))
   443  		token, err = vcWallet.auth()
   444  		require.NoError(t, err)
   445  		require.NotEmpty(t, token)
   446  
   447  		// close wallet
   448  		require.True(t, vcWallet.Close())
   449  		require.False(t, vcWallet.Close())
   450  		token, err = vcWallet.auth()
   451  		require.Empty(t, token)
   452  		require.Error(t, err)
   453  		require.True(t, errors.Is(err, ErrWalletLocked))
   454  	})
   455  }
   456  
   457  func TestClient_Export(t *testing.T) {
   458  	mockctx := newMockProvider(t)
   459  	err := CreateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   460  	require.NoError(t, err)
   461  
   462  	vcWalletClient, err := New(sampleUserID, mockctx)
   463  	require.NotEmpty(t, vcWalletClient)
   464  	require.NoError(t, err)
   465  
   466  	result, err := vcWalletClient.Export("")
   467  	require.Empty(t, result)
   468  	require.Error(t, err)
   469  	require.EqualError(t, err, toBeImplementedErr)
   470  }
   471  
   472  func TestClient_Import(t *testing.T) {
   473  	mockctx := newMockProvider(t)
   474  	err := CreateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   475  	require.NoError(t, err)
   476  
   477  	vcWalletClient, err := New(sampleUserID, mockctx)
   478  	require.NotEmpty(t, vcWalletClient)
   479  	require.NoError(t, err)
   480  
   481  	err = vcWalletClient.Import("", nil)
   482  	require.Error(t, err)
   483  	require.EqualError(t, err, toBeImplementedErr)
   484  }
   485  
   486  func TestClient_Add(t *testing.T) {
   487  	mockctx := newMockProvider(t)
   488  	err := CreateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   489  	require.NoError(t, err)
   490  
   491  	vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
   492  	require.NotEmpty(t, vcWalletClient)
   493  	require.NoError(t, err)
   494  
   495  	err = vcWalletClient.Add(wallet.Metadata, testdata.SampleWalletContentMetadata)
   496  	require.NoError(t, err)
   497  
   498  	// try locked wallet
   499  	vcWalletClient, err = New(sampleUserID, mockctx)
   500  	require.NotEmpty(t, vcWalletClient)
   501  	require.NoError(t, err)
   502  
   503  	err = vcWalletClient.Add(wallet.Metadata, testdata.SampleWalletContentMetadata)
   504  	require.Contains(t, err.Error(), "wallet locked")
   505  }
   506  
   507  func TestClient_Get(t *testing.T) {
   508  	mockctx := newMockProvider(t)
   509  	err := CreateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   510  	require.NoError(t, err)
   511  
   512  	vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
   513  	require.NotEmpty(t, vcWalletClient)
   514  	require.NoError(t, err)
   515  
   516  	err = vcWalletClient.Add(wallet.Metadata, testdata.SampleWalletContentMetadata)
   517  	require.NoError(t, err)
   518  
   519  	content, err := vcWalletClient.Get(wallet.Metadata, "did:example:123456789abcdefghi")
   520  	require.NoError(t, err)
   521  	require.NotEmpty(t, content)
   522  	require.Equal(t, string(testdata.SampleWalletContentMetadata), string(content))
   523  
   524  	// try locked wallet
   525  	require.True(t, vcWalletClient.Close())
   526  	content, err = vcWalletClient.Get(wallet.Metadata, "did:example:123456789abcdefghi")
   527  	require.True(t, errors.Is(err, ErrWalletLocked))
   528  	require.Empty(t, content)
   529  }
   530  
   531  func TestClient_GetAll(t *testing.T) {
   532  	const vcContent = `{
   533        "@context": [
   534          "https://www.w3.org/2018/credentials/v1",
   535          "https://www.w3.org/2018/credentials/examples/v1"
   536        ],
   537        "id": "%s",
   538        "issuer": {
   539          "id": "did:example:76e12ec712ebc6f1c221ebfeb1f"
   540        },
   541        "type": [
   542          "VerifiableCredential",
   543          "UniversityDegreeCredential"
   544        ]
   545      }`
   546  
   547  	const orgCollection = `{
   548                      "@context": ["https://w3id.org/wallet/v1"],
   549                      "id": "did:example:acme123456789abcdefghi",
   550                      "type": "Organization",
   551                      "name": "Acme Corp.",
   552                      "image": "https://via.placeholder.com/150",
   553                      "description" : "A software company.",
   554                      "tags": ["professional", "organization"],
   555                      "correlation": ["4058a72a-9523-11ea-bb37-0242ac130002"]
   556                  }`
   557  
   558  	const collectionID = "did:example:acme123456789abcdefghi"
   559  
   560  	mockctx := newMockProvider(t)
   561  	err := CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
   562  	require.NoError(t, err)
   563  
   564  	vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
   565  	require.NotEmpty(t, vcWalletClient)
   566  	require.NoError(t, err)
   567  
   568  	// save test data
   569  	const count = 5
   570  
   571  	for i := 0; i < count; i++ {
   572  		require.NoError(t, vcWalletClient.Add(wallet.Credential, []byte(fmt.Sprintf(vcContent, uuid.New().String()))))
   573  	}
   574  
   575  	// save a collection
   576  	require.NoError(t, vcWalletClient.Add(wallet.Collection, []byte(orgCollection)))
   577  
   578  	// save contents by collection
   579  	for i := 0; i < count; i++ {
   580  		require.NoError(t, vcWalletClient.Add(wallet.Credential,
   581  			[]byte(fmt.Sprintf(vcContent, uuid.New().String())), wallet.AddByCollection(collectionID)))
   582  	}
   583  
   584  	// get all by content
   585  	vcs, err := vcWalletClient.GetAll(wallet.Credential)
   586  	require.NoError(t, err)
   587  	require.Len(t, vcs, count*2)
   588  
   589  	// get all by content & collection
   590  	vcs, err = vcWalletClient.GetAll(wallet.Credential, wallet.FilterByCollection(collectionID))
   591  	require.NoError(t, err)
   592  	require.Len(t, vcs, count)
   593  
   594  	// try locked wallet
   595  	require.True(t, vcWalletClient.Close())
   596  	vcs, err = vcWalletClient.GetAll(wallet.Credential, wallet.FilterByCollection(collectionID))
   597  	require.True(t, errors.Is(err, ErrWalletLocked))
   598  	require.Empty(t, vcs)
   599  }
   600  
   601  func TestClient_Remove(t *testing.T) {
   602  	mockctx := newMockProvider(t)
   603  	err := CreateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
   604  	require.NoError(t, err)
   605  
   606  	vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
   607  	require.NotEmpty(t, vcWalletClient)
   608  	require.NoError(t, err)
   609  
   610  	err = vcWalletClient.Add(wallet.Metadata, testdata.SampleWalletContentMetadata)
   611  	require.NoError(t, err)
   612  
   613  	content, err := vcWalletClient.Get(wallet.Metadata, "did:example:123456789abcdefghi")
   614  	require.NoError(t, err)
   615  	require.NotEmpty(t, content)
   616  
   617  	err = vcWalletClient.Remove(wallet.Metadata, "did:example:123456789abcdefghi")
   618  	require.NoError(t, err)
   619  
   620  	content, err = vcWalletClient.Get(wallet.Metadata, "did:example:123456789abcdefghi")
   621  	require.Empty(t, content)
   622  	require.Error(t, err)
   623  	require.True(t, errors.Is(err, storage.ErrDataNotFound))
   624  
   625  	// try locked wallet
   626  	require.True(t, vcWalletClient.Close())
   627  	err = vcWalletClient.Remove(wallet.Metadata, "did:example:123456789abcdefghi")
   628  	require.True(t, errors.Is(err, ErrWalletLocked))
   629  }
   630  
   631  func TestClient_Query(t *testing.T) {
   632  	customVDR := &mockvdr.MockVDRegistry{
   633  		ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
   634  			if strings.HasPrefix(didID, "did:key:") {
   635  				k := key.New()
   636  
   637  				d, e := k.Read(didID)
   638  				if e != nil {
   639  					return nil, e
   640  				}
   641  
   642  				return d, nil
   643  			}
   644  
   645  			return nil, fmt.Errorf("did not found")
   646  		},
   647  	}
   648  
   649  	mockctx := newMockProvider(t)
   650  	mockctx.VDRegistryValue = customVDR
   651  	mockctx.CryptoValue = &cryptomock.Crypto{}
   652  
   653  	vc1, err := (&verifiable.Credential{
   654  		Context: []string{verifiable.ContextURI},
   655  		Types:   []string{verifiable.VCType},
   656  		ID:      "http://example.edu/credentials/9999",
   657  		CustomFields: map[string]interface{}{
   658  			"first_name": "Jesse",
   659  		},
   660  		Issued: &util.TimeWrapper{
   661  			Time: time.Now(),
   662  		},
   663  		Issuer: verifiable.Issuer{
   664  			ID: "did:example:76e12ec712ebc6f1c221ebfeb1f",
   665  		},
   666  		Subject: uuid.New().String(),
   667  	}).MarshalJSON()
   668  	require.NoError(t, err)
   669  
   670  	sampleVC := strings.ReplaceAll(string(testdata.SampleUDCVCWithCredentialSchema),
   671  		"https://example.com/schema", verifiable.ContextURI)
   672  	vcForQuery := []byte(strings.ReplaceAll(sampleVC,
   673  		"http://example.edu/credentials/1872", "http://example.edu/credentials/1879"))
   674  	vcForDerive := testdata.SampleUDCVCWithProofBBS
   675  
   676  	err = CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
   677  	require.NoError(t, err)
   678  
   679  	vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
   680  	require.NotEmpty(t, vcWalletClient)
   681  	require.NoError(t, err)
   682  
   683  	require.NoError(t, vcWalletClient.Add(wallet.Credential, vc1))
   684  	require.NoError(t, vcWalletClient.Add(wallet.Credential, vcForQuery))
   685  	require.NoError(t, vcWalletClient.Add(wallet.Credential, vcForDerive))
   686  
   687  	pd := &presexch.PresentationDefinition{
   688  		ID: uuid.New().String(),
   689  		InputDescriptors: []*presexch.InputDescriptor{{
   690  			ID: uuid.New().String(),
   691  			Schema: []*presexch.Schema{{
   692  				URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
   693  			}},
   694  			Constraints: &presexch.Constraints{
   695  				Fields: []*presexch.Field{{
   696  					Path: []string{"$.first_name"},
   697  				}},
   698  			},
   699  		}},
   700  	}
   701  
   702  	// presentation exchange
   703  	pdJSON, err := json.Marshal(pd)
   704  	require.NoError(t, err)
   705  	require.NotEmpty(t, pdJSON)
   706  
   707  	// query by example
   708  	queryByExample := []byte(strings.ReplaceAll(string(testdata.SampleWalletQueryByExample),
   709  		"did:example:abcd", verifiable.ContextURI))
   710  	// query by frame
   711  	queryByFrame := testdata.SampleWalletQueryByFrame
   712  
   713  	t.Run("test wallet queries", func(t *testing.T) {
   714  		tests := []struct {
   715  			name        string
   716  			params      []*wallet.QueryParams
   717  			resultCount int
   718  			vcCount     map[int]int
   719  			error       string
   720  		}{
   721  			{
   722  				name: "query by presentation exchange - success",
   723  				params: []*wallet.QueryParams{
   724  					{Type: "PresentationExchange", Query: []json.RawMessage{pdJSON}},
   725  				},
   726  				resultCount: 1,
   727  				vcCount:     map[int]int{0: 1},
   728  			},
   729  			{
   730  				name: "query by example - success",
   731  				params: []*wallet.QueryParams{
   732  					{Type: "QueryByExample", Query: []json.RawMessage{queryByExample}},
   733  				},
   734  				resultCount: 1,
   735  				vcCount:     map[int]int{0: 1},
   736  			},
   737  			{
   738  				name: "query by frame - success",
   739  				params: []*wallet.QueryParams{
   740  					{Type: "QueryByFrame", Query: []json.RawMessage{queryByFrame}},
   741  				},
   742  				resultCount: 1,
   743  				vcCount:     map[int]int{0: 1},
   744  			},
   745  			{
   746  				name: "did authorization - success",
   747  				params: []*wallet.QueryParams{
   748  					{Type: "DIDAuth"},
   749  				},
   750  				resultCount: 1,
   751  				vcCount:     map[int]int{0: 0},
   752  			},
   753  			{
   754  				name: "multiple queries - success",
   755  				params: []*wallet.QueryParams{
   756  					{Type: "PresentationExchange", Query: []json.RawMessage{pdJSON}},
   757  					{Type: "QueryByExample", Query: []json.RawMessage{queryByExample}},
   758  					{Type: "QueryByFrame", Query: []json.RawMessage{queryByFrame}},
   759  				},
   760  				resultCount: 2,
   761  				vcCount:     map[int]int{0: 1, 1: 2},
   762  			},
   763  			{
   764  				name: "invalid query type",
   765  				params: []*wallet.QueryParams{
   766  					{Type: "invalid"},
   767  				},
   768  				error: "unsupported query type",
   769  			},
   770  			{
   771  				name:   "empty query type",
   772  				params: []*wallet.QueryParams{},
   773  				error:  "no result found",
   774  			},
   775  		}
   776  
   777  		t.Parallel()
   778  
   779  		for _, test := range tests {
   780  			tc := test
   781  			t.Run(tc.name, func(t *testing.T) {
   782  				results, err := vcWalletClient.Query(tc.params...)
   783  
   784  				if tc.error != "" {
   785  					require.Empty(t, results)
   786  					require.Error(t, err)
   787  					require.Contains(t, err.Error(), tc.error)
   788  
   789  					return
   790  				}
   791  
   792  				require.NoError(t, err)
   793  				require.Len(t, results, tc.resultCount)
   794  
   795  				for i, result := range results {
   796  					require.Len(t, result.Credentials(), tc.vcCount[i])
   797  				}
   798  			})
   799  		}
   800  
   801  		// test wallet locked
   802  		require.True(t, vcWalletClient.Close())
   803  
   804  		results, err := vcWalletClient.Query(&wallet.QueryParams{
   805  			Type: "DIDAuth",
   806  		})
   807  		require.True(t, errors.Is(err, ErrWalletLocked))
   808  		require.Empty(t, results)
   809  	})
   810  }
   811  
   812  func TestClient_Issue(t *testing.T) {
   813  	customVDR := &mockvdr.MockVDRegistry{
   814  		ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
   815  			if strings.HasPrefix(didID, "did:key:") {
   816  				k := key.New()
   817  
   818  				d, e := k.Read(didID)
   819  				if e != nil {
   820  					return nil, e
   821  				}
   822  
   823  				return d, nil
   824  			}
   825  
   826  			return nil, fmt.Errorf("did not found")
   827  		},
   828  	}
   829  
   830  	mockctx := newMockProvider(t)
   831  	mockctx.VDRegistryValue = customVDR
   832  	mockctx.CryptoValue = &cryptomock.Crypto{}
   833  
   834  	err := CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
   835  	require.NoError(t, err)
   836  
   837  	t.Run("Test VC wallet client issue using controller - success", func(t *testing.T) {
   838  		vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
   839  		require.NotEmpty(t, vcWalletClient)
   840  		require.NoError(t, err)
   841  
   842  		defer vcWalletClient.Close()
   843  
   844  		// save a DID & corresponding key
   845  		require.NoError(t, vcWalletClient.Add(wallet.Key, testdata.SampleWalletContentKeyBase58))
   846  		require.NoError(t, vcWalletClient.Add(wallet.DIDResolutionResponse, testdata.SampleDocResolutionResponse))
   847  
   848  		result, err := vcWalletClient.Issue(testdata.SampleUDCVC, &wallet.ProofOptions{
   849  			Controller: sampleDIDKey,
   850  		})
   851  
   852  		require.NoError(t, err)
   853  		require.NotEmpty(t, result)
   854  		require.NotEmpty(t, result.Proofs)
   855  	})
   856  
   857  	t.Run("Test VC wallet client issue using controller - failure", func(t *testing.T) {
   858  		vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
   859  		require.NotEmpty(t, vcWalletClient)
   860  		require.NoError(t, err)
   861  
   862  		defer vcWalletClient.Close()
   863  
   864  		// sign with just controller
   865  		result, err := vcWalletClient.Issue(testdata.SampleUDCVC, &wallet.ProofOptions{
   866  			Controller: sampleDIDKey2,
   867  		})
   868  		require.Error(t, err)
   869  		require.Contains(t, err.Error(), "failed to read json keyset from reader")
   870  		require.Empty(t, result)
   871  	})
   872  
   873  	t.Run("Test VC wallet client issue using controller - wallet locked", func(t *testing.T) {
   874  		vcWalletClient, err := New(sampleUserID, mockctx)
   875  		require.NotEmpty(t, vcWalletClient)
   876  		require.NoError(t, err)
   877  
   878  		defer vcWalletClient.Close()
   879  
   880  		// sign with just controller
   881  		result, err := vcWalletClient.Issue(testdata.SampleUDCVC, &wallet.ProofOptions{
   882  			Controller: sampleDIDKey,
   883  		})
   884  		require.Error(t, err)
   885  		require.True(t, errors.Is(err, ErrWalletLocked))
   886  		require.Empty(t, result)
   887  	})
   888  }
   889  
   890  func TestClient_Prove(t *testing.T) {
   891  	customVDR := &mockvdr.MockVDRegistry{
   892  		ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
   893  			if strings.HasPrefix(didID, "did:key:") {
   894  				k := key.New()
   895  
   896  				d, e := k.Read(didID)
   897  				if e != nil {
   898  					return nil, e
   899  				}
   900  
   901  				return d, nil
   902  			}
   903  
   904  			return nil, fmt.Errorf("did not found")
   905  		},
   906  	}
   907  
   908  	mockctx := newMockProvider(t)
   909  	mockctx.VDRegistryValue = customVDR
   910  	mockctx.CryptoValue = &cryptomock.Crypto{}
   911  
   912  	err := CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
   913  	require.NoError(t, err)
   914  
   915  	t.Run("Test VC wallet client prove using controller - success", func(t *testing.T) {
   916  		vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
   917  		require.NotEmpty(t, vcWalletClient)
   918  		require.NoError(t, err)
   919  
   920  		defer vcWalletClient.Close()
   921  
   922  		// save a credential, DID & key
   923  		require.NoError(t, vcWalletClient.Add(wallet.Credential, testdata.SampleUDCVC))
   924  		require.NoError(t, vcWalletClient.Add(wallet.Key, testdata.SampleWalletContentKeyBase58))
   925  		require.NoError(t, vcWalletClient.Add(wallet.DIDResolutionResponse, testdata.SampleDocResolutionResponse))
   926  
   927  		result, err := vcWalletClient.Prove(&wallet.ProofOptions{Controller: sampleDIDKey},
   928  			wallet.WithStoredCredentialsToProve("http://example.edu/credentials/1872"),
   929  			wallet.WithRawCredentialsToProve(testdata.SampleUDCVC),
   930  		)
   931  		require.NoError(t, err)
   932  		require.NotEmpty(t, result)
   933  		require.NotEmpty(t, result.Proofs)
   934  	})
   935  
   936  	t.Run("Test VC wallet client prove using controller - failure", func(t *testing.T) {
   937  		vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
   938  		require.NotEmpty(t, vcWalletClient)
   939  		require.NoError(t, err)
   940  
   941  		defer vcWalletClient.Close()
   942  
   943  		require.NoError(t, vcWalletClient.Remove(wallet.Credential, "http://example.edu/credentials/1872"))
   944  		require.NoError(t, vcWalletClient.Add(wallet.Credential, testdata.SampleUDCVC))
   945  
   946  		result, err := vcWalletClient.Prove(&wallet.ProofOptions{Controller: sampleDIDKey2},
   947  			wallet.WithStoredCredentialsToProve("http://example.edu/credentials/1872"),
   948  			wallet.WithRawCredentialsToProve(testdata.SampleUDCVC),
   949  		)
   950  		require.Error(t, err)
   951  		require.Contains(t, err.Error(), "failed to read json keyset from reader")
   952  		require.Empty(t, result)
   953  	})
   954  
   955  	t.Run("Test VC wallet client prove using controller - wallet locked", func(t *testing.T) {
   956  		vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
   957  		require.NotEmpty(t, vcWalletClient)
   958  		require.NoError(t, err)
   959  
   960  		defer vcWalletClient.Close()
   961  
   962  		require.NoError(t, vcWalletClient.Remove(wallet.Credential, "http://example.edu/credentials/1872"))
   963  		require.NoError(t, vcWalletClient.Add(wallet.Credential, testdata.SampleUDCVC))
   964  
   965  		vcWalletClient.Close()
   966  
   967  		result, err := vcWalletClient.Prove(&wallet.ProofOptions{Controller: sampleDIDKey},
   968  			wallet.WithStoredCredentialsToProve("http://example.edu/credentials/1872"),
   969  			wallet.WithRawCredentialsToProve(testdata.SampleUDCVC),
   970  		)
   971  		require.Error(t, err)
   972  		require.True(t, errors.Is(err, ErrWalletLocked))
   973  		require.Empty(t, result)
   974  	})
   975  }
   976  
   977  func TestClient_Verify(t *testing.T) {
   978  	customVDR := &mockvdr.MockVDRegistry{
   979  		ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
   980  			if strings.HasPrefix(didID, "did:key:") {
   981  				k := key.New()
   982  
   983  				d, e := k.Read(didID)
   984  				if e != nil {
   985  					return nil, e
   986  				}
   987  
   988  				return d, nil
   989  			}
   990  
   991  			return nil, fmt.Errorf("did not found")
   992  		},
   993  	}
   994  
   995  	mockctx := newMockProvider(t)
   996  	mockctx.VDRegistryValue = customVDR
   997  	mockctx.CryptoValue = &cryptomock.Crypto{}
   998  
   999  	err := CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
  1000  	require.NoError(t, err)
  1001  
  1002  	t.Run("Test VC wallet verify credential - success", func(t *testing.T) {
  1003  		vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
  1004  		require.NotEmpty(t, vcWalletClient)
  1005  		require.NoError(t, err)
  1006  
  1007  		defer vcWalletClient.Close()
  1008  
  1009  		// store credential in wallet
  1010  		require.NoError(t, vcWalletClient.Add(wallet.Credential, testdata.SampleUDCVCWithProof))
  1011  
  1012  		// verify stored VC
  1013  		ok, err := vcWalletClient.Verify(wallet.WithStoredCredentialToVerify("http://example.edu/credentials/1872"))
  1014  		require.NoError(t, err)
  1015  		require.True(t, ok)
  1016  
  1017  		// verify raw VC
  1018  		ok, err = vcWalletClient.Verify(wallet.WithRawCredentialToVerify(testdata.SampleUDCVCWithProof))
  1019  		require.NoError(t, err)
  1020  		require.True(t, ok)
  1021  	})
  1022  
  1023  	t.Run("Test VC wallet verify credential - invalid signature", func(t *testing.T) {
  1024  		vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
  1025  		require.NotEmpty(t, vcWalletClient)
  1026  		require.NoError(t, err)
  1027  
  1028  		defer vcWalletClient.Close()
  1029  
  1030  		// store tampered credential in wallet
  1031  		tamperedVC := strings.ReplaceAll(string(testdata.SampleUDCVCWithProof),
  1032  			`"name": "Example University"`, `"name": "Fake University"`)
  1033  		require.NoError(t, vcWalletClient.Remove(wallet.Credential, "http://example.edu/credentials/1872"))
  1034  		require.NoError(t, vcWalletClient.Add(wallet.Credential, []byte(tamperedVC)))
  1035  
  1036  		ok, err := vcWalletClient.Verify(wallet.WithStoredCredentialToVerify("http://example.edu/credentials/1872"))
  1037  		require.Error(t, err)
  1038  		require.Contains(t, err.Error(), "invalid signature")
  1039  		require.False(t, ok)
  1040  
  1041  		ok, err = vcWalletClient.Verify(wallet.WithRawCredentialToVerify([]byte(tamperedVC)))
  1042  		require.Error(t, err)
  1043  		require.Contains(t, err.Error(), "invalid signature")
  1044  		require.False(t, ok)
  1045  	})
  1046  
  1047  	t.Run("Test VC wallet verify presentation - success", func(t *testing.T) {
  1048  		vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
  1049  		require.NotEmpty(t, vcWalletClient)
  1050  		require.NoError(t, err)
  1051  
  1052  		defer vcWalletClient.Close()
  1053  
  1054  		// verify raw VC
  1055  		ok, err := vcWalletClient.Verify(wallet.WithRawPresentationToVerify(testdata.SampleUDCPresentation))
  1056  		require.NoError(t, err)
  1057  		require.True(t, ok)
  1058  	})
  1059  
  1060  	t.Run("Test VC wallet verify presentation - invalid signature", func(t *testing.T) {
  1061  		vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
  1062  		require.NotEmpty(t, vcWalletClient)
  1063  		require.NoError(t, err)
  1064  
  1065  		defer vcWalletClient.Close()
  1066  
  1067  		tamperedVP := strings.ReplaceAll(string(testdata.SampleUDCPresentation),
  1068  			`"holder": "did:key:z6MknC1wwS6DEYwtGbZZo2QvjQjkh2qSBjb4GYmbye8dv4S5"`,
  1069  			`"holder": "did:key:z6MknC1wwS6DEYwtGbZZo2QvjQjkh2qSBjb4GYmbye8dv464"`)
  1070  
  1071  		ok, err := vcWalletClient.Verify(wallet.WithRawPresentationToVerify([]byte(tamperedVP)))
  1072  		require.Error(t, err)
  1073  		require.Contains(t, err.Error(), "invalid signature")
  1074  		require.False(t, ok)
  1075  	})
  1076  
  1077  	t.Run("Test VC wallet verify presentation - test wallet locked", func(t *testing.T) {
  1078  		vcWalletClient, err := New(sampleUserID, mockctx)
  1079  		require.NotEmpty(t, vcWalletClient)
  1080  		require.NoError(t, err)
  1081  
  1082  		ok, err := vcWalletClient.Verify(wallet.WithRawPresentationToVerify(testdata.SampleUDCPresentation))
  1083  		require.True(t, errors.Is(err, ErrWalletLocked))
  1084  		require.False(t, ok)
  1085  	})
  1086  }
  1087  
  1088  func TestWallet_Derive(t *testing.T) {
  1089  	customVDR := &mockvdr.MockVDRegistry{
  1090  		ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
  1091  			if strings.HasPrefix(didID, "did:key:") {
  1092  				k := key.New()
  1093  
  1094  				d, e := k.Read(didID)
  1095  				if e != nil {
  1096  					return nil, e
  1097  				}
  1098  
  1099  				return d, nil
  1100  			}
  1101  
  1102  			return nil, fmt.Errorf("did not found")
  1103  		},
  1104  	}
  1105  
  1106  	mockctx := newMockProvider(t)
  1107  	mockctx.VDRegistryValue = customVDR
  1108  
  1109  	customCrypto, err := tinkcrypto.New()
  1110  	require.NoError(t, err)
  1111  
  1112  	mockctx.CryptoValue = customCrypto
  1113  
  1114  	// create profile
  1115  	err = CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
  1116  	require.NoError(t, err)
  1117  
  1118  	// prepare frame
  1119  	var frameDoc map[string]interface{}
  1120  
  1121  	require.NoError(t, json.Unmarshal(testdata.SampleFrame, &frameDoc))
  1122  
  1123  	t.Run("Test derive a credential from wallet - success", func(t *testing.T) {
  1124  		walletInstance, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
  1125  		require.NoError(t, err)
  1126  		require.NotEmpty(t, walletInstance)
  1127  
  1128  		// save BBS VC in store
  1129  		require.NoError(t, walletInstance.Add(wallet.Credential, testdata.SampleUDCVCWithProofBBS))
  1130  
  1131  		sampleNonce := uuid.New().String()
  1132  
  1133  		verifyBBSProof := func(proofs []verifiable.Proof) {
  1134  			require.Len(t, proofs, 1)
  1135  			require.NotEmpty(t, proofs[0])
  1136  			require.Equal(t, proofs[0]["type"], "BbsBlsSignatureProof2020")
  1137  			require.NotEmpty(t, proofs[0]["nonce"])
  1138  			require.EqualValues(t, proofs[0]["nonce"], base64.StdEncoding.EncodeToString([]byte(sampleNonce)))
  1139  			require.NotEmpty(t, proofs[0]["proofValue"])
  1140  		}
  1141  
  1142  		// derive stored credential
  1143  		vc, err := walletInstance.Derive(wallet.FromStoredCredential("http://example.edu/credentials/1872"),
  1144  			&wallet.DeriveOptions{
  1145  				Nonce: sampleNonce,
  1146  				Frame: frameDoc,
  1147  			})
  1148  		require.NoError(t, err)
  1149  		require.NotEmpty(t, vc)
  1150  		verifyBBSProof(vc.Proofs)
  1151  
  1152  		// derive raw credential
  1153  		vc, err = walletInstance.Derive(wallet.FromRawCredential(testdata.SampleUDCVCWithProofBBS), &wallet.DeriveOptions{
  1154  			Nonce: sampleNonce,
  1155  			Frame: frameDoc,
  1156  		})
  1157  		require.NoError(t, err)
  1158  		require.NotEmpty(t, vc)
  1159  		verifyBBSProof(vc.Proofs)
  1160  
  1161  		// derive from credential instance
  1162  		pkFetcher := verifiable.WithPublicKeyFetcher(
  1163  			verifiable.NewVDRKeyResolver(customVDR).PublicKeyFetcher(),
  1164  		)
  1165  
  1166  		loader, err := ldtestutil.DocumentLoader()
  1167  		require.NoError(t, err)
  1168  
  1169  		credential, err := verifiable.ParseCredential(testdata.SampleUDCVCWithProofBBS, pkFetcher,
  1170  			verifiable.WithJSONLDDocumentLoader(loader))
  1171  		require.NoError(t, err)
  1172  		vc, err = walletInstance.Derive(wallet.FromCredential(credential), &wallet.DeriveOptions{
  1173  			Nonce: sampleNonce,
  1174  			Frame: frameDoc,
  1175  		})
  1176  		require.NoError(t, err)
  1177  		require.NotEmpty(t, vc)
  1178  		verifyBBSProof(vc.Proofs)
  1179  	})
  1180  
  1181  	t.Run("Test derive credential failures", func(t *testing.T) {
  1182  		walletInstance, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
  1183  		require.NotEmpty(t, walletInstance)
  1184  		require.NoError(t, err)
  1185  
  1186  		// invalid request
  1187  		vc, err := walletInstance.Derive(wallet.FromStoredCredential(""), &wallet.DeriveOptions{})
  1188  		require.Empty(t, vc)
  1189  		require.Error(t, err)
  1190  		require.Contains(t, err.Error(), "invalid request to derive credential")
  1191  
  1192  		// credential not found in store
  1193  		vc, err = walletInstance.Derive(wallet.FromStoredCredential("invalid-id"), &wallet.DeriveOptions{})
  1194  		require.Empty(t, vc)
  1195  		require.Error(t, err)
  1196  		require.Contains(t, err.Error(), "data not found")
  1197  
  1198  		// invalid credential in store
  1199  		require.NoError(t, walletInstance.Add(wallet.Credential, []byte(sampleInvalidDIDContent)))
  1200  
  1201  		vc, err = walletInstance.Derive(wallet.FromStoredCredential("did:example:sampleInvalidDIDContent"),
  1202  			&wallet.DeriveOptions{})
  1203  		require.Empty(t, vc)
  1204  		require.Error(t, err)
  1205  		require.Contains(t, err.Error(), "credential type of unknown structure")
  1206  
  1207  		// invalid raw credential
  1208  		vc, err = walletInstance.Derive(wallet.FromRawCredential([]byte(sampleInvalidDIDContent)), &wallet.DeriveOptions{})
  1209  		require.Empty(t, vc)
  1210  		require.Error(t, err)
  1211  		require.Contains(t, err.Error(), "credential type of unknown structure")
  1212  
  1213  		// try deriving wrong proof type - no BbsBlsSignature2020 proof present
  1214  		vc, err = walletInstance.Derive(wallet.FromRawCredential(testdata.SampleUDCVCWithProof), &wallet.DeriveOptions{
  1215  			Frame: frameDoc,
  1216  		})
  1217  		require.Empty(t, vc)
  1218  		require.Error(t, err)
  1219  		require.Contains(t, err.Error(), "no BbsBlsSignature2020 proof present")
  1220  	})
  1221  
  1222  	t.Run("Test derive credential failures - test wallet locked", func(t *testing.T) {
  1223  		vcWalletClient, err := New(sampleUserID, mockctx)
  1224  		require.NotEmpty(t, vcWalletClient)
  1225  		require.NoError(t, err)
  1226  
  1227  		result, err := vcWalletClient.Derive(wallet.FromRawCredential(testdata.SampleUDCVCWithProof), &wallet.DeriveOptions{
  1228  			Frame: frameDoc,
  1229  		})
  1230  		require.True(t, errors.Is(err, ErrWalletLocked))
  1231  		require.Empty(t, result)
  1232  	})
  1233  }
  1234  
  1235  func TestClient_CreateKeyPair(t *testing.T) {
  1236  	sampleUser := uuid.New().String()
  1237  	mockctx := newMockProvider(t)
  1238  
  1239  	err := CreateProfile(sampleUser, mockctx, wallet.WithPassphrase(samplePassPhrase))
  1240  	require.NoError(t, err)
  1241  
  1242  	vcWallet, err := New(sampleUser, mockctx)
  1243  	require.NoError(t, err)
  1244  	require.NotEmpty(t, vcWallet)
  1245  
  1246  	err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
  1247  	require.NoError(t, err)
  1248  
  1249  	t.Run("test creating key pair", func(t *testing.T) {
  1250  		keyPair, err := vcWallet.CreateKeyPair(kms.ED25519)
  1251  		require.NoError(t, err)
  1252  		require.NotEmpty(t, keyPair)
  1253  		require.NotEmpty(t, keyPair.KeyID)
  1254  		require.NotEmpty(t, keyPair.PublicKey)
  1255  	})
  1256  
  1257  	t.Run("test failure while creating key pair", func(t *testing.T) {
  1258  		keyPair, err := vcWallet.CreateKeyPair(kms.KeyType("invalid"))
  1259  		require.Error(t, err)
  1260  		require.Contains(t, err.Error(), "failed to create new key")
  1261  		require.Empty(t, keyPair)
  1262  	})
  1263  
  1264  	t.Run("test failure while creating key pair (closed wallet)", func(t *testing.T) {
  1265  		require.True(t, vcWallet.Close())
  1266  
  1267  		keyPair, err := vcWallet.CreateKeyPair(kms.KeyType("invalid"))
  1268  		require.True(t, errors.Is(err, ErrWalletLocked))
  1269  		require.Empty(t, keyPair)
  1270  	})
  1271  }
  1272  
  1273  func TestClient_Connect(t *testing.T) {
  1274  	sampleUser := uuid.New().String()
  1275  	mockctx := newMockProvider(t)
  1276  
  1277  	err := CreateProfile(sampleUser, mockctx, wallet.WithPassphrase(samplePassPhrase))
  1278  	require.NoError(t, err)
  1279  
  1280  	t.Run("test did connect success", func(t *testing.T) {
  1281  		sampleConnID := uuid.New().String()
  1282  
  1283  		oobSvc := &mockoutofband.MockOobService{
  1284  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
  1285  				return sampleConnID, nil
  1286  			},
  1287  		}
  1288  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
  1289  
  1290  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
  1291  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1292  				ch <- service.StateMsg{
  1293  					Type:       service.PostState,
  1294  					StateID:    didexchange.StateIDCompleted,
  1295  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
  1296  				}
  1297  
  1298  				return nil
  1299  			},
  1300  		}
  1301  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
  1302  
  1303  		vcWallet, err := New(sampleUser, mockctx)
  1304  		require.NoError(t, err)
  1305  		require.NotEmpty(t, vcWallet)
  1306  
  1307  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
  1308  		require.NoError(t, err)
  1309  		defer vcWallet.Close()
  1310  
  1311  		connectionID, err := vcWallet.Connect(&outofband.Invitation{})
  1312  		require.NoError(t, err)
  1313  		require.Equal(t, sampleConnID, connectionID)
  1314  	})
  1315  
  1316  	t.Run("test did connect failure", func(t *testing.T) {
  1317  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
  1318  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1319  				return fmt.Errorf(sampleClientErr)
  1320  			},
  1321  		}
  1322  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
  1323  
  1324  		vcWallet, err := New(sampleUser, mockctx)
  1325  		require.NoError(t, err)
  1326  		require.NotEmpty(t, vcWallet)
  1327  
  1328  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
  1329  		require.NoError(t, err)
  1330  		defer vcWallet.Close()
  1331  
  1332  		connectionID, err := vcWallet.Connect(&outofband.Invitation{})
  1333  		require.Error(t, err)
  1334  		require.Contains(t, err.Error(), sampleClientErr)
  1335  		require.Empty(t, connectionID)
  1336  	})
  1337  
  1338  	t.Run("test did connect failure - auth error", func(t *testing.T) {
  1339  		vcWallet, err := New(sampleUser, mockctx)
  1340  		require.NoError(t, err)
  1341  		require.NotEmpty(t, vcWallet)
  1342  
  1343  		connectionID, err := vcWallet.Connect(&outofband.Invitation{})
  1344  		require.True(t, errors.Is(err, ErrWalletLocked))
  1345  		require.Empty(t, connectionID)
  1346  	})
  1347  }
  1348  
  1349  func TestClient_ProposePresentation(t *testing.T) {
  1350  	sampleUser := uuid.New().String()
  1351  	mockctx := newMockProvider(t)
  1352  
  1353  	err := CreateProfile(sampleUser, mockctx, wallet.WithPassphrase(samplePassPhrase))
  1354  	require.NoError(t, err)
  1355  
  1356  	const (
  1357  		myDID    = "did:mydid:123"
  1358  		theirDID = "did:theirdid:123"
  1359  	)
  1360  
  1361  	t.Run("test propose presentation success", func(t *testing.T) {
  1362  		sampleConnID := uuid.New().String()
  1363  
  1364  		oobSvc := &mockoutofband.MockOobService{
  1365  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
  1366  				return sampleConnID, nil
  1367  			},
  1368  		}
  1369  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
  1370  
  1371  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
  1372  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1373  				ch <- service.StateMsg{
  1374  					Type:       service.PostState,
  1375  					StateID:    didexchange.StateIDCompleted,
  1376  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
  1377  				}
  1378  
  1379  				return nil
  1380  			},
  1381  		}
  1382  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
  1383  
  1384  		thID := uuid.New().String()
  1385  
  1386  		ppSvc := &mockpresentproof.MockPresentProofSvc{
  1387  			ActionsFunc: func() ([]presentproofSvc.Action, error) {
  1388  				return []presentproofSvc.Action{
  1389  					{
  1390  						PIID: thID,
  1391  						Msg: service.NewDIDCommMsgMap(&presentproofSvc.RequestPresentationV2{
  1392  							Comment: "mock msg",
  1393  						}),
  1394  						MyDID:    myDID,
  1395  						TheirDID: theirDID,
  1396  					},
  1397  				}, nil
  1398  			},
  1399  			HandleFunc: func(service.DIDCommMsg) (string, error) {
  1400  				return thID, nil
  1401  			},
  1402  		}
  1403  		mockctx.ServiceMap[presentproofSvc.Name] = ppSvc
  1404  
  1405  		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
  1406  		require.NoError(t, err)
  1407  
  1408  		record := &connection.Record{
  1409  			ConnectionID: sampleConnID,
  1410  			MyDID:        myDID,
  1411  			TheirDID:     theirDID,
  1412  		}
  1413  		recordBytes, err := json.Marshal(record)
  1414  		require.NoError(t, err)
  1415  		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
  1416  
  1417  		vcWallet, err := New(sampleUser, mockctx)
  1418  		require.NoError(t, err)
  1419  		require.NotEmpty(t, vcWallet)
  1420  
  1421  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
  1422  		require.NoError(t, err)
  1423  		defer vcWallet.Close()
  1424  
  1425  		msg, err := vcWallet.ProposePresentation(&wallet.GenericInvitation{})
  1426  		require.NoError(t, err)
  1427  		require.NotEmpty(t, msg)
  1428  	})
  1429  
  1430  	t.Run("test propose presentation failure", func(t *testing.T) {
  1431  		oobSvc := &mockoutofband.MockOobService{
  1432  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
  1433  				return "", fmt.Errorf(sampleClientErr)
  1434  			},
  1435  		}
  1436  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
  1437  
  1438  		vcWallet, err := New(sampleUser, mockctx)
  1439  		require.NoError(t, err)
  1440  		require.NotEmpty(t, vcWallet)
  1441  
  1442  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
  1443  		require.NoError(t, err)
  1444  		defer vcWallet.Close()
  1445  
  1446  		msg, err := vcWallet.ProposePresentation(&wallet.GenericInvitation{})
  1447  		require.Error(t, err)
  1448  		require.Empty(t, msg)
  1449  	})
  1450  
  1451  	t.Run("test propose presentation failure - auth error", func(t *testing.T) {
  1452  		vcWallet, err := New(sampleUser, mockctx)
  1453  		require.NoError(t, err)
  1454  		require.NotEmpty(t, vcWallet)
  1455  
  1456  		msg, err := vcWallet.ProposePresentation(&wallet.GenericInvitation{})
  1457  		require.True(t, errors.Is(err, ErrWalletLocked))
  1458  		require.Empty(t, msg)
  1459  	})
  1460  }
  1461  
  1462  func TestClient_PresentProof(t *testing.T) {
  1463  	sampleUser := uuid.New().String()
  1464  	mockctx := newMockProvider(t)
  1465  
  1466  	err := CreateProfile(sampleUser, mockctx, wallet.WithPassphrase(samplePassPhrase))
  1467  	require.NoError(t, err)
  1468  
  1469  	t.Run("test present proof success", func(t *testing.T) {
  1470  		vcWallet, err := New(sampleUser, mockctx)
  1471  		require.NoError(t, err)
  1472  		require.NotEmpty(t, vcWallet)
  1473  
  1474  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
  1475  		require.NoError(t, err)
  1476  		defer vcWallet.Close()
  1477  
  1478  		response, err := vcWallet.PresentProof(uuid.New().String(), wallet.FromPresentation(&verifiable.Presentation{}))
  1479  		require.NoError(t, err)
  1480  		require.NotEmpty(t, response)
  1481  		require.Equal(t, model.AckStatusPENDING, response.Status)
  1482  	})
  1483  
  1484  	t.Run("test present proof success - wait for done", func(t *testing.T) {
  1485  		thID := uuid.New().String()
  1486  		mockPresentProofSvc := &mockpresentproof.MockPresentProofSvc{
  1487  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1488  				ch <- service.StateMsg{
  1489  					Type:    service.PostState,
  1490  					StateID: presentproofSvc.StateNameDone,
  1491  					Properties: &mockdidexchange.MockEventProperties{
  1492  						Properties: map[string]interface{}{
  1493  							webRedirectStatusKey: model.AckStatusOK,
  1494  							webRedirectURLKey:    exampleWebRedirect,
  1495  						},
  1496  					},
  1497  					Msg: &mockMsg{thID: thID},
  1498  				}
  1499  
  1500  				return nil
  1501  			},
  1502  		}
  1503  		mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc
  1504  
  1505  		vcWallet, err := New(sampleUser, mockctx)
  1506  		require.NoError(t, err)
  1507  		require.NotEmpty(t, vcWallet)
  1508  
  1509  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
  1510  		require.NoError(t, err)
  1511  		defer vcWallet.Close()
  1512  
  1513  		response, err := vcWallet.PresentProof(thID, wallet.FromPresentation(&verifiable.Presentation{}),
  1514  			wallet.WaitForDone(1*time.Millisecond))
  1515  		require.NoError(t, err)
  1516  		require.NotEmpty(t, response)
  1517  		require.Equal(t, model.AckStatusOK, response.Status)
  1518  		require.Equal(t, exampleWebRedirect, response.RedirectURL)
  1519  	})
  1520  
  1521  	t.Run("test present proof failure - auth error", func(t *testing.T) {
  1522  		vcWallet, err := New(sampleUser, mockctx)
  1523  		require.NoError(t, err)
  1524  		require.NotEmpty(t, vcWallet)
  1525  
  1526  		response, err := vcWallet.PresentProof(uuid.New().String(), wallet.FromPresentation(&verifiable.Presentation{}))
  1527  		require.True(t, errors.Is(err, ErrWalletLocked))
  1528  		require.Empty(t, response)
  1529  	})
  1530  }
  1531  
  1532  func TestClient_ProposeCredential(t *testing.T) {
  1533  	sampleUser := uuid.New().String()
  1534  	mockctx := newMockProvider(t)
  1535  
  1536  	err := CreateProfile(sampleUser, mockctx, wallet.WithPassphrase(samplePassPhrase))
  1537  	require.NoError(t, err)
  1538  
  1539  	const (
  1540  		myDID    = "did:mydid:123"
  1541  		theirDID = "did:theirdid:123"
  1542  	)
  1543  
  1544  	t.Run("test propose credential success", func(t *testing.T) {
  1545  		sampleConnID := uuid.New().String()
  1546  
  1547  		oobSvc := &mockoutofband.MockOobService{
  1548  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
  1549  				return sampleConnID, nil
  1550  			},
  1551  		}
  1552  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
  1553  
  1554  		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
  1555  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1556  				ch <- service.StateMsg{
  1557  					Type:       service.PostState,
  1558  					StateID:    didexchange.StateIDCompleted,
  1559  					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
  1560  				}
  1561  
  1562  				return nil
  1563  			},
  1564  		}
  1565  		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
  1566  
  1567  		thID := uuid.New().String()
  1568  
  1569  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1570  			ActionsFunc: func() ([]issuecredentialsvc.Action, error) {
  1571  				return []issuecredentialsvc.Action{
  1572  					{
  1573  						PIID: thID,
  1574  						Msg: service.NewDIDCommMsgMap(&issuecredentialsvc.OfferCredentialV2{
  1575  							Comment: sampleMsgComment,
  1576  						}),
  1577  						MyDID:    myDID,
  1578  						TheirDID: theirDID,
  1579  					},
  1580  				}, nil
  1581  			},
  1582  			HandleFunc: func(service.DIDCommMsg) (string, error) {
  1583  				return thID, nil
  1584  			},
  1585  		}
  1586  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1587  
  1588  		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
  1589  		require.NoError(t, err)
  1590  
  1591  		record := &connection.Record{
  1592  			ConnectionID: sampleConnID,
  1593  			MyDID:        myDID,
  1594  			TheirDID:     theirDID,
  1595  		}
  1596  		recordBytes, err := json.Marshal(record)
  1597  		require.NoError(t, err)
  1598  		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
  1599  
  1600  		vcWallet, err := New(sampleUser, mockctx)
  1601  		require.NoError(t, err)
  1602  		require.NotEmpty(t, vcWallet)
  1603  
  1604  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
  1605  		require.NoError(t, err)
  1606  		defer vcWallet.Close()
  1607  
  1608  		msg, err := vcWallet.ProposeCredential(&wallet.GenericInvitation{})
  1609  		require.NoError(t, err)
  1610  		require.NotEmpty(t, msg)
  1611  
  1612  		offer := &issuecredentialsvc.OfferCredentialV2{}
  1613  
  1614  		err = msg.Decode(offer)
  1615  		require.NoError(t, err)
  1616  		require.NotEmpty(t, offer)
  1617  		require.Equal(t, sampleMsgComment, offer.Comment)
  1618  	})
  1619  
  1620  	t.Run("test propose presentation failure", func(t *testing.T) {
  1621  		oobSvc := &mockoutofband.MockOobService{
  1622  			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
  1623  				return "", fmt.Errorf(sampleClientErr)
  1624  			},
  1625  		}
  1626  		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
  1627  
  1628  		vcWallet, err := New(sampleUser, mockctx)
  1629  		require.NoError(t, err)
  1630  		require.NotEmpty(t, vcWallet)
  1631  
  1632  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
  1633  		require.NoError(t, err)
  1634  		defer vcWallet.Close()
  1635  
  1636  		msg, err := vcWallet.ProposeCredential(&wallet.GenericInvitation{})
  1637  		require.Error(t, err)
  1638  		require.Empty(t, msg)
  1639  	})
  1640  
  1641  	t.Run("test propose presentation failure - auth error", func(t *testing.T) {
  1642  		vcWallet, err := New(sampleUser, mockctx)
  1643  		require.NoError(t, err)
  1644  		require.NotEmpty(t, vcWallet)
  1645  
  1646  		msg, err := vcWallet.ProposeCredential(&wallet.GenericInvitation{})
  1647  		require.True(t, errors.Is(err, ErrWalletLocked))
  1648  		require.Empty(t, msg)
  1649  	})
  1650  }
  1651  
  1652  func TestClient_RequestCredential(t *testing.T) {
  1653  	sampleUser := uuid.New().String()
  1654  	mockctx := newMockProvider(t)
  1655  
  1656  	err := CreateProfile(sampleUser, mockctx, wallet.WithPassphrase(samplePassPhrase))
  1657  	require.NoError(t, err)
  1658  
  1659  	t.Run("test present proof success", func(t *testing.T) {
  1660  		vcWallet, err := New(sampleUser, mockctx)
  1661  		require.NoError(t, err)
  1662  		require.NotEmpty(t, vcWallet)
  1663  
  1664  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
  1665  		require.NoError(t, err)
  1666  		defer vcWallet.Close()
  1667  
  1668  		response, err := vcWallet.RequestCredential(uuid.New().String(), wallet.FromPresentation(&verifiable.Presentation{}))
  1669  		require.NoError(t, err)
  1670  		require.NotEmpty(t, response)
  1671  		require.Equal(t, model.AckStatusPENDING, response.Status)
  1672  	})
  1673  
  1674  	t.Run("test present proof success - wait for done", func(t *testing.T) {
  1675  		thID := uuid.New().String()
  1676  
  1677  		icSvc := &mockissuecredential.MockIssueCredentialSvc{
  1678  			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
  1679  				ch <- service.StateMsg{
  1680  					Type:    service.PostState,
  1681  					StateID: "done",
  1682  					Properties: &mockdidexchange.MockEventProperties{
  1683  						Properties: map[string]interface{}{
  1684  							webRedirectStatusKey: model.AckStatusOK,
  1685  							webRedirectURLKey:    exampleWebRedirect,
  1686  						},
  1687  					},
  1688  					Msg: &mockMsg{thID: thID},
  1689  				}
  1690  
  1691  				return nil
  1692  			},
  1693  		}
  1694  		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
  1695  
  1696  		vcWallet, err := New(sampleUser, mockctx)
  1697  		require.NoError(t, err)
  1698  		require.NotEmpty(t, vcWallet)
  1699  
  1700  		err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
  1701  		require.NoError(t, err)
  1702  		defer vcWallet.Close()
  1703  
  1704  		response, err := vcWallet.RequestCredential(thID, wallet.FromPresentation(&verifiable.Presentation{}),
  1705  			wallet.WaitForDone(0))
  1706  		require.NoError(t, err)
  1707  		require.NotEmpty(t, response)
  1708  		require.Equal(t, model.AckStatusOK, response.Status)
  1709  		require.Equal(t, exampleWebRedirect, response.RedirectURL)
  1710  	})
  1711  
  1712  	t.Run("test present proof failure - auth error", func(t *testing.T) {
  1713  		vcWallet, err := New(sampleUser, mockctx)
  1714  		require.NoError(t, err)
  1715  		require.NotEmpty(t, vcWallet)
  1716  
  1717  		response, err := vcWallet.RequestCredential(uuid.New().String(), wallet.FromPresentation(&verifiable.Presentation{}))
  1718  		require.True(t, errors.Is(err, ErrWalletLocked))
  1719  		require.Empty(t, response)
  1720  	})
  1721  }
  1722  
  1723  func TestClient_ResolveCredentialManifest(t *testing.T) {
  1724  	sampleUser := uuid.New().String()
  1725  	mockctx := newMockProvider(t)
  1726  
  1727  	err := CreateProfile(sampleUser, mockctx, wallet.WithPassphrase(samplePassPhrase))
  1728  	require.NoError(t, err)
  1729  
  1730  	vcWallet, err := New(sampleUser, mockctx)
  1731  	require.NoError(t, err)
  1732  	require.NotEmpty(t, vcWallet)
  1733  
  1734  	err = vcWallet.Open(wallet.WithUnlockByPassphrase(samplePassPhrase))
  1735  	require.NoError(t, err)
  1736  
  1737  	t.Run("test resolving credential response", func(t *testing.T) {
  1738  		resolved, err := vcWallet.ResolveCredentialManifest(testdata.CredentialManifestMultipleVCs,
  1739  			wallet.ResolveRawResponse(testdata.CredentialResponseWithMultipleVCs))
  1740  		require.NoError(t, err)
  1741  		require.Len(t, resolved, 2)
  1742  	})
  1743  
  1744  	t.Run("test resolving credential by descriptor ID", func(t *testing.T) {
  1745  		resolved, err := vcWallet.ResolveCredentialManifest(testdata.CredentialManifestMultipleVCs,
  1746  			wallet.ResolveRawCredential("udc_output", testdata.SampleUDCVC))
  1747  		require.NoError(t, err)
  1748  		require.Len(t, resolved, 1)
  1749  	})
  1750  
  1751  	t.Run("test failure while resolving (closed wallet)", func(t *testing.T) {
  1752  		require.True(t, vcWallet.Close())
  1753  
  1754  		resolved, err := vcWallet.ResolveCredentialManifest(testdata.CredentialManifestMultipleVCs,
  1755  			wallet.ResolveRawCredential("udc_output", testdata.SampleUDCVC))
  1756  		require.True(t, errors.Is(err, ErrWalletLocked))
  1757  		require.Empty(t, resolved)
  1758  	})
  1759  }
  1760  
  1761  func newMockProvider(t *testing.T) *mockprovider.Provider {
  1762  	t.Helper()
  1763  
  1764  	loader, err := ldtestutil.DocumentLoader()
  1765  	require.NoError(t, err)
  1766  
  1767  	serviceMap := map[string]interface{}{
  1768  		presentproofSvc.Name:    &mockpresentproof.MockPresentProofSvc{},
  1769  		outofbandSvc.Name:       &mockoutofband.MockOobService{},
  1770  		didexchange.DIDExchange: &mockdidexchange.MockDIDExchangeSvc{},
  1771  		mediator.Coordination:   &mockmediator.MockMediatorSvc{},
  1772  		issuecredentialsvc.Name: &mockissuecredential.MockIssueCredentialSvc{},
  1773  		oobv2.Name:              &mockoutofbandv2.MockOobService{},
  1774  	}
  1775  
  1776  	return &mockprovider.Provider{
  1777  		StorageProviderValue:              mockstorage.NewMockStoreProvider(),
  1778  		ProtocolStateStorageProviderValue: mockstorage.NewMockStoreProvider(),
  1779  		DocumentLoaderValue:               loader,
  1780  		ServiceMap:                        serviceMap,
  1781  	}
  1782  }
  1783  
  1784  func createSampleProfile(t *testing.T, mockctx *mockprovider.Provider) {
  1785  	t.Helper()
  1786  
  1787  	err := CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
  1788  	require.NoError(t, err)
  1789  
  1790  	vcWallet, err := New(sampleUserID, mockctx)
  1791  	require.NoError(t, err)
  1792  	require.NotEmpty(t, vcWallet)
  1793  }
  1794  
  1795  type mockStorageProvider struct {
  1796  	*mockstorage.MockStoreProvider
  1797  	config  storage.StoreConfiguration
  1798  	failure error
  1799  }
  1800  
  1801  func (s *mockStorageProvider) SetStoreConfig(name string, config storage.StoreConfiguration) error {
  1802  	s.config = config
  1803  
  1804  	return s.failure
  1805  }
  1806  
  1807  func (s *mockStorageProvider) GetStoreConfig(name string) (storage.StoreConfiguration, error) {
  1808  	return s.config, nil
  1809  }
  1810  
  1811  // mockMsg containing custom parent thread ID.
  1812  type mockMsg struct {
  1813  	*service.DIDCommMsgMap
  1814  	thID string
  1815  }
  1816  
  1817  func (m *mockMsg) ParentThreadID() string {
  1818  	return m.thID
  1819  }
  1820  
  1821  func (m *mockMsg) ThreadID() (string, error) {
  1822  	return m.thID, nil
  1823  }