Current section
Files
Jump to
Current section
Files
src/kryptos@aead.erl
-module(kryptos@aead).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/kryptos/aead.gleam").
-export([gcm/1, gcm_with_nonce_size/2, ccm/1, ccm_with_sizes/3, chacha20_poly1305/1, xchacha20_poly1305/1, nonce_size/1, tag_size/1, seal_with_aad/4, seal/3, open_with_aad/5, open/4, cipher_name/1, cipher_key/1, is_ccm/1, is_gcm/1, is_chacha20_poly1305/1]).
-export_type([aead_context/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Authenticated Encryption with Associated Data (AEAD).\n"
"\n"
" AEAD provides both confidentiality and integrity for data, with optional\n"
" authenticated additional data (AAD) that is integrity-protected but not\n"
" encrypted.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import kryptos/aead\n"
" import kryptos/block\n"
" import kryptos/crypto\n"
"\n"
" let assert Ok(cipher) = block.aes_256(crypto.random_bytes(32))\n"
" let ctx = aead.gcm(cipher)\n"
" let nonce = crypto.random_bytes(aead.nonce_size(ctx))\n"
" let assert Ok(#(ciphertext, tag)) = aead.seal(ctx, nonce:, plaintext: <<\"secret\":utf8>>)\n"
" ```\n"
).
-opaque aead_context() :: {gcm, kryptos@block:block_cipher(), integer()} |
{ccm, kryptos@block:block_cipher(), integer(), integer()} |
{cha_cha20_poly1305, bitstring()} |
{x_cha_cha20_poly1305, bitstring()}.
-file("src/kryptos/aead.gleam", 60).
?DOC(
" Creates an AES-GCM context with the given block cipher.\n"
"\n"
" Uses standard parameters: 16-byte (128-bit) authentication tag and\n"
" 12-byte (96-bit) nonce.\n"
"\n"
" **Note:** This library only supports the full 16-byte authentication tag.\n"
" Truncated tags (as permitted by NIST SP 800-38D) are not supported due to\n"
" their reduced security guarantees.\n"
"\n"
" ## Parameters\n"
" - `cipher`: The AES block cipher (128, 192, or 256 bit)\n"
"\n"
" ## Returns\n"
" An AES-GCM context ready for encryption or decryption.\n"
).
-spec gcm(kryptos@block:block_cipher()) -> aead_context().
gcm(Cipher) ->
{gcm, Cipher, 12}.
-file("src/kryptos/aead.gleam", 76).
?DOC(
" Creates an AES-GCM context with a custom nonce size.\n"
"\n"
" GCM supports variable nonce sizes, though 12 bytes is strongly recommended.\n"
" This function is primarily useful for compatibility testing with test vectors.\n"
"\n"
" ## Parameters\n"
" - `cipher`: The AES block cipher (128, 192, or 256 bit)\n"
" - `nonce_size`: The nonce size in bytes (1-64)\n"
"\n"
" ## Returns\n"
" - `Ok(AeadContext)` if the nonce size is valid\n"
" - `Error(Nil)` if the nonce size is out of range\n"
).
-spec gcm_with_nonce_size(kryptos@block:block_cipher(), integer()) -> {ok,
aead_context()} |
{error, nil}.
gcm_with_nonce_size(Cipher, Nonce_size) ->
case (Nonce_size >= 1) andalso (Nonce_size =< 64) of
true ->
{ok, {gcm, Cipher, Nonce_size}};
false ->
{error, nil}
end.
-file("src/kryptos/aead.gleam", 96).
?DOC(
" Creates an AES-CCM context with the given block cipher.\n"
"\n"
" Uses standard parameters: 16-byte (128-bit) authentication tag and\n"
" 13-byte (104-bit) nonce, which allows messages up to 64KB.\n"
"\n"
" ## Parameters\n"
" - `cipher`: The AES block cipher (128, 192, or 256 bit)\n"
"\n"
" ## Returns\n"
" An AES-CCM context ready for encryption or decryption.\n"
).
-spec ccm(kryptos@block:block_cipher()) -> aead_context().
ccm(Cipher) ->
{ccm, Cipher, 13, 16}.
-file("src/kryptos/aead.gleam", 114).
?DOC(
" Creates an AES-CCM context with custom nonce and tag sizes.\n"
"\n"
" CCM allows flexible nonce and tag sizes per RFC 3610:\n"
" - Nonce size affects maximum message length (larger nonce = smaller max message)\n"
" - Tag size affects authentication strength (larger tag = stronger)\n"
"\n"
" ## Parameters\n"
" - `cipher`: The AES block cipher (128, 192, or 256 bit)\n"
" - `nonce_size`: Nonce size in bytes (7-13)\n"
" - `tag_size`: Authentication tag size in bytes (4, 6, 8, 10, 12, 14, or 16)\n"
"\n"
" ## Returns\n"
" - `Ok(AeadContext)` if the sizes are valid\n"
" - `Error(Nil)` if any size is out of range\n"
).
-spec ccm_with_sizes(kryptos@block:block_cipher(), integer(), integer()) -> {ok,
aead_context()} |
{error, nil}.
ccm_with_sizes(Cipher, Nonce_size, Tag_size) ->
Valid_nonce = (Nonce_size >= 7) andalso (Nonce_size =< 13),
Valid_tag = gleam@list:contains([4, 6, 8, 10, 12, 14, 16], Tag_size),
case Valid_nonce andalso Valid_tag of
true ->
{ok, {ccm, Cipher, Nonce_size, Tag_size}};
false ->
{error, nil}
end.
-file("src/kryptos/aead.gleam", 138).
?DOC(
" Creates a ChaCha20-Poly1305 AEAD context with the given key.\n"
"\n"
" Uses standard parameters per RFC 8439: 12-byte (96-bit) nonce and\n"
" 16-byte (128-bit) authentication tag.\n"
"\n"
" ## Parameters\n"
" - `key`: A 32-byte (256-bit) key\n"
"\n"
" ## Returns\n"
" - `Ok(AeadContext)` if the key is exactly 32 bytes\n"
" - `Error(Nil)` if the key size is incorrect\n"
).
-spec chacha20_poly1305(bitstring()) -> {ok, aead_context()} | {error, nil}.
chacha20_poly1305(Key) ->
case erlang:byte_size(Key) =:= 32 of
true ->
{ok, {cha_cha20_poly1305, Key}};
false ->
{error, nil}
end.
-file("src/kryptos/aead.gleam", 157).
?DOC(
" Creates an XChaCha20-Poly1305 AEAD context with the given key.\n"
"\n"
" Uses extended parameters: 24-byte (192-bit) nonce and 16-byte (128-bit)\n"
" authentication tag. The extended nonce provides better collision resistance\n"
" when generating random nonces.\n"
"\n"
" ## Parameters\n"
" - `key`: A 32-byte (256-bit) key\n"
"\n"
" ## Returns\n"
" - `Ok(AeadContext)` if the key is exactly 32 bytes\n"
" - `Error(Nil)` if the key size is incorrect\n"
).
-spec xchacha20_poly1305(bitstring()) -> {ok, aead_context()} | {error, nil}.
xchacha20_poly1305(Key) ->
case erlang:byte_size(Key) =:= 32 of
true ->
{ok, {x_cha_cha20_poly1305, Key}};
false ->
{error, nil}
end.
-file("src/kryptos/aead.gleam", 171).
?DOC(
" Returns the required nonce size in bytes for an AEAD context.\n"
"\n"
" ## Parameters\n"
" - `ctx`: The AEAD context\n"
"\n"
" ## Returns\n"
" The nonce size in bytes (12 for AES-GCM and ChaCha20-Poly1305, 24 for XChaCha20-Poly1305).\n"
).
-spec nonce_size(aead_context()) -> integer().
nonce_size(Ctx) ->
case Ctx of
{gcm, _, Nonce_size} ->
Nonce_size;
{ccm, _, Nonce_size@1, _} ->
Nonce_size@1;
{cha_cha20_poly1305, _} ->
12;
{x_cha_cha20_poly1305, _} ->
24
end.
-file("src/kryptos/aead.gleam", 187).
?DOC(
" Returns the authentication tag size in bytes for an AEAD context.\n"
"\n"
" ## Parameters\n"
" - `ctx`: The AEAD context\n"
"\n"
" ## Returns\n"
" The tag size in bytes (16 for AES-GCM)\n"
).
-spec tag_size(aead_context()) -> integer().
tag_size(Ctx) ->
case Ctx of
{gcm, _, _} ->
16;
{ccm, _, _, Tag_size} ->
Tag_size;
{cha_cha20_poly1305, _} ->
16;
{x_cha_cha20_poly1305, _} ->
16
end.
-file("src/kryptos/aead.gleam", 327).
?DOC(" Derives the subkey and ChaCha20 nonce for XChaCha20-Poly1305.\n").
-spec xchacha20_derive(bitstring(), bitstring()) -> {ok,
{bitstring(), bitstring()}} |
{error, nil}.
xchacha20_derive(Key, Nonce) ->
case Nonce of
<<Hchacha_input:16/binary, Nonce_suffix:8/binary>> ->
Subkey = kryptos@internal@hchacha20:subkey(Key, Hchacha_input),
Chacha_nonce = <<0:32, Nonce_suffix/bitstring>>,
{ok, {Subkey, Chacha_nonce}};
_ ->
{error, nil}
end.
-file("src/kryptos/aead.gleam", 229).
?DOC(
" Encrypts and authenticates plaintext with additional authenticated data.\n"
"\n"
" The AAD is authenticated but not encrypted. It can be used for headers,\n"
" metadata, or context that should be tamper-proof but remain readable.\n"
"\n"
" ## Parameters\n"
" - `ctx`: The AEAD context to use\n"
" - `nonce`: A unique nonce (must be exactly `nonce_size` bytes and non-empty)\n"
" - `plaintext`: The data to encrypt\n"
" - `additional_data`: Data to authenticate but not encrypt\n"
"\n"
" ## Returns\n"
" - `Ok(#(ciphertext, tag))` with the encrypted data and authentication tag\n"
" - `Error(Nil)` if the nonce size is incorrect or empty\n"
).
-spec seal_with_aad(aead_context(), bitstring(), bitstring(), bitstring()) -> {ok,
{bitstring(), bitstring()}} |
{error, nil}.
seal_with_aad(Ctx, Nonce, Plaintext, Aad) ->
Nonce_len = erlang:byte_size(Nonce),
case (Nonce_len > 0) andalso (Nonce_len =:= nonce_size(Ctx)) of
true ->
case Ctx of
{x_cha_cha20_poly1305, Key} ->
gleam@result:'try'(
xchacha20_derive(Key, Nonce),
fun(_use0) ->
{Subkey, Chacha_nonce} = _use0,
kryptos_ffi:aead_seal(
{cha_cha20_poly1305, Subkey},
Chacha_nonce,
Plaintext,
Aad
)
end
);
_ ->
kryptos_ffi:aead_seal(Ctx, Nonce, Plaintext, Aad)
end;
false ->
{error, nil}
end.
-file("src/kryptos/aead.gleam", 207).
?DOC(
" Encrypts and authenticates plaintext using AEAD.\n"
"\n"
" ## Parameters\n"
" - `ctx`: The AEAD context to use\n"
" - `nonce`: A unique nonce (must be exactly `nonce_size` bytes).\n"
" Never reuse a nonce with the same key.\n"
" - `plaintext`: The data to encrypt\n"
"\n"
" ## Returns\n"
" - `Ok(#(ciphertext, tag))` with the encrypted data and authentication tag\n"
" - `Error(Nil)` if the nonce size is incorrect\n"
).
-spec seal(aead_context(), bitstring(), bitstring()) -> {ok,
{bitstring(), bitstring()}} |
{error, nil}.
seal(Ctx, Nonce, Plaintext) ->
seal_with_aad(Ctx, Nonce, Plaintext, <<>>).
-file("src/kryptos/aead.gleam", 292).
?DOC(
" Decrypts and verifies AEAD-encrypted data with additional authenticated data.\n"
"\n"
" The AAD must match exactly what was provided during encryption.\n"
"\n"
" ## Parameters\n"
" - `ctx`: The AEAD context to use\n"
" - `nonce`: The nonce used during encryption (must be non-empty)\n"
" - `tag`: The authentication tag from encryption\n"
" - `ciphertext`: The encrypted data\n"
" - `additional_data`: The same AAD used during encryption\n"
"\n"
" ## Returns\n"
" - `Ok(plaintext)` if authentication succeeds\n"
" - `Error(Nil)` if authentication fails, AAD mismatch, or nonce size is incorrect/empty\n"
).
-spec open_with_aad(
aead_context(),
bitstring(),
bitstring(),
bitstring(),
bitstring()
) -> {ok, bitstring()} | {error, nil}.
open_with_aad(Ctx, Nonce, Tag, Ciphertext, Aad) ->
Nonce_len = erlang:byte_size(Nonce),
Tag_len = erlang:byte_size(Tag),
case ((Nonce_len > 0) andalso (Nonce_len =:= nonce_size(Ctx))) andalso (Tag_len
=:= tag_size(Ctx)) of
true ->
case Ctx of
{x_cha_cha20_poly1305, Key} ->
gleam@result:'try'(
xchacha20_derive(Key, Nonce),
fun(_use0) ->
{Subkey, Chacha_nonce} = _use0,
kryptos_ffi:aead_open(
{cha_cha20_poly1305, Subkey},
Chacha_nonce,
Tag,
Ciphertext,
Aad
)
end
);
_ ->
kryptos_ffi:aead_open(Ctx, Nonce, Tag, Ciphertext, Aad)
end;
false ->
{error, nil}
end.
-file("src/kryptos/aead.gleam", 269).
?DOC(
" Decrypts and verifies AEAD-encrypted data.\n"
"\n"
" ## Parameters\n"
" - `ctx`: The AEAD context to use\n"
" - `nonce`: The nonce used during encryption\n"
" - `tag`: The authentication tag from encryption\n"
" - `ciphertext`: The encrypted data\n"
"\n"
" ## Returns\n"
" - `Ok(plaintext)` if authentication succeeds\n"
" - `Error(Nil)` if authentication fails or nonce size is incorrect\n"
).
-spec open(aead_context(), bitstring(), bitstring(), bitstring()) -> {ok,
bitstring()} |
{error, nil}.
open(Ctx, Nonce, Tag, Ciphertext) ->
open_with_aad(Ctx, Nonce, Tag, Ciphertext, <<>>).
-file("src/kryptos/aead.gleam", 345).
?DOC(false).
-spec cipher_name(aead_context()) -> binary().
cipher_name(Ctx) ->
case Ctx of
{gcm, Cipher, _} ->
<<<<"aes-"/utf8,
(erlang:integer_to_binary(kryptos@block:key_size(Cipher)))/binary>>/binary,
"-gcm"/utf8>>;
{ccm, Cipher@1, _, _} ->
<<<<"aes-"/utf8,
(erlang:integer_to_binary(kryptos@block:key_size(Cipher@1)))/binary>>/binary,
"-ccm"/utf8>>;
{cha_cha20_poly1305, _} ->
<<"chacha20-poly1305"/utf8>>;
{x_cha_cha20_poly1305, _} ->
<<"chacha20-poly1305"/utf8>>
end.
-file("src/kryptos/aead.gleam", 357).
?DOC(false).
-spec cipher_key(aead_context()) -> bitstring().
cipher_key(Ctx) ->
case Ctx of
{gcm, Cipher, _} ->
kryptos@block:aes_key(Cipher);
{ccm, Cipher, _, _} ->
kryptos@block:aes_key(Cipher);
{cha_cha20_poly1305, Key} ->
Key;
{x_cha_cha20_poly1305, Key} ->
Key
end.
-file("src/kryptos/aead.gleam", 365).
?DOC(false).
-spec is_ccm(aead_context()) -> boolean().
is_ccm(Ctx) ->
case Ctx of
{ccm, _, _, _} ->
true;
_ ->
false
end.
-file("src/kryptos/aead.gleam", 373).
?DOC(false).
-spec is_gcm(aead_context()) -> boolean().
is_gcm(Ctx) ->
case Ctx of
{gcm, _, _} ->
true;
_ ->
false
end.
-file("src/kryptos/aead.gleam", 381).
?DOC(false).
-spec is_chacha20_poly1305(aead_context()) -> boolean().
is_chacha20_poly1305(Ctx) ->
case Ctx of
{cha_cha20_poly1305, _} ->
true;
_ ->
false
end.