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:
parent
862f4a2df6
commit
5050570b23
24 changed files with 356 additions and 139 deletions
79
CHANGELOG.md
79
CHANGELOG.md
|
|
@ -1,33 +1,78 @@
|
|||
# VaptVupt Changelog
|
||||
|
||||
|
||||
## [5.0.0] — unreleased — genuine FIPS 203 ML-KEM-768 (breaking PQ change)
|
||||
## [5.0.0] — 2026-07-10 — genuine FIPS 203 ML-KEM-768; GUI + CLI hardening
|
||||
|
||||
### Security / correctness — ML-KEM-768 is now FIPS 203-conformant
|
||||
|
||||
- The in-tree ML-KEM-768 was **round-3 CRYSTALS-Kyber, not final FIPS 203**, and
|
||||
therefore not interoperable with a compliant ML-KEM despite the "FIPS 203"
|
||||
label. Three deviations were found and fixed:
|
||||
1. **Matrix  transpose convention** — K-PKE.KeyGen must use `SampleNTT(ρ‖j‖i)`
|
||||
and K-PKE.Encrypt `SampleNTT(ρ‖i‖j)`; the implementation had both swapped.
|
||||
Self-consistent (round-trips passed) but non-standard, which is exactly why
|
||||
a self-consistency-only test never caught it.
|
||||
2. **Encaps/decaps KDF** — FIPS 203 outputs `K` from `G(m‖H(ek))` directly; the
|
||||
round-3 final `K = KDF(K̄‖H(c))` step was removed.
|
||||
- The in-tree ML-KEM-768 was **round-3 CRYSTALS-Kyber, not final FIPS 203**, so
|
||||
it was **not interoperable** with a compliant ML-KEM despite the "FIPS 203"
|
||||
label. Found by validating against OpenSSL 3.5's ML-KEM-768. Three deviations,
|
||||
all fixed in `src/zupt_mlkem.c`:
|
||||
1. **Matrix  transpose convention** — FIPS 203 K-PKE.KeyGen samples
|
||||
`Â[i][j] = SampleNTT(XOF(ρ, j, i))` and K-PKE.Encrypt uses `(ρ, i, j)`; both
|
||||
index orders were swapped. Self-consistent (round-trips passed) but
|
||||
transposed vs the standard — precisely why a self-consistency-only test
|
||||
never caught it.
|
||||
2. **Encaps/decaps KDF** — the shared secret is now `K` from `G(m‖H(ek))`
|
||||
directly; the round-3 final `K = KDF(K̄‖H(c))` step was removed.
|
||||
3. **Implicit rejection** — now `K̄ = J(z‖c)` (SHAKE256 over the full
|
||||
ciphertext) instead of `KDF(z‖H(c))`.
|
||||
- **Validated for genuine conformance against OpenSSL 3.5's FIPS 203 ML-KEM-768**
|
||||
(`tests/test_mlkem_fips203.sh`, wired into `make check`): deterministic keygen
|
||||
produces byte-identical `ek`, and the shared secret matches in **both**
|
||||
cross-decapsulation directions (our encaps ↔ OpenSSL decaps, and vice-versa).
|
||||
This replaces the previous self-consistency-only round-trip test.
|
||||
- **Validated for genuine conformance against OpenSSL 3.5's FIPS 203
|
||||
ML-KEM-768** (`tests/test_mlkem_fips203.sh`, wired into `make check`):
|
||||
deterministic keygen produces a byte-identical `ek`, and the shared secret
|
||||
matches in **both** cross-decapsulation directions. This permanent conformance
|
||||
test replaces the previous self-consistency-only round-trip.
|
||||
|
||||
### Security — CLI
|
||||
|
||||
- **Data-loss guard.** `compress -p out.zupt file1 file2` used to let `-p`
|
||||
swallow the archive name as the password, then overwrite `file1` with the
|
||||
archive (silent, exit 0). Now refuses to overwrite an existing non-`.zupt`
|
||||
file as the output archive (override with `-y`/`--force`), plus a
|
||||
self-overwrite guard.
|
||||
- **Silent-plaintext guard.** `compress out.zupt dir -p pw` used to write an
|
||||
**unencrypted** archive (exit 0) because options after the first positional
|
||||
were treated as files. Now errors on a misplaced option (`--` escapes a real
|
||||
dashed filename).
|
||||
- **Heap OOB read** in the AVX2 decoder fast path on crafted archives: the 2-/3-
|
||||
byte match-offset read is now bounded like the scalar tail path.
|
||||
- Wipe ML-KEM/X25519 secret buffers on hybrid-decrypt key-read failure; bound
|
||||
the attacker-controlled `encryption_header_off` in the `info` reader.
|
||||
- `version`/`help`/banners now state the build's real KDF (PBKDF2-SHA256 on the
|
||||
source-only build) and repo URL.
|
||||
|
||||
### GUI — reworked for the source-only build
|
||||
|
||||
- The GUI defaulted every encryption path to the libzuptsdk "SDK v2" modes,
|
||||
which are absent from the source-only build and fail — so key generation and
|
||||
encryption failed out of the box. Reworked around the native modes: a
|
||||
build-aware **PQ-mode selector** (Hybrid `--pq` default · Full-PQ `--pq-only` ·
|
||||
SDK v2 only when the binary reports `WITH_SDK` support), detected from
|
||||
`version`. Extract/Verify gained a PQ private-key input with **auto-detect**
|
||||
(reads the archive via `info` to pick `--pq` vs `--pq-only`); Verify could not
|
||||
check any PQ archive before. Fixed a DiskTab QThread-lifetime bug (two buttons
|
||||
shared one slot); corrected the About tab (codec, default KDF, `--pq-only`,
|
||||
repo URL). Guix packaging: put Shiboken6 on the launcher path so PySide6
|
||||
actually imports.
|
||||
|
||||
### Packaging & cross-platform
|
||||
|
||||
- `debian/rules`, `aur`, `nix`, `homebrew` no longer install the removed
|
||||
vendored `.so`/`AUDIT.md` or use stale `/zupt` URLs; `opensuse` `%files` now
|
||||
ships the shell completions (no more "unpackaged files" rpmbuild failure).
|
||||
- New **portable cross-platform GUI package** (`packaging/portable/`) that runs
|
||||
on Windows/macOS/Linux/BSD with Python + PySide6, and a **GitHub Actions
|
||||
workflow** (`.github/workflows/cross-platform.yml`) that builds native
|
||||
Windows (`.exe` + Inno Setup installer) and macOS (`.dmg`) artifacts on real
|
||||
runners and attaches them to the release.
|
||||
|
||||
### BREAKING
|
||||
|
||||
- **`--pq` and `--pq-only` keys and archives created by ≤ 4.2.1 are not
|
||||
readable by this release** (the KEM math changed). Regenerate keys
|
||||
(`keygen`/`keygen --pq-only`) and re-encrypt affected archives. Password mode
|
||||
(`-p`) and plain compression are unaffected. Wire format stays v1.6.
|
||||
(`keygen` / `keygen --pq-only`) and re-encrypt affected archives. Password
|
||||
mode (`-p`) and plain compression are unaffected. Wire format stays v1.6.
|
||||
|
||||
|
||||
## [4.2.1] — 2026-07-10 — `info` correctly reports the post-quantum mode
|
||||
|
|
|
|||
Loading…
Reference in a new issue