Add support for a dedicated HMAC type in Transit. (#16668)

* Get import correct

* limits, docs

* changelog

* unit tests

* And fix import for hmac unit test

* typo

* Update website/content/api-docs/secret/transit.mdx

Co-authored-by: Matt Schultz <975680+schultz-is@users.noreply.github.com>

* Update builtin/logical/transit/path_keys.go

Co-authored-by: Matt Schultz <975680+schultz-is@users.noreply.github.com>

* Validate key sizes a bit more carefully

* Update sdk/helper/keysutil/policy.go

Co-authored-by: Matt Schultz <975680+schultz-is@users.noreply.github.com>

Co-authored-by: Matt Schultz <975680+schultz-is@users.noreply.github.com>
This commit is contained in:
Scott Miller 2022-09-06 10:17:58 -05:00 committed by GitHub
parent 95b5449e8a
commit 606edb66d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 268 additions and 176 deletions

View File

@ -14,71 +14,167 @@ import (
func TestTransit_HMAC(t *testing.T) {
b, storage := createBackendWithSysView(t)
// First create a key
req := &logical.Request{
Storage: storage,
Operation: logical.UpdateOperation,
Path: "keys/foo",
}
_, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
cases := []struct {
name string
typ string
}{
{
name: "foo",
typ: "",
},
{
name: "dedicated",
typ: "hmac",
},
}
// Now, change the key value to something we control
p, _, err := b.GetPolicy(context.Background(), keysutil.PolicyRequest{
Storage: storage,
Name: "foo",
}, b.GetRandomReader())
if err != nil {
t.Fatal(err)
}
// We don't care as we're the only one using this
latestVersion := strconv.Itoa(p.LatestVersion)
keyEntry := p.Keys[latestVersion]
keyEntry.HMACKey = []byte("01234567890123456789012345678901")
p.Keys[latestVersion] = keyEntry
if err = p.Persist(context.Background(), storage); err != nil {
t.Fatal(err)
}
req.Path = "hmac/foo"
req.Data = map[string]interface{}{
"input": "dGhlIHF1aWNrIGJyb3duIGZveA==",
}
doRequest := func(req *logical.Request, errExpected bool, expected string) {
path := req.Path
defer func() { req.Path = path }()
resp, err := b.HandleRequest(context.Background(), req)
if err != nil && !errExpected {
panic(fmt.Sprintf("%v", err))
for _, c := range cases {
req := &logical.Request{
Storage: storage,
Operation: logical.UpdateOperation,
Path: "keys/" + c.name,
}
if resp == nil {
t.Fatal("expected non-nil response")
_, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if errExpected {
if !resp.IsError() {
// Now, change the key value to something we control
p, _, err := b.GetPolicy(context.Background(), keysutil.PolicyRequest{
Storage: storage,
Name: c.name,
}, b.GetRandomReader())
if err != nil {
t.Fatal(err)
}
// We don't care as we're the only one using this
latestVersion := strconv.Itoa(p.LatestVersion)
keyEntry := p.Keys[latestVersion]
keyEntry.HMACKey = []byte("01234567890123456789012345678901")
keyEntry.Key = []byte("01234567890123456789012345678901")
p.Keys[latestVersion] = keyEntry
if err = p.Persist(context.Background(), storage); err != nil {
t.Fatal(err)
}
req.Path = "hmac/" + c.name
req.Data = map[string]interface{}{
"input": "dGhlIHF1aWNrIGJyb3duIGZveA==",
}
doRequest := func(req *logical.Request, errExpected bool, expected string) {
path := req.Path
defer func() { req.Path = path }()
resp, err := b.HandleRequest(context.Background(), req)
if err != nil && !errExpected {
panic(fmt.Sprintf("%v", err))
}
if resp == nil {
t.Fatal("expected non-nil response")
}
if errExpected {
if !resp.IsError() {
t.Fatalf("bad: got error response: %#v", *resp)
}
return
}
if resp.IsError() {
t.Fatalf("bad: got error response: %#v", *resp)
}
return
}
if resp.IsError() {
t.Fatalf("bad: got error response: %#v", *resp)
}
value, ok := resp.Data["hmac"]
if !ok {
t.Fatalf("no hmac key found in returned data, got resp data %#v", resp.Data)
}
if value.(string) != expected {
panic(fmt.Sprintf("mismatched hashes; expected %s, got resp data %#v", expected, resp.Data))
value, ok := resp.Data["hmac"]
if !ok {
t.Fatalf("no hmac key found in returned data, got resp data %#v", resp.Data)
}
if value.(string) != expected {
panic(fmt.Sprintf("mismatched hashes; expected %s, got resp data %#v", expected, resp.Data))
}
// Now verify
req.Path = strings.ReplaceAll(req.Path, "hmac", "verify")
req.Data["hmac"] = value.(string)
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatalf("%v: %v", err, resp)
}
if resp == nil {
t.Fatal("expected non-nil response")
}
if resp.Data["valid"].(bool) == false {
panic(fmt.Sprintf("error validating hmac;\nreq:\n%#v\nresp:\n%#v", *req, *resp))
}
}
// Now verify
req.Path = strings.ReplaceAll(req.Path, "hmac", "verify")
req.Data["hmac"] = value.(string)
resp, err = b.HandleRequest(context.Background(), req)
// Comparisons are against values generated via openssl
// Test defaults -- sha2-256
doRequest(req, false, "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4=")
// Test algorithm selection in the path
req.Path = "hmac/" + c.name + "/sha2-224"
doRequest(req, false, "vault:v1:3p+ZWVquYDvu2dSTCa65Y3fgoMfIAc6fNaBbtg==")
// Reset and test algorithm selection in the data
req.Path = "hmac/" + c.name
req.Data["algorithm"] = "sha2-224"
doRequest(req, false, "vault:v1:3p+ZWVquYDvu2dSTCa65Y3fgoMfIAc6fNaBbtg==")
req.Data["algorithm"] = "sha2-384"
doRequest(req, false, "vault:v1:jDB9YXdPjpmr29b1JCIEJO93IydlKVfD9mA2EO9OmJtJQg3QAV5tcRRRb7IQGW9p")
req.Data["algorithm"] = "sha2-512"
doRequest(req, false, "vault:v1:PSXLXvkvKF4CpU65e2bK1tGBZQpcpCEM32fq2iUoiTyQQCfBcGJJItQ+60tMwWXAPQrC290AzTrNJucGrr4GFA==")
// Test returning as base64
req.Data["format"] = "base64"
doRequest(req, false, "vault:v1:PSXLXvkvKF4CpU65e2bK1tGBZQpcpCEM32fq2iUoiTyQQCfBcGJJItQ+60tMwWXAPQrC290AzTrNJucGrr4GFA==")
// Test SHA3
req.Path = "hmac/" + c.name
req.Data["algorithm"] = "sha3-224"
doRequest(req, false, "vault:v1:TGipmKH8LR/BkMolYpDYy0BJCIhTtGPDhV2VkQ==")
req.Data["algorithm"] = "sha3-256"
doRequest(req, false, "vault:v1:+px9V/7QYLfdK808zPESC2T/L33uFf4Blzsn9Jy838o=")
req.Data["algorithm"] = "sha3-384"
doRequest(req, false, "vault:v1:YGoRwN4UdTRYZeOER86jsQOB8piWenzLDzJ2wJQK/Jq59rAsY8lh7SCdqqCyFg70")
req.Data["algorithm"] = "sha3-512"
doRequest(req, false, "vault:v1:GrNA8sU88naMPEQ7UZGj9EJl7YJhl03AFHfxcEURFrtvnobdea9ZlZHePpxAx/oCaC7R2HkrAO+Tu3uXPIl3lg==")
// Test returning SHA3 as base64
req.Data["format"] = "base64"
doRequest(req, false, "vault:v1:GrNA8sU88naMPEQ7UZGj9EJl7YJhl03AFHfxcEURFrtvnobdea9ZlZHePpxAx/oCaC7R2HkrAO+Tu3uXPIl3lg==")
req.Data["algorithm"] = "foobar"
doRequest(req, true, "")
req.Data["algorithm"] = "sha2-256"
req.Data["input"] = "foobar"
doRequest(req, true, "")
req.Data["input"] = "dGhlIHF1aWNrIGJyb3duIGZveA=="
// Rotate
err = p.Rotate(context.Background(), storage, b.GetRandomReader())
if err != nil {
t.Fatal(err)
}
keyEntry = p.Keys["2"]
// Set to another value we control
keyEntry.HMACKey = []byte("12345678901234567890123456789012")
p.Keys["2"] = keyEntry
if err = p.Persist(context.Background(), storage); err != nil {
t.Fatal(err)
}
doRequest(req, false, "vault:v2:Dt+mO/B93kuWUbGMMobwUNX5Wodr6dL3JH4DMfpQ0kw=")
// Verify a previous version
req.Path = "verify/" + c.name
req.Data["hmac"] = "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatalf("%v: %v", err, resp)
}
@ -86,116 +182,36 @@ func TestTransit_HMAC(t *testing.T) {
t.Fatal("expected non-nil response")
}
if resp.Data["valid"].(bool) == false {
panic(fmt.Sprintf("error validating hmac;\nreq:\n%#v\nresp:\n%#v", *req, *resp))
t.Fatalf("error validating hmac\nreq\n%#v\nresp\n%#v", *req, *resp)
}
}
// Comparisons are against values generated via openssl
// Try a bad value
req.Data["hmac"] = "vault:v1:UcBvm4VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatalf("%v: %v", err, resp)
}
if resp == nil {
t.Fatal("expected non-nil response")
}
if resp.Data["valid"].(bool) {
t.Fatalf("expected error validating hmac")
}
// Test defaults -- sha2-256
doRequest(req, false, "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4=")
// Set min decryption version, attempt to verify
p.MinDecryptionVersion = 2
if err = p.Persist(context.Background(), storage); err != nil {
t.Fatal(err)
}
// Test algorithm selection in the path
req.Path = "hmac/foo/sha2-224"
doRequest(req, false, "vault:v1:3p+ZWVquYDvu2dSTCa65Y3fgoMfIAc6fNaBbtg==")
// Reset and test algorithm selection in the data
req.Path = "hmac/foo"
req.Data["algorithm"] = "sha2-224"
doRequest(req, false, "vault:v1:3p+ZWVquYDvu2dSTCa65Y3fgoMfIAc6fNaBbtg==")
req.Data["algorithm"] = "sha2-384"
doRequest(req, false, "vault:v1:jDB9YXdPjpmr29b1JCIEJO93IydlKVfD9mA2EO9OmJtJQg3QAV5tcRRRb7IQGW9p")
req.Data["algorithm"] = "sha2-512"
doRequest(req, false, "vault:v1:PSXLXvkvKF4CpU65e2bK1tGBZQpcpCEM32fq2iUoiTyQQCfBcGJJItQ+60tMwWXAPQrC290AzTrNJucGrr4GFA==")
// Test returning as base64
req.Data["format"] = "base64"
doRequest(req, false, "vault:v1:PSXLXvkvKF4CpU65e2bK1tGBZQpcpCEM32fq2iUoiTyQQCfBcGJJItQ+60tMwWXAPQrC290AzTrNJucGrr4GFA==")
// Test SHA3
req.Path = "hmac/foo"
req.Data["algorithm"] = "sha3-224"
doRequest(req, false, "vault:v1:TGipmKH8LR/BkMolYpDYy0BJCIhTtGPDhV2VkQ==")
req.Data["algorithm"] = "sha3-256"
doRequest(req, false, "vault:v1:+px9V/7QYLfdK808zPESC2T/L33uFf4Blzsn9Jy838o=")
req.Data["algorithm"] = "sha3-384"
doRequest(req, false, "vault:v1:YGoRwN4UdTRYZeOER86jsQOB8piWenzLDzJ2wJQK/Jq59rAsY8lh7SCdqqCyFg70")
req.Data["algorithm"] = "sha3-512"
doRequest(req, false, "vault:v1:GrNA8sU88naMPEQ7UZGj9EJl7YJhl03AFHfxcEURFrtvnobdea9ZlZHePpxAx/oCaC7R2HkrAO+Tu3uXPIl3lg==")
// Test returning SHA3 as base64
req.Data["format"] = "base64"
doRequest(req, false, "vault:v1:GrNA8sU88naMPEQ7UZGj9EJl7YJhl03AFHfxcEURFrtvnobdea9ZlZHePpxAx/oCaC7R2HkrAO+Tu3uXPIl3lg==")
req.Data["algorithm"] = "foobar"
doRequest(req, true, "")
req.Data["algorithm"] = "sha2-256"
req.Data["input"] = "foobar"
doRequest(req, true, "")
req.Data["input"] = "dGhlIHF1aWNrIGJyb3duIGZveA=="
// Rotate
err = p.Rotate(context.Background(), storage, b.GetRandomReader())
if err != nil {
t.Fatal(err)
}
keyEntry = p.Keys["2"]
// Set to another value we control
keyEntry.HMACKey = []byte("12345678901234567890123456789012")
p.Keys["2"] = keyEntry
if err = p.Persist(context.Background(), storage); err != nil {
t.Fatal(err)
}
doRequest(req, false, "vault:v2:Dt+mO/B93kuWUbGMMobwUNX5Wodr6dL3JH4DMfpQ0kw=")
// Verify a previous version
req.Path = "verify/foo"
req.Data["hmac"] = "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatalf("%v: %v", err, resp)
}
if resp == nil {
t.Fatal("expected non-nil response")
}
if resp.Data["valid"].(bool) == false {
t.Fatalf("error validating hmac\nreq\n%#v\nresp\n%#v", *req, *resp)
}
// Try a bad value
req.Data["hmac"] = "vault:v1:UcBvm4VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatalf("%v: %v", err, resp)
}
if resp == nil {
t.Fatal("expected non-nil response")
}
if resp.Data["valid"].(bool) {
t.Fatalf("expected error validating hmac")
}
// Set min decryption version, attempt to verify
p.MinDecryptionVersion = 2
if err = p.Persist(context.Background(), storage); err != nil {
t.Fatal(err)
}
req.Data["hmac"] = "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="
resp, err = b.HandleRequest(context.Background(), req)
if err == nil {
t.Fatalf("expected an error, got response %#v", resp)
}
if err != logical.ErrInvalidRequest {
t.Fatalf("expected invalid request error, got %v", err)
req.Data["hmac"] = "vault:v1:UcBvm5VskkukzZHlPgm3p5P/Yr/PV6xpuOGZISya3A4="
resp, err = b.HandleRequest(context.Background(), req)
if err == nil {
t.Fatalf("expected an error, got response %#v", resp)
}
if err != logical.ErrInvalidRequest {
t.Fatalf("expected invalid request error, got %v", err)
}
}
}

View File

@ -177,6 +177,8 @@ func (b *backend) pathImportWrite(ctx context.Context, req *logical.Request, d *
polReq.KeyType = keysutil.KeyType_RSA3072
case "rsa-4096":
polReq.KeyType = keysutil.KeyType_RSA4096
case "hmac":
polReq.KeyType = keysutil.KeyType_HMAC
default:
return logical.ErrorResponse(fmt.Sprintf("unknown key type: %v", keyType)), logical.ErrInvalidRequest
}

View File

@ -30,6 +30,7 @@ var keyTypes = []string{
"rsa-2048",
"rsa-3072",
"rsa-4096",
"hmac",
}
var hashFns = []string{
@ -543,7 +544,7 @@ func wrapTargetKeyForImport(t *testing.T, wrappingKey *rsa.PublicKey, targetKey
var ok bool
var err error
switch targetKeyType {
case "aes128-gcm96", "aes256-gcm96", "chacha20-poly1305":
case "aes128-gcm96", "aes256-gcm96", "chacha20-poly1305", "hmac":
preppedTargetKey, ok = targetKey.([]byte)
if !ok {
t.Fatal("failed to wrap target key for import: symmetric key not provided in byte format")
@ -600,7 +601,7 @@ func generateKey(keyType string) (interface{}, error) {
switch keyType {
case "aes128-gcm96":
return uuid.GenerateRandomBytes(16)
case "aes256-gcm96":
case "aes256-gcm96", "hmac":
return uuid.GenerateRandomBytes(32)
case "chacha20-poly1305":
return uuid.GenerateRandomBytes(32)

View File

@ -103,6 +103,11 @@ being automatically rotated. A value of 0
(default) disables automatic rotation for the
key.`,
},
"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),
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
@ -130,6 +135,7 @@ func (b *backend) pathPolicyWrite(ctx context.Context, req *logical.Request, d *
derived := d.Get("derived").(bool)
convergent := d.Get("convergent_encryption").(bool)
keyType := d.Get("type").(string)
keySize := d.Get("key_size").(int)
exportable := d.Get("exportable").(bool)
allowPlaintextBackup := d.Get("allow_plaintext_backup").(bool)
autoRotatePeriod := time.Second * time.Duration(d.Get("auto_rotate_period").(int))
@ -152,6 +158,7 @@ func (b *backend) pathPolicyWrite(ctx context.Context, req *logical.Request, d *
AllowPlaintextBackup: allowPlaintextBackup,
AutoRotatePeriod: autoRotatePeriod,
}
switch keyType {
case "aes128-gcm96":
polReq.KeyType = keysutil.KeyType_AES128_GCM96
@ -173,9 +180,20 @@ func (b *backend) pathPolicyWrite(ctx context.Context, req *logical.Request, d *
polReq.KeyType = keysutil.KeyType_RSA3072
case "rsa-4096":
polReq.KeyType = keysutil.KeyType_RSA4096
case "hmac":
polReq.KeyType = keysutil.KeyType_HMAC
default:
return logical.ErrorResponse(fmt.Sprintf("unknown key type %v", keyType)), logical.ErrInvalidRequest
}
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
}
p, upserted, err := b.GetPolicy(ctx, polReq, b.GetRandomReader())
if err != nil {
@ -242,6 +260,9 @@ func (b *backend) pathPolicyRead(ctx context.Context, req *logical.Request, d *f
"imported_key": p.Imported,
},
}
if p.KeySize != 0 {
resp.Data["key_size"] = p.KeySize
}
if p.Imported {
resp.Data["imported_key_allow_rotation"] = p.AllowImportedKeyRotation

3
changelog/16668.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
secrets/transit: Add a dedicated HMAC key type, which can be used with key import.
```

View File

@ -36,6 +36,9 @@ type PolicyRequest struct {
// The key type
KeyType KeyType
// The key size for variable key size algorithms
KeySize int
// Whether it should be derived
Derived bool
@ -373,6 +376,11 @@ func (lm *LockManager) GetPolicy(ctx context.Context, req PolicyRequest, rand io
cleanup()
return nil, false, fmt.Errorf("key derivation and convergent encryption not supported for keys of type %v", req.KeyType)
}
case KeyType_HMAC:
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()
@ -387,6 +395,7 @@ func (lm *LockManager) GetPolicy(ctx context.Context, req PolicyRequest, rand io
Exportable: req.Exportable,
AllowPlaintextBackup: req.AllowPlaintextBackup,
AutoRotatePeriod: req.AutoRotatePeriod,
KeySize: req.KeySize,
}
if req.Derived {

View File

@ -45,6 +45,9 @@ import (
const (
Kdf_hmac_sha256_counter = iota // built-in helper
Kdf_hkdf_sha256 // golang.org/x/crypto/hkdf
HmacMinKeySize = 256 / 8
HmacMaxKeySize = 4096 / 8
)
// Or this one...we need the default of zero to be the original AES256-GCM96
@ -59,6 +62,7 @@ const (
KeyType_ECDSA_P521
KeyType_AES128_GCM96
KeyType_RSA3072
KeyType_HMAC
)
const (
@ -160,6 +164,8 @@ func (kt KeyType) String() string {
return "rsa-3072"
case KeyType_RSA4096:
return "rsa-4096"
case KeyType_HMAC:
return "hmac"
}
return "[unknown]"
@ -318,9 +324,10 @@ type Policy struct {
// served after a delete.
deleted uint32
Name string `json:"name"`
Key []byte `json:"key,omitempty"` // DEPRECATED
Keys keyEntryMap `json:"keys"`
Name string `json:"name"`
Key []byte `json:"key,omitempty"` // DEPRECATED
KeySize int `json:"key_size,omitempty"` // For algorithms with variable key sizes
Keys keyEntryMap `json:"keys"`
// Derived keys MUST provide a context and the master underlying key is
// never used. If convergent encryption is true, the context will be used
@ -1025,10 +1032,13 @@ func (p *Policy) HMACKey(version int) ([]byte, error) {
if err != nil {
return nil, err
}
if p.Type == KeyType_HMAC {
return keyEntry.Key, nil
}
if keyEntry.HMACKey == nil {
return nil, fmt.Errorf("no HMAC key exists for that key version")
}
return keyEntry.HMACKey, nil
}
@ -1386,19 +1396,25 @@ func (p *Policy) Import(ctx context.Context, storage logical.Storage, key []byte
DeprecatedCreationTime: now.Unix(),
}
hmacKey, err := uuid.GenerateRandomBytesWithReader(32, randReader)
if err != nil {
return err
if p.Type != KeyType_HMAC {
hmacKey, err := uuid.GenerateRandomBytesWithReader(32, randReader)
if err != nil {
return err
}
entry.HMACKey = hmacKey
}
entry.HMACKey = hmacKey
if (p.Type == KeyType_AES128_GCM96 && len(key) != 16) ||
((p.Type == KeyType_AES256_GCM96 || p.Type == KeyType_ChaCha20_Poly1305) && len(key) != 32) {
((p.Type == KeyType_AES256_GCM96 || p.Type == KeyType_ChaCha20_Poly1305) && len(key) != 32) ||
(p.Type == KeyType_HMAC && (len(key) < HmacMinKeySize || len(key) > HmacMaxKeySize)) {
return fmt.Errorf("invalid key size %d bytes for key type %s", len(key), p.Type)
}
if p.Type == KeyType_AES128_GCM96 || p.Type == KeyType_AES256_GCM96 || p.Type == KeyType_ChaCha20_Poly1305 {
if p.Type == KeyType_AES128_GCM96 || p.Type == KeyType_AES256_GCM96 || p.Type == KeyType_ChaCha20_Poly1305 || p.Type == KeyType_HMAC {
entry.Key = key
if p.Type == KeyType_HMAC {
p.KeySize = len(key)
}
} else {
parsedPrivateKey, err := x509.ParsePKCS8PrivateKey(key)
if err != nil {
@ -1549,11 +1565,16 @@ func (p *Policy) RotateInMemory(randReader io.Reader) (retErr error) {
entry.HMACKey = hmacKey
switch p.Type {
case KeyType_AES128_GCM96, KeyType_AES256_GCM96, KeyType_ChaCha20_Poly1305:
case KeyType_AES128_GCM96, KeyType_AES256_GCM96, KeyType_ChaCha20_Poly1305, KeyType_HMAC:
// Default to 256 bit key
numBytes := 32
if p.Type == KeyType_AES128_GCM96 {
numBytes = 16
} else if p.Type == KeyType_HMAC {
numBytes := p.KeySize
if numBytes < HmacMinKeySize || numBytes > HmacMaxKeySize {
return fmt.Errorf("invalid key size for HMAC key, must be between %d and %d bytes", HmacMinKeySize, HmacMaxKeySize)
}
}
newKey, err := uuid.GenerateRandomBytesWithReader(numBytes, randReader)
if err != nil {

View File

@ -63,10 +63,19 @@ values set here cannot be changed after key creation.
- `rsa-2048` - RSA with bit size of 2048 (asymmetric)
- `rsa-3072` - RSA with bit size of 3072 (asymmetric)
- `rsa-4096` - RSA with bit size of 4096 (asymmetric)
- `hmac` - HMAC (HMAC generation, verification)
~> **Note**: In FIPS 140-2 mode, the following algorithms are not certified
and thus should not be used: `chacha20-poly1305` and `ed25519`.
~> **Note**: All key types support HMAC through the use of a second randomly
generated key created key creation time or rotation. The HMAC key type only
supports HMAC, and behaves identically to other algorithms with
respect to the HMAC operations but supports key import. By default,
the HMAC key type uses a 256-bit key.
- `key_size` `(int: "0", optional)` - The key size in bytes for algorithms
that allow variable key sizes. Currently only applicable to HMAC, where
it must be between 16 and 512 bytes.
- `auto_rotate_period` `(duration: "0", optional)` The period at which
this key should be rotated automatically. Setting this to "0" (the default)
will disable automatic key rotation. This value cannot be shorter than one
@ -431,6 +440,9 @@ ciphertext to be encrypted with the latest version of the key, use the `rewrap`
endpoint. This is only supported with keys that support encryption and
decryption operations.
For algorithms with a configurable key size, the rotated key will use the same
key size as the previous version.
~> **Note**: For imported keys, rotation is only supported if the
`allow_rotation` field was set to `true` on import. Once an imported key is
rotated within Vault, it will not support further import operations.

View File

@ -85,10 +85,17 @@ types also generate separate HMAC keys):
signature verification
- `rsa-4096`: 4096-bit RSA key; supports encryption, decryption, signing, and
signature verification
- `hmac`: HMAC; supporting HMAC generation and verification.
~> **Note**: In FIPS 140-2 mode, the following algorithms are not certified
and thus should not be used: `chacha20-poly1305` and `ed25519`.
~> **Note**: All key types support HMAC operations through the use of a second randomly
generated key created key creation time or rotation. The HMAC key type only
supports HMAC, and behaves identically to other algorithms with
respect to the HMAC operations but supports key import. By default,
the HMAC key type uses a 256-bit key.
## Convergent Encryption
Convergent encryption is a mode where the same set of plaintext+context always