/ Documentation / Security / Threat model

Threat model

What LUKSbox defends against, and what it explicitly doesn't.

The threat model below is paired with the security architecture page (trust boundaries, on-disk graph, unlock + crash-safety pipelines, with diagrams) and the tests page (regression suite, fuzz harnesses, audit rounds, contribution paths).

Defended

Threat Defence
Offline analysis of a stolen vault file AEAD on every chunk; header HMAC; passphrase keyslot brute-forcing rate-limited by Argon2id (256 MiB / 540 ms / unlock by default)
Tampering of any header byte HMAC-SHA256 over the entire 8 KiB header under an MVK-derived subkey
Tampering of any chunk byte Per-chunk AEAD with AAD = file_id || chunk_idx || generation
Replay of older chunks within a file The AAD's generation counter increments on every overwrite
Cross-file chunk substitution The AAD's file_id differs per file, so a chunk from file A doesn't decrypt in file B
Whole-vault rollback The .anchor sidecar carries an MVK-keyed HMAC over a monotonic generation counter; rolling back the vault to an older snapshot trips the anchor check
Wrong unlock material All FIDO2 slots, passphrase slots, and hybrid slots fail with UnlockFailed (no oracle distinguishing "wrong passphrase" from "wrong device" from "tampered slot")
Post-quantum "harvest now, decrypt later" attacks Hybrid-PQ keyslots add ML-KEM-768 / 1024 on top of the classical KEK derivation
Rogue / MITM FIDO2 device returning attacker-chosen values FFI-boundary length cap + downstream Keyslot::new_* cap + AEAD wrap rejection of wrong KEK (verified in tests/rogue_authenticator.rs)
Vault-internal symlinks pointing outside Impossible by construction: VFS has no Symlink kind, FUSE rejects symlink()/link(), WinFsp ignores reparse points, validate_metadata_tree enforces only File / Directory inodes
Symlink-target overwrite during plaintext extraction secure_create_or_truncate opens with O_NOFOLLOW; pre-existing symlink destinations (e.g. attacker-staged /tmp/output -> /etc/passwd) fail with ELOOP instead of redirecting the write
Concurrent open from a second process Exclusive flock on the vault handle; second opener gets VaultLocked before it can read a stale header
Path substitution between open and lock Post-lock verify_path_inode rejects rename-during-open swaps with Error::PathSubstituted
Power loss during atomic sidecar replacement Temp-file fsync, atomic rename, then parent-directory fsync on Unix (fsync on dir handle) and Windows (FILE_FLAG_BACKUP_SEMANTICS + FlushFileBuffers)
Malicious authenticated metadata (chunk-id wraparound, dangling inodes) Vfs::open validates the entire decrypted metadata tree (root, parent/child consistency, chunk offset bounds, generation sanity, free-list overlap with live chunks) before any VFS operation can use it
TPM keyslot taken to a different machine tpm2-sealed + tpm2-pin + tpm2-fido2 slots are bound to the local chip's endorsement seed; another machine cannot unseal even with the PIN

Not defended

Threat Why not
Attacker-controlled host while the vault is open Memory snapshots, kernel exploits, ptrace, debugging - all bypass any host-side protection
Coerced user (rubber-hose attack) No "duress passphrase" / hidden-volume yet. v0.2 ships deniable mode (the vault file itself is indistinguishable from random output, so the user can deny it is a vault at all); two-passphrase hidden / nested volumes are planned for v1.0
Compromise of the FIDO2 device's secure element Vendor-side problem; out of LUKSbox's control
Quantum recovery of FIDO2 hmac-secret from sniffed USB-HID traffic Defended only when paired with a hybrid-PQ keyslot - pure FIDO2 vaults are vulnerable to this in the CRQC era
Storage-layer corruption / hardware bit-flips AEAD will reject corrupted chunks; that's data loss, not a confidentiality break
Plausible-deniability hidden volumes Not yet. Planned for v1.0. The v0.2 deniable mode is a partial answer (the vault itself is unidentifiable) but does not provide the VeraCrypt-style "two passphrases unlock two distinct vaults inside one file" property
Operating-system-level attackers (e.g. EDR running as your user) Out of scope for any user-space encryption tool
Filesystem snapshots taken while the vault is unlocked APFS / btrfs / ZFS / LVM snapshots are content-encrypted on disk (FUSE never materializes plaintext), but a full-VM / hibernate / vmss snapshot captures the MVK from RAM. Mitigation is operational: disable hibernation on hosts holding live vaults, verify swap is encrypted (default on macOS, manual on Linux), prefer FileVault on macOS so swap inherits volume encryption.
Kernel-resident attacker (rootkit, kernel exploit) LUKSbox is userspace; the kernel is in its trusted computing base. A malicious kernel module can read process memory, intercept FUSE traffic before decryption, and substitute on-disk bytes between read and AEAD-verify. Same statement applies to a hostile hypervisor on virtualized hosts.
Hardware tampering beyond the FIDO2 device Cold-boot RAM attacks (de-energizing DRAM seconds after power-off), JTAG / debug-port access, DMA over Thunderbolt with IOMMU disabled, supply-chain-implanted SPI flash. None of these are software-mitigable. Defences are physical: full-memory encryption (Intel TME / AMD SME), IOMMU enforcement, lockscreens that zeroize keys on session lock (roadmap).
Evil-maid attack on the LUKSbox binary itself An attacker with brief physical access to an unattended unlocked machine can replace luksbox / luksbox-gui with a trojan that exfiltrates the next passphrase typed into it. The defence is full-disk encryption with pre-boot authentication: FileVault on macOS, BitLocker on Windows, LUKS on Linux. LUKSbox cannot verify its own binary against a tamper-resistant root of trust at runtime.
Supply-chain attack on the LUKSbox build pipeline or dependencies A malicious Cargo dep (RustSec-disclosed or zero-day), a compromised crates.io account, or a tampered release artifact would compromise the resulting binary. We pin via Cargo.lock, run cargo audit in CI, and ship Sigstore attestations on every release, but a determined supply-chain attacker is out of scope for source-level review. Users at high risk should build from source against a verified Cargo.lock on a system they fully control.

Adversary capability matrix

Adversary Has the disk? Has the device? Has the passphrase? Has the .kyber seed? Outcome
Lost laptop Yes No No No Vault unreadable. Argon2id rate-limit makes passphrase guessing infeasible if entropy >= 60 bits
Lost YubiKey No Yes Maybe No Without the disk, nothing to attack
Stolen YubiKey + stolen laptop Yes Yes No No If FIDO2 PIN not exhausted: opens. Defence is the device's PIN counter (typically 8 attempts then wipe)
Insider with disk + passphrase guess Yes No Trying No Argon2id rate-limit + per-vault salt; brute-force depends on passphrase entropy
State-level CRQC + recorded USB traffic Yes No No No Pure FIDO2: breaks. Hybrid-PQ-FIDO2: holds (also needs the .kyber seed)