Repository navigation
Security
-
Fixed: Tightened
MAX_ARGON2_M_COSTfrom 4 GiB to 1 GiB (kelly-report-2.md, finding K-9). An attacker-crafted header specifying maximumm_costcombined with Rayon's parallel key derivation could forceparallel_threads × m_costbytes of memory allocation during decrypt — on an 8-core machine, 16 GiB of Argon2id memory before any ciphertext was touched, enough to OOM a 16 GiB workstation. The new 1 GiB cap reduces worst-case to 4 GiB on the same machine. Default encryption params (64 MiB) are unaffected; no legitimate file uses m_cost above 1 GiB. -
Fixed: Tightened
MAX_CHUNK_COUNTfromu32::MAX(~4.29 billion) to2^40(~1.1 trillion) (kelly-report-2.md, finding K-10). Each chunk requires independent Argon2id key derivation on decrypt — an attacker delivering a file claiming billions of chunks could pin the victim's CPU for hours to years of sustained compute. The new ceiling still covers any plausible file (1 EB at 1 MiB chunks = 10^12, within 2^40) while cutting the attacker's compute-DoS budget by a factor of ~16 million. -
Fixed: Non-chunked header parser (
parse_header_line) had no length ceiling — an attacker-crafted header with megabytes of data in a hash field could force hex decoding of the oversized field before the file was rejected (kelly-review.md, finding K-4). Header line scan is now capped at 64 KiB, matching the existing cap in the chunked decrypt path. -
Fixed: Keyfile read (
--keyfile) was unbounded — a path pointing at a large file (attacker-influenced or accidental) would load the entire file into RAM as password material (kelly-report-2.md, finding K-12). Keyfile size is now capped at 1 MiB with a clear error message. -
Fixed: Memory locking (
mlock) failure was silent — derived keys could fall back to unlocked (swappable) memory with no indication to the user (kelly-report-2.md, finding K-13). A one-time warning is now emitted to stderr when mlock fails, unless--silentis set. -
Fixed: Keyfile generated by
keygenandexport-pubkeyon Windows inherited the parent directory's ACL, potentially leaving private key material readable by other users (kelly-review.md, finding K-8). Windows keyfiles now have an explicit owner-only DACL applied viaSetFileSecurityW. Unix permissions (0600) were already enforced since v0.5.2. -
Fixed: Non-chunked decrypt no longer OOMs on files larger than available RAM (
kelly-review.md, finding K-2). The previous code path calledfs::read(path)which loaded the entire file into memory before any validation — an adversarial 100 GiB file delivered to a victim on a 16 GiB workstation would trigger OOM before the ciphertext hash could be checked. Non-chunked decrypt now routes to a new mmap-backed pipeline whenfile_size > available_memory / 2: the ciphertext body is streamed to aSecureTempFilewith incremental SHA-256, then each cipher cascade layer runs via memory-mapped I/O against ping-pong temp files, with plaintext written directly into the output mapping (no per-layerVec<u8>allocation). The final base64-encoded plaintext is mmap'd, UTF-8 validated by sequential scan, and stream-decoded to the output sink. Peak physical RAM is bounded by copy buffers and the OS page cache — never by file size. Small files (below the threshold) continue to use the existing fast RAM path unchanged.
Added
cascade::decrypt_nonchunked_mmap()— public API for bounded-RAM non-chunked decrypt via mmap. Used internally by the CLI whenfile_size > available_memory / 2.cascade::decrypt_layers_mmap()— lower-level primitive that runs the cipher cascade with mmap'd intermediate buffers.crypto::decrypt_mmap()and per-cipherMmapDecryptFnimplementations for all 20 algorithms — write plaintext directly into a caller-provided output buffer instead of returningVec<u8>.buffer::process_mmap()/buffer::transform_in_place_mmap()— mmap-backed layer processing and in-place byte transforms.encoder::decode_streaming()— streaming base64 decode viabase64::read::DecoderReader, reads a length prefix and streams exactly that many bytes to a writer.SecureTempFile::append(),file(),file_mut(),set_len()— accessors for mmap integration.- New
memmap2 = "0.9"dependency.
Changed
- All caps (
MAX_ARGON2_M_COST,MAX_CHUNK_COUNT, header line length, keyfile size) apply only to the decrypt/parse path — encryption behavior is unaffected. buffer::get_available_memory()visibility widened frompub(crate)topubso the CLI routing logic can query it.
Documentation
- Added security note that
--pubkeyheader encryption provides confidentiality but not sender authenticity — anyone with the recipient's public key can encrypt to them (kelly-report-2.md, finding K-11). - Added security note that AES requires AES-NI for constant-time operation; ChaCha20-based algorithms (
-C,-X) recommended on hardware without AES-NI (kelly-report-2.md, finding K-15).
Compatibility
- No wire format changes. All files encrypted by any prior version decrypt under v0.8.0. No legitimate file's Argon2 params, chunk count, header line length, or keyfile size approaches the new caps.
- The mmap decrypt path is transparent: files produced by any prior version decrypt bit-identically whether routed through the RAM or mmap path (the threshold only affects resource usage, not output).