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).
85 lines
6 KiB
Markdown
85 lines
6 KiB
Markdown
<!-- 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).
|