v5.0.0: version bump, audit fixes, documentation overhaul

Version bumped to 5.0.0 across include/zupt.h, all packaging recipes, man
page, and docs.

Audit fixes (pre-5.0.0 review):
- src/zupt_format.c: overflow-safe bound in the solid-mode `test` path
  (off+sz could wrap and drive an OOB read in zupt_xxh64 on a crafted archive;
  the extract path was already hardened, the test path was not).
- gui: run_async now marshals the completion callback onto the GUI thread with
  QueuedConnection (a bare functor connected DirectConnection and touched
  widgets off the worker thread); Extract auto-detect note survives the log
  clear via a new `info` param.
- .github/workflows/ci.yml: trigger on `master` (was main/develop, so CI never
  ran); `make dist` tarball is vaptvupt-*.tar.gz not zupt-*; the ASAN PQ
  round-trip uses native --pq (was --pq-sdk, which fails on the source-only
  build and blocked the release job).

Documentation:
- New AUDIT.md (methodology, FIPS 203 conformance validation, findings, repro).
- CHANGELOG 5.0.0 entry covers the FIPS 203 conformance fix + BREAKING note and
  the GUI/CLI/security/packaging work.
- README "What's new in 5.0.0", download tables (incl. Windows/macOS/BSD +
  portable GUI), version-history row.
- SECURITY.md + THREAT_MODEL.md: ML-KEM-768 documented as FIPS 203, validated
  byte-for-byte against OpenSSL 3.5.
- Accuracy fixes: man page (--kdf default is PBKDF2 on source-only; codec
  2.60.4), rpm %description, debian control/copyright, homebrew header
  (no vendored library on source-only builds).

make check 16/16 (FIPS 203 conformance 3/3, all distro-safe checks).
This commit is contained in:
Cristian Cezar Moisés 2026-07-10 17:22:02 -03:00
commit 5050570b23
24 changed files with 356 additions and 139 deletions

85
AUDIT.md Normal file
View file

@ -0,0 +1,85 @@
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
# VaptVupt — Security Audit
This document records the security review of VaptVupt: what is checked, how, the
findings and their resolutions, and how to reproduce the checks. It complements
[SECURITY.md](SECURITY.md) (policy + primitives) and
[THREAT_MODEL.md](THREAT_MODEL.md) (what is and isn't defended).
Scope: the pure-C11 CLI (`src/`, `include/`) and the PySide6/PyQt6 GUI
(`gui/src/zupt_gui.py`). Out of scope: the optional, separately distributed
`libzuptsdk` / `libpqvaptvupt` binaries (only present in a `make WITH_SDK=1`
build); the shipped source-only build contains no vendored binaries.
> **Not independently certified.** This is the project's own structured review,
> not a third-party accredited audit. Treat it as "reviewed, with reproducible
> evidence" and do your own review for high-assurance use.
## Methodology
| Technique | What it covers | Where |
|---|---|---|
| **Cryptographic conformance vs an independent reference** | ML-KEM-768 is validated byte-for-byte against **OpenSSL 3.5's FIPS 203 ML-KEM-768** — deterministic keygen `ek` equality plus shared-secret agreement in both cross-decapsulation directions. | `tests/test_mlkem_fips203.sh`, in `make check` |
| **NIST/RFC known-answer vectors** | SHA-256 (FIPS 180-4), SHA-3/SHAKE (FIPS 202), AES-256-CTR (SP 800-38A F.5.5/F.5.6), HMAC-SHA256 (RFC 4231), X25519 (RFC 7748), ML-KEM-768, PBKDF2. | `tests/test_vectors.c` |
| **Byte-level tamper sweep** | Every byte position of a representative archive is flipped and re-opened; zero silent-accepts required (F-09). | `tests/` byte-sweep |
| **Authenticated-encryption fuzzing** | HMAC / integrity-trailer fuzz over many trials (F-06, F-08). | `tests/` |
| **Constant-time measurement** | dudect-style Welch t-test on the MAC-tag compare and the ML-KEM FO implicit-rejection compare (the two decapsulation-oracle-sensitive paths). | `tests/test_ct_timing.*` |
| **Memory-safety sanitizers** | ASan + UBSan builds; exact-size decode cases; crafted-input decode. | `make test-asan` |
| **Static analysis** | cppcheck (warning/style/performance) on the first-party sources; strict `-Wall -Wextra -Wpedantic -Werror` gcc + clang matrix. | CI |
| **Formal annotations** | Frama-C/ACSL contracts on memory-safety-critical functions; 5 Jasmin-verified constant-time assembly routines (x86_64). | `include/zupt_acsl.h`, `jasmin/` |
| **Adversarial multi-agent review** | Independent reviewers per dimension (crypto, parser/memory-safety, CLI, GUI↔CLI contract, packaging), each finding then adversarially refuted before it is accepted. | manual, per release |
## Cryptographic conformance
- **ML-KEM-768 — genuine FIPS 203 (v5.0.0).** Earlier releases shipped round-3
CRYSTALS-Kyber under a "FIPS 203" label; it was self-consistent and secure as
an IND-CCA2 KEM but **not interoperable** with a compliant ML-KEM. Validating
against OpenSSL 3.5 revealed three deviations — a transposed matrix-`Â`
sampling convention (in both K-PKE.KeyGen and K-PKE.Encrypt), the round-3 final
KDF, and the implicit-rejection domain. All three were fixed and the result is
now byte-for-byte interoperable with OpenSSL in both directions. A permanent
conformance test guards against regression. This changed the shared secret, so
it is a wire-breaking change for `--pq`/`--pq-only` archives (see CHANGELOG
5.0.0 BREAKING).
- **Hybrid is the flagship.** `--pq` combines ML-KEM-768 with X25519 through a
SHA3-512 combiner; the archive key is secure if **either** primitive holds —
the strongest real-world posture and the recommended default. `--pq-only`
offers pure ML-KEM-768 for single-primitive compliance mandates.
- **Envelope.** AES-256-CTR with a **fresh random 128-bit nonce per block**
(the dedup keystream-reuse bug is fixed and regression-tested), HMAC-SHA256
Encrypt-then-MAC verified before any decryption, and an archive-integrity
trailer over the header/footer.
## Notable findings and resolutions (recent)
| Sev | Finding | Resolution |
|---|---|---|
| High | ML-KEM-768 not FIPS 203-conformant / not interoperable | Fixed (transpose + KDF); validated vs OpenSSL; permanent conformance test |
| High | `compress -p out.zupt f1 f2` overwrote an input file (data loss, exit 0) | Refuse to overwrite an existing non-`.zupt` output without `-y/--force`; self-overwrite guard |
| High | `compress out.zupt dir -p pw` wrote an **unencrypted** archive (exit 0) | Error on a misplaced option after the archive (`--` escape available) |
| Critical | AES-CTR keystream reuse across `--dedup` blocks (many-time-pad) | Fresh random per-block nonce; regression test |
| Medium | Heap OOB read in the AVX2 decoder fast path on crafted input | Bound the 2-/3-byte offset read like the scalar tail path |
| Medium | GUI defaulted to SDK modes absent from the source-only build (unusable) | Reworked to native `--pq`/`--pq-only`; SDK shown only when supported |
| Low | Hybrid-decrypt did not wipe secret buffers on key-read failure | Wipe on the error path (matches the pq-only path) |
| Low | Untruthful banner (Argon2id-default / `/zupt` URL) on source-only builds | Build-aware, accurate `version`/`help` output |
| Critical* | Packaging (`debian/rules`, `aur`, `nix`, `homebrew`, `opensuse`) would fail a source-only build | Removed vendored-lib/`AUDIT.md` steps, fixed URLs, added completions |
\* build-time failure, not a runtime security issue.
## Known limitations / non-goals
- No protection against a compromised endpoint, a weak password, or key
custody failures (see THREAT_MODEL.md).
- Metadata (total archive size, block count) is observable.
- The review is reproducible but not third-party certified.
## Reproducing
```sh
make check # vectors, tamper sweep, FIPS 203 conformance, guards
make test-asan # ASan + UBSan
bash tests/test_mlkem_fips203.sh # FIPS 203 interop vs OpenSSL (needs openssl 3.5+)
```
FIPS 203 conformance needs an ML-KEM-capable OpenSSL (3.5+); the test skips
gracefully otherwise (e.g. inside a distro package build).