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
29
.github/workflows/ci.yml
vendored
29
.github/workflows/ci.yml
vendored
|
|
@ -21,10 +21,10 @@ name: CI
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
branches: [master]
|
||||
tags: ['v*']
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
# ─── Plain build + test, exactly as a user would do it ───
|
||||
|
|
@ -76,15 +76,18 @@ jobs:
|
|||
run: sudo apt-get update && sudo apt-get install -y build-essential python3
|
||||
- name: Build with ASAN + UBSAN
|
||||
run: make test-asan
|
||||
- name: PQ-SDK byte-exact roundtrip under ASAN
|
||||
- name: Native --pq byte-exact roundtrip under ASAN
|
||||
env:
|
||||
ASAN_OPTIONS: detect_leaks=0:abort_on_error=1
|
||||
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
|
||||
run: |
|
||||
./zupt_asan keygen --sdk -o /tmp/k.priv
|
||||
./zupt_asan compress --pq-sdk /tmp/k.priv.pub /tmp/a.zupt include/
|
||||
# Native hybrid ML-KEM-768 + X25519 (the source-only default; --pq-sdk
|
||||
# needs a WITH_SDK=1 build and is unavailable here).
|
||||
./zupt_asan keygen -o /tmp/k.priv
|
||||
./zupt_asan keygen --pub -o /tmp/k.pub -k /tmp/k.priv
|
||||
./zupt_asan compress --pq /tmp/k.pub /tmp/a.zupt include/
|
||||
mkdir -p /tmp/extracted
|
||||
./zupt_asan extract --pq-sdk /tmp/k.priv -o /tmp/extracted /tmp/a.zupt
|
||||
./zupt_asan extract --pq /tmp/k.priv -o /tmp/extracted /tmp/a.zupt
|
||||
diff -qr include /tmp/extracted/include
|
||||
|
||||
# ─── PIE hardening build — verifies no runtime breakage from -fPIE ───
|
||||
|
|
@ -147,7 +150,7 @@ jobs:
|
|||
id: sha1
|
||||
run: |
|
||||
VER=$(grep '^#define ZUPT_VERSION_STRING' include/zupt.h | awk -F'"' '{print $2}')
|
||||
SHA=$(sha256sum /tmp/zupt-$VER.tar.gz | awk '{print $1}')
|
||||
SHA=$(sha256sum /tmp/vaptvupt-$VER.tar.gz | awk '{print $1}')
|
||||
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
|
||||
echo "ver=$VER" >> "$GITHUB_OUTPUT"
|
||||
echo "Run 1: $SHA"
|
||||
|
|
@ -156,7 +159,7 @@ jobs:
|
|||
- name: Verify reproducibility
|
||||
run: |
|
||||
VER="${{ steps.sha1.outputs.ver }}"
|
||||
SHA2=$(sha256sum /tmp/zupt-$VER.tar.gz | awk '{print $1}')
|
||||
SHA2=$(sha256sum /tmp/vaptvupt-$VER.tar.gz | awk '{print $1}')
|
||||
if [ "$SHA2" != "${{ steps.sha1.outputs.sha }}" ]; then
|
||||
echo "::error::make dist is NOT reproducible"
|
||||
echo " run 1: ${{ steps.sha1.outputs.sha }}"
|
||||
|
|
@ -168,7 +171,7 @@ jobs:
|
|||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: zupt-source-tarball
|
||||
path: /tmp/zupt-*.tar.gz
|
||||
path: /tmp/vaptvupt-*.tar.gz
|
||||
|
||||
# ─── Packaging-recipe syntax (cross-distro) ───
|
||||
packaging-syntax:
|
||||
|
|
@ -214,15 +217,15 @@ jobs:
|
|||
id: sha
|
||||
run: |
|
||||
VER="${{ steps.ver.outputs.version }}"
|
||||
SHA=$(sha256sum /tmp/zupt-$VER.tar.gz | awk '{print $1}')
|
||||
SHA=$(sha256sum /tmp/vaptvupt-$VER.tar.gz | awk '{print $1}')
|
||||
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
|
||||
echo "$SHA zupt-$VER.tar.gz" > /tmp/zupt-$VER.tar.gz.sha256
|
||||
echo "$SHA vaptvupt-$VER.tar.gz" > /tmp/vaptvupt-$VER.tar.gz.sha256
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
/tmp/zupt-${{ steps.ver.outputs.version }}.tar.gz
|
||||
/tmp/zupt-${{ steps.ver.outputs.version }}.tar.gz.sha256
|
||||
/tmp/vaptvupt-${{ steps.ver.outputs.version }}.tar.gz
|
||||
/tmp/vaptvupt-${{ steps.ver.outputs.version }}.tar.gz.sha256
|
||||
body: |
|
||||
## Zupt v${{ steps.ver.outputs.version }}
|
||||
|
||||
|
|
|
|||
85
AUDIT.md
Normal file
85
AUDIT.md
Normal 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).
|
||||
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
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Every packaging recipe expects an upstream tarball `vaptvupt-VERSION.tar.gz` pro
|
|||
|
||||
```sh
|
||||
make dist
|
||||
# → /tmp/vaptvupt-4.2.1.tar.gz
|
||||
# → /tmp/vaptvupt-5.0.0.tar.gz
|
||||
```
|
||||
|
||||
Re-running `make dist` on the same source tree produces an identical sha256 (verified by `tests/test_dist_reproducible.sh`, wired into `make test`). This lets distros pin a stable hash in their recipes.
|
||||
|
|
@ -56,13 +56,13 @@ Maintainer flow:
|
|||
```sh
|
||||
# 1. Produce the upstream tarball
|
||||
make dist
|
||||
# → /tmp/vaptvupt-4.2.1.tar.gz
|
||||
# → /tmp/vaptvupt-5.0.0.tar.gz
|
||||
|
||||
# 2. Upload to a stable URL (e.g. git.securityops.co releases)
|
||||
|
||||
# 3. Update packaging/aur/PKGBUILD:
|
||||
# - Set pkgver=4.2.1
|
||||
# - Set sha256sums=("$(sha256sum /tmp/vaptvupt-4.2.1.tar.gz | awk '{print $1}')")
|
||||
# - Set pkgver=5.0.0
|
||||
# - Set sha256sums=("$(sha256sum /tmp/vaptvupt-5.0.0.tar.gz | awk '{print $1}')")
|
||||
|
||||
# 4. Generate .SRCINFO
|
||||
cd packaging/aur && makepkg --printsrcinfo > .SRCINFO
|
||||
|
|
@ -73,7 +73,7 @@ makepkg -s
|
|||
# 6. Push to AUR
|
||||
git clone ssh://aur@aur.archlinux.org/vaptvupt.git aur-vaptvupt
|
||||
cp packaging/aur/PKGBUILD packaging/aur/.SRCINFO aur-vaptvupt/
|
||||
cd aur-vaptvupt && git add -A && git commit -m "v4.2.1" && git push
|
||||
cd aur-vaptvupt && git add -A && git commit -m "v5.0.0" && git push
|
||||
```
|
||||
|
||||
User install:
|
||||
|
|
@ -117,10 +117,10 @@ The `packaging/debian/` tree is a Debian source-package layout. Maintainer flow:
|
|||
# 1. Produce the upstream tarball with the standard Debian
|
||||
# orig.tar.gz naming convention:
|
||||
make dist
|
||||
cp /tmp/vaptvupt-4.2.1.tar.gz /tmp/vaptvupt_4.2.1.orig.tar.gz
|
||||
cp /tmp/vaptvupt-5.0.0.tar.gz /tmp/vaptvupt_5.0.0.orig.tar.gz
|
||||
|
||||
# 2. Unpack and overlay the debian/ tree:
|
||||
cd /tmp && tar xzf vaptvupt_4.2.1.orig.tar.gz && cd vaptvupt-4.2.1
|
||||
cd /tmp && tar xzf vaptvupt_5.0.0.orig.tar.gz && cd vaptvupt-5.0.0
|
||||
cp -a /path/to/vaptvupt/packaging/debian ./debian
|
||||
|
||||
# 3. Build the source package:
|
||||
|
|
@ -128,7 +128,7 @@ dpkg-buildpackage -S -us -uc # source-only
|
|||
dpkg-buildpackage -b -us -uc # binary
|
||||
|
||||
# 4. Lint:
|
||||
lintian vaptvupt_4.2.1-1_*.deb
|
||||
lintian vaptvupt_5.0.0-1_*.deb
|
||||
|
||||
# 5. Submit via the standard Debian mentors process:
|
||||
# https://mentors.debian.net/intro-maintainers/
|
||||
|
|
@ -145,7 +145,7 @@ sudo apt install vaptvupt
|
|||
```sh
|
||||
# 1. Produce the tarball
|
||||
make dist
|
||||
cp /tmp/vaptvupt-4.2.1.tar.gz ~/rpmbuild/SOURCES/
|
||||
cp /tmp/vaptvupt-5.0.0.tar.gz ~/rpmbuild/SOURCES/
|
||||
|
||||
# 2. Drop the .spec into the SPECS directory:
|
||||
cp packaging/rpm/vaptvupt.spec ~/rpmbuild/SPECS/
|
||||
|
|
@ -154,7 +154,7 @@ cp packaging/rpm/vaptvupt.spec ~/rpmbuild/SPECS/
|
|||
cd ~/rpmbuild && rpmbuild -ba SPECS/vaptvupt.spec
|
||||
|
||||
# 4. Lint:
|
||||
rpmlint RPMS/x86_64/vaptvupt-4.2.1-1.fc*.rpm
|
||||
rpmlint RPMS/x86_64/vaptvupt-5.0.0-1.fc*.rpm
|
||||
|
||||
# 5. Submit via the Fedora new-package review process:
|
||||
# https://docs.fedoraproject.org/en-US/package-maintainers/Package_Review_Process/
|
||||
|
|
@ -177,7 +177,7 @@ The `packaging/opensuse/` tree carries an RPM `.spec` suited to the Open Build S
|
|||
make dist
|
||||
|
||||
# 2. In an OBS package checkout (osc), stage the sources and spec:
|
||||
cp /tmp/vaptvupt-4.2.1.tar.gz .
|
||||
cp /tmp/vaptvupt-5.0.0.tar.gz .
|
||||
cp /path/to/vaptvupt/packaging/opensuse/vaptvupt.spec .
|
||||
|
||||
# 3. Build locally against a target repository:
|
||||
|
|
@ -230,7 +230,7 @@ nix build github:cristiancmoises/vaptvupt#vaptvupt
|
|||
nix run github:cristiancmoises/vaptvupt#vaptvupt -- version
|
||||
|
||||
# 2. To consume from another flake:
|
||||
# inputs.vaptvupt.url = "github:cristiancmoises/vaptvupt?ref=v4.2.1";
|
||||
# inputs.vaptvupt.url = "github:cristiancmoises/vaptvupt?ref=v5.0.0";
|
||||
# packages.x86_64-linux.default = inputs.vaptvupt.packages.x86_64-linux.vaptvupt;
|
||||
|
||||
# 3. To submit to nixpkgs (https://github.com/NixOS/nixpkgs):
|
||||
|
|
|
|||
18
INSTALL.md
18
INSTALL.md
|
|
@ -5,13 +5,13 @@ If you're seeing the error:
|
|||
```
|
||||
vaptvupt-gui depende de python3-pyqt6 | python3-pyside6; porém:
|
||||
Pacote python3-pyqt6 não está instalado.
|
||||
vaptvupt-gui depende de vaptvupt (>= 4.2.1); porém:
|
||||
vaptvupt-gui depende de vaptvupt (>= 5.0.0); porém:
|
||||
Versão de vaptvupt no sistema é 2.1.7-1.
|
||||
```
|
||||
|
||||
This is correct behavior. The `vaptvupt-gui` deb requires:
|
||||
- Python 3 with **PyQt6** or **PySide6** (the GUI toolkit)
|
||||
- The **vaptvupt CLI 4.2.1** or newer
|
||||
- The **vaptvupt CLI 5.0.0** or newer
|
||||
|
||||
## The fastest fix — one command (Linux Mint, Ubuntu, Debian)
|
||||
|
||||
|
|
@ -33,8 +33,8 @@ the right order.
|
|||
sudo apt update
|
||||
sudo apt install -y python3-pyqt6
|
||||
|
||||
# 2. Upgrade vaptvupt CLI to 4.2.1
|
||||
sudo dpkg -i vaptvupt_4.2.1_amd64.deb
|
||||
# 2. Upgrade vaptvupt CLI to 5.0.0
|
||||
sudo dpkg -i vaptvupt_5.0.0_amd64.deb
|
||||
|
||||
# 3. Install the GUI
|
||||
sudo dpkg -i vaptvupt-gui_1.3.0_all.deb
|
||||
|
|
@ -50,7 +50,7 @@ sudo apt --fix-broken install
|
|||
|
||||
```bash
|
||||
sudo dnf install -y python3-pyqt6
|
||||
sudo dnf install -y vaptvupt-4.2.1-1.x86_64.rpm vaptvupt-gui-1.3.0-1.noarch.rpm
|
||||
sudo dnf install -y vaptvupt-5.0.0-1.x86_64.rpm vaptvupt-gui-1.3.0-1.noarch.rpm
|
||||
```
|
||||
|
||||
(Or build the RPM from the SRPM tarball with `rpmbuild -bb SPECS/vaptvupt.spec`)
|
||||
|
|
@ -98,7 +98,7 @@ Qt6 inside the deb because:
|
|||
- Bundling would make the deb 80 MB+ instead of 35 KB
|
||||
- Distribution-managed Qt gets security updates automatically
|
||||
|
||||
## Why does the GUI need vaptvupt 4.2.1?
|
||||
## Why does the GUI need vaptvupt 5.0.0?
|
||||
|
||||
The GUI calls `vaptvupt --pq` and `vaptvupt keygen` for native
|
||||
post-quantum encryption (ML-KEM-768 + X25519, in-tree implementation).
|
||||
|
|
@ -108,7 +108,7 @@ fail against them.
|
|||
## After installing — verify
|
||||
|
||||
```bash
|
||||
vaptvupt version # should show: 4.2.1
|
||||
vaptvupt version # should show: 5.0.0
|
||||
vaptvupt-gui # should launch the GUI window
|
||||
```
|
||||
|
||||
|
|
@ -178,8 +178,8 @@ sudo zypper install gcc make # openSUSE
|
|||
### Build VaptVupt itself
|
||||
|
||||
```bash
|
||||
tar -xzf vaptvupt-4.2.1-source.tar.gz
|
||||
cd vaptvupt-4.2.1
|
||||
tar -xzf vaptvupt-5.0.0-source.tar.gz
|
||||
cd vaptvupt-5.0.0
|
||||
|
||||
make # build the `./vaptvupt` binary
|
||||
sudo make install # install to /usr/local/bin (override with PREFIX=/usr)
|
||||
|
|
|
|||
101
README.md
101
README.md
|
|
@ -17,44 +17,40 @@ License: AGPL-3.0-or-later (dual-licensed AGPL + commercial).
|
|||
> command is preserved as a symlink to `vaptvupt` for one major version
|
||||
> cycle.
|
||||
|
||||
## What's new in 4.2.1 / 4.2.0
|
||||
## What's new in 5.0.0
|
||||
|
||||
- **4.2.1 (patch):** `vaptvupt info` now correctly reports the post-quantum
|
||||
mode — a `--pq-only` archive reads "ML-KEM-768 only, no classical layer"
|
||||
instead of being mislabelled as hybrid. Reader-side only; no wire-format
|
||||
change, and existing 4.2.0 archives are relabelled with no re-encryption.
|
||||
|
||||
The 4.2.0 feature set (all included in 4.2.1):
|
||||
|
||||
- **Full (pure) post-quantum mode — `--pq-only`.** ML-KEM-768 (FIPS 203) as
|
||||
the *sole* key-establishment mechanism, with no classical X25519 component.
|
||||
For compliance postures that mandate a single NIST-standardised PQ primitive
|
||||
with no classical KEM in the envelope (CNSA 2.0-style "PQ-only"). Envelope
|
||||
type `0x06`; archive key `SHA3-512(ml_ss ‖ ml_ct ‖ "ZUPT-PQ-ONLY-v1")`.
|
||||
In-tree crypto, available in the default build.
|
||||
- **Hybrid `--pq` stays the default recommendation.** `--pq-only` trades away
|
||||
the hybrid safety net: a future break of ML-KEM-768 *alone* would break a
|
||||
`--pq-only` archive, whereas `--pq` would still require breaking X25519 too.
|
||||
Use `--pq-only` only when a policy forbids the classical component.
|
||||
- **Security fix (critical): AES-CTR keystream reuse under `--dedup`.** Dedup
|
||||
blocks all share sequence 0, and the old nonce (`base_nonce XOR seq`)
|
||||
therefore collapsed to one value across blocks — reusing the CTR keystream
|
||||
(a many-time-pad). Every block now uses a **fresh random 128-bit nonce**.
|
||||
Re-encrypt any `--dedup` + encrypted archives written by ≤ 4.1.0. Regression
|
||||
test: `tests/test_dedup_nonce.sh`.
|
||||
- **Clearer SDK keygen guidance.** `keygen --sdk`/`--box` on the source-only
|
||||
build now points you to native `--pq`/`--pq-only` (or a `WITH_SDK=1` build).
|
||||
- **Wire/on-disk format is v1.6, unchanged.** All 4.x archives interoperate;
|
||||
the new `0x06` envelope is additive.
|
||||
- **Genuine FIPS 203 ML-KEM-768 — validated against OpenSSL.** Earlier releases
|
||||
shipped round-3 CRYSTALS-Kyber under a "FIPS 203" label; it was secure but
|
||||
**not interoperable** with a compliant ML-KEM. Three deviations (a transposed
|
||||
matrix-`Â` sampling convention, the round-3 KDF, and the implicit-rejection
|
||||
domain) are fixed, and the result is now **byte-for-byte interoperable with
|
||||
OpenSSL 3.5's FIPS 203 ML-KEM-768** in both directions — checked on every
|
||||
`make check` (`tests/test_mlkem_fips203.sh`). Hybrid `--pq` (ML-KEM-768 +
|
||||
X25519) remains the recommended flagship; `--pq-only` is pure ML-KEM-768.
|
||||
- **⚠ Breaking:** because the KEM math changed, `--pq`/`--pq-only` **keys and
|
||||
archives from ≤ 4.2.1 no longer decrypt** — regenerate keys and re-encrypt.
|
||||
Password mode (`-p`) and plain compression are unaffected; wire format is still v1.6.
|
||||
- **CLI security fixes.** A `compress -p out.zupt file1 file2` **data-loss** bug
|
||||
(the archive name was eaten as the password and overwrote `file1`) and a
|
||||
`compress out.zupt dir -p pw` **silent-plaintext** bug are both guarded now; a
|
||||
**heap OOB read** in the AVX2 decoder on crafted archives is bounded; banners
|
||||
report the build's real KDF.
|
||||
- **GUI reworked so it actually works.** It used to default every encryption
|
||||
path to SDK modes absent from the source-only build (key generation failed out
|
||||
of the box). Now a build-aware Hybrid/Full-PQ selector, PQ-key auto-detect on
|
||||
Extract/Verify, and About/threading fixes.
|
||||
- **Cross-platform.** A portable GUI package (Windows/macOS/Linux/BSD, needs
|
||||
Python + PySide6) and a CI workflow that builds native Windows `.exe`/installer
|
||||
and macOS `.dmg` on real runners.
|
||||
|
||||
> **F-16 (data loss):** archives created by **≤ 3.8.0** at `-l 8`/`-l 9`
|
||||
> whose inputs included x86/ELF/PE executables may be **undecodable by any
|
||||
> version** (write-time defect in the old in-tree BCJ encoder). Re-create
|
||||
> such archives with 4.2.1 and verify extraction before deleting source
|
||||
> such archives with 5.0.0 and verify extraction before deleting source
|
||||
> data. Details in [CHANGELOG.md](CHANGELOG.md).
|
||||
|
||||
Binaries for the CLI (4.2.1) and GUI (1.3.0) are on the
|
||||
[release page](https://git.securityops.co/cristiancmoises/vaptvupt/releases/tag/v4.2.1).
|
||||
Binaries for the CLI (5.0.0) and GUI (1.3.0) are on the
|
||||
[release page](https://git.securityops.co/cristiancmoises/vaptvupt/releases/tag/v5.0.0).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -129,18 +125,18 @@ Argon2id KDF.
|
|||
### Pre-built packages
|
||||
|
||||
Assets are published on the
|
||||
[v4.2.1 release page](https://git.securityops.co/cristiancmoises/vaptvupt/releases/tag/v4.2.1)
|
||||
[v5.0.0 release page](https://git.securityops.co/cristiancmoises/vaptvupt/releases/tag/v5.0.0)
|
||||
and verifiable against the published `SHA256SUMS.txt`.
|
||||
|
||||
**Command-line tool (`vaptvupt` 4.2.1):**
|
||||
**Command-line tool (`vaptvupt` 5.0.0):**
|
||||
|
||||
| Format | File | Distros |
|
||||
|---|---|---|
|
||||
| Debian/Ubuntu | `vaptvupt_4.2.1_amd64.deb` | Debian 11+, Ubuntu 22.04+, Mint 21+ |
|
||||
| RPM | `vaptvupt-4.2.1-1.x86_64.rpm` | Fedora 38+, RHEL 9+, openSUSE, AlmaLinux, Rocky, other RPM-based distributions |
|
||||
| AppDir tarball | `vaptvupt-4.2.1-x86_64.AppDir.tar.gz` | Any glibc 2.28+ (extract & run, no FUSE) |
|
||||
| Source tarball | `vaptvupt-4.2.1.tar.gz` | Build from source on any platform |
|
||||
| openSUSE OBS | `vaptvupt-4.2.1-opensuse-obs.tar.gz` | Open Build Service source bundle |
|
||||
| Debian/Ubuntu | `vaptvupt_5.0.0_amd64.deb` | Debian 11+, Ubuntu 22.04+, Mint 21+ |
|
||||
| RPM | `vaptvupt-5.0.0-1.x86_64.rpm` | Fedora 38+, RHEL 9+, openSUSE, AlmaLinux, Rocky, other RPM-based distributions |
|
||||
| AppDir tarball | `vaptvupt-5.0.0-x86_64.AppDir.tar.gz` | Any glibc 2.28+ (extract & run, no FUSE) |
|
||||
| Source tarball | `vaptvupt-5.0.0.tar.gz` | Build from source on any platform |
|
||||
| openSUSE OBS | `vaptvupt-5.0.0-opensuse-obs.tar.gz` | Open Build Service source bundle |
|
||||
|
||||
**Graphical front-end (`vaptvupt-gui` 1.3.0):**
|
||||
|
||||
|
|
@ -151,22 +147,36 @@ and verifiable against the published `SHA256SUMS.txt`.
|
|||
| AppImage | `VaptVupt-GUI-1.3.0-x86_64.AppImage` | Any glibc 2.28+ (single-file, no install) |
|
||||
| AppDir tarball | `VaptVupt-GUI-1.3.0-x86_64.AppDir.tar.gz` | Any glibc 2.28+ (extract & run) |
|
||||
|
||||
**Windows / macOS / BSD:**
|
||||
|
||||
| Platform | File | Notes |
|
||||
|---|---|---|
|
||||
| Windows | `VaptVupt-Setup-5.0.0.exe`, `vaptvupt-gui-5.0.0-windows-x86_64.exe`, `vaptvupt-5.0.0-windows-x86_64.exe` | Native installer + standalone GUI + CLI, built on a Windows runner by CI |
|
||||
| macOS | `VaptVupt-5.0.0.dmg`, `vaptvupt-5.0.0-macos` | `.dmg` GUI bundle + CLI, built on a macOS runner by CI |
|
||||
| Any OS (portable GUI) | `vaptvupt-gui-5.0.0-portable.zip` | Python GUI + launchers for Windows/macOS/Linux/BSD; needs Python 3.8+ and PySide6 (or PyQt6), plus the `vaptvupt` CLI on PATH |
|
||||
| BSD / others | `vaptvupt-5.0.0.tar.gz` | Build the CLI from source (`make`); run the portable GUI |
|
||||
|
||||
The native Windows/macOS installers are produced by the project's CI
|
||||
(`.github/workflows/cross-platform.yml`) on real Windows and macOS runners — see
|
||||
the GitHub release. The portable GUI package runs the same GUI everywhere Python
|
||||
and Qt are available.
|
||||
|
||||
```bash
|
||||
# Verify downloads first
|
||||
sha256sum -c SHA256SUMS.txt
|
||||
|
||||
# Debian / Ubuntu / Mint
|
||||
sudo dpkg -i vaptvupt_4.2.1_amd64.deb
|
||||
sudo dpkg -i vaptvupt_5.0.0_amd64.deb
|
||||
sudo apt-get install -f # resolve any missing deps
|
||||
|
||||
# Fedora / RHEL / openSUSE / AlmaLinux / Rocky and other RPM-based distros
|
||||
sudo rpm -i vaptvupt-4.2.1-1.x86_64.rpm
|
||||
sudo rpm -i vaptvupt-5.0.0-1.x86_64.rpm
|
||||
# or
|
||||
sudo dnf install ./vaptvupt-4.2.1-1.x86_64.rpm
|
||||
sudo dnf install ./vaptvupt-5.0.0-1.x86_64.rpm
|
||||
|
||||
# AppDir tarball (no install, no FUSE required)
|
||||
tar xzf vaptvupt-4.2.1-x86_64.AppDir.tar.gz
|
||||
./vaptvupt-4.2.1-x86_64.AppDir/AppRun --help
|
||||
tar xzf vaptvupt-5.0.0-x86_64.AppDir.tar.gz
|
||||
./vaptvupt-5.0.0-x86_64.AppDir/AppRun --help
|
||||
|
||||
# GUI AppImage (single executable)
|
||||
chmod +x VaptVupt-GUI-1.3.0-x86_64.AppImage
|
||||
|
|
@ -176,10 +186,10 @@ chmod +x VaptVupt-GUI-1.3.0-x86_64.AppImage
|
|||
### Building from SRPM (Fedora / RHEL / RPM-based distributions)
|
||||
|
||||
```bash
|
||||
tar xzf vaptvupt-4.2.1.srpm.tar.gz
|
||||
tar xzf vaptvupt-5.0.0.srpm.tar.gz
|
||||
cd ~/rpmbuild # or use rpmbuild --define "_topdir $(pwd)"
|
||||
rpmbuild -bb SPECS/vaptvupt.spec
|
||||
sudo rpm -i RPMS/x86_64/vaptvupt-4.2.1-1.*.rpm
|
||||
sudo rpm -i RPMS/x86_64/vaptvupt-5.0.0-1.*.rpm
|
||||
```
|
||||
|
||||
### Basic usage
|
||||
|
|
@ -680,6 +690,7 @@ VaptVupt archives require VaptVupt v2.0+.
|
|||
| v4.1.0 | Source-only tree (prebuilt libzuptsdk/libpqvaptvupt removed); default build needs only a C compiler + make; native `--pq` is the default PQ mode; `--pq-sdk`/`--pq-box`/Argon2id gated behind `make WITH_SDK=1`. Wire format stays v1.6 |
|
||||
| v4.2.0 | Full (pure) post-quantum mode `--pq-only` (ML-KEM-768 only, envelope 0x06); critical fix for AES-CTR keystream reuse under `--dedup` (fresh random per-block nonce); clearer SDK keygen guidance. Wire format stays v1.6 |
|
||||
| v4.2.1 | `vaptvupt info` now reports the real post-quantum mode (`--pq-only` no longer mislabelled as hybrid); reader-side only, no wire-format change |
|
||||
| v5.0.0 | Genuine FIPS 203 ML-KEM-768 (validated vs OpenSSL); CLI data-loss/plaintext guards; AVX2 decoder OOB-read fix; GUI reworked for native PQ modes; cross-platform packaging. **Breaking:** `--pq`/`--pq-only` keys+archives from ≤4.2.1 do not decrypt |
|
||||
|
||||
See [CHANGELOG.md](CHANGELOG.md) for detailed per-version changes.
|
||||
|
||||
|
|
|
|||
12
SECURITY.md
12
SECURITY.md
|
|
@ -1,4 +1,4 @@
|
|||
# Security Policy — VaptVupt 4.2.1
|
||||
# Security Policy — VaptVupt 5.0.0
|
||||
|
||||
## Reporting Vulnerabilities
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ build and are not defaults.
|
|||
| Authentication | HMAC-SHA256 | RFC 2104 | 256-bit | 128-bit post-quantum (Grover) |
|
||||
| Password KDF (default) | PBKDF2-SHA256 | RFC 8018 | 600K iterations | Password-dependent |
|
||||
| Password KDF (WITH_SDK=1 option) | Argon2id | RFC 9106 | OWASP minimums | Password-dependent, memory-hard |
|
||||
| Post-quantum KEM | ML-KEM-768 | FIPS 203 | 1184B pk / 2400B sk | NIST Level 3 |
|
||||
| Post-quantum KEM | ML-KEM-768 | FIPS 203 (validated vs OpenSSL 3.5) | 1184B ek / 2400B dk | NIST Level 3 |
|
||||
| Classical KEM | X25519 | RFC 7748 | 32B scalar | ~128-bit classical |
|
||||
| Hybrid KDF (`--pq`) | SHA3-512 | FIPS 202 | 512-bit output | Secure if either KEM holds |
|
||||
| PQ-only KDF (`--pq-only`) | SHA3-512 | FIPS 202 | 512-bit output | Secure if ML-KEM-768 holds (no classical fallback) |
|
||||
|
|
@ -112,6 +112,14 @@ decryption. This prevents:
|
|||
|
||||
### Hybrid Post-Quantum KEM (`--pq`)
|
||||
|
||||
> **FIPS 203 conformance (v5.0.0).** The ML-KEM-768 implementation is validated
|
||||
> byte-for-byte against OpenSSL 3.5's FIPS 203 ML-KEM-768: deterministic keygen
|
||||
> produces an identical `ek`, and the shared secret agrees in both
|
||||
> cross-decapsulation directions (our encaps ↔ OpenSSL decaps, and vice-versa).
|
||||
> This is checked on every `make check` by `tests/test_mlkem_fips203.sh`.
|
||||
> Releases ≤ 4.2.1 used round-3 CRYSTALS-Kyber (secure, but not interoperable);
|
||||
> 5.0.0's `--pq`/`--pq-only` archives are therefore not backward-compatible.
|
||||
|
||||
```
|
||||
Encapsulation:
|
||||
ML-KEM-768.Encaps(pk) → ml_ct[1088], ml_ss[32]
|
||||
|
|
|
|||
|
|
@ -108,10 +108,10 @@ machines under the user's own control.
|
|||
|
||||
### 5. Post-quantum forward secrecy (`--pq`, `--pq-only`, and optional `--pq-sdk`)
|
||||
|
||||
The native `--pq` mode uses ML-KEM-768 (FIPS 203) hybridized with
|
||||
X25519 via an HKDF combiner. Archives encrypted today cannot be
|
||||
decrypted by a future quantum adversary holding only the ciphertext,
|
||||
assuming:
|
||||
The native `--pq` mode uses ML-KEM-768 (FIPS 203 — validated byte-for-byte
|
||||
against OpenSSL 3.5's ML-KEM-768; see AUDIT.md) hybridized with X25519 via an
|
||||
HKDF combiner. Archives encrypted today cannot be decrypted by a future quantum
|
||||
adversary holding only the ciphertext, assuming:
|
||||
|
||||
- ML-KEM-768 retains its claimed security level (NIST Category 3,
|
||||
192-bit classical / 96-bit quantum strength)
|
||||
|
|
@ -323,6 +323,6 @@ normally.
|
|||
## Document version
|
||||
|
||||
This threat model covers archive format v1.6 as shipped in VaptVupt
|
||||
4.2.1. It is part of the source tree (`THREAT_MODEL.md`) and
|
||||
5.0.0. It is part of the source tree (`THREAT_MODEL.md`) and
|
||||
versioned with the project; this section will be updated as the
|
||||
format evolves.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
.\" Manpage for vaptvupt (formerly zupt; INPI Brasil trademark rename in v3.0.0)
|
||||
.\" SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
.\" Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
.TH VAPTVUPT 1 "July 2026" "vaptvupt 4.2.1" "User Commands"
|
||||
.TH VAPTVUPT 1 "July 2026" "vaptvupt 5.0.0" "User Commands"
|
||||
|
||||
.SH NAME
|
||||
vaptvupt \- post-quantum backup compression utility (formerly zupt)
|
||||
|
|
@ -72,7 +72,7 @@ every byte of the archive — header, footer, per-block metadata, comments — i
|
|||
.PP
|
||||
The compression layer is the
|
||||
.B VaptVupt LZ + ANS
|
||||
codec (version 2.53.3), which prioritises decode speed and ratio over
|
||||
codec (version 2.60.4), which prioritises decode speed and ratio over
|
||||
encode speed. Aggregate decode throughput on this build is 1.27\(mu
|
||||
zstd\-3; encode throughput is 0.2\(mu\(en0.5\(mu zstd\-3 depending on
|
||||
content. See
|
||||
|
|
@ -239,9 +239,10 @@ Read password from file descriptor N.
|
|||
|
||||
.TP
|
||||
.B --kdf \fIalgo\fR
|
||||
Choose key-derivation function for password mode:
|
||||
.BR argon2id " (default since v2.4.1; memory-hard) or "
|
||||
.BR pbkdf2 " (SHA-256, 600 000 iter; needed for compatibility with v2.4.0 and earlier readers)."
|
||||
Choose key-derivation function for password mode. In the default source-only
|
||||
build the only KDF is
|
||||
.BR pbkdf2 " (SHA-256, 600 000 iter), which is the default;"
|
||||
.BR argon2id " (memory-hard) is available only in a " WITH_SDK=1 " build."
|
||||
|
||||
.TP
|
||||
.B --keyfile \fIpath\fR
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ The GUI calls the vaptvupt CLI binary — all cryptography runs in native C, not
|
|||
|
||||
## Credits
|
||||
|
||||
- **vaptvupt** v4.2.1 — Cristian Cezar Moisés ([github](https://git.securityops.co/cristiancmoises/vaptvupt))
|
||||
- **vaptvupt** v5.0.0 — Cristian Cezar Moisés ([github](https://git.securityops.co/cristiancmoises/vaptvupt))
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
|||
|
|
@ -398,10 +398,14 @@ class PathField(QWidget):
|
|||
def scrollable(w):
|
||||
sa = QScrollArea(); sa.setWidgetResizable(True); sa.setWidget(w); sa.setFrameShape(QFrame.Shape.NoFrame); return sa
|
||||
|
||||
def run_async(parent, cmd, btn, log, progress=None):
|
||||
log.clear(); btn.setEnabled(False)
|
||||
def run_async(parent, cmd, btn, log, progress=None, info=None):
|
||||
log.clear()
|
||||
if info: # e.g. an auto-detect note; appended AFTER the clear so it survives
|
||||
log.append(info)
|
||||
btn.setEnabled(False)
|
||||
if progress: progress.show()
|
||||
t = QThread(); w = Worker(cmd); w.moveToThread(t)
|
||||
# log.append targets a main-thread QObject -> Qt queues it to the GUI thread.
|
||||
w.log.connect(log.append)
|
||||
# Keep a LIST of live (thread, worker) refs on the parent. Tabs with more
|
||||
# than one action button (Disk: backup + restore) previously shared a
|
||||
|
|
@ -417,7 +421,10 @@ def run_async(parent, cmd, btn, log, progress=None):
|
|||
log.append("\nDone." if code == 0 else f"\nFailed (exit {code}).")
|
||||
t.quit()
|
||||
parent._jobs = [(th, wk) for (th, wk) in parent._jobs if th is not t]
|
||||
w.done.connect(finish)
|
||||
# `done` is emitted from the worker thread and `finish` touches GUI widgets;
|
||||
# a bare functor would connect DirectConnection and run OFF the GUI thread.
|
||||
# QueuedConnection marshals it onto the GUI event loop.
|
||||
w.done.connect(finish, Qt.ConnectionType.QueuedConnection)
|
||||
t.started.connect(w.run); t.start()
|
||||
|
||||
# ── Tabs ──
|
||||
|
|
@ -621,6 +628,7 @@ class ExtractTab(QWidget):
|
|||
arc = self.arc.path()
|
||||
if not arc: QMessageBox.warning(self, "VaptVupt", "Select an archive."); return
|
||||
cmd = ["extract"]
|
||||
info = None
|
||||
if self.out.path(): cmd += ["-o", self.out.path()]
|
||||
if self.pw.text(): cmd += ["-p", self.pw.text()]
|
||||
if self.pq.path():
|
||||
|
|
@ -629,11 +637,11 @@ class ExtractTab(QWidget):
|
|||
# The private-key format must match how the archive was encrypted;
|
||||
# inspect the header (vaptvupt info) to choose the right flag.
|
||||
tok = _detect_archive_pq(arc) or "pq"
|
||||
self.log.append(f"[auto-detect] using {_PQ_FLAG[tok][1]}")
|
||||
info = f"[auto-detect] using {_PQ_FLAG[tok][1]}"
|
||||
_, flag = _PQ_FLAG[tok]
|
||||
cmd += [flag, self.pq.path()]
|
||||
cmd.append(arc)
|
||||
run_async(self, cmd, self.btn, self.log, self.progress)
|
||||
run_async(self, cmd, self.btn, self.log, self.progress, info=info)
|
||||
|
||||
|
||||
class VerifyTab(QWidget):
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
#define ZUPT_PRODUCT_EXTENSION ".zupt" /* on-disk archive extension (kept stable) */
|
||||
#define ZUPT_PRODUCT_TAGLINE "Post-quantum backup compression"
|
||||
|
||||
#define ZUPT_VERSION_STRING "4.2.1"
|
||||
#define ZUPT_VERSION_STRING "5.0.0"
|
||||
/* Vendored codec release (upstream tag) — single source for display strings.
|
||||
* The codec's own VV_VERSION_* is its internal API version, not the release. */
|
||||
#define ZUPT_CODEC_RELEASE "2.60.4"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
# zupt-VERSION.tar.gz alongside the PKGBUILD.
|
||||
|
||||
pkgname=vaptvupt
|
||||
pkgver=4.2.1
|
||||
pkgver=5.0.0
|
||||
pkgrel=1
|
||||
provides=('zupt')
|
||||
replaces=('zupt')
|
||||
|
|
|
|||
|
|
@ -1,3 +1,25 @@
|
|||
vaptvupt (5.0.0-1) UNRELEASED; urgency=high
|
||||
|
||||
* ML-KEM-768 is now genuinely FIPS 203-conformant. Earlier releases shipped
|
||||
round-3 CRYSTALS-Kyber under a "FIPS 203" label; it was secure but not
|
||||
interoperable. Fixed a transposed matrix-A sampling convention (keygen +
|
||||
encrypt), the round-3 KDF, and the implicit-rejection domain. Validated
|
||||
byte-for-byte against OpenSSL 3.5's FIPS 203 ML-KEM-768 in both
|
||||
cross-decapsulation directions (tests/test_mlkem_fips203.sh, in make check).
|
||||
* BREAKING: --pq / --pq-only keys and archives from <= 4.2.1 no longer decrypt
|
||||
(the KEM math changed). Regenerate keys and re-encrypt. Password mode and
|
||||
plain compression are unaffected; wire format stays v1.6.
|
||||
* Security: compress -p <archive> <files> data-loss guard (was overwriting an
|
||||
input file); compress <archive> <dir> -p <pw> silent-plaintext guard; heap
|
||||
OOB read in the AVX2 decoder fast path bounded; overflow-safe bound in the
|
||||
solid-mode test path; secret-wipe on hybrid-decrypt key-read error.
|
||||
* GUI reworked for the source-only build: build-aware Hybrid/Full-PQ selector
|
||||
(no more SDK-mode defaults that fail), PQ-key auto-detect on Extract/Verify,
|
||||
thread-safety + About fixes.
|
||||
* Truthful banner/help (real default KDF); cross-platform packaging.
|
||||
|
||||
-- Cristian Cezar Moisés <sac@securityops.co> Fri, 10 Jul 2026 18:00:00 +0000
|
||||
|
||||
vaptvupt (4.2.1-1) UNRELEASED; urgency=medium
|
||||
|
||||
* Fix: `vaptvupt info` mislabelled full post-quantum (--pq-only, enc_type
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ Description: Post-quantum backup compression utility (formerly Zupt)
|
|||
* Post-quantum hybrid encryption (ML-KEM-768 + X25519, FIPS 203)
|
||||
* AES-256-CTR + HMAC-SHA256 authenticated encryption (Encrypt-then-MAC)
|
||||
* PBKDF2-SHA256 password key derivation (Argon2id in WITH_SDK=1 builds)
|
||||
* Multi-threaded compression with the VaptVupt LZ + ANS codec 2.48.5
|
||||
* Multi-threaded compression with the VaptVupt LZ + ANS codec 2.60.4
|
||||
* Full-disk backup and restore with sparse-region detection
|
||||
* End-to-end byte-level tamper detection on encrypted archives
|
||||
(0 silent-accept positions in the v1.6 exhaustive byte sweep)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: zupt
|
||||
Upstream-Contact: Cristian Cezar Moisés <sac@securityops.co>
|
||||
Source: https://git.securityops.co/cristiancmoises/zupt
|
||||
Source: https://git.securityops.co/cristiancmoises/vaptvupt
|
||||
|
||||
Files: *
|
||||
Copyright: 2025-2026 Cristian Cezar Moisés
|
||||
|
|
|
|||
|
|
@ -17,14 +17,13 @@
|
|||
# Notes for macOS:
|
||||
# * Jasmin assembly is disabled at build time on Darwin (no jasminc dep);
|
||||
# the C fallback for AES-256-CTR / HMAC compare paths is shipped.
|
||||
# * libzuptsdk is vendored and installed alongside the binary; the binary
|
||||
# uses @loader_path rpath so users don't have to set DYLD paths.
|
||||
# * Source-only build: no vendored libraries; native crypto only.
|
||||
|
||||
class Vaptvupt < Formula
|
||||
desc "Post-quantum backup compression utility (ML-KEM-768 + AES-256-CTR + HMAC-SHA256)"
|
||||
homepage "https://git.securityops.co/cristiancmoises/vaptvupt"
|
||||
url "https://git.securityops.co/cristiancmoises/vaptvupt/releases/download/v4.2.1/vaptvupt-4.2.1.tar.gz"
|
||||
version "4.2.1"
|
||||
url "https://git.securityops.co/cristiancmoises/vaptvupt/releases/download/v5.0.0/vaptvupt-5.0.0.tar.gz"
|
||||
version "5.0.0"
|
||||
sha256 "REPLACE_WITH_SHA256_OF_RELEASE_TARBALL"
|
||||
license "AGPL-3.0-or-later"
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
zupt = pkgs.stdenv.mkDerivation {
|
||||
pname = "vaptvupt";
|
||||
version = "4.2.1";
|
||||
version = "5.0.0";
|
||||
|
||||
# When publishing, replace this with `fetchurl` against the
|
||||
# release tarball. For local development the flake assumes it
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
# openSUSE Build Service update for `home:cabelo:innovators/vaptvupt`
|
||||
|
||||
This directory contains the three files needed to build vaptvupt `4.2.1`
|
||||
This directory contains the three files needed to build vaptvupt `5.0.0`
|
||||
in OBS:
|
||||
|
||||
| File | Purpose |
|
||||
|---------------|-------------------------------------------------------------------------|
|
||||
| `_service` | `revision` pinned to `v4.2.1`. Format unchanged (still `tar_scm`). |
|
||||
| `vaptvupt.spec` | `Version: 4.2.1`. `License: AGPL-3.0-or-later`. `%check` calls `make check`. |
|
||||
| `_service` | `revision` pinned to `v5.0.0`. Format unchanged (still `tar_scm`). |
|
||||
| `vaptvupt.spec` | `Version: 5.0.0`. `License: AGPL-3.0-or-later`. `%check` calls `make check`. |
|
||||
| `vaptvupt.changes`| Changelog for the 4.x series. Older history preserved verbatim. |
|
||||
|
||||
## Spec notes
|
||||
|
|
@ -58,22 +58,22 @@ cp /path/to/vaptvupt-source/packaging/opensuse/_service .
|
|||
cp /path/to/vaptvupt-source/packaging/opensuse/vaptvupt.spec .
|
||||
cp /path/to/vaptvupt-source/packaging/opensuse/vaptvupt.changes .
|
||||
|
||||
# 3. Trigger the service locally to fetch v4.2.1 from GitHub
|
||||
# 3. Trigger the service locally to fetch v5.0.0 from GitHub
|
||||
osc service runall
|
||||
# Produces vaptvupt-4.2.1.tar.gz in the current directory.
|
||||
# Produces vaptvupt-5.0.0.tar.gz in the current directory.
|
||||
|
||||
# 4. (Optional) Local build to verify before committing
|
||||
osc build openSUSE_Tumbleweed x86_64
|
||||
|
||||
# 5. Commit upstream
|
||||
osc status # confirm vaptvupt-4.2.1.tar.gz is staged alongside the
|
||||
osc status # confirm vaptvupt-5.0.0.tar.gz is staged alongside the
|
||||
# three text files
|
||||
osc commit -m "Update to 4.2.1"
|
||||
osc commit -m "Update to 5.0.0"
|
||||
```
|
||||
|
||||
## Notes for future updates
|
||||
|
||||
* The `_service` `revision` is pinned to `v4.2.1`. To track a new
|
||||
* The `_service` `revision` is pinned to `v5.0.0`. To track a new
|
||||
release, edit that one line and re-run `osc service runall`.
|
||||
* The spec's `Version:` field is hard-coded — when you bump `_service`
|
||||
`revision`, also bump `Version:` to match.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<service name="tar_scm" mode="manual">
|
||||
<param name="url">https://github.com/cristiancmoises/vaptvupt</param>
|
||||
<param name="scm">git</param>
|
||||
<param name="revision">v4.2.1</param>
|
||||
<param name="revision">v5.0.0</param>
|
||||
<param name="versionformat">@PARENT_TAG@</param>
|
||||
<param name="versionrewrite-pattern">v(.*)</param>
|
||||
<param name="submodules">enable</param>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,21 @@
|
|||
-------------------------------------------------------------------
|
||||
Fri Jul 10 18:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
|
||||
|
||||
- Update to 5.0.0:
|
||||
* ML-KEM-768 is now genuinely FIPS 203-conformant (was round-3
|
||||
CRYSTALS-Kyber): fixed a transposed matrix-A sampling convention,
|
||||
the round-3 KDF, and the implicit-rejection domain. Validated
|
||||
byte-for-byte against OpenSSL 3.5's FIPS 203 ML-KEM-768
|
||||
(tests/test_mlkem_fips203.sh, run in %check).
|
||||
* BREAKING: --pq / --pq-only keys and archives from <= 4.2.1 no
|
||||
longer decrypt (the KEM math changed). Regenerate keys and
|
||||
re-encrypt. Password mode and plain compression are unaffected;
|
||||
wire format stays v1.6.
|
||||
* Security: compress data-loss and silent-plaintext guards; heap
|
||||
OOB read in the AVX2 decoder bounded; overflow-safe solid-mode
|
||||
test path; secret-wipe on hybrid-decrypt key-read error.
|
||||
* GUI reworked for the source-only build; truthful banner/help.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Fri Jul 10 12:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
|
||||
Name: vaptvupt
|
||||
Version: 4.2.1
|
||||
Version: 5.0.0
|
||||
Release: 0
|
||||
Summary: Post-quantum backup compression with AES-256 + ML-KEM-768 hybrid encryption
|
||||
License: AGPL-3.0-or-later
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
# in the base.
|
||||
|
||||
Name: vaptvupt
|
||||
Version: 4.2.1
|
||||
Version: 5.0.0
|
||||
Release: 1%{?dist}
|
||||
Summary: Post-quantum backup compression utility (AES-256 + ML-KEM-768 + Argon2id, formerly Zupt)
|
||||
|
||||
|
|
@ -48,10 +48,12 @@ Requires: glibc
|
|||
%description
|
||||
Zupt is a pure-C11 backup compression utility featuring:
|
||||
|
||||
* Post-quantum hybrid encryption (ML-KEM-768 + X25519, FIPS 203)
|
||||
* Post-quantum hybrid encryption (ML-KEM-768 + X25519, FIPS 203,
|
||||
validated byte-for-byte against OpenSSL's ML-KEM-768) and full
|
||||
pure ML-KEM-768 (--pq-only)
|
||||
* AES-256-CTR + HMAC-SHA256 authenticated encryption (Encrypt-then-MAC)
|
||||
* Argon2id password-based key derivation (default since 2.4.1)
|
||||
* Multi-threaded compression with the VaptVupt LZ codec
|
||||
* PBKDF2-SHA256 password key derivation (Argon2id in WITH_SDK=1 builds)
|
||||
* Multi-threaded compression with the VaptVupt LZ + ANS codec
|
||||
* Full-disk backup and restore with sparse-region detection
|
||||
* End-to-end byte-level tamper detection on encrypted archives
|
||||
(0 silent-accept positions in the v1.6 exhaustive byte sweep)
|
||||
|
|
@ -107,6 +109,17 @@ and optional encrypted comments.
|
|||
%endif
|
||||
|
||||
%changelog
|
||||
* Fri Jul 10 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.0.0-1
|
||||
- ML-KEM-768 is now genuinely FIPS 203-conformant (was round-3 CRYSTALS-Kyber):
|
||||
fixed a transposed matrix-A sampling convention, the round-3 KDF, and the
|
||||
implicit-rejection domain. Validated byte-for-byte against OpenSSL 3.5's
|
||||
FIPS 203 ML-KEM-768 (tests/test_mlkem_fips203.sh, run in %%check).
|
||||
- BREAKING: --pq / --pq-only keys and archives from <= 4.2.1 no longer decrypt.
|
||||
Regenerate keys and re-encrypt. Password mode / plain compression unaffected.
|
||||
- Security: compress data-loss + silent-plaintext guards; AVX2 decoder heap
|
||||
OOB-read bound; overflow-safe solid-mode test path; secret-wipe on error.
|
||||
- GUI reworked for the source-only build; truthful banner/help.
|
||||
|
||||
* Fri Jul 10 2026 Cristian Cezar Moisés <sac@securityops.co> - 4.2.1-1
|
||||
- Fix: "vaptvupt info" mislabelled full post-quantum (--pq-only) archives as
|
||||
"PQ Hybrid (ML-KEM-768 + X25519)". info now reads the real enc_type from the
|
||||
|
|
|
|||
|
|
@ -2797,7 +2797,11 @@ zupt_error_t zupt_test_archive(const char *arc, zupt_options_t *opts) {
|
|||
uint64_t sz = e->uncompressed_size;
|
||||
int fok = 1;
|
||||
|
||||
if (off + sz > total_size) {
|
||||
/* Overflow-safe bound: off+sz can wrap (both are attacker-controlled
|
||||
* index fields), so `off + sz > total_size` could pass falsely and
|
||||
* feed a wild pointer / oversized length to zupt_xxh64. Match the
|
||||
* hardened extract path. */
|
||||
if (off > (uint64_t)total_size || sz > (uint64_t)total_size - off) {
|
||||
fok = 0;
|
||||
} else if (sz > 0) {
|
||||
uint64_t ck = zupt_xxh64(solid_buf + off, (size_t)sz, 0);
|
||||
|
|
|
|||
Loading…
Reference in a new issue