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

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package wallet
     8  
     9  import (
    10  	"encoding/base64"
    11  	"encoding/json"
    12  	"errors"
    13  	"fmt"
    14  
    15  	"github.com/piprate/json-gold/ld"
    16  
    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/doc/cm"
    20  	"github.com/hyperledger/aries-framework-go/pkg/doc/did"
    21  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld"
    22  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/signer"
    23  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite"
    24  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/bbsblssignature2020"
    25  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/ed25519signature2018"
    26  	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/jsonwebsignature2020"
    27  	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
    28  	"github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
    29  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    30  	"github.com/hyperledger/aries-framework-go/spi/storage"
    31  )
    32  
    33  // Proof types.
    34  const (
    35  	// Ed25519Signature2018 ed25519 signature suite.
    36  	Ed25519Signature2018 = "Ed25519Signature2018"
    37  	// JSONWebSignature2020 json web signature suite.
    38  	JSONWebSignature2020 = "JsonWebSignature2020"
    39  	// BbsBlsSignature2020 BBS signature suite.
    40  	BbsBlsSignature2020 = "BbsBlsSignature2020"
    41  )
    42  
    43  // miscellaneous constants.
    44  const (
    45  	bbsContext     = "https://w3id.org/security/bbs/v1"
    46  	emptyRawLength = 4
    47  
    48  	// web redirect constants.
    49  	webRedirectStatusKey = "status"
    50  	webRedirectURLKey    = "url"
    51  )
    52  
    53  // proof options.
    54  // nolint:gochecknoglobals
    55  var (
    56  	defaultSignatureRepresentation = verifiable.SignatureJWS
    57  	supportedRelationships         = map[did.VerificationRelationship]string{
    58  		did.Authentication:  "authentication",
    59  		did.AssertionMethod: "assertionMethod",
    60  	}
    61  )
    62  
    63  var logger = log.New("aries-framework/wallet")
    64  
    65  // provider contains dependencies for the verifiable credential wallet
    66  // and is typically created by using aries.Context().
    67  type provider interface {
    68  	StorageProvider() storage.Provider
    69  	VDRegistry() vdr.Registry
    70  	Crypto() crypto.Crypto
    71  	JSONLDDocumentLoader() ld.DocumentLoader
    72  	MediaTypeProfiles() []string
    73  }
    74  
    75  type provable interface {
    76  	AddLinkedDataProof(context *verifiable.LinkedDataProofContext, jsonldOpts ...jsonld.ProcessorOpts) error
    77  }
    78  
    79  type jwtClaims interface {
    80  	MarshalJWS(signatureAlg verifiable.JWSAlgorithm, signer verifiable.Signer, keyID string) (string, error)
    81  }
    82  
    83  // Wallet enables access to verifiable credential wallet features.
    84  type Wallet struct {
    85  	// ID of wallet content owner
    86  	userID string
    87  
    88  	// wallet profile
    89  	profile *profile
    90  
    91  	// wallet content store
    92  	contents *contentStore
    93  
    94  	// crypto for wallet
    95  	walletCrypto crypto.Crypto
    96  
    97  	// storage provider
    98  	storeProvider storage.Provider
    99  
   100  	// wallet VDR
   101  	vdr vdr.Registry
   102  
   103  	// document loader for JSON-LD contexts
   104  	jsonldDocumentLoader ld.DocumentLoader
   105  }
   106  
   107  // New returns new verifiable credential wallet for given user.
   108  // returns error if wallet profile is not found.
   109  // To create a new wallet profile, use `CreateProfile()`.
   110  // To update an existing profile, use `UpdateProfile()`.
   111  func New(userID string, ctx provider) (*Wallet, error) {
   112  	store, err := newProfileStore(ctx.StorageProvider())
   113  	if err != nil {
   114  		return nil, fmt.Errorf("failed to get store to fetch VC wallet profile info: %w", err)
   115  	}
   116  
   117  	profile, err := store.get(userID)
   118  	if err != nil {
   119  		return nil, fmt.Errorf("failed to get VC wallet profile: %w", err)
   120  	}
   121  
   122  	return &Wallet{
   123  		userID:               userID,
   124  		profile:              profile,
   125  		storeProvider:        ctx.StorageProvider(),
   126  		walletCrypto:         ctx.Crypto(),
   127  		contents:             newContentStore(ctx.StorageProvider(), ctx.JSONLDDocumentLoader(), profile),
   128  		vdr:                  ctx.VDRegistry(),
   129  		jsonldDocumentLoader: ctx.JSONLDDocumentLoader(),
   130  	}, nil
   131  }
   132  
   133  // CreateProfile creates a new verifiable credential wallet profile for given user.
   134  // returns error if wallet profile is already created.
   135  // Use `UpdateProfile()` for replacing an already created verifiable credential wallet profile.
   136  func CreateProfile(userID string, ctx provider, options ...ProfileOptions) error {
   137  	return createOrUpdate(userID, ctx, false, options...)
   138  }
   139  
   140  // UpdateProfile updates existing verifiable credential wallet profile.
   141  // Caution:
   142  // - you might lose your existing keys if you change kms options.
   143  // - you might lose your existing wallet contents if you change storage/EDV options
   144  // (ex: switching context storage provider or changing EDV settings).
   145  func UpdateProfile(userID string, ctx provider, options ...ProfileOptions) error {
   146  	return createOrUpdate(userID, ctx, true, options...)
   147  }
   148  
   149  // CreateDataVaultKeyPairs can be used create EDV key pairs for given profile.
   150  // Wallet will create key pairs in profile kms and updates profile with newly generate EDV encryption & MAC key IDs.
   151  func CreateDataVaultKeyPairs(userID string, ctx provider, options ...UnlockOptions) error {
   152  	store, err := newProfileStore(ctx.StorageProvider())
   153  	if err != nil {
   154  		return fmt.Errorf("failed to get wallet user profile: failed to get store: %w", err)
   155  	}
   156  
   157  	profile, err := store.get(userID)
   158  	if err != nil {
   159  		return fmt.Errorf("failed to get wallet user profile: %w", err)
   160  	}
   161  
   162  	if profile.EDVConf == nil {
   163  		return fmt.Errorf("invalid operation, no edv configuration found in profile: %w", err)
   164  	}
   165  
   166  	opts := &unlockOpts{}
   167  
   168  	for _, opt := range options {
   169  		opt(opts)
   170  	}
   171  
   172  	kmsStore, err := kms.NewAriesProviderWrapper(ctx.StorageProvider())
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	// unlock key manager
   178  	kmsm, err := keyManager().createKeyManager(profile, kmsStore, opts)
   179  	if err != nil {
   180  		return fmt.Errorf("failed to get key manager: %w", err)
   181  	}
   182  
   183  	// update profile
   184  	err = updateProfile(kmsm, profile)
   185  	if err != nil {
   186  		return fmt.Errorf("failed to create key pairs: %w", err)
   187  	}
   188  
   189  	// update profile
   190  	err = store.save(profile, true)
   191  	if err != nil {
   192  		return fmt.Errorf("failed to update profile: %w", err)
   193  	}
   194  
   195  	return nil
   196  }
   197  
   198  func createOrUpdate(userID string, ctx provider, update bool, options ...ProfileOptions) error {
   199  	opts := &profileOpts{}
   200  
   201  	for _, opt := range options {
   202  		opt(opts)
   203  	}
   204  
   205  	store, err := newProfileStore(ctx.StorageProvider())
   206  	if err != nil {
   207  		return fmt.Errorf("failed to get store to save VC wallet profile: %w", err)
   208  	}
   209  
   210  	var profile *profile
   211  
   212  	// nolint: nestif
   213  	if update {
   214  		// find existing profile and update it.
   215  		profile, err = store.get(userID)
   216  		if err != nil {
   217  			return fmt.Errorf("failed to update wallet user profile: %w", err)
   218  		}
   219  
   220  		err = profile.setKMSOptions(opts.passphrase, opts.secretLockSvc, opts.keyServerURL)
   221  		if err != nil {
   222  			return fmt.Errorf("failed to update wallet user profile KMS options: %w", err)
   223  		}
   224  
   225  		err = profile.setEDVOptions(opts.edvConf)
   226  		if err != nil {
   227  			return fmt.Errorf("failed to update EDV configuration")
   228  		}
   229  	} else {
   230  		// create new profile.
   231  		profile, err = createProfile(userID, opts)
   232  		if err != nil {
   233  			return fmt.Errorf("failed to create new  wallet user profile: %w", err)
   234  		}
   235  	}
   236  
   237  	err = store.save(profile, update)
   238  	if err != nil {
   239  		return fmt.Errorf("failed to save VC wallet profile: %w", err)
   240  	}
   241  
   242  	return nil
   243  }
   244  
   245  // ProfileExists checks if profile exists for given wallet user, returns error if not found.
   246  func ProfileExists(userID string, ctx provider) error {
   247  	store, err := newProfileStore(ctx.StorageProvider())
   248  	if err != nil {
   249  		return fmt.Errorf("failed to get store to get VC wallet profile: %w", err)
   250  	}
   251  
   252  	_, err = store.get(userID)
   253  
   254  	return err
   255  }
   256  
   257  // Open unlocks wallet's key manager instance & open wallet content store and
   258  // returns a token for subsequent use of wallet features.
   259  //
   260  //	Args:
   261  //		- unlock options for opening wallet.
   262  //
   263  //	Returns token with expiry that can be used for subsequent use of wallet features.
   264  func (c *Wallet) Open(options ...UnlockOptions) (string, error) {
   265  	opts := &unlockOpts{}
   266  
   267  	for _, opt := range options {
   268  		opt(opts)
   269  	}
   270  
   271  	kmsStore, err := kms.NewAriesProviderWrapper(c.storeProvider)
   272  	if err != nil {
   273  		return "", err
   274  	}
   275  
   276  	// unlock key manager
   277  	keyManager, err := keyManager().createKeyManager(c.profile, kmsStore, opts)
   278  	if err != nil {
   279  		return "", err
   280  	}
   281  
   282  	token, err := sessionManager().createSession(c.profile.User, keyManager, opts.tokenExpiry)
   283  	if err != nil {
   284  		return "", err
   285  	}
   286  
   287  	// open content store using token
   288  	err = c.contents.Open(keyManager, opts)
   289  	if err != nil {
   290  		// close wallet if it fails to open store
   291  		c.Close()
   292  
   293  		return "", err
   294  	}
   295  
   296  	return token, nil
   297  }
   298  
   299  // Close expires token issued to this VC wallet, removes the key manager instance and closes wallet content store.
   300  // returns false if token is not found or already expired for this wallet user.
   301  func (c *Wallet) Close() bool {
   302  	return sessionManager().closeSession(c.userID) && c.contents.Close()
   303  }
   304  
   305  // Export produces a serialized exported wallet representation.
   306  // Only ciphertext wallet contents can be exported.
   307  //
   308  //	Args:
   309  //		- auth: token to be used to lock the wallet before exporting.
   310  //
   311  //	Returns exported locked wallet.
   312  //
   313  // Supported data models:
   314  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
   315  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
   316  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
   317  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
   318  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
   319  func (c *Wallet) Export(auth string) (json.RawMessage, error) {
   320  	// TODO to be added #2433
   321  	return nil, fmt.Errorf("to be implemented")
   322  }
   323  
   324  // Import Takes a serialized exported wallet representation as input
   325  // and imports all contents into wallet.
   326  //
   327  //	Args:
   328  //		- contents: wallet content to be imported.
   329  //		- auth: token used while exporting the wallet.
   330  //
   331  // Supported data models:
   332  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
   333  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
   334  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
   335  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
   336  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
   337  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Key
   338  func (c *Wallet) Import(auth string, contents json.RawMessage) error {
   339  	// TODO to be added #2433
   340  	return fmt.Errorf("to be implemented")
   341  }
   342  
   343  // Add adds given data model to wallet contents store.
   344  //
   345  // Supported data models:
   346  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
   347  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
   348  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
   349  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
   350  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
   351  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Key
   352  func (c *Wallet) Add(authToken string, contentType ContentType, content json.RawMessage, options ...AddContentOptions) error { //nolint: lll
   353  	return c.contents.Save(authToken, contentType, content, options...)
   354  }
   355  
   356  // Remove removes wallet content by content ID.
   357  //
   358  // Supported data models:
   359  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
   360  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
   361  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
   362  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
   363  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
   364  func (c *Wallet) Remove(authToken string, contentType ContentType, contentID string) error {
   365  	return c.contents.Remove(authToken, contentID, contentType)
   366  }
   367  
   368  // Get fetches a wallet content by content ID.
   369  //
   370  // Supported data models:
   371  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
   372  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
   373  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
   374  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
   375  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
   376  func (c *Wallet) Get(authToken string, contentType ContentType, contentID string) (json.RawMessage, error) {
   377  	return c.contents.Get(authToken, contentID, contentType)
   378  }
   379  
   380  // GetAll fetches all wallet contents of given type.
   381  // Returns map of key value from content store for given content type.
   382  //
   383  // Supported data models:
   384  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
   385  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
   386  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
   387  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
   388  //   - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
   389  func (c *Wallet) GetAll(authToken string, contentType ContentType, options ...GetAllContentsOptions) (map[string]json.RawMessage, error) { //nolint: lll
   390  	opts := &getAllContentsOpts{}
   391  
   392  	for _, option := range options {
   393  		option(opts)
   394  	}
   395  
   396  	if opts.collectionID != "" {
   397  		return c.contents.GetAllByCollection(authToken, opts.collectionID, contentType)
   398  	}
   399  
   400  	return c.contents.GetAll(authToken, contentType)
   401  }
   402  
   403  // Query runs query against wallet credential contents and returns presentation containing credential results.
   404  //
   405  // This function may return multiple presentations as query result based on combination of query types used.
   406  //
   407  // https://w3c-ccg.github.io/universal-wallet-interop-spec/#query
   408  //
   409  // Supported Query Types:
   410  //   - https://www.w3.org/TR/json-ld11-framing
   411  //   - https://identity.foundation/presentation-exchange
   412  //   - https://w3c-ccg.github.io/vp-request-spec/#query-by-example
   413  //   - https://w3c-ccg.github.io/vp-request-spec/#did-authentication-request
   414  func (c *Wallet) Query(authToken string, params ...*QueryParams) ([]*verifiable.Presentation, error) {
   415  	vcContents, err := c.contents.GetAll(authToken, Credential)
   416  	if err != nil {
   417  		return nil, fmt.Errorf("failed to query credentials: %w", err)
   418  	}
   419  
   420  	query := NewQuery(verifiable.NewVDRKeyResolver(newContentBasedVDR(authToken, c.vdr, c.contents)).PublicKeyFetcher(),
   421  		c.jsonldDocumentLoader, params...)
   422  
   423  	return query.PerformQuery(vcContents)
   424  }
   425  
   426  // Issue adds proof to a Verifiable Credential.
   427  //
   428  //	Args:
   429  //		- auth token for unlocking kms.
   430  //		- A verifiable credential with or without proof.
   431  //		- Proof options.
   432  func (c *Wallet) Issue(authToken string, credential json.RawMessage,
   433  	options *ProofOptions) (*verifiable.Credential, error) {
   434  	vc, err := verifiable.ParseCredential(credential, verifiable.WithDisabledProofCheck(),
   435  		verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader))
   436  	if err != nil {
   437  		return nil, fmt.Errorf("failed to parse credential: %w", err)
   438  	}
   439  
   440  	purpose := did.AssertionMethod
   441  
   442  	err = c.validateProofOption(authToken, options, purpose)
   443  	if err != nil {
   444  		return nil, fmt.Errorf("failed to prepare proof: %w", err)
   445  	}
   446  
   447  	switch options.ProofFormat {
   448  	case ExternalJWTProofFormat:
   449  		claims, e := vc.JWTClaims(false)
   450  		if e != nil {
   451  			return nil, fmt.Errorf("failed to generate JWT claims for VC: %w", e)
   452  		}
   453  
   454  		jws, e := c.verifiableClaimsToJWT(authToken, claims, options)
   455  		if e != nil {
   456  			return nil, fmt.Errorf("failed to generate JWT VC: %w", e)
   457  		}
   458  
   459  		vc.JWT = jws
   460  	default: // default case is EmbeddedLDProofFormat
   461  		err = c.addLinkedDataProof(authToken, vc, options, purpose)
   462  		if err != nil {
   463  			return nil, fmt.Errorf("failed to issue credential: %w", err)
   464  		}
   465  	}
   466  
   467  	return vc, nil
   468  }
   469  
   470  // Prove produces a Verifiable Presentation.
   471  //
   472  //	Args:
   473  //		- auth token for unlocking kms.
   474  //		- list of interfaces (string of credential IDs which can be resolvable to stored credentials in wallet or
   475  //		raw credential or a presentation).
   476  //		- proof options
   477  func (c *Wallet) Prove(authToken string, proofOptions *ProofOptions, credentials ...ProveOptions) (*verifiable.Presentation, error) { //nolint: lll
   478  	presentation, err := c.resolveOptionsToPresent(authToken, credentials...)
   479  	if err != nil {
   480  		return nil, fmt.Errorf("failed to resolve credentials from request: %w", err)
   481  	}
   482  
   483  	purpose := did.Authentication
   484  
   485  	err = c.validateProofOption(authToken, proofOptions, purpose)
   486  	if err != nil {
   487  		return nil, fmt.Errorf("failed to prepare proof: %w", err)
   488  	}
   489  
   490  	presentation.Holder = proofOptions.Controller
   491  
   492  	switch proofOptions.ProofFormat {
   493  	case ExternalJWTProofFormat:
   494  		// TODO: look into passing audience identifier
   495  		//  https://github.com/hyperledger/aries-framework-go/issues/3354
   496  		claims, e := presentation.JWTClaims(nil, false)
   497  		if e != nil {
   498  			return nil, fmt.Errorf("failed to generate JWT claims for VP: %w", e)
   499  		}
   500  
   501  		jws, e := c.verifiableClaimsToJWT(authToken, claims, proofOptions)
   502  		if e != nil {
   503  			return nil, fmt.Errorf("failed to generate JWT VP: %w", e)
   504  		}
   505  
   506  		presentation.JWT = jws
   507  	default: // default case is EmbeddedLDProofFormat
   508  		err = c.addLinkedDataProof(authToken, presentation, proofOptions, purpose)
   509  		if err != nil {
   510  			return nil, fmt.Errorf("failed to prove credentials: %w", err)
   511  		}
   512  	}
   513  
   514  	return presentation, nil
   515  }
   516  
   517  // Verify takes Takes a Verifiable Credential or Verifiable Presentation as input,.
   518  //
   519  //	Args:
   520  //		- verification option for sending different models (stored credential ID, raw credential, raw presentation).
   521  //
   522  // Returns: a boolean verified, and an error if verified is false.
   523  func (c *Wallet) Verify(authToken string, options VerificationOption) (bool, error) {
   524  	requestOpts := &verifyOpts{}
   525  
   526  	options(requestOpts)
   527  
   528  	switch {
   529  	case requestOpts.credentialID != "":
   530  		raw, err := c.contents.Get(authToken, requestOpts.credentialID, Credential)
   531  		if err != nil {
   532  			return false, fmt.Errorf("failed to get credential: %w", err)
   533  		}
   534  
   535  		return c.verifyCredential(authToken, raw)
   536  	case len(requestOpts.rawCredential) > 0:
   537  		return c.verifyCredential(authToken, requestOpts.rawCredential)
   538  	case len(requestOpts.rawPresentation) > 0:
   539  		return c.verifyPresentation(authToken, requestOpts.rawPresentation)
   540  	default:
   541  		return false, fmt.Errorf("invalid verify request")
   542  	}
   543  }
   544  
   545  // Derive derives a credential and returns response credential.
   546  //
   547  //	Args:
   548  //		- credential to derive (ID of the stored credential, raw credential or credential instance).
   549  //		- derive options.
   550  func (c *Wallet) Derive(authToken string, credential CredentialToDerive, options *DeriveOptions) (*verifiable.Credential, error) { //nolint: lll
   551  	vc, err := c.resolveCredentialToDerive(authToken, credential)
   552  	if err != nil {
   553  		return nil, fmt.Errorf("failed to resolve request : %w", err)
   554  	}
   555  
   556  	derived, err := vc.GenerateBBSSelectiveDisclosure(options.Frame, []byte(options.Nonce),
   557  		verifiable.WithPublicKeyFetcher(
   558  			verifiable.NewVDRKeyResolver(newContentBasedVDR(authToken, c.vdr, c.contents)).PublicKeyFetcher(),
   559  		), verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader))
   560  	if err != nil {
   561  		return nil, fmt.Errorf("failed to derive credential : %w", err)
   562  	}
   563  
   564  	return derived, nil
   565  }
   566  
   567  // CreateKeyPair creates key pair inside a wallet.
   568  //
   569  //	Args:
   570  //		- authToken: authorization for performing create key pair operation.
   571  //		- keyType: type of the key to be created.
   572  func (c *Wallet) CreateKeyPair(authToken string, keyType kms.KeyType) (*KeyPair, error) {
   573  	session, err := sessionManager().getSession(authToken)
   574  	if err != nil {
   575  		return nil, err
   576  	}
   577  
   578  	kid, pubBytes, err := session.KeyManager.CreateAndExportPubKeyBytes(keyType)
   579  	if err != nil {
   580  		return nil, err
   581  	}
   582  
   583  	return &KeyPair{
   584  		KeyID:     kid,
   585  		PublicKey: base64.RawURLEncoding.EncodeToString(pubBytes),
   586  	}, nil
   587  }
   588  
   589  // ResolveCredentialManifest resolves given credential manifest by credential response or credential.
   590  // Supports: https://identity.foundation/credential-manifest/
   591  //
   592  // Args:
   593  //   - authToken: authorization for performing operation.
   594  //   - manifest: Credential manifest data model in raw format.
   595  //   - resolve: options to provide credential response or credential to resolve.
   596  //
   597  // Returns:
   598  //   - list of resolved descriptors.
   599  //   - error if operation fails.
   600  func (c *Wallet) ResolveCredentialManifest(authToken string, manifest json.RawMessage, resolve ResolveManifestOption) ([]*cm.ResolvedDescriptor, error) { //nolint: lll,gocyclo
   601  	credentialManifest := &cm.CredentialManifest{}
   602  
   603  	err := credentialManifest.UnmarshalJSON(manifest)
   604  	if err != nil {
   605  		return nil, fmt.Errorf("failed to read credential manifest: %w", err)
   606  	}
   607  
   608  	opts := &resolveManifestOpts{}
   609  
   610  	if resolve != nil {
   611  		resolve(opts)
   612  	}
   613  
   614  	switch {
   615  	case len(opts.rawResponse) > 0:
   616  		opts.response, err = verifiable.ParsePresentation(opts.rawResponse,
   617  			verifiable.WithPresDisabledProofCheck(),
   618  			verifiable.WithPresJSONLDDocumentLoader(c.jsonldDocumentLoader))
   619  		if err != nil {
   620  			return nil, err
   621  		}
   622  
   623  		fallthrough
   624  	case opts.response != nil:
   625  		return credentialManifest.ResolveResponse(opts.response)
   626  	case opts.credentialID != "":
   627  		opts.rawCredential, err = c.Get(authToken, Credential, opts.credentialID)
   628  		if err != nil {
   629  			return nil, fmt.Errorf("failed to get credential to resolve from wallet: %w", err)
   630  		}
   631  
   632  		fallthrough
   633  	case len(opts.rawCredential) > 0:
   634  		opts.credential, err = verifiable.ParseCredential(opts.rawCredential, verifiable.WithDisabledProofCheck(),
   635  			verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader))
   636  		if err != nil {
   637  			return nil, err
   638  		}
   639  
   640  		resolved, err := credentialManifest.ResolveCredential(opts.descriptorID,
   641  			cm.RawCredentialToResolve(opts.rawCredential))
   642  		if err != nil {
   643  			return nil, fmt.Errorf("failed to resolve raw credential by descriptor ID '%s':  %w",
   644  				opts.descriptorID, err)
   645  		}
   646  
   647  		return []*cm.ResolvedDescriptor{resolved}, nil
   648  	case opts.credential != nil:
   649  		resolved, err := credentialManifest.ResolveCredential(opts.descriptorID,
   650  			cm.CredentialToResolve(opts.credential))
   651  		if err != nil {
   652  			return nil, fmt.Errorf("failed to resolve given credential by descriptor ID '%s' : %w",
   653  				opts.descriptorID, err)
   654  		}
   655  
   656  		return []*cm.ResolvedDescriptor{resolved}, nil
   657  	default:
   658  		return nil, errors.New("failed to resolve credential manifest, invalid option")
   659  	}
   660  }
   661  
   662  // nolint: funlen,gocyclo
   663  func (c *Wallet) resolveOptionsToPresent(auth string, credentials ...ProveOptions) (*verifiable.Presentation, error) {
   664  	var allCredentials []*verifiable.Credential
   665  
   666  	opts := &proveOpts{}
   667  
   668  	for _, opt := range credentials {
   669  		opt(opts)
   670  	}
   671  
   672  	for _, id := range opts.storedCredentials {
   673  		raw, err := c.contents.Get(auth, id, Credential)
   674  		if err != nil {
   675  			return nil, err
   676  		}
   677  
   678  		// proof check is disabled while resolving credentials from store. A wallet UI may or may not choose to
   679  		// show credentials as verified. If a wallet implementation chooses to show credentials as 'verified' it
   680  		// may to call 'wallet.Verify()' for each credential being presented.
   681  		// (More details can be found in issue #2677).
   682  		credential, err := verifiable.ParseCredential(raw, verifiable.WithDisabledProofCheck(),
   683  			verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader))
   684  		if err != nil {
   685  			return nil, err
   686  		}
   687  
   688  		allCredentials = append(allCredentials, credential)
   689  	}
   690  
   691  	for _, raw := range opts.rawCredentials {
   692  		// proof check is disabled while resolving credentials from raw bytes. A wallet UI may or may not choose to
   693  		// show credentials as verified. If a wallet implementation chooses to show credentials as 'verified' it
   694  		// may to call 'wallet.Verify()' for each credential being presented.
   695  		// (More details can be found in issue #2677).
   696  		credential, err := verifiable.ParseCredential(raw, verifiable.WithDisabledProofCheck(),
   697  			verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader))
   698  		if err != nil {
   699  			return nil, err
   700  		}
   701  
   702  		allCredentials = append(allCredentials, credential)
   703  	}
   704  
   705  	if len(opts.credentials) > 0 {
   706  		allCredentials = append(allCredentials, opts.credentials...)
   707  	}
   708  
   709  	if opts.presentation != nil {
   710  		opts.presentation.AddCredentials(allCredentials...)
   711  
   712  		return opts.presentation, nil
   713  	} else if len(opts.rawPresentation) > emptyRawLength {
   714  		vp, err := verifiable.ParsePresentation(opts.rawPresentation, verifiable.WithPresDisabledProofCheck(),
   715  			verifiable.WithPresJSONLDDocumentLoader(c.jsonldDocumentLoader))
   716  		if err != nil {
   717  			return nil, err
   718  		}
   719  
   720  		vp.AddCredentials(allCredentials...)
   721  
   722  		return vp, nil
   723  	}
   724  
   725  	return verifiable.NewPresentation(verifiable.WithCredentials(allCredentials...))
   726  }
   727  
   728  func (c *Wallet) resolveCredentialToDerive(auth string, credential CredentialToDerive) (*verifiable.Credential, error) {
   729  	opts := &deriveOpts{}
   730  
   731  	credential(opts)
   732  
   733  	if opts.credential != nil {
   734  		return opts.credential, nil
   735  	}
   736  
   737  	if len(opts.rawCredential) > 0 {
   738  		// proof check is disabled while resolving credentials from store. A wallet UI may or may not choose to
   739  		// show credentials as verified. If a wallet implementation chooses to show credentials as 'verified' it
   740  		// may to call 'wallet.Verify()' for each credential being presented.
   741  		// (More details can be found in issue #2677).
   742  		return verifiable.ParseCredential(opts.rawCredential, verifiable.WithDisabledProofCheck(),
   743  			verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader))
   744  	}
   745  
   746  	if opts.credentialID != "" {
   747  		raw, err := c.contents.Get(auth, opts.credentialID, Credential)
   748  		if err != nil {
   749  			return nil, err
   750  		}
   751  
   752  		// proof check is disabled while resolving credentials from store. A wallet UI may or may not choose to
   753  		// show credentials as verified. If a wallet implementation chooses to show credentials as 'verified' it
   754  		// may to call 'wallet.Verify()' for each credential being presented.
   755  		// (More details can be found in issue #2677).
   756  		return verifiable.ParseCredential(raw, verifiable.WithDisabledProofCheck(),
   757  			verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader))
   758  	}
   759  
   760  	return nil, errors.New("invalid request to derive credential")
   761  }
   762  
   763  func (c *Wallet) verifyCredential(authToken string, credential json.RawMessage) (bool, error) {
   764  	_, err := verifiable.ParseCredential(credential, verifiable.WithPublicKeyFetcher(
   765  		verifiable.NewVDRKeyResolver(newContentBasedVDR(authToken, c.vdr, c.contents)).PublicKeyFetcher(),
   766  	), verifiable.WithJSONLDDocumentLoader(c.jsonldDocumentLoader))
   767  	if err != nil {
   768  		return false, fmt.Errorf("credential verification failed: %w", err)
   769  	}
   770  
   771  	return true, nil
   772  }
   773  
   774  func (c *Wallet) verifyPresentation(authToken string, presentation json.RawMessage) (bool, error) {
   775  	vp, err := verifiable.ParsePresentation(presentation, verifiable.WithPresPublicKeyFetcher(
   776  		verifiable.NewVDRKeyResolver(newContentBasedVDR(authToken, c.vdr, c.contents)).PublicKeyFetcher(),
   777  	), verifiable.WithPresJSONLDDocumentLoader(c.jsonldDocumentLoader))
   778  	if err != nil {
   779  		return false, fmt.Errorf("presentation verification failed: %w", err)
   780  	}
   781  
   782  	// verify proof of each credential
   783  	for _, cred := range vp.Credentials() {
   784  		vc, err := json.Marshal(cred)
   785  		if err != nil {
   786  			return false, fmt.Errorf("failed to read credentials from presentation: %w", err)
   787  		}
   788  
   789  		_, err = c.verifyCredential(authToken, vc)
   790  		if err != nil {
   791  			return false, fmt.Errorf("presentation verification failed: %w", err)
   792  		}
   793  	}
   794  
   795  	return true, nil
   796  }
   797  
   798  func (c *Wallet) verifiableClaimsToJWT(authToken string, claims jwtClaims, options *ProofOptions) (string, error) {
   799  	s, err := newKMSSigner(authToken, c.walletCrypto, options)
   800  	if err != nil {
   801  		return "", fmt.Errorf("initializing signer: %w", err)
   802  	}
   803  
   804  	var alg verifiable.JWSAlgorithm
   805  
   806  	switch s.KeyType {
   807  	case kms.ED25519Type:
   808  		alg = verifiable.EdDSA
   809  	case kms.ECDSAP256TypeIEEEP1363:
   810  		alg = verifiable.ECDSASecp256r1
   811  	case kms.ECDSAP384TypeIEEEP1363:
   812  		alg = verifiable.ECDSASecp384r1
   813  	case kms.ECDSAP521TypeIEEEP1363:
   814  		alg = verifiable.ECDSASecp521r1
   815  	default:
   816  		return "", fmt.Errorf("unsupported keytype for JWT")
   817  	}
   818  
   819  	jws, err := claims.MarshalJWS(alg, s, options.VerificationMethod)
   820  	if err != nil {
   821  		return "", fmt.Errorf("failed to sign JWS: %w", err)
   822  	}
   823  
   824  	return jws, nil
   825  }
   826  
   827  func (c *Wallet) addLinkedDataProof(authToken string, p provable, opts *ProofOptions,
   828  	relationship did.VerificationRelationship) error {
   829  	s, err := newKMSSigner(authToken, c.walletCrypto, opts)
   830  	if err != nil {
   831  		return err
   832  	}
   833  
   834  	var signatureSuite signer.SignatureSuite
   835  
   836  	switch opts.ProofType {
   837  	case Ed25519Signature2018:
   838  		signatureSuite = ed25519signature2018.New(suite.WithSigner(s))
   839  	case JSONWebSignature2020:
   840  		signatureSuite = jsonwebsignature2020.New(suite.WithSigner(s))
   841  	case BbsBlsSignature2020:
   842  		addContext(p, bbsContext)
   843  
   844  		signatureSuite = bbsblssignature2020.New(suite.WithSigner(s))
   845  	default:
   846  		return fmt.Errorf("unsupported signature type '%s'", opts.ProofType)
   847  	}
   848  
   849  	signingCtx := &verifiable.LinkedDataProofContext{
   850  		VerificationMethod:      opts.VerificationMethod,
   851  		SignatureRepresentation: *opts.ProofRepresentation,
   852  		SignatureType:           opts.ProofType,
   853  		Suite:                   signatureSuite,
   854  		Created:                 opts.Created,
   855  		Domain:                  opts.Domain,
   856  		Challenge:               opts.Challenge,
   857  		Purpose:                 supportedRelationships[relationship],
   858  	}
   859  
   860  	err = p.AddLinkedDataProof(signingCtx, jsonld.WithDocumentLoader(c.jsonldDocumentLoader))
   861  	if err != nil {
   862  		return fmt.Errorf("failed to add linked data proof: %w", err)
   863  	}
   864  
   865  	return nil
   866  }
   867  
   868  func (c *Wallet) validateProofOption(authToken string, opts *ProofOptions, method did.VerificationRelationship) error {
   869  	if opts == nil || opts.Controller == "" {
   870  		return errors.New("invalid proof option, 'controller' is required")
   871  	}
   872  
   873  	resolvedDoc, err := newContentBasedVDR(authToken, c.vdr, c.contents).Resolve(opts.Controller)
   874  	if err != nil {
   875  		return err
   876  	}
   877  
   878  	err = c.validateVerificationMethod(resolvedDoc.DIDDocument, opts, method)
   879  	if err != nil {
   880  		return err
   881  	}
   882  
   883  	if opts.ProofFormat == "" {
   884  		opts.ProofFormat = EmbeddedLDProofFormat
   885  	}
   886  
   887  	if opts.ProofRepresentation == nil {
   888  		opts.ProofRepresentation = &defaultSignatureRepresentation
   889  	}
   890  
   891  	if opts.ProofType == "" {
   892  		opts.ProofType = Ed25519Signature2018
   893  	}
   894  
   895  	return nil
   896  }
   897  
   898  func (c *Wallet) validateVerificationMethod(didDoc *did.Doc, opts *ProofOptions,
   899  	relationship did.VerificationRelationship) error {
   900  	vms := didDoc.VerificationMethods(relationship)[relationship]
   901  
   902  	for _, vm := range vms {
   903  		if opts.VerificationMethod == "" {
   904  			opts.VerificationMethod = vm.VerificationMethod.ID
   905  			return nil
   906  		}
   907  
   908  		if opts.VerificationMethod == vm.VerificationMethod.ID {
   909  			return nil
   910  		}
   911  	}
   912  
   913  	return fmt.Errorf("unable to find '%s' for given verification method", supportedRelationships[relationship])
   914  }
   915  
   916  // addContext adds context if not found in given data model.
   917  func addContext(v interface{}, ldcontext string) {
   918  	if vc, ok := v.(*verifiable.Credential); ok {
   919  		for _, ctx := range vc.Context {
   920  			if ctx == ldcontext {
   921  				return
   922  			}
   923  		}
   924  
   925  		vc.Context = append(vc.Context, ldcontext)
   926  	}
   927  }
   928  
   929  func updateProfile(keyManager kms.KeyManager, profile *profile) error {
   930  	// setup key pairs
   931  	err := profile.setupEDVEncryptionKey(keyManager)
   932  	if err != nil {
   933  		return fmt.Errorf("failed to create EDV encryption key pair: %w", err)
   934  	}
   935  
   936  	err = profile.setupEDVMacKey(keyManager)
   937  	if err != nil {
   938  		return fmt.Errorf("failed to create EDV MAC key pair: %w", err)
   939  	}
   940  
   941  	return nil
   942  }