# Security Policy
`rsecure` is a file encryption CLI built on top of [AES-256-GCM][aes-gcm]. Because it handles
sensitive data, the following document describes what it does and does not protect against,
and how to responsibly report a vulnerability.
## Supported Versions
Only the latest released version receives security fixes. Older versions are unsupported.
Check the latest release at https://github.com/containerscrew/rsecure/releases.
## Cryptographic Design
### File format v3 (current)
| Cipher | AES-256-GCM (256-bit key, 128-bit tag) |
| Construction | STREAM (chunked AEAD) via `aes_gcm::aead::stream::EncryptorBE32` |
| Chunk size | 131072 bytes (128 KiB), declared per-file in the header |
| Key derivation | HKDF-SHA256(ikm=master_key, salt=random 32 B per file, info=`"rsecure-v3-aes256gcm-stream"`) → 32-byte AES-256 subkey |
| STREAM nonce | Fixed all-zero 7-byte salt; uniqueness is provided by the per-file HKDF subkey, with STREAM's 4-byte BE32 counter ensuring uniqueness across chunks within the file |
| File header | `RSEC` (4 B) + version `0x03` (1 B) + flags (1 B) + chunk_size (u32 LE, 4 B) + HKDF salt (32 B) = **42 bytes**; passphrase mode appends Argon2 params (9 B) + Argon2 salt (16 B) for **67 bytes** total |
| Header authenticity | The entire on-disk header is passed as AAD on every chunk; any tampering invalidates the first GCM tag and decryption fails before any plaintext is recovered |
| Master key source | Either a 32-byte keyfile (default) or derived once-per-invocation from a passphrase via Argon2id, see below |
#### Master key sources
`flags & 0x01 == 0` (**keyfile**): the master key is the 32-byte keyfile passed
via `-p`. This is the strongest default — the master key has 256 bits of OS-RNG
entropy.
`flags & 0x01 == 1` (**passphrase**): the master key is derived via
Argon2id(passphrase, argon2_salt, params) where the salt and parameters live in
the file header. Default parameters: `m_cost = 19456 KiB (~19 MiB)`,
`t_cost = 2`, `p_cost = 1`, output length 32 bytes. These defaults can be
overridden per-invocation via `--argon2-memory`, `--argon2-time`, and
`--argon2-parallelism`; the chosen values are recorded in each file's header so
decryption picks them up automatically. **Raising the parameters (more memory,
more iterations) strengthens the KDF; lowering them below the defaults weakens
it and is discouraged.** The salt is generated once per invocation, so an entire
`encrypt` batch shares one Argon2 derivation; the decrypter caches by salt to
avoid re-running the KDF on subsequent files of the same batch.
**Security in passphrase mode is bounded by the entropy of your passphrase.**
Argon2id raises the cost-per-attempt enough to make weak passphrases
significantly harder to brute-force, but a 6-character dictionary word remains
weak regardless of the KDF.
Because the AES-256 subkey is unique per file (derived from a fresh 256-bit
random salt), the `(key, nonce)` pair is globally unique across all files even
with a fixed STREAM nonce. This eliminates the birthday-bound nonce-collision
concern that would otherwise apply to AES-GCM's 96-bit nonce when many files
are encrypted under the same master key.
On decrypt, a sanity bound (`chunk_size ≤ 16 MiB`) rejects pathological headers
before any buffer is allocated, so a hostile `.enc` cannot trigger an unbounded
allocation.
### File formats v1 and v2 (legacy, decrypt-only)
- **v1** (rsecure ≤ 0.5.0): AES-256-GCM STREAM with a 7-byte random nonce
derived directly from the master key, no HKDF, no magic header.
- **v2** (interim, brief release window before v3): `RSEC` `0x02` header,
HKDF-derived subkey, AAD-bound — same scheme as v3 keyfile mode but without
the flags byte.
`rsecure decrypt` reads both transparently; the dispatcher picks the right
code path from the magic + version. New encryptions always use v3.
The cryptographic primitives are provided by the [`aes-gcm`][aes-gcm-crate],
[`hkdf`][hkdf-crate], and [`argon2`][argon2-crate] crates from [RustCrypto],
widely-used, audited, pure-Rust implementations.
## Post-Quantum Considerations
`rsecure` is being developed with the intent of remaining safe against a
large-scale quantum adversary. This is a **stated design goal**, not a formal
certification — the project is small, evolving, and has not undergone
independent cryptanalytic review. Read this section for what the intent means
in concrete terms and where the honest limits sit.
### Primitives and their post-quantum status
| AES-256-GCM | 256-bit key | ~128-bit (Grover) | NIST PQC Category 5; part of NSA CNSA 2.0 symmetric baseline. |
| HKDF-SHA256 | 256-bit preimage | ~128-bit preimage (Grover) | Used for per-file subkey derivation only; not for long-lived signatures. |
| Argon2id | Memory-hard | Memory-hard (√ speedup only) | Symmetric password KDF; unaffected by Shor. See RFC 9106. |
No asymmetric primitives are used anywhere in `rsecure`. There is no RSA, no
elliptic-curve Diffie–Hellman, no ECDSA, and no X25519 — so Shor's algorithm
has nothing to break in the current design. This is the single biggest
post-quantum risk in tools that rely on public-key primitives for key
agreement or signing, and rsecure sidesteps it by construction.
### What this does NOT mean
- **No formal PQ certification exists for this implementation.** NIST PQC
categories apply to the primitives, not to this specific codebase or
parameter choices.
- **No post-quantum KEM or signature is included.** rsecure does not ship
ML-KEM (Kyber) or ML-DSA (Dilithium) today because there is no
key-exchange or signing surface in scope. If a future feature ever
requires asymmetric crypto (e.g., recipient-based encryption), the
intention is to reach for NIST PQC standards rather than pre-quantum
primitives.
- **Legacy formats (v1, v2)** inherit the parameters of their era. The
post-quantum posture applies to files produced by the current v3 format;
re-encrypting long-lived archives is recommended.
- **Distributing keys and passphrases** still relies on out-of-band channels;
the post-quantum properties of those channels are outside `rsecure`'s
scope and remain the user's responsibility.
- **Side-channel resistance** is best-effort (inherited from `aes-gcm`) and
is not itself a post-quantum property, but is listed here alongside the
other honest caveats.
### References
- NSA CNSA 2.0 (2022) — AES-256 and SHA-384+ as post-quantum symmetric baseline.
- NIST FIPS 203 (ML-KEM), FIPS 204 (ML-DSA), FIPS 205 (SLH-DSA) — standardized
post-quantum asymmetric primitives, referenced here for future work only.
- NIST SP 800-208 — stateful hash-based signatures (not used by rsecure).
## Threat Model
### What rsecure guarantees
- **Confidentiality** of file contents under a chosen-plaintext attacker that does not
possess the master key.
- **Integrity & authenticity** of each chunk — any tampering will be detected on decrypt
(GCM authentication tag).
- **Resistance to chunk reordering or truncation** — STREAM binds chunks via a counter
and a final-chunk marker.
- **No catastrophic nonce reuse across files** — the per-file HKDF subkey makes the
`(key, nonce)` pair globally unique even with a fixed STREAM nonce.
- **Best-effort memory hygiene** for secrets. Passphrases, master keys, and
derived subkeys are wrapped in [`zeroize`](https://crates.io/crates/zeroize)
guards and cleared on drop. This is best-effort — Rust does not guarantee
freedom from compiler-introduced copies or spilled stack slots, and swap /
hibernation / core dumps can still leak secrets outside the process — but it
narrows the residual-memory attack surface.
- **Header authenticity (v2 only).** Modifying any byte of the on-disk header
(magic, version, chunk_size, salt) causes decryption to fail with an auth error
on the first chunk, before any plaintext is written. There is no downgrade,
rebinding, or wrong-key-via-salt-swap path that produces valid plaintext.
- **Post-quantum design intent (symmetric-only).** No RSA, ECDH, ECDSA, or
X25519 is used, so Shor's algorithm has nothing to break in the current
design. AES-256 and SHA-256 are only reduced to ~128-bit security by
Grover. See [Post-Quantum Considerations](#post-quantum-considerations)
for the honest limits — this is a design goal, not a certification.
### What rsecure does NOT guarantee
- **Filename and directory structure are not protected.** Only the file contents are
encrypted; metadata (paths, sizes, timestamps) remain visible.
- **Key storage is the user's responsibility.** A master key file left on disk in
plaintext offers no protection against an attacker with filesystem access. Use
full-disk encryption, a hardware token, or a password manager for key custody.
- **No forward secrecy / no post-compromise security.** If the master key (or
passphrase) is compromised, all past and future ciphertext under it is
exposed (HKDF subkeys are derived deterministically from the master key and
the per-file salt).
- **No authenticated key exchange.** Distributing the master key to another party is
out of scope; use an out-of-band secure channel (Signal, age, GPG, in person).
- **No plausible deniability.** Encrypted files are clearly identifiable as such (`.enc`
extension and structured header).
- **Side-channel resistance** is best-effort, inherited from `aes-gcm`. The crate uses
constant-time arithmetic but does not formally guarantee freedom from cache or timing
side channels on every target architecture. On CPUs without AES-NI (or equivalent
hardware-accelerated AES), software AES is more exposed to cache-timing side channels.
- **Legacy v1 nonce collisions.** Files encrypted by rsecure ≤ 0.5.0 used a 7-byte
random nonce (56 bits), which approaches the birthday bound around 2²⁸ files
(~268M) and crosses NIST's 2⁻³² safety margin around ~6k files. Re-encrypt
long-lived v1 archives with the current version to migrate them to v3.
- **Passphrase strength.** In passphrase mode, the master key's effective
security is bounded by your passphrase's entropy. Argon2id raises the
per-attempt cost but cannot rescue a weak passphrase from an offline
brute-force attacker who obtains a `.enc` file. Use a key file for the
strongest guarantee, or a long, high-entropy passphrase (e.g., a diceware
phrase of 6+ words).
## Reporting a Vulnerability
**Do not file public issues for security vulnerabilities.**
Please report security issues via one of these channels:
1. **GitHub Security Advisories (preferred):** open a private advisory at
https://github.com/containerscrew/rsecure/security/advisories/new
2. **Email:** `info@containerscrew.com` with the subject prefixed by `[rsecure-sec]`.
Please include:
- A description of the issue and its impact.
- A minimal reproduction (file, command, expected vs. actual behavior).
- Your suggested fix, if any.
You can expect an acknowledgement within 7 days and a status update within 30 days.
Coordinated disclosure is preferred; please give us a reasonable window to ship a fix
before public disclosure.
[aes-gcm]: https://en.wikipedia.org/wiki/Galois/Counter_Mode
[aes-gcm-crate]: https://crates.io/crates/aes-gcm
[hkdf-crate]: https://crates.io/crates/hkdf
[argon2-crate]: https://crates.io/crates/argon2
[RustCrypto]: https://github.com/RustCrypto