github.com/hyperledger/aries-framework-go@v0.3.2/pkg/client/vcwallet/client.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  	"encoding/json"
    11  	"errors"
    12  	"fmt"
    13  
    14  	"github.com/piprate/json-gold/ld"
    15  
    16  	"github.com/hyperledger/aries-framework-go/pkg/client/outofband"
    17  	"github.com/hyperledger/aries-framework-go/pkg/common/log"
    18  	"github.com/hyperledger/aries-framework-go/pkg/crypto"
    19  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
    20  	"github.com/hyperledger/aries-framework-go/pkg/doc/cm"
    21  	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
    22  	"github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
    23  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    24  	"github.com/hyperledger/aries-framework-go/pkg/wallet"
    25  	"github.com/hyperledger/aries-framework-go/spi/storage"
    26  )
    27  
    28  var logger = log.New("aries-framework/client/vcwallet")
    29  
    30  // ErrWalletLocked when key manager related operation attempted on locked wallet.
    31  var ErrWalletLocked = errors.New("wallet locked")
    32  
    33  // provider contains dependencies for the verifiable credential wallet client
    34  // and is typically created by using aries.Context().
    35  type provider interface {
    36  	StorageProvider() storage.Provider
    37  	VDRegistry() vdr.Registry
    38  	Crypto() crypto.Crypto
    39  	JSONLDDocumentLoader() ld.DocumentLoader
    40  	MediaTypeProfiles() []string
    41  	didCommProvider // to be used only if wallet needs to be participated in DIDComm.
    42  }
    43  
    44  // didCommProvider to be used only if wallet needs to be participated in DIDComm operation.
    45  // TODO: using wallet KMS instead of provider KMS.
    46  // TODO: reconcile Protocol storage with wallet store.
    47  type didCommProvider interface {
    48  	KMS() kms.KeyManager
    49  	ServiceEndpoint() string
    50  	ProtocolStateStorageProvider() storage.Provider
    51  	Service(id string) (interface{}, error)
    52  	KeyType() kms.KeyType
    53  	KeyAgreementType() kms.KeyType
    54  }
    55  
    56  // walletAuth is auth function which returns wallet unlock token.
    57  type walletAuth func() (string, error)
    58  
    59  // noAuth default auth when wallet is still locked.
    60  // nolint:gochecknoglobals
    61  var noAuth walletAuth = func() (string, error) { return "", ErrWalletLocked }
    62  
    63  // Client enable access to verifiable credential wallet features.
    64  type Client struct {
    65  	wallet  *wallet.Wallet
    66  	didComm *wallet.DidComm
    67  	auth    walletAuth
    68  }
    69  
    70  // New returns new verifiable credential wallet client for given user.
    71  //
    72  //	Args:
    73  //		- userID : unique user identifier used for login.
    74  //		- provider: dependencies for the verifiable credential wallet client.
    75  //		- options : options for unlocking wallet. Any other existing wallet instance of same wallet user will be locked
    76  //		once this instance is unlocked.
    77  //
    78  // returns error if wallet profile is not found.
    79  // To create a new wallet profile, use `CreateProfile()`.
    80  // To update an existing profile, use `UpdateProfile()`.
    81  func New(userID string, ctx provider, options ...wallet.UnlockOptions) (*Client, error) {
    82  	w, err := wallet.New(userID, ctx)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	didComm, err := wallet.NewDidComm(w, ctx)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	client := &Client{wallet: w, didComm: didComm, auth: noAuth}
    93  
    94  	if len(options) > 0 {
    95  		if client.Close() {
    96  			logger.Debugf("wallet was already open, existing wallet instance key manager is now closed")
    97  		}
    98  
    99  		err = client.Open(options...)
   100  		if err != nil {
   101  			return nil, err
   102  		}
   103  	}
   104  
   105  	return client, nil
   106  }
   107  
   108  // CreateProfile creates a new verifiable credential wallet profile for given user.
   109  // returns error if wallet profile is already created.
   110  // Use `UpdateProfile()` for replacing an already created verifiable credential wallet profile.
   111  func CreateProfile(userID string, ctx provider, options ...wallet.ProfileOptions) error {
   112  	return wallet.CreateProfile(userID, ctx, options...)
   113  }
   114  
   115  // UpdateProfile updates existing verifiable credential wallet profile.
   116  // Will create new profile if no profile exists for given user.
   117  // Caution: you might lose your existing keys if you change kms options.
   118  func UpdateProfile(userID string, ctx provider, options ...wallet.ProfileOptions) error {
   119  	return wallet.UpdateProfile(userID, ctx, options...)
   120  }
   121  
   122  // ProfileExists checks if profile exists for given wallet user, returns error if not found.
   123  func ProfileExists(userID string, ctx provider) error {
   124  	return wallet.ProfileExists(userID, ctx)
   125  }
   126  
   127  // Open unlocks wallet client's key manager instance and returns a token for subsequent use of wallet features.
   128  //
   129  //	Args:
   130  //		- unlock options for opening wallet.
   131  //
   132  //	Returns token with expiry that can be used for subsequent use of wallet features.
   133  func (c *Client) Open(options ...wallet.UnlockOptions) error {
   134  	authToken, err := c.wallet.Open(options...)
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	c.auth = func() (s string, e error) {
   140  		return authToken, nil
   141  	}
   142  
   143  	return nil
   144  }
   145  
   146  // Close expires token issued to this VC wallet client.
   147  // returns false if token is not found or already expired for this wallet user.
   148  func (c *Client) Close() bool {
   149  	c.auth = noAuth
   150  
   151  	return c.wallet.Close()
   152  }
   153  
   154  // Export produces a serialized exported wallet representation.
   155  // Only ciphertext wallet contents can be exported.
   156  //
   157  //	Args:
   158  //		- auth: token to be used to lock the wallet before exporting.
   159  //
   160  //	Returns exported locked wallet.
   161  //
   162  // Supported data models:
   163  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
   164  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
   165  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
   166  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
   167  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
   168  //
   169  func (c *Client) Export(auth string) (json.RawMessage, error) {
   170  	// TODO to be added #2433
   171  	return nil, fmt.Errorf("to be implemented")
   172  }
   173  
   174  // Import Takes a serialized exported wallet representation as input
   175  // and imports all contents into wallet.
   176  //
   177  //	Args:
   178  //		- contents: wallet content to be imported.
   179  //		- auth: token used while exporting the wallet.
   180  //
   181  // Supported data models:
   182  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
   183  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
   184  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
   185  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
   186  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
   187  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Key
   188  //
   189  func (c *Client) Import(auth string, contents json.RawMessage) error {
   190  	// TODO to be added #2433
   191  	return fmt.Errorf("to be implemented")
   192  }
   193  
   194  // Add adds given data model to wallet contents store.
   195  //
   196  // Supported data models:
   197  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
   198  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
   199  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
   200  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
   201  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
   202  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Key
   203  //
   204  // TODO: (#2433) support for correlation between wallet contents (ex: credentials to a profile/collection).
   205  func (c *Client) Add(contentType wallet.ContentType, content json.RawMessage, options ...wallet.AddContentOptions) error { //nolint: lll
   206  	auth, err := c.auth()
   207  	if err != nil {
   208  		return err
   209  	}
   210  
   211  	return c.wallet.Add(auth, contentType, content, options...)
   212  }
   213  
   214  // Remove removes wallet content by content ID.
   215  //
   216  // Supported data models:
   217  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
   218  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
   219  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
   220  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
   221  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
   222  //
   223  func (c *Client) Remove(contentType wallet.ContentType, contentID string) error {
   224  	auth, err := c.auth()
   225  	if err != nil {
   226  		return err
   227  	}
   228  
   229  	return c.wallet.Remove(auth, contentType, contentID)
   230  }
   231  
   232  // Get fetches a wallet content by content ID.
   233  //
   234  // Supported data models:
   235  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
   236  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
   237  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
   238  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
   239  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
   240  //
   241  func (c *Client) Get(contentType wallet.ContentType, contentID string) (json.RawMessage, error) {
   242  	auth, err := c.auth()
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  
   247  	return c.wallet.Get(auth, contentType, contentID)
   248  }
   249  
   250  // GetAll fetches all wallet contents of given type.
   251  //
   252  // Supported data models:
   253  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
   254  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
   255  // 	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
   256  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
   257  //	- https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
   258  //
   259  func (c *Client) GetAll(contentType wallet.ContentType, options ...wallet.GetAllContentsOptions) (map[string]json.RawMessage, error) { //nolint: lll
   260  	auth, err := c.auth()
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  
   265  	return c.wallet.GetAll(auth, contentType, options...)
   266  }
   267  
   268  // Query runs query against wallet credential contents and returns presentation containing credential results.
   269  //
   270  // https://w3c-ccg.github.io/universal-wallet-interop-spec/#query
   271  //
   272  // Supported Query Types:
   273  // 	- https://www.w3.org/TR/json-ld11-framing
   274  // 	- https://identity.foundation/presentation-exchange
   275  // 	- https://w3c-ccg.github.io/vp-request-spec/#query-by-example
   276  //
   277  func (c *Client) Query(params ...*wallet.QueryParams) ([]*verifiable.Presentation, error) {
   278  	auth, err := c.auth()
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  
   283  	return c.wallet.Query(auth, params...)
   284  }
   285  
   286  // Issue adds proof to a Verifiable Credential.
   287  //
   288  //	Args:
   289  //		- A verifiable credential with or without proof
   290  //		- Proof options
   291  //
   292  func (c *Client) Issue(credential json.RawMessage,
   293  	options *wallet.ProofOptions) (*verifiable.Credential, error) {
   294  	auth, err := c.auth()
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  
   299  	return c.wallet.Issue(auth, credential, options)
   300  }
   301  
   302  // Prove produces a Verifiable Presentation.
   303  //
   304  //	Args:
   305  //		- list of interfaces (string of credential IDs which can be resolvable to stored credentials in wallet or
   306  //		raw credential).
   307  //		- proof options
   308  //
   309  func (c *Client) Prove(opts *wallet.ProofOptions, creds ...wallet.ProveOptions) (*verifiable.Presentation, error) { //nolint: lll
   310  	auth, err := c.auth()
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  
   315  	return c.wallet.Prove(auth, opts, creds...)
   316  }
   317  
   318  // Verify takes Takes a Verifiable Credential or Verifiable Presentation as input,.
   319  //
   320  //	Args:
   321  //		- verification option for sending different models (stored credential ID, raw credential, raw presentation).
   322  //
   323  // Returns: a boolean verified, and an error if verified is false.
   324  func (c *Client) Verify(option wallet.VerificationOption) (bool, error) {
   325  	auth, err := c.auth()
   326  	if err != nil {
   327  		return false, err
   328  	}
   329  
   330  	return c.wallet.Verify(auth, option)
   331  }
   332  
   333  // Derive derives a credential and returns response credential.
   334  //
   335  //	Args:
   336  //		- credential to derive (ID of the stored credential, raw credential or credential instance).
   337  //		- derive options.
   338  //
   339  func (c *Client) Derive(credential wallet.CredentialToDerive, options *wallet.DeriveOptions) (*verifiable.Credential, error) { //nolint: lll
   340  	auth, err := c.auth()
   341  	if err != nil {
   342  		return nil, err
   343  	}
   344  
   345  	return c.wallet.Derive(auth, credential, options)
   346  }
   347  
   348  // CreateKeyPair creates key pair inside a wallet.
   349  //
   350  //	Args:
   351  //		- authToken: authorization for performing create key pair operation.
   352  //		- keyType: type of the key to be created.
   353  //
   354  func (c *Client) CreateKeyPair(keyType kms.KeyType) (*wallet.KeyPair, error) {
   355  	auth, err := c.auth()
   356  	if err != nil {
   357  		return nil, err
   358  	}
   359  
   360  	return c.wallet.CreateKeyPair(auth, keyType)
   361  }
   362  
   363  // Connect accepts out-of-band invitations and performs DID exchange.
   364  //
   365  // Args:
   366  // 		- invitation: out-of-band invitation.
   367  // 		- options: connection options.
   368  //
   369  // Returns:
   370  // 		- connection ID if DID exchange is successful.
   371  // 		- error if operation false.
   372  //
   373  func (c *Client) Connect(invitation *outofband.Invitation, options ...wallet.ConnectOptions) (string, error) {
   374  	auth, err := c.auth()
   375  	if err != nil {
   376  		return "", err
   377  	}
   378  
   379  	return c.didComm.Connect(auth, invitation, options...)
   380  }
   381  
   382  // ProposePresentation accepts out-of-band invitation and sends message proposing presentation
   383  // from wallet to relying party.
   384  //
   385  // https://w3c-ccg.github.io/universal-wallet-interop-spec/#proposepresentation
   386  //
   387  // Currently Supporting
   388  // [0454-present-proof-v2](https://github.com/hyperledger/aries-rfcs/tree/master/features/0454-present-proof-v2)
   389  //
   390  // Args:
   391  // 		- invitation: out-of-band invitation from relying party.
   392  // 		- options: options for accepting invitation and send propose presentation message.
   393  //
   394  // Returns:
   395  // 		- DIDCommMsgMap containing request presentation message if operation is successful.
   396  // 		- error if operation fails.
   397  //
   398  func (c *Client) ProposePresentation(invitation *wallet.GenericInvitation, options ...wallet.InitiateInteractionOption) (*service.DIDCommMsgMap, error) { //nolint: lll
   399  	auth, err := c.auth()
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  
   404  	return c.didComm.ProposePresentation(auth, invitation, options...)
   405  }
   406  
   407  // PresentProof sends message present proof message from wallet to relying party.
   408  // https://w3c-ccg.github.io/universal-wallet-interop-spec/#presentproof
   409  //
   410  // Currently Supporting
   411  // [0454-present-proof-v2](https://github.com/hyperledger/aries-rfcs/tree/master/features/0454-present-proof-v2)
   412  //
   413  // Args:
   414  // 		- thID: thread ID (action ID) of request presentation.
   415  // 		- presentation: presentation to be sent.
   416  //
   417  // Returns:
   418  // 		- Credential interaction status containing status, redirectURL.
   419  // 		- error if operation fails.
   420  //
   421  func (c *Client) PresentProof(thID string, presentProofFrom ...wallet.ConcludeInteractionOptions) (*wallet.CredentialInteractionStatus, error) { //nolint: lll
   422  	auth, err := c.auth()
   423  	if err != nil {
   424  		return nil, err
   425  	}
   426  
   427  	return c.didComm.PresentProof(auth, thID, presentProofFrom...)
   428  }
   429  
   430  // ProposeCredential sends propose credential message from wallet to issuer.
   431  // https://w3c-ccg.github.io/universal-wallet-interop-spec/#requestcredential
   432  //
   433  // Currently Supporting : 0453-issueCredentialV2
   434  // https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md
   435  //
   436  // Args:
   437  // 		- invitation: out-of-band invitation from issuer.
   438  // 		- options: options for accepting invitation and send propose credential message.
   439  //
   440  // Returns:
   441  // 		- DIDCommMsgMap containing offer credential message if operation is successful.
   442  // 		- error if operation fails.
   443  //
   444  func (c *Client) ProposeCredential(invitation *wallet.GenericInvitation, options ...wallet.InitiateInteractionOption) (*service.DIDCommMsgMap, error) { // nolint: lll
   445  	auth, err := c.auth()
   446  	if err != nil {
   447  		return nil, err
   448  	}
   449  
   450  	return c.didComm.ProposeCredential(auth, invitation, options...)
   451  }
   452  
   453  // RequestCredential sends request credential message from wallet to issuer and
   454  // optionally waits for credential response.
   455  // https://w3c-ccg.github.io/universal-wallet-interop-spec/#proposecredential
   456  //
   457  // Currently Supporting : 0453-issueCredentialV2
   458  // https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md
   459  //
   460  // Args:
   461  // 		- thID: thread ID (action ID) of offer credential message previously received.
   462  // 		- concludeInteractionOptions: options to conclude interaction like presentation to be shared etc.
   463  //
   464  // Returns:
   465  // 		- Credential interaction status containing status, redirectURL.
   466  // 		- error if operation fails.
   467  //
   468  func (c *Client) RequestCredential(thID string, options ...wallet.ConcludeInteractionOptions) (*wallet.CredentialInteractionStatus, error) { // nolint: lll
   469  	auth, err := c.auth()
   470  	if err != nil {
   471  		return nil, err
   472  	}
   473  
   474  	return c.didComm.RequestCredential(auth, thID, options...)
   475  }
   476  
   477  // ResolveCredentialManifest resolves given credential manifest by credential response or credential.
   478  // Supports: https://identity.foundation/credential-manifest/
   479  //
   480  // Args:
   481  // 		- authToken: authorization for performing operation.
   482  // 		- manifest: Credential manifest data model in raw format.
   483  // 		- resolve: options to provide credential response or credential to resolve.
   484  //
   485  // Returns:
   486  // 		- list of resolved descriptors.
   487  // 		- error if operation fails.
   488  //
   489  func (c *Client) ResolveCredentialManifest(manifest json.RawMessage, resolve wallet.ResolveManifestOption) ([]*cm.ResolvedDescriptor, error) { // nolint: lll
   490  	auth, err := c.auth()
   491  	if err != nil {
   492  		return nil, err
   493  	}
   494  
   495  	return c.wallet.ResolveCredentialManifest(auth, manifest, resolve)
   496  }