SpeckEx.AEAD (SpeckEx v0.1.0)
View SourceAuthenticated Encryption with Associated Data (AEAD) using Speck-Poly1305.
This module provides authenticated encryption using Speck in CTR mode combined with Poly1305 MAC for authentication. This construction provides both confidentiality and authenticity, protecting against tampering and forgery.
Here be dragons
This is a "no guardrails" implementation module, where nonce generation is your own responsibility. Use the main SpeckEx module unless you know what you are doing. Be sure to read the SpeckEx security guidelines.
Supported Variants
Only Speck variants with 32, 64 and 128-bit block sizes are supported because of limitations of the backing Rust "ctr" crate:
:speck32_64- 32-bit block, 64-bit key (4-byte block, 8-byte key):speck64_96- 64-bit block, 96-bit key (8-byte block, 12-byte key):speck64_128- 64-bit block, 128-bit key (8-byte block, 16-byte key):speck128_128- 128-bit block, 128-bit key (16-byte block, 16-byte key):speck128_192- 128-bit block, 192-bit key (16-byte block, 24-byte key):speck128_256- 128-bit block, 256-bit key (16-byte block, 32-byte key, default)
Usage
# Generate a random key (32 bytes for default speck128_256)
iex> key = :crypto.strong_rand_bytes(32)
iex> nonce = :crypto.strong_rand_bytes(16)
iex> aad = "user_id:12345"
iex> {ciphertext, tag} = SpeckEx.AEAD.encrypt("Secret message", key, nonce, aad)
iex> SpeckEx.AEAD.decrypt(ciphertext, tag, key, nonce, aad)
{:ok, "Secret message"}Associated Data (AAD)
The associated data parameter allows you to authenticate additional data that is not encrypted. This is useful for protocol headers, metadata, or any data that must be authenticated but not kept confidential.
- AAD can be empty (
"") - AAD is not included in the ciphertext
- Modifying AAD will cause authentication to fail
Why Speck-Poly1305
Most authenticated encryption modes are only defined for 128 bits ciphers, which makes sense because they tend to create a verification tag of the block size of the underlying cipher, and a 64-bits MAC is too short to be secure. By using Poly1305, the MAC (or tag) is always 128 bits regardless of the block size of the cipher used to generate the Poly1305 key. This is somewhat sanctioned, at least, by RFC8439:
There is nothing special about AES here. One can replace AES with an arbitrary keyed function from an arbitrary set of nonces to 16-byte strings.
Summary
Functions
Verifies the authentication tag and decrypts the ciphertext.
Encrypts plaintext and computes an authentication tag.
Types
@type variant() :: SpeckEx.CTR.variant()
AEAD mode supports Speck variants with 32, 64 and 128-bit block sizes.
Functions
@spec decrypt(binary(), binary(), binary(), binary(), binary(), variant()) :: {:ok, binary()} | {:error, :authentication_failed}
Verifies the authentication tag and decrypts the ciphertext.
Returns {:ok, plaintext} if authentication succeeds, or
{:error, :authentication_failed} if the tag is invalid.
Parameters
ciphertext- The encrypted data (binary, any length)tag- The 16-byte Poly1305 authentication tagkey- The encryption key (size depends on variant)nonce- The nonce (size depends on variant, must match encryption)aad- Associated authenticated data (must match encryption)variant- The Speck variant to use (default::speck128_256)
Examples
# Successful decryption
iex> key = :crypto.strong_rand_bytes(32)
iex> nonce = :crypto.strong_rand_bytes(16)
iex> {ciphertext, tag} = SpeckEx.AEAD.encrypt("Hello", key, nonce, "aad")
iex> SpeckEx.AEAD.decrypt(ciphertext, tag, key, nonce, "aad")
{:ok, "Hello"}
# Failed authentication
iex> key = :crypto.strong_rand_bytes(32)
iex> nonce = :crypto.strong_rand_bytes(16)
iex> {ciphertext, tag} = SpeckEx.AEAD.encrypt("Hello", key, nonce, "aad")
iex> tampered = <<0>> <> binary_part(ciphertext, 1, byte_size(ciphertext) - 1)
iex> SpeckEx.AEAD.decrypt(tampered, tag, key, nonce, "aad")
{:error, :authentication_failed}
Encrypts plaintext and computes an authentication tag.
Returns a tuple {ciphertext, tag} where:
ciphertextis the encrypted data (same length as plaintext)tagis a 16-byte Poly1305 authentication tag
Parameters
plaintext- The data to encrypt (binary, any length)key- The encryption key (size depends on variant)nonce- The nonce (size depends on variant, MUST be unique per key)aad- Associated authenticated data (binary, any length, can be empty)variant- The Speck variant to use (default::speck128_256)
Examples
iex> key = :crypto.strong_rand_bytes(32)
iex> nonce = :crypto.strong_rand_bytes(16)
iex> {ciphertext, tag} = SpeckEx.AEAD.encrypt("Hello", key, nonce, "metadata")
iex> is_binary(ciphertext) and byte_size(tag) == 16
true