From b238a80879b5d2d0278e4286506c63de4007d43f Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Tue, 24 Mar 2020 23:45:37 +0000 Subject: [PATCH] Additional Authenticated Data AAD for Cloud KMS secrets (#3271) * AAD for KMS secrets * add test for decryption * update datasource docs for google_kms_secret (decrypt) * add test for encryption with aad Signed-off-by: Modular Magician --- .changelog/3271.txt | 6 +++ google/data_source_google_kms_secret.go | 8 ++++ ...ource_google_kms_secret_ciphertext_test.go | 2 +- google/data_source_google_kms_secret_test.go | 44 ++++++++++++++++++- google/resource_kms_secret_ciphertext.go | 21 +++++++++ google/resource_kms_secret_ciphertext_test.go | 32 +++++++++++++- .../docs/d/google_kms_secret.html.markdown | 1 + .../r/kms_secret_ciphertext.html.markdown | 4 ++ 8 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 .changelog/3271.txt diff --git a/.changelog/3271.txt b/.changelog/3271.txt new file mode 100644 index 00000000000..236c6bb1456 --- /dev/null +++ b/.changelog/3271.txt @@ -0,0 +1,6 @@ +```release-note:enhancement +kms: Added new field "Additional Authenticated Data" for Cloud KMS data source `google_kms_secret` +``` +```release-note:enhancement +kms: Added new field "Additional Authenticated Data" for Cloud KMS resource `google_kms_secret_ciphertext` +``` diff --git a/google/data_source_google_kms_secret.go b/google/data_source_google_kms_secret.go index 580d0f64749..10b2aaa2213 100644 --- a/google/data_source_google_kms_secret.go +++ b/google/data_source_google_kms_secret.go @@ -27,6 +27,10 @@ func dataSourceGoogleKmsSecret() *schema.Resource { Computed: true, Sensitive: true, }, + "additional_authenticated_data": { + Type: schema.TypeString, + Optional: true, + }, }, } } @@ -46,6 +50,10 @@ func dataSourceGoogleKmsSecretRead(d *schema.ResourceData, meta interface{}) err Ciphertext: ciphertext, } + if aad, ok := d.GetOk("additional_authenticated_data"); ok { + kmsDecryptRequest.AdditionalAuthenticatedData = aad.(string) + } + decryptResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Decrypt(cryptoKeyId.cryptoKeyId(), kmsDecryptRequest).Do() if err != nil { diff --git a/google/data_source_google_kms_secret_ciphertext_test.go b/google/data_source_google_kms_secret_ciphertext_test.go index f13409161fa..d20b40f80bd 100644 --- a/google/data_source_google_kms_secret_ciphertext_test.go +++ b/google/data_source_google_kms_secret_ciphertext_test.go @@ -23,7 +23,7 @@ func TestAccDataKmsSecretCiphertext_basic(t *testing.T) { { Config: testGoogleKmsSecretCiphertext_datasource(kms.CryptoKey.Name, plaintext), Check: func(s *terraform.State) error { - plaintext, err := testAccDecryptSecretDataWithCryptoKey(s, kms.CryptoKey.Name, "data.google_kms_secret_ciphertext.acceptance") + plaintext, err := testAccDecryptSecretDataWithCryptoKey(s, kms.CryptoKey.Name, "data.google_kms_secret_ciphertext.acceptance", "") if err != nil { return err diff --git a/google/data_source_google_kms_secret_test.go b/google/data_source_google_kms_secret_test.go index 1b3d49a8f2f..e7766487d81 100644 --- a/google/data_source_google_kms_secret_test.go +++ b/google/data_source_google_kms_secret_test.go @@ -23,6 +23,7 @@ func TestAccKmsSecret_basic(t *testing.T) { cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) plaintext := fmt.Sprintf("secret-%s", acctest.RandString(10)) + aad := "plainaad" // The first test creates resources needed to encrypt plaintext and produce ciphertext resource.Test(t, resource.TestCase{ @@ -32,7 +33,7 @@ func TestAccKmsSecret_basic(t *testing.T) { { Config: testGoogleKmsCryptoKey_basic(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName), Check: func(s *terraform.State) error { - ciphertext, cryptoKeyId, err := testAccEncryptSecretDataWithCryptoKey(s, "google_kms_crypto_key.crypto_key", plaintext) + ciphertext, cryptoKeyId, err := testAccEncryptSecretDataWithCryptoKey(s, "google_kms_crypto_key.crypto_key", plaintext, "") if err != nil { return err @@ -50,6 +51,31 @@ func TestAccKmsSecret_basic(t *testing.T) { }, }) + return nil + }, + }, + // With AAD + { + Config: testGoogleKmsCryptoKey_basic(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName), + Check: func(s *terraform.State) error { + ciphertext, cryptoKeyId, err := testAccEncryptSecretDataWithCryptoKey(s, "google_kms_crypto_key.crypto_key", plaintext, aad) + + if err != nil { + return err + } + + // The second test asserts that the data source has the correct plaintext, given the created ciphertext + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testGoogleKmsSecret_aadDatasource(cryptoKeyId.terraformId(), ciphertext, base64.StdEncoding.EncodeToString([]byte(aad))), + Check: resource.TestCheckResourceAttr("data.google_kms_secret.acceptance", "plaintext", plaintext), + }, + }, + }) + return nil }, }, @@ -57,7 +83,7 @@ func TestAccKmsSecret_basic(t *testing.T) { }) } -func testAccEncryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyResourceName, plaintext string) (string, *kmsCryptoKeyId, error) { +func testAccEncryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyResourceName, plaintext, aad string) (string, *kmsCryptoKeyId, error) { config := testAccProvider.Meta().(*Config) rs, ok := s.RootModule().Resources[cryptoKeyResourceName] @@ -75,6 +101,10 @@ func testAccEncryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyResource Plaintext: base64.StdEncoding.EncodeToString([]byte(plaintext)), } + if aad != "" { + kmsEncryptRequest.AdditionalAuthenticatedData = base64.StdEncoding.EncodeToString([]byte(aad)) + } + encryptResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Encrypt(cryptoKeyId.cryptoKeyId(), kmsEncryptRequest).Do() if err != nil { @@ -94,3 +124,13 @@ data "google_kms_secret" "acceptance" { } `, cryptoKeyTerraformId, ciphertext) } + +func testGoogleKmsSecret_aadDatasource(cryptoKeyTerraformId, ciphertext, aad string) string { + return fmt.Sprintf(` +data "google_kms_secret" "acceptance" { + crypto_key = "%s" + ciphertext = "%s" + additional_authenticated_data = "%s" +} +`, cryptoKeyTerraformId, ciphertext, aad) +} diff --git a/google/resource_kms_secret_ciphertext.go b/google/resource_kms_secret_ciphertext.go index 712c216fb7e..3533dabcb95 100644 --- a/google/resource_kms_secret_ciphertext.go +++ b/google/resource_kms_secret_ciphertext.go @@ -51,6 +51,13 @@ Format: ''projects/{{project}}/locations/{{location}}/keyRings/{{keyRing}}/crypt Description: `The plaintext to be encrypted.`, Sensitive: true, }, + "additional_authenticated_data": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `The additional authenticated data used for integrity checks during encryption and decryption.`, + Sensitive: true, + }, "ciphertext": { Type: schema.TypeString, Computed: true, @@ -70,6 +77,12 @@ func resourceKMSSecretCiphertextCreate(d *schema.ResourceData, meta interface{}) } else if v, ok := d.GetOkExists("plaintext"); !isEmptyValue(reflect.ValueOf(plaintextProp)) && (ok || !reflect.DeepEqual(v, plaintextProp)) { obj["plaintext"] = plaintextProp } + additionalAuthenticatedDataProp, err := expandKMSSecretCiphertextAdditionalAuthenticatedData(d.Get("additional_authenticated_data"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("additional_authenticated_data"); !isEmptyValue(reflect.ValueOf(additionalAuthenticatedDataProp)) && (ok || !reflect.DeepEqual(v, additionalAuthenticatedDataProp)) { + obj["additionalAuthenticatedData"] = additionalAuthenticatedDataProp + } url, err := replaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}:encrypt") if err != nil { @@ -160,6 +173,14 @@ func expandKMSSecretCiphertextPlaintext(v interface{}, d TerraformResourceData, return base64.StdEncoding.EncodeToString([]byte(v.(string))), nil } +func expandKMSSecretCiphertextAdditionalAuthenticatedData(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + if v == nil { + return nil, nil + } + + return base64.StdEncoding.EncodeToString([]byte(v.(string))), nil +} + func resourceKMSSecretCiphertextDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) { return res, nil } diff --git a/google/resource_kms_secret_ciphertext_test.go b/google/resource_kms_secret_ciphertext_test.go index 03400ca461f..33e8e891ed1 100644 --- a/google/resource_kms_secret_ciphertext_test.go +++ b/google/resource_kms_secret_ciphertext_test.go @@ -18,6 +18,7 @@ func TestAccKmsSecretCiphertext_basic(t *testing.T) { kms := BootstrapKMSKey(t) plaintext := fmt.Sprintf("secret-%s", acctest.RandString(10)) + aad := "plainaad" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -26,7 +27,20 @@ func TestAccKmsSecretCiphertext_basic(t *testing.T) { { Config: testGoogleKmsSecretCiphertext(kms.CryptoKey.Name, plaintext), Check: func(s *terraform.State) error { - plaintext, err := testAccDecryptSecretDataWithCryptoKey(s, kms.CryptoKey.Name, "google_kms_secret_ciphertext.acceptance") + plaintext, err := testAccDecryptSecretDataWithCryptoKey(s, kms.CryptoKey.Name, "google_kms_secret_ciphertext.acceptance", "") + + if err != nil { + return err + } + + return resource.TestCheckResourceAttr("google_kms_secret_ciphertext.acceptance", "plaintext", plaintext)(s) + }, + }, + // With AAD + { + Config: testGoogleKmsSecretCiphertext_withAAD(kms.CryptoKey.Name, plaintext, aad), + Check: func(s *terraform.State) error { + plaintext, err := testAccDecryptSecretDataWithCryptoKey(s, kms.CryptoKey.Name, "google_kms_secret_ciphertext.acceptance", aad) if err != nil { return err @@ -39,7 +53,7 @@ func TestAccKmsSecretCiphertext_basic(t *testing.T) { }) } -func testAccDecryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyId string, secretCiphertextResourceName string) (string, error) { +func testAccDecryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyId string, secretCiphertextResourceName, aad string) (string, error) { config := testAccProvider.Meta().(*Config) rs, ok := s.RootModule().Resources[secretCiphertextResourceName] if !ok { @@ -54,6 +68,10 @@ func testAccDecryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyId strin Ciphertext: ciphertext, } + if aad != "" { + kmsDecryptRequest.AdditionalAuthenticatedData = base64.StdEncoding.EncodeToString([]byte(aad)) + } + decryptResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Decrypt(cryptoKeyId, kmsDecryptRequest).Do() if err != nil { @@ -80,3 +98,13 @@ resource "google_kms_secret_ciphertext" "acceptance" { } `, cryptoKeyTerraformId, plaintext) } + +func testGoogleKmsSecretCiphertext_withAAD(cryptoKeyTerraformId, plaintext, aad string) string { + return fmt.Sprintf(` +resource "google_kms_secret_ciphertext" "acceptance" { + crypto_key = "%s" + plaintext = "%s" + additional_authenticated_data = "%s" +} +`, cryptoKeyTerraformId, plaintext, aad) +} diff --git a/website/docs/d/google_kms_secret.html.markdown b/website/docs/d/google_kms_secret.html.markdown index ce1a7d62862..28a59ddd9b6 100644 --- a/website/docs/d/google_kms_secret.html.markdown +++ b/website/docs/d/google_kms_secret.html.markdown @@ -90,6 +90,7 @@ The following arguments are supported: * `crypto_key` (Required) - The id of the CryptoKey that will be used to decrypt the provided ciphertext. This is represented by the format `{projectId}/{location}/{keyRingName}/{cryptoKeyName}`. +* `additional_authenticated_data` (Optional) - The [additional authenticated data](https://cloud.google.com/kms/docs/additional-authenticated-data) used for integrity checks during encryption and decryption. ## Attributes Reference diff --git a/website/docs/r/kms_secret_ciphertext.html.markdown b/website/docs/r/kms_secret_ciphertext.html.markdown index c1d352b7d5b..4b4290b1886 100644 --- a/website/docs/r/kms_secret_ciphertext.html.markdown +++ b/website/docs/r/kms_secret_ciphertext.html.markdown @@ -103,6 +103,10 @@ The following arguments are supported: - - - +* `additional_authenticated_data` - + (Optional) + The additional authenticated data used for integrity checks during encryption and decryption. + ## Attributes Reference