Migrate KMS code to the new database DEKs (#324)

This commit is contained in:
Jeff Mitchell authored and GitHub committed 2020-09-03 13:23:46 -04:00
1 parent c55153ff3d
commit 1822c47ef5
17 files changed
+176 -70

No files matched your search

+2 -7
View File
@@ -26,7 +26,6 @@ import (
"github.com/hashicorp/go-hclog"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/shared-secure-libs/configutil"
"github.com/hashicorp/shared-secure-libs/gatedwriter"
"github.com/hashicorp/shared-secure-libs/reloadutil"
@@ -481,13 +480,9 @@ func (b *Server) CreateDevDatabase(dialect string, opt ...Option) error {
cancel()
}()
rootKey, err := uuid.GenerateRandomBytesWithReader(32, b.SecureRandomReader)
_, err = kms.CreateKeysTx(ctx, rw, rw, b.RootKms, b.SecureRandomReader, scope.Global.String())
if err != nil {
return fmt.Errorf("error generating random bytes for scope root key: %w", err)
}
_, _, err = kmsRepo.CreateRootKey(ctx, b.RootKms, scope.Global.String(), rootKey)
if err != nil {
return fmt.Errorf("error saving global scope root key: %w", err)
return fmt.Errorf("error creating global scope kms keys: %w", err)
}
if opts.withSkipAuthMethodCreation {
+1 -7
View File
@@ -24,13 +24,7 @@ func TestRepo(t *testing.T, conn *gorm.DB, rootWrapper wrapping.Wrapper, opt ...
kmsCache := kms.TestKms(t, conn, rootWrapper)
wrapper, err := kmsCache.GetWrapper(context.Background(), scope.Global.String(), kms.KeyPurposeOplog)
if err != nil {
var kmsRepo *kms.Repository
// Assume the root hasn't been created and try to insert it
kmsRepo, err = kms.NewRepository(rw, rw)
require.NoError(err)
rootKey, err := uuid.GenerateRandomBytesWithReader(32, rand.Reader)
require.NoError(err)
_, _, err = kmsRepo.CreateRootKey(context.Background(), rootWrapper, scope.Global.String(), rootKey)
_, err = kms.CreateKeysTx(context.Background(), rw, rw, rootWrapper, rand.Reader, scope.Global.String())
require.NoError(err)
wrapper, err = kmsCache.GetWrapper(context.Background(), scope.Global.String(), kms.KeyPurposeOplog)
if err != nil {
+114 -29
View File
@@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"hash"
"sync"
"github.com/hashicorp/boundary/internal/types/scope"
@@ -12,7 +11,6 @@ import (
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/wrappers/aead"
"github.com/hashicorp/go-kms-wrapping/wrappers/multiwrapper"
"golang.org/x/crypto/blake2b"
)
// ExternalWrappers holds wrappers defined outside of Boundary, e.g. in its
@@ -148,6 +146,10 @@ func generateKeyId(scopeId string, purpose KeyPurpose, version uint32) string {
// multiwrapper. This is not necesary for encryption but should be supplied for
// decryption.
func (k *Kms) GetWrapper(ctx context.Context, scopeId string, purpose KeyPurpose, opt ...Option) (wrapping.Wrapper, error) {
if scopeId == "" {
return nil, errors.New("no scope ID provided")
}
switch purpose {
case KeyPurposeOplog, KeyPurposeDatabase, KeyPurposeTokens, KeyPurposeSessions:
case KeyPurposeUnknown:
@@ -155,6 +157,7 @@ func (k *Kms) GetWrapper(ctx context.Context, scopeId string, purpose KeyPurpose
default:
return nil, fmt.Errorf("unsupported purpose %q", purpose)
}
opts := getOpts(opt...)
// Fast-path: we have a valid key at the scope/purpose. Verify the key with
// that ID is in the multiwrapper; if not, fall through to reload from the
@@ -172,33 +175,24 @@ func (k *Kms) GetWrapper(ctx context.Context, scopeId string, purpose KeyPurpose
// root for the scope as we'll need it to decrypt the value coming from the
// DB. We don't cache the roots as we expect that after a few calls the
// scope-purpose cache will catch everything in steady-state.
rootWrapper, err := k.loadRoot(ctx, scopeId, opt...)
rootWrapper, rootKeyId, err := k.loadRoot(ctx, scopeId, opt...)
if err != nil {
return nil, fmt.Errorf("error loading root key for scope %s: %w", scopeId, err)
}
// TODO: Look up dek in the db, then decrypt with the root wrapper. If we
// have a key ID passed in verify that we find it. For now since we don't
// have DEKs, derive a key.
baseWrapper := rootWrapper.WrapperForKeyID("__base__").(*aead.Wrapper)
derived, err := baseWrapper.NewDerivedWrapper(&aead.DerivedWrapperOptions{
KeyID: generateKeyId(scopeId, purpose, 1),
Hash: func() hash.Hash { b, _ := blake2b.New256(nil); return b },
Salt: []byte(scopeId),
Info: []byte(purpose.String()),
})
if err != nil {
return nil, fmt.Errorf("error creating derived wrapper: %w", err)
if rootWrapper == nil {
return nil, fmt.Errorf("got nil root wrapper for scope %s", scopeId)
}
// Store the looked-up value into the scope cache.
multi := multiwrapper.NewMultiWrapper(derived)
k.scopePurposeCache.Store(scopeId+purpose.String(), multi)
wrapper, err := k.loadDek(ctx, scopeId, purpose, rootWrapper, rootKeyId, opt...)
if err != nil {
return nil, fmt.Errorf("error loading %s for scope %s: %w", purpose.String(), scopeId, err)
}
k.scopePurposeCache.Store(scopeId+purpose.String(), wrapper)
return multi, nil
return wrapper, nil
}
func (k *Kms) loadRoot(ctx context.Context, scopeId string, opt ...Option) (*multiwrapper.MultiWrapper, error) {
func (k *Kms) loadRoot(ctx context.Context, scopeId string, opt ...Option) (*multiwrapper.MultiWrapper, string, error) {
opts := getOpts(opt...)
repo := opts.withRepository
if repo == nil {
@@ -206,7 +200,7 @@ func (k *Kms) loadRoot(ctx context.Context, scopeId string, opt ...Option) (*mul
}
rootKeys, err := repo.ListRootKeys(ctx)
if err != nil {
return nil, fmt.Errorf("error listing root keys: %w", err)
return nil, "", fmt.Errorf("error listing root keys: %w", err)
}
var rootKeyId string
for _, k := range rootKeys {
@@ -216,7 +210,7 @@ func (k *Kms) loadRoot(ctx context.Context, scopeId string, opt ...Option) (*mul
}
}
if rootKeyId == "" {
return nil, fmt.Errorf("error finding root key for scope %s", scopeId)
return nil, "", fmt.Errorf("error finding root key for scope %s", scopeId)
}
// Now: find the external KMS that can be used to decrypt the root values
@@ -225,21 +219,21 @@ func (k *Kms) loadRoot(ctx context.Context, scopeId string, opt ...Option) (*mul
externalWrappers := k.externalScopeCache[scope.Global.String()]
k.externalScopeCacheMutex.Unlock()
if externalWrappers == nil {
return nil, errors.New("could not find kms information at either the needed scope or global fallback")
return nil, "", errors.New("could not find kms information at either the needed scope or global fallback")
}
externalWrappers.m.RLock()
defer externalWrappers.m.RUnlock()
if externalWrappers.root == nil {
return nil, fmt.Errorf("root key wrapper for scope %s is nil", scopeId)
return nil, "", fmt.Errorf("root key wrapper for scope %s is nil", scopeId)
}
rootKeyVersions, err := repo.ListRootKeyVersions(ctx, externalWrappers.root, rootKeyId, WithOrder("version desc"))
if err != nil {
return nil, fmt.Errorf("error looking up root key versions for scope %s with key ID %s: %w", scopeId, externalWrappers.root.KeyID(), err)
return nil, "", fmt.Errorf("error looking up root key versions for scope %s with key ID %s: %w", scopeId, externalWrappers.root.KeyID(), err)
}
if len(rootKeyVersions) == 0 {
return nil, fmt.Errorf("no root key versions found for scope %s", scopeId)
return nil, "", fmt.Errorf("no root key versions found for scope %s", scopeId)
}
var multi *multiwrapper.MultiWrapper
@@ -248,10 +242,101 @@ func (k *Kms) loadRoot(ctx context.Context, scopeId string, opt ...Option) (*mul
if _, err := wrapper.SetConfig(map[string]string{
"key_id": key.GetPrivateId(),
}); err != nil {
return nil, fmt.Errorf("error setting config on aead root wrapper in scope %s: %w", scopeId, err)
return nil, "", fmt.Errorf("error setting config on aead root wrapper in scope %s: %w", scopeId, err)
}
if err := wrapper.SetAESGCMKeyBytes(key.GetKey()); err != nil {
return nil, fmt.Errorf("error setting key bytes on aead root wrapper in scope %s: %w", scopeId, err)
return nil, "", fmt.Errorf("error setting key bytes on aead root wrapper in scope %s: %w", scopeId, err)
}
if i == 0 {
multi = multiwrapper.NewMultiWrapper(wrapper)
} else {
multi.AddWrapper(wrapper)
}
}
return multi, rootKeyId, err
}
// Dek is an interface wrapping dek types to allow a lot less switching in loadDek
type Dek interface {
GetRootKeyId() string
GetPrivateId() string
}
// DekVersion is an interface wrapping versioned dek types to allow a lot less switching in loadDek
type DekVersion interface {
GetPrivateId() string
GetKey() []byte
}
func (k *Kms) loadDek(ctx context.Context, scopeId string, purpose KeyPurpose, rootWrapper wrapping.Wrapper, rootKeyId string, opt ...Option) (*multiwrapper.MultiWrapper, error) {
if rootWrapper == nil {
return nil, fmt.Errorf("got nil root wrapper in loadDek for scope %s", scopeId)
}
if rootKeyId == "" {
return nil, fmt.Errorf("no root key ID provided for scope %s", scopeId)
}
opts := getOpts(opt...)
repo := opts.withRepository
if repo == nil {
repo = k.repo
}
var keys []Dek
var err error
switch purpose {
case KeyPurposeDatabase:
keys, err = repo.ListDatabaseKeys(ctx)
case KeyPurposeOplog:
keys, err = repo.ListOplogKeys(ctx)
case KeyPurposeTokens:
keys, err = repo.ListTokenKeys(ctx)
case KeyPurposeSessions:
keys, err = repo.ListSessionKeys(ctx)
}
if err != nil {
return nil, fmt.Errorf("error listing root keys: %w", err)
}
var keyId string
for _, k := range keys {
if k.GetRootKeyId() == rootKeyId {
keyId = k.GetPrivateId()
break
}
}
if keyId == "" {
return nil, fmt.Errorf("error finding %s key for scope %s", purpose.String(), scopeId)
}
var keyVersions []DekVersion
switch purpose {
case KeyPurposeDatabase:
keyVersions, err = repo.ListDatabaseKeyVersions(ctx, rootWrapper, keyId, WithOrder("version desc"))
case KeyPurposeOplog:
keyVersions, err = repo.ListOplogKeyVersions(ctx, rootWrapper, keyId, WithOrder("version desc"))
case KeyPurposeTokens:
keyVersions, err = repo.ListTokenKeyVersions(ctx, rootWrapper, keyId, WithOrder("version desc"))
case KeyPurposeSessions:
keyVersions, err = repo.ListSessionKeyVersions(ctx, rootWrapper, keyId, WithOrder("version desc"))
}
if err != nil {
return nil, fmt.Errorf("error looking up %s key versions for scope %s with key ID %s: %w", purpose.String(), scopeId, rootWrapper.KeyID(), err)
}
if len(keyVersions) == 0 {
return nil, fmt.Errorf("no %s key versions found for scope %s", purpose.String(), scopeId)
}
var multi *multiwrapper.MultiWrapper
for i, keyVersion := range keyVersions {
wrapper := aead.NewWrapper(nil)
if _, err := wrapper.SetConfig(map[string]string{
"key_id": keyVersion.GetPrivateId(),
}); err != nil {
return nil, fmt.Errorf("error setting config on aead %s wrapper in scope %s: %w", purpose.String(), scopeId, err)
}
if err := wrapper.SetAESGCMKeyBytes(keyVersion.GetKey()); err != nil {
return nil, fmt.Errorf("error setting key bytes on aead %s wrapper in scope %s: %w", purpose.String(), scopeId, err)
}
if i == 0 {
multi = multiwrapper.NewMultiWrapper(wrapper)
+4 -4
View File
@@ -73,14 +73,14 @@ func TestKms(t *testing.T) {
keyIds := map[string]bool{}
scopePurposeMap := map[string]interface{}{}
for i := 1; i < 3; i++ {
// This iterates through wrappers for all three scopes and two purposes,
// This iterates through wrappers for all three scopes and four purposes,
// ensuring that the key bytes and IDs are different for each of them,
// simulating calling the KMS object from different scopes for different
// purposes and ensuring the keys are different when that happens.
t.Run(fmt.Sprintf("verify wrappers different x %d", i), func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
for _, scopeId := range []string{scope.Global.String(), org.GetPublicId(), proj.GetPublicId()} {
for _, purpose := range []kms.KeyPurpose{kms.KeyPurposeUnknown, kms.KeyPurposeOplog, kms.KeyPurposeDatabase} {
for _, purpose := range []kms.KeyPurpose{kms.KeyPurposeUnknown, kms.KeyPurposeOplog, kms.KeyPurposeDatabase, kms.KeyPurposeSessions, kms.KeyPurposeTokens} {
wrapper, err := kmsCache.GetWrapper(ctx, scopeId, purpose)
if purpose == kms.KeyPurposeUnknown {
require.Error(err)
@@ -120,8 +120,8 @@ func TestKms(t *testing.T) {
}
return true
})
// two purposes x 3 scopes
assert.Equal(t, 6, count)
// four purposes x 3 scopes
assert.Equal(t, 12, count)
})
}
}
+3 -3
View File
@@ -73,9 +73,9 @@ type KeyIder interface {
// easily accessed via their KeyType
type Keys map[KeyType]KeyIder
// CreateKeysTx creates the root key and DEKs returns a map of the new keys This
// function encapsulates all the work required within a db.TxHandler and allows
// this capability to be shared with the iam repo.
// CreateKeysTx creates the root key and DEKs returns a map of the new keys.
// This function encapsulates all the work required within a db.TxHandler and
// allows this capability to be shared with the iam repo.
func CreateKeysTx(ctx context.Context, dbReader db.Reader, dbWriter db.Writer, rootWrapper wrapping.Wrapper, randomReader io.Reader, scopeId string) (Keys, error) {
if dbReader == nil {
return nil, fmt.Errorf("create keys: missing db reader: %w", db.ErrInvalidParameter)
+6 -2
View File
@@ -137,11 +137,15 @@ func (r *Repository) DeleteDatabaseKey(ctx context.Context, privateId string, op
}
// ListDatabaseKeys will list the keys. Supports the WithLimit option.
func (r *Repository) ListDatabaseKeys(ctx context.Context, opt ...Option) ([]*DatabaseKey, error) {
func (r *Repository) ListDatabaseKeys(ctx context.Context, opt ...Option) ([]Dek, error) {
var keys []*DatabaseKey
err := r.list(ctx, &keys, "1=1", nil, opt...)
if err != nil {
return nil, fmt.Errorf("list database keys: %w", err)
}
return keys, nil
deks := make([]Dek, 0, len(keys))
for _, key := range keys {
deks = append(deks, key)
}
return deks, nil
}
+1 -1
View File
@@ -284,7 +284,7 @@ func TestRepository_ListDatabaseKeys(t *testing.T) {
assert, require := assert.New(t), require.New(t)
for i := 0; i < tt.createCnt; i++ {
org, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
require.NoError(conn.Where("scope_id in(?)", []interface{}{org.PublicId, proj.PublicId}).Delete(kms.AllocRootKey()).Error)
require.NoError(conn.Where("scope_id in(?)", []interface{}{"global", org.PublicId, proj.PublicId}).Delete(kms.AllocRootKey()).Error)
rk := kms.TestRootKey(t, conn, proj.PublicId)
kms.TestDatabaseKey(t, conn, rk.PrivateId)
require.NoError(err)
@@ -139,7 +139,7 @@ func (r *Repository) LatestDatabaseKeyVersion(ctx context.Context, rkvWrapper wr
}
// ListDatabaseKeyVersions will lists versions of a key. Supports the WithLimit option.
func (r *Repository) ListDatabaseKeyVersions(ctx context.Context, rkvWrapper wrapping.Wrapper, databaseKeyId string, opt ...Option) ([]*DatabaseKeyVersion, error) {
func (r *Repository) ListDatabaseKeyVersions(ctx context.Context, rkvWrapper wrapping.Wrapper, databaseKeyId string, opt ...Option) ([]DekVersion, error) {
if databaseKeyId == "" {
return nil, fmt.Errorf("list database key versions: missing database key id %w", db.ErrInvalidParameter)
}
@@ -156,5 +156,9 @@ func (r *Repository) ListDatabaseKeyVersions(ctx context.Context, rkvWrapper wra
return nil, fmt.Errorf("list database key versions: error decrypting key num %d: %w", i, err)
}
}
return versions, nil
dekVersions := make([]DekVersion, 0, len(versions))
for _, version := range versions {
dekVersions = append(dekVersions, version)
}
return dekVersions, nil
}
+6 -2
View File
@@ -137,11 +137,15 @@ func (r *Repository) DeleteOplogKey(ctx context.Context, privateId string, opt .
}
// ListOplogKeys will list the keys. Supports the WithLimit option.
func (r *Repository) ListOplogKeys(ctx context.Context, opt ...Option) ([]*OplogKey, error) {
func (r *Repository) ListOplogKeys(ctx context.Context, opt ...Option) ([]Dek, error) {
var keys []*OplogKey
err := r.list(ctx, &keys, "1=1", nil, opt...)
if err != nil {
return nil, fmt.Errorf("list oplog keys: %w", err)
}
return keys, nil
deks := make([]Dek, 0, len(keys))
for _, key := range keys {
deks = append(deks, key)
}
return deks, nil
}
+1 -1
View File
@@ -284,7 +284,7 @@ func TestRepository_ListOplogKeys(t *testing.T) {
assert, require := assert.New(t), require.New(t)
for i := 0; i < tt.createCnt; i++ {
org, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
require.NoError(conn.Where("scope_id in(?)", []interface{}{org.PublicId, proj.PublicId}).Delete(kms.AllocRootKey()).Error)
require.NoError(conn.Where("scope_id in(?)", []interface{}{"global", org.PublicId, proj.PublicId}).Delete(kms.AllocRootKey()).Error)
rk := kms.TestRootKey(t, conn, proj.PublicId)
kms.TestOplogKey(t, conn, rk.PrivateId)
require.NoError(err)
+6 -2
View File
@@ -139,7 +139,7 @@ func (r *Repository) LatestOplogKeyVersion(ctx context.Context, rkvWrapper wrapp
}
// ListOplogKeyVersions will lists versions of a key. Supports the WithLimit option.
func (r *Repository) ListOplogKeyVersions(ctx context.Context, rkvWrapper wrapping.Wrapper, oplogKeyId string, opt ...Option) ([]*OplogKeyVersion, error) {
func (r *Repository) ListOplogKeyVersions(ctx context.Context, rkvWrapper wrapping.Wrapper, oplogKeyId string, opt ...Option) ([]DekVersion, error) {
if oplogKeyId == "" {
return nil, fmt.Errorf("list oplog key versions: missing oplog key id %w", db.ErrInvalidParameter)
}
@@ -156,5 +156,9 @@ func (r *Repository) ListOplogKeyVersions(ctx context.Context, rkvWrapper wrappi
return nil, fmt.Errorf("list oplog key versions: error decrypting key num %d: %w", i, err)
}
}
return versions, nil
dekVersions := make([]DekVersion, 0, len(versions))
for _, version := range versions {
dekVersions = append(dekVersions, version)
}
return dekVersions, nil
}
+6 -2
View File
@@ -137,11 +137,15 @@ func (r *Repository) DeleteSessionKey(ctx context.Context, privateId string, opt
}
// ListSessionKeys will list the keys. Supports the WithLimit option.
func (r *Repository) ListSessionKeys(ctx context.Context, opt ...Option) ([]*SessionKey, error) {
func (r *Repository) ListSessionKeys(ctx context.Context, opt ...Option) ([]Dek, error) {
var keys []*SessionKey
err := r.list(ctx, &keys, "1=1", nil, opt...)
if err != nil {
return nil, fmt.Errorf("list session keys: %w", err)
}
return keys, nil
deks := make([]Dek, 0, len(keys))
for _, key := range keys {
deks = append(deks, key)
}
return deks, nil
}
+1 -1
View File
@@ -284,7 +284,7 @@ func TestRepository_ListSessionKeys(t *testing.T) {
assert, require := assert.New(t), require.New(t)
for i := 0; i < tt.createCnt; i++ {
org, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
require.NoError(conn.Where("scope_id in(?)", []interface{}{org.PublicId, proj.PublicId}).Delete(kms.AllocRootKey()).Error)
require.NoError(conn.Where("scope_id in(?)", []interface{}{"global", org.PublicId, proj.PublicId}).Delete(kms.AllocRootKey()).Error)
rk := kms.TestRootKey(t, conn, proj.PublicId)
kms.TestSessionKey(t, conn, rk.PrivateId)
require.NoError(err)
@@ -139,7 +139,7 @@ func (r *Repository) LatestSessionKeyVersion(ctx context.Context, rkvWrapper wra
}
// ListSessionKeyVersions will lists versions of a key. Supports the WithLimit option.
func (r *Repository) ListSessionKeyVersions(ctx context.Context, rkvWrapper wrapping.Wrapper, sessionKeyId string, opt ...Option) ([]*SessionKeyVersion, error) {
func (r *Repository) ListSessionKeyVersions(ctx context.Context, rkvWrapper wrapping.Wrapper, sessionKeyId string, opt ...Option) ([]DekVersion, error) {
if sessionKeyId == "" {
return nil, fmt.Errorf("list session key versions: missing session key id %w", db.ErrInvalidParameter)
}
@@ -156,5 +156,9 @@ func (r *Repository) ListSessionKeyVersions(ctx context.Context, rkvWrapper wrap
return nil, fmt.Errorf("list session key versions: error decrypting key num %d: %w", i, err)
}
}
return versions, nil
dekVersions := make([]DekVersion, 0, len(versions))
for _, version := range versions {
dekVersions = append(dekVersions, version)
}
return dekVersions, nil
}
+6 -2
View File
@@ -137,11 +137,15 @@ func (r *Repository) DeleteTokenKey(ctx context.Context, privateId string, opt .
}
// ListTokenKeys will list the keys. Supports the WithLimit option.
func (r *Repository) ListTokenKeys(ctx context.Context, opt ...Option) ([]*TokenKey, error) {
func (r *Repository) ListTokenKeys(ctx context.Context, opt ...Option) ([]Dek, error) {
var keys []*TokenKey
err := r.list(ctx, &keys, "1=1", nil, opt...)
if err != nil {
return nil, fmt.Errorf("list token keys: %w", err)
}
return keys, nil
deks := make([]Dek, 0, len(keys))
for _, key := range keys {
deks = append(deks, key)
}
return deks, nil
}
+1 -1
View File
@@ -284,7 +284,7 @@ func TestRepository_ListTokenKeys(t *testing.T) {
assert, require := assert.New(t), require.New(t)
for i := 0; i < tt.createCnt; i++ {
org, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
require.NoError(conn.Where("scope_id in(?)", []interface{}{org.PublicId, proj.PublicId}).Delete(kms.AllocRootKey()).Error)
require.NoError(conn.Where("scope_id in(?)", []interface{}{"global", org.PublicId, proj.PublicId}).Delete(kms.AllocRootKey()).Error)
rk := kms.TestRootKey(t, conn, proj.PublicId)
kms.TestTokenKey(t, conn, rk.PrivateId)
require.NoError(err)
+6 -2
View File
@@ -139,7 +139,7 @@ func (r *Repository) LatestTokenKeyVersion(ctx context.Context, rkvWrapper wrapp
}
// ListTokenKeyVersions will lists versions of a key. Supports the WithLimit option.
func (r *Repository) ListTokenKeyVersions(ctx context.Context, rkvWrapper wrapping.Wrapper, tokenKeyId string, opt ...Option) ([]*TokenKeyVersion, error) {
func (r *Repository) ListTokenKeyVersions(ctx context.Context, rkvWrapper wrapping.Wrapper, tokenKeyId string, opt ...Option) ([]DekVersion, error) {
if tokenKeyId == "" {
return nil, fmt.Errorf("list token key versions: missing token key id %w", db.ErrInvalidParameter)
}
@@ -156,5 +156,9 @@ func (r *Repository) ListTokenKeyVersions(ctx context.Context, rkvWrapper wrappi
return nil, fmt.Errorf("list token key versions: error decrypting key num %d: %w", i, err)
}
}
return versions, nil
dekVersions := make([]DekVersion, 0, len(versions))
for _, version := range versions {
dekVersions = append(dekVersions, version)
}
return dekVersions, nil
}