I have a need to encrypt OAuth access and refresh tokens stored in the Google Cloud Datastore.
The goal here is to ensure that if the Datastore entities are accessed independently of the code, the OAuth tokens will be encrypted and thus unusable.
This is not intended to protect against situations where both the code and Datastore are exposed together.
To securely store the data, I have leveraged PyCrypto’s AES implementation, and created a custom property type that automatically encrypts/decrypts the properties when accessing them.
The logic is as follows:
To store – I generate a random initialization vector, encrypt the data, then I base64 encode both the initialization vector and the encrypted data, and store them together in a text property.
To retrieve – I fetch the text data, slice off the base64 encoded initialization vector, and proceed to decode then decrypt the remaining data.
In addition to securing my own application, I am considering publishing the details and distributing the relevant code for others, so I want to ensure I have a secure or "correct" implementation of this functionality
(Note I have stripped out App Engine specific code and just included relevant encryption code here, for simplicity. The actual implementation allows it to be dropped into existing Datastore models in a backwards-compatible fashion).
from Crypto.Cipher import AES from Crypto import Random from base64 import b64encode,b64decode from meta_secure import aes_key #aes_key is a 32 digit alphanumeric string (GUID) #encryption scheme def encrypt_value(value): rand = Random.new() init_vector = rand.read(16) aes = AES.new(aes_key,AES.MODE_CFB,init_vector) encrypted = b64encode(aes.encrypt(value)) return '%s%s'%(b64encode(init_vector),encrypted) def decrypt_value(value): init_vector = b64decode(value[:24]) aes = AES.new(aes_key,AES.MODE_CFB,init_vector) decrypted = aes.decrypt(b64decode(value[24:])) return decrypted
Have I used PyCrypto and AES correctly for the goal as stated above?