github.com/hyperledger/aries-framework-go@v0.3.2/pkg/wallet/profile.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/json"
    11  	"errors"
    12  	"fmt"
    13  
    14  	"github.com/google/uuid"
    15  
    16  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    17  	"github.com/hyperledger/aries-framework-go/pkg/secretlock"
    18  	"github.com/hyperledger/aries-framework-go/spi/storage"
    19  )
    20  
    21  const (
    22  	profileStoreName          = "vcwallet_profiles"
    23  	profileStoreUserKeyPrefix = "vcwallet_usr_%s"
    24  )
    25  
    26  // ErrProfileNotFound error for wallet profile not found scenario.
    27  var ErrProfileNotFound = errors.New("profile does not exist")
    28  
    29  // profile of VC wallet contains wallet specific settings of wallet user to be remembered.
    30  type profile struct {
    31  	// ID unique identifier assigned to this wallet profile.
    32  	ID string
    33  
    34  	// User ID of the wallet profile user.
    35  	User string
    36  
    37  	// Encrypted MasterLock is for localkms.
    38  	MasterLockCipher string
    39  
    40  	// KeyServerURL for remotekms.
    41  	KeyServerURL string
    42  
    43  	// EDV configuration
    44  	EDVConf *edvConf
    45  }
    46  
    47  type edvConf struct {
    48  	// ServerURL for encrypted data vault storage of wallet contents.
    49  	ServerURL string
    50  
    51  	// VaultID for encrypted data vault storage of wallet contents.
    52  	VaultID string
    53  
    54  	// Key ID for encryption key for EDV.
    55  	EncryptionKeyID string
    56  
    57  	// Key ID for MAC key for EDV.
    58  	MACKeyID string
    59  }
    60  
    61  // createProfile creates new verifiable credential wallet profile for given user and saves it in store.
    62  // This profile is required for creating verifiable credential wallet client.
    63  func createProfile(user string, opts *profileOpts) (*profile, error) {
    64  	profile := &profile{User: user, ID: uuid.New().String()}
    65  
    66  	err := profile.setKMSOptions(opts.passphrase, opts.secretLockSvc, opts.keyServerURL)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	err = profile.setEDVOptions(opts.edvConf)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	return profile, nil
    77  }
    78  
    79  func (pr *profile) setKMSOptions(passphrase string, secretLockSvc secretlock.Service, keyServerURL string) error {
    80  	pr.resetKMSOptions()
    81  
    82  	var err error
    83  
    84  	switch {
    85  	case passphrase != "":
    86  		// localkms with passphrase
    87  		secretLockSvc, err = getDefaultSecretLock(passphrase)
    88  		if err != nil {
    89  			return err
    90  		}
    91  
    92  		pr.MasterLockCipher, err = createMasterLock(secretLockSvc)
    93  		if err != nil {
    94  			return err
    95  		}
    96  	case secretLockSvc != nil:
    97  		// localkms with secret lock service
    98  		pr.MasterLockCipher, err = createMasterLock(secretLockSvc)
    99  		if err != nil {
   100  			return err
   101  		}
   102  	case keyServerURL != "":
   103  		// remotekms
   104  		pr.KeyServerURL = keyServerURL
   105  	default:
   106  		return fmt.Errorf("invalid create profile options")
   107  	}
   108  
   109  	return nil
   110  }
   111  
   112  func (pr *profile) setEDVOptions(opts *edvConf) error {
   113  	if opts == nil {
   114  		return nil
   115  	}
   116  
   117  	if opts.ServerURL == "" || opts.VaultID == "" {
   118  		return errors.New("invalid EDV settings in profile")
   119  	}
   120  
   121  	pr.EDVConf = opts
   122  
   123  	return nil
   124  }
   125  
   126  func (pr *profile) setupEDVEncryptionKey(keyManager kms.KeyManager) error {
   127  	kid, _, err := keyManager.Create(kms.NISTP256ECDHKWType)
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	pr.EDVConf.EncryptionKeyID = kid
   133  
   134  	return nil
   135  }
   136  
   137  func (pr *profile) setupEDVMacKey(keyManager kms.KeyManager) error {
   138  	kid, _, err := keyManager.Create(kms.HMACSHA256Tag256Type)
   139  	if err != nil {
   140  		return err
   141  	}
   142  
   143  	pr.EDVConf.MACKeyID = kid
   144  
   145  	return nil
   146  }
   147  
   148  func (pr *profile) resetKMSOptions() {
   149  	pr.KeyServerURL = ""
   150  	pr.MasterLockCipher = ""
   151  }
   152  
   153  // getUserKeyPrefix is key prefix for vc wallet profile store user key.
   154  func getUserKeyPrefix(user string) string {
   155  	return fmt.Sprintf(profileStoreUserKeyPrefix, user)
   156  }
   157  
   158  // newProfileStore creates new profile store.
   159  func newProfileStore(provider storage.Provider) (*profileStore, error) {
   160  	store, err := provider.OpenStore(profileStoreName)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	return &profileStore{store: store}, nil
   166  }
   167  
   168  // profileStore is store for vc wallet profiles contains unique collection of user profile info.
   169  // key --> userID, val --> profile.
   170  type profileStore struct {
   171  	store storage.Store
   172  }
   173  
   174  // getProfile gets profile from store.
   175  func (p *profileStore) get(user string) (*profile, error) {
   176  	profileBytes, err := p.store.Get(getUserKeyPrefix(user))
   177  	if err != nil {
   178  		if errors.Is(err, storage.ErrDataNotFound) {
   179  			return nil, ErrProfileNotFound
   180  		}
   181  
   182  		return nil, err
   183  	}
   184  
   185  	var result profile
   186  
   187  	err = json.Unmarshal(profileBytes, &result)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  
   192  	return &result, nil
   193  }
   194  
   195  // save saves profile into store,
   196  // if argument 'override=true' then replaces existing profile else returns error.
   197  func (p *profileStore) save(val *profile, override bool) error {
   198  	if !override {
   199  		profileBytes, _ := p.get(val.User) //nolint: errcheck
   200  		if profileBytes != nil {
   201  			return fmt.Errorf("profile already exists for given user")
   202  		}
   203  	}
   204  
   205  	profileBytes, err := json.Marshal(val)
   206  	if err != nil {
   207  		return err
   208  	}
   209  
   210  	return p.store.Put(getUserKeyPrefix(val.User), profileBytes)
   211  }