License Build Status

TinyAES 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 exception format introduced with Erlang OTP 25.

Installation

Add TinyAES to your mix.exs dependencies:

{:tiny_aes, "~> 0.1"}

Run mix deps.get to fetch the dependency.

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

The function outputs a base64-encoded key, e.g., OuaO+dtNNgxJjQLGHMLJ9m8rSQDsVdqkGrf7ySSj3Yg=. Add the key to your .env file:

Ensure the ENCRYPTION_KEY environment variable is set in your application (e.g., using env_loader or dotenv).

Usage

Encrypt and decrypt data without optional AAD::

# Encrypt a message
plaintext = "Sensitive data"
ciphertext = TinyAES.encrypt(plaintext)

# Decrypt it
{:ok, decrypted} = TinyAES.decrypt(ciphertext)
assert decrypted == plaintext

Encrypt and decrypt data with optional AAD (e.g., user or session ID)

# Encrypt a message
plaintext = "Sensitive data"
ciphertext = TinyAES.encrypt(plaintext, "optional_aad")

# Decrypt it
{:ok, decrypted} = TinyAES.decrypt(ciphertext, "optional_aad")
assert decrypted == plaintext

Handle edge cases:

# Handle invalid input
TinyAES.decrypt("not a binary")
# {:error, "Ciphertext must be a binary with at least 32 bytes"}

# Handle missing key
System.delete_env("ENCRYPTION_KEY")
TinyAES.encrypt("test")
# {:error, "Encryption key not found in environment variable ENCRYPTION_KEY"}

API

  • TinyAES.encrypt(plaintext, aad \\ ""):
    • Encrypts plaintext, returning <<iv::binary-16, tag::binary-16, ciphertext::binary>> or {:error, reason}.
  • TinyAES.decrypt(ciphertext, aad \\ ""):
    • Decrypts ciphertext, returning {:ok, plaintext} or {:error, reason}.
  • TinyAES.puts_generate_key_env():
    • Generates, prints, and returns :ok for a Base64-encoded 32-byte key.
  • TinyAES.generate_key_env():
    • Generates a Base64-encoded 32-byte key.
  • TinyAES.get_key_env():
    • Retrieves and decodes the key from ENCRYPTION_KEY returning {:ok, key} or {:error, reason}.

See HexDocs for full documentation.

Contributing

Contributions are welcome! Please open an issue or pull request on GitHub. For major changes, discuss them in an issue first. Ensure tests pass with mix test.

License

Released under the MIT License. See the LICENSE file for details.

Acknowledgments

Developed with the help of Grok, created by xAI. Thanks to Erlang's :crypto for providing a solid foundation.