2023-03-15 16:00:52 +00:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
2015-04-16 00:08:12 +00:00
package transit
import (
2018-01-08 18:31:38 +00:00
"context"
2016-09-21 14:29:42 +00:00
"crypto/elliptic"
2017-11-03 14:45:53 +00:00
"crypto/x509"
2017-06-05 19:00:39 +00:00
"encoding/base64"
2017-11-03 14:45:53 +00:00
"encoding/pem"
2015-07-05 21:19:24 +00:00
"fmt"
2015-09-17 22:49:50 +00:00
"strconv"
2017-06-05 19:00:39 +00:00
"time"
"golang.org/x/crypto/ed25519"
2015-04-16 00:08:12 +00:00
2017-07-05 15:25:10 +00:00
"github.com/fatih/structs"
2019-04-13 07:44:06 +00:00
"github.com/hashicorp/vault/sdk/framework"
2019-04-12 21:54:35 +00:00
"github.com/hashicorp/vault/sdk/helper/keysutil"
"github.com/hashicorp/vault/sdk/logical"
2015-04-16 00:08:12 +00:00
)
2016-10-18 14:13:01 +00:00
func ( b * backend ) pathListKeys ( ) * framework . Path {
return & framework . Path {
Pattern : "keys/?$" ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ListOperation : b . pathKeysList ,
} ,
HelpSynopsis : pathPolicyHelpSyn ,
HelpDescription : pathPolicyHelpDesc ,
}
}
2016-01-27 21:24:11 +00:00
func ( b * backend ) pathKeys ( ) * framework . Path {
2015-04-16 00:08:12 +00:00
return & framework . Path {
2015-08-21 07:56:13 +00:00
Pattern : "keys/" + framework . GenericNameRegex ( "name" ) ,
2015-04-16 00:08:12 +00:00
Fields : map [ string ] * framework . FieldSchema {
2021-04-08 16:43:39 +00:00
"name" : {
2015-04-16 00:08:12 +00:00
Type : framework . TypeString ,
2015-04-27 20:52:47 +00:00
Description : "Name of the key" ,
2015-04-16 00:08:12 +00:00
} ,
2015-07-05 21:11:02 +00:00
2021-04-08 16:43:39 +00:00
"type" : {
2016-09-21 14:29:42 +00:00
Type : framework . TypeString ,
Default : "aes256-gcm96" ,
2017-11-03 14:45:53 +00:00
Description : `
2019-10-03 20:11:43 +00:00
The type of key to create . Currently , "aes128-gcm96" ( symmetric ) , "aes256-gcm96" ( symmetric ) , "ecdsa-p256"
2020-02-15 22:40:50 +00:00
( asymmetric ) , "ecdsa-p384" ( asymmetric ) , "ecdsa-p521" ( asymmetric ) , "ed25519" ( asymmetric ) , "rsa-2048" ( asymmetric ) , "rsa-3072"
( asymmetric ) , "rsa-4096" ( asymmetric ) are supported . Defaults to "aes256-gcm96" .
2017-11-03 14:45:53 +00:00
` ,
2016-09-21 14:29:42 +00:00
} ,
2021-04-08 16:43:39 +00:00
"derived" : {
2016-08-05 21:52:44 +00:00
Type : framework . TypeBool ,
Description : ` Enables key derivation mode . This
2016-09-21 14:29:42 +00:00
allows for per - transaction unique
keys for encryption operations . ` ,
2015-07-05 21:11:02 +00:00
} ,
2016-06-20 17:17:48 +00:00
2021-04-08 16:43:39 +00:00
"convergent_encryption" : {
2016-06-20 17:17:48 +00:00
Type : framework . TypeBool ,
2016-08-05 21:52:44 +00:00
Description : ` Whether to support convergent encryption .
2016-06-20 17:17:48 +00:00
This is only supported when using a key with
key derivation enabled and will require all
2016-08-05 21:52:44 +00:00
requests to carry both a context and 96 - bit
2016-08-07 19:53:40 +00:00
( 12 - byte ) nonce . The given nonce will be used
in place of a randomly generated nonce . As a
result , when the same context and nonce are
supplied , the same ciphertext is generated . It
is * very important * when using this mode that
you ensure that all nonces are unique for a
given context . Failing to do so will severely
impact the ciphertext ' s security . ` ,
2016-06-20 17:17:48 +00:00
} ,
2017-01-23 16:04:43 +00:00
2021-04-08 16:43:39 +00:00
"exportable" : {
2017-01-23 16:04:43 +00:00
Type : framework . TypeBool ,
Description : ` Enables keys to be exportable .
This allows for all the valid keys
in the key ring to be exported . ` ,
} ,
2017-06-05 19:00:39 +00:00
2021-04-08 16:43:39 +00:00
"allow_plaintext_backup" : {
2017-12-14 17:51:50 +00:00
Type : framework . TypeBool ,
Description : ` Enables taking a backup of the named
key in plaintext format . Once set ,
this cannot be disabled . ` ,
} ,
2021-04-08 16:43:39 +00:00
"context" : {
2017-06-05 19:00:39 +00:00
Type : framework . TypeString ,
Description : ` Base64 encoded context for key derivation .
When reading a key with key derivation enabled ,
if the key type supports public keys , this will
return the public key for the given context . ` ,
} ,
2022-01-20 15:10:15 +00:00
2022-02-17 20:17:59 +00:00
"auto_rotate_period" : {
2022-01-20 15:10:15 +00:00
Type : framework . TypeDurationSecond ,
Default : 0 ,
Description : ` Amount of time the key should live before
being automatically rotated . A value of 0
( default ) disables automatic rotation for the
key . ` ,
} ,
2022-09-06 15:17:58 +00:00
"key_size" : {
Type : framework . TypeInt ,
Default : 0 ,
Description : fmt . Sprintf ( "The key size in bytes for the algorithm. Only applies to HMAC and must be no fewer than %d bytes and no more than %d" , keysutil . HmacMinKeySize , keysutil . HmacMaxKeySize ) ,
} ,
2023-01-27 19:39:58 +00:00
"managed_key_name" : {
Type : framework . TypeString ,
Description : "The name of the managed key to use for this transit key" ,
} ,
"managed_key_id" : {
Type : framework . TypeString ,
Description : "The UUID of the managed key to use for this transit key" ,
} ,
2015-04-16 00:08:12 +00:00
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2016-01-27 21:24:11 +00:00
logical . UpdateOperation : b . pathPolicyWrite ,
logical . DeleteOperation : b . pathPolicyDelete ,
logical . ReadOperation : b . pathPolicyRead ,
2015-04-16 00:08:12 +00:00
} ,
2015-04-27 19:47:09 +00:00
HelpSynopsis : pathPolicyHelpSyn ,
HelpDescription : pathPolicyHelpDesc ,
2015-04-16 00:08:12 +00:00
}
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathKeysList ( ctx context . Context , req * logical . Request , d * framework . FieldData ) ( * logical . Response , error ) {
2018-01-19 06:44:44 +00:00
entries , err := req . Storage . List ( ctx , "policy/" )
2016-10-18 14:13:01 +00:00
if err != nil {
return nil , err
}
return logical . ListResponse ( entries ) , nil
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathPolicyWrite ( ctx context . Context , req * logical . Request , d * framework . FieldData ) ( * logical . Response , error ) {
2015-04-16 00:08:12 +00:00
name := d . Get ( "name" ) . ( string )
2015-07-05 21:11:02 +00:00
derived := d . Get ( "derived" ) . ( bool )
2016-06-20 17:17:48 +00:00
convergent := d . Get ( "convergent_encryption" ) . ( bool )
2016-09-21 14:29:42 +00:00
keyType := d . Get ( "type" ) . ( string )
2022-09-06 15:17:58 +00:00
keySize := d . Get ( "key_size" ) . ( int )
2017-01-23 16:04:43 +00:00
exportable := d . Get ( "exportable" ) . ( bool )
2017-12-14 17:51:50 +00:00
allowPlaintextBackup := d . Get ( "allow_plaintext_backup" ) . ( bool )
2022-02-17 20:17:59 +00:00
autoRotatePeriod := time . Second * time . Duration ( d . Get ( "auto_rotate_period" ) . ( int ) )
2023-01-27 19:39:58 +00:00
managedKeyName := d . Get ( "managed_key_name" ) . ( string )
managedKeyId := d . Get ( "managed_key_id" ) . ( string )
2022-01-20 15:10:15 +00:00
2022-02-17 20:17:59 +00:00
if autoRotatePeriod != 0 && autoRotatePeriod < time . Hour {
return logical . ErrorResponse ( "auto rotate period must be 0 to disable or at least an hour" ) , nil
2022-01-20 15:10:15 +00:00
}
2016-06-20 17:17:48 +00:00
if ! derived && convergent {
return logical . ErrorResponse ( "convergent encryption requires derivation to be enabled" ) , nil
}
2015-04-16 00:08:12 +00:00
2016-10-26 23:52:31 +00:00
polReq := keysutil . PolicyRequest {
2018-06-12 16:24:12 +00:00
Upsert : true ,
2017-12-14 17:51:50 +00:00
Storage : req . Storage ,
Name : name ,
Derived : derived ,
Convergent : convergent ,
Exportable : exportable ,
AllowPlaintextBackup : allowPlaintextBackup ,
2022-02-17 20:17:59 +00:00
AutoRotatePeriod : autoRotatePeriod ,
2016-09-21 14:29:42 +00:00
}
2022-09-06 15:17:58 +00:00
2016-09-21 14:29:42 +00:00
switch keyType {
2019-10-03 20:11:43 +00:00
case "aes128-gcm96" :
polReq . KeyType = keysutil . KeyType_AES128_GCM96
2016-09-21 14:29:42 +00:00
case "aes256-gcm96" :
2016-10-26 23:52:31 +00:00
polReq . KeyType = keysutil . KeyType_AES256_GCM96
2018-02-14 16:59:46 +00:00
case "chacha20-poly1305" :
polReq . KeyType = keysutil . KeyType_ChaCha20_Poly1305
2016-09-21 14:29:42 +00:00
case "ecdsa-p256" :
2016-10-26 23:52:31 +00:00
polReq . KeyType = keysutil . KeyType_ECDSA_P256
2019-10-03 16:32:43 +00:00
case "ecdsa-p384" :
polReq . KeyType = keysutil . KeyType_ECDSA_P384
case "ecdsa-p521" :
polReq . KeyType = keysutil . KeyType_ECDSA_P521
2017-06-05 19:00:39 +00:00
case "ed25519" :
polReq . KeyType = keysutil . KeyType_ED25519
2017-11-03 14:45:53 +00:00
case "rsa-2048" :
polReq . KeyType = keysutil . KeyType_RSA2048
2020-02-15 22:40:50 +00:00
case "rsa-3072" :
polReq . KeyType = keysutil . KeyType_RSA3072
2017-11-03 14:45:53 +00:00
case "rsa-4096" :
polReq . KeyType = keysutil . KeyType_RSA4096
2022-09-06 15:17:58 +00:00
case "hmac" :
polReq . KeyType = keysutil . KeyType_HMAC
2023-01-27 19:39:58 +00:00
case "managed_key" :
polReq . KeyType = keysutil . KeyType_MANAGED_KEY
2016-09-21 14:29:42 +00:00
default :
return logical . ErrorResponse ( fmt . Sprintf ( "unknown key type %v" , keyType ) ) , logical . ErrInvalidRequest
}
2022-09-06 15:17:58 +00:00
if keySize != 0 {
if polReq . KeyType != keysutil . KeyType_HMAC {
return logical . ErrorResponse ( fmt . Sprintf ( "key_size is not valid for algorithm %v" , polReq . KeyType ) ) , logical . ErrInvalidRequest
}
if keySize < keysutil . HmacMinKeySize || keySize > keysutil . HmacMaxKeySize {
return logical . ErrorResponse ( fmt . Sprintf ( "invalid key_size %d" , keySize ) ) , logical . ErrInvalidRequest
}
polReq . KeySize = keySize
}
2016-09-21 14:29:42 +00:00
2023-01-27 19:39:58 +00:00
if polReq . KeyType == keysutil . KeyType_MANAGED_KEY {
keyId , err := GetManagedKeyUUID ( ctx , b , managedKeyName , managedKeyId )
if err != nil {
return nil , err
}
polReq . ManagedKeyUUID = keyId
}
2021-09-13 21:44:56 +00:00
p , upserted , err := b . GetPolicy ( ctx , polReq , b . GetRandomReader ( ) )
2016-04-26 15:39:19 +00:00
if err != nil {
return nil , err
}
if p == nil {
return nil , fmt . Errorf ( "error generating key: returned policy was nil" )
}
2018-06-12 16:24:12 +00:00
if b . System ( ) . CachingDisabled ( ) {
p . Unlock ( )
}
2016-04-26 15:39:19 +00:00
resp := & logical . Response { }
if ! upserted {
resp . AddWarning ( fmt . Sprintf ( "key %s already existed" , name ) )
}
return nil , nil
2015-04-16 00:08:12 +00:00
}
2017-06-05 19:00:39 +00:00
// Built-in helper type for returning asymmetric keys
type asymKey struct {
2017-07-05 15:25:10 +00:00
Name string ` json:"name" structs:"name" mapstructure:"name" `
PublicKey string ` json:"public_key" structs:"public_key" mapstructure:"public_key" `
CreationTime time . Time ` json:"creation_time" structs:"creation_time" mapstructure:"creation_time" `
2017-06-05 19:00:39 +00:00
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathPolicyRead ( ctx context . Context , req * logical . Request , d * framework . FieldData ) ( * logical . Response , error ) {
2015-04-16 00:08:12 +00:00
name := d . Get ( "name" ) . ( string )
2015-09-14 20:28:46 +00:00
2021-09-13 21:44:56 +00:00
p , _ , err := b . GetPolicy ( ctx , keysutil . PolicyRequest {
2018-06-12 16:24:12 +00:00
Storage : req . Storage ,
Name : name ,
2019-10-17 17:33:00 +00:00
} , b . GetRandomReader ( ) )
2015-04-16 00:08:12 +00:00
if err != nil {
return nil , err
}
2016-04-26 15:39:19 +00:00
if p == nil {
2015-04-16 00:08:12 +00:00
return nil , nil
}
2018-06-12 16:24:12 +00:00
if ! b . System ( ) . CachingDisabled ( ) {
p . Lock ( false )
}
defer p . Unlock ( )
2015-04-16 00:08:12 +00:00
// Return the response
resp := & logical . Response {
Data : map [ string ] interface { } {
2016-04-26 15:39:19 +00:00
"name" : p . Name ,
2016-09-21 14:29:42 +00:00
"type" : p . Type . String ( ) ,
2016-04-26 15:39:19 +00:00
"derived" : p . Derived ,
"deletion_allowed" : p . DeletionAllowed ,
2018-10-17 16:05:05 +00:00
"min_available_version" : p . MinAvailableVersion ,
2016-04-26 15:39:19 +00:00
"min_decryption_version" : p . MinDecryptionVersion ,
2017-06-08 18:07:18 +00:00
"min_encryption_version" : p . MinEncryptionVersion ,
2016-04-26 15:39:19 +00:00
"latest_version" : p . LatestVersion ,
2017-01-23 16:04:43 +00:00
"exportable" : p . Exportable ,
2017-12-14 17:51:50 +00:00
"allow_plaintext_backup" : p . AllowPlaintextBackup ,
2017-01-11 16:05:06 +00:00
"supports_encryption" : p . Type . EncryptionSupported ( ) ,
"supports_decryption" : p . Type . DecryptionSupported ( ) ,
"supports_signing" : p . Type . SigningSupported ( ) ,
"supports_derivation" : p . Type . DerivationSupported ( ) ,
2022-02-17 20:17:59 +00:00
"auto_rotate_period" : int64 ( p . AutoRotatePeriod . Seconds ( ) ) ,
2022-05-16 16:50:38 +00:00
"imported_key" : p . Imported ,
2015-04-16 00:08:12 +00:00
} ,
}
2022-09-06 15:17:58 +00:00
if p . KeySize != 0 {
resp . Data [ "key_size" ] = p . KeySize
}
2016-09-21 14:29:42 +00:00
2022-05-16 16:50:38 +00:00
if p . Imported {
resp . Data [ "imported_key_allow_rotation" ] = p . AllowImportedKeyRotation
}
2018-02-09 18:54:18 +00:00
if p . BackupInfo != nil {
resp . Data [ "backup_info" ] = map [ string ] interface { } {
"time" : p . BackupInfo . Time ,
"version" : p . BackupInfo . Version ,
}
}
if p . RestoreInfo != nil {
resp . Data [ "restore_info" ] = map [ string ] interface { } {
"time" : p . RestoreInfo . Time ,
"version" : p . RestoreInfo . Version ,
}
}
2016-04-26 15:39:19 +00:00
if p . Derived {
2016-08-30 20:29:09 +00:00
switch p . KDF {
2016-10-26 23:52:31 +00:00
case keysutil . Kdf_hmac_sha256_counter :
2016-08-30 20:29:09 +00:00
resp . Data [ "kdf" ] = "hmac-sha256-counter"
resp . Data [ "kdf_mode" ] = "hmac-sha256-counter"
2016-10-26 23:52:31 +00:00
case keysutil . Kdf_hkdf_sha256 :
2016-08-30 20:29:09 +00:00
resp . Data [ "kdf" ] = "hkdf_sha256"
}
2016-06-20 17:17:48 +00:00
resp . Data [ "convergent_encryption" ] = p . ConvergentEncryption
2016-08-26 18:11:03 +00:00
if p . ConvergentEncryption {
resp . Data [ "convergent_encryption_version" ] = p . ConvergentVersion
}
2015-07-06 01:58:31 +00:00
}
2015-09-17 22:49:50 +00:00
2017-06-05 19:00:39 +00:00
contextRaw := d . Get ( "context" ) . ( string )
var context [ ] byte
if len ( contextRaw ) != 0 {
context , err = base64 . StdEncoding . DecodeString ( contextRaw )
if err != nil {
return logical . ErrorResponse ( "failed to base64-decode context" ) , logical . ErrInvalidRequest
}
}
2016-09-21 14:29:42 +00:00
switch p . Type {
2019-10-03 20:11:43 +00:00
case keysutil . KeyType_AES128_GCM96 , keysutil . KeyType_AES256_GCM96 , keysutil . KeyType_ChaCha20_Poly1305 :
2016-09-21 14:29:42 +00:00
retKeys := map [ string ] int64 { }
for k , v := range p . Keys {
2017-12-06 23:24:00 +00:00
retKeys [ k ] = v . DeprecatedCreationTime
2016-09-21 14:29:42 +00:00
}
resp . Data [ "keys" ] = retKeys
2020-02-15 22:40:50 +00:00
case keysutil . KeyType_ECDSA_P256 , keysutil . KeyType_ECDSA_P384 , keysutil . KeyType_ECDSA_P521 , keysutil . KeyType_ED25519 , keysutil . KeyType_RSA2048 , keysutil . KeyType_RSA3072 , keysutil . KeyType_RSA4096 :
2017-07-05 15:25:10 +00:00
retKeys := map [ string ] map [ string ] interface { } { }
2016-09-21 14:29:42 +00:00
for k , v := range p . Keys {
2017-06-05 19:00:39 +00:00
key := asymKey {
PublicKey : v . FormattedPublicKey ,
CreationTime : v . CreationTime ,
}
if key . CreationTime . IsZero ( ) {
key . CreationTime = time . Unix ( v . DeprecatedCreationTime , 0 )
2016-09-21 14:29:42 +00:00
}
2017-06-05 19:00:39 +00:00
switch p . Type {
case keysutil . KeyType_ECDSA_P256 :
key . Name = elliptic . P256 ( ) . Params ( ) . Name
2019-10-03 16:32:43 +00:00
case keysutil . KeyType_ECDSA_P384 :
key . Name = elliptic . P384 ( ) . Params ( ) . Name
case keysutil . KeyType_ECDSA_P521 :
key . Name = elliptic . P521 ( ) . Params ( ) . Name
2017-06-05 19:00:39 +00:00
case keysutil . KeyType_ED25519 :
if p . Derived {
if len ( context ) == 0 {
key . PublicKey = ""
} else {
2017-12-06 23:24:00 +00:00
ver , err := strconv . Atoi ( k )
if err != nil {
2021-04-22 15:20:59 +00:00
return nil , fmt . Errorf ( "invalid version %q: %w" , k , err )
2017-12-06 23:24:00 +00:00
}
2020-10-02 02:04:36 +00:00
derived , err := p . GetKey ( context , ver , 32 )
2017-06-05 19:00:39 +00:00
if err != nil {
2023-03-20 16:00:49 +00:00
return nil , fmt . Errorf ( "failed to derive key to return public component: %w" , err )
2017-06-05 19:00:39 +00:00
}
pubKey := ed25519 . PrivateKey ( derived ) . Public ( ) . ( ed25519 . PublicKey )
key . PublicKey = base64 . StdEncoding . EncodeToString ( pubKey )
}
}
key . Name = "ed25519"
2020-02-15 22:40:50 +00:00
case keysutil . KeyType_RSA2048 , keysutil . KeyType_RSA3072 , keysutil . KeyType_RSA4096 :
2017-11-03 14:45:53 +00:00
key . Name = "rsa-2048"
2020-02-15 22:40:50 +00:00
if p . Type == keysutil . KeyType_RSA3072 {
key . Name = "rsa-3072"
}
2017-11-03 14:45:53 +00:00
if p . Type == keysutil . KeyType_RSA4096 {
key . Name = "rsa-4096"
}
// Encode the RSA public key in PEM format to return over the
// API
derBytes , err := x509 . MarshalPKIXPublicKey ( v . RSAKey . Public ( ) )
if err != nil {
2021-04-22 15:20:59 +00:00
return nil , fmt . Errorf ( "error marshaling RSA public key: %w" , err )
2017-11-03 14:45:53 +00:00
}
pemBlock := & pem . Block {
Type : "PUBLIC KEY" ,
Bytes : derBytes ,
}
pemBytes := pem . EncodeToMemory ( pemBlock )
if pemBytes == nil || len ( pemBytes ) == 0 {
return nil , fmt . Errorf ( "failed to PEM-encode RSA public key" )
}
key . PublicKey = string ( pemBytes )
2017-06-05 19:00:39 +00:00
}
2017-12-06 23:24:00 +00:00
retKeys [ k ] = structs . New ( key ) . Map ( )
2016-09-21 14:29:42 +00:00
}
resp . Data [ "keys" ] = retKeys
2015-09-17 22:49:50 +00:00
}
2015-04-16 00:08:12 +00:00
return resp , nil
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathPolicyDelete ( ctx context . Context , req * logical . Request , d * framework . FieldData ) ( * logical . Response , error ) {
2015-04-16 00:08:12 +00:00
name := d . Get ( "name" ) . ( string )
2016-05-03 03:46:39 +00:00
// Delete does its own locking
2018-01-19 06:44:44 +00:00
err := b . lm . DeletePolicy ( ctx , req . Storage , name )
2016-01-27 17:02:32 +00:00
if err != nil {
2016-01-27 21:24:11 +00:00
return logical . ErrorResponse ( fmt . Sprintf ( "error deleting policy %s: %s" , name , err ) ) , err
2016-01-27 17:02:32 +00:00
}
2015-04-16 00:08:12 +00:00
return nil , nil
}
2015-04-27 19:47:09 +00:00
2015-09-14 20:28:46 +00:00
const pathPolicyHelpSyn = ` Managed named encryption keys `
2015-04-27 19:47:09 +00:00
const pathPolicyHelpDesc = `
This path is used to manage the named keys that are available .
Doing a write with no value against a new named key will create
it using a randomly generated key .
`