Fix KDF for key import with derivation enabled (#9106) (#9218)

* add fix and tests for importing keys with derivation enabled

* add changelog

* add check to fix kdf for existing imported keys

* only set kdf on derived keys

Co-authored-by: Rachel Culpepper <84159930+rculpepper@users.noreply.github.com>
This commit is contained in:
Vault Automation 2025-09-09 13:28:12 -06:00 committed by GitHub
parent 417175772c
commit c19829ccd7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 134 additions and 0 deletions

3
changelog/_9106.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
secrets/transit: Fix error when using ed25519 keys that were imported with derivation enabled
```

View File

@ -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)

View File

@ -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)
}
}
})
}
}

View File

@ -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
}