diff --git a/changelog/_9106.txt b/changelog/_9106.txt new file mode 100644 index 0000000000..93abe4a3bb --- /dev/null +++ b/changelog/_9106.txt @@ -0,0 +1,3 @@ +```release-note:bug +secrets/transit: Fix error when using ed25519 keys that were imported with derivation enabled +``` diff --git a/sdk/helper/keysutil/lock_manager.go b/sdk/helper/keysutil/lock_manager.go index 4b8e719627..ec399857e1 100644 --- a/sdk/helper/keysutil/lock_manager.go +++ b/sdk/helper/keysutil/lock_manager.go @@ -495,6 +495,13 @@ func (lm *LockManager) GetPolicy(ctx context.Context, req PolicyRequest, rand io } } + if p.Imported && p.Derived && p.KDF == Kdf_hmac_sha256_counter { + if err := p.setKDF(ctx, req.Storage, Kdf_hkdf_sha256); err != nil { + cleanup() + return nil, false, err + } + } + if lm.useCache { lm.cache.Store(req.Name, p) } else { @@ -551,6 +558,19 @@ func (lm *LockManager) ImportPolicy(ctx context.Context, req PolicyRequest, key } } + if req.Derived { + p.KDF = Kdf_hkdf_sha256 + if req.Convergent { + p.ConvergentEncryption = true + // As of version 3 we store the version within each key, so we + // set to -1 to indicate that the value in the policy has no + // meaning. We still, for backwards compatibility, fall back to + // this value if the key doesn't have one, which means it will + // only be -1 in the case where every key version is >= 3 + p.ConvergentVersion = -1 + } + } + err = p.ImportPublicOrPrivate(ctx, req.Storage, key, req.IsPrivateKey, rand) if err != nil { return fmt.Errorf("error importing key: %s", err) diff --git a/sdk/helper/keysutil/lock_manager_test.go b/sdk/helper/keysutil/lock_manager_test.go new file mode 100644 index 0000000000..ba71e0873a --- /dev/null +++ b/sdk/helper/keysutil/lock_manager_test.go @@ -0,0 +1,100 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package keysutil + +import ( + "context" + "crypto/rand" + "testing" + + "github.com/hashicorp/vault/sdk/logical" + "github.com/stretchr/testify/require" +) + +func TestImportPolicy(t *testing.T) { + lm, err := NewLockManager(false, 0) + require.NoError(t, err) + + ctx := context.Background() + storage := &logical.InmemStorage{} + + testKeys, err := generateTestKeys() + require.NoError(t, err) + + testCases := map[string]struct { + req PolicyRequest + key []byte + expectErr bool + }{ + "import AES key": { + req: PolicyRequest{ + Name: "test-aes-key", + KeyType: KeyType_AES256_GCM96, + Storage: storage, + IsPrivateKey: true, + }, + key: testKeys[KeyType_AES256_GCM96], + }, + "import RSA key": { + req: PolicyRequest{ + Name: "test-rsa-key", + KeyType: KeyType_RSA2048, + Storage: storage, + IsPrivateKey: true, + }, + key: testKeys[KeyType_RSA2048], + }, + "import ECDSA key": { + req: PolicyRequest{ + Name: "test-ecdsa-key", + KeyType: KeyType_ECDSA_P256, + Storage: storage, + IsPrivateKey: true, + }, + key: testKeys[KeyType_ECDSA_P256], + }, + "import ED25519 key": { + req: PolicyRequest{ + Name: "test-ed25519-key", + KeyType: KeyType_ED25519, + Storage: storage, + IsPrivateKey: true, + }, + key: testKeys[KeyType_ED25519], + }, + "import ed25519 with derivation": { + req: PolicyRequest{ + Name: "ed25519-derived", + KeyType: KeyType_ED25519, + Storage: storage, + IsPrivateKey: true, + Derived: true, + }, + key: testKeys[KeyType_ED25519], + }, + } + + for name, tt := range testCases { + t.Run(name, func(t *testing.T) { + err = lm.ImportPolicy(ctx, tt.req, tt.key, rand.Reader) + if tt.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + + pol, upserted, err := lm.GetPolicy(ctx, PolicyRequest{Name: tt.req.Name, Storage: storage}, rand.Reader) + require.NoError(t, err) + require.False(t, upserted) + + defer pol.Unlock() + + require.Equal(t, tt.req.KeyType, pol.Type) + if tt.req.Derived { + require.True(t, pol.Derived) + require.Equal(t, Kdf_hkdf_sha256, pol.KDF) + } + } + }) + } +} diff --git a/sdk/helper/keysutil/policy.go b/sdk/helper/keysutil/policy.go index 6d69937423..488883f159 100644 --- a/sdk/helper/keysutil/policy.go +++ b/sdk/helper/keysutil/policy.go @@ -2876,3 +2876,14 @@ func generateECDSAKey(keyType KeyType, entry *KeyEntry) error { return nil } + +func (p *Policy) setKDF(ctx context.Context, storage logical.Storage, kdf int) error { + p.KDF = kdf + + err := p.Persist(ctx, storage) + if err != nil { + return err + } + + return nil +}