Decoding TypeORM-Encrypted Data With Elixir
- 2 minutes read - 415 wordsToday’s challege was to figure out how to decrypt an encrypted field created by the TypeORM plugin typeorm-encrypted. Typeorm-encrypted allows you to automatically encrypt/decrypt a field on save/fetch when using TypeORM. This allows you to encrypt sensitive data while at rest in your database.
The Typeorm-encrypted module saves the encrypted (aes-256-gcm) field as a Base64 encoded string in the database. Our challege is to be able to read that field from our database and decrypt the sensitive data for use in our Elixir application.
In order to accomplish, this we’ll utilize the erlang crypto module. We’ll need to read the field from the database using Ecto and then pass it into a function that will do the decrypting and return our original string. Here’s the module with the function, I’ll explain what’s happening after the code blocks.
# config/config.exs
config :appapi_phx,
crypto_key: System.fetch_env!("CRYPTO_KEY")
First we grab the key being used to encrypt the data in TypeORM, which we’ve stored in an environment variable. We’ll use the key later in our decode function.
defmodule MyApp.AesGcm do
@moduledoc """
Add the ability to decrypt gcm encrypted data that is stored in the database as Base 64 strings.
"""
def decode(string) do
# Key used by TypeORM to encode the data
hex_key = Application.fetch_env!(:appapi_phx, :crypto_key)
# Convert the hex key to binary
binary_key = Base.decode16!(hex_key, case: :lower)
# Data field from the database
binary_data = Base.decode64!(string)
# Destructure the binary data into it's parts
<<iv::binary-size(12), cipher_tag::binary-size(16), cipher_text::bitstring>> = binary_data
# Call the erlang fuction to decrypt the data
:crypto.crypto_one_time_aead(:aes_256_gcm, binary_key, iv, cipher_text, <<>>, cipher_tag, false)
end
end
The decode function takes in our Base64 encoded string from the database. The first thing that we’ll need is our key, which is being provided by an enviroment variable. The key for the TypeORM-encrypted module is a 32 bit hex string, so we need to decode that into a binary so we can pass it into the crypto module. Next we convert our Base64 encoded data from the database into binary format. We then pattern match on the binary to break out the IV, Cipher Tag, and encrypted data. We can then call the crypto_one_time_aead method with the Cipher, key, IV, cipher text, AAD, cipher tag, and false for decryption. This should return our original unencrypted data that we put into our database.
# After retrieving the encrypted data field, we pass it into our new function.
original_data = MyApp.AesGcm.decode("YKmKiQGT4JgMjWfuZfnht3e1oc7kXJF3+3eIYShh0wq8ihJCmIYnc9QpsfTB5Ts=")
Hopefully this will demystify encryption just a little bit. Happy coding!