TinyAES (TinyAES v0.1.0)
View SourceTinyAES is a lightweight, dependency-free Elixir wrapper for AES-256-GCM encryption and decryption
using Erlang's :crypto
module. It provides robust error handling, support for Additional
Authenticated Data (AAD), and a simple API. The encryption key is securely retrieved from
the ENCRYPTION_KEY
environment variable.
Features
- Lightweight: Less than 50 lines of core code.
- Dependency-Free: Relies only on Erlang's
:crypto
module. - Robust Error Handling: Covers returned
:error
, raised{etag, reason, stack}
exceptions, and key validation errors. - AES-256-GCM: Secure, authenticated encryption with 16-byte IV and tag.
- AAD Support: Optional Additional Authenticated Data for enhanced security.
- Key Management: Environment-based key retrieval with Base64 encoding.
- 3-tuples Compatible: Handles the latest
:crypto
exceptions formats introduced with Erlang OTP 25.
Setup
Generate a 32-byte encryption key, and add it to your .env
file:
mix run -e 'TinyAES.puts_generate_key_env()'
# Copy the output to .env:
ENCRYPTION_KEY=your_base64_encoded_key_here
Add the output to your .env
file as ENCRYPTION_KEY=your_base64_encoded_key_here
.
Ensure the ENCRYPTION_KEY
environment variable is set in your application.
Usage
plaintext = "Hello, world!"
ciphertext = TinyAES.encrypt(plaintext, "optional_aad")
{:ok, decrypted} = TinyAES.decrypt(ciphertext, "optional_aad")
# decrypted == "Hello, world!"
Security Notes
Uses :crypto.strong_rand_bytes/1 for cryptographically secure IV and key generation. Requires a 32-byte key, base64-encoded in the ENCRYPTION_KEY environment variable. Always use the same AAD for encryption and decryption to ensure successful authentication.
Summary
Functions
Decrypts AES-256-GCM ciphertext with robust error handling.
Expects a binary ciphertext containing a 16-byte IV, a 16-byte tag, and the encrypted data.
Optionally accepts Additional Authenticated Data (AAD) matching the encryption AAD.
Uses the key from get_key_env/0
.
Returns {:ok, plaintext}
on success or {:error, reason}
on failure, handling
Encrypts plaintext using AES-256-GCM, returning a binary containing the IV, tag, and ciphertext.
Optionally accepts Additional Authenticated Data (AAD) for enhanced security. The plaintext is converted
to a binary with to_string/1
. Uses a 32-byte key from get_key_env/0
and a random 16-byte IV.
Generates a secure 32-byte key and convert it to a Base64-encoded string for use in .env
files.
Use this to create a key for the ENCRYPTION_KEY
environment variable. See puts_generate_key_env/0
to print the key directly.
Retrieves and decodes a 32-byte encryption key from the ENCRYPTION_KEY
environment variable.
The key must be a Base64-encoded 32-byte string.
Generates a secure 32-byte key, prints it as a Base64-encoded string for use in .env
files, and returns :ok
.
Add the output to your .env
or .env.dev
file as ENCRYPTION_KEY=your_base64_encoded_key_here
.
Functions
Decrypts AES-256-GCM ciphertext with robust error handling.
Expects a binary ciphertext containing a 16-byte IV, a 16-byte tag, and the encrypted data.
Optionally accepts Additional Authenticated Data (AAD) matching the encryption AAD.
Uses the key from get_key_env/0
.
Returns {:ok, plaintext}
on success or {:error, reason}
on failure, handling:
- Invalid ciphertext format (e.g., too short).
- Failed tag verification (
:error
). - Invalid arguments (e.g., wrong key size, raised as {
badarg, reason, stack
}). - Invalid or missing encryption key.
- Unexpected errors.
Parameters
ciphertext
: Binary containing<<iv::binary-16, tag::binary-16, encrypted_data::binary>>
.aad
: Additional Authenticated Data used during encryption (string or binary, optional, defaults to""
).
Examples
iex> ciphertext = TinyAES.encrypt("Hello, world!")
iex> TinyAES.decrypt(ciphertext)
{:ok, "Hello, World!"}
iex> TinyAES.decrypt(encrypted, "wrong_aad")
{:error, "Decryption failed: unknown error"}
iex> TinyAES.decrypt(<<0::128>>)
{:error, "Ciphertext must be a binary with at least 32 bytes"}
iex> TinyAES.decrypt("not a binary")
{:error, "Ciphertext must be a binary with at least 32 bytes"}
iex> System.delete_env("ENCRYPTION_KEY")
iex> TinyAES.decrypt(TinyAES.encrypt("test"))
{:error, "Encryption key not found in environment variable ENCRYPTION_KEY"}
Encrypts plaintext using AES-256-GCM, returning a binary containing the IV, tag, and ciphertext.
Optionally accepts Additional Authenticated Data (AAD) for enhanced security. The plaintext is converted
to a binary with to_string/1
. Uses a 32-byte key from get_key_env/0
and a random 16-byte IV.
Parameters
- plaintext: The data to encrypt (string or binary).
- aad: Additional Authenticated Data (string or binary, optional, defaults to
""
).
Returns
binary()
:<<iv::binary-16, tag::binary-16, ciphertext::binary>>
on success.{:error, String.t()}
: If the encryption key is invalid.
Examples
iex> ciphertext = TinyAES.encrypt("Hello, world!")
iex> byte_size(ciphertext) >= 32
true
iex> ciphertext = TinyAES.encrypt("data", "aad")
iex> {:ok, "data"} = TinyAES.decrypt(ciphertext, "aad")
iex> System.delete_env("ENCRYPTION_KEY")
iex> TinyAES.encrypt("test")
{:error, "Encryption key not found in environment variable ENCRYPTION_KEY"}
Generates a secure 32-byte key and convert it to a Base64-encoded string for use in .env
files.
Use this to create a key for the ENCRYPTION_KEY
environment variable. See puts_generate_key_env/0
to print the key directly.
Returns
- a Base64-encoded string.
Examples
iex> key = TinyAES.generate_key_env()
iex> String.length(key) > 0
true
iex> {:ok, decoded} = Base.decode64(TinyAES.generate_key_env())
iex> byte_size(decoded)
32
Retrieves and decodes a 32-byte encryption key from the ENCRYPTION_KEY
environment variable.
The key must be a Base64-encoded 32-byte string.
Returns
{:ok, binary()}
: A 32-byte binary key on success.{:error, String.t()}
: An error if:ENCRYPTION_KEY
is not set.- The key is not a valid 32-byte Base64-encoded string.
Examples
iex> System.put_env("ENCRYPTION_KEY", TinyAES.generate_key_env())
iex> {:ok, key} = TinyAES.get_key_env()
iex> byte_size(key)
32
iex> System.delete_env("ENCRYPTION_KEY")
iex> TinyAES.get_key_env()
{:error, "Encryption key not found in environment variable ENCRYPTION_KEY"}
iex> System.put_env("ENCRYPTION_KEY", "invalid")
iex> TinyAES.get_key_env()
{:error, "Invalid encryption key: must be a 32-byte base64-encoded string"}
Generates a secure 32-byte key, prints it as a Base64-encoded string for use in .env
files, and returns :ok
.
Add the output to your .env
or .env.dev
file as ENCRYPTION_KEY=your_base64_encoded_key_here
.
Returns
:ok
Examples
iex> TinyAES.puts_generate_key_env()
# Prints: "PNu96VvFplhWeR/ojYPtDHiTgAdGxjPs9NGKl0Zn3fA=" (example; actual key is random)
:ok