backport of commit 7d4d8cb708de62167340fa84770f8237c7bfdd1e (#22900)
Co-authored-by: Scott Miller <smiller@hashicorp.com>
This commit is contained in:
parent
d019802cc0
commit
bdee24128a
|
@ -1153,10 +1153,12 @@ func testConvergentEncryptionCommon(t *testing.T, ver int, keyType keysutil.KeyT
|
||||||
|
|
||||||
// Now test encrypting the same value twice
|
// Now test encrypting the same value twice
|
||||||
req.Data = map[string]interface{}{
|
req.Data = map[string]interface{}{
|
||||||
"plaintext": "emlwIHphcA==", // "zip zap"
|
"plaintext": "emlwIHphcA==", // "zip zap"
|
||||||
"nonce": "b25ldHdvdGhyZWVl", // "onetwothreee"
|
|
||||||
"context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S",
|
"context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S",
|
||||||
}
|
}
|
||||||
|
if ver == 0 {
|
||||||
|
req.Data["nonce"] = "b25ldHdvdGhyZWVl" // "onetwothreee"
|
||||||
|
}
|
||||||
resp, err = b.HandleRequest(context.Background(), req)
|
resp, err = b.HandleRequest(context.Background(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -1187,11 +1189,10 @@ func testConvergentEncryptionCommon(t *testing.T, ver int, keyType keysutil.KeyT
|
||||||
|
|
||||||
// For sanity, also check a different nonce value...
|
// For sanity, also check a different nonce value...
|
||||||
req.Data = map[string]interface{}{
|
req.Data = map[string]interface{}{
|
||||||
"plaintext": "emlwIHphcA==", // "zip zap"
|
"plaintext": "emlwIHphcA==", // "zip zap"
|
||||||
"nonce": "dHdvdGhyZWVmb3Vy", // "twothreefour"
|
|
||||||
"context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S",
|
"context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S",
|
||||||
}
|
}
|
||||||
if ver < 2 {
|
if ver == 0 {
|
||||||
req.Data["nonce"] = "dHdvdGhyZWVmb3Vy" // "twothreefour"
|
req.Data["nonce"] = "dHdvdGhyZWVmb3Vy" // "twothreefour"
|
||||||
} else {
|
} else {
|
||||||
req.Data["context"] = "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOldandSdd7S"
|
req.Data["context"] = "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOldandSdd7S"
|
||||||
|
@ -1230,10 +1231,12 @@ func testConvergentEncryptionCommon(t *testing.T, ver int, keyType keysutil.KeyT
|
||||||
|
|
||||||
// ...and a different context value
|
// ...and a different context value
|
||||||
req.Data = map[string]interface{}{
|
req.Data = map[string]interface{}{
|
||||||
"plaintext": "emlwIHphcA==", // "zip zap"
|
"plaintext": "emlwIHphcA==", // "zip zap"
|
||||||
"nonce": "dHdvdGhyZWVmb3Vy", // "twothreefour"
|
|
||||||
"context": "qV4h9iQyvn+raODOer4JNAsOhkXBwdT4HZ677Ql4KLqXSU+Jk4C/fXBWbv6xkSYT",
|
"context": "qV4h9iQyvn+raODOer4JNAsOhkXBwdT4HZ677Ql4KLqXSU+Jk4C/fXBWbv6xkSYT",
|
||||||
}
|
}
|
||||||
|
if ver == 0 {
|
||||||
|
req.Data["nonce"] = "dHdvdGhyZWVmb3Vy" // "twothreefour"
|
||||||
|
}
|
||||||
resp, err = b.HandleRequest(context.Background(), req)
|
resp, err = b.HandleRequest(context.Background(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -1345,9 +1348,11 @@ func testConvergentEncryptionCommon(t *testing.T, ver int, keyType keysutil.KeyT
|
||||||
// Finally, check operations on empty values
|
// Finally, check operations on empty values
|
||||||
// First, check without setting a plaintext at all
|
// First, check without setting a plaintext at all
|
||||||
req.Data = map[string]interface{}{
|
req.Data = map[string]interface{}{
|
||||||
"nonce": "b25ldHdvdGhyZWVl", // "onetwothreee"
|
|
||||||
"context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S",
|
"context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S",
|
||||||
}
|
}
|
||||||
|
if ver == 0 {
|
||||||
|
req.Data["nonce"] = "dHdvdGhyZWVmb3Vy" // "twothreefour"
|
||||||
|
}
|
||||||
resp, err = b.HandleRequest(context.Background(), req)
|
resp, err = b.HandleRequest(context.Background(), req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected error, got nil")
|
t.Fatal("expected error, got nil")
|
||||||
|
@ -1362,9 +1367,11 @@ func testConvergentEncryptionCommon(t *testing.T, ver int, keyType keysutil.KeyT
|
||||||
// Now set plaintext to empty
|
// Now set plaintext to empty
|
||||||
req.Data = map[string]interface{}{
|
req.Data = map[string]interface{}{
|
||||||
"plaintext": "",
|
"plaintext": "",
|
||||||
"nonce": "b25ldHdvdGhyZWVl", // "onetwothreee"
|
|
||||||
"context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S",
|
"context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S",
|
||||||
}
|
}
|
||||||
|
if ver == 0 {
|
||||||
|
req.Data["nonce"] = "dHdvdGhyZWVmb3Vy" // "twothreefour"
|
||||||
|
}
|
||||||
resp, err = b.HandleRequest(context.Background(), req)
|
resp, err = b.HandleRequest(context.Background(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
@ -170,6 +170,10 @@ func (b *backend) pathDatakeyWrite(ctx context.Context, req *logical.Request, d
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(nonce) > 0 && !nonceAllowed(p) {
|
||||||
|
return nil, ErrNonceNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
if constants.IsFIPS() && shouldWarnAboutNonceUsage(p, nonce) {
|
if constants.IsFIPS() && shouldWarnAboutNonceUsage(p, nonce) {
|
||||||
resp.AddWarning("A provided nonce value was used within FIPS mode, this violates FIPS 140 compliance.")
|
resp.AddWarning("A provided nonce value was used within FIPS mode, this violates FIPS 140 compliance.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -468,6 +468,13 @@ func (b *backend) pathEncryptWrite(ctx context.Context, req *logical.Request, d
|
||||||
successesInBatch := false
|
successesInBatch := false
|
||||||
for i, item := range batchInputItems {
|
for i, item := range batchInputItems {
|
||||||
if batchResponseItems[i].Error != "" {
|
if batchResponseItems[i].Error != "" {
|
||||||
|
userErrorInBatch = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if item.Nonce != "" && !nonceAllowed(p) {
|
||||||
|
userErrorInBatch = true
|
||||||
|
batchResponseItems[i].Error = ErrNonceNotAllowed.Error()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,6 +575,25 @@ func (b *backend) pathEncryptWrite(ctx context.Context, req *logical.Request, d
|
||||||
return batchRequestResponse(d, resp, req, successesInBatch, userErrorInBatch, internalErrorInBatch)
|
return batchRequestResponse(d, resp, req, successesInBatch, userErrorInBatch, internalErrorInBatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nonceAllowed(p *keysutil.Policy) bool {
|
||||||
|
var supportedKeyType bool
|
||||||
|
switch p.Type {
|
||||||
|
case keysutil.KeyType_MANAGED_KEY:
|
||||||
|
return true
|
||||||
|
case keysutil.KeyType_AES128_GCM96, keysutil.KeyType_AES256_GCM96, keysutil.KeyType_ChaCha20_Poly1305:
|
||||||
|
supportedKeyType = true
|
||||||
|
default:
|
||||||
|
supportedKeyType = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if supportedKeyType && p.ConvergentEncryption && p.ConvergentVersion == 1 {
|
||||||
|
// We only use the user supplied nonce for v1 convergent encryption keys
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Depending on the errors in the batch, different status codes should be returned. User errors
|
// Depending on the errors in the batch, different status codes should be returned. User errors
|
||||||
// will return a 400 and precede internal errors which return a 500. The reasoning behind this is
|
// will return a 400 and precede internal errors which return a 500. The reasoning behind this is
|
||||||
// that user errors are non-retryable without making changes to the request, and should be surfaced
|
// that user errors are non-retryable without making changes to the request, and should be surfaced
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -654,13 +655,26 @@ func TestTransit_BatchEncryptionCase12(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case13: Incorrect input for nonce when we aren't in convergent encryption should fail the operation
|
// Case13: Incorrect input for nonce when we aren't in convergent encryption should fail the operation
|
||||||
func TestTransit_BatchEncryptionCase13(t *testing.T) {
|
func TestTransit_EncryptionCase13(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
b, s := createBackendWithStorage(t)
|
b, s := createBackendWithStorage(t)
|
||||||
|
|
||||||
|
// Non-batch first
|
||||||
|
data := map[string]interface{}{"plaintext": "bXkgc2VjcmV0IGRhdGE=", "nonce": "R80hr9eNUIuFV52e"}
|
||||||
|
req := &logical.Request{
|
||||||
|
Operation: logical.CreateOperation,
|
||||||
|
Path: "encrypt/my-key",
|
||||||
|
Storage: s,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
resp, err := b.HandleRequest(context.Background(), req)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected invalid request")
|
||||||
|
}
|
||||||
|
|
||||||
batchInput := []interface{}{
|
batchInput := []interface{}{
|
||||||
map[string]interface{}{"plaintext": "bXkgc2VjcmV0IGRhdGE=", "nonce": "YmFkbm9uY2U="},
|
map[string]interface{}{"plaintext": "bXkgc2VjcmV0IGRhdGE=", "nonce": "R80hr9eNUIuFV52e"},
|
||||||
}
|
}
|
||||||
|
|
||||||
batchData := map[string]interface{}{
|
batchData := map[string]interface{}{
|
||||||
|
@ -672,10 +686,71 @@ func TestTransit_BatchEncryptionCase13(t *testing.T) {
|
||||||
Storage: s,
|
Storage: s,
|
||||||
Data: batchData,
|
Data: batchData,
|
||||||
}
|
}
|
||||||
_, err = b.HandleRequest(context.Background(), batchReq)
|
resp, err = b.HandleRequest(context.Background(), batchReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := resp.Data["http_status_code"]; !ok || v.(int) != http.StatusBadRequest {
|
||||||
|
t.Fatal("expected request error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case14: Incorrect input for nonce when we are in convergent version 3 should fail
|
||||||
|
func TestTransit_EncryptionCase14(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
b, s := createBackendWithStorage(t)
|
||||||
|
|
||||||
|
cReq := &logical.Request{
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Path: "keys/my-key",
|
||||||
|
Storage: s,
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"convergent_encryption": "true",
|
||||||
|
"derived": "true",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp, err := b.HandleRequest(context.Background(), cReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-batch first
|
||||||
|
data := map[string]interface{}{"plaintext": "bXkgc2VjcmV0IGRhdGE=", "context": "SGVsbG8sIFdvcmxkCg==", "nonce": "R80hr9eNUIuFV52e"}
|
||||||
|
req := &logical.Request{
|
||||||
|
Operation: logical.CreateOperation,
|
||||||
|
Path: "encrypt/my-key",
|
||||||
|
Storage: s,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = b.HandleRequest(context.Background(), req)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected invalid request")
|
||||||
|
}
|
||||||
|
|
||||||
|
batchInput := []interface{}{
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
|
||||||
|
batchData := map[string]interface{}{
|
||||||
|
"batch_input": batchInput,
|
||||||
|
}
|
||||||
|
batchReq := &logical.Request{
|
||||||
|
Operation: logical.CreateOperation,
|
||||||
|
Path: "encrypt/my-key",
|
||||||
|
Storage: s,
|
||||||
|
Data: batchData,
|
||||||
|
}
|
||||||
|
resp, err = b.HandleRequest(context.Background(), batchReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := resp.Data["http_status_code"]; !ok || v.(int) != http.StatusBadRequest {
|
||||||
|
t.Fatal("expected request error")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the fast path function decodeBatchRequestItems behave like mapstructure.Decode() to decode []BatchRequestItem.
|
// Test that the fast path function decodeBatchRequestItems behave like mapstructure.Decode() to decode []BatchRequestItem.
|
||||||
|
|
|
@ -6,6 +6,7 @@ package transit
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/helper/constants"
|
"github.com/hashicorp/vault/helper/constants"
|
||||||
|
@ -16,6 +17,8 @@ import (
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrNonceNotAllowed = errors.New("provided nonce not allowed for this key")
|
||||||
|
|
||||||
func (b *backend) pathRewrap() *framework.Path {
|
func (b *backend) pathRewrap() *framework.Path {
|
||||||
return &framework.Path{
|
return &framework.Path{
|
||||||
Pattern: "rewrap/" + framework.GenericNameRegex("name"),
|
Pattern: "rewrap/" + framework.GenericNameRegex("name"),
|
||||||
|
@ -152,6 +155,11 @@ func (b *backend) pathRewrapWrite(ctx context.Context, req *logical.Request, d *
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if item.Nonce != "" && !nonceAllowed(p) {
|
||||||
|
batchResponseItems[i].Error = ErrNonceNotAllowed.Error()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
plaintext, err := p.Decrypt(item.DecodedContext, item.DecodedNonce, item.Ciphertext)
|
plaintext, err := p.Decrypt(item.DecodedContext, item.DecodedNonce, item.Ciphertext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:security
|
||||||
|
secrets/transit: fix a regression that was honoring nonces provided in non-convergent modes during encryption.
|
||||||
|
```
|
|
@ -2009,9 +2009,15 @@ func (p *Policy) EncryptWithFactory(ver int, context []byte, nonce []byte, value
|
||||||
|
|
||||||
encBytes := 32
|
encBytes := 32
|
||||||
hmacBytes := 0
|
hmacBytes := 0
|
||||||
if p.convergentVersion(ver) > 2 {
|
convergentVersion := p.convergentVersion(ver)
|
||||||
|
if convergentVersion > 2 {
|
||||||
deriveHMAC = true
|
deriveHMAC = true
|
||||||
hmacBytes = 32
|
hmacBytes = 32
|
||||||
|
if len(nonce) > 0 {
|
||||||
|
return "", errutil.UserError{Err: "nonce provided when not allowed"}
|
||||||
|
}
|
||||||
|
} else if len(nonce) > 0 && (!p.ConvergentEncryption || convergentVersion != 1) {
|
||||||
|
return "", errutil.UserError{Err: "nonce provided when not allowed"}
|
||||||
}
|
}
|
||||||
if p.Type == KeyType_AES128_GCM96 {
|
if p.Type == KeyType_AES128_GCM96 {
|
||||||
encBytes = 16
|
encBytes = 16
|
||||||
|
|
Loading…
Reference in New Issue