/ Documentation / Security / Audit reports

Audit reports

Internal audit rounds + the third-party engagement package.

Internal audits

LUKSbox has been through thirteen internal audit rounds, each focused on a specific attack surface.

Round Focus Outcome
1 Crypto primitives + KDF 1 vuln found + fixed (Argon2id DoS via hostile on-disk params)
2 FIDO2 trust boundary (rogue / MITM authenticator) 1 vuln found + fixed (cred_id OOM at FFI boundary); 11 regression tests added
3 Auth-then-process pipeline 1 vuln found + fixed (bincode-OOM via hostile metadata, surfaced via fuzz)
4 Hardware FIDO2 detection layer No vulns; live YubiKey probe
5 End-to-end + zero / all-ones edge cases Round-2 fix verified on real hardware; 6 additional rogue tests
6 Invariant lockdown across the stack No vulns; HKDF info-string uniqueness, header-tamper coverage, slot AEAD AAD field-by-field, AES-NI startup warning
7 (A-E) Maintenance + bindgen + AAD V2 + postcard migration + bincode removal No vulns; 0 cargo-audit advisories on Linux/macOS
8 V3 keyslot layout + live multi-vendor FIDO2 hardware validation No vulns; supports authenticators with 64-352 byte cred IDs. Verified end-to-end on YubiKey 4/5/Bio, Google Titan v2, SoloKey Solo 2, and Windows Hello. Nitrokey 3 and USB Armory Mk II + custom firmware are on the next-release test plan; Trezor Safe 3/5, Token2, and OnlyKey are spec-compliant per their documentation but not yet in our matrix, see FIDO2 keyslots, Supported devices.
9 (concurrency + crash + secret memory) Lock-before-read in Container::open; post-lock verify_path_inode for open-then-rename swaps; parent-directory fsync after rename on Unix + Windows; symlink-target overwrite refused via O_NOFOLLOW; full MVK-lifecycle audit with every intermediate buffer wrapped in Zeroizing (AEAD plaintext, HKDF I/O for all KEK derivations, ML-KEM shared-secret destination, CLI / GUI PIN copies); TPM PIN now passes by slice into Auth::try_from skipping an unzeroized Vec<u8> round-trip; metadata tree validated structurally (root, parents, children, chunk-id wraparound, free-list overlap) before VFS uses it; defensively checked-arithmetic in chunk offset + alloc paths. Captured in the architecture page on the website sections B5-B11b.
11 Deniable v2 cryptographic sweep 3 fixes (per-vault salt mixed into the inner-header AAD; envelope plaintext wrapped in Zeroizing; Zeroizing<[u8;32]> propagated through deniable_pq_decap so the ML-KEM shared secret is wiped after the slot KEK derives). 5 new workflow tests + 3 new fuzz targets (slot_payload_decode, slot_payload_roundtrip, deniable_header_parse).
13 (local filesystem boundary races + header durability + sidecar DoS + secret-copy hygiene) 0 CRITICAL + 2 HIGH + 5 MEDIUM + 2 LOW + 1 INFO; all 9 findings shipped fixes same revision. Headline fixes: R13-01 closes the intermediate-directory symlink swap in secure_create_or_truncate (luksbox get / GUI extract) via openat(parent_dir_fd, basename, O_NOFOLLOW) against a canonical parent fd; R13-02 routes luksbox header restore through the container's verified handle (Container::restore_header_bytes) so the rewrite cannot be redirected through a post-verify path swap. Other fixes: R13-03 clamps the chunk-0 authenticated real-size against allocated capacity (no more inode.chunks[idx] panic on hostile hide-size vaults); R13-04 persist_header uses sync_all + atomic_secure_write on detached; R13-05 .kyber seed reads add O_NOFOLLOW + regular-file + size preflight; R13-06 hybrid sidecar reader caps at 32 KiB + reparse-point rejection on Windows; R13-07 MAX_FILE_SIZE = 1 << 44 cap + Error::FileSizeExceedsCap on Vfs::write / truncate; R13-08 FUSE read caps requester-supplied size at 16 MiB; R13-09 SecretBox::clone allocates fresh secret memory + direct copy_from_slice (no by-value stack temporary). Full report at docs/SECURITY_AUDIT_ROUND_13.md. New regression coverage: 4 test files (14 tests total, 0 ignored).
14 (v3 format + deniable rotation + filesystem-boundary follow-up) External-reporter audit on the v0.2.0-dev branch surfaced 6 findings on the path-handling / TOCTOU surfaces; all 6 shipped fixes same revision. High-severity: panic-destroy (luksbox panic CLI + wizard + GUI) followed symlinks via is_file() + later OpenOptions::open() -- arbitrary file overwrite if attacker controlled the vault's parent dir, especially severe with --wipe-data (blast radius = file size) or run as root. Fixed via a new luksbox_core::file_util::secure_open_existing_no_follow helper using O_NOFOLLOW (Unix) / FILE_FLAG_OPEN_REPARSE_POINT (Windows); handles open BEFORE the confirmation prompt so the prompt itself can no longer be raced. Medium-severity: deniable open (try_open_envelope_v2_deniable and open_with_mvk_deniable) bypassed LUKSBOX_NO_FOLLOW_SYMLINKS -- both routed through the same hardened open_rw_checked + verify_path_inode as standard open; deniable mount used is_dir() + canonicalize() instead of the hardened `O_DIRECTORY O_NOFOLLOW+ canonical deny-list -- same hardened check now applied; header-backupout.exists()+secure_create_or_truncaterace -- switched toatomic_secure_create_new(POSIXlink(2)/ WindowsMoveFileExW(0)); cmd_mountand FUSE-T helper canonicalized vault/header BEFORE the no-follow check fired -- addedLUKSBOX_NO_FOLLOW_SYMLINKSpreflight viastd::fs::symlink_metadatathat runs BEFORE canonicalize. **Low-severity**: unlock prescan (~14 callsites across CLI + GUI) used rawFile::open-- now routed through a newopen_existing_read_no_follow_policyhelper. Concurrent v3-format internal-audit DoS finding:walk_chunk_list_chainhad no upper bound on the caller-suppliedexpected_count-- capped atMAX_FILE_SIZE / CHUNK_PLAINTEXT_SIZE = 2^32(refuses forged metadata before any IO). Deniable rotation footgun:Container::rotate_mvk_v2_deniablerotated MVK without re-encrypting chunks -- added runtime guard that refuses on non-empty vaults (typedError::DeniableRotationRequiresEmptyVault), and a new Vfs::rotate_mvk_deniablethat does the full re-encryption job (chunks + chunk-list blocks + metadata blob). 8 new regression tests acrossluksbox-core, luksbox-format::tests::security_invariants, and luksbox-cli::tests::functionalincluding an end-to-endpanic -y --wipe-data` against a symlinked path that verifies the sentinel file is byte-for-byte unchanged.
12 (FUSE-T subprocess + deniable timing + filesystem TOCTOU + memory safety) 1 CRITICAL + 5 HIGH + 7 MEDIUM + 6 LOW; all 19 findings shipped fixes same revision (R12-14 formally superseded by R12-11). Headline fix: R12-01's deniable envelope discovery loop is now constant-time via subtle::Choice-driven byte selection + single-shot SlotPayload::decode on the chosen slot. Other fixes: CLI seed-pw blank-= envelope reuse, helper --header canonicalization + sandbox HEADER_DIR, MountBackend::Subprocess Drop kill+reap, helper mountpoint `O_DIRECTORY O_NOFOLLOWprobe, hybrid sidecarO_NOFOLLOW, sandbox-subpath canonicalization, system-dir extract deny-list, atomic .rotatingcreate withO_EXCL

Third-party audit

Not yet performed. The handover package is ready in the third-party FIDO2 FFI engagement scope package: scope, threat model, call graph, 30 invariants the auditor should verify, test entry points, and the residual gaps that internal review can't credibly close (notably formal lifetime / aliasing proofs of the 1,400 LOC of unsafe Rust in the FIDO2 FFI and FUSE adapters).

Test coverage

Tier Frequency Scope
Unit tests every commit per-module crypto round-trips, parser correctness, [u8] round-trips
Security-regression tests every commit 65+ tests pinning each known-fix invariant, Argon2 DoS guard, rogue authenticator, slot AAD coverage, generation rollback, lock contention, symlink swap, AES-NI warning, V3 layout
Functional tests every commit end-to-end CLI workflows via subprocess
Fuzz smoke (libFuzzer) every PR 5 min per target x 9 targets, cheap parser bugs
Fuzz nightly (libFuzzer) nightly 1 hour per target
Fuzz extended (AFL++) weekly / on demand days per campaign on dedicated hardware
Hardware FIDO2 tests manual, per release live YubiKey 4/5/Bio, Google Titan v2, SoloKey Solo 2, and Windows Hello via crates/luksbox-fido2/examples/{probe,multidev_probe,full_probe}.rs. Nitrokey 3 + USB Armory next

Reporting a vulnerability

Open an issue in our GitHub. Response SLA, and coordinated- disclosure policy in Disclosure.