package transit import ( "context" "testing" "github.com/hashicorp/vault/sdk/logical" "github.com/mitchellh/mapstructure" ) // Case1: Ensure that batch encryption did not affect the normal flow of // encrypting the plaintext with a pre-existing key. func TestTransit_BatchEncryptionCase1(t *testing.T) { var resp *logical.Response var err error b, s := createBackendWithStorage(t) // Create the policy policyReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "keys/existing_key", Storage: s, } resp, err = b.HandleRequest(context.Background(), policyReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" // "the quick brown fox" encData := map[string]interface{}{ "plaintext": plaintext, } encReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "encrypt/existing_key", Storage: s, Data: encData, } resp, err = b.HandleRequest(context.Background(), encReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } ciphertext := resp.Data["ciphertext"] decData := map[string]interface{}{ "ciphertext": ciphertext, } decReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "decrypt/existing_key", Storage: s, Data: decData, } resp, err = b.HandleRequest(context.Background(), decReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } if resp.Data["plaintext"] != plaintext { t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) } } // Case2: Ensure that batch encryption did not affect the normal flow of // encrypting the plaintext with the key upserted. func TestTransit_BatchEncryptionCase2(t *testing.T) { var resp *logical.Response var err error b, s := createBackendWithStorage(t) // Upsert the key and encrypt the data plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" encData := map[string]interface{}{ "plaintext": plaintext, } encReq := &logical.Request{ Operation: logical.CreateOperation, Path: "encrypt/upserted_key", Storage: s, Data: encData, } resp, err = b.HandleRequest(context.Background(), encReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } ciphertext := resp.Data["ciphertext"] decData := map[string]interface{}{ "ciphertext": ciphertext, } policyReq := &logical.Request{ Operation: logical.ReadOperation, Path: "keys/upserted_key", Storage: s, } resp, err = b.HandleRequest(context.Background(), policyReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } decReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "decrypt/upserted_key", Storage: s, Data: decData, } resp, err = b.HandleRequest(context.Background(), decReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } if resp.Data["plaintext"] != plaintext { t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) } } // Case3: If batch encryption input is not base64 encoded, it should fail. func TestTransit_BatchEncryptionCase3(t *testing.T) { var err error b, s := createBackendWithStorage(t) batchInput := `[{"plaintext":"dGhlIHF1aWNrIGJyb3duIGZveA=="}]` batchData := map[string]interface{}{ "batch_input": batchInput, } batchReq := &logical.Request{ Operation: logical.CreateOperation, Path: "encrypt/upserted_key", Storage: s, Data: batchData, } _, err = b.HandleRequest(context.Background(), batchReq) if err == nil { t.Fatal("expected an error") } } // Case4: Test batch encryption with an existing key func TestTransit_BatchEncryptionCase4(t *testing.T) { var resp *logical.Response var err error b, s := createBackendWithStorage(t) policyReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "keys/existing_key", Storage: s, } resp, err = b.HandleRequest(context.Background(), policyReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } batchInput := []interface{}{ map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, } batchData := map[string]interface{}{ "batch_input": batchInput, } batchReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "encrypt/existing_key", Storage: s, Data: batchData, } resp, err = b.HandleRequest(context.Background(), batchReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } batchResponseItems := resp.Data["batch_results"].([]BatchResponseItem) decReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "decrypt/existing_key", Storage: s, } plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" for _, item := range batchResponseItems { decReq.Data = map[string]interface{}{ "ciphertext": item.Ciphertext, } resp, err = b.HandleRequest(context.Background(), decReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } if resp.Data["plaintext"] != plaintext { t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) } } } // Case5: Test batch encryption with an existing derived key func TestTransit_BatchEncryptionCase5(t *testing.T) { var resp *logical.Response var err error b, s := createBackendWithStorage(t) policyData := map[string]interface{}{ "derived": true, } policyReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "keys/existing_key", Storage: s, Data: policyData, } resp, err = b.HandleRequest(context.Background(), policyReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } batchInput := []interface{}{ map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "dmlzaGFsCg=="}, map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "dmlzaGFsCg=="}, } batchData := map[string]interface{}{ "batch_input": batchInput, } batchReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "encrypt/existing_key", Storage: s, Data: batchData, } resp, err = b.HandleRequest(context.Background(), batchReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } batchResponseItems := resp.Data["batch_results"].([]BatchResponseItem) decReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "decrypt/existing_key", Storage: s, } plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" for _, item := range batchResponseItems { decReq.Data = map[string]interface{}{ "ciphertext": item.Ciphertext, "context": "dmlzaGFsCg==", } resp, err = b.HandleRequest(context.Background(), decReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } if resp.Data["plaintext"] != plaintext { t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) } } } // Case6: Test batch encryption with an upserted non-derived key func TestTransit_BatchEncryptionCase6(t *testing.T) { var resp *logical.Response var err error b, s := createBackendWithStorage(t) batchInput := []interface{}{ map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, } batchData := map[string]interface{}{ "batch_input": batchInput, } batchReq := &logical.Request{ Operation: logical.CreateOperation, Path: "encrypt/upserted_key", Storage: s, Data: batchData, } resp, err = b.HandleRequest(context.Background(), batchReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } batchResponseItems := resp.Data["batch_results"].([]BatchResponseItem) decReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "decrypt/upserted_key", Storage: s, } plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" for _, responseItem := range batchResponseItems { var item BatchResponseItem if err := mapstructure.Decode(responseItem, &item); err != nil { t.Fatal(err) } decReq.Data = map[string]interface{}{ "ciphertext": item.Ciphertext, } resp, err = b.HandleRequest(context.Background(), decReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } if resp.Data["plaintext"] != plaintext { t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) } } } // Case7: Test batch encryption with an upserted derived key func TestTransit_BatchEncryptionCase7(t *testing.T) { var resp *logical.Response var err error b, s := createBackendWithStorage(t) batchInput := []interface{}{ map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "dmlzaGFsCg=="}, map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "dmlzaGFsCg=="}, } batchData := map[string]interface{}{ "batch_input": batchInput, } batchReq := &logical.Request{ Operation: logical.CreateOperation, Path: "encrypt/upserted_key", Storage: s, Data: batchData, } resp, err = b.HandleRequest(context.Background(), batchReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } batchResponseItems := resp.Data["batch_results"].([]BatchResponseItem) decReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "decrypt/upserted_key", Storage: s, } plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" for _, item := range batchResponseItems { decReq.Data = map[string]interface{}{ "ciphertext": item.Ciphertext, "context": "dmlzaGFsCg==", } resp, err = b.HandleRequest(context.Background(), decReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } if resp.Data["plaintext"] != plaintext { t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) } } } // Case8: If plaintext is not base64 encoded, encryption should fail func TestTransit_BatchEncryptionCase8(t *testing.T) { var resp *logical.Response var err error b, s := createBackendWithStorage(t) // Create the policy policyReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "keys/existing_key", Storage: s, } resp, err = b.HandleRequest(context.Background(), policyReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } batchInput := []interface{}{ map[string]interface{}{"plaintext": "simple_plaintext"}, } batchData := map[string]interface{}{ "batch_input": batchInput, } batchReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "encrypt/existing_key", Storage: s, Data: batchData, } resp, err = b.HandleRequest(context.Background(), batchReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } plaintext := "simple plaintext" encData := map[string]interface{}{ "plaintext": plaintext, } encReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "encrypt/existing_key", Storage: s, Data: encData, } resp, err = b.HandleRequest(context.Background(), encReq) if err == nil { t.Fatal("expected an error") } } // Case9: If both plaintext and batch inputs are supplied, plaintext should be // ignored. func TestTransit_BatchEncryptionCase9(t *testing.T) { var resp *logical.Response var err error b, s := createBackendWithStorage(t) batchInput := []interface{}{ map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, } plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" batchData := map[string]interface{}{ "batch_input": batchInput, "plaintext": plaintext, } batchReq := &logical.Request{ Operation: logical.CreateOperation, Path: "encrypt/upserted_key", Storage: s, Data: batchData, } resp, err = b.HandleRequest(context.Background(), batchReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } _, ok := resp.Data["ciphertext"] if ok { t.Fatal("ciphertext field should not be set") } } // Case10: Inconsistent presence of 'context' in batch input should be caught func TestTransit_BatchEncryptionCase10(t *testing.T) { var err error b, s := createBackendWithStorage(t) batchInput := []interface{}{ map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "dmlzaGFsCg=="}, } batchData := map[string]interface{}{ "batch_input": batchInput, } batchReq := &logical.Request{ Operation: logical.CreateOperation, Path: "encrypt/upserted_key", Storage: s, Data: batchData, } _, err = b.HandleRequest(context.Background(), batchReq) if err == nil { t.Fatalf("expected an error") } } // Case11: Incorrect inputs for context and nonce should not fail the operation func TestTransit_BatchEncryptionCase11(t *testing.T) { var err error b, s := createBackendWithStorage(t) batchInput := []interface{}{ map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "dmlzaGFsCg=="}, map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "not-encoded"}, } batchData := map[string]interface{}{ "batch_input": batchInput, } batchReq := &logical.Request{ Operation: logical.CreateOperation, Path: "encrypt/upserted_key", Storage: s, Data: batchData, } _, err = b.HandleRequest(context.Background(), batchReq) if err != nil { t.Fatal(err) } } // Case12: Invalid batch input func TestTransit_BatchEncryptionCase12(t *testing.T) { var err error b, s := createBackendWithStorage(t) batchInput := []interface{}{ map[string]interface{}{}, "unexpected_interface", } batchData := map[string]interface{}{ "batch_input": batchInput, } batchReq := &logical.Request{ Operation: logical.CreateOperation, Path: "encrypt/upserted_key", Storage: s, Data: batchData, } _, err = b.HandleRequest(context.Background(), batchReq) if err == nil { t.Fatalf("expected an error") } }