aead
Authenticated encryption with associated data — ChaCha20-Poly1305 (RFC 8439) and AES-128-GCM, self-contained (no OpenSSL). These are the record ciphers behind the tls module and are equally usable directly. AES-GCM takes a runtime-selected AES-NI + PCLMULQDQ fast path on capable x86 CPUs, with a portable scalar fallback everywhere else — identical results either way.
import aead
let ct = aead.chacha20poly1305_encrypt(key_hex, nonce_hex, aad, "attack at dawn")
let pt = aead.chacha20poly1305_decrypt(key_hex, nonce_hex, aad, ct)Functions
chacha20poly1305_encrypt(key_hex, nonce_hex, aad, plaintext)/chacha20poly1305_decrypt(key_hex, nonce_hex, aad, ciphertext)— RFC 8439 AEAD; decrypt returns""on an authentication failure (a tampered message never yields plaintext).aes128gcm_encrypt(key_hex, nonce_hex, aad, plaintext)/aes128gcm_decrypt(key_hex, nonce_hex, aad, ciphertext)— AES-128-GCM, same contract.crypto_hardware_active()— whether AES-GCM is currently on the hardware (AES-NI) path; lets a deployment assert which implementation it exercises.
Allocation-free variants (C++ only)
chacha20poly1305_encrypt_into(key, nonce, aad, aad_len, plaintext, plaintext_len, out)/chacha20poly1305_decrypt_into(key, nonce, aad, aad_len, ciphertext, ciphertext_len, out)
The functions above return a std::string, so they allocate their result and, internally, a buffer to assemble the MAC input. That rules them out wherever allocation is forbidden or simply unwelcome: signal handlers, embedded targets, and hot paths that already own their memory. These take raw byte pointers and a caller-provided output buffer, allocate nothing, and may encrypt in place (out may alias the input). out needs plaintext_len + 16 bytes on encrypt — the ciphertext followed by the tag.
They are the same algorithm on the same code paths, not a second implementation: the tag is streamed through Poly1305 segment by segment instead of over one concatenated buffer, which is possible because the AEAD pads every segment to a 16-byte boundary. A test asserts byte-identical output to the string forms over the RFC 8439 vector and 200 randomized sizes. Being allocation-free, they are also async-signal-safe. Measured ~11% faster than the string forms (490 vs 438 MiB/s on the benchmark's payload) — exactly the two allocations they skip.
Keys and nonces are lowercase hex; plaintext/ciphertext/aad are raw bytes (NUL-safe, length-carried). Checked against the RFC 8439 and NIST GCM test vectors, with the portable and hardware paths cross-checked against each other.
Per-function docs (parameters, runtime complexity, heap behavior) are in aead.hpp. Tested in ../tests/aead_test.cpp; ASan + Valgrind clean via the QA gate (security/run-valgrind.sh).
Functions
Pin (or release) the portable scalar AES-GCM path — the test/determinism hook whose full contract lives on the declaration in aead.hpp (kept out of the public docs via \cond there).
on | true to pin the portable scalar path; false to allow the hardware path again. |
O(1).
none.
Whether AES-GCM (AES-128 and AES-256) is currently using the CPU's crypto instructions: the x86 AES-NI + PCLMULQDQ path, or the ARMv8 AES + PMULL path on AArch64 (e.g.
Apple Silicon). True on a capable CPU unless set_force_portable_crypto(true) is in effect; false where neither ISA is present (the portable scalar reference then runs). Lets a platform test report and assert which implementation a given machine actually exercised.
true iff a hardware AES-GCM path (x86 AES-NI/PCLMULQDQ or ARMv8 AES/PMULL) is active.
O(1).
none after the first call (the first call may run the one-time hardware known-answer self-test, which allocates short scratch strings).
CryptoPlatform.ReportWhether one AEAD message is under the 64 GiB counter-wrap cap above.
Every encrypt/decrypt checks it; the over-cap branch is unreachable in a test (the message would not fit in memory).
msg | the plaintext or ciphertext. |
true iff msg is within the single-message limit.
O(1).
none.
CheatahAead.Rfc8439Encryptstd::string chacha20poly1305_encrypt(std::string_view key_hex, std::string_view nonce_hex, std::string_view aad, std::string_view plaintext)
source#
Encrypt + authenticate: ChaCha20-Poly1305(key, nonce, aad, plaintext).
key_hex | the 64-char hex key (32 bytes). |
nonce_hex | the 24-char hex nonce (12 bytes); MUST be unique per key. |
aad | additional authenticated data — authenticated but not encrypted ("" for none). |
plaintext | the raw-byte message to encrypt (binary-safe). |
ciphertext with the 16-byte tag appended, or "" on malformed key/nonce hex or a message over the 64 GiB single-message cap.
O(|plaintext| + |aad|).
the returned string plus a temporary Poly1305 MAC-input buffer (|aad| + |plaintext| + padding).
The nonce MUST never repeat under the same key: nonce reuse leaks the XOR of the plaintexts and lets an attacker forge tags.
CheatahAead.Rfc8439EncryptTlsSys.HandshakeAgainstOpensslbool chacha20poly1305_encrypt_into(const unsigned char key[32], const unsigned char nonce[12], const unsigned char *aad, std::size_t aad_len, const unsigned char *plaintext, std::size_t plaintext_len, unsigned char *out)
source#
ChaCha20-Poly1305 encryption into a CALLER-PROVIDED buffer — the allocation-free form.
The string-returning form above allocates its result and a temporary MAC-input buffer, which makes it unusable where allocation is forbidden or merely unwelcome: signal handlers, embedded targets, hot loops that own their memory, and any caller that already has the bytes in place. This form allocates NOTHING — the tag is computed by streaming the AEAD's segments through Poly1305 rather than concatenating them — and is therefore safe to call from such contexts. Byte-for-byte identical output to the string form (asserted by an equivalence test).
key | the 32-byte key. |
nonce | the 12-byte nonce; MUST be unique per key. |
aad | additional authenticated data (may be null when |
aad_len | length of |
plaintext | the message to encrypt (may be null when |
plaintext_len | length of |
out | receives |
false on a null buffer with a nonzero length, or a message over the 64 GiB single-message cap; true otherwise.
O(|plaintext| + |aad|).
none. @thread any thread; no shared state. Async-signal-safe (no allocation, no locks, no errno use).
The nonce MUST never repeat under the same key: nonce reuse leaks the XOR of the plaintexts and lets an attacker forge tags.
bool chacha20poly1305_decrypt_into(const unsigned char key[32], const unsigned char nonce[12], const unsigned char *aad, std::size_t aad_len, const unsigned char *ciphertext, std::size_t ciphertext_len, unsigned char *out)
source#
Verify + decrypt into a CALLER-PROVIDED buffer — the allocation-free inverse of chacha20poly1305_encrypt_into.
The tag is verified in constant time before any plaintext is written back to the caller.
key | the 32-byte key. |
nonce | the 12-byte nonce used to encrypt. |
aad | the same additional authenticated data (may be null when |
aad_len | length of |
ciphertext | the ciphertext WITH its trailing 16-byte tag. |
ciphertext_len | total length including the tag; must be >= 16. |
out | receives |
false when the tag does not verify (nothing is written), or on a malformed argument; true on success.
O(|ciphertext| + |aad|).
none. @thread any thread; no shared state. Async-signal-safe (no allocation, no locks, no errno use).
std::string chacha20poly1305_decrypt(std::string_view key_hex, std::string_view nonce_hex, std::string_view aad, std::string_view ciphertext)
source#
Verify + decrypt the inverse of chacha20poly1305_encrypt.
key_hex | the 64-char hex key (32 bytes) used to encrypt. |
nonce_hex | the 24-char hex nonce (12 bytes) used to encrypt. |
aad | the same additional authenticated data supplied at encryption ("" for none). |
ciphertext | the ciphertext with its 16-byte tag appended. |
the plaintext, or "" when the tag does not verify (tampering / wrong key or nonce / malformed input) — the tag check is constant-time.
O(|ciphertext| + |aad|).
the returned string plus a temporary Poly1305 MAC-input buffer (|aad| + |ciphertext| + padding).
CheatahAead.Rfc8439DecryptTlsSys.HandshakeAgainstOpensslstd::string aes128gcm_encrypt(std::string_view key_hex, std::string_view nonce_hex, std::string_view aad, std::string_view plaintext)
source#
Encrypt + authenticate: AES-128-GCM(key, nonce, aad, plaintext) — the other TLS 1.3 record cipher (TLS_AES_128_GCM_SHA256).
The nonce is used as the GCM IV (J0 = nonce || 0x00000001); the 16-byte GCM tag is appended.
key_hex | the 32-char hex key (16 bytes, AES-128). |
nonce_hex | the 24-char hex nonce (12 bytes), used as the GCM IV; MUST be unique per key. |
aad | additional authenticated data — authenticated but not encrypted ("" for none). |
plaintext | the raw-byte message to encrypt (binary-safe). |
ciphertext with the 16-byte tag appended, or "" on malformed key/nonce hex or a message over the 64 GiB single-message cap.
O(|plaintext| + |aad|).
the returned string.
The nonce MUST never repeat under the same key: GCM nonce reuse leaks the XOR of the plaintexts AND the GHASH authentication key (forgeries follow).
CheatahAead.AesGcmNistCase4TlsSys.HandshakeAes128Gcmstd::string aes128gcm_decrypt(std::string_view key_hex, std::string_view nonce_hex, std::string_view aad, std::string_view ciphertext)
source#
Verify + decrypt the inverse of aes128gcm_encrypt.
key_hex | the 32-char hex key (16 bytes, AES-128) used to encrypt. |
nonce_hex | the 24-char hex nonce (12 bytes) used to encrypt. |
aad | the same additional authenticated data supplied at encryption ("" for none). |
ciphertext | the ciphertext with its 16-byte GCM tag appended. |
the plaintext, or "" when the tag does not verify (the check is constant-time) or the input is malformed.
O(|ciphertext| + |aad|).
the returned string (the hardware path allocates the candidate plaintext even when the tag check fails and "" is returned).
std::string aes256gcm_encrypt(std::string_view key_hex, std::string_view nonce_hex, std::string_view aad, std::string_view plaintext)
source#
Encrypt + authenticate: AES-256-GCM(key, nonce, aad, plaintext) — the record cipher of TLS 1.3's TLS_AES_256_GCM_SHA384 suite.
The nonce is the GCM IV (J0 = nonce || 0x00000001); the 16-byte tag is appended. Same runtime dispatch as AES-128-GCM: the hardware path (x86 AES-NI/PCLMULQDQ or ARMv8 AES/PMULL) when present and self-tested, otherwise the portable scalar reference.
key_hex | the 64-char hex key (32 bytes, AES-256). |
nonce_hex | the 24-char hex nonce (12 bytes), used as the GCM IV; MUST be unique per key. |
aad | additional authenticated data — authenticated but not encrypted ("" for none). |
plaintext | the raw-byte message to encrypt (binary-safe). |
ciphertext with the 16-byte tag appended, or "" on malformed key/nonce hex or a message over the 64 GiB single-message cap.
O(|plaintext| + |aad|).
the returned string.
The nonce MUST never repeat under the same key: GCM nonce reuse leaks the XOR of the plaintexts AND the GHASH authentication key (forgeries follow).
CheatahAead.Aes256GcmNistKatTlsSys.HandshakeAes256GcmSha384std::string aes256gcm_decrypt(std::string_view key_hex, std::string_view nonce_hex, std::string_view aad, std::string_view ciphertext)
source#
Verify + decrypt the inverse of aes256gcm_encrypt.
key_hex | the 64-char hex key (32 bytes, AES-256) used to encrypt. |
nonce_hex | the 24-char hex nonce (12 bytes) used to encrypt. |
aad | the same additional authenticated data supplied at encryption ("" for none). |
ciphertext | the ciphertext with its 16-byte GCM tag appended. |
the plaintext, or "" when the tag does not verify (constant-time check) or the input is malformed.
O(|ciphertext| + |aad|).
the returned string (the hardware path allocates the candidate plaintext even when the tag check fails and "" is returned).
CheatahAead.Aes256GcmRejectsTamper