Skip to content

Security Model

AegisQ implements ML-KEM and AES-256-GCM. Their security depends on cryptographic assumptions, correct implementation, key management, and the execution environment. This page is not a certification or a proof of implementation security.

PropertyImplemented mechanismLimit
Key encapsulationML-KEM with implicit rejectionNo universal attack-probability bound is asserted
Payload confidentiality and integrityAES-256-GCM with a 128-bit tagDoes not authenticate the sender’s identity
Timing defensesConstant-time capsule comparison and key selection; Barrett reductionNot an end-to-end side-channel audit
Memory cleanupExplicit shared-secret cleanup after one-shot AES operations; Zeroizing in selected Rust pathsNot all copies or error paths are covered
Integer overflowoverflow-checks = true in release buildsIntentional wrapping operations remain explicit exceptions
One-shot noncesFresh random 96-bit nonce from the OS per encryptionRandom sampling is not a guarantee of uniqueness

ML-KEM is designed for IND-CCA2 security (Indistinguishability under Adaptive Chosen Ciphertext Attack). For invalid capsule contents of the correct size, decapsulation returns J(z || c), a pseudorandom 32-byte key, rather than a validity error. Here J is SHAKE256 with a 32-byte output. Incorrect key or capsule sizes are structural errors and raise InvalidParameterError in the current API.

mlkem/decaps.rs uses subtle::ConstantTimeEq to compare capsules and ConditionallySelectable to select the output key without a validity-dependent branch. Field arithmetic uses Barrett reduction. These mechanisms do not establish timing immunity for the complete Rust core, FFI bridge, Python runtime, compiler, or hardware.

hybrid.rs explicitly zeroizes its shared-secret buffer after AES-GCM returns, including authentication failure. Some other Rust paths use Zeroizing. This is not complete secret-erasure coverage: for example, nonce-generation failure in one-shot encryption occurs before that explicit cleanup.

The Python-facing KeyPair stores its secret in a Rust Vec<u8> without destruction-time zeroization in the current bridge. Its secret_key getter creates an immutable Python bytes copy. Neither deleting that copy nor leaving with AegisCipher() guarantees its erasure. ML-KEM intermediate buffers and copies also require separate lifecycle review.

The cipher context manager only overwrites mutable buffers registered with its private hook. It does not erase caller-owned keys, plaintext, or a suspended streaming generator’s Rust state. See Context Manager.

The Cargo release profile sets overflow-checks = true, retaining overflow checks for ordinary integer operations in release builds. Explicit wrapping operations bypass those checks and must be reviewed separately; this setting alone does not prove arithmetic correctness.

Public keys must be obtained through a trusted or authenticated channel. Anyone with a public key can encrypt to its holder; successful AES-GCM verification is not proof of sender identity. AegisQ also does not provide replay protection, metadata privacy, or persistent-key storage policy.

Understanding how errors propagate through the hybrid KEM-DEM system is critical for security:

ScenarioML-KEM DecapsAES-GCM Decrypt
Invalid capsule contents, correct sizeReturns pseudorandom KTag verification is expected to fail
Incorrect key/capsule size or too-short packageStructural error (InvalidParameterError)Not reached
Correct capsule, wrong AES keyN/A (key derived from capsule)Error: DecryptionError
Auth tag mismatch (tampered payload)N/AError: DecryptionError
Correct everythingReturns shared secretReturns plaintext

AES-GCM authentication failures surface as DecryptionError, without a separate capsule-validity signal. Structural errors remain distinct. These mechanisms do not replace protocol review or independent security auditing.