From f65c46755b56a8d3c7e2a4e31f03d99d5a242a86 Mon Sep 17 00:00:00 2001
From: Russ Amos <rra@google.com>
Date: Wed, 16 Aug 2017 13:08:28 -0400
Subject: [PATCH] KMS: Clean up base64 logic in the encrypt and decrypt
 functions.
 [(#1074)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1074)

The use of base64 is essentially an implementation detail of the Cloud KMS REST
API: it is required only so that arbitrary binary data can be included in a JSON
string, which only allows Unicode characters. Therefore, the "encrypt" sample
function should decode the base64-encoded ciphertext before writing the
file. Similarly, "decrypt" should not assume that an input file is
base64-encoded, but should perform the base64-encoding itself before sending the
encrypted data to KMS.

This aligns with how the "gcloud kms encrypt" and "gcloud kms decrypt" commands
behave. See https://stackoverflow.com/q/45699472 for an example of user
confusion caused by the mismatch.
---
 .../samples/snippets/snippets.py                | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/packages/google-cloud-kms/samples/snippets/snippets.py b/packages/google-cloud-kms/samples/snippets/snippets.py
index 8196c5840f03..9400ffdf066a 100644
--- a/packages/google-cloud-kms/samples/snippets/snippets.py
+++ b/packages/google-cloud-kms/samples/snippets/snippets.py
@@ -77,17 +77,18 @@ def encrypt(project_id, location, keyring, cryptokey, plaintext_file_name,
     # Read text from the input file.
     with io.open(plaintext_file_name, 'rb') as plaintext_file:
         plaintext = plaintext_file.read()
-    encoded_text = base64.b64encode(plaintext)
 
     # Use the KMS API to encrypt the text.
     cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys()
     request = cryptokeys.encrypt(
-        name=name, body={'plaintext': encoded_text.decode('utf-8')})
+        name=name,
+        body={'plaintext': base64.b64encode(plaintext).decode('ascii')})
     response = request.execute()
+    ciphertext = base64.b64decode(response['ciphertext'].encode('ascii'))
 
     # Write the encrypted text to a file.
     with io.open(encrypted_file_name, 'wb') as encrypted_file:
-        encrypted_file.write(response['ciphertext'].encode('utf-8'))
+        encrypted_file.write(ciphertext)
 
     print('Saved encrypted text to {}.'.format(encrypted_file_name))
 # [END kms_encrypt]
@@ -109,19 +110,19 @@ def decrypt(project_id, location, keyring, cryptokey, encrypted_file_name,
 
     # Read cipher text from the input file.
     with io.open(encrypted_file_name, 'rb') as encrypted_file:
-        cipher_text = encrypted_file.read()
+        ciphertext = encrypted_file.read()
 
     # Use the KMS API to decrypt the text.
     cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys()
     request = cryptokeys.decrypt(
-        name=name, body={'ciphertext': cipher_text.decode('utf-8')})
+        name=name,
+        body={'ciphertext': base64.b64encode(ciphertext).decode('ascii')})
     response = request.execute()
+    plaintext = base64.b64decode(response['plaintext'].encode('ascii'))
 
     # Write the plain text to a file.
     with io.open(decrypted_file_name, 'wb') as decrypted_file:
-        plaintext_encoded = response['plaintext']
-        plaintext_decoded = base64.b64decode(plaintext_encoded)
-        decrypted_file.write(plaintext_decoded)
+        decrypted_file.write(plaintext)
 
     print('Saved decrypted text to {}.'.format(decrypted_file_name))
 # [END kms_decrypt]