Skip to content

Commit

Permalink
Additional Authenticated Data AAD for Cloud KMS secrets (#3271) (#5968)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
modular-magician authored Mar 24, 2020
1 parent c5687d1 commit 3edcd93
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 5 deletions.
6 changes: 6 additions & 0 deletions .changelog/3271.txt
Original file line number Diff line number Diff line change
@@ -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`
```
8 changes: 8 additions & 0 deletions google/data_source_google_kms_secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ func dataSourceGoogleKmsSecret() *schema.Resource {
Computed: true,
Sensitive: true,
},
"additional_authenticated_data": {
Type: schema.TypeString,
Optional: true,
},
},
}
}
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion google/data_source_google_kms_secret_ciphertext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 42 additions & 2 deletions google/data_source_google_kms_secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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
Expand All @@ -50,14 +51,39 @@ 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
},
},
},
})
}

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]
Expand All @@ -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 {
Expand All @@ -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)
}
21 changes: 21 additions & 0 deletions google/resource_kms_secret_ciphertext.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
32 changes: 30 additions & 2 deletions google/resource_kms_secret_ciphertext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) },
Expand All @@ -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
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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)
}
1 change: 1 addition & 0 deletions website/docs/d/google_kms_secret.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions website/docs/r/kms_secret_ciphertext.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 3edcd93

Please sign in to comment.