From 4874010d0eb4ec07e048d75092b5f9118c8b05d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristian=20Cezar=20Mois=C3=A9s?= Date: Tue, 7 Jul 2026 19:45:37 -0300 Subject: [PATCH] v4.1.0: source-only build, multithreaded-encryption fix, security hardening Build from source with no vendored binaries: - Remove prebuilt libzuptsdk.so / libpqvaptvupt.so (and a stray .pyc). The default build needs only a C compiler + make; it links no external library and installs no .so. The libzuptsdk-backed modes (Argon2id KDF, --pq-sdk, --pq-box) are gated behind an opt-in `make WITH_SDK=1`. The default password KDF is PBKDF2-SHA256 and --pq (native ML-KEM-768 + X25519) is the built-in PQ mode. openSUSE/RPM/deb/AUR/Homebrew/Nix recipes bumped to 4.1.0; the openSUSE spec now builds source-only (%files ships no .so, %build/%install WITH_SDK=0). Fix: multithreaded encrypted archives were unextractable on the native AEAD path. The parallel compress/decompress workers skipped the F-09 frame-preface AAD that the serial path and the archive's AAD_PREFACE flag bind into every block MAC, so each multithreaded block failed authentication. The workers now bind the preface via a shared serializer; output is byte-identical across thread counts and interoperates with single-threaded archives (also fixes `--kdf pbkdf2 -t N` in any build). Security hardening (crafted-archive memory safety + crypto): - LZH raw code-length stack overflow + huff_lut OOB write - overflow-safe bounds in parse_index and solid-mode extract (heap OOB read) - SEQ decoder safe-zone heap overflow (litlen+matchlen reserve) - require the per-block ENCRYPTED flag on encrypted archives (plaintext forgery) - cap archive-supplied PBKDF2 iteration count (KDF-amplification DoS) - non-elidable secret wipe in the SDK path; restored disk images created 0600 Docs: remove AUDIT.md / BENCHMARKS.md / ROADMAP.md; trim marketing/AI-styled text and correct KDF/PQ facts across README, SECURITY, INSTALL, DISTRIBUTION, THREAT_MODEL, THIRD-PARTY-NOTICES, the man page, and packaging READMEs. Wire format v1.6 unchanged. --- .gitignore | 7 +- AUDIT.md | 1630 ----------------- BENCHMARKS.md | 113 -- CHANGELOG.md | 45 + DISTRIBUTION.md | 130 +- INSTALL.md | 94 +- Makefile | 31 +- README.md | 567 +++--- ROADMAP.md | 90 - SECURITY.md | 331 ++-- THIRD-PARTY-NOTICES.md | 63 +- THREAT_MODEL.md | 188 +- doc/vaptvupt.1 | 12 +- gui/README.md | 7 +- include/zupt.h | 18 +- packaging/aur/PKGBUILD | 2 +- packaging/debian/changelog | 19 + packaging/homebrew/vaptvupt.rb | 4 +- packaging/nix/flake.nix | 2 +- packaging/opensuse/README.md | 118 +- packaging/opensuse/_service | 2 +- packaging/opensuse/vaptvupt.changes | 25 + packaging/opensuse/vaptvupt.spec | 43 +- packaging/rpm/vaptvupt.spec | 4 +- sdk/README.md | 8 +- .../__pycache__/zuptsdk.cpython-312.pyc | Bin 27397 -> 0 bytes src/vv_ans.c | 25 +- src/zupt_crypto_pqbox.c | 34 + src/zupt_crypto_sdk.c | 68 +- src/zupt_disk.c | 12 +- src/zupt_format.c | 45 +- src/zupt_lzh.c | 27 +- src/zupt_parallel.c | 55 +- vendor/pqvaptvupt/libpqvaptvupt.so | 1 - vendor/pqvaptvupt/libpqvaptvupt.so.0 | 1 - vendor/pqvaptvupt/libpqvaptvupt.so.0.6.0 | Bin 56504 -> 0 bytes vendor/zuptsdk/libzuptsdk.so | 1 - vendor/zuptsdk/libzuptsdk.so.2 | 1 - vendor/zuptsdk/libzuptsdk.so.2.0.0 | Bin 737552 -> 0 bytes 39 files changed, 1075 insertions(+), 2748 deletions(-) delete mode 100644 AUDIT.md delete mode 100644 BENCHMARKS.md delete mode 100644 ROADMAP.md delete mode 100644 sdk/bindings/python/__pycache__/zuptsdk.cpython-312.pyc delete mode 120000 vendor/pqvaptvupt/libpqvaptvupt.so delete mode 120000 vendor/pqvaptvupt/libpqvaptvupt.so.0 delete mode 100755 vendor/pqvaptvupt/libpqvaptvupt.so.0.6.0 delete mode 120000 vendor/zuptsdk/libzuptsdk.so delete mode 120000 vendor/zuptsdk/libzuptsdk.so.2 delete mode 100755 vendor/zuptsdk/libzuptsdk.so.2.0.0 diff --git a/.gitignore b/.gitignore index f1b35c1..24d970d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,13 +6,14 @@ *.so *.so.* *.dylib -# ...but keep vendored prebuilt libraries (shipped, no in-tree source) -!vendor/**/*.so -!vendor/**/*.so.* *.exe *.obj *.lib +# Python bytecode caches +__pycache__/ +*.pyc + # Coverage / profiling *.gcda *.gcno diff --git a/AUDIT.md b/AUDIT.md deleted file mode 100644 index 3a191ca..0000000 --- a/AUDIT.md +++ /dev/null @@ -1,1630 +0,0 @@ -# Security Audit — VaptVupt v4.0.0 - -**Date:** 2026-05-20 -**Author:** Cristian Cezar Moisés -**Audit type:** Self-audit with formal verification (Jasmin CT proofs, ACSL contracts) and NIST/RFC test vectors -**Status:** No independent third-party audit performed - -History: -- v2.0.0 — 2026-03-29 — initial audit baseline. -- v2.2.4 — 2026-05-19 — five findings (F-01..F-05) closed. -- v2.2.5 — 2026-05-19 — F-06 (high) and F-07 closed. -- v2.3.0 — 2026-05-20 — F-08 closed via AIT (format v1.4 → v1.5). -- v2.3.1 — 2026-05-20 — F-09 closed via preface-AAD MAC (format v1.5 → v1.6). - Exhaustive byte sweep on PQ-SDK archive: **0/1827 undetected**. -- v2.4.0 — 2026-05-20 — Methodology release. §3.5 byte-sweep mandate added. -- v2.4.1 — 2026-05-20 — F-10: KDF default flipped to Argon2id. -- v2.4.2 — 2026-05-20 — F-11 closed: verbal probe-oracle eliminated. -- v2.4.3 — 2026-05-20 — F-12: encrypted archive comments. Byte sweep on - 1878-byte PQ-SDK+comment archive: **0/1878 undetected**. -- v2.4.4 — 2026-05-20 — Distribution packaging + reproducible `make dist`. -- v2.4.5 — 2026-05-20 — Packaging completion: RPM, Nix flake, DISTRIBUTION.md. -- v2.4.6 — 2026-05-20 — CI rewrite + THREAT_MODEL.md. -- v2.4.7 — 2026-05-20 — Manpage refresh and shell completions (bash, zsh, - fish). Also corrected three stale banner strings that claimed PBKDF2 was - the default KDF despite the v2.4.1 flip to Argon2id (user-visible text - only; no behavioural change). No source changes that affect security - posture; audit posture unchanged from v2.4.3. -- v2.4.8 — 2026-05-24 — Distro-friendly release. New `make check` target - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - -- v3.0.0 — 2026-05-25 — MAJOR: Zupt → VaptVupt INPI Brasil trademark rename. - Codec upgrade to VaptVupt LZ + ANS 2.48.5 (two libFuzzer-found fixes: - csz==0 OOB-READ in vv_dstream_decompress_chunk; UBSan-safe pointer - arithmetic in vv_copy_match). GUI binary-discovery bug fixed with - liveness-checking _find_vaptvupt + discovery log. Enhanced manpage - (597 lines). Wire format unchanged at v1.6. Bidirectional v2.x ↔ v3.0.0 - archive compatibility verified. F-09 byte sweep still 0/1827 silent - accepts; F-06 HMAC fuzz still 0/2000. No new findings, no findings - reopened. Security posture unchanged. - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - -- v3.0.1 — 2026-05-26 — GUI license cleanup (removed MIT credit line; - gui/LICENSE-GUI rewritten AGPL-3.0-or-later with historical - correction note). GUI version-string parsing bug fix (the - v3.0.0 `replace("zupt ", "")` parser matched the wrong substring - inside the new rename parenthetical, causing garbled window - titles; fixed with anchored `_VERSION_RE`). New regression test - tests/test_gui_branding.sh (11 assertions) wired into make check - and make test. No source crypto changes; audit posture unchanged - from v3.0.0 (which itself preserved the v2.3.1 baseline). - Codec upgrade to VaptVupt LZ + ANS 2.48.5 (two libFuzzer-found fixes: - csz==0 OOB-READ in vv_dstream_decompress_chunk; UBSan-safe pointer - arithmetic in vv_copy_match). GUI binary-discovery bug fixed with - liveness-checking _find_vaptvupt + discovery log. Enhanced manpage - (597 lines). Wire format unchanged at v1.6. Bidirectional v2.x ↔ v3.0.0 - archive compatibility verified. F-09 byte sweep still 0/1827 silent - accepts; F-06 HMAC fuzz still 0/2000. No new findings, no findings - reopened. Security posture unchanged. - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - -- v3.0.2 — 2026-05-26 — F-13 closed: usage() string literal in - src/zupt_main.c exceeded C99's 4095-char ISO limit (was 4121). - Refactored to 5 fprintf sections; -Woverlength-strings added to - default CFLAGS so future regressions fail the build. Help text - drift cleanup (stale "zupt" examples → "vaptvupt", stale "LZ77 + - Huffman" → "VaptVupt LZ + ANS 2.48.5", license attribution). - New regression test tests/test_help_consistency.sh (10 assertions - including the F-13 byte-level guard). No source crypto changes; - audit posture unchanged from v3.0.1. - gui/LICENSE-GUI rewritten AGPL-3.0-or-later with historical - correction note). GUI version-string parsing bug fix (the - v3.0.0 `replace("zupt ", "")` parser matched the wrong substring - inside the new rename parenthetical, causing garbled window - titles; fixed with anchored `_VERSION_RE`). New regression test - tests/test_gui_branding.sh (11 assertions) wired into make check - and make test. No source crypto changes; audit posture unchanged - from v3.0.0 (which itself preserved the v2.3.1 baseline). - Codec upgrade to VaptVupt LZ + ANS 2.48.5 (two libFuzzer-found fixes: - csz==0 OOB-READ in vv_dstream_decompress_chunk; UBSan-safe pointer - arithmetic in vv_copy_match). GUI binary-discovery bug fixed with - liveness-checking _find_vaptvupt + discovery log. Enhanced manpage - (597 lines). Wire format unchanged at v1.6. Bidirectional v2.x ↔ v3.0.0 - archive compatibility verified. F-09 byte sweep still 0/1827 silent - accepts; F-06 HMAC fuzz still 0/2000. No new findings, no findings - reopened. Security posture unchanged. - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - -- v3.0.3 — 2026-05-26 — Static-analysis cleanup. cppcheck - knownConditionTrueFalse findings closed in varint decoders - (dead `&& (x&0x80)` AND-branch after preceding terminator-byte - early-return). -Wconversion / -Wsign-conversion findings closed - with explicit casts at two sites. Our 9-file non-vendored C now - compiles clean under the union of strict GCC warnings including - -Wconversion -Wsign-conversion -Werror. New regression test - tests/test_static_analysis.sh (7 assertions) wired into make - check and make test. Behaviour byte-identical; F-09 byte sweep - still 0/1827 silent accepts. No source crypto changes. - src/zupt_main.c exceeded C99's 4095-char ISO limit (was 4121). - Refactored to 5 fprintf sections; -Woverlength-strings added to - default CFLAGS so future regressions fail the build. Help text - drift cleanup (stale "zupt" examples → "vaptvupt", stale "LZ77 + - Huffman" → "VaptVupt LZ + ANS 2.48.5", license attribution). - New regression test tests/test_help_consistency.sh (10 assertions - including the F-13 byte-level guard). No source crypto changes; - audit posture unchanged from v3.0.1. - gui/LICENSE-GUI rewritten AGPL-3.0-or-later with historical - correction note). GUI version-string parsing bug fix (the - v3.0.0 `replace("zupt ", "")` parser matched the wrong substring - inside the new rename parenthetical, causing garbled window - titles; fixed with anchored `_VERSION_RE`). New regression test - tests/test_gui_branding.sh (11 assertions) wired into make check - and make test. No source crypto changes; audit posture unchanged - from v3.0.0 (which itself preserved the v2.3.1 baseline). - Codec upgrade to VaptVupt LZ + ANS 2.48.5 (two libFuzzer-found fixes: - csz==0 OOB-READ in vv_dstream_decompress_chunk; UBSan-safe pointer - arithmetic in vv_copy_match). GUI binary-discovery bug fixed with - liveness-checking _find_vaptvupt + discovery log. Enhanced manpage - (597 lines). Wire format unchanged at v1.6. Bidirectional v2.x ↔ v3.0.0 - archive compatibility verified. F-09 byte sweep still 0/1827 silent - accepts; F-06 HMAC fuzz still 0/2000. No new findings, no findings - reopened. Security posture unchanged. - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - -- v3.1.0 — 2026-05-31 — VaptVupt codec 2.48.5 -> 2.53.3 (API - byte-identical; 3 .c files changed). Inherits 6 upstream - corrupt-input decoder memory-safety fixes (v2.52.4 + v2.53.2). - F-14 closed: ASAN found a heap-buffer-overflow WRITE of size 32 - in OUR decode wrapper — decode buffers were malloc(uncompressed - _size) with no slack, but the codec AVX2 over-copy needs >=32 B - slack (documented contract in vaptvupt.h). Old codec never - reached it; new wider AVX2 hot path does. Fixed with shared - ZUPT_VV_DECODE_SLACK (64 B) guard on both single-threaded - (zupt_format.c) and parallel (zupt_parallel.c) decode paths. - Verified: ASAN 24/24 single-threaded + 15/15 multi-threaded; - bit-flip fuzz 300 trials 0 crashes / 300 clean rejects. F-09 - byte sweep still 0/1827. vv_decoder.c scalar build made - -Werror clean for aarch64/Termux. New test tests/test_vv_decode - _slack.sh. Wire format unchanged (v1.6); 3.0.3 archives extract - byte-exact. - knownConditionTrueFalse findings closed in varint decoders - (dead `&& (x&0x80)` AND-branch after preceding terminator-byte - early-return). -Wconversion / -Wsign-conversion findings closed - with explicit casts at two sites. Our 9-file non-vendored C now - compiles clean under the union of strict GCC warnings including - -Wconversion -Wsign-conversion -Werror. New regression test - tests/test_static_analysis.sh (7 assertions) wired into make - check and make test. Behaviour byte-identical; F-09 byte sweep - still 0/1827 silent accepts. No source crypto changes. - src/zupt_main.c exceeded C99's 4095-char ISO limit (was 4121). - Refactored to 5 fprintf sections; -Woverlength-strings added to - default CFLAGS so future regressions fail the build. Help text - drift cleanup (stale "zupt" examples → "vaptvupt", stale "LZ77 + - Huffman" → "VaptVupt LZ + ANS 2.48.5", license attribution). - New regression test tests/test_help_consistency.sh (10 assertions - including the F-13 byte-level guard). No source crypto changes; - audit posture unchanged from v3.0.1. - gui/LICENSE-GUI rewritten AGPL-3.0-or-later with historical - correction note). GUI version-string parsing bug fix (the - v3.0.0 `replace("zupt ", "")` parser matched the wrong substring - inside the new rename parenthetical, causing garbled window - titles; fixed with anchored `_VERSION_RE`). New regression test - tests/test_gui_branding.sh (11 assertions) wired into make check - and make test. No source crypto changes; audit posture unchanged - from v3.0.0 (which itself preserved the v2.3.1 baseline). - Codec upgrade to VaptVupt LZ + ANS 2.48.5 (two libFuzzer-found fixes: - csz==0 OOB-READ in vv_dstream_decompress_chunk; UBSan-safe pointer - arithmetic in vv_copy_match). GUI binary-discovery bug fixed with - liveness-checking _find_vaptvupt + discovery log. Enhanced manpage - (597 lines). Wire format unchanged at v1.6. Bidirectional v2.x ↔ v3.0.0 - archive compatibility verified. F-09 byte sweep still 0/1827 silent - accepts; F-06 HMAC fuzz still 0/2000. No new findings, no findings - reopened. Security posture unchanged. - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - -- v3.2.0 — 2026-06-01 — SHA-256 hardware acceleration (Intel - SHA-NI). New SHA256RNDS2/MSG1/MSG2 compression path with CPUID - runtime dispatch (has_shani, CPUID.07H:EBX[29]). Accelerates the - Encrypt-then-MAC second pass (HMAC-SHA256) and PBKDF2. - Security-relevant property: SHA-NI is constant-time by - construction (no data-dependent memory access or branches), - strengthening the side-channel posture of HMAC verification over - attacker-influenced ciphertext vs the scalar software path. No - cryptographic-correctness change: same SHA-256, same HMAC, same - Encrypt-then-MAC, same wire bytes (format v1.6 unchanged). NIST - FIPS 180-4 vectors pass on both paths; the 64 SHA-NI round - constants are verified bit-identical to the scalar K[] table. - Speedup ([ESTIMATED] 3-8x) NOT measured in this release — the - build host lacks SHA-NI; figure to be confirmed on SHA-NI - hardware. Scalar fallback unchanged and remains the path on - non-SHA-NI CPUs (incl. aarch64). F-09 byte sweep still 0/1827. - byte-identical; 3 .c files changed). Inherits 6 upstream - corrupt-input decoder memory-safety fixes (v2.52.4 + v2.53.2). - F-14 closed: ASAN found a heap-buffer-overflow WRITE of size 32 - in OUR decode wrapper — decode buffers were malloc(uncompressed - _size) with no slack, but the codec AVX2 over-copy needs >=32 B - slack (documented contract in vaptvupt.h). Old codec never - reached it; new wider AVX2 hot path does. Fixed with shared - ZUPT_VV_DECODE_SLACK (64 B) guard on both single-threaded - (zupt_format.c) and parallel (zupt_parallel.c) decode paths. - Verified: ASAN 24/24 single-threaded + 15/15 multi-threaded; - bit-flip fuzz 300 trials 0 crashes / 300 clean rejects. F-09 - byte sweep still 0/1827. vv_decoder.c scalar build made - -Werror clean for aarch64/Termux. New test tests/test_vv_decode - _slack.sh. Wire format unchanged (v1.6); 3.0.3 archives extract - byte-exact. - knownConditionTrueFalse findings closed in varint decoders - (dead `&& (x&0x80)` AND-branch after preceding terminator-byte - early-return). -Wconversion / -Wsign-conversion findings closed - with explicit casts at two sites. Our 9-file non-vendored C now - compiles clean under the union of strict GCC warnings including - -Wconversion -Wsign-conversion -Werror. New regression test - tests/test_static_analysis.sh (7 assertions) wired into make - check and make test. Behaviour byte-identical; F-09 byte sweep - still 0/1827 silent accepts. No source crypto changes. - src/zupt_main.c exceeded C99's 4095-char ISO limit (was 4121). - Refactored to 5 fprintf sections; -Woverlength-strings added to - default CFLAGS so future regressions fail the build. Help text - drift cleanup (stale "zupt" examples → "vaptvupt", stale "LZ77 + - Huffman" → "VaptVupt LZ + ANS 2.48.5", license attribution). - New regression test tests/test_help_consistency.sh (10 assertions - including the F-13 byte-level guard). No source crypto changes; - audit posture unchanged from v3.0.1. - gui/LICENSE-GUI rewritten AGPL-3.0-or-later with historical - correction note). GUI version-string parsing bug fix (the - v3.0.0 `replace("zupt ", "")` parser matched the wrong substring - inside the new rename parenthetical, causing garbled window - titles; fixed with anchored `_VERSION_RE`). New regression test - tests/test_gui_branding.sh (11 assertions) wired into make check - and make test. No source crypto changes; audit posture unchanged - from v3.0.0 (which itself preserved the v2.3.1 baseline). - Codec upgrade to VaptVupt LZ + ANS 2.48.5 (two libFuzzer-found fixes: - csz==0 OOB-READ in vv_dstream_decompress_chunk; UBSan-safe pointer - arithmetic in vv_copy_match). GUI binary-discovery bug fixed with - liveness-checking _find_vaptvupt + discovery log. Enhanced manpage - (597 lines). Wire format unchanged at v1.6. Bidirectional v2.x ↔ v3.0.0 - archive compatibility verified. F-09 byte sweep still 0/1827 silent - accepts; F-06 HMAC fuzz still 0/2000. No new findings, no findings - reopened. Security posture unchanged. - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - -- v3.3.0 — 2026-06-01 — Incremental HMAC-SHA256 for the per-block - Encrypt-then-MAC hot path. ipad/opad key prefix folded once per - keyring; MAC streamed (aad || nonce || ciphertext || seq) instead - of concatenated into a malloc'd buffer. SECURITY-RELEVANT: - identical authentication semantics — the MAC is byte-for-byte the - same (RFC 2104 + SHA-256 Merkle-Damgard associativity), proven by - RFC 4231 vectors, a new equivalence test, and byte-exact - decryption of 3.2.x archives. Constant-time tag compares unchanged - (byte-OR accumulator / Jasmin zupt_mac_verify_ct). F-09 byte sweep - 0/1827; F-06 1-bit HMAC fuzz 0/2000. Reduces secret-data heap - footprint: the old path copied the full ciphertext into a second - malloc'd buffer per block — now removed. No cryptographic- - correctness change, no wire-format change (v1.6). ASan clean on - both KDF paths. - SHA-NI). New SHA256RNDS2/MSG1/MSG2 compression path with CPUID - runtime dispatch (has_shani, CPUID.07H:EBX[29]). Accelerates the - Encrypt-then-MAC second pass (HMAC-SHA256) and PBKDF2. - Security-relevant property: SHA-NI is constant-time by - construction (no data-dependent memory access or branches), - strengthening the side-channel posture of HMAC verification over - attacker-influenced ciphertext vs the scalar software path. No - cryptographic-correctness change: same SHA-256, same HMAC, same - Encrypt-then-MAC, same wire bytes (format v1.6 unchanged). NIST - FIPS 180-4 vectors pass on both paths; the 64 SHA-NI round - constants are verified bit-identical to the scalar K[] table. - Speedup ([ESTIMATED] 3-8x) NOT measured in this release — the - build host lacks SHA-NI; figure to be confirmed on SHA-NI - hardware. Scalar fallback unchanged and remains the path on - non-SHA-NI CPUs (incl. aarch64). F-09 byte sweep still 0/1827. - byte-identical; 3 .c files changed). Inherits 6 upstream - corrupt-input decoder memory-safety fixes (v2.52.4 + v2.53.2). - F-14 closed: ASAN found a heap-buffer-overflow WRITE of size 32 - in OUR decode wrapper — decode buffers were malloc(uncompressed - _size) with no slack, but the codec AVX2 over-copy needs >=32 B - slack (documented contract in vaptvupt.h). Old codec never - reached it; new wider AVX2 hot path does. Fixed with shared - ZUPT_VV_DECODE_SLACK (64 B) guard on both single-threaded - (zupt_format.c) and parallel (zupt_parallel.c) decode paths. - Verified: ASAN 24/24 single-threaded + 15/15 multi-threaded; - bit-flip fuzz 300 trials 0 crashes / 300 clean rejects. F-09 - byte sweep still 0/1827. vv_decoder.c scalar build made - -Werror clean for aarch64/Termux. New test tests/test_vv_decode - _slack.sh. Wire format unchanged (v1.6); 3.0.3 archives extract - byte-exact. - knownConditionTrueFalse findings closed in varint decoders - (dead `&& (x&0x80)` AND-branch after preceding terminator-byte - early-return). -Wconversion / -Wsign-conversion findings closed - with explicit casts at two sites. Our 9-file non-vendored C now - compiles clean under the union of strict GCC warnings including - -Wconversion -Wsign-conversion -Werror. New regression test - tests/test_static_analysis.sh (7 assertions) wired into make - check and make test. Behaviour byte-identical; F-09 byte sweep - still 0/1827 silent accepts. No source crypto changes. - src/zupt_main.c exceeded C99's 4095-char ISO limit (was 4121). - Refactored to 5 fprintf sections; -Woverlength-strings added to - default CFLAGS so future regressions fail the build. Help text - drift cleanup (stale "zupt" examples → "vaptvupt", stale "LZ77 + - Huffman" → "VaptVupt LZ + ANS 2.48.5", license attribution). - New regression test tests/test_help_consistency.sh (10 assertions - including the F-13 byte-level guard). No source crypto changes; - audit posture unchanged from v3.0.1. - gui/LICENSE-GUI rewritten AGPL-3.0-or-later with historical - correction note). GUI version-string parsing bug fix (the - v3.0.0 `replace("zupt ", "")` parser matched the wrong substring - inside the new rename parenthetical, causing garbled window - titles; fixed with anchored `_VERSION_RE`). New regression test - tests/test_gui_branding.sh (11 assertions) wired into make check - and make test. No source crypto changes; audit posture unchanged - from v3.0.0 (which itself preserved the v2.3.1 baseline). - Codec upgrade to VaptVupt LZ + ANS 2.48.5 (two libFuzzer-found fixes: - csz==0 OOB-READ in vv_dstream_decompress_chunk; UBSan-safe pointer - arithmetic in vv_copy_match). GUI binary-discovery bug fixed with - liveness-checking _find_vaptvupt + discovery log. Enhanced manpage - (597 lines). Wire format unchanged at v1.6. Bidirectional v2.x ↔ v3.0.0 - archive compatibility verified. F-09 byte sweep still 0/1827 silent - accepts; F-06 HMAC fuzz still 0/2000. No new findings, no findings - reopened. Security posture unchanged. - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - -- v3.4.0 — 2026-06-01 — F-15: Argon2id KDF parameter transparency. - The 0x04 Argon2id enc-header recorded only [type|salt|nonce] and - nothing about the KDF cost (the PBKDF2 header records its iteration - count). New archives append a one-byte KDF profile descriptor at - offset 33 (ZUPT_ARGON2_PROFILE_MODERATE), making the header self- - describing so a reader always knows which Argon2id cost produced - the archive — eliminating a silent-undecryptability risk if the - preset ever changes. The descriptor is covered by the F-08 archive- - integrity trailer (tamper-evident; a flipped byte fails decryption, - verified). Additive + back-compatible: legacy 33-byte archives - decrypt byte-exact; 33B and 34B headers derive identical keys; - unknown profiles are refused fail-closed (no wrong-key guessing). - Build-time SDK-drift guard: the F-15 test asserts the KDF is - deterministic and meets a coarse memory-hard cost floor (>=20 ms), - failing the build if libzuptsdk is swapped for a weak/stub Argon2id. - No cryptographic-correctness change, no wire-format change (v1.6). - F-09 byte sweep 0/1827; F-06 1-bit HMAC fuzz 0/2000. Note: the - explicit RFC 9106 zsdk_argon2id() is header-declared but NOT - exported by the vendored libzuptsdk.so, so the cost is recorded in- - band rather than re-parameterised; revisit if the SDK exports it. - Encrypt-then-MAC hot path. ipad/opad key prefix folded once per - keyring; MAC streamed (aad || nonce || ciphertext || seq) instead - of concatenated into a malloc'd buffer. SECURITY-RELEVANT: - identical authentication semantics — the MAC is byte-for-byte the - same (RFC 2104 + SHA-256 Merkle-Damgard associativity), proven by - RFC 4231 vectors, a new equivalence test, and byte-exact - decryption of 3.2.x archives. Constant-time tag compares unchanged - (byte-OR accumulator / Jasmin zupt_mac_verify_ct). F-09 byte sweep - 0/1827; F-06 1-bit HMAC fuzz 0/2000. Reduces secret-data heap - footprint: the old path copied the full ciphertext into a second - malloc'd buffer per block — now removed. No cryptographic- - correctness change, no wire-format change (v1.6). ASan clean on - both KDF paths. - SHA-NI). New SHA256RNDS2/MSG1/MSG2 compression path with CPUID - runtime dispatch (has_shani, CPUID.07H:EBX[29]). Accelerates the - Encrypt-then-MAC second pass (HMAC-SHA256) and PBKDF2. - Security-relevant property: SHA-NI is constant-time by - construction (no data-dependent memory access or branches), - strengthening the side-channel posture of HMAC verification over - attacker-influenced ciphertext vs the scalar software path. No - cryptographic-correctness change: same SHA-256, same HMAC, same - Encrypt-then-MAC, same wire bytes (format v1.6 unchanged). NIST - FIPS 180-4 vectors pass on both paths; the 64 SHA-NI round - constants are verified bit-identical to the scalar K[] table. - Speedup ([ESTIMATED] 3-8x) NOT measured in this release — the - build host lacks SHA-NI; figure to be confirmed on SHA-NI - hardware. Scalar fallback unchanged and remains the path on - non-SHA-NI CPUs (incl. aarch64). F-09 byte sweep still 0/1827. - byte-identical; 3 .c files changed). Inherits 6 upstream - corrupt-input decoder memory-safety fixes (v2.52.4 + v2.53.2). - F-14 closed: ASAN found a heap-buffer-overflow WRITE of size 32 - in OUR decode wrapper — decode buffers were malloc(uncompressed - _size) with no slack, but the codec AVX2 over-copy needs >=32 B - slack (documented contract in vaptvupt.h). Old codec never - reached it; new wider AVX2 hot path does. Fixed with shared - ZUPT_VV_DECODE_SLACK (64 B) guard on both single-threaded - (zupt_format.c) and parallel (zupt_parallel.c) decode paths. - Verified: ASAN 24/24 single-threaded + 15/15 multi-threaded; - bit-flip fuzz 300 trials 0 crashes / 300 clean rejects. F-09 - byte sweep still 0/1827. vv_decoder.c scalar build made - -Werror clean for aarch64/Termux. New test tests/test_vv_decode - _slack.sh. Wire format unchanged (v1.6); 3.0.3 archives extract - byte-exact. - knownConditionTrueFalse findings closed in varint decoders - (dead `&& (x&0x80)` AND-branch after preceding terminator-byte - early-return). -Wconversion / -Wsign-conversion findings closed - with explicit casts at two sites. Our 9-file non-vendored C now - compiles clean under the union of strict GCC warnings including - -Wconversion -Wsign-conversion -Werror. New regression test - tests/test_static_analysis.sh (7 assertions) wired into make - check and make test. Behaviour byte-identical; F-09 byte sweep - still 0/1827 silent accepts. No source crypto changes. - src/zupt_main.c exceeded C99's 4095-char ISO limit (was 4121). - Refactored to 5 fprintf sections; -Woverlength-strings added to - default CFLAGS so future regressions fail the build. Help text - drift cleanup (stale "zupt" examples → "vaptvupt", stale "LZ77 + - Huffman" → "VaptVupt LZ + ANS 2.48.5", license attribution). - New regression test tests/test_help_consistency.sh (10 assertions - including the F-13 byte-level guard). No source crypto changes; - audit posture unchanged from v3.0.1. - gui/LICENSE-GUI rewritten AGPL-3.0-or-later with historical - correction note). GUI version-string parsing bug fix (the - v3.0.0 `replace("zupt ", "")` parser matched the wrong substring - inside the new rename parenthetical, causing garbled window - titles; fixed with anchored `_VERSION_RE`). New regression test - tests/test_gui_branding.sh (11 assertions) wired into make check - and make test. No source crypto changes; audit posture unchanged - from v3.0.0 (which itself preserved the v2.3.1 baseline). - Codec upgrade to VaptVupt LZ + ANS 2.48.5 (two libFuzzer-found fixes: - csz==0 OOB-READ in vv_dstream_decompress_chunk; UBSan-safe pointer - arithmetic in vv_copy_match). GUI binary-discovery bug fixed with - liveness-checking _find_vaptvupt + discovery log. Enhanced manpage - (597 lines). Wire format unchanged at v1.6. Bidirectional v2.x ↔ v3.0.0 - archive compatibility verified. F-09 byte sweep still 0/1827 silent - accepts; F-06 HMAC fuzz still 0/2000. No new findings, no findings - reopened. Security posture unchanged. - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - -- v3.5.0 — 2026-06-01 — Measured constant-time MAC comparison - (dudect-style). The MAC tag compare — the most timing-sensitive - operation, where a leak is a forgery oracle — was carried as three - duplicated inline byte-OR loops marked CT-REQUIRED but never - measured. Consolidated into one audited primitive zupt_ct_memeq - (volatile OR-accumulate, no early exit, branch-free fold), used by - the v1.6 strict decrypt path and the F-08 archive-integrity-trailer - check. New dudect-style timing test (tests/test_ct_timing.c): - Welch t-test over fixed-equal vs random-differing tag classes, - built at -O2 so it exercises the shipped code (incl. that the - volatile sink survives the optimiser). Verdict is environment- - relative: a leaky-memcmp positive control must show a clear leak in - the same environment, and zupt_ct_memeq must show <=20% of that - signal (measured ~1%; median of 5 runs; INCONCLUSIVE rather than - vacuous-pass if the host is too coarse). A reintroduced early- - return/branch pushes the ratio toward 1.0 and fails. This turns an - asserted CT property into a measured one + CI regression guard. - The formally-verified Jasmin zupt_mac_verify_ct path (v1.4/v1.5 - legacy compare) and the F-06 two-candidate fold are unchanged. No - cryptographic-correctness change, no wire-format change (v1.6). - F-09 byte sweep 0/1827; F-06 1-bit HMAC fuzz 0/2000. - The 0x04 Argon2id enc-header recorded only [type|salt|nonce] and - nothing about the KDF cost (the PBKDF2 header records its iteration - count). New archives append a one-byte KDF profile descriptor at - offset 33 (ZUPT_ARGON2_PROFILE_MODERATE), making the header self- - describing so a reader always knows which Argon2id cost produced - the archive — eliminating a silent-undecryptability risk if the - preset ever changes. The descriptor is covered by the F-08 archive- - integrity trailer (tamper-evident; a flipped byte fails decryption, - verified). Additive + back-compatible: legacy 33-byte archives - decrypt byte-exact; 33B and 34B headers derive identical keys; - unknown profiles are refused fail-closed (no wrong-key guessing). - Build-time SDK-drift guard: the F-15 test asserts the KDF is - deterministic and meets a coarse memory-hard cost floor (>=20 ms), - failing the build if libzuptsdk is swapped for a weak/stub Argon2id. - No cryptographic-correctness change, no wire-format change (v1.6). - F-09 byte sweep 0/1827; F-06 1-bit HMAC fuzz 0/2000. Note: the - explicit RFC 9106 zsdk_argon2id() is header-declared but NOT - exported by the vendored libzuptsdk.so, so the cost is recorded in- - band rather than re-parameterised; revisit if the SDK exports it. - Encrypt-then-MAC hot path. ipad/opad key prefix folded once per - keyring; MAC streamed (aad || nonce || ciphertext || seq) instead - of concatenated into a malloc'd buffer. SECURITY-RELEVANT: - identical authentication semantics — the MAC is byte-for-byte the - same (RFC 2104 + SHA-256 Merkle-Damgard associativity), proven by - RFC 4231 vectors, a new equivalence test, and byte-exact - decryption of 3.2.x archives. Constant-time tag compares unchanged - (byte-OR accumulator / Jasmin zupt_mac_verify_ct). F-09 byte sweep - 0/1827; F-06 1-bit HMAC fuzz 0/2000. Reduces secret-data heap - footprint: the old path copied the full ciphertext into a second - malloc'd buffer per block — now removed. No cryptographic- - correctness change, no wire-format change (v1.6). ASan clean on - both KDF paths. - SHA-NI). New SHA256RNDS2/MSG1/MSG2 compression path with CPUID - runtime dispatch (has_shani, CPUID.07H:EBX[29]). Accelerates the - Encrypt-then-MAC second pass (HMAC-SHA256) and PBKDF2. - Security-relevant property: SHA-NI is constant-time by - construction (no data-dependent memory access or branches), - strengthening the side-channel posture of HMAC verification over - attacker-influenced ciphertext vs the scalar software path. No - cryptographic-correctness change: same SHA-256, same HMAC, same - Encrypt-then-MAC, same wire bytes (format v1.6 unchanged). NIST - FIPS 180-4 vectors pass on both paths; the 64 SHA-NI round - constants are verified bit-identical to the scalar K[] table. - Speedup ([ESTIMATED] 3-8x) NOT measured in this release — the - build host lacks SHA-NI; figure to be confirmed on SHA-NI - hardware. Scalar fallback unchanged and remains the path on - non-SHA-NI CPUs (incl. aarch64). F-09 byte sweep still 0/1827. - byte-identical; 3 .c files changed). Inherits 6 upstream - corrupt-input decoder memory-safety fixes (v2.52.4 + v2.53.2). - F-14 closed: ASAN found a heap-buffer-overflow WRITE of size 32 - in OUR decode wrapper — decode buffers were malloc(uncompressed - _size) with no slack, but the codec AVX2 over-copy needs >=32 B - slack (documented contract in vaptvupt.h). Old codec never - reached it; new wider AVX2 hot path does. Fixed with shared - ZUPT_VV_DECODE_SLACK (64 B) guard on both single-threaded - (zupt_format.c) and parallel (zupt_parallel.c) decode paths. - Verified: ASAN 24/24 single-threaded + 15/15 multi-threaded; - bit-flip fuzz 300 trials 0 crashes / 300 clean rejects. F-09 - byte sweep still 0/1827. vv_decoder.c scalar build made - -Werror clean for aarch64/Termux. New test tests/test_vv_decode - _slack.sh. Wire format unchanged (v1.6); 3.0.3 archives extract - byte-exact. - knownConditionTrueFalse findings closed in varint decoders - (dead `&& (x&0x80)` AND-branch after preceding terminator-byte - early-return). -Wconversion / -Wsign-conversion findings closed - with explicit casts at two sites. Our 9-file non-vendored C now - compiles clean under the union of strict GCC warnings including - -Wconversion -Wsign-conversion -Werror. New regression test - tests/test_static_analysis.sh (7 assertions) wired into make - check and make test. Behaviour byte-identical; F-09 byte sweep - still 0/1827 silent accepts. No source crypto changes. - src/zupt_main.c exceeded C99's 4095-char ISO limit (was 4121). - Refactored to 5 fprintf sections; -Woverlength-strings added to - default CFLAGS so future regressions fail the build. Help text - drift cleanup (stale "zupt" examples → "vaptvupt", stale "LZ77 + - Huffman" → "VaptVupt LZ + ANS 2.48.5", license attribution). - New regression test tests/test_help_consistency.sh (10 assertions - including the F-13 byte-level guard). No source crypto changes; - audit posture unchanged from v3.0.1. - gui/LICENSE-GUI rewritten AGPL-3.0-or-later with historical - correction note). GUI version-string parsing bug fix (the - v3.0.0 `replace("zupt ", "")` parser matched the wrong substring - inside the new rename parenthetical, causing garbled window - titles; fixed with anchored `_VERSION_RE`). New regression test - tests/test_gui_branding.sh (11 assertions) wired into make check - and make test. No source crypto changes; audit posture unchanged - from v3.0.0 (which itself preserved the v2.3.1 baseline). - Codec upgrade to VaptVupt LZ + ANS 2.48.5 (two libFuzzer-found fixes: - csz==0 OOB-READ in vv_dstream_decompress_chunk; UBSan-safe pointer - arithmetic in vv_copy_match). GUI binary-discovery bug fixed with - liveness-checking _find_vaptvupt + discovery log. Enhanced manpage - (597 lines). Wire format unchanged at v1.6. Bidirectional v2.x ↔ v3.0.0 - archive compatibility verified. F-09 byte sweep still 0/1827 silent - accepts; F-06 HMAC fuzz still 0/2000. No new findings, no findings - reopened. Security posture unchanged. - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - -- v3.6.0 — 2026-06-01 — NIST SP 800-38A AES-256-CTR known-answer - vectors + ML-KEM self-test fixes. AES-256-CTR — the bulk cipher — - previously had only indirect roundtrip coverage; added the - canonical SP 800-38A F.5.5 (encrypt) and F.5.6 (decrypt) vectors, - validating zupt_aes256_ctr against the standard on both the Jasmin - AES-NI path (zupt_aes256_ctr4 + zupt_aes256_blk) and the C T-table - fallback. Both match exactly, confirming the Jasmin single-block - AES is correct vs the standard (retires the stale stack-offset - concern for zupt_aes256_blk). Also fixed two ML-KEM-768 self-test - bugs: (1) an inverted result check in test_vectors that printed OK - when the self-test returned failure — it had been passing - vacuously; (2) the NTT roundtrip self-test asserted a false - ntt∘inv_ntt == identity (this pqcrystals/Kyber Montgomery - convention recovers each coefficient scaled by R^-1 mod q = 169), - now rewritten to assert the true consistent-linear-scaling - invariant, which still catches genuine NTT bugs and no longer - emits a misleading stderr "NTT roundtrip FAILED". ML-KEM - correctness end-to-end was never affected — the K-PKE, KEM, and - FIPS 203 roundtrip vectors and implicit-rejection all pass. No - source-crypto behaviour change, no wire-format change (v1.6). - test_vectors now 16 passed / 0 failed (was 14, one vacuous). F-09 - byte sweep 0/1827; F-06 1-bit HMAC fuzz 0/2000. - (dudect-style). The MAC tag compare — the most timing-sensitive - operation, where a leak is a forgery oracle — was carried as three - duplicated inline byte-OR loops marked CT-REQUIRED but never - measured. Consolidated into one audited primitive zupt_ct_memeq - (volatile OR-accumulate, no early exit, branch-free fold), used by - the v1.6 strict decrypt path and the F-08 archive-integrity-trailer - check. New dudect-style timing test (tests/test_ct_timing.c): - Welch t-test over fixed-equal vs random-differing tag classes, - built at -O2 so it exercises the shipped code (incl. that the - volatile sink survives the optimiser). Verdict is environment- - relative: a leaky-memcmp positive control must show a clear leak in - the same environment, and zupt_ct_memeq must show <=20% of that - signal (measured ~1%; median of 5 runs; INCONCLUSIVE rather than - vacuous-pass if the host is too coarse). A reintroduced early- - return/branch pushes the ratio toward 1.0 and fails. This turns an - asserted CT property into a measured one + CI regression guard. - The formally-verified Jasmin zupt_mac_verify_ct path (v1.4/v1.5 - legacy compare) and the F-06 two-candidate fold are unchanged. No - cryptographic-correctness change, no wire-format change (v1.6). - F-09 byte sweep 0/1827; F-06 1-bit HMAC fuzz 0/2000. - The 0x04 Argon2id enc-header recorded only [type|salt|nonce] and - nothing about the KDF cost (the PBKDF2 header records its iteration - count). New archives append a one-byte KDF profile descriptor at - offset 33 (ZUPT_ARGON2_PROFILE_MODERATE), making the header self- - describing so a reader always knows which Argon2id cost produced - the archive — eliminating a silent-undecryptability risk if the - preset ever changes. The descriptor is covered by the F-08 archive- - integrity trailer (tamper-evident; a flipped byte fails decryption, - verified). Additive + back-compatible: legacy 33-byte archives - decrypt byte-exact; 33B and 34B headers derive identical keys; - unknown profiles are refused fail-closed (no wrong-key guessing). - Build-time SDK-drift guard: the F-15 test asserts the KDF is - deterministic and meets a coarse memory-hard cost floor (>=20 ms), - failing the build if libzuptsdk is swapped for a weak/stub Argon2id. - No cryptographic-correctness change, no wire-format change (v1.6). - F-09 byte sweep 0/1827; F-06 1-bit HMAC fuzz 0/2000. Note: the - explicit RFC 9106 zsdk_argon2id() is header-declared but NOT - exported by the vendored libzuptsdk.so, so the cost is recorded in- - band rather than re-parameterised; revisit if the SDK exports it. - Encrypt-then-MAC hot path. ipad/opad key prefix folded once per - keyring; MAC streamed (aad || nonce || ciphertext || seq) instead - of concatenated into a malloc'd buffer. SECURITY-RELEVANT: - identical authentication semantics — the MAC is byte-for-byte the - same (RFC 2104 + SHA-256 Merkle-Damgard associativity), proven by - RFC 4231 vectors, a new equivalence test, and byte-exact - decryption of 3.2.x archives. Constant-time tag compares unchanged - (byte-OR accumulator / Jasmin zupt_mac_verify_ct). F-09 byte sweep - 0/1827; F-06 1-bit HMAC fuzz 0/2000. Reduces secret-data heap - footprint: the old path copied the full ciphertext into a second - malloc'd buffer per block — now removed. No cryptographic- - correctness change, no wire-format change (v1.6). ASan clean on - both KDF paths. - SHA-NI). New SHA256RNDS2/MSG1/MSG2 compression path with CPUID - runtime dispatch (has_shani, CPUID.07H:EBX[29]). Accelerates the - Encrypt-then-MAC second pass (HMAC-SHA256) and PBKDF2. - Security-relevant property: SHA-NI is constant-time by - construction (no data-dependent memory access or branches), - strengthening the side-channel posture of HMAC verification over - attacker-influenced ciphertext vs the scalar software path. No - cryptographic-correctness change: same SHA-256, same HMAC, same - Encrypt-then-MAC, same wire bytes (format v1.6 unchanged). NIST - FIPS 180-4 vectors pass on both paths; the 64 SHA-NI round - constants are verified bit-identical to the scalar K[] table. - Speedup ([ESTIMATED] 3-8x) NOT measured in this release — the - build host lacks SHA-NI; figure to be confirmed on SHA-NI - hardware. Scalar fallback unchanged and remains the path on - non-SHA-NI CPUs (incl. aarch64). F-09 byte sweep still 0/1827. - byte-identical; 3 .c files changed). Inherits 6 upstream - corrupt-input decoder memory-safety fixes (v2.52.4 + v2.53.2). - F-14 closed: ASAN found a heap-buffer-overflow WRITE of size 32 - in OUR decode wrapper — decode buffers were malloc(uncompressed - _size) with no slack, but the codec AVX2 over-copy needs >=32 B - slack (documented contract in vaptvupt.h). Old codec never - reached it; new wider AVX2 hot path does. Fixed with shared - ZUPT_VV_DECODE_SLACK (64 B) guard on both single-threaded - (zupt_format.c) and parallel (zupt_parallel.c) decode paths. - Verified: ASAN 24/24 single-threaded + 15/15 multi-threaded; - bit-flip fuzz 300 trials 0 crashes / 300 clean rejects. F-09 - byte sweep still 0/1827. vv_decoder.c scalar build made - -Werror clean for aarch64/Termux. New test tests/test_vv_decode - _slack.sh. Wire format unchanged (v1.6); 3.0.3 archives extract - byte-exact. - knownConditionTrueFalse findings closed in varint decoders - (dead `&& (x&0x80)` AND-branch after preceding terminator-byte - early-return). -Wconversion / -Wsign-conversion findings closed - with explicit casts at two sites. Our 9-file non-vendored C now - compiles clean under the union of strict GCC warnings including - -Wconversion -Wsign-conversion -Werror. New regression test - tests/test_static_analysis.sh (7 assertions) wired into make - check and make test. Behaviour byte-identical; F-09 byte sweep - still 0/1827 silent accepts. No source crypto changes. - src/zupt_main.c exceeded C99's 4095-char ISO limit (was 4121). - Refactored to 5 fprintf sections; -Woverlength-strings added to - default CFLAGS so future regressions fail the build. Help text - drift cleanup (stale "zupt" examples → "vaptvupt", stale "LZ77 + - Huffman" → "VaptVupt LZ + ANS 2.48.5", license attribution). - New regression test tests/test_help_consistency.sh (10 assertions - including the F-13 byte-level guard). No source crypto changes; - audit posture unchanged from v3.0.1. - gui/LICENSE-GUI rewritten AGPL-3.0-or-later with historical - correction note). GUI version-string parsing bug fix (the - v3.0.0 `replace("zupt ", "")` parser matched the wrong substring - inside the new rename parenthetical, causing garbled window - titles; fixed with anchored `_VERSION_RE`). New regression test - tests/test_gui_branding.sh (11 assertions) wired into make check - and make test. No source crypto changes; audit posture unchanged - from v3.0.0 (which itself preserved the v2.3.1 baseline). - Codec upgrade to VaptVupt LZ + ANS 2.48.5 (two libFuzzer-found fixes: - csz==0 OOB-READ in vv_dstream_decompress_chunk; UBSan-safe pointer - arithmetic in vv_copy_match). GUI binary-discovery bug fixed with - liveness-checking _find_vaptvupt + discovery log. Enhanced manpage - (597 lines). Wire format unchanged at v1.6. Bidirectional v2.x ↔ v3.0.0 - archive compatibility verified. F-09 byte sweep still 0/1827 silent - accepts; F-06 HMAC fuzz still 0/2000. No new findings, no findings - reopened. Security posture unchanged. - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - -- v3.7.0 — 2026-06-01 — ML-KEM-768 decapsulation comparison routed - through the audited constant-time primitive. The implicit- - rejection check (received ct vs re-encrypted ct', 1088 bytes) was - an inline byte-OR loop marked CT-REQUIRED but never measured and - not sharing the zupt_ct_memeq primitive introduced in 3.5.0. A - timing leak there is a KEM decapsulation oracle (valid vs invalid - ciphertext) that breaks IND-CCA2. It now calls - zupt_ct_memeq(ct, ct_prime, 1088); the fail bit is (1 - equal) so - ML-KEM output is byte-identical (FIPS 203 roundtrip, implicit- - rejection vector, PQ-hybrid roundtrip, wrong-key rejection all - pass). This was the LAST security-critical comparison using a - bespoke inline loop — MAC tag, F-08 trailer, and ML-KEM decaps now - all route through one audited, length-independent primitive. - tests/test_ct_timing extended to 1088 bytes with a source-routing - guard. HONEST SCOPING: the 1088-byte dudect numbers are reported - informational, not pass/fail — at that size on a shared vCPU the - signal is memory-dominated and memcmp is not a cleanly-leaking - control, so the 32-byte environment-relative ratio does not - transfer. Constant-timeness of the 1088-byte compare instead - follows from (a) the 32-byte pass proving zupt_ct_memeq is CT, - (b) zupt_ct_memeq being length-independent by construction - (OR-accumulate, no early exit, no data-dependent branch), and - (c) the source-routing guard confirming decaps uses it. No - cryptographic-correctness change, no wire-format change (v1.6). - test_vectors 16/0; F-09 byte sweep 0/1827; F-06 0/2000. - vectors + ML-KEM self-test fixes. AES-256-CTR — the bulk cipher — - previously had only indirect roundtrip coverage; added the - canonical SP 800-38A F.5.5 (encrypt) and F.5.6 (decrypt) vectors, - validating zupt_aes256_ctr against the standard on both the Jasmin - AES-NI path (zupt_aes256_ctr4 + zupt_aes256_blk) and the C T-table - fallback. Both match exactly, confirming the Jasmin single-block - AES is correct vs the standard (retires the stale stack-offset - concern for zupt_aes256_blk). Also fixed two ML-KEM-768 self-test - bugs: (1) an inverted result check in test_vectors that printed OK - when the self-test returned failure — it had been passing - vacuously; (2) the NTT roundtrip self-test asserted a false - ntt∘inv_ntt == identity (this pqcrystals/Kyber Montgomery - convention recovers each coefficient scaled by R^-1 mod q = 169), - now rewritten to assert the true consistent-linear-scaling - invariant, which still catches genuine NTT bugs and no longer - emits a misleading stderr "NTT roundtrip FAILED". ML-KEM - correctness end-to-end was never affected — the K-PKE, KEM, and - FIPS 203 roundtrip vectors and implicit-rejection all pass. No - source-crypto behaviour change, no wire-format change (v1.6). - test_vectors now 16 passed / 0 failed (was 14, one vacuous). F-09 - byte sweep 0/1827; F-06 1-bit HMAC fuzz 0/2000. - (dudect-style). The MAC tag compare — the most timing-sensitive - operation, where a leak is a forgery oracle — was carried as three - duplicated inline byte-OR loops marked CT-REQUIRED but never - measured. Consolidated into one audited primitive zupt_ct_memeq - (volatile OR-accumulate, no early exit, branch-free fold), used by - the v1.6 strict decrypt path and the F-08 archive-integrity-trailer - check. New dudect-style timing test (tests/test_ct_timing.c): - Welch t-test over fixed-equal vs random-differing tag classes, - built at -O2 so it exercises the shipped code (incl. that the - volatile sink survives the optimiser). Verdict is environment- - relative: a leaky-memcmp positive control must show a clear leak in - the same environment, and zupt_ct_memeq must show <=20% of that - signal (measured ~1%; median of 5 runs; INCONCLUSIVE rather than - vacuous-pass if the host is too coarse). A reintroduced early- - return/branch pushes the ratio toward 1.0 and fails. This turns an - asserted CT property into a measured one + CI regression guard. - The formally-verified Jasmin zupt_mac_verify_ct path (v1.4/v1.5 - legacy compare) and the F-06 two-candidate fold are unchanged. No - cryptographic-correctness change, no wire-format change (v1.6). - F-09 byte sweep 0/1827; F-06 1-bit HMAC fuzz 0/2000. - The 0x04 Argon2id enc-header recorded only [type|salt|nonce] and - nothing about the KDF cost (the PBKDF2 header records its iteration - count). New archives append a one-byte KDF profile descriptor at - offset 33 (ZUPT_ARGON2_PROFILE_MODERATE), making the header self- - describing so a reader always knows which Argon2id cost produced - the archive — eliminating a silent-undecryptability risk if the - preset ever changes. The descriptor is covered by the F-08 archive- - integrity trailer (tamper-evident; a flipped byte fails decryption, - verified). Additive + back-compatible: legacy 33-byte archives - decrypt byte-exact; 33B and 34B headers derive identical keys; - unknown profiles are refused fail-closed (no wrong-key guessing). - Build-time SDK-drift guard: the F-15 test asserts the KDF is - deterministic and meets a coarse memory-hard cost floor (>=20 ms), - failing the build if libzuptsdk is swapped for a weak/stub Argon2id. - No cryptographic-correctness change, no wire-format change (v1.6). - F-09 byte sweep 0/1827; F-06 1-bit HMAC fuzz 0/2000. Note: the - explicit RFC 9106 zsdk_argon2id() is header-declared but NOT - exported by the vendored libzuptsdk.so, so the cost is recorded in- - band rather than re-parameterised; revisit if the SDK exports it. - Encrypt-then-MAC hot path. ipad/opad key prefix folded once per - keyring; MAC streamed (aad || nonce || ciphertext || seq) instead - of concatenated into a malloc'd buffer. SECURITY-RELEVANT: - identical authentication semantics — the MAC is byte-for-byte the - same (RFC 2104 + SHA-256 Merkle-Damgard associativity), proven by - RFC 4231 vectors, a new equivalence test, and byte-exact - decryption of 3.2.x archives. Constant-time tag compares unchanged - (byte-OR accumulator / Jasmin zupt_mac_verify_ct). F-09 byte sweep - 0/1827; F-06 1-bit HMAC fuzz 0/2000. Reduces secret-data heap - footprint: the old path copied the full ciphertext into a second - malloc'd buffer per block — now removed. No cryptographic- - correctness change, no wire-format change (v1.6). ASan clean on - both KDF paths. - SHA-NI). New SHA256RNDS2/MSG1/MSG2 compression path with CPUID - runtime dispatch (has_shani, CPUID.07H:EBX[29]). Accelerates the - Encrypt-then-MAC second pass (HMAC-SHA256) and PBKDF2. - Security-relevant property: SHA-NI is constant-time by - construction (no data-dependent memory access or branches), - strengthening the side-channel posture of HMAC verification over - attacker-influenced ciphertext vs the scalar software path. No - cryptographic-correctness change: same SHA-256, same HMAC, same - Encrypt-then-MAC, same wire bytes (format v1.6 unchanged). NIST - FIPS 180-4 vectors pass on both paths; the 64 SHA-NI round - constants are verified bit-identical to the scalar K[] table. - Speedup ([ESTIMATED] 3-8x) NOT measured in this release — the - build host lacks SHA-NI; figure to be confirmed on SHA-NI - hardware. Scalar fallback unchanged and remains the path on - non-SHA-NI CPUs (incl. aarch64). F-09 byte sweep still 0/1827. - byte-identical; 3 .c files changed). Inherits 6 upstream - corrupt-input decoder memory-safety fixes (v2.52.4 + v2.53.2). - F-14 closed: ASAN found a heap-buffer-overflow WRITE of size 32 - in OUR decode wrapper — decode buffers were malloc(uncompressed - _size) with no slack, but the codec AVX2 over-copy needs >=32 B - slack (documented contract in vaptvupt.h). Old codec never - reached it; new wider AVX2 hot path does. Fixed with shared - ZUPT_VV_DECODE_SLACK (64 B) guard on both single-threaded - (zupt_format.c) and parallel (zupt_parallel.c) decode paths. - Verified: ASAN 24/24 single-threaded + 15/15 multi-threaded; - bit-flip fuzz 300 trials 0 crashes / 300 clean rejects. F-09 - byte sweep still 0/1827. vv_decoder.c scalar build made - -Werror clean for aarch64/Termux. New test tests/test_vv_decode - _slack.sh. Wire format unchanged (v1.6); 3.0.3 archives extract - byte-exact. - knownConditionTrueFalse findings closed in varint decoders - (dead `&& (x&0x80)` AND-branch after preceding terminator-byte - early-return). -Wconversion / -Wsign-conversion findings closed - with explicit casts at two sites. Our 9-file non-vendored C now - compiles clean under the union of strict GCC warnings including - -Wconversion -Wsign-conversion -Werror. New regression test - tests/test_static_analysis.sh (7 assertions) wired into make - check and make test. Behaviour byte-identical; F-09 byte sweep - still 0/1827 silent accepts. No source crypto changes. - src/zupt_main.c exceeded C99's 4095-char ISO limit (was 4121). - Refactored to 5 fprintf sections; -Woverlength-strings added to - default CFLAGS so future regressions fail the build. Help text - drift cleanup (stale "zupt" examples → "vaptvupt", stale "LZ77 + - Huffman" → "VaptVupt LZ + ANS 2.48.5", license attribution). - New regression test tests/test_help_consistency.sh (10 assertions - including the F-13 byte-level guard). No source crypto changes; - audit posture unchanged from v3.0.1. - gui/LICENSE-GUI rewritten AGPL-3.0-or-later with historical - correction note). GUI version-string parsing bug fix (the - v3.0.0 `replace("zupt ", "")` parser matched the wrong substring - inside the new rename parenthetical, causing garbled window - titles; fixed with anchored `_VERSION_RE`). New regression test - tests/test_gui_branding.sh (11 assertions) wired into make check - and make test. No source crypto changes; audit posture unchanged - from v3.0.0 (which itself preserved the v2.3.1 baseline). - Codec upgrade to VaptVupt LZ + ANS 2.48.5 (two libFuzzer-found fixes: - csz==0 OOB-READ in vv_dstream_decompress_chunk; UBSan-safe pointer - arithmetic in vv_copy_match). GUI binary-discovery bug fixed with - liveness-checking _find_vaptvupt + discovery log. Enhanced manpage - (597 lines). Wire format unchanged at v1.6. Bidirectional v2.x ↔ v3.0.0 - archive compatibility verified. F-09 byte sweep still 0/1827 silent - accepts; F-06 HMAC fuzz still 0/2000. No new findings, no findings - reopened. Security posture unchanged. - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - -- v4.0.0 — 2026-06-10 — Stack integration: codec → canonical 2.60.4 - (security release; OOB heap write in AVX2 exact-size decode fixed; - CBMC-verified BCJ). F-16 found, disclosed, fixed (see Findings). New - --pq-box mode via vendored libpqvaptvupt 0.6.0 (HKDF-SHA256 - domain-separated combiner; 13/13 adversarial checks; ASan/UBSan - clean). New exact-content_size decode regression (80 cases, ASan). - SHA-NI measured 5.8× same-box; v3.2.0 estimate retired. Clang strict - build restored (as(1) for Jasmin .s). 8-mode back-compat byte-exact. -- v4.0.0 — 2026-06-10 — F-16: data loss in ≤3.8.0 BCJ encoding - (pre-existing; found by the 4.0.0 back-compat matrix; fixed by the - codec move to canonical 2.60.4). The ≤3.8.0 tree vendored a divergent - pre-release BCJ (upstream 2.53.3 has no BCJ; it landed in 2.53.4). - On BCJ-detected executable content at L8/L9 the old encoder emitted - streams that no decoder accepts — including 3.8.0 itself (verified: - the 3.8.0 binary fails on the archive it just wrote; defect is at - write time, deterministic on the binary fixture). L≤7 and non-BCJ - content unaffected: the 8-mode matrix (plain L1/L5/L9, store, - Argon2id, PBKDF2, --pq, --pq-sdk) decodes byte-exact under 4.0.0. - Remediation: re-create affected archives with ≥4.0.0 and verify - extraction before deleting sources. Regression guard: - tests/test_codec_exact_size.sh includes tool-level BCJ roundtrips - (L5/L9, real ELF fixture) plus 80 exact-size codec decodes under - ASan covering the upstream OOB fix class. Forward-compat note: - ≤3.8.0 cannot read 4.0.0 archives where the auto-filter fired - (L3+ on ELF/PE/Mach-O); upgrade readers first in mixed fleets. - -- v3.8.0 — 2026-06-01 — Documentation-only release: consolidated - measured benchmarks (BENCHMARKS.md). No source, cryptographic, or - wire-format change — the binary is identical in behaviour to - 3.7.0 (format v1.6). Publishes a complete reproducible benchmark - set (compression ratio/throughput, encode-speed-vs-level, the - KDF-vs-per-block crypto overhead split, and a head-to-head ratio - comparison against zstd that shows where VaptVupt loses) with the - test machine and method stated per table. The SHA-NI speedup is - explicitly marked [ESTIMATED] as the test box has no SHA-NI. No - new findings; all prior guarantees unchanged: test_vectors 16/0, - F-09 byte sweep 0/1827, F-06 1-bit HMAC fuzz 0/2000, every - security-critical comparison through the audited constant-time - primitive. - through the audited constant-time primitive. The implicit- - rejection check (received ct vs re-encrypted ct', 1088 bytes) was - an inline byte-OR loop marked CT-REQUIRED but never measured and - not sharing the zupt_ct_memeq primitive introduced in 3.5.0. A - timing leak there is a KEM decapsulation oracle (valid vs invalid - ciphertext) that breaks IND-CCA2. It now calls - zupt_ct_memeq(ct, ct_prime, 1088); the fail bit is (1 - equal) so - ML-KEM output is byte-identical (FIPS 203 roundtrip, implicit- - rejection vector, PQ-hybrid roundtrip, wrong-key rejection all - pass). This was the LAST security-critical comparison using a - bespoke inline loop — MAC tag, F-08 trailer, and ML-KEM decaps now - all route through one audited, length-independent primitive. - tests/test_ct_timing extended to 1088 bytes with a source-routing - guard. HONEST SCOPING: the 1088-byte dudect numbers are reported - informational, not pass/fail — at that size on a shared vCPU the - signal is memory-dominated and memcmp is not a cleanly-leaking - control, so the 32-byte environment-relative ratio does not - transfer. Constant-timeness of the 1088-byte compare instead - follows from (a) the 32-byte pass proving zupt_ct_memeq is CT, - (b) zupt_ct_memeq being length-independent by construction - (OR-accumulate, no early exit, no data-dependent branch), and - (c) the source-routing guard confirming decaps uses it. No - cryptographic-correctness change, no wire-format change (v1.6). - test_vectors 16/0; F-09 byte sweep 0/1827; F-06 0/2000. - vectors + ML-KEM self-test fixes. AES-256-CTR — the bulk cipher — - previously had only indirect roundtrip coverage; added the - canonical SP 800-38A F.5.5 (encrypt) and F.5.6 (decrypt) vectors, - validating zupt_aes256_ctr against the standard on both the Jasmin - AES-NI path (zupt_aes256_ctr4 + zupt_aes256_blk) and the C T-table - fallback. Both match exactly, confirming the Jasmin single-block - AES is correct vs the standard (retires the stale stack-offset - concern for zupt_aes256_blk). Also fixed two ML-KEM-768 self-test - bugs: (1) an inverted result check in test_vectors that printed OK - when the self-test returned failure — it had been passing - vacuously; (2) the NTT roundtrip self-test asserted a false - ntt∘inv_ntt == identity (this pqcrystals/Kyber Montgomery - convention recovers each coefficient scaled by R^-1 mod q = 169), - now rewritten to assert the true consistent-linear-scaling - invariant, which still catches genuine NTT bugs and no longer - emits a misleading stderr "NTT roundtrip FAILED". ML-KEM - correctness end-to-end was never affected — the K-PKE, KEM, and - FIPS 203 roundtrip vectors and implicit-rejection all pass. No - source-crypto behaviour change, no wire-format change (v1.6). - test_vectors now 16 passed / 0 failed (was 14, one vacuous). F-09 - byte sweep 0/1827; F-06 1-bit HMAC fuzz 0/2000. - (dudect-style). The MAC tag compare — the most timing-sensitive - operation, where a leak is a forgery oracle — was carried as three - duplicated inline byte-OR loops marked CT-REQUIRED but never - measured. Consolidated into one audited primitive zupt_ct_memeq - (volatile OR-accumulate, no early exit, branch-free fold), used by - the v1.6 strict decrypt path and the F-08 archive-integrity-trailer - check. New dudect-style timing test (tests/test_ct_timing.c): - Welch t-test over fixed-equal vs random-differing tag classes, - built at -O2 so it exercises the shipped code (incl. that the - volatile sink survives the optimiser). Verdict is environment- - relative: a leaky-memcmp positive control must show a clear leak in - the same environment, and zupt_ct_memeq must show <=20% of that - signal (measured ~1%; median of 5 runs; INCONCLUSIVE rather than - vacuous-pass if the host is too coarse). A reintroduced early- - return/branch pushes the ratio toward 1.0 and fails. This turns an - asserted CT property into a measured one + CI regression guard. - The formally-verified Jasmin zupt_mac_verify_ct path (v1.4/v1.5 - legacy compare) and the F-06 two-candidate fold are unchanged. No - cryptographic-correctness change, no wire-format change (v1.6). - F-09 byte sweep 0/1827; F-06 1-bit HMAC fuzz 0/2000. - The 0x04 Argon2id enc-header recorded only [type|salt|nonce] and - nothing about the KDF cost (the PBKDF2 header records its iteration - count). New archives append a one-byte KDF profile descriptor at - offset 33 (ZUPT_ARGON2_PROFILE_MODERATE), making the header self- - describing so a reader always knows which Argon2id cost produced - the archive — eliminating a silent-undecryptability risk if the - preset ever changes. The descriptor is covered by the F-08 archive- - integrity trailer (tamper-evident; a flipped byte fails decryption, - verified). Additive + back-compatible: legacy 33-byte archives - decrypt byte-exact; 33B and 34B headers derive identical keys; - unknown profiles are refused fail-closed (no wrong-key guessing). - Build-time SDK-drift guard: the F-15 test asserts the KDF is - deterministic and meets a coarse memory-hard cost floor (>=20 ms), - failing the build if libzuptsdk is swapped for a weak/stub Argon2id. - No cryptographic-correctness change, no wire-format change (v1.6). - F-09 byte sweep 0/1827; F-06 1-bit HMAC fuzz 0/2000. Note: the - explicit RFC 9106 zsdk_argon2id() is header-declared but NOT - exported by the vendored libzuptsdk.so, so the cost is recorded in- - band rather than re-parameterised; revisit if the SDK exports it. - Encrypt-then-MAC hot path. ipad/opad key prefix folded once per - keyring; MAC streamed (aad || nonce || ciphertext || seq) instead - of concatenated into a malloc'd buffer. SECURITY-RELEVANT: - identical authentication semantics — the MAC is byte-for-byte the - same (RFC 2104 + SHA-256 Merkle-Damgard associativity), proven by - RFC 4231 vectors, a new equivalence test, and byte-exact - decryption of 3.2.x archives. Constant-time tag compares unchanged - (byte-OR accumulator / Jasmin zupt_mac_verify_ct). F-09 byte sweep - 0/1827; F-06 1-bit HMAC fuzz 0/2000. Reduces secret-data heap - footprint: the old path copied the full ciphertext into a second - malloc'd buffer per block — now removed. No cryptographic- - correctness change, no wire-format change (v1.6). ASan clean on - both KDF paths. - SHA-NI). New SHA256RNDS2/MSG1/MSG2 compression path with CPUID - runtime dispatch (has_shani, CPUID.07H:EBX[29]). Accelerates the - Encrypt-then-MAC second pass (HMAC-SHA256) and PBKDF2. - Security-relevant property: SHA-NI is constant-time by - construction (no data-dependent memory access or branches), - strengthening the side-channel posture of HMAC verification over - attacker-influenced ciphertext vs the scalar software path. No - cryptographic-correctness change: same SHA-256, same HMAC, same - Encrypt-then-MAC, same wire bytes (format v1.6 unchanged). NIST - FIPS 180-4 vectors pass on both paths; the 64 SHA-NI round - constants are verified bit-identical to the scalar K[] table. - Speedup ([ESTIMATED] 3-8x) NOT measured in this release — the - build host lacks SHA-NI; figure to be confirmed on SHA-NI - hardware. Scalar fallback unchanged and remains the path on - non-SHA-NI CPUs (incl. aarch64). F-09 byte sweep still 0/1827. - byte-identical; 3 .c files changed). Inherits 6 upstream - corrupt-input decoder memory-safety fixes (v2.52.4 + v2.53.2). - F-14 closed: ASAN found a heap-buffer-overflow WRITE of size 32 - in OUR decode wrapper — decode buffers were malloc(uncompressed - _size) with no slack, but the codec AVX2 over-copy needs >=32 B - slack (documented contract in vaptvupt.h). Old codec never - reached it; new wider AVX2 hot path does. Fixed with shared - ZUPT_VV_DECODE_SLACK (64 B) guard on both single-threaded - (zupt_format.c) and parallel (zupt_parallel.c) decode paths. - Verified: ASAN 24/24 single-threaded + 15/15 multi-threaded; - bit-flip fuzz 300 trials 0 crashes / 300 clean rejects. F-09 - byte sweep still 0/1827. vv_decoder.c scalar build made - -Werror clean for aarch64/Termux. New test tests/test_vv_decode - _slack.sh. Wire format unchanged (v1.6); 3.0.3 archives extract - byte-exact. - knownConditionTrueFalse findings closed in varint decoders - (dead `&& (x&0x80)` AND-branch after preceding terminator-byte - early-return). -Wconversion / -Wsign-conversion findings closed - with explicit casts at two sites. Our 9-file non-vendored C now - compiles clean under the union of strict GCC warnings including - -Wconversion -Wsign-conversion -Werror. New regression test - tests/test_static_analysis.sh (7 assertions) wired into make - check and make test. Behaviour byte-identical; F-09 byte sweep - still 0/1827 silent accepts. No source crypto changes. - src/zupt_main.c exceeded C99's 4095-char ISO limit (was 4121). - Refactored to 5 fprintf sections; -Woverlength-strings added to - default CFLAGS so future regressions fail the build. Help text - drift cleanup (stale "zupt" examples → "vaptvupt", stale "LZ77 + - Huffman" → "VaptVupt LZ + ANS 2.48.5", license attribution). - New regression test tests/test_help_consistency.sh (10 assertions - including the F-13 byte-level guard). No source crypto changes; - audit posture unchanged from v3.0.1. - gui/LICENSE-GUI rewritten AGPL-3.0-or-later with historical - correction note). GUI version-string parsing bug fix (the - v3.0.0 `replace("zupt ", "")` parser matched the wrong substring - inside the new rename parenthetical, causing garbled window - titles; fixed with anchored `_VERSION_RE`). New regression test - tests/test_gui_branding.sh (11 assertions) wired into make check - and make test. No source crypto changes; audit posture unchanged - from v3.0.0 (which itself preserved the v2.3.1 baseline). - Codec upgrade to VaptVupt LZ + ANS 2.48.5 (two libFuzzer-found fixes: - csz==0 OOB-READ in vv_dstream_decompress_chunk; UBSan-safe pointer - arithmetic in vv_copy_match). GUI binary-discovery bug fixed with - liveness-checking _find_vaptvupt + discovery log. Enhanced manpage - (597 lines). Wire format unchanged at v1.6. Bidirectional v2.x ↔ v3.0.0 - archive compatibility verified. F-09 byte sweep still 0/1827 silent - accepts; F-06 HMAC fuzz still 0/2000. No new findings, no findings - reopened. Security posture unchanged. - (curated 10-suite, 91-assertion subset) for OBS / Debian / RPM `%check` - sections. openSUSE OBS files rewritten for cabelo - (`home:cabelo:innovators/zupt`): license corrected MIT → AGPL-3.0-or-later, - version bumped 1.5.5 → 2.4.8, changelog history preserved. No source - changes; audit posture unchanged from v2.3.1. - ---- - -## 1. Cryptographic Test Vector Verification - -All primitives tested against published reference vectors: - -| Primitive | Standard | Vectors | Status | -|-----------|----------|---------|--------| -| SHA-256 | FIPS 180-4 | 3 (empty, "abc", 448-bit) | **PASS** | -| HMAC-SHA256 | RFC 4231 | 2 (TC2: "Jefe", TC3: 20×0xAA) | **PASS** | -| AES-256-CTR | NIST SP 800-38A | 2 (F.5.5 encrypt, F.5.6 decrypt; 4 blocks) | **PASS** | -| SHA3-256 | FIPS 202 | 2 (empty, "abc") | **PASS** | -| SHAKE-128 | FIPS 202 | 1 (empty, 128-bit output) | **PASS** | -| X25519 | RFC 7748 §5.2 | 2 (both test vectors) | **PASS** | -| ML-KEM-768 | FIPS 203 | 2 (5-trial roundtrip + implicit rejection) | **PASS** | -| XXH64 | xxHash spec | 1 (empty string, seed=0) | **PASS** | -| ML-KEM-768 self-test | internal | 1 (NTT consistent-scaling + CBD bounds) | **PASS** | -| **Total** | | **16** | **16/16 PASS** | - -## 2. Jasmin Constant-Time Verification - -| Function | Purpose | Status | -|----------|---------|--------| -| `zupt_mac_verify_ct` | HMAC comparison | **✅ Linked, CT-proven** | -| `zupt_ct_select_32` | ML-KEM FO select | **✅ Linked, CT-proven** | -| `zupt_fe_cswap` | X25519 conditional swap | **✅ Linked, CT-proven** | -| `zupt_aes256_blk` | AES-256 single-block (AES-NI) | **✅ Linked, CT by hardware** | -| `zupt_aes256_ctr4` | AES-256 4-block pipeline | **✅ Linked, CT by hardware** | - -## 3. ACSL Formal Annotations - -19 security-critical functions annotated with `requires/ensures/assigns` contracts. -Target: `frama-c -wp -wp-rte -wp-model Typed+Cast` - -## 4. Security Hardening - -| Feature | Status | -|---------|--------| -| mlock() key protection | **✅ Active** | -| Buffer canaries (keyring) | **✅ Active** | -| Always-decrypt timing mitigation | **✅ Active** | -| AFL++ fuzz harnesses | **✅ Available** (`make fuzz-build`) | - -## 5. VaptVupt Codec Tests - -| Test | Status | -|------|--------| -| Roundtrip all 3 modes (UF/BAL/EXT) | **PASS** | -| Roundtrip + AES-256 encryption | **PASS** | -| Roundtrip + PQ hybrid encryption | **PASS** | -| Roundtrip + multi-threaded | **PASS** | -| Roundtrip + solid mode | **PASS** | -| Incompressible fallback to store | **PASS** | -| Empty/small input | **PASS** | -| Multi-block (2 MB) | **PASS** | -| **Total** | **11/11 PASS** | - -| Suite | Tests | Result | What It Covers | -|-------|-------|--------|----------------| -| Regression | 16 | **16/16 PASS** | All codecs, modes, encryption, edge cases, corruption detection | -| Multi-threaded | 14 | **14/14 PASS** | N=1/2/4/8 threads, large files, 1000 files, MT+encryption | -| Post-quantum | 10 | **10/10 PASS** | Keygen, PQ encrypt/decrypt, wrong key, password compat, PQ+MT, 2MB | -| Quick smoke | 9 | **9/9 PASS** | Normal, solid, encrypted, wrong pw, MT, fast, store, PQ, integrity | -| NIST vectors | 14 | **14/14 PASS** | See table above | -| **Total** | **62** | **62/62 PASS** | | - -Reproduction: `make test-all` - ---- - -## 3. Memory Safety - -| Tool | Command | Result | -|------|---------|--------| -| AddressSanitizer | `make test-asan` | **Zero errors** | -| UndefinedBehaviorSanitizer | Built with `-fsanitize=address,undefined` | **Zero errors** | -| All code paths tested | Normal + solid + encrypted + PQ + MT | **Clean** | - -Reproduction: -```bash -make test-asan -./zupt_asan compress /tmp/t.zupt /path/to/data/ -./zupt_asan extract -o /tmp/out/ /tmp/t.zupt -./zupt_asan keygen -o /tmp/k.key -./zupt_asan compress --pq /tmp/pub.key /tmp/pq.zupt /path/to/data/ -./zupt_asan extract --pq /tmp/k.key -o /tmp/pqout/ /tmp/pq.zupt -``` - ---- - -## 4. Compiler Warning Audit - -| Compiler | Flags | Warnings | -|----------|-------|----------| -| GCC 13.x | `-Wall -Wextra -Wpedantic -O2 -std=c11` | **Zero** | -| Clang 18.x | `-Wall -Wextra -Wpedantic -O2 -std=c11` | **Zero** | - ---- - -## 5. Constant-Time Analysis - -| Function | Location | CT Method | Jasmin Verified? | Risk Level | -|----------|----------|-----------|-----------------|------------| -| HMAC comparison | `zupt_crypto.c:252` | 4×u64 XOR accumulation | **Yes** — `zupt_mac_verify_ct` linked | **None** (Jasmin proven) | -| ML-KEM FO select | `zupt_mlkem.c:593` | 4×u64 masked select | **Yes** — `zupt_ct_select_32` linked | **None** (Jasmin proven) | -| ML-KEM NTT butterfly | `zupt_mlkem.c` | Montgomery reduction (branchless) | No | Low | -| ML-KEM CBD sampling | `zupt_mlkem.c` | Bitwise operations only | No | Low | -| X25519 fe_cswap | `zupt_x25519.c:95` | Masked XOR swap | No (limb mismatch) | Low (C is branchless) | -| X25519 Montgomery ladder | `zupt_x25519.c:243` | Fixed 255 iterations | No | Low | -| AES-256 encrypt | `zupt_aes256.c:59` | **Table-based S-box** | **No** | **HIGH on shared HW** | -| SHA-256 | `zupt_sha256.c` | Table-based constants | No | Low (not secret-indexed) | -| Keccak-f[1600] | `zupt_keccak.c` | Bitwise XOR/ROT only | No | None | -| Key wipe | `zupt_crypto.c` | `explicit_bzero` / volatile | No | Low | - -### Jasmin Assembly Verification - -Two functions confirmed active in binary via `nm`: - -``` -0000000000014ae0 T zupt_mac_verify_ct ← Jasmin assembly, CT proven -0000000000014b20 T zupt_ct_select_32 ← Jasmin assembly, CT proven -``` - -Assembly generated by `jasminc 2026.03.0`. Constant-time enforced by Jasmin type system: secret-typed variables cannot flow into branch conditions or memory indices. - -### Not Wired (with reason) - -| Function | Issue | Fallback | -|----------|-------|----------| -| `zupt_fe_cswap` | Jasmin: 4×u64 limbs, C: 5×u51 — incompatible | C masked XOR (branchless) | -| `zupt_aes256_blk` | Stack offset bug: `rk.[1]` → `[rsp+1]` not `[rsp+16]` | C table-based AES | - ---- - -## 6. Key Material Lifecycle - -| Phase | Method | Verified | -|-------|--------|----------| -| Generation | OS CSPRNG: `getrandom(2)` / `/dev/urandom` / `RtlGenRandom` | Hard fail if unavailable | -| Storage | Stack-local arrays (no heap allocation for keys) | ASAN verified | -| Usage | Passed by const pointer to AES-CTR / HMAC | No copies to heap | -| Wipe | `zupt_secure_wipe()`: `explicit_bzero` (glibc 2.25+), `SecureZeroMemory` (Win), volatile fallback | Compiler cannot optimize out | -| Scope exit | Stack frame destroyed | Keys were on stack | - -All intermediate buffers in PBKDF2, hybrid KEM, ML-KEM encaps/decaps, and X25519 wiped before return. - ---- - -## 7. Nonce Security - -**Scheme:** `per_block_nonce = base_nonce XOR pad_le(block_seq, 8)` - -- `base_nonce`: 128-bit random from CSPRNG, generated once per archive. -- `block_seq`: monotonically increasing 0, 1, 2, ... per archive. -- **Uniqueness within archive:** Guaranteed (distinct seq → distinct nonce). -- **Uniqueness across archives:** 2^-128 collision probability per pair (birthday bound on random base). - ---- - -## 8. Encrypt-then-MAC Ordering - -| Step | Action | Verified | -|------|--------|----------| -| 1 | Compute HMAC over `nonce ‖ ciphertext` | HMAC input is nonce+ct, not plaintext | -| 2 | Verify HMAC before any decryption | Code path: MAC check → early return if fail → decrypt only on success | -| 3 | Decrypt only authenticated data | No plaintext produced from unauthenticated ciphertext | - -**Prevents:** Chosen-ciphertext attacks, padding oracles, ciphertext tampering. - ---- - -## 9. Bugs Found and Fixed (v0.5.1 → v1.5.0) - -| Bug | Severity | Version Fixed | Impact | -|-----|----------|---------------|--------| -| Huffman Kraft-inequality violation | Critical | v0.5.1 | Data corruption on specific inputs | -| Heap-buffer-overflow in LZ match finder | Critical | v0.5.1 | Potential code execution | -| `rand()` CSPRNG fallback | Critical | v0.5.1 | Predictable encryption keys | -| ML-KEM `poly_basemul` OOB | Critical | v1.0.0 | Buffer overread in NTT | -| ML-KEM missing `poly_tomont` | Critical | v1.0.0 | Public key in wrong domain | -| ML-KEM inverted FO `cmov` | Critical | v1.0.0 | Always selected rejection key | -| ML-KEM `inv_ntt` wrong table | High | v1.0.0 | NTT roundtrip failure | -| PQ nonce mismatch | High | v1.0.0 | Encrypt/decrypt used different nonces | -| X25519 `AA + a24*E` formula | High | v1.1.0 | Wrong curve, not interoperable | -| Dead `match_cost()` | Low | v1.1.0 | Clang warning | -| `const polyvec` qualifier | Low | v1.1.0 | Pedantic warnings | -| `__int128` pedantic | Low | v1.1.0 | Pedantic warning | - ---- - -## 10. Known Limitations - -| Limitation | Impact | Mitigation | Status | -|------------|--------|------------|--------| -| Table-based AES (C fallback) | Cache-timing on shared hardware | Jasmin AES-NI path exists but has offset bug | **Open** — fix `.jazz` source | -| Table-based SHA-256 | Theoretical cache-timing | Not used on secret-indexed data | **Accepted** | -| PBKDF2 not quantum-safe | Quantum password brute-force | Use `--pq` mode | **Documented** | -| No `mlock()` | Keys swappable to disk | Short key lifetime + `zupt_secure_wipe` | **Planned** | -| No fuzzing performed | Undiscovered bugs | AFL++ setup in FUZZING.md | **Planned** | -| No independent audit | Self-assessed only | Open source + Jasmin proofs | **Planned** | -| X25519 Jasmin not linked | C fallback for fe_cswap | C is branchless but compiler-dependent | **Open** — limb mismatch | - ---- - -© 2026 Cristian Cezar Moisés — AGPL-3.0-or-later - ---- - -## v2.2.1 audit pass — 2026-04-27 - -This pass focused on the production-readiness of the libzuptsdk integration -introduced in v2.2.0 and on adversarial review of the existing code paths -not previously audited. - -### Methodology - -Two-pass adversarial review: - -- **Pass A (read-and-reason):** read each source file, identify invariants, - ask "what does an attacker control?", "what happens at boundaries?". -- **Pass B (test-driven):** write a failing test that exercises the suspected - bug, fix it, write a regression test that fails before the fix and passes - after. - -When A and B disagreed, the discrepancy was investigated rather than -papered over. - -### Findings (all fixed in v2.2.1) - -| # | File:line | Severity | Description | -|---|---|---|---| -| 1 | `zupt_format.c:146` | low | varint reader truncated at 9 bytes | -| 2 | `zupt_format.c:1529..1699` (×6) | medium | unchecked `fwrite` in extract path → silent corruption | -| 3 | `zupt_crypto_sdk.c:90..` | low (defense-in-depth) | `mac_key` aliased to `enc_key` in SDK paths | -| 4 | `zupt_lz.c:33` | high | `size_t` overflow in LZ length decoder | -| 5 | `zupt_format.c:1610,1681` | high | dedup-ref recursion + OOB seek (DoS) | -| 6 | `zupt_format.c:446,883` | low | encrypt failure left partial archive | - -The only finding rated as high severity (#4 and #5) are exploitable from a -malicious archive: an attacker who can convince the user to extract their -archive could trigger a process crash. None of the findings allow code -execution or key recovery; the AEAD layer's authentication tag still -prevents arbitrary writes. - -### Test coverage after fixes - -| Suite | Count | Status | -|---|---|---| -| Native (run_quick.sh) | 9 | ✓ | -| SDK roundtrip (test_sdk.sh) | 11 | ✓ | -| Audit double-validated (test_audit.sh) | 10 | ✓ NEW | -| Inherited from libzuptsdk 2.1.5 | 169 | ✓ | -| Inherited fuzz iterations (ASAN-clean) | 750,000 | ✓ | -| **Total verified test points** | **199 + 750k fuzz** | **✓** | - -### Notes for users - -If you are using zupt in production: - -- v2.2.1 is a recommended upgrade. -- Archives written with v2.2.0 or earlier remain readable; no migration - needed. -- The high-severity findings (#4, #5) only affect the *extract* path. If - you only ever extract archives you created yourself, you are not - affected by them. If you accept third-party archives, upgrade. -- The `--pq-sdk` mode introduced in v2.2.0 was not affected by any of - these findings; it was introduced clean and remained clean. - ---- - -## 2026-04-27 — v2.2.1 audit pass - -Internal code review against an internal audit checklist (AUDIT_PROMPT — superseded by FORMAL_AUDIT_PROMPT.md). Six bugs -identified and fixed in the same release. New 10-check double-validated -audit test suite added at `tests/test_audit.sh`. - -### Bugs found and fixed - -| # | File:line | Severity | Description | -|---|---|---|---| -| 1 | `src/zupt_format.c:146` | low | uint64 varint truncated to 63 bits | -| 2 | `src/zupt_format.c` (×6) | medium | unchecked `fwrite` returns in extract path | -| 3 | `src/zupt_crypto_sdk.c` | low | `mac_key` was copy of `enc_key`, now KDF-split | -| 4 | `src/zupt_lz.c:33` | high | `lz_read_extra` size_t overflow → OOB copy | -| 5 | `src/zupt_format.c` (×2) | medium | dedup-ref forward offset + recursion accepted | -| 6 | `src/zupt_format.c` (×2) | low | partial archive not removed on encrypt-init fail | - -### Test methodology - -- **Path A**: code review identifies invariant; a failing test is constructed. -- **Path B**: an independent property-based check exercises the same invariant from a different angle. -- A test passes only when A and B agree. Disagreement is treated as a finding. - -10 audit checks across four categories (authenticated archives, format security, format compatibility, robustness). All passing. - -### Cumulative test surface (2.2.1) - -| Suite | Tests | Status | -|---|---|---| -| `make test` (run_quick) | 9 | ✓ | -| `tests/test_sdk.sh` | 11 | ✓ | -| `tests/test_audit.sh` | 10 | ✓ | -| **zupt total** | **30** | **✓** | -| Inherited libzuptsdk audit | 42 | ✓ | -| Inherited libzuptsdk RFC + roundtrip | 84 | ✓ | -| Inherited libzuptsdk binding contracts | 57 | ✓ | -| Inherited libzuptsdk Wycheproof | 5 | ✓ | -| **Combined zupt + SDK** | **218** | **✓** | -| Mutation-fuzz iters (ASAN/UBSAN) | 750,000 | ✓ | - -### Open items (not blockers) - -- No external audit yet. -- `make test-asan` not wired into the zupt Makefile (only the SDK Makefile has it). -- The deduplication path is structurally complex and would benefit from - property-based testing (currently covered by 30 tests, none property-based). - ---- - -## 2026-04-27 — v2.2.2 audit pass - -Second internal review against the same audit checklist, focused on format -parser robustness and dedup path correctness. - -### Bugs found and fixed (4) - -| # | File:line | Severity | Description | -|---|---|---|---| -| 7 | `zupt_format.c:166` | medium | realloc-pair atomicity: UB on partial failure | -| 8 | `zupt_format.c:138` | low | in-memory varint decoder had same 9-byte truncation as file variant | -| 9 | `zupt_format.c:1267` | medium | `encryption_header_off` not bounds-checked before seek | -| 10 | `zupt_format.c:1402` | medium | `index_offset` not bounds-checked before seek | - -### New test surface - -- 12 dedup property-based checks (`test_dedup_props.sh`) — covers - byte-exact roundtrip, dedup space savings, 100%-duplicate sets, - and dedup + PQ encryption interaction. -- 1000 ASAN/UBSAN fuzz iterations (`fuzz_format`) — zero crashes, - zero memory errors. - -### Cumulative test surface (2.2.2) - -| Suite | Tests | Status | -|---|---|---| -| run_quick.sh | 9 | ✓ | -| test_sdk.sh | 11 | ✓ | -| test_audit.sh | 10 | ✓ | -| test_dedup_props.sh | 12 | ✓ NEW | -| **zupt total** | **42** | **✓** | -| Format mutation fuzz (ASAN/UBSAN) | 1,000 iters | ✓ NEW | -| Inherited libzuptsdk audit | 42 | ✓ | -| Inherited libzuptsdk RFC + roundtrip | 84 | ✓ | -| Inherited libzuptsdk binding contracts | 57 | ✓ | -| Inherited libzuptsdk Wycheproof | 5 | ✓ | -| Inherited libzuptsdk fuzz | 750,000 iters | ✓ | -| **Combined zupt + SDK** | **260 tests + 751k fuzz** | **✓** | - -### CI - -GitHub Actions workflow added at `.github/workflows/ci.yml`: -build-and-test, asan-build, fuzz-format, package-deb. Each run -exercises the full test surface plus fuzz under sanitizers and -verifies the .deb installs cleanly. - -### Open items - -- External audit still pending (cost-bound, not engineering-bound). -- AppImage build via real `appimagetool` not yet automated in CI. -- The fuzz harness uses a single fixed seed archive; corpus - diversification (different file types, multi-file archives, - encrypted seeds) would strengthen coverage further. - ---- - -## 2026-04-27 — v2.2.2 formal audit (no version bump) - -Formal cryptographic audit pass conducted using methodology in -`FORMAL_AUDIT_PROMPT.md`. Auditor profile: senior cryptographic -engineering (15+ years production crypto). Threat model: government -archives with 30+ year retention, financial institutions under Brazilian -Central Bank Resolução 4.658/2018, healthcare (LGPD-Saúde), defense -(CNSA 2.0 alignment). - -### Methodology - -- **Path A**: line-by-line manual review with documented preconditions, - postconditions, invariants, trust boundaries, failure modes. -- **Path B**: independent adversarial test exercising the same invariant. -- Bug confirmed only when both paths agreed. - -### Bugs found and fixed (4) - -| # | File | Severity | Description | -|---|---|---|---| -| 11 | `zupt_format.c` (×2) | **HIGH** | Zip Slip path traversal in extract — `e->path` to `fopen` without validation | -| 12 | `zupt_format.c` (×2) | **MEDIUM** | symlink-follow on extract output (`fopen "wb"` follows symlinks) | -| 13 | `zupt_format.c:1593` | LOW | `size_t` overflow on solid-extract size cap (32-bit) | -| 14 | `zupt_format.c:parse_index` | LOW | `count * sizeof(entry)` overflow before calloc (32-bit) | - -### Cryptographic primitive review (no findings) - -Reviewed every public crypto path against: -- FIPS 197 (AES) — key/IV size, counter init, nonce reuse -- FIPS 202 (Keccak/SHA-3) — rate/capacity, no domain confusion -- FIPS 203 (ML-KEM) — parameter set correctness, key sanitization, decap fault resistance -- RFC 5297 (AES-SIV) — nonce-misuse resistance, AD coverage -- RFC 5869 (HKDF) — salt-vs-IKM separation, info domain separation -- RFC 7748 (X25519) — scalar clamping, all-zero output rejection -- RFC 8439 (ChaCha20-Poly1305) — 192-bit XChaCha nonce, AD coverage -- RFC 9106 (Argon2) — m≥64 MiB, t≥3, p≥1, salt≥16B -- RFC 9180 (HPKE) — suite ID, mode binding, encap context - -Findings: **none**. All primitives correctly implemented. - -### New regression test suite - -`tests/test_path_traversal.sh` — 5 property checks covering: -1. Patched archive with `../` entry does not escape parent dir -2. Patched archive with absolute path does not write to `/tmp/owned` -3. Symlink at extract target is not followed (sentinel preserved) -4. Legitimate paths still extract correctly -5. Deep nested safe paths still work - -### Cumulative test surface (2.2.2 final) - -| Suite | Tests | Status | -|---|---|---| -| run_quick.sh | 9 | ✓ | -| test_sdk.sh | 11 | ✓ | -| test_audit.sh | 10 | ✓ | -| test_dedup_props.sh | 12 | ✓ | -| test_path_traversal.sh | 5 | ✓ NEW | -| **zupt total** | **47** | **✓** | -| Format mutation fuzz (ASAN/UBSAN) | 1,000 iters | ✓ | -| Inherited libzuptsdk audit | 42 | ✓ | -| Inherited libzuptsdk RFC + roundtrip | 84 | ✓ | -| Inherited libzuptsdk binding contracts | 57 | ✓ | -| Inherited libzuptsdk Wycheproof | 5 | ✓ | -| Inherited libzuptsdk fuzz | 750,000 iters | ✓ | -| **Combined zupt + SDK** | **265 tests + 751k fuzz** | **✓** | - -### Portability re-verification - -Static portability scan: clean. -- No unaligned pointer casts -- No raw `/` separators (uses `ZUPT_PATH_SEP`) -- No `htonl`/`ntohl`/struct casts (LE helpers throughout) -- No POSIX-only headers without `#ifdef _WIN32` guards - -GCC + `-Wpedantic` build: clean. -Win32 paths verified via `-D_WIN32 -E` synthetic preprocessing. - -### Cumulative bug count across audit sprints - -| Sprint | Bugs found | Severity range | -|---|---|---| -| v2.2.1 (first audit) | 6 | low to high | -| v2.2.2 (second audit) | 4 | low to medium | -| v2.2.2 formal | 4 | low to **high** (Zip Slip path traversal) | -| v2.2.2 sprint 4 | 1 | **critical** (silent extract via arg parser) | -| v2.2.2 god-tier audit | 1 | **critical** (block-swap AEAD) | -| **Total** | **16** | **all fixed and regression-tested** | - -### Open items - -- External independent audit still pending (cost, not engineering) -- Side-channel timing leak testing not performed -- Cross-OS CI (macOS / Windows / FreeBSD runners) not yet wired -- Formal verification beyond Jasmin constant-time primitives (F*, ProVerif) - not pursued - - - -## 2026-05-01 — v2.2.3 release audit (VaptVupt 2.48.2 integration) - -Two independent test passes performed: one on the working tree, a -second on a clean build from the produced source tarball -(`zupt-2.2.3-source.tar.gz`). Both passes identical and clean. - -### Surfaces verified - -| Surface | Test target | Pass 1 | Pass 2 | Notes | -|---|---|---|---|---| -| Quick suite | `make test` | 9 + 11 + 10 + 12 + 5 + 8 + 6 = 61 OK | 61 OK | All `tests/*.sh` | -| Regression | `tests/regression.sh` | 22/22 | 22/22 | T17 fixed (see CHANGELOG) | -| Threaded | `tests/test_threaded.sh` | 14/14 | 14/14 | MT compress/decompress | -| Post-quantum | `tests/test_pq.sh` | 10/10 | 10/10 | `--pq-sdk` and legacy `--pq` | -| VaptVupt unit | `make test-vv` | 11/11 | 11/11 | All modes + format_v2 | -| NIST vectors | `make test-vectors` | 14/14 | 14/14 | XXH64, SHA-256, ML-KEM (incl. internal self-test), X25519, AES, HMAC | -| ASAN/UBSan | `make test-asan` | clean | clean | plain + password + `--pq-sdk`; levels 1, 5, 9 | -| Format mutation fuzz | `make fuzz-format-run` | 1000 iters, 0 crashes | 1000 iters, 0 crashes | ASAN-instrumented binary as victim | -| License audit | `make audit-licenses` | clean | clean | All SPDX correct (AGPL for Zupt, GPL for VaptVupt) | -| GCC strict warnings | `-Wall -Wextra -Wpedantic` | 0 | 0 | C11 strict | -| Disk backup | `zupt disk backup`/`restore` | byte-exact sha256 | — | 5 MB image, all PATTERN markers preserved | - -Cumulative cases passing: **112 across 12 suites**, both passes. - -### Defect found and fixed in this release cycle - -VaptVupt 2.48.2 + `format_v2 = 1` + `VV_MODE_ULTRA_FAST` produces -output the decoder rejects with `VV_ERR_OVERFLOW`. The combination -is **not in VaptVupt's upstream test matrix** -(`vaptvupt-2.48.2/tests/test_zupt_integration.c` exercises -`format_v2` only with `BALANCED` and `EXTREME`). Caught by Zupt's own -`tests/regression.sh` T17 (VaptVupt all levels) before release. - -Workaround in `src/vaptvupt_api.c`: set `opts.format_v2 = 0` for -levels 1–2 (`VV_MODE_ULTRA_FAST`); leave `format_v2 = 1` for levels -3–9. To be reported upstream; once VaptVupt validates the combination -the guard can be lifted. - -### Defect found and fixed in this release cycle (build system) - -The `STALE_OBJS` arch-safety guard in `Makefile` was comparing the -canonical strings `x86-64` (from `file(1)`) against `x86_64` (from -`$(CC) -dumpmachine`) and treating them as different architectures, -causing every `make` invocation to wipe and rebuild every `.o` file -even on a consistent host. Both sides are now normalised through -`tr -d '_-' | tr [:upper:] [:lower:]` so the comparison succeeds on a -same-arch tree and only fires when the tarball really did include -cross-arch objects. - -### Packages produced and verified - -All built from the same source tree, then exercised end-to-end -(encrypted compress + extract + sha256 byte-compare) outside the build -host's normal library search path: - -| Package | File | Size | Roundtrip | -|---|---|---|---| -| Debian/Ubuntu | `zupt_2.2.3_amd64.deb` | 365 KB | encrypted OK | -| RPM | `zupt-2.2.3-1.x86_64.rpm` | 468 KB | encrypted OK | -| AppImage | `zupt-2.2.3-x86_64.AppImage` | 569 KB | encrypted OK (extracted) | -| AppDir tarball | `zupt-2.2.3-x86_64.AppDir.tar.gz` | 377 KB | encrypted OK | -| Generic Linux | `zupt-2.2.3-linux-x86_64.tar.gz` | 430 KB | encrypted OK | -| Source | `zupt-2.2.3-source.tar.gz` | 736 KB | rebuilt + full suite OK | - -All six produce byte-identical output on the test corpus (records.csv -+ 256 KB random binary + hello.txt). diff --git a/BENCHMARKS.md b/BENCHMARKS.md deleted file mode 100644 index d76a2af..0000000 --- a/BENCHMARKS.md +++ /dev/null @@ -1,113 +0,0 @@ -# VaptVupt Benchmarks - -All numbers here are **measured**, not aspirational. Each table states the -machine, the build, and the method. Where VaptVupt loses to a competitor, -the table shows it. - -> **Superseded data note.** The v3.8.0 edition of this file was measured -> on a different machine (Xeon 2.80 GHz, no SHA-NI) against a different -> generation of the synthetic fixtures. Absolute numbers below are not -> comparable to that edition; the codec-stability question is settled by -> the same-input gate table (§1), not by cross-edition comparison. - -## Test environment - -| Property | Value | -|----------|-------| -| CPU | Intel Xeon @ 2.10 GHz | -| Cores used | 1 (single-threaded measurement) | -| Hardware accel present | AES-NI, **SHA-NI**, AVX2 (codec) | -| Build | VaptVupt 4.0.0, `make` defaults, `-O2`, Jasmin AES-NI path active | -| Codec | VaptVupt **2.60.4** (LZ + ANS, canonical BCJ) | -| Method | best of 3 runs, wall clock | - -## 1. Codec ratio gate: 2.53.3-era vs 2.60.4, identical inputs - -Upstream 2.60.4 claims compressed output byte-identical to prior -releases. Verified here by compressing the **same fixture bytes** with -the shipped 3.8.0 binary and the 4.0.0 binary (L9, plain): - -| Fixture | 3.8.0 archive | 4.0.0 archive | Δ | -|---------|--------------:|--------------:|---| -| text | 1 990 322 B | 1 990 322 B | **0.00 %** | -| source | 1 698 907 B | 1 698 907 B | **0.00 %** | -| redundant | 3 344 B | 3 344 B | **0.00 %** | -| binary | 3 119 605 B | 3 356 213 B | +7.58 % — **not comparable**: the 3.8.0 stream is the F-16 *corrupt* output (undecodable by any version); 4.0.0 emits the canonical BCJ stream that actually decodes | - -Gate **holds** everywhere a valid stream exists on both sides. - -## 2. Compression ratio + throughput (plain, level 9, this box's fixtures) - -| Fixture | In (MB) | Ratio | Encode (MB/s) | Decode (MB/s) | -|---------|--------:|------:|--------------:|--------------:| -| text | 10.0 | 5.27 | 2 | 314 | -| binary | 7.5 | 2.34 | 2 | 209 | -| source | 10.0 | 6.17 | 1 | 304 | -| redundant | 10.0 | 3135.69 | 299 | 692 | -| random | 5.0 | 1.00 | 24 | 570 | - -Decode 209–692 MB/s; L9 encode remains 1–2 MB/s on compressible data -(optimal parser) — use lower levels when encode speed matters. - -## 3. SHA-256: scalar vs SHA-NI (measured, same box) - -The v3.2.0 SHA-NI path could only be **estimated** (3–8×) because the -old measurement box lacked the instruction set. Measured now, 256 MiB -single buffer, runtime dispatch vs forced scalar: - -| Path | Throughput | -|------|-----------:| -| scalar C | 204 MB/s | -| SHA-NI | **1184 MB/s** | -| **speedup** | **5.8×** | - -(Independently consistent with libpqvaptvupt 0.6.0's own measurement of -5.9× on its SHA-256.) The estimate label is hereby retired. - -## 4. Encryption overhead (store mode isolates crypto from the codec) - -Per-MB ≈ (t₄₀MB − t₁MB) ÷ 39; KDF ≈ t₁MB − per-MB. - -| Mode | Per-MB crypto | One-time KDF | -|------|--------------:|-------------:| -| plain (no encryption) | 1.94 ms (515 MB/s) | ≈3 ms | -| password — Argon2id (default) | 3.42 ms (**293 MB/s**) | ≈839 ms | -| password — PBKDF2 | 3.58 ms (280 MB/s) | ≈550 ms | -| **pq-box** (`--pq-box`, v4.0.0) | same as plain + MAC path | seal ≈3 ms / open ≈3 ms | - -Readings: -- Encrypted per-block throughput is **~2× the 3.8.0-era figure on a - slower clock** (293 MB/s at 2.10 GHz vs 146 MB/s at 2.80 GHz) — the - HMAC-SHA256 Encrypt-then-MAC second pass now runs on SHA-NI. -- The Argon2id one-time cost (~0.8 s) is memory-hardness working as - designed, not a target for optimization. -- PBKDF2's KDF also benefits from SHA-NI (~550 ms here vs ~1.56 s on the - old non-SHA-NI box) — but Argon2id remains the default for its - memory-hardness, not its speed. -- `--pq-box` adds ~3 ms one-time seal/open for the 32-byte session key - (ML-KEM-768 + X25519 + HKDF); per-block cost is the standard AES-NI + - SHA-NI path. - -## 5. What the product is - -The codec is competitive on decode, not the reason to use VaptVupt -(zstd-19 wins pure ratio). The reason is the combination: post-quantum -hybrid recipient encryption (three modes, newest = HKDF-domain-separated -sealed box), Argon2id by default with a self-describing KDF header, -per-block Encrypt-then-MAC with every security-critical comparison -routed through one audited measured-constant-time primitive, canonical -CBMC-verified BCJ filters, and 16 NIST/RFC known-answer vectors in CI. - -## Reproducing - -```sh -make -./vaptvupt c -l 9 /tmp/a.zupt fixtures/text.dat # ratio/speed -./vaptvupt c -s -p PW /tmp/p.zupt big.dat # crypto overhead -./vaptvupt keygen --box -o k && \ - ./vaptvupt c -s --pq-box k.pub /tmp/b.zupt big.dat # pq-box -make test-vectors && ./test_vectors # NIST/RFC vectors -``` - -Absolute numbers vary by machine; the shape (KDF-dominated password -cost, SHA-NI ≈6× on SHA-256, ~3 ms pq-box envelope) is stable. diff --git a/CHANGELOG.md b/CHANGELOG.md index 6773a76..aa3a317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,51 @@ # VaptVupt Changelog +## [4.1.0] — 2026-07-07 — Source-only build; multithreaded-encryption fix; hardening + +### Source-only build (no vendored binaries) + +- The prebuilt vendored libraries `libzuptsdk.so` and `libpqvaptvupt.so` are + removed from the repository. The default build (`make`) now compiles entirely + from the in-tree C sources with no external library dependency and installs no + shared object. +- The libzuptsdk-backed modes — the Argon2id password KDF, `--pq-sdk`, and + `--pq-box` — are gated behind an opt-in `make WITH_SDK=1` build against the + separately distributed libraries. In the default build they report as + unsupported. The default password KDF is **PBKDF2-SHA256** (600k iterations) + and `--pq` (native **ML-KEM-768 + X25519**) is the built-in PQ mode. +- openSUSE packaging builds source-only: `%build`/`%install` pass `WITH_SDK=0` + and `%files` no longer ships the `.so`. + +### Fixed + +- **Multithreaded encrypted archives were unextractable** on the native AEAD + path. The parallel compress/decompress workers used the non-preface + encrypt/decrypt calls, but the archive's `ZUPT_FLAG_AAD_PREFACE` (F-09) and the + serial path bind the 29-byte frame preface into every block MAC — so each + multithreaded block failed authentication. The workers now bind the preface via + the shared `zupt_serialize_preface_aad_scalars`. Output is byte-identical across + thread counts, and archives interoperate between single- and multithreaded + compress/extract. Also fixes `--kdf pbkdf2 -t N`. + +### Security + +- LZH decoder: bound the raw code-length header against the destination stack + buffers and reject code lengths > 15 (stack overflow + `huff_lut` OOB write on + crafted archives). +- Format parser: overflow-safe bounds in `parse_index` and solid-mode extract + (heap OOB reads via wrapped 64-bit length/offset fields). +- ANS SEQ decoder: reserve a full worst-case sequence (litlen + matchlen) in the + fast path (heap OOB write past the output buffer). +- Require the per-block ENCRYPTED flag on every block of an encrypted archive + (plaintext-injection / authentication bypass). +- Cap the archive-supplied PBKDF2 iteration count (KDF-amplification DoS). +- Non-elidable secret wipe on the SDK crypto path; restored disk images are + created with mode 0600. + +Wire format **v1.6** unchanged; pre-4.1 archives remain readable. + + ## [4.0.0] — 2026-06-10 — Codec 2.60.4 (security), pq-box mode, F-16 disclosure Major release: the vendored codec moves to the canonical **VaptVupt diff --git a/DISTRIBUTION.md b/DISTRIBUTION.md index 30d38b9..0dcc11f 100644 --- a/DISTRIBUTION.md +++ b/DISTRIBUTION.md @@ -1,40 +1,29 @@ # Distributing VaptVupt -This document describes the upstream packaging recipes shipped under -`packaging/` and the path from "local source tree" to "package -installable on every major Linux distribution and macOS." +This document describes the upstream packaging recipes shipped under `packaging/` and the path from a local source tree to an installable package. -Recipes are upstream-maintained but distro-submission-ready. Real -submission to AUR / Debian / Fedora / Homebrew / NixOS is operational -work outside this repository. +Real submission to AUR / Debian / Fedora / Homebrew / NixOS / openSUSE is operational work outside this repository. ## Producing a reproducible source tarball -Every packaging recipe expects an upstream tarball `vaptvupt-VERSION.tar.gz` -produced by the project's `make dist` target. The tarball is -**byte-reproducible**: +Every packaging recipe expects an upstream tarball `vaptvupt-VERSION.tar.gz` produced by the project's `make dist` target. The tarball is byte-reproducible: ```sh make dist -# → /tmp/vaptvupt-2.4.4.tar.gz -# → sha256: 407d20ef03e5bf857195b99e04843ef3b07357416a4115add1e8aaa2007a769f -# → bytes: 813113 +# → /tmp/vaptvupt-4.1.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. +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. The reproducibility properties: - Files sorted by name (deterministic order across filesystems) -- mtime fixed to `SOURCE_DATE_EPOCH` (default `1747699200`; override - via env) +- mtime fixed to `SOURCE_DATE_EPOCH` (default `1747699200`; override via env) - uid/gid pinned to root (0/0) via `--owner=0 --group=0 --numeric-owner` - gzip wrapped with `-9n` (no embedded timestamp or filename) - Source-only — no `.o`, no built binaries, no `.git/` tree -- Includes the vendored `libzuptsdk.so.2.0.0` real file plus its two - symlinks (`libzuptsdk.so`, `libzuptsdk.so.2`) + +The tree is source-only. The default `make` build needs only a C compiler, make, libm, and pthread — no external crypto library, and it installs no `.so`. The optional SDK-backed modes (`--pq-sdk`, `--pq-box`) and the Argon2id KDF are built only with `make WITH_SDK=1` against the separately distributed `libzuptsdk` / `libpqvaptvupt` libraries. To force a specific epoch (for distro release-day pinning): @@ -48,20 +37,17 @@ SOURCE_DATE_EPOCH=1727740800 make dist # 2024-10-01 UTC |-------------------|---------------------------------|----------------| | Arch Linux | `packaging/aur/PKGBUILD` | AUR PKGBUILD | | Debian / Ubuntu | `packaging/debian/` | Source package (`3.0 (quilt)`) | -| Fedora / RHEL | `packaging/rpm/vaptvupt.spec` | RPM .spec | -| macOS | `packaging/homebrew/vaptvupt.rb` | Homebrew formula | +| Fedora / RHEL | `packaging/rpm/vaptvupt.spec` | RPM .spec | +| openSUSE | `packaging/opensuse/` | RPM .spec (OBS) | +| macOS | `packaging/homebrew/vaptvupt.rb`| Homebrew formula | | NixOS / Nix flake | `packaging/nix/flake.nix` | Nix flake | All recipes: - Install the binary to `$PREFIX/bin/vaptvupt` (default `/usr/bin/vaptvupt`) -- Install the vendored `libzuptsdk.so*` triple to `$PREFIX/lib/vaptvupt/` - (the binary uses relative `rpath` so users don't need `LD_LIBRARY_PATH`) - Install manpage to `$PREFIX/share/man/man1/vaptvupt.1.gz` -- Install docs (README, SECURITY, CHANGELOG, AUDIT) to - `$PREFIX/share/doc/vaptvupt/` -- Run the full upstream regression suite (`make test`) during build - when the distro's package guidelines allow check-phase execution +- Install docs (README, SECURITY, CHANGELOG) to `$PREFIX/share/doc/vaptvupt/` +- Run the full upstream regression suite (`make test`) during build when the distro's package guidelines allow check-phase execution ## Arch Linux (AUR) @@ -70,13 +56,13 @@ Maintainer flow: ```sh # 1. Produce the upstream tarball make dist -# → /tmp/vaptvupt-2.4.4.tar.gz +# → /tmp/vaptvupt-4.1.0.tar.gz # 2. Upload to a stable URL (e.g. git.securityops.co releases) # 3. Update packaging/aur/PKGBUILD: -# - Set pkgver=2.4.4 -# - Set sha256sums=("$(sha256sum /tmp/vaptvupt-2.4.4.tar.gz | awk '{print $1}')") +# - Set pkgver=4.1.0 +# - Set sha256sums=("$(sha256sum /tmp/vaptvupt-4.1.0.tar.gz | awk '{print $1}')") # 4. Generate .SRCINFO cd packaging/aur && makepkg --printsrcinfo > .SRCINFO @@ -87,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 "v2.4.4" && git push +cd aur-vaptvupt && git add -A && git commit -m "v4.1.0" && git push ``` User install: @@ -96,10 +82,9 @@ User install: yay -S vaptvupt # or paru, pikaur, etc. ``` -## Shell completions (v2.4.7+) +## Shell completions -`make install` automatically installs Bash, zsh, and fish completion -files alongside the binary and manpage: +`make install` automatically installs Bash, zsh, and fish completion files alongside the binary and manpage: | Shell | Path | |---|---| @@ -107,10 +92,7 @@ files alongside the binary and manpage: | zsh | `$PREFIX/share/zsh/site-functions/_vaptvupt` | | fish | `$PREFIX/share/fish/vendor_completions.d/vaptvupt.fish` | -The source files live under `completions/` in the project tree. -Distros that prefer a different install location should override -the relevant paths in their `make install` invocation; the -underlying recipe is straightforward. +The source files live under `completions/` in the project tree. Distros that prefer a different install location should override the relevant paths in their `make install` invocation. For per-user installation without root: @@ -125,24 +107,20 @@ cp completions/_vaptvupt ~/.zsh/completion/_vaptvupt cp completions/vaptvupt.fish ~/.config/fish/completions/vaptvupt.fish ``` -Completions cover every CLI flag the binary actually parses -(`--kdf`, `--comment`, `--comment-file`, `--pq-sdk`, `--dedup`, -etc.) and are validated on every CI run via -`tests/test_completions_manpage.sh`. +Completions cover every CLI flag the binary actually parses (`--kdf`, `--comment`, `--comment-file`, `--pq`, `--dedup`, etc.) and are validated on every CI run via `tests/test_completions_manpage.sh`. ## Debian / Ubuntu -The `packaging/debian/` tree is a Debian source-package layout. -Maintainer flow: +The `packaging/debian/` tree is a Debian source-package layout. Maintainer flow: ```sh # 1. Produce the upstream tarball with the standard Debian # orig.tar.gz naming convention: make dist -cp /tmp/vaptvupt-2.4.4.tar.gz /tmp/vaptvupt_2.4.4.orig.tar.gz +cp /tmp/vaptvupt-4.1.0.tar.gz /tmp/vaptvupt_4.1.0.orig.tar.gz # 2. Unpack and overlay the debian/ tree: -cd /tmp && tar xzf vaptvupt_2.4.4.orig.tar.gz && cd vaptvupt-2.4.4 +cd /tmp && tar xzf vaptvupt_4.1.0.orig.tar.gz && cd vaptvupt-4.1.0 cp -a /path/to/vaptvupt/packaging/debian ./debian # 3. Build the source package: @@ -150,7 +128,7 @@ dpkg-buildpackage -S -us -uc # source-only dpkg-buildpackage -b -us -uc # binary # 4. Lint: -lintian vaptvupt_2.4.4-1_*.deb +lintian vaptvupt_4.1.0-1_*.deb # 5. Submit via the standard Debian mentors process: # https://mentors.debian.net/intro-maintainers/ @@ -167,7 +145,7 @@ sudo apt install vaptvupt ```sh # 1. Produce the tarball make dist -cp /tmp/vaptvupt-2.4.4.tar.gz ~/rpmbuild/SOURCES/ +cp /tmp/vaptvupt-4.1.0.tar.gz ~/rpmbuild/SOURCES/ # 2. Drop the .spec into the SPECS directory: cp packaging/rpm/vaptvupt.spec ~/rpmbuild/SPECS/ @@ -176,7 +154,7 @@ cp packaging/rpm/vaptvupt.spec ~/rpmbuild/SPECS/ cd ~/rpmbuild && rpmbuild -ba SPECS/vaptvupt.spec # 4. Lint: -rpmlint RPMS/x86_64/vaptvupt-2.4.4-1.fc*.rpm +rpmlint RPMS/x86_64/vaptvupt-4.1.0-1.fc*.rpm # 5. Submit via the Fedora new-package review process: # https://docs.fedoraproject.org/en-US/package-maintainers/Package_Review_Process/ @@ -190,6 +168,31 @@ sudo dnf install vaptvupt # Fedora sudo dnf install epel-release vaptvupt # RHEL/CentOS via EPEL ``` +## openSUSE + +The `packaging/opensuse/` tree carries an RPM `.spec` suited to the Open Build Service (OBS). + +```sh +# 1. Produce the tarball +make dist + +# 2. In an OBS package checkout (osc), stage the sources and spec: +cp /tmp/vaptvupt-4.1.0.tar.gz . +cp /path/to/vaptvupt/packaging/opensuse/vaptvupt.spec . + +# 3. Build locally against a target repository: +osc build openSUSE_Tumbleweed x86_64 + +# 4. Commit to OBS once the build and check phase pass: +osc addremove && osc commit +``` + +User install (after the package lands in a distribution or OBS repository): + +```sh +sudo zypper install vaptvupt +``` + ## macOS (Homebrew) ```sh @@ -227,12 +230,12 @@ nix build github:cristiancmoises/vaptvupt#vaptvupt nix run github:cristiancmoises/vaptvupt#vaptvupt -- version # 2. To consume from another flake: -# inputs.zupt.url = "github:cristiancmoises/vaptvupt?ref=v2.4.4"; -# packages.x86_64-linux.default = inputs.zupt.packages.x86_64-linux.zupt; +# inputs.vaptvupt.url = "github:cristiancmoises/vaptvupt?ref=v4.1.0"; +# packages.x86_64-linux.default = inputs.vaptvupt.packages.x86_64-linux.vaptvupt; # 3. To submit to nixpkgs (https://github.com/NixOS/nixpkgs): # - Adapt packaging/nix/flake.nix's `vaptvupt` derivation into a -# pkgs/by-name/zu/vaptvupt/package.nix using fetchurl and a hash. +# pkgs/by-name/va/vaptvupt/package.nix using fetchurl and a hash. # - Follow the nixpkgs contribution guide: # https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md ``` @@ -241,28 +244,15 @@ nix run github:cristiancmoises/vaptvupt#vaptvupt -- version Before pushing any recipe to a distro repository: -- [ ] `make dist` produces a reproducible tarball (verified by - `tests/test_dist_reproducible.sh` on every `make test`) +- [ ] `make dist` produces a reproducible tarball (verified by `tests/test_dist_reproducible.sh` on every `make test`) - [ ] The tarball is uploaded to a stable, immutable URL -- [ ] The recipe's checksum field is updated to match - `sha256sum /tmp/vaptvupt-VERSION.tar.gz` +- [ ] The recipe's checksum field is updated to match `sha256sum /tmp/vaptvupt-VERSION.tar.gz` - [ ] The recipe builds and tests pass in a clean chroot/container - [ ] The CHANGELOG mentions distro-relevant changes since the last release -- [ ] The license metadata is correct (AGPL-3.0-or-later for VaptVupt core; - GPL-3.0-or-later for the vendored VaptVupt codec) +- [ ] The license metadata is correct (AGPL-3.0-or-later for VaptVupt core; GPL-3.0-or-later for the vendored VaptVupt codec) ## Security posture for downstream -Every packaging recipe runs `make test` during build (`check()` for AUR, -`override_dh_auto_test` for Debian, `%check` for RPM, `checkPhase` for -Nix, `test` block for Homebrew). The suite includes: +Every packaging recipe runs `make test` during build (`check()` for AUR, `override_dh_auto_test` for Debian, `%check` for RPM and openSUSE, `checkPhase` for Nix, `test` block for Homebrew). The test suite runs in each recipe's check phase, including the tamper/integrity regressions and the `make dist` byte-identical reproducibility check. -- **F-06**: 2 000 HMAC tamper trials, 0 silent accepts required -- **F-08**: top-MAC header/footer integrity-trailer regression -- **F-09**: 1 827-position exhaustive byte sweep on PQ-SDK archive, - 0 silent accepts required -- **F-10..F-12**: KDF default, auth-fail message, encrypted comments -- **dist reproducibility**: `make dist` byte-identical across two runs - -A build that doesn't pass `make test` will fail at distro check time — -the recipes don't paper over regressions. +A build that doesn't pass `make test` will fail at distro check time — the recipes don't paper over regressions. diff --git a/INSTALL.md b/INSTALL.md index 5d23147..c817a66 100644 --- a/INSTALL.md +++ b/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 (>= 2.2.3); porém: +vaptvupt-gui depende de vaptvupt (>= 4.1.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 2.2.3** or newer +- The **vaptvupt CLI 4.1.0** or newer ## The fastest fix — one command (Linux Mint, Ubuntu, Debian) @@ -22,7 +22,7 @@ sudo bash install-zupt-gui.sh ``` This script auto-detects your distribution and installs everything in -the right order. Done. +the right order. ## Manual fix — three commands (if you prefer) @@ -33,8 +33,8 @@ the right order. Done. sudo apt update sudo apt install -y python3-pyqt6 -# 2. Upgrade vaptvupt CLI to 4.0.0 -sudo dpkg -i vaptvupt_4.0.0_amd64.deb +# 2. Upgrade vaptvupt CLI to 4.1.0 +sudo dpkg -i vaptvupt_4.1.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.0.0-1.x86_64.rpm vaptvupt-gui-1.3.0-1.noarch.rpm +sudo dnf install -y vaptvupt-4.1.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`) @@ -79,8 +79,7 @@ cd vaptvupt-gui.AppDir ./AppRun ``` -The AppImage still needs Python 3 + Qt6 binding on the host (those are -universally available on every Linux distribution since 2022). For a +The AppImage still needs Python 3 + Qt6 binding on the host. For a fully standalone executable with no Python dependency, use a future PyInstaller-built version (not in this release). @@ -91,28 +90,25 @@ auto-detects whichever is installed). These are bindings to the Qt 6 graphical toolkit — they're how the GUI draws windows, buttons, and dialogs. -PyQt6 is in the default repositories of every major Linux distribution -since 2022, so installing it is one apt/dnf/zypper/pacman command away. -We don't bundle Qt6 inside the deb because: +PyQt6 is in the default repositories of major Linux distributions, so +installing it is one apt/dnf/zypper/pacman command away. We don't bundle +Qt6 inside the deb because: - It's already on most modern systems - Bundling would make the deb 80 MB+ instead of 35 KB - Distribution-managed Qt gets security updates automatically -## Why does the GUI need vaptvupt 2.2.3? +## Why does the GUI need vaptvupt 4.1.0? -The GUI calls `vaptvupt --pq-sdk` and `vaptvupt keygen --sdk` for state-of-the-art -post-quantum encryption (HKDF-SHA3 hybrid combiner, key commitment, HPKE -binding, Argon2id). These flags didn't exist in 2.1.7 — they were added -in 2.2.0. - -If you have an older vaptvupt installed, the GUI's compress/extract will fail -with "unknown option --pq-sdk". +The GUI calls `vaptvupt --pq` and `vaptvupt keygen` for native +post-quantum encryption (ML-KEM-768 + X25519, in-tree implementation). +Older CLI versions lack these flags, so the GUI's compress/extract will +fail against them. ## After installing — verify ```bash -vaptvupt version # should show: 2.2.3 +vaptvupt version # should show: 4.1.0 vaptvupt-gui # should launch the GUI window ``` @@ -150,49 +146,40 @@ at https://git.securityops.co/cristiancmoises/vaptvupt/issues with: ## Building from source If you want to build VaptVupt from the source tarball instead of installing -the pre-built `.deb` / `.rpm` packages, you'll need: +the pre-built `.deb` / `.rpm` packages, you'll need only a C compiler and +make. The default build has NO external crypto dependency and installs no +shared library. -### Build dependencies +### Build dependencies (default build) | Component | Why needed | |---|---| | `gcc` ≥ 7 or `clang` ≥ 10 | C11 compiler | | `make` | build driver | -| `libargon2-dev` | Argon2id KDF | -| `libssl-dev` | OpenSSL libcrypto (AES, SHA-256) | -| **`libzuptsdk-dev` 2.0.0+** | VaptVupt's cryptographic SDK | +| libm, pthread | math and threading (part of the standard C library/toolchain) | -The `libzuptsdk-dev` package is a separate sister project — it contains -the post-quantum hybrid cryptography that VaptVupt uses on its `--pq-sdk` -path. Both libraries are by the same author (Cristian Cezar Moisés) but -are distributed as separate source/binary packages so each can evolve -on its own release cadence. +The default build uses PBKDF2-SHA256 (600k iterations) for password KDF +and the in-tree native `--pq` mode (ML-KEM-768 + X25519) for post-quantum +encryption. No `libzuptsdk`, no OpenSSL, no libargon2 is required. ### Install build dependencies (Debian/Ubuntu/Mint) ```bash -sudo apt install build-essential libargon2-dev libssl-dev - -# Then install libzuptsdk from its package: -sudo apt install ./libzuptsdk2_2.0.0_amd64.deb \ - ./libzuptsdk-dev_2.0.0_amd64.deb +sudo apt install build-essential ``` ### Install build dependencies (Fedora/RHEL/openSUSE) ```bash -sudo dnf install gcc make libargon2-devel openssl-devel -# libzuptsdk from its SRPM: -tar -xzf libzuptsdk-2.0.0.srpm.tar.gz -rpmbuild -bb SPECS/libzuptsdk.spec -sudo rpm -i ~/rpmbuild/RPMS/x86_64/libzuptsdk-2.0.0-*.rpm +sudo dnf install gcc make # Fedora/RHEL +sudo zypper install gcc make # openSUSE ``` ### Build VaptVupt itself ```bash -tar -xzf vaptvupt-2.2.3-source.tar.gz -cd vaptvupt-2.2.3 +tar -xzf vaptvupt-4.1.0-source.tar.gz +cd vaptvupt-4.1.0 make # build the `./vaptvupt` binary sudo make install # install to /usr/local/bin (override with PREFIX=/usr) @@ -210,9 +197,9 @@ The `make` step takes 10-30 seconds. The build emits the binary as make test ``` -61 tests pass: roundtrip, audit, multi-file, cross-block, dedup property, -path-traversal, argument-order, block-swap regression. Each suite reports -its own pass/fail count. +Covers roundtrip, multi-file, cross-block, dedup property, path-traversal, +argument-order, and block-swap regression. Each suite reports its own +pass/fail count. ### Cross-compilation @@ -228,15 +215,18 @@ The Makefile auto-detects target architecture via `$(CC) -dumpmachine` and selects the appropriate SIMD flags (NEON on AArch64, SSE4/AVX2 on x86_64). -### Static linking against libzuptsdk +### Optional: WITH_SDK=1 build -If you want a fully self-contained `vaptvupt` binary (no `libzuptsdk.so.2` -runtime dependency), you can link against the static library: +The SDK-backed modes — `--pq-sdk`, `--pq-box` (sealed-box), and the +Argon2id KDF — are optional. They are not in the default build and require +building against the separately distributed `libzuptsdk` / `libpqvaptvupt` +libraries: ```bash -make LDLIBS='-l:libzuptsdk.a -lcrypto -largon2' +make WITH_SDK=1 ``` -This produces a binary that doesn't need `libzuptsdk2` installed at -runtime — useful for containers, embedded systems, or distribution to -machines without package management. +This build additionally needs the SDK development package and its runtime +dependencies (OpenSSL libcrypto, libargon2), which ship with the SDK +distribution. Without `WITH_SDK=1`, the `--pq-sdk` and `--pq-box` flags are +unavailable; use the native `--pq` mode instead. diff --git a/Makefile b/Makefile index 0f12439..7395e4e 100644 --- a/Makefile +++ b/Makefile @@ -63,10 +63,21 @@ ZUPT_SOURCES = src/zupt_main.c src/zupt_format.c src/zupt_lz.c src/zupt_lzh.c \ src/zupt_x25519.c src/zupt_mlkem.c src/zupt_cpuid.c src/zupt_mlock.c \ src/zupt_filetype.c src/zupt_disk.c src/zupt_dedup.c -# --- libzuptsdk linkage (vendored) --- +# --- Optional vendored libraries (libzuptsdk + libpqvaptvupt) --- +# +# These are PREBUILT shared libraries shipped only as binaries (no source), so +# they are NOT part of the source tree and a distro/source build must not need +# them. WITH_SDK is therefore OFF by default: the tool builds entirely from the +# in-tree C sources, using native crypto (PBKDF2-SHA256 password KDF and native +# ML-KEM-768 + X25519 via --pq). The SDK-backed modes (--pq-sdk, --pq-box, and +# the Argon2id password KDF) compile to "unsupported" stubs in that case. +# +# Set WITH_SDK=1 (with the vendored libs present under vendor/) to enable them. +WITH_SDK ?= 0 +ifeq ($(WITH_SDK),1) ZUPTSDK_DIR ?= vendor/zuptsdk ZUPTSDK_ABS := $(abspath $(ZUPTSDK_DIR)) -CFLAGS += -I$(ZUPTSDK_DIR)/include +CFLAGS += -DZUPT_WITH_SDK -I$(ZUPTSDK_DIR)/include PQVV_DIR ?= vendor/pqvaptvupt CFLAGS += -I$(PQVV_DIR)/include LDFLAGS += -L$(ZUPTSDK_DIR) -Wl,-rpath,$(ZUPTSDK_ABS) -Wl,-rpath,'$$ORIGIN/$(ZUPTSDK_DIR)' @@ -77,6 +88,7 @@ LDFLAGS += -L$(PQVV_DIR) -Wl,-rpath,$(PQVV_ABS) -Wl,-rpath,'$$ORIGIN/$(PQVV_DIR) # binary a matching relative rpath so `make install` is self-contained. LDFLAGS += -Wl,-rpath,'$$ORIGIN/../lib/vaptvupt' LDLIBS += -lpqvaptvupt +endif # --- VAPTVUPT: VaptVupt codec sources (GPL-3.0-or-later; tool is AGPL-3.0-or-later) --- VV_SOURCES = src/vv_encoder.c src/vv_decoder.c src/vv_ans.c src/vv_bcj.c \ @@ -313,8 +325,11 @@ install: $(TARGET) echo "Installed: $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/$(TARGET).fish"; \ fi - # Vendored runtime libraries (NEEDED by the binary): libzuptsdk - # (password KDF + --pq-sdk) and libpqvaptvupt (--pq-box, v4.0.0+). + # Vendored runtime libraries — installed ONLY for a WITH_SDK=1 build. In the + # default source-only build the binary links no external library and there is + # nothing to install here (the vendored .so are prebuilt binaries kept out of + # the source tree). +ifeq ($(WITH_SDK),1) $(Q)mkdir -p $(DESTDIR)$(PREFIX)/lib/vaptvupt $(Q)install -m 755 vendor/zuptsdk/libzuptsdk.so.2.0.0 $(DESTDIR)$(PREFIX)/lib/vaptvupt/libzuptsdk.so.2.0.0 $(Q)ln -sf libzuptsdk.so.2.0.0 $(DESTDIR)$(PREFIX)/lib/vaptvupt/libzuptsdk.so.2 @@ -322,6 +337,7 @@ install: $(TARGET) $(Q)install -m 755 vendor/pqvaptvupt/libpqvaptvupt.so.0.6.0 $(DESTDIR)$(PREFIX)/lib/vaptvupt/libpqvaptvupt.so.0.6.0 $(Q)ln -sf libpqvaptvupt.so.0.6.0 $(DESTDIR)$(PREFIX)/lib/vaptvupt/libpqvaptvupt.so.0 $(Q)ln -sf libpqvaptvupt.so.0.6.0 $(DESTDIR)$(PREFIX)/lib/vaptvupt/libpqvaptvupt.so +endif @echo "Installed: $(DESTDIR)$(BINDIR)/$(TARGET) (legacy: $(DESTDIR)$(BINDIR)/$(LEGACY_LINK) -> $(TARGET))" @@ -507,10 +523,9 @@ test-vv: tests/test_vaptvupt.c $(HEADERS) $(Q)./test_vaptvupt test-asan: $(SOURCES) $(HEADERS) $(JAZZ_O) - $(Q)$(CC) -Wall -Wextra -std=c11 -Iinclude -Isrc -I$(ZUPTSDK_DIR)/include \ - -fsanitize=address,undefined -g -O1 \ - $(VV_SIMD_FLAGS) $(SHANI_FLAGS) -I$(PQVV_DIR)/include -L$(ZUPTSDK_DIR) -Wl,-rpath,$(ZUPTSDK_ABS) \ - $(SOURCES) $(JAZZ_O) -o zupt_asan -lzuptsdk -L$(PQVV_DIR) -Wl,-rpath,$(PQVV_ABS) $(LDLIBS) + $(Q)$(CC) $(CFLAGS) -fsanitize=address,undefined -g -O1 \ + $(VV_SIMD_FLAGS) $(SHANI_FLAGS) $(LDFLAGS) \ + $(SOURCES) $(JAZZ_O) -o zupt_asan $(LDLIBS) @echo "ASAN build: ./zupt_asan" # Build the format-parser fuzz harness. Runs against ./zupt_asan to catch diff --git a/README.md b/README.md index 9b781a8..4bac1fc 100644 --- a/README.md +++ b/README.md @@ -3,65 +3,99 @@ # VaptVupt -**Compress everything. Trust nothing. Encrypt always.** +Backup compression with hardware-adaptive codec selection, AES-256 +authenticated encryption, post-quantum key encapsulation, and full-disk +backup. Pure C11, ~13,000 lines. Builds and runs on x86_64, aarch64, +armhf, ppc64le, s390x, and riscv64. -![Build](https://img.shields.io/badge/build-passing-brightgreen) -![License](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue) -![Version](https://img.shields.io/badge/version-4.0.0-brightgreen) -![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20macOS%20%7C%20Windows-lightgrey) +License: AGPL-3.0-or-later (dual-licensed AGPL + commercial). > **Renamed from "Zupt" in v3.0.0** because of a prior INPI Brasil > trademark registration on the name "Zupt" for unrelated software. > The `.zupt` archive extension and `ZUPT` header magic bytes are -> unchanged — v2.x and v3.0.0 archives are bidirectionally compatible. -> The `zupt` command is preserved as a symlink to `vaptvupt` for one -> major version cycle. +> unchanged — v2.x and v3.0.0 archives remain compatible. The `zupt` +> command is preserved as a symlink to `vaptvupt` for one major version +> cycle. -## What's new in 4.0.0 +## What's new in 4.1.0 -- **Codec security release** — vendored codec upgraded to VaptVupt 2.60.4, fixing a high-severity OOB heap write in the AVX2 decode fast path (reachable on valid streams with exact-size output buffers). 80 new exact-size decode regression cases under ASan. -- **`--pq-box` sealed-box mode** — third post-quantum recipient mode via vendored libpqvaptvupt 0.6.0: ML-KEM-768 + X25519 through an HKDF-SHA256 domain-separated combiner. `vaptvupt keygen --box` writes magic-tagged keypairs that reject key-type confusion. -- **F-16 disclosure and fix** — archives created by ≤ 3.8.0 at `-l 8`/`-l 9` containing x86/ELF/PE executables may be unreadable by *any* version (write-time defect in the old in-tree BCJ encoder). Re-create them with 4.0.0; details below and in [CHANGELOG.md](CHANGELOG.md). -- **SHA-NI measured** — 5.8× over scalar SHA-256 (204 → 1184 MB/s); encrypted per-block throughput now 293 MB/s. +- **Source-only tree.** The prebuilt vendored libraries `libzuptsdk.so` + and `libpqvaptvupt.so` have been removed. The default `make` needs only + a C compiler and make (plus libm/pthread) — no external crypto library — + and installs no `.so`. +- **Native `--pq` is the default post-quantum mode.** ML-KEM-768 + X25519 + hybrid KEM, in-tree C implementation, available in the default build. +- **SDK-backed modes are optional.** `--pq-sdk`, `--pq-box`, and the + Argon2id KDF are only available in an upstream `make WITH_SDK=1` build + linked against the separately distributed `libzuptsdk`/`libpqvaptvupt`. +- **Wire/on-disk format is v1.6, unchanged.** Archives created by 4.0.0 + are read and written identically. -Binaries for the CLI (4.0.0) and GUI (1.3.0) are on the -[release page](https://git.securityops.co/cristiancmoises/vaptvupt/releases/tag/v4.0.0). +> **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.1.0 and verify extraction before deleting source +> data. Details in [CHANGELOG.md](CHANGELOG.md). -Backup compression with hardware-adaptive codec selection, AES-256 -authenticated encryption, post-quantum key encapsulation, and -full-disk backup. Pure C11, zero dependencies, ~13,000 lines. Builds -and runs on x86_64, aarch64, armhf, ppc64le, s390x, and riscv64. +Binaries for the CLI (4.1.0) and GUI (1.3.0) are on the +[release page](https://git.securityops.co/cristiancmoises/vaptvupt/releases/tag/v4.1.0). --- -## Why VaptVupt +## Features -- **Hardware-adaptive codec** — auto-detects AVX2/NEON at runtime and selects the best codec: VaptVupt (LZ77 + tANS + SIMD decode) on capable hardware, VaptVupt-LZHP on everything else. Override with `--vv` or `--lzhp`. -- **Post-quantum encryption** — `--pq` mode uses ML-KEM-768 + X25519 hybrid KEM (same approach as Signal and iMessage). Protects against "harvest now, decrypt later" quantum attacks. -- **AES-NI hardware acceleration** — AES-256-CTR via Jasmin-verified assembly with 4-block interleaved pipeline. Safe AVX detection with OSXSAVE/XCR0 validation — no SIGILL on any CPU. Falls back to C table-based AES on unsupported hardware. -- **SHA-NI hardware acceleration** — HMAC-SHA256 (the Encrypt-then-MAC second pass) and PBKDF2 use an Intel SHA-NI compression path (`SHA256RNDS2`/`MSG1`/`MSG2`) when the CPU supports it (Intel Goldmont+/Ice Lake+, AMD Zen+), selected at runtime via CPUID. **measured 5.8×** over the scalar path (204 → 1184 MB/s, 256 MiB, Xeon 2.10 GHz) *and* constant-time by construction. Bit-identical output; scalar C fallback elsewhere (incl. aarch64). `vaptvupt version` prints the live acceleration set for your CPU. -- **Incremental HMAC** — the per-block MAC streams its segments through an incremental HMAC-SHA256 (key prefix folded once per keyring) instead of copying each block's ciphertext into a temporary buffer. Removes a per-block heap allocation and full-payload copy on both encrypt and decrypt, with a byte-for-byte identical MAC (RFC 2104). -- **Multi-threaded** — Compression and decompression both parallelized. `-t 0` auto-detects cores. -- **Full-disk backup** — `vaptvupt disk backup` clones entire disks or partitions in one command. Sparse block detection skips zero regions, real-time progress bar, all encryption modes supported. Restore with byte-for-byte verification via per-block XXH64 checksums. -- **Encrypted backups in one command** — `vaptvupt compress -p changeme backup.zupt ~/data/` — AES-256 + HMAC-SHA256, file names hidden. -- **Per-block integrity** — XXH64 checksum + HMAC-SHA256 per block. Wrong password rejected instantly. -- **Self-describing KDF** — password archives record their key-derivation profile in the (authenticated) header, so an archive always carries the parameters needed to open it years later. Unknown profiles are refused fail-closed rather than mis-derived. Argon2id is the default; PBKDF2 (600K iter) via `--kdf pbkdf2`. -- **Measured constant-time comparisons** — every security-critical comparison (HMAC tag, archive-integrity trailer, and the ML-KEM-768 decapsulation implicit-rejection check) routes through a single audited primitive (`zupt_ct_memeq`, branch-free, volatile accumulator, length-independent) verified by a dudect-style Welch t-test in CI, not just annotated. Its data-dependent timing signal measures ~1% of a leaky-`memcmp` control in the same environment; a reintroduced early-return or inline loop fails the test (timing + source-routing guard). -- **Sealed-box PQ recipients (`--pq-box`, v4.0.0)** — third post-quantum mode via vendored libpqvaptvupt: ML-KEM-768 + X25519 combined through HKDF-SHA256 with domain separation ("pqvv-seal-v1"), AES-256-CTR + HMAC-SHA256 EtM in the box, magic-tagged keypair files that reject key-type confusion. Legacy `--pq` and `--pq-sdk` remain readable. -- **Formally verified crypto** — 5 Jasmin assembly functions with constant-time proofs. 19 ACSL-annotated functions for Frama-C memory safety analysis. -- **Multi-architecture** — builds on x86_64, aarch64, armhf, ppc64le, s390x, riscv64. Jasmin CT crypto on x86_64, C fallback everywhere else. Any archive decompresses on any architecture. -- **Zero dependencies** — ML-KEM, X25519, Keccak, SHA-256, AES-256, HMAC, PBKDF2, VaptVupt codec — all pure C11. Builds with `gcc` or `cl` alone. +- **Hardware-adaptive codec** — auto-detects AVX2/NEON at runtime and + selects the codec: VaptVupt (LZ77 + tANS + SIMD decode) on capable + hardware, VaptVupt-LZHP on everything else. Override with `--vv` or + `--lzhp`. +- **Post-quantum encryption** — `--pq` uses ML-KEM-768 + X25519 hybrid + KEM (the approach used by Signal and iMessage), protecting against + "harvest now, decrypt later" attacks. In-tree, available in the default + build. +- **AES-NI acceleration** — AES-256-CTR via Jasmin-verified assembly with + a 4-block interleaved pipeline. AVX detection validates OSXSAVE/XCR0 (no + SIGILL). Falls back to C table-based AES on unsupported hardware. +- **SHA-NI acceleration** — HMAC-SHA256 (the Encrypt-then-MAC pass) and + PBKDF2 use the Intel SHA-NI compression path when the CPU supports it + (Intel Goldmont+/Ice Lake+, AMD Zen+), selected at runtime via CPUID. + Bit-identical output; scalar C fallback elsewhere. `vaptvupt version` + prints the acceleration set for your CPU. +- **Incremental HMAC** — the per-block MAC streams its segments through an + incremental HMAC-SHA256 instead of copying each block's ciphertext into + a temporary buffer, removing a per-block heap allocation and full-payload + copy on encrypt and decrypt with a byte-for-byte identical MAC (RFC 2104). +- **Multi-threaded** — compression and decompression both parallelized. + `-t 0` auto-detects cores. +- **Full-disk backup** — `vaptvupt disk backup` clones disks or partitions + in one command. Sparse block detection skips zero regions; all encryption + modes supported; restore verifies per-block XXH64 checksums. +- **Per-block integrity** — XXH64 checksum + HMAC-SHA256 per block. Wrong + password rejected immediately. +- **Self-describing KDF** — password archives record their key-derivation + profile in the authenticated header, so an archive carries the parameters + needed to open it later. Unknown profiles are refused fail-closed rather + than mis-derived. Default is PBKDF2-SHA256 (600K iterations); Argon2id is + available in a `WITH_SDK=1` build. +- **Constant-time comparisons** — every security-critical comparison (HMAC + tag, archive-integrity trailer, ML-KEM-768 implicit-rejection check) + routes through a single primitive (`zupt_ct_memeq`, branch-free, volatile + accumulator, length-independent), checked by a dudect-style Welch t-test + in CI. +- **Formally verified crypto** — 5 Jasmin assembly functions with + constant-time proofs; 19 ACSL-annotated functions for Frama-C memory + safety analysis. +- **Multi-architecture** — builds on x86_64, aarch64, armhf, ppc64le, + s390x, riscv64. Jasmin CT crypto on x86_64, C fallback everywhere else. + Any archive decompresses on any architecture. +- **No external dependencies (default build)** — ML-KEM, X25519, Keccak, + SHA-256, AES-256, HMAC, PBKDF2 and the VaptVupt codec are all pure C11. + Builds with `gcc` or `cl` alone. --- ## Quick Start -### Fast installation -``` -curl -fsSL https://short.securityops.co/vaptvupt | bash -``` - -### Build & Install +### Build & install ``` git clone https://git.securityops.co/cristiancmoises/vaptvupt.git && \ cd vaptvupt && \ @@ -69,20 +103,26 @@ make && \ sudo make install ``` +The default build needs only a C compiler and `make` (plus libm/pthread). +`make WITH_SDK=1` additionally links the separately distributed +`libzuptsdk`/`libpqvaptvupt` to enable `--pq-sdk`, `--pq-box`, and the +Argon2id KDF. + ### Pre-built packages -All assets are published on the [v4.0.0 release page](https://git.securityops.co/cristiancmoises/vaptvupt/releases/tag/v4.0.0) +Assets are published on the +[v4.1.0 release page](https://git.securityops.co/cristiancmoises/vaptvupt/releases/tag/v4.1.0) and verifiable against the published `SHA256SUMS.txt`. -**Command-line tool (`vaptvupt` 4.0.0):** +**Command-line tool (`vaptvupt` 4.1.0):** | Format | File | Distros | |---|---|---| -| Debian/Ubuntu | `vaptvupt_4.0.0_amd64.deb` | Debian 11+, Ubuntu 22.04+, Mint 21+ | -| RPM | `vaptvupt-4.0.0-1.x86_64.rpm` | Fedora 38+, RHEL 9+, openSUSE, AlmaLinux, Rocky, and other RPM-based distributions | -| AppDir tarball | `vaptvupt-4.0.0-x86_64.AppDir.tar.gz` | Any glibc 2.28+ (extract & run, no FUSE) | -| Source tarball | `vaptvupt-4.0.0.tar.gz` | Build from source on any platform | -| openSUSE OBS | `vaptvupt-4.0.0-opensuse-obs.tar.gz` | Open Build Service source bundle | +| Debian/Ubuntu | `vaptvupt_4.1.0_amd64.deb` | Debian 11+, Ubuntu 22.04+, Mint 21+ | +| RPM | `vaptvupt-4.1.0-1.x86_64.rpm` | Fedora 38+, RHEL 9+, openSUSE, AlmaLinux, Rocky, other RPM-based distributions | +| AppDir tarball | `vaptvupt-4.1.0-x86_64.AppDir.tar.gz` | Any glibc 2.28+ (extract & run, no FUSE) | +| Source tarball | `vaptvupt-4.1.0.tar.gz` | Build from source on any platform | +| openSUSE OBS | `vaptvupt-4.1.0-opensuse-obs.tar.gz` | Open Build Service source bundle | **Graphical front-end (`vaptvupt-gui` 1.3.0):** @@ -98,19 +138,19 @@ and verifiable against the published `SHA256SUMS.txt`. sha256sum -c SHA256SUMS.txt # Debian / Ubuntu / Mint -sudo dpkg -i vaptvupt_4.0.0_amd64.deb +sudo dpkg -i vaptvupt_4.1.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.0.0-1.x86_64.rpm +sudo rpm -i vaptvupt-4.1.0-1.x86_64.rpm # or -sudo dnf install ./vaptvupt-4.0.0-1.x86_64.rpm +sudo dnf install ./vaptvupt-4.1.0-1.x86_64.rpm # AppDir tarball (no install, no FUSE required) -tar xzf vaptvupt-4.0.0-x86_64.AppDir.tar.gz -./vaptvupt-4.0.0-x86_64.AppDir/AppRun --help +tar xzf vaptvupt-4.1.0-x86_64.AppDir.tar.gz +./vaptvupt-4.1.0-x86_64.AppDir/AppRun --help -# GUI AppImage (single executable, runs anywhere) +# GUI AppImage (single executable) chmod +x VaptVupt-GUI-1.3.0-x86_64.AppImage ./VaptVupt-GUI-1.3.0-x86_64.AppImage ``` @@ -118,16 +158,16 @@ chmod +x VaptVupt-GUI-1.3.0-x86_64.AppImage ### Building from SRPM (Fedora / RHEL / RPM-based distributions) ```bash -tar xzf vaptvupt-4.0.0.srpm.tar.gz +tar xzf vaptvupt-4.1.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.0.0-1.*.rpm +sudo rpm -i RPMS/x86_64/vaptvupt-4.1.0-1.*.rpm ``` ### Basic usage ```bash -# Compress a directory (auto-selects best codec for your hardware) +# Compress a directory (auto-selects codec for your hardware) vaptvupt compress backup.zupt ~/Documents/ # Compress at a specific level (1=fast, 5=balanced, 9=extreme) @@ -136,26 +176,24 @@ vaptvupt compress -l 9 backup.zupt ~/Documents/ # Force the VaptVupt codec (default on AVX2/NEON hardware) vaptvupt compress --vv -l 5 backup.zupt ~/Documents/ -# Compress with multi-threading (-t 0 = auto-detect cores) +# Multi-threading (-t 0 = auto-detect cores) vaptvupt compress -t 0 -l 5 backup.zupt ~/Documents/ -# Compress with password encryption (AES-256-CTR + HMAC-SHA256) +# Password encryption (AES-256-CTR + HMAC-SHA256, PBKDF2-SHA256 KDF) vaptvupt compress -p "my-strong-password" backup.zupt ~/Documents/ # List archive contents vaptvupt list backup.zupt -# Show archive metadata (codec, blocks, encryption — no password needed) +# Show archive metadata (no password needed) vaptvupt info backup.zupt # Verify archive integrity (HMAC + per-block checksums) vaptvupt test backup.zupt vaptvupt test -p "my-strong-password" backup.zupt -# Extract everything +# Extract vaptvupt extract -o ~/restored/ backup.zupt - -# Extract from encrypted archive vaptvupt extract -p "my-strong-password" -o ~/restored/ backup.zupt # Benchmark all 9 levels on a file @@ -165,32 +203,36 @@ vaptvupt bench big-file.tar #### Post-quantum encryption ```bash -# Recommended: SDK v2 (HKDF combiner + key commitment + HPKE binding + Argon2id). -# New archives should use this. +# Native --pq (ML-KEM-768 + X25519 hybrid KEM, in-tree, default build). +# Recommended for new archives. +vaptvupt keygen -o mykey.key +vaptvupt keygen --pub -o pub.key -k mykey.key +vaptvupt compress --pq pub.key backup.zupt ~/Documents/ +vaptvupt extract --pq mykey.key -o ~/restored/ backup.zupt +``` + +The SDK-backed modes below require a `make WITH_SDK=1` build linked against +the separately distributed `libzuptsdk`/`libpqvaptvupt`: + +```bash +# --pq-sdk (HKDF combiner + key commitment + HPKE binding + Argon2id) vaptvupt keygen --sdk -o mykey.priv # writes mykey.priv and mykey.priv.pub vaptvupt compress --pq-sdk mykey.priv.pub backup.zupt ~/Documents/ vaptvupt extract --pq-sdk mykey.priv -o ~/restored/ backup.zupt -# pq-box sealed-box workflow (v4.0.0; HKDF-SHA256 domain-separated combiner) +# --pq-box sealed-box (ML-KEM-768 + X25519 via HKDF-SHA256 combiner) vaptvupt keygen --box -o box.key # writes box.key + box.key.pub vaptvupt compress --pq-box box.key.pub backup.zupt ~/Documents/ vaptvupt extract --pq-box box.key -o ~/restored/ backup.zupt - -# Legacy --pq mode (XOR+SHA3-512 combiner) — kept for back-compat with -# archives created by Zupt 2.0–2.1. Do NOT use for new archives. -vaptvupt keygen -o mykey.key -vaptvupt keygen --pub -o pub.key -k mykey.key -vaptvupt compress --pq pub.key backup.zupt ~/Documents/ -vaptvupt extract --pq mykey.key -o ~/restored/ backup.zupt ``` #### Full-disk backup ```bash -# Backup an entire disk or partition (sparse-detection skips zero regions) +# Backup a disk or partition (sparse-detection skips zero regions) sudo vaptvupt disk backup -l 5 disk.zupt /dev/sda -# Backup with encryption +# With encryption sudo vaptvupt disk backup -p "passphrase" -l 5 disk.zupt /dev/sda # Restore (writes raw bytes back to a block device or file) @@ -205,33 +247,38 @@ vaptvupt disk backup -l 5 part.zupt /path/to/partition.img ## Auto Codec Detection -VaptVupt automatically selects the best compression codec based on your hardware (since v2.0.0). No flags needed — just run `vaptvupt compress` and it picks the fastest option available. +VaptVupt selects the compression codec based on your hardware (since +v2.0.0). No flags needed — `vaptvupt compress` picks the fastest option +available. | Architecture | SIMD Available | Default Codec | Decode Throughput | |---|---|---|---| -| x86_64 + AVX2 | AVX2 inline SIMD | **VaptVupt** | ~2–3 GB/s | +| x86_64 + AVX2 | AVX2 inline SIMD | VaptVupt | ~2–3 GB/s | | x86_64 (no AVX2) | Scalar | VaptVupt-LZHP | ~500 MB/s | -| aarch64 + NEON | NEON SIMD | **VaptVupt** | ~1–2 GB/s | +| aarch64 + NEON | NEON SIMD | VaptVupt | ~1–2 GB/s | | armhf, ppc64le, s390x, riscv64 | Scalar | VaptVupt-LZHP | ~300–500 MB/s | -**Decompression is universal.** An archive created with VaptVupt on x86_64 extracts on aarch64 (using NEON or scalar decode), and vice versa. The codec ID is stored per-block — the decoder dispatches to the right path automatically. - -Override with `--vv` (force VaptVupt) or `--lzhp` (force VaptVupt-LZHP) when you know what you want. +Decompression is universal. An archive created with VaptVupt on x86_64 +extracts on aarch64 (NEON or scalar decode) and vice versa. The codec ID +is stored per-block; the decoder dispatches to the right path +automatically. Override with `--vv` or `--lzhp`. --- ## VaptVupt Codec -VaptVupt is the project's high-performance compression codec. It combines LZ77 dictionary matching with tANS (table-based Asymmetric Numeral Systems) entropy coding and SIMD-accelerated decompression. +VaptVupt combines LZ77 dictionary matching with tANS (table-based +Asymmetric Numeral Systems) entropy coding and SIMD-accelerated +decompression. -**This release embeds VaptVupt 2.60.4** (security release: fixes an OOB -heap write in the AVX2 decode fast path; adds canonical CBMC-verified -BCJ filters with auto-detection). The codec API is byte-identical -to the 2.48.x line; the 2.48.5 → 2.60.4 upgrades add the optimal parser -(measured: text −1.95%, binary −1.31%, source −4.72% smaller on our -fixtures), large-window extreme mode, faster decode (now roughly on par -with zstd-19, up from 1.5–2× slower), and six upstream corrupt-input -decoder memory-safety fixes. See `CHANGELOG.md` for the full list. +This release embeds VaptVupt codec 2.60.4 (security release: fixes an OOB +heap write in the AVX2 decode fast path; adds CBMC-verified BCJ filters +with auto-detection). The codec API is byte-identical to the 2.48.x line; +the 2.48.5 → 2.60.4 upgrades add the optimal parser (measured on our +fixtures: text −1.95%, binary −1.31%, source −4.72% smaller), large-window +extreme mode, faster decode (roughly on par with zstd-19, up from 1.5–2× +slower), and six upstream corrupt-input decoder memory-safety fixes. See +[CHANGELOG.md](CHANGELOG.md). ### Architecture @@ -245,7 +292,7 @@ Decoder: AVX2 inline SIMD copies, tiered by offset (32/16/8/overlap), safe-zone Format: v1 frame (default) and v2 frame (T-tag, min_match=3) for binary data ``` -### Three modes +### Modes | Mode | CLI | Chain Depth | Entropy | Use Case | |------|-----|-------------|---------|----------| @@ -253,119 +300,105 @@ Format: v1 frame (default) and v2 frame (T-tag, min_match=3) for binary data | Balanced | `-l 3` to `-l 7` (default) | 48 | 4-way ANS | General backup data | | Extreme | `-l 8` to `-l 9` | 256 | Order-1 context ANS + cost-aware lazy parser | Maximum compression | -The VaptVupt wrapper enables VaptVupt's `format_v2` flag (4–7% better real-binary ratio) automatically for Balanced and Extreme modes. Ultra-Fast stays on the v1 frame because the `format_v2 + ULTRA_FAST` combination is not yet covered by VaptVupt's upstream test matrix. +The wrapper enables the codec's `format_v2` flag (4–7% better real-binary +ratio) for Balanced and Extreme modes. Ultra-Fast stays on the v1 frame +because the `format_v2 + ULTRA_FAST` combination is not yet covered by the +codec's upstream test matrix. -### Benchmark Results (codec 2.60.4) +### Measured benchmark (codec 2.60.4) -> Full, reproducible measured benchmarks — compression ratio/speed -> across levels, crypto overhead (KDF vs per-block), and a head-to-head -> ratio comparison against zstd — are in **[`BENCHMARKS.md`](BENCHMARKS.md)**, -> with the test machine and method stated for every table. - -> **F-16 (data loss, fixed in 4.0.0):** archives created by **≤ 3.8.0** at -> `-l 8`/`-l 9` whose inputs included x86/ELF/PE executables may be -> **undecodable by any version** (defect at write time in the old -> divergent BCJ encoder). Re-create such archives with 4.0.0 and verify -> extraction before deleting source data. Details in CHANGELOG/AUDIT. - -**Measured against gzip-9, zstd-3, zstd-19** on a 4-fixture suite -(text 10 MB, binary-struct 7.5 MB, source code 10 MB, random 5 MB). -Decode timed across 3 runs, minimum reported; wall-clock including the -`.zupt` envelope (HMAC etc.). Host: Intel Xeon @ 2.1 GHz, single vCPU, -codec built at the distribution's default optimisation level (AVX2). -**Reproduce with `vaptvupt bench `** to compare VaptVupt levels, -or the comparative harness in the source tree. +Measured against gzip-9, zstd-3, zstd-19 on a 4-fixture suite (text 10 MB, +binary-struct 7.5 MB, source code 10 MB, random 5 MB). Decode timed across +3 runs, minimum reported; wall-clock including the `.zupt` envelope (HMAC +etc.). Host: Intel Xeon @ 2.1 GHz, single vCPU, AVX2 build. Reproduce with +`vaptvupt bench `. | Fixture | Tool | Ratio | Dec MB/s | |---------------|-----------|---------:|---------:| | text 10 MB | vv-9 | 25.6% | 278 | | text 10 MB | gzip-9 | 22.6% | 156 | | text 10 MB | zstd-3 | 24.2% | 556 | -| text 10 MB | zstd-19 | **17.6%**| 435 | +| text 10 MB | zstd-19 | 17.6% | 435 | | binary 7.5 MB | vv-9 | 46.1% | 300 | | binary 7.5 MB | gzip-9 | 46.8% | 123 | | binary 7.5 MB | zstd-3 | 44.8% | 577 | -| binary 7.5 MB | zstd-19 | **41.1%**| 417 | +| binary 7.5 MB | zstd-19 | 41.1% | 417 | | source 10 MB | vv-9 | 4.5% | 714 | | source 10 MB | gzip-9 | 4.0% | 238 | | source 10 MB | zstd-3 | 5.6% | 1000 | -| source 10 MB | zstd-19 | **2.7%** | 769 | +| source 10 MB | zstd-19 | 2.7% | 769 | | random 5 MB | vv-9 | 100.0% | 625 | | random 5 MB | zstd-3 | 100.0% | 681 | -Honest reading (these are measured numbers, not aspirations): +Reading these numbers: -- **On ratio, zstd-19 wins every fixture.** VaptVupt L9 lands between - zstd-3 and zstd-19 on text and binary, beats zstd-3 on source (4.5% - vs 5.6%), and loses to zstd-19 everywhere. If smallest-file is the - only goal, use `xz -9` or `zstd -19`. -- **Decode is now competitive**, not a weakness: 278–714 MB/s, in the - same band as zstd-19 (and within ~1.3× of zstd-3). The 2.60.4 codec's - decode-speed work (Sprint 53/58) closed most of the gap that existed - at 2.48.5. The earlier "1.27× zstd-3 decode" headline (inherited from - upstream docs) is **not claimed here** — it did not reproduce in our - own single-vCPU measurement. -- **Encode throughput remains the weakness.** The optimal parser and - depth-24 hash-chain walk that win ratio cost encode speed; balanced - mode is ~6× slower than fast and ~14× slower than zstd-1. For - encode-latency-bound workloads use `vaptvupt compress -l 1`/`-l 2`. -- **On a degenerate single-pattern input**, large-window extreme (L9) - is slightly *worse* than L5/L7 — a known tradeoff of optimizing for - real long-range matches. Doesn't affect realistic corpora. -- **On random / already-compressed data**, all codecs hit the - incompressibility wall; the comparison degenerates to - framing-overhead measurement. +- On ratio, zstd-19 wins every fixture. VaptVupt L9 lands between zstd-3 + and zstd-19 on text and binary, beats zstd-3 on source (4.5% vs 5.6%), + and loses to zstd-19 everywhere. For smallest-file only, use `xz -9` or + `zstd -19`. +- Decode is competitive: 278–714 MB/s, in the same band as zstd-19 and + within ~1.3× of zstd-3. +- Encode throughput is the weakness. The optimal parser and hash-chain + walk that win ratio cost encode speed; balanced mode is ~6× slower than + fast mode. For encode-latency-bound workloads use `-l 1`/`-l 2`. +- On a degenerate single-pattern input, large-window extreme (L9) can be + slightly worse than L5/L7 — a tradeoff of optimizing for real long-range + matches. It does not affect realistic corpora. +- On random / already-compressed data, all codecs hit the + incompressibility wall. -### Security Test Results (v4.0.0 release) +### Security regression tests -Every release re-runs the full security regression matrix. These are -the v4.0.0 numbers: +Every release re-runs the security regression matrix (`make check`, +≈2 minutes on x86_64 and aarch64). It covers: -| Test | Coverage | Result | -|---------------------------------|--------------------------------------------------------------------------|-------------------| -| F-06 HMAC tamper fuzz | 2000 trials, single-bit flip in HMAC tag | **0 silent accepts** / 2000 honest roundtrips OK | -| F-08 archive-integrity trailer | Header/footer tamper detection | **5/5 pass** | -| F-09 byte-level integrity sweep | 1827 positions on a PQ-SDK archive, every byte flipped exhaustively | **0/1827 silent accepts** ✓ | -| F-10 KDF default | Argon2id is the default; PBKDF2 available via `--kdf pbkdf2` | **10/10 pass** | -| F-11 auth-fail wording | Wrong-password vs tampered-archive messages are indistinguishable | **12/12 pass** | -| F-12 encrypted comments | Comment block bound to per-block AAD; tamper rejected at extract | **11/11 pass** | -| F-15 KDF transparency | Argon2id header self-describes its profile; back-compat + fail-closed | **5/5 pass** | -| Constant-time comparisons | dudect Welch t-test (MAC tag + ML-KEM decaps); ~1% of leaky-memcmp + source-routing guard | **2/2 pass** | -| Codec exact-size decode (OOB) | 80 exact-`content_size` cases incl. BCJ payloads, ASan (codec 2.60.4 fix class) | **80/80 pass** | -| pq-box mode | roundtrips L1/L9/BCJ; wrong-key/key-confusion/tamper/cross-mode rejection | **13/13 pass** | -| NIST/RFC test vectors | SHA-256, SHA-3, SHAKE-128, ML-KEM-768, AES-256-CTR (SP 800-38A), HMAC-SHA256, X25519, XXH64 | **16/16 pass** | -| Path-traversal | Absolute paths and `..` components refused | **5/5 pass** | -| Block-swap | Re-ordered block detection | **6/6 pass** | -| Dedup property | Deduplication never produces wrong output | **12/12 pass** | -| Audit suite | Curated smoke tests | **10/10 pass** | -| Argument-order | CLI flag ordering doesn't change semantics | **8/8 pass** | -| Distro-safe `make check` | Aggregate of the above (no flaky threading, no `make clean` mid-stream) | **91/91 pass** | +- HMAC single-bit tamper detection and honest roundtrips. +- Archive-integrity trailer (header/footer tamper detection). +- Byte-level integrity sweep on a PQ archive (every byte flipped). +- KDF default (PBKDF2-SHA256) and self-describing header transparency, + with back-compat and fail-closed on unknown profiles. +- Indistinguishable wrong-password vs tampered-archive error messages. +- Encrypted comment block bound to per-block AAD. +- Constant-time comparison (dudect Welch t-test on MAC tag and ML-KEM + decaps) plus a source-routing guard. +- Codec exact-`content_size` decode cases (incl. BCJ payloads) under ASan. +- NIST/RFC test vectors: SHA-256, SHA-3, SHAKE-128, ML-KEM-768, + AES-256-CTR (SP 800-38A), HMAC-SHA256, X25519, XXH64. +- Path-traversal refusal, block-swap detection, deduplication correctness, + and CLI argument-order invariance. -Reproduce: `make check` (≈2 minutes, 91 assertions across 10 suites, -all green on x86_64 + aarch64). `make test` runs the full 15-suite -arc including dist reproducibility and packaging-syntax. +`make test` runs the full suite including dist reproducibility and +packaging-syntax checks. -### Why VaptVupt? +### Codec notes -VaptVupt's architectural advantages over traditional Huffman-based codecs: - -- **tANS entropy** — asymptotically optimal coding with single-instruction decode per symbol (vs Huffman's multi-step tree walk) -- **4-way interleaved ANS** — decodes 4 symbols per bitstream refill cycle, reducing refill overhead by 4× -- **4-stream Huffman literal coding** (`lit_fmt=4`) — Sprint 105 addition that further improves ratio on structured data -- **AVX2/NEON SIMD decode** — inline 32-byte copies with tiered offset handling (no function-pointer dispatch). Falls back to scalar on unsupported hardware. -- **Rep-match** — checks 3 recent offsets before hash probe (O(1) vs O(chain_depth)), hits ~30% of matches. Saves 10–15 bits per repeated offset. -- **Order-1 context model** — captures byte-pair correlations in structured data (JSON, CSV, logs) -- **Cost-aware lazy parser** (Sprint 120) — the breakthrough that put EXTREME ahead of zstd-3 in aggregate ratio -- **Adaptive window** — trial-compresses at wlog=16 vs wlog=20, picks larger window only if ≥3% improvement -- **`format_v2` (T-tag, min_match=3)** — 4–7% better binary ratio; transparent to v2.33.0+ decoders -- **Memory hygiene** (Sprint 118) — encoder working buffers scrubbed via `vv_secure_zero` before `free()` -- **~6,500 lines** of pure C11 — auditable, portable, no external dependencies +- **tANS entropy** — asymptotically optimal coding with single-instruction + decode per symbol (vs Huffman's multi-step tree walk). +- **4-way interleaved ANS** — decodes 4 symbols per bitstream refill cycle. +- **4-stream Huffman literal coding** (`lit_fmt=4`) — improves ratio on + structured data. +- **AVX2/NEON SIMD decode** — inline 32-byte copies with tiered offset + handling. Scalar fallback on unsupported hardware. +- **Rep-match** — checks 3 recent offsets before the hash probe (O(1) vs + O(chain_depth)), hitting ~30% of matches. +- **Order-1 context model** — captures byte-pair correlations in structured + data (JSON, CSV, logs). +- **Cost-aware lazy parser** — puts Extreme mode ahead of zstd-3 in + aggregate ratio. +- **Adaptive window** — trial-compresses at wlog=16 vs wlog=20, picking the + larger window only if ≥3% improvement. +- **`format_v2`** (T-tag, min_match=3) — 4–7% better binary ratio; + transparent to v2.33.0+ decoders. +- **Memory hygiene** — encoder working buffers scrubbed via + `vv_secure_zero` before `free()`. +- **~6,500 lines** of pure C11. --- ## Post-Quantum Encryption -`--pq` mode uses hybrid ML-KEM-768 + X25519 key encapsulation per NIST FIPS 203. +`--pq` uses hybrid ML-KEM-768 + X25519 key encapsulation per NIST FIPS 203, +in-tree and available in the default build. ``` Public key → ML-KEM-768 Encaps + X25519 ECDH → hybrid shared secret @@ -373,22 +406,28 @@ Public key → ML-KEM-768 Encaps + X25519 ECDH → hybrid shared secret → AES-256-CTR + HMAC-SHA256 per block ``` -**Security model:** Secure if EITHER ML-KEM-768 (post-quantum) OR X25519 (classical) is secure. +Security model: secure if EITHER ML-KEM-768 (post-quantum) OR X25519 +(classical) is secure. -**Password mode (`-p`) is NOT quantum-safe.** Use `--pq` for long-term protection. +Password mode (`-p`) is not quantum-safe. Use `--pq` for long-term +protection. + +The SDK-backed `--pq-sdk` and `--pq-box` modes are optional and require a +`make WITH_SDK=1` build against `libzuptsdk`/`libpqvaptvupt`. --- ## Full-Disk Backup -Clone entire disks, partitions, or raw images with compression and encryption in one command. +Clone disks, partitions, or raw images with compression and encryption in +one command. ### Quick start ```bash # Clone a partition (requires read access) sudo vaptvupt disk backup backup.zupt /dev/sda1 -# Clone with post-quantum encryption (strongest) +# Clone with post-quantum encryption vaptvupt keygen -o mykey.key vaptvupt keygen --pub -o pub.key -k mykey.key sudo vaptvupt disk backup --pq pub.key backup.zupt /dev/nvme0n1p2 @@ -414,19 +453,24 @@ Source device → Read 4MB blocks → Sparse detection → Compress → Encrypt └─ Zero blocks stored as STORE (near-zero overhead) ``` -VaptVupt reads the source device sequentially in 4MB chunks. Each block is checked for all-zero content (sparse detection uses 8-byte-wide comparison). Zero blocks are stored with codec `STORE` — effectively just the block header with no payload, saving both compression CPU time and archive space. Non-zero blocks are compressed with the selected codec and optionally encrypted. Per-block XXH64 checksums ensure byte-for-byte integrity on restore. +VaptVupt reads the source device sequentially in 4MB chunks. Each block is +checked for all-zero content (8-byte-wide comparison). Zero blocks are +stored with codec `STORE` — effectively just the block header with no +payload. Non-zero blocks are compressed with the selected codec and +optionally encrypted. Per-block XXH64 checksums ensure byte-for-byte +integrity on restore. ### Best practices -**Encryption hierarchy (strongest → fastest):** +Encryption modes: | Mode | Command | Security Level | Speed Impact | |------|---------|---------------|-------------| | PQ Hybrid | `--pq pub.key` | Quantum-resistant + classical | ~5% overhead | -| Password | `-p` | AES-256, PBKDF2 600K iter | ~3% overhead | +| Password | `-p` | AES-256, PBKDF2-SHA256 600K iter | ~3% overhead | | None | (default) | Integrity only (XXH64) | Fastest | -**Compression levels for disks:** +Compression levels for disks: | Level | Mode | Best for | Typical ratio | |-------|------|----------|--------------| @@ -434,41 +478,38 @@ VaptVupt reads the source device sequentially in 4MB chunks. Each block is check | `-l 4` to `-l 7` | Balanced (default) | General partitions, ext4/NTFS | 2–5:1 | | `-l 8` to `-l 9` | Extreme | Cold storage, archival backups | 3–10:1 | -**Operational guidance:** +Operational guidance: -- **Unmount before backup** for filesystem consistency. For live systems, use LVM snapshots or filesystem freeze: `fsfreeze -f /mnt/data && vaptvupt disk backup ... && fsfreeze -u /mnt/data`. -- **Block devices require root** on Linux. Regular files (disk images, `.img`, `.raw`) do not. -- **Sparse-heavy disks** (freshly formatted, VMs with thin provisioning) compress extremely well — the sparse detector skips zero blocks at memory-copy speed with no compression overhead. -- **Verify after backup** with `vaptvupt test archive.zupt` — checks every block's XXH64 checksum without extracting. -- **PQ encryption for long-term** — disk backups stored for years should use `--pq` to resist future quantum attacks. Generate one keypair, store the private key offline, distribute the public key. -- **Restore is non-destructive on files** — writing to a regular file creates/overwrites it. Writing to a block device overwrites the raw device. Double-check the target path before restoring to a device. - -### Comparison with other tools - -| Feature | VaptVupt disk | dd + gzip | Clonezilla | partclone | -|---------|-----------|-----------|------------|-----------| -| Compression | VaptVupt/LZHP (adaptive) | gzip (fixed) | Multiple | Multiple | -| Encryption | AES-256 + PQ hybrid | None (pipe to gpg) | None | None | -| Sparse detection | Automatic | None | Filesystem-aware | Filesystem-aware | -| Per-block integrity | XXH64 per block | None | None | CRC32 | -| Single binary | ✓ (zero deps) | 2+ tools | ISO boot | Multiple | -| Post-quantum | ML-KEM-768 | — | — | — | -| Cross-platform | 6 architectures | ✓ | x86 only | Linux only | +- Unmount before backup for filesystem consistency. For live systems use + LVM snapshots or filesystem freeze: + `fsfreeze -f /mnt/data && vaptvupt disk backup ... && fsfreeze -u /mnt/data`. +- Block devices require root on Linux. Regular files (disk images, `.img`, + `.raw`) do not. +- Sparse-heavy disks compress well — the sparse detector skips zero blocks + at memory-copy speed with no compression overhead. +- Verify after backup with `vaptvupt test archive.zupt` — checks every + block's XXH64 checksum without extracting. +- For long-term disk backups use `--pq`. Generate one keypair, store the + private key offline, distribute the public key. +- Restore is non-destructive on files (creates/overwrites the file); + writing to a block device overwrites the raw device. Double-check the + target path before restoring to a device. --- ## Multi-Architecture Support -VaptVupt builds and runs on all major architectures. The Makefile auto-detects the platform and enables the best available features. +The Makefile auto-detects the platform and enables the best available +features. | Feature | x86_64 | aarch64 | armhf | ppc64le | s390x | riscv64 | |---------|--------|---------|-------|---------|-------|---------| -| Jasmin CT crypto | ✓ | C fallback | C fallback | C fallback | C fallback | C fallback | -| AES-NI hardware | ✓ (with AVX) | — | — | — | — | — | -| AVX2 SIMD decode | ✓ | — | — | — | — | — | -| NEON SIMD decode | — | ✓ | — | — | — | — | +| Jasmin CT crypto | yes | C fallback | C fallback | C fallback | C fallback | C fallback | +| AES-NI hardware | yes (with AVX) | — | — | — | — | — | +| AVX2 SIMD decode | yes | — | — | — | — | — | +| NEON SIMD decode | — | yes | — | — | — | — | | Default codec | VaptVupt | VaptVupt | LZHP | LZHP | LZHP | LZHP | -| All codecs decode | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| All codecs decode | yes | yes | yes | yes | yes | yes | Build for packaging (PIE, hardening flags): ```bash @@ -478,34 +519,11 @@ make install DESTDIR=/buildroot --- -## Feature Comparison - -| Feature | VaptVupt v4.0 | gzip | zstd | 7-Zip | -|---------|-----------|------|------|-------| -| Default codec | VaptVupt/LZHP (auto) | DEFLATE | FSE+Huffman | LZMA2 | -| Full-disk backup | **`vaptvupt disk`** | — | — | — | -| Post-quantum encryption | **ML-KEM-768** | — | — | — | -| Password encryption | AES-256 + HMAC | — | — | AES-256 | -| AES-NI hardware accel | **Jasmin-verified** | — | — | — | -| Per-block integrity | XXH64 + HMAC | CRC32 | XXH64 | CRC32 | -| Multi-threaded compress | ✓ | — (pigz) | ✓ | ✓ | -| Multi-threaded decompress | **✓** | — | ✓ | ✓ | -| Formal verification | **Jasmin CT + ACSL** | — | — | — | -| mlock() key protection | ✓ | — | — | — | -| AFL++ fuzz harness | ✓ | — | ✓ | — | -| Multi-architecture | **6 arches** | ✓ | ✓ | ✓ | -| Zero dependencies | ✓ | ✓ | — | — | -| Codebase | ~12K lines | ~10K | ~75K | ~100K+ | -| License | **AGPL+GPL** | GPL/zlib | BSD-3 | LGPL+unRAR | - ---- - ## Security ``` Password mode: Password → PBKDF2-SHA256 (600K iter) → enc_key + mac_key PQ hybrid mode: Public key → ML-KEM-768 Encaps + X25519 ECDH → enc_key + mac_key -SDK v2 mode: HKDF-SHA3 combiner with domain separation + key commitment + HPKE binding Per-block: AES-256-CTR(enc_key, nonce ⊕ seq) + HMAC-SHA256(mac_key) Key protection: mlock() prevents swap, buffer canaries detect overflow Timing: Always-decrypt mitigation (no timing oracle on MAC failure) @@ -514,16 +532,16 @@ Path safety: Zip Slip / symlink defenses (zupt_path_is_safe + O_NOFOLLOW) Verification: 5 Jasmin CT proofs, 19 ACSL contracts, 16 NIST/RFC test vectors ``` -**Audit history:** Three internal audit sprints conducted on the 2.2.x line. -**14 bugs** found and fixed across the sprints — including one **HIGH-severity -Zip Slip path traversal** caught in the formal audit pass. Cumulative test -surface: **265 tests** (47 vaptvupt + 169 SDK + 49 inherited) plus **751,000 -mutation-fuzz iterations** under ASAN/UBSAN, all passing. No external audit -yet — see SECURITY.md for honest scope. +The `WITH_SDK=1` build adds an HKDF-SHA3 combiner with domain separation, +key commitment, and HPKE binding for the `--pq-sdk`/`--pq-box` modes, plus +the Argon2id KDF. -See [SECURITY.md](SECURITY.md) for threat model. See [AUDIT.md](AUDIT.md) for -audit history. See [FORMAL_AUDIT_PROMPT.md](FORMAL_AUDIT_PROMPT.md) for the -methodology used in audit sprints. +Internal audit passes on the 2.2.x line fixed 14 bugs, including a +HIGH-severity Zip Slip path traversal. There has been no external audit. +See [SECURITY.md](SECURITY.md) for the threat model and honest scope, and +[FORMAL_AUDIT_PROMPT.md](FORMAL_AUDIT_PROMPT.md) for the audit methodology. + +Report security vulnerabilities per [SECURITY.md](SECURITY.md). --- @@ -546,7 +564,7 @@ vaptvupt help |--------|-------------| | `-l <1-9>` | Compression level (default: 7) | | `-t ` | Thread count (0=auto, 1=single, 2–64) | -| `-p [PW]` | Password encryption (PBKDF2 → AES-256) | +| `-p [PW]` | Password encryption (PBKDF2-SHA256 → AES-256) | | `--pq ` | Post-quantum hybrid encryption | | `-o ` | Output directory (extract) | | `-s` | Store without compression | @@ -562,9 +580,10 @@ vaptvupt help ## Building ```bash -make # Auto-detects arch, Jasmin, AVX2 +make # Default build: C compiler + make only +make WITH_SDK=1 # Link libzuptsdk/libpqvaptvupt: --pq-sdk, --pq-box, Argon2id make V=1 # Verbose build output -make test-all # 77 tests: regression + NIST + VV + MT + PQ + disk +make test-all # Regression + NIST + VV + MT + PQ + disk make test-vv # VaptVupt codec unit tests only make test-asan # AddressSanitizer + UBSan build make fuzz-build # AFL++ fuzzing harnesses @@ -586,13 +605,15 @@ vaptvupt bench --compare ~/Documents/ # Compare codecs on your own data | ID | Name | Algorithm | Default on | Override | |----|------|-----------|------------|----------| -| `0x0010` | **VaptVupt** | LZ77 + tANS + AVX2/NEON SIMD | x86_64 (AVX2), aarch64 (NEON) | `--vv` | -| `0x000A` | **VaptVupt-LZHP** | LZ77 + Huffman + byte prediction | armhf, ppc64le, s390x, riscv64 | `--lzhp` | +| `0x0010` | VaptVupt | LZ77 + tANS + AVX2/NEON SIMD | x86_64 (AVX2), aarch64 (NEON) | `--vv` | +| `0x000A` | VaptVupt-LZHP | LZ77 + Huffman + byte prediction | armhf, ppc64le, s390x, riscv64 | `--lzhp` | | `0x0009` | VaptVupt-LZH | LZ77 + Huffman | — | — | | `0x0008` | VaptVupt-LZ | Fast LZ77, 64KB window | — | `-f` | | `0x0000` | Store | No compression | — | `-s` | -All codecs are forward-compatible: archives created with any codec can be read by any VaptVupt version that includes that codec, on any architecture. VaptVupt archives require VaptVupt v2.0+. +All codecs are forward-compatible: archives created with any codec can be +read by any VaptVupt version that includes that codec, on any architecture. +VaptVupt archives require VaptVupt v2.0+. --- @@ -603,28 +624,17 @@ All codecs are forward-compatible: archives created with any codec can be read b | v0.1–v0.6 | LZ77 compression, AES-256 encryption, multi-threading | | v0.7 | Post-quantum hybrid encryption (ML-KEM-768 + X25519) | | v1.0 | Stable release — format frozen v1.4, security audit | -| v1.1–v1.4 | X25519 fix, NIST vectors, CPUID detection, Jasmin source files fixed | -| v1.5 | Jasmin CT assembly linked (MAC verify + ML-KEM select active) | -| v1.5.5 | Build system improvements: man page install rules, verbose mode, multi-arch detection | -| v2.0 | VaptVupt 1.1.0 codec, auto hardware detection, all 5 Jasmin wired, AVX SIGILL fix, copy_match/litlen fixes, ACSL, mlock, fuzzing, canaries, AES-NI pipeline, MT decompress, multi-arch (6 arches), --lzhp flag | -| v2.1.0 | VaptVupt 1.4.0: cross-block dictionary carry, context decode prefetch, faster adaptive window (2.6× encode), integration API | -| v2.1.1 | Termux/Android build fix, arch-safety guard, Keccak ROL64 UB fix, zero UBSan violations | -| v2.1.2 | Full-disk backup/restore (`disk` subcommand), sparse detection, all encryption modes, progress bar | -| v2.1.3 | LZHP prediction encoding fix (data corruption on structured data), shared write_enc_header, SOLID flag removed from disk, 78 tests | -| v2.1.4 | CodeQL: 4 security fixes — TOCTOU races eliminated (fstat on fd), X25519 scalar wipe via volatile | -| v2.1.5 | Block-level deduplication (`--dedup`), XXH64 fingerprint index, DEDUP_REF block type, 81 tests | -| v2.2.0–v2.2.2 | libzuptsdk 2.0 integration (HKDF-SHA3 combiner + key commitment + HPKE binding + Argon2id), `--pq-sdk` mode (XChaCha20-Poly1305 / AES-256-SIV), license-hygiene cleanup, full SPDX coverage | -| v2.2.3 | VaptVupt 2.48.2 codec integration: cost-aware lazy parser, 4-stream Huffman, `format_v2` flag (4–7% better binary), encoder memory hygiene (`vv_secure_zero` on free). Makefile arch-detection fix. ASAN/UBSAN clean across plain/password/PQ-SDK at all levels | -| v2.2.4–v2.2.5 | Audit sprint: findings F-01..F-07 closed, including F-06 (high) HMAC accept-on-disjoint-bits | -| v2.3.0–v2.3.1 | F-08/F-09 closed: archive-integrity trailer + preface-AAD MAC — exhaustive byte sweep 0/1827 undetected (format v1.5 → v1.6) | -| v2.4.x | Argon2id default KDF (F-10), error-message hygiene (F-11), encrypted archive comments (F-12), packaging arc (deb, RPM, AUR, Nix, Homebrew, openSUSE OBS), CI rewrite, THREAT_MODEL.md, manpage + shell completions, distro-safe `make check` | -| v3.0.0 | **Renamed Zupt → VaptVupt** (INPI Brasil trademark), VV codec 2.48.5, GUI binary-discovery fix. Wire format unchanged; `zupt` kept as compat symlink | -| v3.0.1–v3.0.3 | GUI license/version-parsing cleanup, F-13 (usage() literal size), static-analysis cleanup | -| v3.1.0 | Codec 2.48.5 → 2.53.3, decode over-copy fix | -| v3.2.0–v3.3.0 | SHA-256 hardware acceleration (Intel SHA-NI), incremental per-block HMAC (drops a malloc + full copy per block) | -| v3.4.0–v3.7.0 | F-15 KDF parameter transparency, measured constant-time MAC comparison (dudect), NIST SP 800-38A AES-CTR vectors, ML-KEM decaps routed through the audited CT primitive | -| v3.8.0 | Consolidated measured benchmarks + constant-time test robustness | -| **v4.0.0** | **Codec 2.60.4 security release (OOB heap write fixed in AVX2 decode fast path), `--pq-box` sealed-box mode (libpqvaptvupt 0.6.0, HKDF-SHA256 combiner), F-16 data-loss disclosure + fix (old in-tree BCJ encoder), CBMC-verified BCJ filters with auto ELF/PE/Mach-O detection, SHA-NI measured 5.8×. Wire format stays v1.6** | +| v1.1–v1.5.5 | X25519 fix, NIST vectors, CPUID detection, Jasmin CT assembly linked, build-system improvements | +| v2.0 | VaptVupt codec, auto hardware detection, all 5 Jasmin wired, AVX SIGILL fix, ACSL, mlock, fuzzing, canaries, AES-NI pipeline, MT decompress, multi-arch (6 arches), `--lzhp` | +| v2.1.x | Cross-block dictionary carry, Termux/Android build fix, full-disk backup/restore, LZHP fix, CodeQL fixes, block-level deduplication | +| v2.2.x | libzuptsdk integration (`--pq-sdk`), VaptVupt 2.48.2 codec (cost-aware lazy parser, 4-stream Huffman, `format_v2`), audit findings F-01..F-07 closed (incl. F-06 high) | +| v2.3.x | F-08/F-09 closed: archive-integrity trailer + preface-AAD MAC (format v1.5 → v1.6) | +| v2.4.x | PBKDF2/Argon2id KDF work (F-10), error-message hygiene (F-11), encrypted comments (F-12), packaging arc (deb/RPM/AUR/Nix/Homebrew/OBS), THREAT_MODEL.md, manpage + completions, distro-safe `make check` | +| v3.0.x | Renamed Zupt → VaptVupt (INPI Brasil trademark), VV codec 2.48.5, GUI fixes, F-13 fix. Wire format unchanged; `zupt` kept as compat symlink | +| v3.1.0–v3.3.0 | Codec 2.48.5 → 2.53.3, decode over-copy fix, SHA-256 hardware acceleration (Intel SHA-NI), incremental per-block HMAC | +| v3.4.0–v3.8.0 | F-15 KDF parameter transparency, measured constant-time MAC comparison (dudect), NIST SP 800-38A AES-CTR vectors, ML-KEM decaps through the CT primitive, consolidated benchmarks | +| v4.0.0 | Codec 2.60.4 security release (OOB heap write fixed in AVX2 decode fast path), `--pq-box` sealed-box mode, F-16 data-loss disclosure + fix (old in-tree BCJ encoder), CBMC-verified BCJ filters with auto ELF/PE/Mach-O detection, SHA-NI acceleration. Wire format v1.6 | +| 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 | See [CHANGELOG.md](CHANGELOG.md) for detailed per-version changes. @@ -632,15 +642,19 @@ See [CHANGELOG.md](CHANGELOG.md) for detailed per-version changes. ## License -VaptVupt is **dual-licensed**: +VaptVupt is dual-licensed: -- **AGPL-3.0-or-later** — most of the codebase (CLI, libzuptsdk, GUI, Jasmin source). See [`LICENSE`](LICENSE). -- **GPL-3.0-or-later** — the VaptVupt LZ codec only (`src/vv_*.c`, `src/vaptvupt_api.c` and headers). VaptVupt is GPL so it can be considered for upstreaming into the Linux/BSD kernels. -- **Commercial license** available for relief from AGPL/GPL terms. Contact `sac@securityops.co`. +- **AGPL-3.0-or-later** — most of the codebase (CLI, GUI, Jasmin source). + See [`LICENSE`](LICENSE). +- **GPL-3.0-or-later** — the VaptVupt LZ codec only (`src/vv_*.c`, + `src/vaptvupt_api.c` and headers), so it can be considered for + upstreaming into the Linux/BSD kernels. +- **Commercial license** available for relief from AGPL/GPL terms. Contact + `sac@securityops.co`. -Every source file carries an explicit SPDX header. See [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md) for full attribution. VaptVupt contains **no third-party source code** — every line is original work. - -Security vulnerabilities: see [SECURITY.md](SECURITY.md). +Every source file carries an explicit SPDX header. See +[THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md) for full attribution. +VaptVupt contains no third-party source code. ## Related projects @@ -652,8 +666,5 @@ All by Cristian Cezar Moisés, hosted on git.securityops.co: - [libvuptsdk](https://git.securityops.co/cristiancmoises/libvuptsdk) — Standalone C SDK - [vaptvupt-codec](https://git.securityops.co/cristiancmoises/vaptvupt-codec) — Standalone LZ + tANS codec -## Support the Project -If you find VaptVupt useful, please consider sharing it or contributing — see the README footer for contact links. - --- © 2026 Cristian Cezar Moisés — [git.securityops.co/cristiancmoises](https://git.securityops.co/cristiancmoises) diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index 709d3b0..0000000 --- a/ROADMAP.md +++ /dev/null @@ -1,90 +0,0 @@ -# VaptVupt — Roadmap - -## Released - -| Version | Status | Description | -|---------|--------|-------------| -| v0.1 | ✅ | Initial release — LZ77 compression, `.zupt` format, XXH64 checksums | -| v0.2 | ✅ | AES-256-CTR + HMAC-SHA256 encryption, PBKDF2, directory recursion | -| v0.3 | ✅ | VaptVupt-LZH codec — LZ77 + Huffman, 1MB window, near-optimal parsing | -| v0.4 | ✅ | Byte prediction preprocessor (VaptVupt-LZHP), solid mode | -| v0.5 | ✅ | Security hardening — 16 bug fixes, Huffman codec fix, CSPRNG hardened | -| v0.6 | ✅ | Multi-threaded compression (`-t N`), batch-parallel pipeline | -| v0.7 | ✅ | Post-quantum hybrid encryption (ML-KEM-768 + X25519) | -| v1.0 | ✅ | Stable release — format frozen v1.4, security audit, MIT license | -| v1.1 | ✅ | X25519 formula fix, 13 NIST/RFC test vectors, zero `-Wpedantic` warnings | -| v1.2 | ✅ | CPUID runtime detection (AES-NI, AVX2, SSE4.1, PCLMUL) | -| v1.3 | ✅ | ACSL predicates, Jasmin source files (initial), security review | -| v1.4 | ✅ | All 4 Jasmin `.jazz` files compile on jasminc 2026.03.0 | -| **v1.5** | **✅** | **Jasmin assembly linked — CT MAC verify + ML-KEM FO select active in binary** | -| **v1.5.5** | **✅** | **Build system improvements: man page install rules, verbose mode, multi-arch detection** | -| **v2.0** | **✅** | **VaptVupt 1.1.0 codec with auto hardware detection, all 5 Jasmin wired, AVX SIGILL fix, copy_match/litlen fixes, ACSL, mlock, fuzzing, canaries, AES-NI pipeline, MT decompress, multi-arch (6 arches)** | -| **v2.1** | **✅** | **VaptVupt 1.4.0: cross-block dictionary, context prefetch, faster adaptive window, integration API** | -| **v2.1.1** | **✅** | **Termux/Android build fix, arch-safety guard, Keccak UB fix, no stale .o in tarballs** | -| **v2.1.2** | **✅** | **Full-disk backup/restore with sparse detection, all encryption modes, progress bar, 77 tests** | -| **v2.1.3** | **✅** | **Disk restore rewritten — shared block I/O, fixes checksum mismatch on encrypted/PQ archives** | -| **v2.1.3** | **✅** | **LZHP prediction encoding fix, shared write_enc_header, SOLID flag removed from disk, 78 tests** | -| **v2.1.4** | **✅** | **CodeQL: 4 security fixes — TOCTOU races (fstat on fd), X25519 scalar wipe (volatile), 78 tests** | -| **v2.1.5** | **✅** | **Block-level deduplication (--dedup), XXH64 fingerprint index, DEDUP_REF block type, 81 tests** | -| v2.2.4 | ✅ | Five-finding audit pass: help-format, flaky audit (F-02a), `-Wshadow`, missing-prototype on ML-KEM selftest (now wired as vector 14/14), three `const` cppcheck hints; F-02b (index MAC) opened as deferred | -| v2.2.5 | ✅ | F-06 (high): HMAC verifier silently accepted ~6.35% of single-bit MAC tampers on the Jasmin (x86_64) path. F-07: structural check that block at `index_offset` claims `INDEX` type. F-02b reclassified as resolved (the index IS MAC'd; the verifier was buggy). F-08 (cosmetic-metadata coverage) opened, deferred to v2.3.0. | -| v2.3.0 | ✅ | F-08 closed: archive-integrity-trailer (32B AIT after footer; HMAC-SHA256 over `hdr ‖ ft[0..23]` in encrypted modes, XXH64 best-effort in plaintext). Format v1.4 → v1.5. Backward-compat read path with downgrade warning on legacy v1.4 archives. Exhaustive byte sweep: 86 → 18 undetected positions (all per-block-header trivia, deferred to v2.3.1 as F-09). New `tests/test_f08_topmac.sh` regression. | -| v2.3.1 | ✅ | F-09 closed: per-block frame preface bound into MAC via extended-AAD primitives + strict structural validation of the encryption-header block. Format v1.5 → v1.6. Reaches 100% byte-level tamper detection on encrypted archives — exhaustive sweep on 1827-byte v1.6 PQ-SDK archive: 0/1827 silent accepts. v2.3.0 archives extract unchanged; v2.3.0 cleanly rejects v2.3.1 archives (no silent corruption). | -| v2.4.0 | ✅ | Methodology release. `PROMPT.md` → v2: NEW §3.5 exhaustive byte-sweep mandate after format changes, sprint protocol gains a step, §11 outage table grows four rows for F-06..F-09. Makefile help banner now auto-derived from `include/zupt.h` (closes a recurring banner-drift bug). No source/binary changes; archives byte-identical to v2.3.1. | -| v2.4.1 | ✅ | F-10: password-mode KDF default flipped from PBKDF2-SHA256 to Argon2id (libzuptsdk). PBKDF2 remains available via `--kdf pbkdf2` for v2.4.0-and-older reader compatibility. No format change; v2.4.0 already supports reading Argon2id archives via existing enc_type dispatch. F-11 (auth-fail vs integrity-fail error message UX) opened, deferred. | -| v2.4.2 | ✅ | F-11 closed: wrong-password and tampered-archive error messages collapsed into one uniform `Authentication failed (wrong key, wrong password, or tampered archive)` line. Detailed top-MAC wording moves behind `--verbose`. Plaintext tamper keeps detailed XXH64 wording (no key, no oracle concern). Eliminates a verbal probe-oracle that was leaking which failure cause hit first. No format change. | -| v2.4.3 | ✅ | F-12 closed: encrypted archive comments. Implements the previously-reserved `comment_offset` header field via new block type `ZUPT_BLOCK_COMMENT = 0x05`. Comments are UTF-8, up to 4096 bytes, encrypted using the same per-block AEAD pipeline as data blocks (including F-09 preface AAD). `hdr.comment_offset` is in the AIT-signed region, so pointer tampering → auth-fail. CLI flags `-c` / `--comment` and `--comment-file`. `vaptvupt info` reports presence without decrypting; `vaptvupt x` displays comment after extract. v2.4.2 readers extract v2.4.3 archives byte-exact (they ignore `comment_offset`). Format still v1.6. Exhaustive byte sweep on 1878-byte archive with comment: 0/1878 silent accepts. | -| v2.4.4 | ✅ | Distribution packaging + reproducible source tarball. New `make dist` produces byte-identical `vaptvupt-VERSION.tar.gz`; regression test `tests/test_dist_reproducible.sh` asserts two consecutive runs produce identical sha256. Upstream packaging recipes added at `packaging/aur/PKGBUILD`, `packaging/debian/{control,rules,changelog,copyright,source/format}`, and `packaging/homebrew/vaptvupt.rb`. No source-code changes, no format changes. | -| v2.4.5 | ✅ | Packaging arc completion. New `packaging/rpm/vaptvupt.spec` (Fedora/RHEL/CentOS) and `packaging/nix/flake.nix` (NixOS, x86_64 + aarch64). New `DISTRIBUTION.md` covers all 5 packaging methods with concrete submission flows. New `tests/test_packaging_syntax.sh` (18 assertions, wired into `make test`) enforces cross-recipe version consistency and basic syntax validity. No source-code changes. | -| v2.4.6 | ✅ | CI + threat model. Rewrote `.github/workflows/ci.yml` from 4 jobs to 8 (matrix builds, strict warnings, ASAN, PIE, aarch64, dist-reproducibility, packaging-syntax, tag-triggered release). New `THREAT_MODEL.md` (12 KB) documents what VaptVupt protects against and — explicitly per userPreferences — what it does NOT. Packaging-syntax test expanded 18 → 22. No source-code changes. | -| v2.4.7 | ✅ | Manpage refresh + shell completions. | -| v2.4.8 | ✅ | Distro-safe `make check` target + binary packages. | -| v3.0.0 | ✅ | MAJOR: Zupt → VaptVupt rename, VV codec 2.48.5, GUI binary-discovery fix. | -| v3.0.1 | ✅ | GUI license + version-parsing cleanup. | -| v3.0.2 | ✅ | F-13 closed (usage() string-literal length) + help-text drift cleanup. | -| v3.0.3 | ✅ | Static-analysis cleanup (cppcheck + -Wconversion). | -| v3.1.0 | ✅ | VaptVupt codec 2.48.5 → 2.53.3 + F-14 decode over-copy fix. | -| v3.2.0 | ✅ | SHA-256 hardware acceleration (Intel SHA-NI). | -| v3.3.0 | ✅ | Incremental HMAC-SHA256 (per-block MAC malloc + copy eliminated). | -| v3.4.0 | ✅ | F-15: Argon2id KDF parameter transparency (self-describing header). | -| v3.5.0 | ✅ | Measured constant-time MAC comparison (dudect-style). | -| v3.6.0 | ✅ | NIST SP 800-38A AES-256-CTR vectors + ML-KEM self-test fixes. | -| v3.7.0 | ✅ | ML-KEM decaps comparison routed through the audited CT primitive. | -| **v4.0.0** | **✅ Current** | **Stack integration release. Codec → canonical VaptVupt 2.60.4 (security: fixes high-severity OOB heap write in AVX2 decode on exact-`content_size` buffers; brings CBMC-verified BCJ with auto-detection; ratio gate verified byte-identical on identical inputs). F-16 disclosed and fixed: ≤3.8.0's divergent pre-release BCJ encoder wrote undecodable archives on executable content at L8/L9 — affected archives must be re-created with 4.0.0. New `--pq-box` recipient mode (ZUPT_ENC_PQ_BOX_V1, 0x05) via vendored libpqvaptvupt 0.6.0: ML-KEM-768 + X25519 through HKDF-SHA256 domain-separated combiner, magic-tagged keyfiles, 13/13 adversarial suite, ASan/UBSan clean. SHA-NI finally measured on capable silicon: 5.8× (204→1184 MB/s) — the v3.2.0 [ESTIMATED] is retired. Clang restored to the strict matrix (as(1) for Jasmin output). Wire v1.6 unchanged; 8-mode back-compat matrix byte-exact. 26 suites, test_vectors 16/0.** | -| v3.8.0 | ✅ Shipped | **Consolidated measured benchmarks (documentation-only; no source/crypto/wire change, v1.6 identical to 3.7.0). New BENCHMARKS.md publishes a complete reproducible benchmark set with the test machine + method stated for every table: compression ratio + encode/decode throughput at L9 across 5 fixtures; encode-speed-vs-level trade-off (L1 ≈88 MB/s at 2.55×, L9 ≈1 MB/s at 3.90×); encryption overhead separating the one-time KDF (Argon2id ≈741 ms, PBKDF2 ≈1562 ms) from per-block crypto (≈147 MB/s) and plain throughput (≈944 MB/s); and a head-to-head ratio comparison vs zstd-3/zstd-19 that plainly shows where VaptVupt loses. Previously the only documented benchmarks were codec-ratio numbers dated v3.1.0; the crypto-path data measured across 3.2.0–3.7.0 was never consolidated. SHA-NI speedup explicitly marked [ESTIMATED] (test box has no SHA-NI). README benchmark section re-dated and linked to BENCHMARKS.md. 24/24 suites green, test_vectors 16/0, F-09 0/1827.** | - -## Planned - -| Version | Status | Description | -|---------|--------|-------------| -| v2.3.0 | ✅ shipped | (see "released" table) | -| v2.3.1 | ✅ shipped | (see "released" table — F-09 closed) | -| v2.1 | 📋 Planned | Homebrew, AUR, Debian, RPM, Nix packages | -| v2.2 | 📋 Planned | Coverity Scan, clang-tidy security checkers, Frama-C Eva analysis | -| v2.3 | 📋 Planned | Silesia corpus benchmarks, performance tuning, NEON ARM64 decode path | -| v3.0 | 🔮 Future | EasyCrypt machine-verified proofs for Jasmin crypto, independent audit | - -## Priority Order - -``` -v1.6 AES-NI wired in ← closes #1 security gap (table-based AES) -v1.7 X25519 Jasmin wired in ← all 4 Jasmin functions active -v1.8 ACSL + Frama-C ← formal memory safety proofs -v1.9 mlock + fuzzing ← closes remaining hardening gaps -v2.0 Performance ← 4× AES throughput, parallel decompression -``` - -## Security Gap Status - -| Gap | Severity | Status | -|-----|----------|--------| -| Table-based AES (cache-timing) | High | **✅ Closed v2.0** — AES-NI Jasmin | -| X25519 fe_cswap CT | Low | **✅ Closed v2.0** — Jasmin | -| No mlock() for keys | Medium | **✅ Closed v2.0** | -| No fuzzing | Medium | **✅ Closed v2.0** — AFL++ | -| ACSL unproved | Low | **✅ Closed v2.0** — 19 contracts | -| No independent audit | Medium | Open — target v3.0 | - ---- - -© 2026 Cristian Cezar Moisés — AGPL-3.0-or-later diff --git a/SECURITY.md b/SECURITY.md index 1a6f17b..3f96b39 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,13 +1,23 @@ -# Security Policy — VaptVupt +# Security Policy — VaptVupt 4.1.0 ## Reporting Vulnerabilities -**Be free to report vulnerabilities. For high-risk send an email.** +Report privately by email to **zupt@riseup.net** with `[security]` in the +subject. Do not open a public issue on the project's git server. -Email: **zupt@riseup.net** +Include: -Include: description, reproduction steps, impact assessment. -Response within 48 hours. Fix within 30 days for critical issues. +- Version (`vaptvupt --version`) and platform. +- Description, impact assessment, and a reproduction (a minimal archive or + a code snippet). + +Disclosure SLA: acknowledgement within 5 business days; target fix within +30 days for high-severity issues. Coordinated disclosure preferred; the +timeline is discussed case by case. A PGP key is on the project's +keyserver entry. + +The project has not had an external independent audit. For high-stakes +deployments, treat it as "reviewed but unaudited" and do your own review. --- @@ -15,11 +25,19 @@ Response within 48 hours. Fix within 30 days for critical issues. | Mode | CLI Flag | Algorithm | PQ-Safe? | Use Case | |------|----------|-----------|----------|----------| -| Password | `-p` | PBKDF2-SHA256 → AES-256-CTR + HMAC-SHA256 | **No** | Short-term backups, personal use | -| PQ Hybrid | `--pq` | ML-KEM-768 + X25519 → AES-256-CTR + HMAC-SHA256 | **Yes** | Long-term archives, high-value data | +| Password | `-p` | PBKDF2-SHA256 → AES-256-CTR + HMAC-SHA256 | No | Short-term backups, personal use | +| PQ Hybrid | `--pq` | ML-KEM-768 + X25519 → AES-256-CTR + HMAC-SHA256 | Yes | Long-term archives, high-value data | | None | (default) | No encryption (compression only) | N/A | Non-sensitive data | -**Password mode (`-p`) is NOT quantum-safe.** For protection against "harvest now, decrypt later" quantum attacks, use `--pq` mode. +Password mode (`-p`) is not quantum-safe. For protection against "harvest +now, decrypt later" quantum attacks, use `--pq` — the recommended +post-quantum mode. `--pq` is native and in-tree; it needs no external +library. + +Optional SDK modes (`--pq-sdk`, `--pq-box`) are available only in an +upstream `make WITH_SDK=1` build linked against the separately distributed +libzuptsdk / libpqvaptvupt libraries. They are not part of the default +build and are not defaults. --- @@ -29,7 +47,8 @@ Response within 48 hours. Fix within 30 days for critical issues. |-----------|-----------|----------|----------|---------------| | Symmetric encryption | AES-256-CTR | FIPS 197 | 256-bit | 128-bit post-quantum (Grover) | | Authentication | HMAC-SHA256 | RFC 2104 | 256-bit | 128-bit post-quantum (Grover) | -| Password KDF | PBKDF2-SHA256 | RFC 8018 | 600K iterations | Password-dependent | +| 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 | | Classical KEM | X25519 | RFC 7748 | 32B scalar | ~128-bit classical | | Hybrid KDF | SHA3-512 | FIPS 202 | 512-bit output | Secure if either KEM holds | @@ -37,6 +56,9 @@ Response within 48 hours. Fix within 30 days for critical issues. | Hashing | SHA3-256, SHA3-512 | FIPS 202 | 256/512-bit | Standard | | Random | OS CSPRNG | getrandom(2) / RtlGenRandom | N/A | Hard fail if unavailable | +The default build uses PBKDF2-SHA256 (600k iterations) for password mode. +Argon2id is available only in a `make WITH_SDK=1` build. + --- ## Security Architecture @@ -54,12 +76,14 @@ For each data block (sequence 0, 1, 2, ...): ### Encrypt-then-MAC -HMAC is computed over `nonce ‖ ciphertext` and verified **before** any decryption. This prevents: +HMAC is computed over `nonce ‖ ciphertext` and verified **before** any +decryption. This prevents: + - Chosen-ciphertext attacks - Padding oracle attacks - Processing of tampered data -### Hybrid Post-Quantum KEM +### Hybrid Post-Quantum KEM (`--pq`) ``` Encapsulation: @@ -73,7 +97,14 @@ Encapsulation: mac_key = archive_key[32:64] ``` -**Security model:** Secure if EITHER ML-KEM-768 (post-quantum, NIST Level 3) OR X25519 (classical, ~128-bit) remains unbroken. Both must be compromised simultaneously to recover the archive key. Same approach as Signal (PQXDH), Apple iMessage (PQ3), and OpenSSH 9.0+. +Security model: secure if EITHER ML-KEM-768 (post-quantum, NIST Level 3) +OR X25519 (classical, ~128-bit) remains unbroken. Both must be compromised +simultaneously to recover the archive key. Same approach as Signal +(PQXDH), Apple iMessage (PQ3), and OpenSSH 9.0+. + +The `--pq-sdk` mode (WITH_SDK=1 only) uses an HKDF-SHA3-256 combiner, a +32-byte key commitment tag, HPKE-style context binding (RFC 9180 §5), +anti-fault double decapsulation, and XChaCha20-Poly1305 AEAD. --- @@ -86,7 +117,10 @@ Encapsulation: | `zupt_mac_verify_ct` | HMAC comparison (32 bytes) | Jasmin type system: no branch on diff value | | `zupt_ct_select_32` | ML-KEM FO implicit rejection | Jasmin type system: no branch on cond value | -These functions are compiled from Jasmin source to x86-64 assembly. The Jasmin compiler enforces that no secret-typed variable flows into branch conditions or memory addresses. This guarantee holds at the machine code level — no C compiler optimization can introduce timing leaks. +These functions are compiled from Jasmin source to x86-64 assembly. The +Jasmin compiler enforces that no secret-typed variable flows into branch +conditions or memory addresses. This guarantee holds at the machine code +level — no C compiler optimization can introduce timing leaks. ### C Constant-Time (branchless, compiler-dependent) @@ -101,7 +135,7 @@ These functions are compiled from Jasmin source to x86-64 assembly. The Jasmin c | Function | Risk | Mitigation | |----------|------|------------| -| AES-256 block encrypt | **HIGH** on shared hardware — S-box table lookups leak via cache timing | Jasmin AES-NI path planned; do not use on multi-tenant VMs | +| AES-256 block encrypt | HIGH on shared hardware — S-box table lookups leak via cache timing | Jasmin AES-NI path planned; do not use on multi-tenant VMs | | SHA-256 | Low — table constants are public, not indexed by secret data | Accepted | --- @@ -114,37 +148,66 @@ These functions are compiled from Jasmin source to x86-64 assembly. The Jasmin c |-------|-----------| | File contents | AES-256-CTR encryption | | File names, sizes, structure | Encrypted in central index block, HMAC-protected | -| Archive integrity (payloads + index) | Per-block HMAC-SHA256 (v2.2.5+ verifier — see CHANGELOG F-06) | -| Archive integrity (header + footer metadata) | **v1.5 archives**: 32-byte archive-integrity-trailer HMAC-SHA256 over `hdr ‖ ft[0..23]`. **v1.4 archives**: not covered, downgrade warning on extract (see F-08 / CHANGELOG 2.3.0). | +| Archive integrity (payloads + index) | Per-block HMAC-SHA256 | +| Archive integrity (header + footer metadata) | v1.5+ archives: 32-byte archive-integrity-trailer HMAC-SHA256 over `hdr ‖ ft[0..23]`. v1.4 archives: not covered, downgrade warning on extract. | | Against stolen backups | AES-256 requires key/password to read | -| Against tampering of file contents, names, sizes, offsets | HMAC detects any modification (overwhelming probability after F-06 fix) | -| Against tampering of header+footer metadata | **v1.5: top-MAC detects tamper.** v1.4: not detected (legacy; re-archive with v2.3.0+ to upgrade). | -| Against tampering of per-block frame preface bytes (codec_id, block_flags, varints, plaintext-XXH64) | **v1.6 (F-09)**: per-block MAC binds the canonical preface AAD; encryption-header block validated structurally. v1.5 and older: partial detection only (parser/decoder rejects malformed values; explicit MAC coverage was v1.6 work). | -| Against tampering of archive comment (when present) | v2.4.3 (F-12): comment block goes through the same per-block AEAD pipeline as data (AES-256-CTR + HMAC-SHA256 + preface AAD); `hdr.comment_offset` pointer is in the AIT-signed region. Both payload and pointer are MAC-covered end-to-end. | -| Against quantum adversary | `--pq` mode: ML-KEM-768 (NIST Level 3) | +| Against tampering of file contents, names, sizes, offsets | HMAC detects any modification | +| Against tampering of per-block frame preface bytes (codec_id, block_flags, varints, plaintext-XXH64) | v1.6: per-block MAC binds the canonical preface AAD; encryption-header block validated structurally | +| Against tampering of archive comment (when present) | Comment block goes through the same per-block AEAD pipeline as data (AES-256-CTR + HMAC-SHA256 + preface AAD); `hdr.comment_offset` pointer is in the AIT-signed region | +| Against block-swap (reorder) attacks | MAC binds an 8-byte position AAD; a block moved to another position fails verification and its partial output is unlinked. Dedup refs use sentinel seq=0 and rely on plaintext XXH64 for per-block integrity. | +| Against malicious archive entries (Zip Slip / path traversal) | `zupt_path_is_safe()` rejects `..`, absolute paths, Windows drive/UNC paths, embedded NULs | +| Against symlink at extract target (TOCTOU) | `zupt_safe_fopen_output()` uses `O_NOFOLLOW` on POSIX. Windows relies on directory ACLs (documented limitation). | +| Against quantum adversary | `--pq` mode: ML-KEM-768 (NIST Level 3) hybridized with X25519 | + +The wire/on-disk format is v1.6. See CHANGELOG.md for the per-release +finding history behind these protections. ### What VaptVupt Does NOT Protect Against | Threat | Reason | Mitigation Path | |--------|--------|----------------| | Attacker who knows the password or has the private key | Fundamental to encryption | Use strong passwords (12+ chars); protect key files | -| Cache-timing side channels (C AES) | Table-based S-box lookups | Build with Jasmin AES-NI when available | +| Endpoint compromise (keylogger, malware on the host) | Outside the archive's trust boundary | Secure the machine where you type the password or hold the key | +| Cache-timing side channels (C AES) | Table-based S-box lookups | Build with Jasmin AES-NI when available; avoid multi-tenant VMs | | Memory forensics during operation | Keys on stack during compress/extract | `zupt_secure_wipe()` on completion; `mlock()` planned | | Deniability | Archive header identifies format | `.zupt` magic bytes visible; ENCRYPTED flag in header | -| Weak passwords | Argon2id (default, v2.4.1+) is memory-hard and adds ~25–30 bits of work factor vs ~20 for PBKDF2. PBKDF2-SHA256 with 600k iterations available via `--kdf pbkdf2` for legacy reader compatibility. | Use `--pq` or `--pq-sdk` mode for critical data — keys are random, not derived from a password. | -| Traffic analysis | Archive size reveals data volume | Outside VaptVupt's scope | -| File permission/ownership | Not stored in archive | Documented in README.md (Architecture & platform support) | +| Weak passwords | PBKDF2-SHA256 (600k) is the default KDF; Argon2id (memory-hard) is available in a WITH_SDK=1 build | Use `--pq` mode for critical data — keys are random, not derived from a password | +| Traffic analysis / metadata | Archive size reveals data volume; file list, sizes, mtimes not padded | Outside VaptVupt's scope | +| File permission/ownership | Not stored in archive | Documented in README.md | +| Spectre-class side channels in callers | Below the constant-time primitive layer | Host OS / compiler mitigations | ### Quantum Threat Analysis -**Scenario:** Adversary captures encrypted archive today, stores it, and attempts decryption when a cryptographically-relevant quantum computer is available. +Scenario: adversary captures an encrypted archive today, stores it, and +attempts decryption when a cryptographically-relevant quantum computer is +available. | Mode | Classical Security | Quantum Security | Verdict | |------|-------------------|-----------------|---------| -| Password (`-p`) | Password-dependent + 256-bit AES | ~128-bit (Grover on AES) but PBKDF2 accelerated | **Vulnerable** — use `--pq` | -| PQ Hybrid (`--pq`) | ~128-bit (X25519) | NIST Level 3 (ML-KEM-768) | **Protected** | +| Password (`-p`) | Password-dependent + 256-bit AES | ~128-bit (Grover on AES), PBKDF2 accelerated | Vulnerable — use `--pq` | +| PQ Hybrid (`--pq`) | ~128-bit (X25519) | NIST Level 3 (ML-KEM-768) | Protected | -In `--pq` mode: even if Shor's algorithm breaks X25519, ML-KEM-768 protects the archive. Even if a novel classical attack breaks ML-KEM, X25519 still provides ~128-bit security. The hybrid design ensures the archive is secure if **either** component holds. +In `--pq` mode: even if Shor's algorithm breaks X25519, ML-KEM-768 +protects the archive; even if a novel classical attack breaks ML-KEM, +X25519 still provides ~128-bit security. The hybrid design is secure if +either component holds. + +### Extracting untrusted archives — operational guidance + +The in-binary defenses are the primary control; the following are defense +in depth: + +1. Extract into a dedicated empty directory (not `~/Downloads` or `/tmp`). +2. Audit symlinks in the target directory before extraction. +3. Run extraction as a low-privilege user, never root. +4. On Windows, pre-create the target directory with restrictive ACLs + (the `O_NOFOLLOW` defense is POSIX-only). + +### Out of scope + +- External independent audit. +- Side-channel testing on production hardware (timing leaks). +- Formal verification beyond the Jasmin constant-time primitives. --- @@ -152,11 +215,14 @@ In `--pq` mode: even if Shor's algorithm breaks X25519, ML-KEM-768 protects the | Platform | Primary Source | Fallback | Failure Mode | |----------|---------------|----------|--------------| -| Linux | `getrandom(2)` | `/dev/urandom` | **Hard exit** — no encryption without CSPRNG | -| macOS | `/dev/urandom` | None | **Hard exit** | -| Windows | `RtlGenRandom` | None | **Hard exit** | +| Linux | `getrandom(2)` | `/dev/urandom` | Hard exit — no encryption without CSPRNG | +| macOS | `/dev/urandom` | None | Hard exit | +| Windows | `RtlGenRandom` | None | Hard exit | -There is no `rand()`, `srand()`, or any weak PRNG fallback anywhere in the codebase. If the OS CSPRNG is unavailable, VaptVupt exits with an error. This is a deliberate design choice — weak random keys are worse than no encryption. +There is no `rand()`, `srand()`, or any weak PRNG fallback anywhere in the +codebase. If the OS CSPRNG is unavailable, VaptVupt exits with an error. +This is a deliberate design choice — weak random keys are worse than no +encryption. --- @@ -164,7 +230,7 @@ There is no `rand()`, `srand()`, or any weak PRNG fallback anywhere in the codeb | Platform | Compiler | Threading | CSPRNG | Status | |----------|----------|-----------|--------|--------| -| Linux x86-64 | GCC 5+ / Clang 3.5+ | pthreads | `getrandom(2)` | **Primary** | +| Linux x86-64 | GCC 5+ / Clang 3.5+ | pthreads | `getrandom(2)` | Primary | | Linux ARM64 | GCC 5+ | pthreads | `getrandom(2)` | Tested | | macOS x86-64/ARM64 | Apple Clang | pthreads | `/dev/urandom` | Tested | | Windows x86-64 | MinGW / MSVC 2015+ | Win32 threads | `RtlGenRandom` | Tested | @@ -172,34 +238,23 @@ There is no `rand()`, `srand()`, or any weak PRNG fallback anywhere in the codeb --- -## Disclosure Timeline - -| Date | Event | -|------|-------| -| 2026-01-01 | v0.1.0 — Initial release | -| 2026-03-21 | v0.5.1 — 16 security bug fixes including CSPRNG hardening | -| 2026-03-21 | v1.0.0 — 5 critical ML-KEM bugs fixed, format frozen | -| 2026-03-28 | v1.1.0 — X25519 formula bug fixed (not interoperable with RFC 7748) | -| 2026-03-28 | v1.5.0 — Jasmin assembly linked (MAC verify + ML-KEM select) | - ---- - ## Verification Commands -Anyone can verify every security claim: +Anyone can verify the security claims. The default build needs only a C +compiler + make (plus libm/pthread); no external crypto library. ```bash # Build -make # Zero warnings +make -# All functional tests -make test-all # 62/62 pass +# Functional tests +make test-all # Memory safety -make test-asan # Zero ASAN/UBSAN errors +make test-asan # NIST/RFC test vectors -make test-vectors && ./test_vectors # 13/13 pass +make test-vectors && ./test_vectors # Verify Jasmin symbols are active nm vaptvupt | grep "zupt_mac_verify_ct\|zupt_ct_select_32" @@ -213,174 +268,4 @@ jasminc -arch x86-64 -o /dev/null jasmin/zupt_mlkem_select.jazz --- -© 2026 Cristian Cezar Moisés — AGPL-3.0-or-later - -## Production deployment notes (v2.2.1) - -VaptVupt is deployed in production environments. The following supported -configurations are considered current and receive security fixes: - -| Channel | Supported | Notes | -|---|---|---| -| 2.2.x (latest) | Yes | Recommended for new deployments | -| 2.1.x | Yes (security only) | Supported through 2026-Q4 | -| 2.0.x | No | End of life | -| 1.x | No | End of life | - -### Recommended configuration - -For new archives, use the libzuptsdk-backed mode: - -```bash -vaptvupt keygen --sdk -o key.priv -vaptvupt c --pq-sdk key.priv.pub backup.zupt /path/to/data -vaptvupt x --pq-sdk key.priv backup.zupt -``` - -This selects: - -- ML-KEM-768 + X25519 hybrid KEM with HKDF-SHA3-256 combiner (RFC-style - KDF rather than ad-hoc XOR construction) -- 32-byte HKDF-derived key commitment tag (protects against partitioning - oracle attacks across recipients) -- HPKE-style context binding (RFC 9180 §5) -- Anti-fault double ML-KEM decapsulation -- XChaCha20-Poly1305 AEAD with 24-byte random nonces -- Argon2id (RFC 9106 OWASP minimums) when password mode is used - -### Threat model - -VaptVupt assumes: - -- The recipient's private key file is kept secret and is not exfiltrated. -- The execution environment has a working `getrandom(2)` / `/dev/urandom`. -- The archive metadata (file list, sizes, mtimes) is not considered - confidential. Padding to hide file sizes is not implemented. -- An attacker may have full write access to the archive in transit; AEAD - + commitment + HPKE binding ensures any modification is detected. - -VaptVupt does **not** defend against: - -- Endpoint compromise (keylogger, malware on the machine where you type - the password or hold the private key). -- Side-channel attacks against the host OS that bypass the constant-time - Jasmin-verified primitives (e.g. Spectre v1 in callers). -- Quantum attacks against X25519 alone — but the ML-KEM-768 component - guarantees post-quantum security via the hybrid KDF. - -### Reporting findings - -If you find a security issue: - -1. **Do not** open a public issue on the project's git server. -2. Email `zupt@riseup.net` with subject `SECURITY: `. -3. Include the version (`vaptvupt --version`), platform, and a - reproduction (a minimal archive or a code snippet). -4. Expect acknowledgement within 7 days. Coordinated disclosure - timeline will be discussed case by case. - -### Disclosure history - -| Date | Version | Findings | Severity | -|---|---|---|---| -| 2026-04-27 | 2.2.1 | 6 internally-found bugs (audit pass) | 2 high, 1 medium, 3 low | - ---- - -## v2.2.1 audit findings - -The 2.2.1 release fixed six bugs found by code review and added a 10-check -double-validated audit test suite. Detailed root-cause analysis for each -finding is in `CHANGELOG.md` under the 2.2.1 entry. - -| # | Severity | Component | Bug | -|---|---|---|---| -| 1 | Low (correctness) | format parser | varint reader truncated values at 2^63 | -| 2 | Medium (data loss) | extract path | unchecked `fwrite` in 6 call sites — silent corruption on disk-full | -| 3 | Low (defense-in-depth) | SDK keyring | `mac_key` was a copy of `enc_key` rather than KDF-derived | -| 4 | High (memory safety) | LZ decoder | `size_t` overflow in length accumulator could enable out-of-bounds copy | -| 5 | Medium (DoS / amplification) | dedup ref blocks | unbounded forward offset + recursion accepted | -| 6 | Low (UX) | encrypt path | partial archive left on disk after encrypt-init failure | - -All six fixed in 2.2.1. Regression tests added. - -## Reporting vulnerabilities - -Email `zupt@riseup.net` with `[security]` in the subject. PGP key on the -project's keyserver entry. Coordinated disclosure preferred; we will -acknowledge within 5 business days and aim for a fix within 30 days for -high-severity issues. - -The project does not yet have an external audit. The 2.2.1 audit pass was -internal code review combined with the 169-check libzuptsdk audit suite -inherited via vendored linkage. For high-stakes deployments, treat this as -"reviewed but unaudited" and do your own review. - ---- - -## v2.2.2 formal audit findings (2026-04-27) - -A formal cryptographic audit pass was conducted using the methodology -documented in `FORMAL_AUDIT_PROMPT.md` (auditor profile: senior -cryptographic engineer with 15+ years of production crypto systems -experience). Two security-relevant bugs and two robustness bugs found -and fixed; version unchanged at 2.2.2 — same release with hardened -internals. - -| # | Severity | Component | Bug | -|---|---|---|---| -| 11 | **HIGH** | extract path | Zip Slip / path traversal — `e->path` from archive used directly in `fopen` | -| 12 | **MEDIUM** | extract output | symlink-following — `fopen "wb"` followed symlinks at output target | -| 13 | LOW (32-bit only) | size cap | 4 GiB cap exceeds `size_t` on 32-bit | -| 14 | LOW (32-bit only) | calloc on parsed count | `count * sizeof(entry)` overflowed `size_t` before calloc internal check | - -All four fixed and regression-tested. - -### Threat model coverage (post-audit) - -The following attack vectors are now explicitly defended against: - -- **Malicious archive with path-traversal entries** (Zip Slip 2018 pattern): - rejected by `zupt_path_is_safe()` — blocks `..`, absolute paths, Windows - drive letters, UNC paths, embedded NULs. -- **Symlink at extract target** (TOCTOU pre-extraction): refused by - `zupt_safe_fopen_output()` using `O_NOFOLLOW` on POSIX. Windows path - unchanged — relies on directory ACLs (documented limitation). -- **Malformed archive headers**: bounds-checked offsets (`encryption_header_off`, - `index_offset`); rejected if outside file size. -- **Format parser overflow**: varint truncation, dedup-ref recursion, - realloc-pair atomicity, length-overflow in LZ decoder — all fixed in - prior 2.2.x sprints. -- **Cryptographic key reuse / nonce misuse**: per-block nonce is `base ⊕ - block_seq`; `base_nonce` is per-archive random; mac_key is KDF-split - from enc_key (defense in depth even though SDK path doesn't use it). -- **Block-swap (reorder) attack on encrypted archives** (bug #16, fixed - in 2.2.2 god-tier audit): MAC binds 8-byte AAD seq computed as - `((file_index_in_archive + 1) << 32) | per_file_block_seq`. An attacker - who swaps two valid encrypted blocks between positions in the archive - produces blocks whose AAD no longer matches their position; both MAC - candidates (v2 with AAD, v1 legacy fallback) reject the swapped block. - Empty/partial output files are `unlink()`'d on auth failure. - Limitation: dedup mode uses sentinel seq=0 (refs can't derive source - AAD); plaintext XXH64 still provides per-block integrity. - -### Path traversal — operational guidance - -Even with the in-binary defenses, operators extracting untrusted archives -should: - -1. Extract into a dedicated empty directory (not `~/Downloads` or `/tmp`). -2. Audit symlinks in the target directory before extraction. -3. Run extraction as a low-privilege user, never root. -4. On Windows, pre-create the target directory with restrictive ACLs - (the `O_NOFOLLOW` defense is POSIX-only). - -These are belt-and-suspenders — the in-binary defenses are the primary -control, but defense in depth is good practice. - -### Out-of-scope (still) - -- External independent audit (cost-bound, on roadmap) -- Side-channel testing on production hardware (timing leaks) -- Formal verification beyond Jasmin constant-time primitives - +© 2026 Cristian Cezar Moisés — AGPL-3.0-or-later (dual-licensed AGPL + commercial) diff --git a/THIRD-PARTY-NOTICES.md b/THIRD-PARTY-NOTICES.md index 65cacbf..60fedea 100644 --- a/THIRD-PARTY-NOTICES.md +++ b/THIRD-PARTY-NOTICES.md @@ -1,36 +1,25 @@ THIRD-PARTY NOTICES =================== -**VaptVupt contains no third-party source code.** Every line of source in -this repository is the work of Cristian Cezar Moisés. This document -exists for transparency about runtime dependencies and build-time -tools. - -If you redistribute VaptVupt, you must preserve this attribution document -along with the LICENSE file. +This document records VaptVupt's runtime dependencies and build-time +tools. If you redistribute VaptVupt, you must preserve this attribution +document along with the LICENSE file. ------------------------------------------------------------------------- -Components shipped in this repository (all original work) +Licensing ------------------------------------------------------------------------- -| Component | Location | License | Author | -|---|---|---|---| -| vaptvupt CLI | src/, include/ | AGPL-3.0-or-later | Cristian Cezar Moisés | -| libzuptsdk | sdk/, vendor/zuptsdk/include/ | AGPL-3.0-or-later | Cristian Cezar Moisés | -| VaptVupt LZ codec | src/vv_*.c, src/vaptvupt_api.c, include/vaptvupt*.h | **GPL-3.0-or-later** | Cristian Cezar Moisés | -| Jasmin constant-time crypto | jasmin/*.jazz, jasmin/*.s | AGPL-3.0-or-later | Cristian Cezar Moisés | -| VaptVupt GUI (Python) | gui/ | AGPL-3.0-or-later | Cristian Cezar Moisés | +**Note on VaptVupt LZ codec licensing**: the VaptVupt LZ codec +(src/vv_*.c, src/vaptvupt_api.c, include/vaptvupt*.h) is licensed +GPL-3.0-or-later (not AGPL like the rest of the project) so that, with +sufficient maturity, it can be considered for upstreaming into the Linux +or BSD kernels, which require GPL-compatible licenses. The author retains +the right to dual-license the codec under other terms for commercial use; +contact sac@securityops.co for inquiries. -**Note on VaptVupt licensing**: VaptVupt is licensed GPL-3.0-or-later -(not AGPL like the rest of VaptVupt) so that, with sufficient maturity, it -can be considered for upstreaming into the Linux or BSD kernels, which -require GPL-compatible licenses. The author retains the right to dual- -license VaptVupt under other terms for commercial use; contact -sac@securityops.co for inquiries. - -The rest of the project (vaptvupt CLI, libzuptsdk, Jasmin source, GUI) is -licensed AGPL-3.0-or-later. Commercial licenses (relief from AGPL -network-use clause) are available; contact sac@securityops.co. +The rest of the project (vaptvupt CLI, Jasmin source, GUI) is licensed +AGPL-3.0-or-later. Commercial licenses (relief from the AGPL network-use +clause) are available; contact sac@securityops.co. ------------------------------------------------------------------------- Build-time tool (not redistributed) @@ -59,12 +48,15 @@ at runtime and are NOT redistributed as part of VaptVupt. **libargon2** — Argon2id password hashing function (RFC 9106) + Required only for: the optional `make WITH_SDK=1` build. The default + build uses native PBKDF2-SHA256 and does not link + libargon2. Linked at runtime: libargon2.so.1 Version expected: 1.0+ (Debian/Ubuntu: libargon2-1) Upstream: https://github.com/P-H-C/phc-winner-argon2 License: Apache-2.0 OR CC0-1.0 (dual) Copyright: (c) 2015 The Argon2 Authors - Used by: Password-derived encryption mode + Used by: Argon2id password-derived encryption mode **OpenSSL libcrypto** — AES, SHA-256, AES-NI hardware backends @@ -79,9 +71,11 @@ at runtime and are NOT redistributed as part of VaptVupt. Compatibility with public standards ------------------------------------------------------------------------- -Where VaptVupt implements public standards, it does so independently -from any reference implementation. No code has been copied from -external projects. Standards followed: +Where VaptVupt implements public standards, it does so independently from +any reference implementation. Other projects in the post-quantum hybrid +encryption space (libsodium, age, Tink, rustls, etc.) were referenced as +prior art during design, but no code was copied from any external +project. Standards followed: - FIPS 197 (AES) - FIPS 202 (Keccak / SHA-3) @@ -94,11 +88,6 @@ external projects. Standards followed: - RFC 9106 (Argon2) - RFC 9180 (HPKE) -The VaptVupt project was designed independently. Other projects in the -post-quantum hybrid encryption space (libsodium, age, Tink, rustls, -etc.) were referenced as prior art during design but no code was -copied. VaptVupt does not include any code from these projects. - ------------------------------------------------------------------------- Reporting attribution issues ------------------------------------------------------------------------- @@ -114,8 +103,8 @@ with the subject "[third-party]" and details of the issue. License summary ------------------------------------------------------------------------- - VaptVupt CLI, libzuptsdk, Jasmin source, GUI: AGPL-3.0-or-later - VaptVupt LZ codec: GPL-3.0-or-later - Commercial license (any component): contact sac@securityops.co + VaptVupt CLI, Jasmin source, GUI: AGPL-3.0-or-later + VaptVupt LZ codec: GPL-3.0-or-later + Commercial license (any component): contact sac@securityops.co Project home: https://git.securityops.co/cristiancmoises/vaptvupt diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md index c20c5dc..8147b47 100644 --- a/THREAT_MODEL.md +++ b/THREAT_MODEL.md @@ -10,21 +10,37 @@ trusting VaptVupt with anything you can't afford to lose. ## TL;DR -VaptVupt is designed for **at-rest backup encryption** by someone who +VaptVupt is designed for at-rest backup encryption by someone who controls the machine doing the encryption and the machine doing the -extraction. It is **not** a network protocol, a multi-party scheme, or +extraction. It is not a network protocol, a multi-party scheme, or a substitute for full-disk encryption. | Use case | VaptVupt is appropriate? | |---|---| -| Backing up files to an untrusted cloud (S3, Backblaze, Google Drive) | **Yes** | -| Backing up a disk image to external media you might lose | **Yes** | -| Long-term archival of personal/business data | **Yes** | -| Sharing an encrypted archive with someone you trust to handle the key | **Yes, with care** (see "Key distribution" below) | -| Real-time encrypted communication | **No** (use Signal, age, or TLS) | -| Multi-party access (n-of-m) | **No** (no threshold scheme) | -| Hiding the existence of an archive (steganography) | **No** (archive header has fixed magic bytes) | -| Protecting against a hostile machine you're encrypting on | **No** (a compromised host can read plaintext before encryption) | +| Backing up files to an untrusted cloud (S3, Backblaze, Google Drive) | Yes | +| Backing up a disk image to external media you might lose | Yes | +| Long-term archival of personal/business data | Yes | +| Sharing an encrypted archive with someone you trust to handle the key | Yes, with care (see "Key distribution" below) | +| Real-time encrypted communication | No (use Signal, age, or TLS) | +| Multi-party access (n-of-m) | No (no threshold scheme) | +| Hiding the existence of an archive (steganography) | No (archive header has fixed magic bytes) | +| Protecting against a hostile machine you're encrypting on | No (a compromised host can read plaintext before encryption) | + +--- + +## Modes referenced in this document + +- `-p` / password mode: symmetric encryption with a key derived from a + password. The default build derives the key with PBKDF2-SHA256 + (600k iterations). Argon2id is available only in an upstream + `make WITH_SDK=1` build against the separately distributed + libraries. +- `--pq`: native post-quantum mode (ML-KEM-768 + X25519), the PQ mode + in the default build. The ML-KEM-768 implementation is in-tree. +- `--pq-sdk` / `--pq-box`: optional post-quantum modes backed by the + separately distributed `libzuptsdk` / `libpqvaptvupt` libraries. + Available only in a `make WITH_SDK=1` build. Key files for these + modes are produced by `vaptvupt keygen --sdk`, also SDK-only. --- @@ -34,31 +50,27 @@ a substitute for full-disk encryption. An attacker with read access to the archive bytes cannot recover plaintext file contents, file names, file sizes, file modes, or -embedded comments **without the key/password**, assuming: +embedded comments without the key/password, assuming: -- The chosen mode is one of the encrypted modes (`-p`, `--pq`, `--pq-sdk`, or `--pq-box`) +- The chosen mode is one of the encrypted modes (`-p`, `--pq`, or the + optional `--pq-sdk` / `--pq-box`) - The password is strong enough to resist offline brute-force - (Argon2id default with m=64 MB, t=3, p=4 makes this very expensive - but not infinite — see "Password strength" below) -- The key file (for `--pq-sdk` / `--pq-box`) was not compromised at generation time + (see "Password strength" below) +- The key file (for `--pq-sdk` / `--pq-box`) was not compromised at + generation time ### 2. Integrity of every byte of an encrypted archive If any single bit of the on-disk archive bytes is flipped, the -extraction **must fail** with an authentication error. This has been -verified by the v1.6 exhaustive byte sweep: +extraction fails with an authentication error. Coverage layers: -- 0 silent-accept positions out of 1827 (encrypted, no comment) -- 0 silent-accept positions out of 1878 (encrypted, with comment) - -Coverage layers: - -- **Per-block HMAC-SHA256** with frame-preface AAD (F-09): every data +- Per-block HMAC-SHA256 with frame-preface AAD (F-09): every data block carries an HMAC over its ciphertext and over the canonical - 29-byte preface (block_type, codec_id, block_flags, sizes, plaintext-XXH64) -- **Archive Integrity Trailer (F-08)**: HMAC-SHA256 over the - 64-byte header and 24 bytes of footer, appended after the footer -- **Strict structural validation of the encryption-header block (F-09)**: + 29-byte preface (block_type, codec_id, block_flags, sizes, + plaintext-XXH64) +- Archive Integrity Trailer (F-08): HMAC-SHA256 over the 64-byte + header and 24 bytes of footer, appended after the footer +- Strict structural validation of the encryption-header block (F-09): codec must be `STORE`, flags must be 0, csz must equal usz, the plaintext XXH64 must match @@ -66,10 +78,10 @@ Coverage layers: Plaintext archives (no `-p`, no `--pq*`) are protected by XXH64 plaintext checksums per block plus structural validation. This is -**not cryptographic integrity** — a determined attacker with -write access can produce a tampered plaintext archive that passes -the checksum (XXH64 is not collision-resistant). It does catch -accidental corruption and naive tampering. +not cryptographic integrity — a determined attacker with write access +can produce a tampered plaintext archive that passes the checksum +(XXH64 is not collision-resistant). It does catch accidental +corruption and naive tampering. Use an encrypted mode if you need cryptographic integrity. @@ -87,33 +99,34 @@ also constant (HMAC is always run, branchless return). The detailed cause is available via `--verbose` for debugging on machines under the user's own control. -### 5. Post-quantum forward secrecy (in `--pq-sdk` mode) +### 5. Post-quantum forward secrecy (`--pq` and optional `--pq-sdk`) -`--pq-sdk` 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) 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) - X25519 hybridization protects against an unforeseen ML-KEM break - The recipient's private key is not later compromised +The optional `--pq-sdk` mode provides the same hybrid guarantee via +the separately distributed SDK libraries. + ### 6. Side-channel resistance for cryptographic primitives The hot crypto paths (AES-256-CTR, HMAC-SHA256 comparison, X25519 field operations, ML-KEM polynomial arithmetic) are implemented in Jasmin and proved constant-time at the assembly level on x86_64. -Non-Jasmin platforms (aarch64, fallback x86_64) use careful C -implementations that avoid secret-dependent branches and memory -accesses where feasible — but **without formal proof**. +Non-Jasmin platforms (aarch64, fallback x86_64) use C implementations +that avoid secret-dependent branches and memory accesses where +feasible — but without formal proof. --- ## What VaptVupt does NOT protect against -This list is **exhaustive of the major omissions** — if you have a -concern that doesn't appear here, please file an issue. - ### 1. Compromised endpoints VaptVupt cannot protect against: @@ -134,7 +147,7 @@ If you don't trust the machine, VaptVupt cannot help. If the password or `~/.zupt-key` is leaked: - All archives encrypted with that key are decryptable -- VaptVupt has **no forward secrecy across archives** — each archive +- VaptVupt has no forward secrecy across archives — each archive is encrypted under a single static key derived from the password or stored in the key file - There is no key-rotation feature; rotate by re-encrypting @@ -147,37 +160,33 @@ another layer (e.g. on an encrypted USB), and rotate periodically. ### 3. Password strength -Argon2id with m=64 MB, t=3, p=4 makes a single guess cost roughly -~200 ms on commodity hardware. That's **not enough** to protect a -short, common password against a determined attacker with GPU -clusters or cloud compute. +Password mode derives the key with PBKDF2-SHA256 (600k iterations) +in the default build, or Argon2id in a `make WITH_SDK=1` build. A +key derivation function slows offline guessing but does not make a +short, common password safe: a determined attacker with GPU clusters +or cloud compute can still exhaust a weak password. -| Password type | Approximate brute-force resistance with Argon2id | -|---|---| -| 6-char common word | Hours to days | -| 10-char mixed alphanumeric | Years on a single GPU; days on a cluster | -| 6-word diceware passphrase | Centuries to millennia even with cloud-scale resources | -| Random 16-char with full alphabet | Infeasible without quantum breakthrough | - -For critical data, use `--pq-sdk` mode with a random key file -generated by `vaptvupt keygen --sdk` — the key is 64 bytes of CSPRNG -output, not derived from human-typed text. +Use a long, high-entropy password — a multi-word diceware passphrase +or a random 16+ character string with a full alphabet. For critical +data, use a key-file mode (native `--pq`, or the optional `--pq-sdk` +with a random key file from `vaptvupt keygen --sdk`) so the key is +CSPRNG output, not derived from human-typed text. ### 4. Metadata leakage from archive structure Even with encryption, an attacker who can see the archive bytes can infer: -- **Approximate file count** (from `total_blocks` in the footer) -- **Total archive size** (file size on disk) -- **Whether the archive is encrypted at all** (`ZUPT_FLAG_ENCRYPTED` +- Approximate file count (from `total_blocks` in the footer) +- Total archive size (file size on disk) +- Whether the archive is encrypted at all (`ZUPT_FLAG_ENCRYPTED` in the global flags is visible) -- **Whether the archive is solid or per-file mode** (visible flag) -- **Whether post-quantum mode is in use** (visible flag) -- **Approximate file size distribution** (block sizes are visible +- Whether the archive is solid or per-file mode (visible flag) +- Whether post-quantum mode is in use (visible flag) +- Approximate file size distribution (block sizes are visible even when block payloads are encrypted) -- **Archive creation time** (a 64-bit timestamp in the header) -- **A random 16-byte UUID per archive** (no information leak, but +- Archive creation time (a 64-bit timestamp in the header) +- A random 16-byte UUID per archive (no information leak, but globally identifies the archive across copies) If metadata privacy matters, layer VaptVupt under another tool that @@ -197,8 +206,8 @@ VaptVupt is not a network protocol. There is no: ### 6. Multi-party schemes -There is **no threshold cryptography, no n-of-m sharing, no -multi-party computation, no proxy re-encryption**. Each archive +There is no threshold cryptography, no n-of-m sharing, no +multi-party computation, no proxy re-encryption. Each archive has exactly one decryption credential (one password OR one recipient key). To give two people access to the same archive, they must share the password or the key file. @@ -207,7 +216,7 @@ they must share the password or the key file. VaptVupt archives have a fixed 6-byte magic `\x90\x5a\x55\x50\x54\x01` at offset 0. Anyone scanning the bytes can see it's a VaptVupt -archive. VaptVupt has **no hidden-volume or duress-password feature**. +archive. VaptVupt has no hidden-volume or duress-password feature. ### 8. Side channels we don't claim to address @@ -219,29 +228,27 @@ archive. VaptVupt has **no hidden-volume or duress-password feature**. ### 9. Trusted setup of post-quantum primitives -The ML-KEM-768 implementation lives in `libzuptsdk` and was not -independently audited at the time of writing. We use NIST KAT -vectors for correctness verification but have not formally proven -constant-time properties for every PQ code path. +The in-tree ML-KEM-768 implementation was not independently audited +at the time of writing. We use NIST KAT vectors for correctness +verification but have not formally proven constant-time properties +for every PQ code path. -For maximum assurance, treat `--pq-sdk` as the post-quantum -**hedge** — it does not replace the X25519 layer; both must be -broken for an attacker to recover plaintext. +For maximum assurance, treat the post-quantum layer as a hedge — it +does not replace the X25519 layer; both must be broken for an +attacker to recover plaintext. ### 10. Format extension attacks The format is versioned (v1.6). Older readers may accept newer archives in unexpected ways. We try to maintain forward -compatibility (v2.4.5 readers correctly handle v1.6 archives -including encrypted comments and the Argon2id KDF path), but a -careful attacker who can produce malformed-but-just-valid -archives may find parser-state issues that don't rise to the -level of a CVE. The fuzzing harness (`make fuzz-format`) is the -primary mitigation; report bugs. +compatibility, but a careful attacker who can produce +malformed-but-just-valid archives may find parser-state issues that +don't rise to the level of a CVE. The fuzzing harness +(`make fuzz-format`) is the primary mitigation; report bugs. ### 11. Compression-side-channel attacks (CRIME / BREACH style) -VaptVupt compresses **before** encryption. If an attacker can: +VaptVupt compresses before encryption. If an attacker can: - Influence part of the plaintext (e.g. inject a known prefix) - Observe the resulting archive size precisely @@ -251,9 +258,9 @@ the rest of the plaintext — this is the classic CRIME/BREACH attack against TLS compression. VaptVupt is designed for offline backup, where attacker-controlled -plaintext injection is rare. **If your threat model includes +plaintext injection is rare. If your threat model includes attacker-chosen plaintext mixed with secret plaintext in the same -archive**, use `--no-compress` (codec 0 = STORE) to disable the +archive, use `--no-compress` (codec 0 = STORE) to disable the LZ codec and eliminate this side channel. --- @@ -266,9 +273,9 @@ VaptVupt's security rests on the following standard assumptions: |---|---| | AES-256-CTR is a secure stream cipher | All encrypted archives become readable | | HMAC-SHA256 is a secure PRF / MAC | Tamper detection fails; integrity can be forged | -| Argon2id is a secure password KDF | Password-mode archives become brute-forceable faster | -| ML-KEM-768 retains NIST Category 3 security | `--pq-sdk` mode reduces to the X25519 layer | -| X25519 retains 128-bit security (no quantum) | `--pq-sdk` mode reduces to the ML-KEM layer; legacy `--pq` mode broken | +| PBKDF2-SHA256 (or Argon2id, WITH_SDK) is a secure password KDF | Password-mode archives become brute-forceable faster | +| ML-KEM-768 retains NIST Category 3 security | `--pq` / `--pq-sdk` reduce to the X25519 layer | +| X25519 retains 128-bit security (no quantum) | PQ modes reduce to the ML-KEM layer; classical password mode unaffected | | HKDF-SHA256 is a secure key-derivation construction | Combined PQ + classical keys may be predictable | | SHA3 / SHAKE retain pre-image and collision resistance | Auxiliary protocol bindings may be forged | @@ -298,8 +305,7 @@ normally. ## Document version -- **v1.0** (sprint 2.4.6): initial threat model. Covers archive - format v1.6. -- Document is part of the source tree (`THREAT_MODEL.md`) and - versioned with the project; this section will be updated as - the format evolves. +This threat model covers archive format v1.6 as shipped in VaptVupt +4.1.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. diff --git a/doc/vaptvupt.1 b/doc/vaptvupt.1 index 7ee0eb2..2b54d3c 100644 --- a/doc/vaptvupt.1 +++ b/doc/vaptvupt.1 @@ -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 "May 2026" "vaptvupt 4.0.0" "User Commands" +.TH VAPTVUPT 1 "July 2026" "vaptvupt 4.1.0" "User Commands" .SH NAME vaptvupt \- post-quantum backup compression utility (formerly zupt) @@ -205,7 +205,8 @@ Force a specific codec by id. Accepted values: .TP .BR -p " " \fIpassword\fR -Enable password-based encryption (Argon2id KDF by default since v2.4.1). +Enable password-based encryption (PBKDF2-SHA256 KDF; Argon2id is available +only in a WITH_SDK=1 build via \fB--kdf argon2id\fR). Reading the password from a flag exposes it in .BR ps (1) output; prefer @@ -517,7 +518,7 @@ On random / already-compressed data, all codecs hit the incompressibility wall; .SH EXAMPLES .PP -Compress with default settings (Argon2id password, VaptVupt level 7, multi-threaded): +Compress with default settings (PBKDF2-SHA256 password, VaptVupt level 7, multi-threaded): .RS .nf @@ -530,8 +531,9 @@ Compress with post-quantum hybrid encryption to a published public key: .RS .nf $ vaptvupt keygen --sdk -o ~/.config/vaptvupt-mykey -$ vaptvupt keygen --sdk --pub -o mykey.pub -k ~/.config/vaptvupt-mykey -$ vaptvupt compress --pq-sdk mykey.pub backup.zupt ~/Documents +$ vaptvupt keygen -o ~/.config/vaptvupt-mykey +$ vaptvupt keygen --pub -o mykey.pub -k ~/.config/vaptvupt-mykey +$ vaptvupt compress --pq mykey.pub backup.zupt ~/Documents .fi .RE diff --git a/gui/README.md b/gui/README.md index 6ca2fe1..624bf7a 100644 --- a/gui/README.md +++ b/gui/README.md @@ -1,4 +1,4 @@ -# VaptVupt GUI — Cross-Platform Post-Quantum Backup +# VaptVupt GUI Desktop application for [vaptvupt](https://git.securityops.co/cristiancmoises/vaptvupt) backup compression with ML-KEM-768 + X25519 post-quantum hybrid encryption. @@ -14,9 +14,6 @@ tar xzf vaptvupt-gui.tar.gz && cd vaptvupt-gui ./install.sh --user # adds right-click menu integration ``` -After install, right-click any file in Nemo/Nautilus to see "Compress with VaptVupt". -Double-click any .zupt file to open it in the GUI. - ### Windows **Option A — Installer (recommended):** @@ -119,7 +116,7 @@ The GUI calls the vaptvupt CLI binary — all cryptography runs in native C, not ## Credits -- **vaptvupt** v2.2.3 — Cristian Cezar Moisés ([github](https://git.securityops.co/cristiancmoises/vaptvupt)) +- **vaptvupt** v4.1.0 — Cristian Cezar Moisés ([github](https://git.securityops.co/cristiancmoises/vaptvupt)) ## License diff --git a/include/zupt.h b/include/zupt.h index bf2025c..c295c6c 100644 --- a/include/zupt.h +++ b/include/zupt.h @@ -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.0.0" +#define ZUPT_VERSION_STRING "4.1.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" @@ -177,6 +177,12 @@ #define ZUPT_HMAC_SIZE 32 #define ZUPT_AES_KEY_SIZE 32 #define ZUPT_KDF_ITERATIONS 600000 +/* SECURITY (DoS guard): the PBKDF2 iteration count is read from the archive + * header, which is attacker-controlled. The writer only ever stamps + * ZUPT_KDF_ITERATIONS; a crafted archive could demand 2^32-1 iterations to + * pin a CPU core for many minutes before authentication can even fail. + * Reject anything above this generous cap (≈166× the default). */ +#define ZUPT_KDF_MAX_ITERATIONS 100000000u typedef enum { ZUPT_OK = 0, ZUPT_ERR_IO = -1, ZUPT_ERR_CORRUPT = -2, @@ -417,6 +423,16 @@ uint8_t *zupt_decrypt_buffer_aad(const zupt_keyring_t *kr, size_t *olen); void zupt_random_bytes(uint8_t *buf, size_t len); +/* F-09 frame-preface AAD (v1.6). Serialised canonical bytes bound into the + * per-block MAC. Shared by the serial (zupt_format.c) and parallel + * (zupt_parallel.c) compress paths so both produce byte-identical prefaces — + * a mismatch makes every multithreaded encrypted block fail to authenticate. */ +#define ZUPT_PREFACE_AAD_LEN 29 +void zupt_serialize_preface_aad_scalars( + uint8_t block_type, uint16_t codec_id, uint16_t block_flags, + uint64_t uncompressed_size, uint64_t compressed_size, uint64_t checksum, + uint8_t out[ZUPT_PREFACE_AAD_LEN]); + /* ─── Memory locking for key material ─── */ int zupt_mlock_keys(void *ptr, size_t len); void zupt_munlock_keys(void *ptr, size_t len); diff --git a/packaging/aur/PKGBUILD b/packaging/aur/PKGBUILD index 3144215..f893a82 100644 --- a/packaging/aur/PKGBUILD +++ b/packaging/aur/PKGBUILD @@ -11,7 +11,7 @@ # zupt-VERSION.tar.gz alongside the PKGBUILD. pkgname=vaptvupt -pkgver=4.0.0 +pkgver=4.1.0 pkgrel=1 provides=('zupt') replaces=('zupt') diff --git a/packaging/debian/changelog b/packaging/debian/changelog index a06a1ac..45a1118 100644 --- a/packaging/debian/changelog +++ b/packaging/debian/changelog @@ -1,3 +1,22 @@ +vaptvupt (4.1.0-1) UNRELEASED; urgency=high + + * Source-only build: the prebuilt vendored libraries libzuptsdk.so and + libpqvaptvupt.so are removed; the package builds with no external + library dependency and ships no shared object. The default password + KDF is PBKDF2-SHA256 (600k); the Argon2id KDF and the --pq-sdk / + --pq-box modes are gated behind an upstream WITH_SDK=1 build. Native + --pq (ML-KEM-768 + X25519) is unchanged. + * Fix: multithreaded encrypted archives were unextractable on the + native AEAD path — the parallel workers now bind the F-09 frame- + preface AAD like the serial path. Byte-identical across thread counts. + * Security: LZH raw code-length stack overflow and huff_lut OOB; + integer-overflow heap OOB reads in the index and solid-mode parsers; + SEQ decoder safe-zone heap overflow; per-block ENCRYPTED-flag + authentication gate; PBKDF2 iteration-count DoS cap; non-elidable + secret wipe; restored disk images created 0600. Wire format v1.6. + + -- Cristian Cezar Moisés Tue, 07 Jul 2026 12:00:00 +0000 + vaptvupt (4.0.0-1) UNRELEASED; urgency=high * Codec upgraded to canonical VaptVupt 2.60.4 (security release): diff --git a/packaging/homebrew/vaptvupt.rb b/packaging/homebrew/vaptvupt.rb index 51844ee..86af2d4 100644 --- a/packaging/homebrew/vaptvupt.rb +++ b/packaging/homebrew/vaptvupt.rb @@ -23,8 +23,8 @@ class Vaptvupt < Formula desc "Post-quantum backup compression utility (ML-KEM-768 + AES-256-CTR + HMAC-SHA256)" homepage "https://git.securityops.co/cristiancmoises/zupt" - url "https://git.securityops.co/cristiancmoises/zupt/releases/download/v4.0.0/vaptvupt-4.0.0.tar.gz" - version "4.0.0" + url "https://git.securityops.co/cristiancmoises/zupt/releases/download/v4.1.0/vaptvupt-4.1.0.tar.gz" + version "4.1.0" sha256 "REPLACE_WITH_SHA256_OF_RELEASE_TARBALL" license "AGPL-3.0-or-later" diff --git a/packaging/nix/flake.nix b/packaging/nix/flake.nix index 99cb4eb..2755f17 100644 --- a/packaging/nix/flake.nix +++ b/packaging/nix/flake.nix @@ -33,7 +33,7 @@ zupt = pkgs.stdenv.mkDerivation { pname = "vaptvupt"; - version = "4.0.0"; + version = "4.1.0"; # When publishing, replace this with `fetchurl` against the # release tarball. For local development the flake assumes it diff --git a/packaging/opensuse/README.md b/packaging/opensuse/README.md index 36178b7..5bb2e09 100644 --- a/packaging/opensuse/README.md +++ b/packaging/opensuse/README.md @@ -1,54 +1,49 @@ # openSUSE Build Service update for `home:cabelo:innovators/vaptvupt` -This directory contains the three files you need to update your OBS -package from `1.5.5` to `2.4.8`: +This directory contains the three files needed to build vaptvupt `4.1.0` +in OBS: -| File | Status vs. your current files | -|---------------|---------------------------------------------------------------------| -| `_service` | Updated `revision` to `v2.4.8`. Format unchanged (still `tar_scm`). | -| `vaptvupt.spec` | Version → `2.4.8`. License corrected `MIT` → `AGPL-3.0-or-later`. `%check` now calls `make check` (new distro-safe target). | -| `vaptvupt.changes`| 13 new entries prepended (2.0.0 → 2.4.8). Your existing 1.0.0–1.5.4 history is preserved verbatim. | +| File | Purpose | +|---------------|-------------------------------------------------------------------------| +| `_service` | `revision` pinned to `v4.1.0`. Format unchanged (still `tar_scm`). | +| `vaptvupt.spec` | `Version: 4.1.0`. `License: AGPL-3.0-or-later`. `%check` calls `make check`. | +| `vaptvupt.changes`| Changelog for the 4.x series. Older history preserved verbatim. | -## What changed in the spec +## Spec notes -1. **License correction** — your spec says `License: MIT`, but the - upstream license is **AGPL-3.0-or-later** (dual-licensed - AGPL-3.0-or-later + commercial). This was a bug that should - probably trigger a rebuild even without the version bump. +1. **License** — `AGPL-3.0-or-later` (dual-licensed AGPL-3.0-or-later + + commercial). -2. **`%check` target** — your spec calls `test-all` on non-s390x - architectures. In v2.4.x, `test-all` includes threading tests - that are flaky on emulated build hosts (3 false positives on - x86_64 GitHub-Actions-style sandboxes). The new `make check` - target added in 2.4.8 runs a curated subset: +2. **No BuildRequires beyond the toolchain** — the default build needs + only `gcc gzip make` (plus `libm`/`pthread` from glibc). There are + **no system library BuildRequires**. The repository is source-only: + the previously vendored `libzuptsdk.so` and `libpqvaptvupt.so` have + been removed from the tree, `%build` and `%install` run with + `WITH_SDK=0`, and `%files` no longer lists any `.so`. The package + installs no shared library. Do not add system crypto BuildRequires. - * F-06 HMAC tamper detection (2000 trials) - * F-08 archive-integrity-trailer - * F-09 byte-level integrity preface AAD - * F-10 KDF default - * F-11 auth-fail message - * F-12 encrypted comments - * NIST/RFC vectors (SHA-256, SHA-3, ML-KEM-768, AES-256-CTR, - HMAC, X25519, PBKDF2, Argon2id) - * Path-traversal, argument-order, block-swap regressions - * Quick smoke test + The optional SDK modes (`--pq-sdk`, `--pq-box`) and the Argon2id KDF + require an upstream `make WITH_SDK=1` build linked against the + separately distributed `libzuptsdk`/`libpqvaptvupt` libraries. They + are not part of this package. - Total ~91 assertions, runs in <2 minutes, no flakes on emulated - hosts. The s390x branch still falls back to just `test-vectors`. +3. **`%check` target** — the s390x branch falls back to `test-vectors`; + other architectures run `make check`. This exercises the HMAC tamper + detection, archive-integrity trailer, byte-level integrity preface + AAD, default-KDF, auth-fail, and encrypted-comment suites, the + NIST/RFC vectors (SHA-256, SHA-3, ML-KEM-768, AES-256-CTR, HMAC, + X25519, PBKDF2), and the path-traversal, argument-order, and + block-swap regressions. -3. **Upstream URL in `URL:` field** updated to - `https://git.securityops.co/cristiancmoises/vaptvupt` (the canonical - project URL). The `_service` file still pulls from GitHub - (`https://github.com/cristiancmoises/vaptvupt`) since that's where - your `tar_scm` is already configured and what works in OBS today. + The default password KDF is **PBKDF2-SHA256** (600k iterations). + Argon2id test vectors run only in a `WITH_SDK=1` build and are not + checked here. -4. **`BuildRequires: make`** added — newer openSUSE chroots don't - always pull `make` in transitively. Harmless on older targets. - -5. **Docs** — `%doc README.md SECURITY.md CHANGELOG.md` now ships - the security boundary docs as well as the README. THREAT_MODEL.md - exists upstream but isn't listed here to keep the package small; - add `%doc THREAT_MODEL.md` if you want it included. +4. **URLs** — the `URL:` field points at the canonical project URL + `https://git.securityops.co/cristiancmoises/vaptvupt`. The `_service` + file still fetches from GitHub + (`https://github.com/cristiancmoises/vaptvupt`), which is what the + existing `tar_scm` configuration uses in OBS. ## How to apply @@ -63,50 +58,31 @@ 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 v2.4.8 from GitHub +# 3. Trigger the service locally to fetch v4.1.0 from GitHub osc service runall - -# This produces vaptvupt-2.4.8.tar.gz in the current directory and -# updates vaptvupt.changes with a service-generated entry if you have -# changesgenerate enabled (you don't, so this is a no-op for -# changes; tar_scm just downloads). +# Produces vaptvupt-4.1.0.tar.gz in the current directory. # 4. (Optional) Local build to verify before committing osc build openSUSE_Tumbleweed x86_64 -# Expected: build succeeds, %check runs `make check`, all 10 suites -# (~91 assertions) pass, package is produced. - # 5. Commit upstream -osc status # confirm vaptvupt-2.4.8.tar.gz is staged alongside the +osc status # confirm vaptvupt-4.1.0.tar.gz is staged alongside the # three text files -osc commit -m "Update to 2.4.8: distro-safe make check target; license fix MIT -> AGPL" +osc commit -m "Update to 4.1.0" ``` ## Notes for future updates -* The `_service` `revision` is pinned to `v2.4.8`. To track a new - release, just 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. The - `set_version` service in `_service` will auto-sync at OBS-build - time if you want; it's mode="manual" today, which is safer. -* `BuildRequires` is intentionally minimal (just `gcc gzip make`). - VaptVupt has no external library dependencies — `libargon2`, - `libcrypto`, etc. used by other Linux packagers come from - *vendored* code that's compiled in. This is a deliberate - design choice; don't add system library BuildRequires. +* The `_service` `revision` is pinned to `v4.1.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. +* `BuildRequires` is intentionally minimal (`gcc gzip make`). vaptvupt + has no external library dependencies in the default build; do not add + system crypto BuildRequires. ## Reporting issues * Upstream bugs: https://git.securityops.co/cristiancmoises/vaptvupt * openSUSE packaging bugs: https://bugs.opensuse.org/ * Cabelo's OBS project: https://build.opensuse.org/project/show/home:cabelo:innovators - -## Author of these update files - -Generated against upstream `vaptvupt-2.4.8` source tree. Spec mirrors -cabelo's existing 1.5.5 conventions (minimal `BuildRequires`, -`%autosetup -p1`, `V=1` verbose build, `%ifarch s390x` branch in -`%check`, no separate libzuptsdk subpackage) — only the necessary -fields are changed. diff --git a/packaging/opensuse/_service b/packaging/opensuse/_service index 71d27ac..44dc47d 100644 --- a/packaging/opensuse/_service +++ b/packaging/opensuse/_service @@ -2,7 +2,7 @@ https://github.com/cristiancmoises/zupt git - v4.0.0 + v4.1.0 @PARENT_TAG@ v(.*) enable diff --git a/packaging/opensuse/vaptvupt.changes b/packaging/opensuse/vaptvupt.changes index c5d1912..ea2896c 100644 --- a/packaging/opensuse/vaptvupt.changes +++ b/packaging/opensuse/vaptvupt.changes @@ -1,3 +1,28 @@ +------------------------------------------------------------------- +Tue Jul 7 12:00:00 UTC 2026 - Alessandro de Oliveira Faria + +- Update to 4.1.0: + * Source-only build. The prebuilt vendored libraries (libzuptsdk, + libpqvaptvupt) are removed from the tree; the package now builds + with no external library dependency and ships no .so. The + libzuptsdk-backed modes (Argon2id KDF, --pq-sdk, --pq-box) are + gated behind an optional upstream WITH_SDK=1 build; the default + password KDF is PBKDF2-SHA256 (600k) and --pq (native ML-KEM-768 + + X25519) is unchanged. spec %files no longer lists the .so; + %build/%install pass WITH_SDK=0. + * Fix: multithreaded encrypted archives were unextractable on the + native AEAD path. The parallel compress/decompress workers did not + bind the F-09 frame-preface AAD that the serial path and the + archive's AAD_PREFACE flag require, so every multithreaded block + failed authentication. Now byte-identical across thread counts; + also fixes `--kdf pbkdf2 -t N`. + * Security: LZH raw code-length stack overflow and huff_lut OOB on + crafted archives; integer-overflow heap OOB reads in the index and + solid-mode parsers; SEQ decoder safe-zone heap overflow; per-block + ENCRYPTED-flag authentication gate; PBKDF2 iteration-count DoS cap; + non-elidable secret wipe in the SDK path; restored disk images now + 0600. Wire format v1.6 unchanged. + ------------------------------------------------------------------- Wed Jun 10 12:00:00 UTC 2026 - Alessandro de Oliveira Faria diff --git a/packaging/opensuse/vaptvupt.spec b/packaging/opensuse/vaptvupt.spec index e620295..6a55236 100644 --- a/packaging/opensuse/vaptvupt.spec +++ b/packaging/opensuse/vaptvupt.spec @@ -19,7 +19,7 @@ Name: vaptvupt -Version: 4.0.0 +Version: 4.1.0 Release: 0 Summary: Post-quantum backup compression with AES-256 + ML-KEM-768 hybrid encryption License: AGPL-3.0-or-later @@ -38,25 +38,29 @@ Obsoletes: zupt < 3.0.0 %description VaptVupt (formerly Zupt; renamed in v3.0.0 due to a prior INPI Brasil -trademark on the name "Zupt") compresses and encrypts backup archives. LZ77+Huffman compression -(VaptVupt codec, ~2-3 GB/s decompression on x86_64 with AVX2 / aarch64 -with NEON), AES-256-CTR + HMAC-SHA256 per-block authenticated -encryption, multi-threaded, with optional ML-KEM-768 + X25519 -post-quantum hybrid key encapsulation (FIPS 203 + RFC 7748). The -default password KDF is Argon2id; PBKDF2-SHA256 remains available -via --kdf pbkdf2 for backward compatibility. +trademark on the name "Zupt") compresses and encrypts backup archives. +LZ + ANS compression (VaptVupt codec, ~2-3 GB/s decompression on x86_64 +with AVX2 / aarch64 with NEON), AES-256-CTR + HMAC-SHA256 per-block +authenticated encryption, multi-threaded, with ML-KEM-768 + X25519 +post-quantum hybrid key encapsulation (FIPS 203 + RFC 7748) via --pq. -Pure C11, vendored libzuptsdk, ~5,000 lines of core code. Constant- -time cryptographic primitives are formally verified with Jasmin on -x86_64 (zupt_mac_verify_ct, zupt_ct_select_32); a clean C fallback -runs on aarch64 and other architectures. +This package builds entirely from source with no external library +dependency. The password KDF is PBKDF2-SHA256 (600k iterations). The +optional libzuptsdk-backed modes (Argon2id KDF, --pq-sdk, --pq-box) are +not built here; they require an upstream WITH_SDK=1 build against the +separately distributed libzuptsdk/libpqvaptvupt. + +Pure C11, ~5,000 lines of core code. Constant-time cryptographic +primitives are formally verified with Jasmin on x86_64 +(zupt_mac_verify_ct, zupt_ct_select_32); a clean C fallback runs on +aarch64 and other architectures. %prep %autosetup -p1 chmod +x tests/*.sh %build -%make_build V=1 \ +%make_build V=1 WITH_SDK=0 \ CFLAGS="%{optflags} -fPIE -Wall -Wextra -std=c11 -Iinclude -Isrc" \ LDFLAGS="%{?build_ldflags} -pie" \ LDLIBS="-lm -lpthread" @@ -71,14 +75,14 @@ chmod +x tests/*.sh # On s390x, fall back to just the vector tests (Jasmin assembly is # x86_64-only; threading harness has been flaky on big-endian). %ifarch s390x -%make_build V=1 \ +%make_build V=1 WITH_SDK=0 \ CFLAGS="%{optflags} -fPIE -Wall -Wextra -std=c11 -Iinclude -Isrc" \ LDFLAGS="%{?build_ldflags} -pie" \ LDLIBS="-lm -lpthread" \ test-vectors ./test_vectors %else -%make_build V=1 \ +%make_build V=1 WITH_SDK=0 \ CFLAGS="%{optflags} -fPIE -Wall -Wextra -std=c11 -Iinclude -Isrc" \ LDFLAGS="%{?build_ldflags} -pie" \ LDLIBS="-lm -lpthread" \ @@ -86,7 +90,7 @@ chmod +x tests/*.sh %endif %install -%make_install PREFIX=%{_prefix} +%make_install WITH_SDK=0 PREFIX=%{_prefix} %files %license LICENSE @@ -95,12 +99,5 @@ chmod +x tests/*.sh %{_bindir}/zupt %{_mandir}/man1/vaptvupt.1%{?ext_man} %{_mandir}/man1/zupt.1%{?ext_man} -%dir %{_prefix}/lib/vaptvupt -%{_prefix}/lib/vaptvupt/libzuptsdk.so -%{_prefix}/lib/vaptvupt/libzuptsdk.so.2 -%{_prefix}/lib/vaptvupt/libzuptsdk.so.2.0.0 -%{_prefix}/lib/vaptvupt/libpqvaptvupt.so -%{_prefix}/lib/vaptvupt/libpqvaptvupt.so.0 -%{_prefix}/lib/vaptvupt/libpqvaptvupt.so.0.6.0 %changelog diff --git a/packaging/rpm/vaptvupt.spec b/packaging/rpm/vaptvupt.spec index d28a0de..c4550b7 100644 --- a/packaging/rpm/vaptvupt.spec +++ b/packaging/rpm/vaptvupt.spec @@ -20,7 +20,7 @@ # in the base. Name: vaptvupt -Version: 4.0.0 +Version: 4.1.0 Release: 1%{?dist} Summary: Post-quantum backup compression utility (AES-256 + ML-KEM-768 + Argon2id, formerly Zupt) @@ -99,7 +99,7 @@ ln -sf libpqvaptvupt.so.0.6.0 %{buildroot}%{_libdir}/%{name}/libpqvaptvupt.so %files %license LICENSE -%doc README.md SECURITY.md CHANGELOG.md AUDIT.md +%doc README.md SECURITY.md CHANGELOG.md %{_bindir}/zupt %{_libdir}/%{name}/libzuptsdk.so.2.0.0 %{_libdir}/%{name}/libzuptsdk.so.2 diff --git a/sdk/README.md b/sdk/README.md index c6fe516..5d4fb9a 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -2,7 +2,7 @@ Public C ABI for the [VaptVupt](https://git.securityops.co/cristiancmoises/vaptvupt) backup compression library. -Provides post-quantum encrypted compression as a stable, embeddable shared library — completely independent of the `vaptvupt` CLI.No dependency on any other compression library; everything is built from VaptVupt's own implementations. +Provides post-quantum encrypted compression as a stable, embeddable shared library, independent of the `vaptvupt` CLI and of any external compression library — everything is built from VaptVupt's own implementations. - **Version:** 1.0.0 - **License:** AGPL-3.0-or-later @@ -18,7 +18,7 @@ Provides post-quantum encrypted compression as a stable, embeddable shared libra - **Secure memory** — mlock-backed buffers for passwords and keys, zeroed on destroy - **Constant-time crypto** — Jasmin-verified assembly on x86_64 - **Per-context state** — no globals; safe to use from any thread on distinct contexts -- **Custom allocator hooks** — embed cleanly in any runtime +- **Custom allocator hooks** — supply your own malloc/free ## Quick start (C) @@ -95,6 +95,8 @@ make sdk-test # runs C roundtrip suite (15 tests) sudo make sdk-install PREFIX=/usr/local ``` +This SDK is built from source via `make sdk`; the previously vendored prebuilt `vendor/zuptsdk/libzuptsdk.so` has been removed from the tree. + This installs: - `/usr/local/include/zuptsdk.h` - `/usr/local/lib/libzuptsdk.so.1.0.0` (with versioned `.so.1` and `.so` symlinks) @@ -174,7 +176,7 @@ sdk/ libzuptsdk is licensed under **AGPL-3.0-or-later** (see `sdk/LICENSE`). -The AGPL allows everyone to use the library freely, but anyone running it as a network service must publish their source code modifications. This protects the project from enterprise exploitation while keeping it usable by individuals, small businesses, and the open-source community. +The AGPL allows everyone to use the library freely, but anyone running it as a network service must publish their source code modifications. ## Contact diff --git a/sdk/bindings/python/__pycache__/zuptsdk.cpython-312.pyc b/sdk/bindings/python/__pycache__/zuptsdk.cpython-312.pyc deleted file mode 100644 index 39eb391fc225b054345916c389d31c57ad06ae2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27397 zcmdUYdvFx@nPB(4-=i0L@N!GYpb>gt%){8mSO@{)AqfQ7$hn=2rdwjfNHgvk0W%Sf z?2wIc*$3<<3)I>NsrVA9$QE+Ncb4n6&SaCyO=@esce@3M4PKJV z-uL~wf89N!*2A&0wP{;l|K{u8`}h66*YE4*FTGwD1J~Jq{Kv?hqYU$N{6R0Pl9+qM zGR#$mXSx`k2uQxv^#IP1$E%XGQ9Ny(`AEF zHno)9#Oz~u+XaTV-!jShx*P=O0Gw0BISI}MIJe-sWma;!3DyHxui#d(9)k4&)~~~Q z3AO;Rg*vQ{V2c1-ti$>Vwgj*%bl3vE6#8FCo|VC~a_R9q>+RRXR`#g!41${xN?~DC*`@&L&4s+RsUN~(>bT>(YJkm z9{zFqv}<+B6j(p_XE4>S^k6)h;7@aZGyF}iBb7WA8{oPl1AJtlH^KG9L@pZXR4^y9(_GWOjzjfNHf*YoiS^M?QV?CPqe71$3IkzbUi-bmfFMF9o;wo>ap9hx zNCVd)#?C}|A;E=|?2$0n)Y0mKekWsbF4n^(PYGNz*R;F!87?7&MIo8sYT}_p;%rRh z6Ls8aA(aSlAyMG^Lj$2+s5~;@>W{|4r|Y{z;nVQDd$0$3lz@stNiHpjF%By44T;^5 zn+wOHQ6Zd+#0DB%t|Nny@M$iQ42j97T^wAI{&-A;Lh_^%)Y(Y#6h$;N#|DzZxnvD= zAi;%`=j2jYG9-piMa~G^PDD3^WBqYaNF;)CUuwFmP6^RyjDs;m`Kmf@Ak;7HlyMEo zbIGbY(iGOS_(kVoXLpr6H&p?(Bp*tKl;)v}Vkn%Xtt)-2;}$CpGc3T+V~+i zN!F(KvrX+_cQg{le(j_^i4&C8!{yAd7&!wJpIeQL3%EYPP2Cn?CG+0Bt)agvgzbyV zolM0AUg!Zj6bHj9@dbg7G-6OF5|P2=Gm90A#gVuZa#31LK`v2LnciSzpeII2KBz}7 zQ65NgP!y2rpmo}`hFqewGV8e=SCC5-g@uBQ%52ewW#m#NVQti+YDpQ1MafKuVLp>w z_!Kb6TAJq!a2S~9AS@d2+L|-52p^EwB{HC`Ae3)F0fPm4t)asr^u^WI z;ypH)RNBGO<&**jo)lxL{9;f_Hwif<5<)VVJOz#M34>DdgecFUiurw|6r9&zicUhG zA)Q#L(==ax$0;7lV{xP2(=0fGaEuqiYX9jIrLa5*l4YrSqQV&=x_CV#%To350zVjE zydGg7)EyP{#Hp?e?LUqz5sOCn<<>NRd33v6S)oKEEd-Ms-s;pAb=h#mfKx2AV7i?7 zYo>byl|UUvv!u(^DCkf7r6unm?3t%vB(=53|FOSyWK@VEgVgp(&hFAR)g#9G zgIKyo-rQ=Hh*N7LQ$?m$;be>4=zi5Tf<4qGbp8@iR!T*%Iw#7Whnu!gG&5FuI=;{% z(Omf}MDbAiblhllXaV=$Z zUMJMTav$c?P`+ME7fVM%OB1lXl(Z;iLNIbjCYIihIiwJc^+v*>Xb=_ZWfwKxeq?ti z(#xoYc0G6VQRR9W739tURaCfIQ3yxk5wNy`@zcwwMjb~jRnl^p6GbM{Ku`16GS5h$ zgZeB>na9nP233Zpbztei(B^;JInA3=s=?{!G_j01)uwblvsVaY(aB84f_y|YVy#Q= zi!X38<*H;hg_|7v^lxMd3}+A2uh@9@BAS3-;K!)UG&X*)P51}Tk2xbJ^BoZ4FhOL6jsy~zr5-TZBcF(Kx zGgXMT)-o&9?;gWW4i18}~a4tgltEX!Vq#VeCdsii9;M4Y43^@xH% zjBKtpo#_2mW@gnoDU>DvhR}4@Dc{3H~W8Le5?; zKR0+{YG{zG-Gh-RpXQolgHfIvh$T5RG<2s2q!O+sy)DqdbqYx?uX~&@aE3br4l+1$ zh=8RQibjbeA9^8%#8gATBbj0e$qv~fVr)P%gHbM7(1eogpq!xkF4;rf34(D1gFTU` z5DZGTP&_US@RFr378#H%K{!H?Y3`nei3eUn{;+Fu1nO`_ums}TJmwXp}lVvqoM_|TLG2s_wdNMYiDs z4+)ocjKw!?S&^}<_-6Oj-YdOV zqgSFgw~UsICdXfxe6BrP+A(E0vPhnZShlon%F_PV{^H^NGv2cMjLljx!hY;8nDLa{ zHJP0iv&CgspSkkPwU__+xsN=Rcb)M3fsb()O_ppKe{uZ4U54GYp9NUl%FZ#Y`v9h` z2iQ*^SeXK-j&)Ydl$F1^|5ta-kbIOVhoS$dq@?*7(~q8U?kTYT*kan_w*J^{g>()N zRQMM&R}CeuG6DlY2smsEfyR5vW%f?%~?7XeZk+KIIlC>FqZ6Y$sPq6$a zB!!&x6AJ=d(YAn70%Aex0b+3lgZ(jnkPwb17=*)$sPe?pbo^MmhbxGFFW9>&<#+W&%k`ZxK;wlRM1QH1jzy)T;UjSAueOLA zX%XY0?}x`r51qGnIeAByi+6Upc~_T*cXxStPnVDPcKLZ}VMDR-l|AgQHf;j~D0kj2*#7Z1Q6@qmDBvboA z$=%v^qUliUo?z3_eUh`SJ$Sg~aQo4dlBu;_a_??JeDnU+6D^Vle{?i;cD~SlbdO}m zUk6)G%8A2G&2r`wEk`?B+uP*K&HG!L4|X0uEEViNzISiS(crQ6_F(7XrbCA$Z(IAZ zU`t!`(UTp=TJ}hVwBF#+mLtboj{?fyaRlqv5bon`oyR*m+K)m3u@=WbmV~TlJ^n;+ z1O5{20AM}WtcA5(^Vsk~1g!|PpNH`0J{6EGfa4}DB>;yHljI6awgY(epxhR=khA{#?6Qn7=h!Z5Cljzp&Msg|WOdUuyF&?q6V8PNa;ig4bVCCv z9be{%tG^*k8^XqLYvAv>_W*p0y~+%MxDG;EzLKiqVWe&(Rpr1)i5OCq5h?-=0}EhK z^dKCBs!6mDnL#K*>Q9-s&F9UUVsX(j#JstNVKkA-GAWpZwnx^%WKaE5l3f(y(NI`O zo1W%?D_GFIE*8K*B%6Hn0#X5ZvQ$9zVR}>oazen&k~J|H7ewR}k%34u7zBA?NeIy% zaUZEgJ|vOM{fS=584QA4fMeUBDlmp+esCD_ri;{tX`q$85BNj}fKP{+nUeAm%Nx#F zU(u!07f-)cJ-xCqv$ApAl3lqq>)ST%+nMq0oanui%KDCsm_K$G&a5cE+;Xkx+TLqV zO%_#;G|xJ`7ws8G`L*JoIo1$*8e`i6w){P54+i;II2aVMPau}$E{EZK8OFwnN3ci8 zFrSxu-V8|*!GFRBICGy~U_Q2NerU2<*WP0Q$fS!|!tkeFD$_6&&f~aM;Qb1?!sPOV&L=<@Aw*CN{K=2p2Z6RveZFxJ6 zz?3cC(3i1(1V6%rHYh}eDHT8(^jw9n{4HRCtLTjom8W1v`8r(#)v+lyiRH-EXQwIc zo^;T(5huB&JXE=jm*&>)Xcnp51~{G02=QsTXo~d8lT=NSA>fX*^lQ1LRU?3wWf-Nj zY7{I|x>PGgQ%Y&n9m%yw>6J8(c2`ORx0J6%O0T7Pw7XKel45d8C)XL{1D5pGqoD{0 zAq>lrgNNpeqk-5!efvQUVs!?^0dP+7LPML_0yTg*fzP6 zDm&>@*#~_FDh!(}roS{b349o;7ll4$7#a_G-jvi>iP|Rdd>EAC&B!^RM{ZgZQI*mw z)P>@bq0gO5$;O^R6~%Kt1Y~%u!*0>@?8Opj5ytwn4Wg3m&70NURB5P#_Dv zhNX@~)v*32=KL~%VP@7}ajp8D72kJGa+|08PtF#tnq2*4wrIRB82ENpn1jNHpY@s5(P|QI`-fxApEG1F4PT9 zM*SR0BygcYdGr@wN?lA%I?Ctw1N%Vw@rRIDs2}21Z6g@z^~SxYWX>`=yqvDR((Z8mH5{Q}lnao=XPR^4|o*2g7F&78?(l_e`F z6@~)pMW=8M6)nAZ1>p%UEr5!EO6S(*PpD$%K}h|Rk+*#$BK z4SaIxHm!lje~2|wsdchVm69487y@Nlkupf@d7H3m|`GcboVpIP-aW!*#?Vu;AKU#b3BK1eLSFsn#?nYg)Od zT$)NC*wE^xdSI6weuMq3{&o;M+)2*B?6CXs<(c%Ued?=J9Y;7hiULh;eAvLfAco=z z46G60AQjGC1$>JMPF*=XMNPp7}=O`$s01yS` zElA{qE_`7M%8;=NVp3EPRng(m>{S&K@=G=AK|L|IrB$AxOpDIcbr`B9<&-ti0yHpM zG6zq`;WQCRG)NAlok7rfNRnEyE)0{Dx2$*xX2*aV6&zI7hn^LbYP72AEIF*GF5|Bo z?V2dc`k$S0?3^iHaXEBlD;TqmnU!nd=AWrp1B#1JW(}0upm7EbHnLEwp$X9O;Z0D~ zg(0#54q`~kta(f{?S26>QJrS2TE2!%Mu$P*c7^uvq7GF08_Y@N38S+njI>d(l8o9j zf7bSfOhe`{L~>|E0%?^*@M(_;PuJ4@oe*rEN|rty;Z%ik@Z*z(~sA3SsC zZ1$P$yWc}PKtON+JV6m;u?oE z5qJ4&jAU2#O4_duQrtD?sB{=eKG+uAa`fDd0wev@b#7ai_T}b?X8A9K| z$!Y~4sk)9rQJ}Gqx8`JNw1g$3PN?EXc@ifyPtiH|`Z;0;DT>aEV=qpD9klAP8#}J= z06WNmc2Ieq9V9C{i}_Rj4Eyegix6ZbCR5_OIG!IM_&S0-<>)5j5ENnmo`Zz!RB60# zgOla^K8Lm9ZV6-a-2)RTFlUAo1R_}jcU=l@ql()&=TdDatS}6J>IIdPi@1%9{Vgdo zgig4X)7w^vH_lOIkQ)$!`VGiUl#qIdrp7_E69l89O3kZJ1;B8rHIr zKGp7qwUpB5hP5=(r`p}H7Id4EOE()&Qk-_z%EFgDP<2)x5oMWZA`%l=DVc(&Br{kJ zGRLZtiOP-ux z#tmRxJVgBv*d2>S7pPYs^)7jGei?TJvZzt*q=5Eojp1C6B+WTQl~;q(QKys0HR9;v!Xo=5iW1^_sKz_HhGd_Ic6g>8fs7+Cx_-*>#7t2s*je`ZY6$Pws7&6cfupwB|9In&w*3-ze0`T9Rz=Y;J+jIO9Veb zkgxEiOJXwB(V?_L=xj}0&Q*1RBoQ+o*aK-{Mhu?y_v0hC%5c_h~xci z`HXzDclj$fH;(et4ZAW8yC&Cz3E(hZX6;1{X}gy{YbI`yLjZ!6pXY-ytWOp9&`aeqCP@%UipxT4#jr+I^wat<q zm47=`gcw_o?HHd4_e-R`OeE(f9sUvz`Rl)w2b4UpE(YY zEO4JMHK6PA23`q?h5Q&=7|AT;%IB@}v0{NXEwYs7aaNH1lT*ds>%Es>ncqC}DZ)R# zOPc?EI#eW6k!wp+Li;SXv~J$yQ-dCyw>)F|?CWRs#`FtgFH99|on5*1M&Nn?jA=U> z(`ClSv?E{8khe#waUF@s!NVRy>v;wrAn3vlJq8JUE1~K}*Y&Q^mnW=uj?Xc6>p}K@ z5o29HXHm^#ERiFMsGoE00zA<~5sCIIf^Y8oY7?iIt$n1WB*oq=*VPIy3%UkdZVVdU&pCFH?Ush6L@a)QO z$rLuS5DJu~Q@QUTl4l3ZypH?UWW53P5OsaVzy2nioYqb`HmWD5Z#X}87tMN#zPV+( zxG__VE@9`p&WY{W;^v=u_S`iyp5po33R;r66$%DqmWzdKk%*skq1Ig`erpJ75x)Wz z%&#AH5Zo@GEl5i|kCnXwKp&kY4g(^sn=Lt1>pZspDi%QBK+YL1@?pY@{}2)jiGqLK z@D3Wu_Yc@%!fVx-9i*Q5d^S(*C!tqqFI@u?z4uj^0fXqh>WruQ=CSXc{PxKyPvg?j zd#d2tO(zSg&A)Cb2#lv&M&z3KHON0tx}s+@?WaRfJgygT8F8J&ZvZg%Ci)-`dJ~5W zkjKlHk=@tHXgmYrgpsz%MO8ns0AjVA6sz6o=<>>|7N7Pal@{?VKB+}KC*8Cz%_Btl z8+e7N+&~4}M{{cTC3>W))tV>qHF!BFy}mgnh+zm7$B^PiHEgl5EjA!<_0Ms#?$VI1 zr2*tz{~(-2cPl08;Qe9@HHWzKI*csGVgaYlX?_ZNNJ8@D(7y=&>;nMN$?(z0VEn9j z8h!YE$ZycI|w{4%j^6Yf+hD`B>Y;j=H z6PWdtO_uM;`dTI(@(ZJB2Zlp8zXbL-msnuP^J)eu@~4DNIjMQf9t4swe}(F zkZs65B#_NINGk;j_pLnS=#8gec+A~^dt@LKb#(F|*Wnj5*aT{h$z zau0ciytkAYr@IbL#&Jc-r5j%*#cB5;4{sx35MKFCc?GTMyiYgQby_)^qUC9RFzS8a z5wfSBZdSfd%Z0$%Gte6qAa0DihqOP=LmiQ;E_xFl+q&y`jjvrgE*e$_YaChvX9l>OExW0hQ)uzN{G@-3|w+j zmS^RvXKQ(1Bom1kBavb;s712Ld6SHh2V1pIQy>C2eILcTrVpd3JQ$;Y%9nrbvI z$pRJ#d}Sbh7Ch&c_-TMv_(D!FF4^#wNJ}>O@b6$WnLs}VF>Ey;pS}C@`kXGKk|*!M zA0s#VJE&%udEj6QR?d6*VN?Nrte2kjqk zcw!_y?XQH>{3-vs_mc0Y-c5~Ieo?sMYWbD&YjxSe4I|cBo`>{%>&E#X1Ryb9Jz|-A=X6Jqu`dd`? z&E(auT=~k)t)qqCeroi@c+=R)Y}xi~;ZxWG_**M7j*4rK-(;`XOgYxg_*aa0Wra!M zM=)-NL4Jh$W$y1`F1`p32s}>Zf!Kh2X(wgyZ4NW=II|Ha)bTj<5t7SDZl1Sla@puj zAuR&?ylsh`YJ|;syY_ZbvP5mYjHa|ZjIdn$MN`0~&?+UDj!Ri&IdDtS1TsxRgWJTa{1Jo`7^w%4;nTIe<%9({(NKN32yQRgckZ8b1Pq5hK}T zzVv7Xy)D_oI0-w?RfD{szInU?Wc1@nu(fB(gxc zl3NLsAZ!H#3E?yfc!y|%q-aOr0FXe^RM~~hEqu96rb{Wa$i;sTi+>2k;aeo|n$(7O zTE4$ya^2P`|2F+=QhA)A=E;$-O#7=c{;DZ|^`Dn~Sn)x{vN!{Awr#n+V{FHG@1G~` zZ2vDqGaDPnOU4H$dgjb*!CqhjtaBgxi^9_1k_G7f->B>o7O)M5_&a0)+E%kFYL0<2 zv}_h&)7~&%;$uRLk<$gXEAA?C>9(t>Pu;}yDT5wlm>IlqGv88-3yLG(Q!uxQZpcH# zZ@{;Mr>C6R>C`XQA-hNDn= z9-YfI&!=-4|DUn&AQYBg2mBm#u1Gl}T7L5OuCZO19n8NB#0!?myL7aria?_SK@cyh2-9B8Fj8TS%{rhh)pXO*7KnoRpU1+l zJ*2mtAm3+%cu2~AH^||}y%6-6(sadR;-G*Qn}{!rCmN0cD>UM*8ddqIRwjP{Qllp2 z^gu*HG9(`)a+!Q&>aoc+qaX!Jf-iiVRtwLK4UjZ*KYCj&JW6?R_sX zvHb@_caHsZiyYzz98@L(6e#pkiC`QszZ37I@u3o%R4p;xLaG*>#sZS+p&IpR(q|xR z&G3ppdSj^wJq14mk?V)A#}nfeEO7N-j%Wn6m>Uq_-z(r7((37tQj+|`0ZYbR8<#?V zu(tl>v{eadAVeV9$hRnSy_J#$k!cTIbNuxaT=Cto3?K((rFIfNzf!nO)oPWapvYKc zqU^`(?)3hsZnEP<*8jqkQ;x1rl$vmF0?t8 zoopr&DJPFI&x#v-2RFWPCSXtRI zwax#alh_Hx0v*1U!Ns1Q!upM{pCt1cDg^WWf6& zC8-h;IFIgMh9IA@kleu_MF;aH?vdkJ`Kx8miB&Lj@IRA4wH3ax#tw8yvRGFqUsPUv=JPj<9aa1Awgc{wY%_dw{*SiE*s>+E<4y4}CVa@E!x; zVUeG$yvG1|P-kb?UUoiU;P#+|Vb@;{OfiqoGLMg(l<$`-$bG}*)D*J;@RwWR_kEj- zFQi k@O`9f@O`AM8Q7aEw`YA%P5X9be7o-0v%dYWy2%3nf1L+>wg3PC diff --git a/src/vv_ans.c b/src/vv_ans.c index 32ed4fb..f47273c 100644 --- a/src/vv_ans.c +++ b/src/vv_ans.c @@ -2297,12 +2297,17 @@ vva_error_t vva_decode_sequences_impl(const uint8_t *src, size_t src_len, * near the boundaries. We maintain §4 invariants 3 and 5. * * SAFEZONE_MAX_OFFSET covers the legal offset range (1 << wlog_max). - * SAFEZONE_MAX_RUN covers BOTH max litlen and max matchlen (both are - * bounded by the wire format at ≤65535: LL encoding ll_base[35]=61440 - * + up to 4095 extra bits = 65535; ML encoding likewise). So - * op_safe_end = op_end - 65535 guarantees any single sequence's - * total writes (literals + match) fit without per-iter overflow - * checking. + * SAFEZONE_MAX_RUN bounds EACH of litlen and matchlen (both ≤65535 by + * the wire format: LL ll_base[35]=61440 + up to 4095 extra = 65535; + * ML ml_base[35]=32768 + up to 32767 extra = 65535). A single sequence + * writes litlen literals THEN a matchlen match copy — up to TWO max + * runs — and in_safe_zone is computed once (pre-literal) yet gates BOTH + * the literal and the match op_end checks. So the reserve must cover + * the full worst-case sequence: op_safe_end = op_end - 2*SAFEZONE_MAX_RUN + * guarantees litlen+matchlen fit without per-iter overflow checking. + * (Reserving only ONE run let a crafted final sequence write up to + * 65535 bytes past op_end — a heap overflow; the +64 caller slack was + * far too small to absorb it.) * * SPRINT 46: raised from 1<<20 to 1<<24. The 3-byte offset wire * encoding (off_bytes==3 for wlog>16) represents offsets up to @@ -2318,9 +2323,11 @@ vva_error_t vva_decode_sequences_impl(const uint8_t *src, size_t src_len, * An offset > 2^24 remains genuinely corrupt (unrepresentable in * 3 bytes) and is still rejected, preserving the DoS guard. */ enum { SAFEZONE_MAX_OFFSET = 1u << 24 }; /* 3-byte offset wire max */ - enum { SAFEZONE_MAX_RUN = 65535 }; /* litlen or matchlen */ - uint8_t *op_safe_end = (dst_cap > SAFEZONE_MAX_RUN) - ? op_end - SAFEZONE_MAX_RUN : dst; + enum { SAFEZONE_MAX_RUN = 65535 }; /* max litlen OR matchlen */ + /* Reserve for a full worst-case sequence (litlen + matchlen). */ + enum { SAFEZONE_RESERVE = 2 * SAFEZONE_MAX_RUN }; + uint8_t *op_safe_end = (dst_cap > SAFEZONE_RESERVE) + ? op_end - SAFEZONE_RESERVE : dst; const uint8_t *offset_check_floor = dst_base + SAFEZONE_MAX_OFFSET; size_t seqs_decoded = 0; diff --git a/src/zupt_crypto_pqbox.c b/src/zupt_crypto_pqbox.c index fadaa1a..c865782 100644 --- a/src/zupt_crypto_pqbox.c +++ b/src/zupt_crypto_pqbox.c @@ -32,6 +32,8 @@ * [..] raw key bytes (PQVV_PUBLICKEYBYTES / PQVV_SECRETKEYBYTES) */ #include "zupt.h" + +#ifdef ZUPT_WITH_SDK #include "zupt_keccak.h" #include "pqvaptvupt.h" #include @@ -179,3 +181,35 @@ int zupt_pqbox_decrypt_init(zupt_keyring_t *kr, const char *privkeyfile, zupt_secure_wipe(session_key, sizeof(session_key)); return 0; } + +#else /* !ZUPT_WITH_SDK */ + +/* Source-only build (no vendored libpqvaptvupt binary). The --pq-box sealed-box + * mode is unavailable; use native --pq (ML-KEM-768 + X25519) instead, or rebuild + * with `make WITH_SDK=1` (requires the vendored libpqvaptvupt). */ +#include + +static int pqbox_unavailable(const char *what) { + fprintf(stderr, + "Error: this build has no libpqvaptvupt support, so %s is unavailable.\n" + " Use native --pq (ML-KEM-768 + X25519) instead, or rebuild with " + "'make WITH_SDK=1'.\n", what); + return -1; +} + +int zupt_pqbox_keygen(const char *privkeyfile, const char *pubkeyfile) { + (void)privkeyfile; (void)pubkeyfile; + return pqbox_unavailable("--pq-box key generation"); +} +int zupt_pqbox_encrypt_init(zupt_keyring_t *kr, const char *pubkeyfile, + uint8_t *enc_hdr, size_t *enc_hdr_len) { + (void)kr; (void)pubkeyfile; (void)enc_hdr; (void)enc_hdr_len; + return pqbox_unavailable("--pq-box encryption"); +} +int zupt_pqbox_decrypt_init(zupt_keyring_t *kr, const char *privkeyfile, + const uint8_t *payload, size_t payload_len) { + (void)kr; (void)privkeyfile; (void)payload; (void)payload_len; + return pqbox_unavailable("--pq-box decryption (this archive needs it)"); +} + +#endif /* ZUPT_WITH_SDK */ diff --git a/src/zupt_crypto_sdk.c b/src/zupt_crypto_sdk.c index 8933e05..fd3d5c9 100644 --- a/src/zupt_crypto_sdk.c +++ b/src/zupt_crypto_sdk.c @@ -8,6 +8,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ #include "zupt.h" + +#ifdef ZUPT_WITH_SDK #include "zuptsdk.h" #include "zuptsdk_easy.h" #include @@ -68,14 +70,14 @@ int zupt_sdk_hybrid_encrypt_init(zupt_keyring_t *kr, const char *pubkeyfile, size_t blob_sz = 0; int rc = zuptsdk_easy_encrypt(pubkeyfile, session_key, 32, &blob, &blob_sz); if (rc != 0 || !blob) { - memset(session_key, 0, 32); + zupt_secure_wipe(session_key, 32); return -1; } /* Layout: [1B type][4B blob_sz LE][blob] */ if (1 + 4 + blob_sz > 1500) { free(blob); - memset(session_key, 0, 32); + zupt_secure_wipe(session_key, 32); return -1; } @@ -98,7 +100,7 @@ int zupt_sdk_hybrid_encrypt_init(zupt_keyring_t *kr, const char *pubkeyfile, zupt_sha3_256(kdf_buf, sizeof(kdf_buf), kr->enc_key); memcpy(kdf_buf + 32, "ZUPT-SDK-MAC-KEY", 16); zupt_sha3_256(kdf_buf, sizeof(kdf_buf), kr->mac_key); - memset(kdf_buf, 0, sizeof(kdf_buf)); + zupt_secure_wipe(kdf_buf, sizeof(kdf_buf)); kr->canary_head = ZUPT_CANARY; zupt_random_bytes(kr->base_nonce, ZUPT_NONCE_SIZE); @@ -107,7 +109,7 @@ int zupt_sdk_hybrid_encrypt_init(zupt_keyring_t *kr, const char *pubkeyfile, kr->canary_tail = ZUPT_CANARY; free(blob); - memset(session_key, 0, 32); + zupt_secure_wipe(session_key, 32); return 0; } @@ -144,7 +146,7 @@ int zupt_sdk_hybrid_decrypt_init(zupt_keyring_t *kr, const char *privkeyfile, zupt_sha3_256(kdf_buf, sizeof(kdf_buf), kr->enc_key); memcpy(kdf_buf + 32, "ZUPT-SDK-MAC-KEY", 16); zupt_sha3_256(kdf_buf, sizeof(kdf_buf), kr->mac_key); - memset(kdf_buf, 0, sizeof(kdf_buf)); + zupt_secure_wipe(kdf_buf, sizeof(kdf_buf)); kr->canary_head = ZUPT_CANARY; /* base_nonce will be overwritten per-block by the legacy path; in SDK @@ -188,7 +190,7 @@ int zupt_sdk_password_encrypt_init(zupt_keyring_t *kr, const char *password, zupt_sha3_256(kdf_buf, sizeof(kdf_buf), kr->enc_key); memcpy(kdf_buf + 32, "ZUPT-SDK-MAC-KEY", 16); zupt_sha3_256(kdf_buf, sizeof(kdf_buf), kr->mac_key); - memset(kdf_buf, 0, sizeof(kdf_buf)); + zupt_secure_wipe(kdf_buf, sizeof(kdf_buf)); kr->canary_head = ZUPT_CANARY; memcpy(kr->base_nonce, enc_hdr + 17, ZUPT_NONCE_SIZE); @@ -196,8 +198,8 @@ int zupt_sdk_password_encrypt_init(zupt_keyring_t *kr, const char *password, kr->active = 1; kr->canary_tail = ZUPT_CANARY; - memset(key, 0, 32); - memset(salt, 0, 16); + zupt_secure_wipe(key, 32); + zupt_secure_wipe(salt, 16); return 0; } @@ -233,7 +235,7 @@ int zupt_sdk_password_decrypt_init(zupt_keyring_t *kr, const char *password, zupt_sha3_256(kdf_buf2, sizeof(kdf_buf2), kr->enc_key); memcpy(kdf_buf2 + 32, "ZUPT-SDK-MAC-KEY", 16); zupt_sha3_256(kdf_buf2, sizeof(kdf_buf2), kr->mac_key); - memset(kdf_buf2, 0, sizeof(kdf_buf2)); + zupt_secure_wipe(kdf_buf2, sizeof(kdf_buf2)); kr->canary_head = ZUPT_CANARY; memcpy(kr->base_nonce, nonce, ZUPT_NONCE_SIZE); @@ -241,6 +243,52 @@ int zupt_sdk_password_decrypt_init(zupt_keyring_t *kr, const char *password, kr->active = 1; kr->canary_tail = ZUPT_CANARY; - memset(key, 0, 32); + zupt_secure_wipe(key, 32); return 0; } + +#else /* !ZUPT_WITH_SDK */ + +/* Source-only build (no vendored libzuptsdk binary). The SDK-backed modes + * — --pq-sdk and the Argon2id default password KDF — are unavailable. These + * stubs let the project build and link from source with no prebuilt library; + * callers fall back to native crypto (PBKDF2-SHA256 password KDF, native + * ML-KEM-768 + X25519 via --pq) or report the requested mode as unsupported. + * Rebuild with `make WITH_SDK=1` (requires the vendored libzuptsdk) to enable. */ +#include + +static int sdk_unavailable(const char *what) { + fprintf(stderr, + "Error: this build has no libzuptsdk support, so %s is unavailable.\n" + " Use native crypto instead (password mode uses PBKDF2-SHA256; " + "--pq uses ML-KEM-768 + X25519),\n" + " or rebuild with 'make WITH_SDK=1'.\n", what); + return -1; +} + +int zupt_sdk_hybrid_keygen(const char *privkeyfile, const char *pubkeyfile) { + (void)privkeyfile; (void)pubkeyfile; + return sdk_unavailable("--pq-sdk key generation"); +} +int zupt_sdk_hybrid_encrypt_init(zupt_keyring_t *kr, const char *pubkeyfile, + uint8_t *enc_hdr, size_t *enc_hdr_len) { + (void)kr; (void)pubkeyfile; (void)enc_hdr; (void)enc_hdr_len; + return sdk_unavailable("--pq-sdk encryption"); +} +int zupt_sdk_hybrid_decrypt_init(zupt_keyring_t *kr, const char *privkeyfile, + const uint8_t *enc_hdr, size_t enc_hdr_len) { + (void)kr; (void)privkeyfile; (void)enc_hdr; (void)enc_hdr_len; + return sdk_unavailable("--pq-sdk decryption"); +} +int zupt_sdk_password_encrypt_init(zupt_keyring_t *kr, const char *password, + uint8_t *enc_hdr, size_t *enc_hdr_len) { + (void)kr; (void)password; (void)enc_hdr; (void)enc_hdr_len; + return sdk_unavailable("the Argon2id password KDF"); +} +int zupt_sdk_password_decrypt_init(zupt_keyring_t *kr, const char *password, + const uint8_t *enc_hdr, size_t enc_hdr_len) { + (void)kr; (void)password; (void)enc_hdr; (void)enc_hdr_len; + return sdk_unavailable("the Argon2id password KDF (this archive needs it)"); +} + +#endif /* ZUPT_WITH_SDK */ diff --git a/src/zupt_disk.c b/src/zupt_disk.c index 0d5ed03..ef3d760 100644 --- a/src/zupt_disk.c +++ b/src/zupt_disk.c @@ -421,7 +421,11 @@ zupt_error_t zupt_disk_backup(const char *output_path, const char *source_path, } /* ─── Write index (single entry for the disk image) ─── */ - uint8_t idx_buf[4096]; + /* SECURITY: size for the worst case — a path clamped to ZUPT_MAX_PATH-1 + * (4095) PLUS the 4-byte file count, the (≤5-byte) varint length, and the + * 48 bytes of fixed trailing fields. A bare [4096] overflowed by ~57 + * bytes when source_path approached ZUPT_MAX_PATH. */ + uint8_t idx_buf[ZUPT_MAX_PATH + 128]; size_t idx_pos = 0; /* File count (4B LE) */ @@ -706,7 +710,11 @@ zupt_error_t zupt_disk_restore(const char *archive_path, const char *target_path * fall back to O_CREAT | O_TRUNC for new files. */ tgt_fd = open(target_path, O_WRONLY); if (tgt_fd < 0) { - tgt_fd = open(target_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + /* SECURITY: 0600, not 0644 — a restored disk image holds decrypted + * backup contents and must not be world-/group-readable. Matches the + * file-extraction convention in zupt_safe_fopen_output(). (When the + * target is an existing block device the mode is ignored.) */ + tgt_fd = open(target_path, O_WRONLY | O_CREAT | O_TRUNC, 0600); } if (tgt_fd < 0) { fprintf(stderr, "Error: Cannot open target '%s': %s\n", diff --git a/src/zupt_format.c b/src/zupt_format.c index 8000351..8017830 100644 --- a/src/zupt_format.c +++ b/src/zupt_format.c @@ -422,7 +422,15 @@ zupt_error_t write_enc_header(FILE *out, zupt_archive_header_t *hdr, * + F-09 preface-AAD per-block pipeline. Only the KDF and * enc-header bytes differ. Read-path dispatch on enc_type byte * at offset 0 of the enc-header block already handles both. */ - if (opts->kdf_legacy_pbkdf2) { +#ifdef ZUPT_WITH_SDK + int use_pbkdf2 = opts->kdf_legacy_pbkdf2; +#else + /* No libzuptsdk in this build: Argon2id is unavailable, so the password + * KDF is always native PBKDF2-SHA256 (600k iters, AES-256-CTR + HMAC- + * SHA256). Archives written this way are readable by any build. */ + int use_pbkdf2 = 1; +#endif + if (use_pbkdf2) { uint8_t salt[ZUPT_SALT_SIZE], nonce[ZUPT_NONCE_SIZE]; zupt_random_bytes(salt, ZUPT_SALT_SIZE); zupt_random_bytes(nonce, ZUPT_NONCE_SIZE); @@ -584,7 +592,7 @@ static uint64_t get_mtime(const char *path) { * * Excludes block_magic (constant `bb 01`, structurally rejected by read_block * if tampered) and the AES nonce (already part of the existing MAC input). */ -#define ZUPT_PREFACE_AAD_LEN 29 +/* ZUPT_PREFACE_AAD_LEN is defined in zupt.h (shared with the parallel path). */ static void zupt_serialize_preface_aad(const zupt_block_t *b, uint8_t out[ZUPT_PREFACE_AAD_LEN]) { out[0] = (uint8_t)b->block_type; out[1] = (uint8_t)(b->codec_id & 0xFF); @@ -600,7 +608,7 @@ static void zupt_serialize_preface_aad(const zupt_block_t *b, uint8_t out[ZUPT_P * known at MAC time but before the block struct exists. Same byte layout * as zupt_serialize_preface_aad — both sides must produce identical bytes * for the same logical block, or the roundtrip MAC won't match. */ -static void zupt_serialize_preface_aad_scalars( +void zupt_serialize_preface_aad_scalars( uint8_t block_type, uint16_t codec_id, uint16_t block_flags, uint64_t uncompressed_size, uint64_t compressed_size, uint64_t checksum, uint8_t out[ZUPT_PREFACE_AAD_LEN]) @@ -1766,6 +1774,16 @@ zupt_error_t decompress_block(const zupt_block_t *b, const zupt_keyring_t *kr, if (b->uncompressed_size > ZUPT_MAX_BLOCK_SZ) return ZUPT_ERR_OVERFLOW; if (comp_len > ZUPT_MAX_BLOCK_SZ + 1024) return ZUPT_ERR_OVERFLOW; + /* SECURITY: in an encrypted archive every block MUST be encrypted. + * The per-block ENCRYPTED flag is NOT covered by the archive-integrity + * trailer (which only authenticates the header and footer, not block + * bodies/flags). Without this gate an attacker could clear the flag on + * a forged STORE block and inject attacker-chosen plaintext that passes + * only the keyless XXH64 — an authentication bypass / plaintext forgery. + * Fail closed when the keyring is active but the block isn't encrypted. */ + if (kr && kr->active && !(b->block_flags & ZUPT_BFLAG_ENCRYPTED)) + return ZUPT_ERR_AUTH_FAIL; + if (b->block_flags & ZUPT_BFLAG_ENCRYPTED) { if (!kr || !kr->active) return ZUPT_ERR_AUTH_FAIL; size_t dec_len; @@ -2017,6 +2035,10 @@ zupt_error_t read_enc_header(FILE *f, zupt_archive_header_t *hdr, zupt_options_t memcpy(nonce, eb.payload + 33, 16); memcpy(&iter, eb.payload + 49, 4); free(eb.payload); + /* SECURITY: reject an absurd attacker-supplied iteration count before + * spending the CPU on it (KDF-amplification DoS). See + * ZUPT_KDF_MAX_ITERATIONS. */ + if (iter < 1 || iter > ZUPT_KDF_MAX_ITERATIONS) return ZUPT_ERR_CORRUPT; fprintf(stderr, " Deriving decryption key (PBKDF2-SHA256, %u iterations)...\n", iter); zupt_derive_keys(&opts->keyring, opts->password, salt, nonce, iter); return ZUPT_OK; @@ -2033,6 +2055,9 @@ zupt_error_t read_enc_header(FILE *f, zupt_archive_header_t *hdr, zupt_options_t memcpy(nonce, eb.payload + 32, 16); memcpy(&iter, eb.payload + 48, 4); free(eb.payload); + /* SECURITY: reject an absurd attacker-supplied iteration count before + * spending the CPU on it (KDF-amplification DoS). */ + if (iter < 1 || iter > ZUPT_KDF_MAX_ITERATIONS) return ZUPT_ERR_CORRUPT; fprintf(stderr, " Deriving decryption key (PBKDF2-SHA256, %u iterations)...\n", iter); zupt_derive_keys(&opts->keyring, opts->password, salt, nonce, iter); return ZUPT_OK; @@ -2059,7 +2084,13 @@ static zupt_error_t parse_index(const uint8_t *buf, size_t blen, zupt_index_entry_t *e = &(*ents)[i]; uint64_t plen; vn = zupt_decode_varint(buf+p, blen-p, &plen); - if (vn<0||p+(size_t)vn+plen>blen) { free(*ents); return ZUPT_ERR_CORRUPT; } + if (vn<0) { free(*ents); return ZUPT_ERR_CORRUPT; } + /* SECURITY: overflow-safe bound. The decoder consumes at most blen-p + * bytes so p+vn<=blen and blen-p-vn cannot underflow. The previous + * check `p+vn+plen>blen` wrapped around for an attacker-supplied + * ~2^64 plen, passed, then drove an OOB memcpy of ZUPT_MAX_PATH-1 + * bytes past the index buffer. */ + if (plen > (uint64_t)(blen - p - (size_t)vn)) { free(*ents); return ZUPT_ERR_CORRUPT; } p += (size_t)vn; if (plen >= ZUPT_MAX_PATH) plen = ZUPT_MAX_PATH-1; memcpy(e->path, buf+p, (size_t)plen); e->path[plen]='\0'; p += (size_t)plen; @@ -2389,7 +2420,11 @@ zupt_error_t zupt_extract_archive(const char *arc, const char *dir, zupt_options uint64_t off = e->first_block_offset; uint64_t sz = e->uncompressed_size; - if (off + sz <= total_size) { + /* SECURITY: overflow-safe bound. off and sz are both attacker- + * controlled 64-bit index fields; the previous `off+sz<=total_size` + * wrapped on overflow, letting solid_buf+off point far out of + * bounds for an arbitrary-offset OOB heap read. */ + if (off <= total_size && sz <= total_size - off) { if (fwrite(solid_buf + off, 1, (size_t)sz, of) != (size_t)sz) { fprintf(stderr, "Error: write failed (disk full?) for %s\n", e->path); fclose(of); diff --git a/src/zupt_lzh.c b/src/zupt_lzh.c index 071e592..787f08b 100644 --- a/src/zupt_lzh.c +++ b/src/zupt_lzh.c @@ -319,14 +319,19 @@ static void huff_lut(const uint8_t *lengths, int ns, hlut_t *lut) { int sz = 1< LZH_MAX_CODELEN + * would read/write out of bounds and shift by a negative amount (UB). + * Callers validate, but guard here too so the builder is memory-safe + * for any input (defense in depth). */ int lc[LZH_MAX_CODELEN+1]; memset(lc,0,sizeof(lc)); - for(int i=0;i0) lc[lengths[i]]++; + for(int i=0;i0 && lengths[i]<=LZH_MAX_CODELEN) lc[lengths[i]]++; uint32_t nc[LZH_MAX_CODELEN+1]; memset(nc,0,sizeof(nc)); uint32_t cv=0; for(int b=1;b<=LZH_MAX_CODELEN;b++){cv=(cv+lc[b-1])<<1;nc[b]=cv;} for(int i=0;iLZH_MAX_CODELEN) continue; int bits=lengths[i]; uint16_t c=(uint16_t)nc[bits]++; uint16_t rev=0; @@ -761,9 +766,17 @@ size_t zupt_lzh_decompress(const uint8_t *src, size_t slen, if (used < 0) return 0; ip += cl_len; } else { - /* Raw code lengths */ - if (ip + ll_hdr > slen) return 0; + /* Raw code lengths: one byte per symbol. SECURITY: bound the count + * against BOTH the source AND the destination stack buffer + * (ll_lens[LZH_MAX_LITLEN]). ll_hdr is attacker-controlled and may be + * up to 0x7FFF; without the destination bound a crafted archive + * smashes the stack. Also reject out-of-range code-length values + * (raw bytes are unconstrained; legal canonical lengths are 0..15) + * so the LUT builder cannot index past lc[]/nc[]. */ + if (ll_hdr > LZH_MAX_LITLEN || ip + ll_hdr > slen) return 0; memcpy(ll_lens, src + ip, ll_hdr); ip += ll_hdr; + for (size_t k = 0; k < ll_hdr; k++) + if (ll_lens[k] > LZH_MAX_CODELEN) return 0; } /* Read dist code lengths */ @@ -776,8 +789,12 @@ size_t zupt_lzh_decompress(const uint8_t *src, size_t slen, if (used < 0) return 0; ip += cl_len; } else { - if (ip + d_hdr > slen) return 0; + /* Raw dist code lengths — same destination-bound + value-range + * hardening as the litlen path above (d_lens[LZH_MAX_DIST]). */ + if (d_hdr > LZH_MAX_DIST || ip + d_hdr > slen) return 0; memcpy(d_lens, src + ip, d_hdr); ip += d_hdr; + for (size_t k = 0; k < d_hdr; k++) + if (d_lens[k] > LZH_MAX_CODELEN) return 0; } /* Build LUTs */ diff --git a/src/zupt_parallel.c b/src/zupt_parallel.c index efb6554..7a3cddd 100644 --- a/src/zupt_parallel.c +++ b/src/zupt_parallel.c @@ -135,7 +135,26 @@ static void worker_compress(zpar_slot_t *slot, const zupt_keyring_t *kr) { slot->out_bflags = 0; if (kr && kr->active) { size_t enc_len; - uint8_t *enc = zupt_encrypt_buffer(kr, payload, payload_size, slot->block_seq, &enc_len); + uint8_t *enc; + /* F-09: when the archive uses AAD-preface mode, bind the per-block frame + * preface into the MAC EXACTLY as the serial path does (zupt_format.c). + * The extract side honours the archive's ZUPT_FLAG_AAD_PREFACE flag, so + * if this worker skipped the preface every multithreaded encrypted block + * would fail authentication and the archive would be unextractable. The + * scalars mirror the serial call: predicted compressed_size is + * nonce(16) + payload + hmac(32), block_flags is ENCRYPTED. */ + if (kr->use_preface_aad) { + uint8_t preface[ZUPT_PREFACE_AAD_LEN]; + uint64_t predicted_csz = 16 + (uint64_t)payload_size + 32; + zupt_serialize_preface_aad_scalars( + ZUPT_BLOCK_DATA, slot->actual_codec, (uint16_t)ZUPT_BFLAG_ENCRYPTED, + (uint64_t)nread, predicted_csz, slot->checksum, preface); + enc = zupt_encrypt_buffer_aad(kr, payload, payload_size, slot->block_seq, + preface, ZUPT_PREFACE_AAD_LEN, &enc_len); + zupt_secure_wipe(preface, sizeof(preface)); + } else { + enc = zupt_encrypt_buffer(kr, payload, payload_size, slot->block_seq, &enc_len); + } if (!enc) { free(cbuf); slot->error = ZUPT_ERR_NOMEM; return; } /* Output is the encrypted payload (caller frees slot->output) */ slot->output = enc; @@ -169,11 +188,36 @@ static void worker_decompress(zpar_slot_t *slot, const zupt_keyring_t *kr) { if (!comp_data && comp_len > 0) { slot->error = ZUPT_ERR_CORRUPT; return; } if (slot->uncomp_size > ZUPT_MAX_BLOCK_SZ) { slot->error = ZUPT_ERR_OVERFLOW; return; } - /* Decrypt if encrypted — HMAC verified inside zupt_decrypt_buffer (before decryption) */ + /* SECURITY: in an encrypted archive every block MUST be encrypted. The + * per-block ENCRYPTED flag is not covered by the archive-integrity + * trailer, so without this gate an attacker could clear the flag on a + * forged STORE block and inject attacker-chosen plaintext that passes + * only the keyless XXH64 — an authentication bypass. Mirror the + * single-threaded decompress_block fail-closed behaviour. */ + if (kr && kr->active && !(slot->block_flags & ZUPT_BFLAG_ENCRYPTED)) { + slot->error = ZUPT_ERR_AUTH_FAIL; return; + } + + /* Decrypt if encrypted — HMAC verified inside the decrypt call (before + * decryption). Must mirror the compress worker and the serial + * decompress_block: when the archive uses AAD-preface mode, rebuild the + * canonical preface from this block's stored header fields and bind it into + * the MAC, otherwise a multithreaded extract of a preface-bound archive + * fails to authenticate every block. */ if (slot->block_flags & ZUPT_BFLAG_ENCRYPTED) { if (!kr || !kr->active) { slot->error = ZUPT_ERR_AUTH_FAIL; return; } size_t dec_len; - dec_payload = zupt_decrypt_buffer(kr, comp_data, comp_len, slot->block_seq, &dec_len); + if (kr->use_preface_aad) { + uint8_t preface[ZUPT_PREFACE_AAD_LEN]; + zupt_serialize_preface_aad_scalars( + ZUPT_BLOCK_DATA, slot->codec_id, slot->block_flags, + slot->uncomp_size, (uint64_t)comp_len, slot->stored_checksum, preface); + dec_payload = zupt_decrypt_buffer_aad(kr, comp_data, comp_len, slot->block_seq, + preface, ZUPT_PREFACE_AAD_LEN, &dec_len); + zupt_secure_wipe(preface, sizeof(preface)); + } else { + dec_payload = zupt_decrypt_buffer(kr, comp_data, comp_len, slot->block_seq, &dec_len); + } if (!dec_payload) { slot->error = ZUPT_ERR_AUTH_FAIL; return; } comp_data = dec_payload; comp_len = dec_len; @@ -329,6 +373,11 @@ static void *worker_entry(void *arg) { zpar_ctx_t *zpar_create(int nthreads, uint32_t block_size, int mode, const zupt_keyring_t *keyring) { if (nthreads < 1) nthreads = 1; + /* SECURITY (defense in depth): clamp the worker/slot count here too, not + * only at the CLI. A direct library/API caller could otherwise request an + * arbitrary count and exhaust memory (per-slot input buffers) and thread + * handles. The CLI already clamps -t to the same ceiling. */ + if (nthreads > ZUPT_MAX_THREADS) nthreads = ZUPT_MAX_THREADS; zpar_ctx_t *ctx = (zpar_ctx_t *)calloc(1, sizeof(zpar_ctx_t)); if (!ctx) return NULL; diff --git a/vendor/pqvaptvupt/libpqvaptvupt.so b/vendor/pqvaptvupt/libpqvaptvupt.so deleted file mode 120000 index f025946..0000000 --- a/vendor/pqvaptvupt/libpqvaptvupt.so +++ /dev/null @@ -1 +0,0 @@ -libpqvaptvupt.so.0.6.0 \ No newline at end of file diff --git a/vendor/pqvaptvupt/libpqvaptvupt.so.0 b/vendor/pqvaptvupt/libpqvaptvupt.so.0 deleted file mode 120000 index f025946..0000000 --- a/vendor/pqvaptvupt/libpqvaptvupt.so.0 +++ /dev/null @@ -1 +0,0 @@ -libpqvaptvupt.so.0.6.0 \ No newline at end of file diff --git a/vendor/pqvaptvupt/libpqvaptvupt.so.0.6.0 b/vendor/pqvaptvupt/libpqvaptvupt.so.0.6.0 deleted file mode 100755 index 8be60d0ae40a9c27b6e2829a9291076f50367f8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56504 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 Mc)zcq&}f?f1C0_XHUIzs diff --git a/vendor/zuptsdk/libzuptsdk.so b/vendor/zuptsdk/libzuptsdk.so deleted file mode 120000 index 437e80a..0000000 --- a/vendor/zuptsdk/libzuptsdk.so +++ /dev/null @@ -1 +0,0 @@ -libzuptsdk.so.2.0.0 \ No newline at end of file diff --git a/vendor/zuptsdk/libzuptsdk.so.2 b/vendor/zuptsdk/libzuptsdk.so.2 deleted file mode 120000 index 437e80a..0000000 --- a/vendor/zuptsdk/libzuptsdk.so.2 +++ /dev/null @@ -1 +0,0 @@ -libzuptsdk.so.2.0.0 \ No newline at end of file diff --git a/vendor/zuptsdk/libzuptsdk.so.2.0.0 b/vendor/zuptsdk/libzuptsdk.so.2.0.0 deleted file mode 100755 index 6521e636dbb177431aae86394b063b86ade1c7b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 737552 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 x%ge6|_;n$_F5=g4er53MVt!r1uMzyZ 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

RJ<~s8U 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