diff --git a/builtin/logical/transit/path_keys.go b/builtin/logical/transit/path_keys.go index bc931cc30c..0852ebccbe 100644 --- a/builtin/logical/transit/path_keys.go +++ b/builtin/logical/transit/path_keys.go @@ -222,6 +222,10 @@ func (b *backend) pathPolicyWrite(ctx context.Context, req *logical.Request, d * polReq.KeyType = keysutil.KeyType_HMAC case "managed_key": polReq.KeyType = keysutil.KeyType_MANAGED_KEY + case "aes128-cmac": + polReq.KeyType = keysutil.KeyType_AES128_CMAC + case "aes256-cmac": + polReq.KeyType = keysutil.KeyType_AES256_CMAC default: return logical.ErrorResponse(fmt.Sprintf("unknown key type %v", keyType)), logical.ErrInvalidRequest } diff --git a/builtin/logical/transit/path_keys_test.go b/builtin/logical/transit/path_keys_test.go index d32c852d39..8a2069d188 100644 --- a/builtin/logical/transit/path_keys_test.go +++ b/builtin/logical/transit/path_keys_test.go @@ -195,3 +195,113 @@ func TestTransit_CreateKeyWithAutorotation(t *testing.T) { }) } } + +// TestTransit_CreateKey validates transit key creation functionality +func TestTransit_CreateKey(t *testing.T) { + testCases := map[string]struct { + creationParams map[string]interface{} + shouldError bool + }{ + "AES-128": { + creationParams: map[string]interface{}{"type": "aes128-gcm96"}, + shouldError: false, + }, + "AES-256": { + creationParams: map[string]interface{}{"type": "aes256-gcm96"}, + shouldError: false, + }, + "CHACHA20": { + creationParams: map[string]interface{}{"type": "chacha20-poly1305"}, + shouldError: false, + }, + "ECDSA-P256": { + creationParams: map[string]interface{}{"type": "ecdsa-p256"}, + shouldError: false, + }, + "ECDSA-P384": { + creationParams: map[string]interface{}{"type": "ecdsa-p384"}, + shouldError: false, + }, + "ECDSA-P521": { + creationParams: map[string]interface{}{"type": "ecdsa-p521"}, + shouldError: false, + }, + "RSA_2048": { + creationParams: map[string]interface{}{"type": "rsa-2048"}, + shouldError: false, + }, + "RSA_3072": { + creationParams: map[string]interface{}{"type": "rsa-3072"}, + shouldError: false, + }, + "RSA_4096": { + creationParams: map[string]interface{}{"type": "rsa-4096"}, + shouldError: false, + }, + "HMAC": { + creationParams: map[string]interface{}{"type": "hmac", "key_size": 128}, + shouldError: false, + }, + "AES-128 CMAC": { + creationParams: map[string]interface{}{"type": "aes128-cmac"}, + shouldError: false, + }, + "AES-256 CMAC": { + creationParams: map[string]interface{}{"type": "aes256-cmac"}, + shouldError: false, + }, + "bad key type": { + creationParams: map[string]interface{}{"type": "fake-key-type"}, + shouldError: true, + }, + } + + coreConfig := &vault.CoreConfig{ + LogicalBackends: map[string]logical.Factory{ + "transit": transit.Factory, + }, + } + cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{ + HandlerFunc: vaulthttp.Handler, + }) + cluster.Start() + defer cluster.Cleanup() + cores := cluster.Cores + vault.TestWaitActive(t, cores[0].Core) + client := cores[0].Client + err := client.Sys().Mount("transit", &api.MountInput{ + Type: "transit", + }) + if err != nil { + t.Fatal(err) + } + + for name, tt := range testCases { + t.Run(name, func(t *testing.T) { + keyName, err := uuid.GenerateUUID() + if err != nil { + t.Fatalf("error generating key name: %s", err) + } + + resp, err := client.Logical().Write(fmt.Sprintf("transit/keys/%s", keyName), tt.creationParams) + if err != nil && !tt.shouldError { + t.Fatalf("unexpected error creating key: %s", err) + } + + if err == nil && tt.shouldError { + t.Fatal("expected error but got nil") + } + + if err == nil { + keyType, ok := resp.Data["type"] + if !ok { + t.Fatal("missing key type in response") + } + + if keyType != tt.creationParams["type"] { + t.Fatalf("incorrect key type: expected %s, got %s", tt.creationParams["type"], keyType) + } + } + }) + } +} diff --git a/sdk/helper/keysutil/lock_manager.go b/sdk/helper/keysutil/lock_manager.go index 6d2881e0d8..35f29203c3 100644 --- a/sdk/helper/keysutil/lock_manager.go +++ b/sdk/helper/keysutil/lock_manager.go @@ -397,6 +397,12 @@ func (lm *LockManager) GetPolicy(ctx context.Context, req PolicyRequest, rand io return nil, false, fmt.Errorf("key derivation and convergent encryption not supported for keys of type %v", req.KeyType) } + case KeyType_AES128_CMAC, KeyType_AES256_CMAC: + if req.Derived || req.Convergent { + cleanup() + return nil, false, fmt.Errorf("key derivation and convergent encryption not supported for keys of type %v", req.KeyType) + } + default: cleanup() return nil, false, fmt.Errorf("unsupported key type %v", req.KeyType) diff --git a/sdk/helper/keysutil/policy.go b/sdk/helper/keysutil/policy.go index 6b9caad9ca..2a4cadd2f0 100644 --- a/sdk/helper/keysutil/policy.go +++ b/sdk/helper/keysutil/policy.go @@ -68,6 +68,8 @@ const ( KeyType_RSA3072 KeyType_MANAGED_KEY KeyType_HMAC + KeyType_AES128_CMAC + KeyType_AES256_CMAC ) const ( @@ -202,6 +204,10 @@ func (kt KeyType) String() string { return "hmac" case KeyType_MANAGED_KEY: return "managed_key" + case KeyType_AES128_CMAC: + return "aes128-cmac" + case KeyType_AES256_CMAC: + return "aes256-cmac" } return "[unknown]" @@ -1611,17 +1617,20 @@ func (p *Policy) RotateInMemory(randReader io.Reader) (retErr error) { DeprecatedCreationTime: now.Unix(), } - hmacKey, err := uuid.GenerateRandomBytesWithReader(32, randReader) - if err != nil { - return err + if p.Type != KeyType_AES128_CMAC && p.Type != KeyType_AES256_CMAC && p.Type != KeyType_HMAC { + hmacKey, err := uuid.GenerateRandomBytesWithReader(32, randReader) + if err != nil { + return err + } + entry.HMACKey = hmacKey } - entry.HMACKey = hmacKey + var err error switch p.Type { - case KeyType_AES128_GCM96, KeyType_AES256_GCM96, KeyType_ChaCha20_Poly1305, KeyType_HMAC: + case KeyType_AES128_GCM96, KeyType_AES256_GCM96, KeyType_ChaCha20_Poly1305, KeyType_HMAC, KeyType_AES128_CMAC, KeyType_AES256_CMAC: // Default to 256 bit key numBytes := 32 - if p.Type == KeyType_AES128_GCM96 { + if p.Type == KeyType_AES128_GCM96 || p.Type == KeyType_AES128_CMAC { numBytes = 16 } else if p.Type == KeyType_HMAC { numBytes = p.KeySize