cheatah
Module

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

fn void set_force_portable_crypto(bool on) source#

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).

Parameters
on

true to pin the portable scalar path; false to allow the hardware path again.

Complexity

O(1).

Allocation

none.

fn bool crypto_hardware_active() source#

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.

Returns

true iff a hardware AES-GCM path (x86 AES-NI/PCLMULQDQ or ARMv8 AES/PMULL) is active.

Complexity

O(1).

Allocation

none after the first call (the first call may run the one-time hardware known-answer self-test, which allocates short scratch strings).

fn bool aead_len_ok(std::string_view msg) source#

Whether 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).

Parameters
msg

the plaintext or ciphertext.

Returns

true iff msg is within the single-message limit.

Complexity

O(1).

Allocation

none.

fn std::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).

Parameters
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).

Returns

ciphertext with the 16-byte tag appended, or "" on malformed key/nonce hex or a message over the 64 GiB single-message cap.

Complexity

O(|plaintext| + |aad|).

Allocation

the returned string plus a temporary Poly1305 MAC-input buffer (|aad| + |plaintext| + padding).

Warning

The nonce MUST never repeat under the same key: nonce reuse leaks the XOR of the plaintexts and lets an attacker forge tags.

fn bool 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).

Parameters
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 is 0).

aad_len

length of aad in bytes.

plaintext

the message to encrypt (may be null when plaintext_len is 0).

plaintext_len

length of plaintext in bytes.

out

receives plaintext_len + 16 bytes: the ciphertext followed by the tag. May alias plaintext to encrypt in place.

Returns

false on a null buffer with a nonzero length, or a message over the 64 GiB single-message cap; true otherwise.

Complexity

O(|plaintext| + |aad|).

Allocation

none. @thread any thread; no shared state. Async-signal-safe (no allocation, no locks, no errno use).

Warning

The nonce MUST never repeat under the same key: nonce reuse leaks the XOR of the plaintexts and lets an attacker forge tags.

fn 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.

Parameters
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 is 0).

aad_len

length of aad in bytes.

ciphertext

the ciphertext WITH its trailing 16-byte tag.

ciphertext_len

total length including the tag; must be >= 16.

out

receives ciphertext_len - 16 plaintext bytes. May alias ciphertext. May be null ONLY when the message is tag-only (ciphertext_len == 16), i.e. there is no plaintext to write — so authenticating an empty message needs no buffer.

Returns

false when the tag does not verify (nothing is written), or on a malformed argument; true on success.

Complexity

O(|ciphertext| + |aad|).

Allocation

none. @thread any thread; no shared state. Async-signal-safe (no allocation, no locks, no errno use).

fn 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.

Parameters
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.

Returns

the plaintext, or "" when the tag does not verify (tampering / wrong key or nonce / malformed input) — the tag check is constant-time.

Complexity

O(|ciphertext| + |aad|).

Allocation

the returned string plus a temporary Poly1305 MAC-input buffer (|aad| + |ciphertext| + padding).

fn std::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.

Parameters
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).

Returns

ciphertext with the 16-byte tag appended, or "" on malformed key/nonce hex or a message over the 64 GiB single-message cap.

Complexity

O(|plaintext| + |aad|).

Allocation

the returned string.

Warning

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).

fn std::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.

Parameters
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.

Returns

the plaintext, or "" when the tag does not verify (the check is constant-time) or the input is malformed.

Complexity

O(|ciphertext| + |aad|).

Allocation

the returned string (the hardware path allocates the candidate plaintext even when the tag check fails and "" is returned).

fn 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.

Parameters
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).

Returns

ciphertext with the 16-byte tag appended, or "" on malformed key/nonce hex or a message over the 64 GiB single-message cap.

Complexity

O(|plaintext| + |aad|).

Allocation

the returned string.

Warning

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).

fn std::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.

Parameters
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.

Returns

the plaintext, or "" when the tag does not verify (constant-time check) or the input is malformed.

Complexity

O(|ciphertext| + |aad|).

Allocation

the returned string (the hardware path allocates the candidate plaintext even when the tag check fails and "" is returned).

Constants & variables

var std::uint64_t kMaxAeadMessage source#