Merge pull request #1696 from hashicorp/transit-convergent-specify-nonce
Require nonce specification for more flexibility
This commit is contained in:
commit
0a67bcb5bd
|
@ -29,9 +29,10 @@ FEATURES:
|
|||
deprecates App-ID. [GH-1426]
|
||||
* **Convergent Encryption in `Transit`**: The `transit` backend now supports a
|
||||
convergent encryption mode where the same plaintext will produce the same
|
||||
ciphertext. Although very useful in some situations, this has security
|
||||
implications, which are mostly mitigated by requiring the use of key
|
||||
derivation when convergent encryption is enabled. See [the `transit`
|
||||
ciphertext. Although very useful in some situations, this has potential
|
||||
security implications, which are mostly mitigated by requiring the use of
|
||||
key derivation when convergent encryption is enabled. See [the `transit`
|
||||
backend
|
||||
documentation](https://www.vaultproject.io/docs/secrets/transit/index.html)
|
||||
for more details. [GH-1537]
|
||||
* **Improved LDAP Group Filters**: The `ldap` auth backend now uses templates
|
||||
|
|
|
@ -56,19 +56,6 @@ func TestBackend_upsert(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestBackend_upsert_convergent(t *testing.T) {
|
||||
decryptData := make(map[string]interface{})
|
||||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Factory: Factory,
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepReadPolicy(t, "test", true, false),
|
||||
testAccStepEncryptUpsertConvergent(t, "test", testPlaintext, decryptData),
|
||||
testAccStepReadPolicy(t, "test", false, false),
|
||||
testAccStepDecrypt(t, "test", testPlaintext, decryptData),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestBackend_datakey(t *testing.T) {
|
||||
dataKeyInfo := make(map[string]interface{})
|
||||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
|
@ -323,30 +310,6 @@ func testAccStepEncryptUpsert(
|
|||
}
|
||||
}
|
||||
|
||||
func testAccStepEncryptUpsertConvergent(
|
||||
t *testing.T, name, plaintext string, decryptData map[string]interface{}) logicaltest.TestStep {
|
||||
return logicaltest.TestStep{
|
||||
Operation: logical.CreateOperation,
|
||||
Path: "encrypt/" + name,
|
||||
Data: map[string]interface{}{
|
||||
"plaintext": base64.StdEncoding.EncodeToString([]byte(plaintext)),
|
||||
},
|
||||
Check: func(resp *logical.Response) error {
|
||||
var d struct {
|
||||
Ciphertext string `mapstructure:"ciphertext"`
|
||||
}
|
||||
if err := mapstructure.Decode(resp.Data, &d); err != nil {
|
||||
return err
|
||||
}
|
||||
if d.Ciphertext == "" {
|
||||
return fmt.Errorf("missing ciphertext")
|
||||
}
|
||||
decryptData["ciphertext"] = d.Ciphertext
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testAccStepEncryptContext(
|
||||
t *testing.T, name, plaintext, context string, decryptData map[string]interface{}) logicaltest.TestStep {
|
||||
return logicaltest.TestStep{
|
||||
|
@ -633,7 +596,8 @@ func TestConvergentEncryption(t *testing.T) {
|
|||
req.Path = "encrypt/testkey"
|
||||
req.Data = map[string]interface{}{
|
||||
"plaintext": "emlwIHphcA==", // "zip zap"
|
||||
"context": "Zm9vIGJhcg==", // "foo bar"
|
||||
"nonce": "Zm9vIGJhcg==", // "foo bar"
|
||||
"context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S",
|
||||
}
|
||||
resp, err = b.HandleRequest(req)
|
||||
if resp == nil {
|
||||
|
@ -643,10 +607,21 @@ func TestConvergentEncryption(t *testing.T) {
|
|||
t.Fatalf("expected error response, got %#v", *resp)
|
||||
}
|
||||
|
||||
// Ensure we fail if we do not provide a nonce
|
||||
req.Data = map[string]interface{}{
|
||||
"plaintext": "emlwIHphcA==", // "zip zap"
|
||||
"context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S",
|
||||
}
|
||||
resp, err = b.HandleRequest(req)
|
||||
if err == nil && (resp == nil || !resp.IsError()) {
|
||||
t.Fatal("expected error response")
|
||||
}
|
||||
|
||||
// Now test encrypting the same value twice
|
||||
req.Data = map[string]interface{}{
|
||||
"plaintext": "emlwIHphcA==", // "zip zap"
|
||||
"context": "b25ldHdvdGhyZWVl", // "onetwothreee"
|
||||
"nonce": "b25ldHdvdGhyZWVl", // "onetwothreee"
|
||||
"context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S",
|
||||
}
|
||||
resp, err = b.HandleRequest(req)
|
||||
if resp == nil {
|
||||
|
@ -670,10 +645,11 @@ func TestConvergentEncryption(t *testing.T) {
|
|||
t.Fatalf("expected the same ciphertext but got %s and %s", ciphertext1, ciphertext2)
|
||||
}
|
||||
|
||||
// For sanity, also check a different value
|
||||
// For sanity, also check a different nonce value...
|
||||
req.Data = map[string]interface{}{
|
||||
"plaintext": "emlwIHphcA==", // "zip zap"
|
||||
"context": "dHdvdGhyZWVmb3Vy", // "twothreefour"
|
||||
"nonce": "dHdvdGhyZWVmb3Vy", // "twothreefour"
|
||||
"context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S",
|
||||
}
|
||||
resp, err = b.HandleRequest(req)
|
||||
if resp == nil {
|
||||
|
@ -694,12 +670,45 @@ func TestConvergentEncryption(t *testing.T) {
|
|||
ciphertext4 := resp.Data["ciphertext"].(string)
|
||||
|
||||
if ciphertext3 != ciphertext4 {
|
||||
t.Fatalf("expected the same ciphertext but got %s and %s", ciphertext1, ciphertext2)
|
||||
t.Fatalf("expected the same ciphertext but got %s and %s", ciphertext3, ciphertext4)
|
||||
}
|
||||
if ciphertext1 == ciphertext3 {
|
||||
t.Fatalf("expected different ciphertexts")
|
||||
}
|
||||
|
||||
// ...and a different context value
|
||||
req.Data = map[string]interface{}{
|
||||
"plaintext": "emlwIHphcA==", // "zip zap"
|
||||
"nonce": "dHdvdGhyZWVmb3Vy", // "twothreefour"
|
||||
"context": "qV4h9iQyvn+raODOer4JNAsOhkXBwdT4HZ677Ql4KLqXSU+Jk4C/fXBWbv6xkSYT",
|
||||
}
|
||||
resp, err = b.HandleRequest(req)
|
||||
if resp == nil {
|
||||
t.Fatal("expected non-nil response")
|
||||
}
|
||||
if resp.IsError() {
|
||||
t.Fatalf("got error response: %#v", *resp)
|
||||
}
|
||||
ciphertext5 := resp.Data["ciphertext"].(string)
|
||||
|
||||
resp, err = b.HandleRequest(req)
|
||||
if resp == nil {
|
||||
t.Fatal("expected non-nil response")
|
||||
}
|
||||
if resp.IsError() {
|
||||
t.Fatalf("got error response: %#v", *resp)
|
||||
}
|
||||
ciphertext6 := resp.Data["ciphertext"].(string)
|
||||
|
||||
if ciphertext5 != ciphertext6 {
|
||||
t.Fatalf("expected the same ciphertext but got %s and %s", ciphertext5, ciphertext6)
|
||||
}
|
||||
if ciphertext1 == ciphertext5 {
|
||||
t.Fatalf("expected different ciphertexts")
|
||||
}
|
||||
if ciphertext3 == ciphertext5 {
|
||||
t.Fatalf("expected different ciphertexts")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolicyFuzzing(t *testing.T) {
|
||||
|
|
|
@ -132,7 +132,7 @@ func (lm *lockManager) GetPolicyExclusive(storage logical.Storage, name string)
|
|||
// Get the policy with a read lock; if it returns that an exclusive lock is
|
||||
// needed, retry. If successful, call one more time to get a read lock and
|
||||
// return the value.
|
||||
func (lm *lockManager) GetPolicyUpsert(storage logical.Storage, name string, derived bool, convergent bool) (*Policy, *sync.RWMutex, bool, error) {
|
||||
func (lm *lockManager) GetPolicyUpsert(storage logical.Storage, name string, derived, convergent bool) (*Policy, *sync.RWMutex, bool, error) {
|
||||
p, lock, _, err := lm.getPolicyCommon(storage, name, true, derived, convergent, shared)
|
||||
if err == nil ||
|
||||
(err != nil && err != errNeedExclusiveLock) {
|
||||
|
|
|
@ -30,6 +30,11 @@ ciphertext; "wrapped" will return the ciphertext only.`,
|
|||
Description: "Context for key derivation. Required for derived keys.",
|
||||
},
|
||||
|
||||
"nonce": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Nonce for when convergent encryption is used",
|
||||
},
|
||||
|
||||
"bits": &framework.FieldSchema{
|
||||
Type: framework.TypeInt,
|
||||
Description: `Number of bits for the key; currently 128, 256,
|
||||
|
@ -61,17 +66,28 @@ func (b *backend) pathDatakeyWrite(
|
|||
return logical.ErrorResponse("Invalid path, must be 'plaintext' or 'wrapped'"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// Decode the context if any
|
||||
contextRaw := d.Get("context").(string)
|
||||
var context []byte
|
||||
if len(contextRaw) != 0 {
|
||||
var err error
|
||||
context, err = base64.StdEncoding.DecodeString(contextRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse("failed to decode context as base64"), logical.ErrInvalidRequest
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the nonce if any
|
||||
nonceRaw := d.Get("nonce").(string)
|
||||
var nonce []byte
|
||||
if len(nonceRaw) != 0 {
|
||||
nonce, err = base64.StdEncoding.DecodeString(nonceRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse("failed to decode nonce as base64"), logical.ErrInvalidRequest
|
||||
}
|
||||
}
|
||||
|
||||
// Get the policy
|
||||
p, lock, err := b.lm.GetPolicyShared(req.Storage, name)
|
||||
if lock != nil {
|
||||
|
@ -100,7 +116,7 @@ func (b *backend) pathDatakeyWrite(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext, err := p.Encrypt(context, base64.StdEncoding.EncodeToString(newKey))
|
||||
ciphertext, err := p.Encrypt(context, nonce, base64.StdEncoding.EncodeToString(newKey))
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case errutil.UserError:
|
||||
|
|
|
@ -27,6 +27,11 @@ func (b *backend) pathDecrypt() *framework.Path {
|
|||
Type: framework.TypeString,
|
||||
Description: "Context for key derivation. Required for derived keys.",
|
||||
},
|
||||
|
||||
"nonce": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Nonce for when convergent encryption is used",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
|
@ -46,17 +51,28 @@ func (b *backend) pathDecryptWrite(
|
|||
return logical.ErrorResponse("missing ciphertext to decrypt"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// Decode the context if any
|
||||
contextRaw := d.Get("context").(string)
|
||||
var context []byte
|
||||
if len(contextRaw) != 0 {
|
||||
var err error
|
||||
context, err = base64.StdEncoding.DecodeString(contextRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse("failed to decode context as base64"), logical.ErrInvalidRequest
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the nonce if any
|
||||
nonceRaw := d.Get("nonce").(string)
|
||||
var nonce []byte
|
||||
if len(nonceRaw) != 0 {
|
||||
nonce, err = base64.StdEncoding.DecodeString(nonceRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse("failed to decode nonce as base64"), logical.ErrInvalidRequest
|
||||
}
|
||||
}
|
||||
|
||||
// Get the policy
|
||||
p, lock, err := b.lm.GetPolicyShared(req.Storage, name)
|
||||
if lock != nil {
|
||||
|
@ -69,7 +85,7 @@ func (b *backend) pathDecryptWrite(
|
|||
return logical.ErrorResponse("policy not found"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
plaintext, err := p.Decrypt(context, ciphertext)
|
||||
plaintext, err := p.Decrypt(context, nonce, ciphertext)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case errutil.UserError:
|
||||
|
|
|
@ -28,6 +28,11 @@ func (b *backend) pathEncrypt() *framework.Path {
|
|||
Type: framework.TypeString,
|
||||
Description: "Context for key derivation. Required for derived keys.",
|
||||
},
|
||||
|
||||
"nonce": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Nonce for when convergent encryption is used",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
|
@ -63,10 +68,11 @@ func (b *backend) pathEncryptWrite(
|
|||
return logical.ErrorResponse("missing plaintext to encrypt"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// Decode the context if any
|
||||
contextRaw := d.Get("context").(string)
|
||||
var context []byte
|
||||
var err error
|
||||
if len(contextRaw) != 0 {
|
||||
context, err = base64.StdEncoding.DecodeString(contextRaw)
|
||||
if err != nil {
|
||||
|
@ -74,6 +80,16 @@ func (b *backend) pathEncryptWrite(
|
|||
}
|
||||
}
|
||||
|
||||
// Decode the nonce if any
|
||||
nonceRaw := d.Get("nonce").(string)
|
||||
var nonce []byte
|
||||
if len(nonceRaw) != 0 {
|
||||
nonce, err = base64.StdEncoding.DecodeString(nonceRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse("failed to decode nonce as base64"), logical.ErrInvalidRequest
|
||||
}
|
||||
}
|
||||
|
||||
// Get the policy
|
||||
var p *Policy
|
||||
var lock *sync.RWMutex
|
||||
|
@ -93,7 +109,7 @@ func (b *backend) pathEncryptWrite(
|
|||
return logical.ErrorResponse("policy not found"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
ciphertext, err := p.Encrypt(context, value)
|
||||
ciphertext, err := p.Encrypt(context, nonce, value)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case errutil.UserError:
|
||||
|
|
|
@ -18,23 +18,25 @@ func (b *backend) pathKeys() *framework.Path {
|
|||
},
|
||||
|
||||
"derived": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Description: "Enables key derivation mode. This allows for per-transaction unique keys",
|
||||
Type: framework.TypeBool,
|
||||
Description: `Enables key derivation mode. This
|
||||
allows for per-transaction unique keys.`,
|
||||
},
|
||||
|
||||
"convergent_encryption": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Description: `Whether to use convergent encryption.
|
||||
Description: `Whether to support convergent encryption.
|
||||
This is only supported when using a key with
|
||||
key derivation enabled and will require all
|
||||
context values to be 96 bits (12 bytes) when
|
||||
base64-decoded. This mode ensures that when
|
||||
the same context is supplied, the same
|
||||
ciphertext is emitted from the encryption
|
||||
function. It is *very important* when using
|
||||
this mode that you ensure that all contexts
|
||||
are *globally unique*. Failing to do so will
|
||||
severely impact the security of the key.`,
|
||||
requests to carry both a context and 96-bit
|
||||
(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.`,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -27,6 +27,11 @@ func (b *backend) pathRewrap() *framework.Path {
|
|||
Type: framework.TypeString,
|
||||
Description: "Context for key derivation. Required for derived keys.",
|
||||
},
|
||||
|
||||
"nonce": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Nonce for when convergent encryption is used",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
|
@ -47,17 +52,28 @@ func (b *backend) pathRewrapWrite(
|
|||
return logical.ErrorResponse("missing ciphertext to decrypt"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// Decode the context if any
|
||||
contextRaw := d.Get("context").(string)
|
||||
var context []byte
|
||||
if len(contextRaw) != 0 {
|
||||
var err error
|
||||
context, err = base64.StdEncoding.DecodeString(contextRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse("failed to decode context as base64"), logical.ErrInvalidRequest
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the nonce if any
|
||||
nonceRaw := d.Get("nonce").(string)
|
||||
var nonce []byte
|
||||
if len(nonceRaw) != 0 {
|
||||
nonce, err = base64.StdEncoding.DecodeString(nonceRaw)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse("failed to decode nonce as base64"), logical.ErrInvalidRequest
|
||||
}
|
||||
}
|
||||
|
||||
// Get the policy
|
||||
p, lock, err := b.lm.GetPolicyShared(req.Storage, name)
|
||||
if lock != nil {
|
||||
|
@ -71,7 +87,7 @@ func (b *backend) pathRewrapWrite(
|
|||
return logical.ErrorResponse("policy not found"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
plaintext, err := p.Decrypt(context, value)
|
||||
plaintext, err := p.Decrypt(context, nonce, value)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case errutil.UserError:
|
||||
|
@ -87,7 +103,7 @@ func (b *backend) pathRewrapWrite(
|
|||
return nil, fmt.Errorf("empty plaintext returned during rewrap")
|
||||
}
|
||||
|
||||
ciphertext, err := p.Encrypt(context, plaintext)
|
||||
ciphertext, err := p.Encrypt(context, nonce, plaintext)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case errutil.UserError:
|
||||
|
|
|
@ -307,10 +307,6 @@ func (p *Policy) DeriveKey(context []byte, ver int) ([]byte, error) {
|
|||
return nil, errutil.InternalError{Err: "unable to access the key; no key versions found"}
|
||||
}
|
||||
|
||||
if p.LatestVersion == 0 {
|
||||
return nil, errutil.InternalError{Err: "unable to access the key; no key versions found"}
|
||||
}
|
||||
|
||||
if ver <= 0 || ver > p.LatestVersion {
|
||||
return nil, errutil.UserError{Err: "invalid key version"}
|
||||
}
|
||||
|
@ -335,7 +331,7 @@ func (p *Policy) DeriveKey(context []byte, ver int) ([]byte, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Policy) Encrypt(context []byte, value string) (string, error) {
|
||||
func (p *Policy) Encrypt(context, nonce []byte, value string) (string, error) {
|
||||
// Decode the plaintext value
|
||||
plaintext, err := base64.StdEncoding.DecodeString(value)
|
||||
if err != nil {
|
||||
|
@ -367,15 +363,12 @@ func (p *Policy) Encrypt(context []byte, value string) (string, error) {
|
|||
return "", errutil.InternalError{Err: err.Error()}
|
||||
}
|
||||
|
||||
if p.ConvergentEncryption && len(context) != gcm.NonceSize() {
|
||||
return "", errutil.UserError{Err: fmt.Sprintf("base64-decoded context must be %d bytes long when using convergent encryption with this key", gcm.NonceSize())}
|
||||
}
|
||||
|
||||
// Compute random nonce
|
||||
var nonce []byte
|
||||
if p.ConvergentEncryption {
|
||||
nonce = context
|
||||
if len(nonce) != gcm.NonceSize() {
|
||||
return "", errutil.UserError{Err: fmt.Sprintf("base64-decoded nonce must be %d bytes long when using convergent encryption with this key", gcm.NonceSize())}
|
||||
}
|
||||
} else {
|
||||
// Compute random nonce
|
||||
nonce = make([]byte, gcm.NonceSize())
|
||||
_, err = rand.Read(nonce)
|
||||
if err != nil {
|
||||
|
@ -387,7 +380,10 @@ func (p *Policy) Encrypt(context []byte, value string) (string, error) {
|
|||
out := gcm.Seal(nil, nonce, plaintext, nil)
|
||||
|
||||
// Place the encrypted data after the nonce
|
||||
full := append(nonce, out...)
|
||||
full := out
|
||||
if !p.ConvergentEncryption {
|
||||
full = append(nonce, out...)
|
||||
}
|
||||
|
||||
// Convert to base64
|
||||
encoded := base64.StdEncoding.EncodeToString(full)
|
||||
|
@ -398,12 +394,16 @@ func (p *Policy) Encrypt(context []byte, value string) (string, error) {
|
|||
return encoded, nil
|
||||
}
|
||||
|
||||
func (p *Policy) Decrypt(context []byte, value string) (string, error) {
|
||||
func (p *Policy) Decrypt(context, nonce []byte, value string) (string, error) {
|
||||
// Verify the prefix
|
||||
if !strings.HasPrefix(value, "vault:v") {
|
||||
return "", errutil.UserError{Err: "invalid ciphertext: no prefix"}
|
||||
}
|
||||
|
||||
if p.ConvergentEncryption && (nonce == nil || len(nonce) == 0) {
|
||||
return "", errutil.UserError{Err: "invalid convergent nonce supplied"}
|
||||
}
|
||||
|
||||
splitVerCiphertext := strings.SplitN(strings.TrimPrefix(value, "vault:v"), ":", 2)
|
||||
if len(splitVerCiphertext) != 2 {
|
||||
return "", errutil.UserError{Err: "invalid ciphertext: wrong number of fields"}
|
||||
|
@ -460,8 +460,13 @@ func (p *Policy) Decrypt(context []byte, value string) (string, error) {
|
|||
}
|
||||
|
||||
// Extract the nonce and ciphertext
|
||||
nonce := decoded[:gcm.NonceSize()]
|
||||
ciphertext := decoded[gcm.NonceSize():]
|
||||
var ciphertext []byte
|
||||
if p.ConvergentEncryption {
|
||||
ciphertext = decoded
|
||||
} else {
|
||||
nonce = decoded[:gcm.NonceSize()]
|
||||
ciphertext = decoded[gcm.NonceSize():]
|
||||
}
|
||||
|
||||
// Verify and Decrypt
|
||||
plain, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
|
|
|
@ -33,7 +33,7 @@ data's attack surface.
|
|||
Key derivation is supported, which allows the same key to be used for multiple
|
||||
purposes by deriving a new key based on a user-supplied context value. In this
|
||||
mode, convergent encryption can optionally be supported, which allows the same
|
||||
context and plaintext to produce the same ciphertext.
|
||||
input values to produce the same ciphertext.
|
||||
|
||||
The backend also supports key rotation, which allows a new version of the named
|
||||
key to be generated. All data encrypted with the key will use the newest
|
||||
|
@ -156,12 +156,16 @@ only encrypt or decrypt using the named keys they need access to.
|
|||
<span class="param-flags">optional</span>
|
||||
If set, the key will support convergent encryption, where the same
|
||||
plaintext creates the same ciphertext. This requires _derived_ to be
|
||||
set to `true`. When enabled, the context value must be exactly 12 bytes
|
||||
(96 bits) and will both be used to derive the key and as the nonce for
|
||||
the encryption operation. Note that while this is useful for particular
|
||||
situations, it also has security implications. In particular, you must
|
||||
ensure that you do **not** use the same context value for more than one
|
||||
plaintext value. Defaults to false.
|
||||
set to `true`. When enabled, each
|
||||
encryption(/decryption/rewrap/datakey) operation will require a `nonce`
|
||||
value to be specified. Note that while this is useful for particular
|
||||
situations, all nonce values used with a given context value **must be
|
||||
unique** or it will compromise the security of your key. A common way
|
||||
to use this will be to generate a unique identifier for the given data
|
||||
(for instance, a SHA-512 sum), then separate the bytes so that twelve
|
||||
bytes are used as the nonce and the remaining as the context, ensuring
|
||||
that all bits of unique identity are used as a part of the encryption
|
||||
operation. Defaults to false.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
@ -347,6 +351,15 @@ only encrypt or decrypt using the named keys they need access to.
|
|||
The key derivation context, provided as base64 encoded.
|
||||
Must be provided if derivation is enabled.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">nonce</span>
|
||||
<span class="param-flags">optional</span>
|
||||
The nonce value, provided as base64 encoded. Must be provided if
|
||||
convergent encryption is enabled for this key. The value must be
|
||||
exactly 96 bits (12 bytes) long and the user must ensure that for any
|
||||
given context (and thus, any given encryption key) this nonce value is
|
||||
**never reused**.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
|
@ -393,6 +406,12 @@ only encrypt or decrypt using the named keys they need access to.
|
|||
The key derivation context, provided as base64 encoded.
|
||||
Must be provided if derivation is enabled.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">nonce</span>
|
||||
<span class="param-flags">optional</span>
|
||||
The nonce value used during encryption, provided as base64 encoded.
|
||||
Must be provided if convergent encryption is enabled for this key.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
|
@ -441,6 +460,12 @@ only encrypt or decrypt using the named keys they need access to.
|
|||
The key derivation context, provided as base64 encoded.
|
||||
Must be provided if derivation is enabled.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">nonce</span>
|
||||
<span class="param-flags">optional</span>
|
||||
The nonce value used during encryption, provided as base64 encoded.
|
||||
Must be provided if convergent encryption is enabled for this key.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
|
@ -493,6 +518,15 @@ only encrypt or decrypt using the named keys they need access to.
|
|||
The key derivation context, provided as base64 encoded.
|
||||
Must be provided if derivation is enabled.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">nonce</span>
|
||||
<span class="param-flags">optional</span>
|
||||
The nonce value, provided as base64 encoded. Must be provided if
|
||||
convergent encryption is enabled for this key. The value must be
|
||||
exactly 96 bits (12 bytes) long and the user must ensure that for any
|
||||
given context (and thus, any given encryption key) this nonce value is
|
||||
**never reused**.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">bits</span>
|
||||
<span class="param-flags">optional</span>
|
||||
|
|
Loading…
Reference in New Issue