Skip to content

Commit

Permalink
feat: apis to create keys in wallet kms
Browse files Browse the repository at this point in the history
-EDV encryption & MAC key IDs cannot be passed as profile settings for
localkms users since wallet creates local kms internally based on user's
secret lock service. So wallet has to expose an API to generate EDV keys
in wallet kms.

- Closes hyperledger-archives#2768

Signed-off-by: sudesh.shetty <sudesh.shetty@securekey.com>
  • Loading branch information
sudeshrshetty committed Apr 29, 2021
1 parent c5e9786 commit f971fcc
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 82 deletions.
3 changes: 3 additions & 0 deletions pkg/controller/command/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ const (

// Outofband error group for outofband command errors.
Outofband = 11000

// VCWallet error group for verifiable Credential wallet command errors.
VCWallet = 12000
)

// Error is the interface for representing an command error condition, with the nil value representing no error.
Expand Down
5 changes: 5 additions & 0 deletions pkg/mock/kms/mock_kms.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type KeyManager struct {
CreateKeyID string
CreateKeyValue *keyset.Handle
CreateKeyErr error
CreateKeyFn func(kt kmsservice.KeyType) (string, interface{}, error)
GetKeyValue *keyset.Handle
GetKeyErr error
RotateKeyID string
Expand All @@ -47,6 +48,10 @@ func (k *KeyManager) Create(kt kmsservice.KeyType) (string, interface{}, error)
return "", nil, k.CreateKeyErr
}

if k.CreateKeyFn != nil {
return k.CreateKeyFn(kt)
}

return k.CreateKeyID, k.CreateKeyValue, nil
}

Expand Down
10 changes: 8 additions & 2 deletions pkg/wallet/contents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,15 @@ func TestContentStores(t *testing.T) {
require.NoError(t, err)
require.NotEmpty(t, tkn)

ok, err := profileInfo.setupEDVKeys(tkn, "", "")
kmgr, err := keyManager().getKeyManger(tkn)
require.NoError(t, err)
require.NotEmpty(t, kmgr)

err = profileInfo.setupEDVEncryptionKey(kmgr)
require.NoError(t, err)

err = profileInfo.setupEDVMacKey(kmgr)
require.NoError(t, err)
require.True(t, ok)

// create new store
contentStore := newContentStore(sp, profileInfo)
Expand Down
50 changes: 13 additions & 37 deletions pkg/wallet/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,11 @@ func (pr *profile) setKMSOptions(passphrase string, secretLockSvc secretlock.Ser
}

func (pr *profile) setEDVOptions(opts *edvConf) error {
// non EDV users.
if opts == nil {
return nil
}

if opts.ServerURL == "" || opts.VaultID == "" || opts.EncryptionKeyID == "" || opts.MACKeyID == "" {
if opts.ServerURL == "" || opts.VaultID == "" {
return errors.New("invalid EDV settings in profile")
}

Expand All @@ -124,49 +123,26 @@ func (pr *profile) setEDVOptions(opts *edvConf) error {
return nil
}

// nolint:gocyclo
func (pr *profile) setupEDVKeys(auth string, encryptionKeyType, macKeyType kms.KeyType) (bool, error) {
setupEncKey := pr.EDVConf != nil && pr.EDVConf.EncryptionKeyID == ""
setupMacKey := pr.EDVConf != nil && pr.EDVConf.MACKeyID == ""

if !(setupEncKey || setupMacKey) {
return false, nil
}

keyMgr, err := keyManager().getKeyManger(auth)
func (pr *profile) setupEDVEncryptionKey(keyManager kms.KeyManager) error {
kid, _, err := keyManager.Create(kms.NISTP256ECDHKWType)
if err != nil {
return false, err
return err
}

// if encryption key is not yet setup then create one and set.
if setupEncKey {
if encryptionKeyType == "" {
encryptionKeyType = kms.NISTP256ECDHKWType
}
pr.EDVConf.EncryptionKeyID = kid

kid, _, err := keyMgr.Create(encryptionKeyType)
if err != nil {
return false, err
}
return nil
}

pr.EDVConf.EncryptionKeyID = kid
func (pr *profile) setupEDVMacKey(keyManager kms.KeyManager) error {
kid, _, err := keyManager.Create(kms.HMACSHA256Tag256Type)
if err != nil {
return err
}

// if MAC key is not yet setup then create one and set.
if setupMacKey {
if macKeyType == "" {
macKeyType = kms.HMACSHA256Tag256Type
}
pr.EDVConf.MACKeyID = kid

kid, _, err := keyMgr.Create(macKeyType)
if err != nil {
return false, err
}

pr.EDVConf.MACKeyID = kid
}

return true, nil
return nil
}

func (pr *profile) resetKMSOptions() {
Expand Down
26 changes: 5 additions & 21 deletions pkg/wallet/profile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/require"

kmsapi "github.com/hyperledger/aries-framework-go/pkg/kms"
mockkms "github.com/hyperledger/aries-framework-go/pkg/mock/kms"
"github.com/hyperledger/aries-framework-go/pkg/mock/secretlock"
mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage"
Expand Down Expand Up @@ -136,37 +135,22 @@ func TestCreateNewProfile(t *testing.T) {
require.NoError(t, keyManager().saveKeyManger(uuid.New().String(), token, kms, 500*time.Millisecond))

// setup edv keys
ok, err := sampleProfile.setupEDVKeys(token, kmsapi.NISTP256ECDHKWType, kmsapi.HMACSHA256Tag256Type)
err := sampleProfile.setupEDVEncryptionKey(&mockkms.KeyManager{CreateKeyID: kid})
require.NoError(t, err)
require.True(t, ok)
require.Equal(t, sampleProfile.EDVConf.EncryptionKeyID, kid)
require.Equal(t, sampleProfile.EDVConf.MACKeyID, kid)

// no update
ok, err = sampleProfile.setupEDVKeys(token, "", "")
err = sampleProfile.setupEDVMacKey(&mockkms.KeyManager{CreateKeyID: kid})
require.NoError(t, err)
require.False(t, ok)
require.Equal(t, sampleProfile.EDVConf.MACKeyID, kid)

// test create key error
require.NoError(t, keyManager().saveKeyManger(uuid.New().String(), token,
&mockkms.KeyManager{CreateKeyErr: errors.New(sampleKeyMgrErr)}, 500*time.Millisecond))

sampleProfile.EDVConf.MACKeyID = ""
ok, err = sampleProfile.setupEDVKeys(token, "", "")
err = sampleProfile.setupEDVEncryptionKey(&mockkms.KeyManager{CreateKeyErr: errors.New(sampleKeyMgrErr)})
require.Error(t, err)
require.Contains(t, err.Error(), sampleKeyMgrErr)
require.False(t, ok)

sampleProfile.EDVConf.EncryptionKeyID = ""
ok, err = sampleProfile.setupEDVKeys(token, "", "")
err = sampleProfile.setupEDVMacKey(&mockkms.KeyManager{CreateKeyErr: errors.New(sampleKeyMgrErr)})
require.Error(t, err)
require.Contains(t, err.Error(), sampleKeyMgrErr)
require.False(t, ok)

// invalid auth
ok, err = sampleProfile.setupEDVKeys(token+"invalid", "", "")
require.Error(t, err)
require.False(t, ok)
})
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/wallet/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ func (s *storageProvider) OpenStore(auth string, opts *unlockOpts, config storag
}

func createEDVStorageProvider(auth string, conf *edvConf, opts *unlockOpts) (storage.Provider, error) {
if conf.EncryptionKeyID == "" || conf.MACKeyID == "" {
return nil, errors.New("invalid EDV configuration found in wallet profile, key IDs for encryption and MAC operations are missing") //nolint: lll
}

// get key manager
keyMgr, err := keyManager().getKeyManger(auth)
if err != nil {
Expand Down
32 changes: 26 additions & 6 deletions pkg/wallet/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,15 @@ func TestStorageProvider_OpenStore(t *testing.T) {
},
}

ok, err := sampleProfile.setupEDVKeys(token, "", "")
kmgr, err := keyManager().getKeyManger(token)
require.NoError(t, err)
require.NotEmpty(t, kmgr)

err = sampleProfile.setupEDVEncryptionKey(kmgr)
require.NoError(t, err)

err = sampleProfile.setupEDVMacKey(kmgr)
require.NoError(t, err)
require.True(t, ok)

wsp := newWalletStorageProvider(sampleProfile, nil)

Expand Down Expand Up @@ -96,14 +102,28 @@ func TestStorageProvider_OpenStore(t *testing.T) {
},
}

ok, err := sampleProfile.setupEDVKeys(token, "", "")
// invalid settings
wsp := newWalletStorageProvider(sampleProfile, nil)
store, err := wsp.OpenStore(token, &unlockOpts{},
storage.StoreConfiguration{TagNames: []string{Credential.Name()}})
require.Error(t, err)
require.Contains(t, err.Error(), "invalid EDV configuration found in wallet profile")
require.Empty(t, store)

kmgr, err := keyManager().getKeyManger(token)
require.NoError(t, err)
require.True(t, ok)
require.NotEmpty(t, kmgr)

wsp := newWalletStorageProvider(sampleProfile, nil)
err = sampleProfile.setupEDVEncryptionKey(kmgr)
require.NoError(t, err)

err = sampleProfile.setupEDVMacKey(kmgr)
require.NoError(t, err)

wsp = newWalletStorageProvider(sampleProfile, nil)

// invalid auth
store, err := wsp.OpenStore(token+".", &unlockOpts{},
store, err = wsp.OpenStore(token+".", &unlockOpts{},
storage.StoreConfiguration{TagNames: []string{Credential.Name()}})
require.Error(t, err)
require.True(t, errors.Is(err, ErrWalletLocked))
Expand Down
74 changes: 71 additions & 3 deletions pkg/wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,60 @@ func CreateProfile(userID string, ctx provider, options ...ProfileOptions) error

// UpdateProfile updates existing verifiable credential wallet profile.
// Will create new profile if no profile exists for given user.
// Caution: - you might lose your existing keys if you change kms options.
// - you might lose your existing wallet contents if you change storage options
// (ex: switching from EDV to/from context storage provider).
// Caution:
// - you might lose your existing keys if you change kms options.
// - you might lose your existing wallet contents if you change storage/EDV options
// (ex: switching context storage provider or changing EDV settings).
func UpdateProfile(userID string, ctx provider, options ...ProfileOptions) error {
return createOrUpdate(userID, ctx, true, options...)
}

// CreateDataVaultKeyPairs can be used create EDV key pairs for given profile.
// Wallet will create key pairs in profile kms and updates profile with newly generate EDV encryption & MAC key IDs.
func CreateDataVaultKeyPairs(userID string, ctx provider, options ...UnlockOptions) error {
store, err := newProfileStore(ctx.StorageProvider())
if err != nil {
return fmt.Errorf("failed to get wallet user profile: failed to get store: %w", err)
}

profile, err := store.get(userID)
if err != nil {
return fmt.Errorf("failed to get wallet user profile: %w", err)
}

if profile.EDVConf == nil {
return fmt.Errorf("invalid operation, no edv configuration found in profile: %w", err)
}

opts := &unlockOpts{}

for _, opt := range options {
opt(opts)
}

// unlock key manager
token, err := keyManager().createKeyManager(profile, ctx.StorageProvider(), opts)
if err != nil {
return fmt.Errorf("failed to get key manager: %w", err)
}

defer keyManager().removeKeyManager(userID)

// update profile
err = updateProfile(token, profile)
if err != nil {
return fmt.Errorf("failed to create key pairs: %w", err)
}

// update profile
err = store.save(profile, true)
if err != nil {
return fmt.Errorf("failed to update profile: %w", err)
}

return nil
}

func createOrUpdate(userID string, ctx provider, update bool, options ...ProfileOptions) error {
opts := &profileOpts{}

Expand Down Expand Up @@ -681,3 +728,24 @@ func addContext(v interface{}, context string) {
vc.Context = append(vc.Context, context)
}
}

func updateProfile(auth string, profile *profile) error {
// get key manager
keyManager, err := keyManager().getKeyManger(auth)
if err != nil {
return err
}

// setup key pairs
err = profile.setupEDVEncryptionKey(keyManager)
if err != nil {
return fmt.Errorf("failed to create EDV encryption key pair: %w", err)
}

err = profile.setupEDVMacKey(keyManager)
if err != nil {
return fmt.Errorf("failed to create EDV MAC key pair: %w", err)
}

return nil
}

0 comments on commit f971fcc

Please sign in to comment.