diff --git a/builtin/logical/transit/backend_test.go b/builtin/logical/transit/backend_test.go index af3f1291d..21d83aec9 100644 --- a/builtin/logical/transit/backend_test.go +++ b/builtin/logical/transit/backend_test.go @@ -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{ @@ -243,6 +230,7 @@ func testAccStepReadPolicy(t *testing.T, name string, expectNone, derived bool) KDFMode string `mapstructure:"kdf_mode"` DeletionAllowed bool `mapstructure:"deletion_allowed"` ConvergentEncryption bool `mapstructure:"convergent_encryption"` + ContextAsNonce bool `mapstructure:"context_as_nonce"` } if err := mapstructure.Decode(resp.Data, &d); err != nil { return err @@ -323,30 +311,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{ @@ -594,6 +558,160 @@ func TestConvergentEncryption(t *testing.T) { System: sysView, }) + req := &logical.Request{ + Storage: storage, + Operation: logical.UpdateOperation, + Path: "keys/testkeynonderived", + Data: map[string]interface{}{ + "derived": false, + "convergent_encryption": true, + }, + } + + resp, err := b.HandleRequest(req) + if err != nil { + t.Fatal(err) + } + if resp == nil { + t.Fatal("expected non-nil response") + } + if !resp.IsError() { + t.Fatalf("bad: expected error response, got %#v", *resp) + } + + req.Path = "keys/testkey" + req.Data = map[string]interface{}{ + "derived": true, + "convergent_encryption": true, + } + + resp, err = b.HandleRequest(req) + if err != nil { + t.Fatal(err) + } + if resp != nil { + t.Fatalf("bad: got resp %#v", *resp) + } + + // First, test using an invalid length of nonce + req.Path = "encrypt/testkey" + req.Data = map[string]interface{}{ + "plaintext": "emlwIHphcA==", // "zip zap" + "nonce": "Zm9vIGJhcg==", // "foo bar" + "context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S", + } + resp, err = b.HandleRequest(req) + if resp == nil { + t.Fatal("expected non-nil response") + } + if !resp.IsError() { + t.Fatalf("expected error response, got %#v", *resp) + } + + // Now test encrypting the same value twice + req.Data = map[string]interface{}{ + "plaintext": "emlwIHphcA==", // "zip zap" + "nonce": "b25ldHdvdGhyZWVl", // "onetwothreee" + "context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S", + } + resp, err = b.HandleRequest(req) + if resp == nil { + t.Fatal("expected non-nil response") + } + if resp.IsError() { + t.Fatalf("got error response: %#v", *resp) + } + ciphertext1 := 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) + } + ciphertext2 := resp.Data["ciphertext"].(string) + + if ciphertext1 != ciphertext2 { + t.Fatalf("expected the same ciphertext but got %s and %s", ciphertext1, ciphertext2) + } + + // For sanity, also check a different nonce value... + req.Data = map[string]interface{}{ + "plaintext": "emlwIHphcA==", // "zip zap" + "nonce": "dHdvdGhyZWVmb3Vy", // "twothreefour" + "context": "pWZ6t/im3AORd0lVYE0zBdKpX6Bl3/SvFtoVTPWbdkzjG788XmMAnOlxandSdd7S", + } + resp, err = b.HandleRequest(req) + if resp == nil { + t.Fatal("expected non-nil response") + } + if resp.IsError() { + t.Fatalf("got error response: %#v", *resp) + } + ciphertext3 := 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) + } + ciphertext4 := resp.Data["ciphertext"].(string) + + if ciphertext3 != ciphertext4 { + 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 TestConvergentEncryptionContextAsNonce(t *testing.T) { + var b *backend + sysView := logical.TestSystemView() + storage := &logical.InmemStorage{} + + b = Backend(&logical.BackendConfig{ + StorageView: storage, + System: sysView, + }) + req := &logical.Request{ Storage: storage, Operation: logical.UpdateOperation, @@ -696,7 +814,7 @@ 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")