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.
This commit is contained in:
parent
136a96ed20
commit
4874010d0e
39 changed files with 1097 additions and 2770 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -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
|
||||
|
|
|
|||
113
BENCHMARKS.md
113
BENCHMARKS.md
|
|
@ -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.
|
||||
45
CHANGELOG.md
45
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
|
||||
|
|
|
|||
130
DISTRIBUTION.md
130
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.
|
||||
|
|
|
|||
94
INSTALL.md
94
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.
|
||||
|
|
|
|||
31
Makefile
31
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
|
||||
|
|
|
|||
567
README.md
567
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.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
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 <file>`** 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 <file>`.
|
||||
|
||||
| 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 <N>` | Thread count (0=auto, 1=single, 2–64) |
|
||||
| `-p [PW]` | Password encryption (PBKDF2 → AES-256) |
|
||||
| `-p [PW]` | Password encryption (PBKDF2-SHA256 → AES-256) |
|
||||
| `--pq <keyfile>` | Post-quantum hybrid encryption |
|
||||
| `-o <DIR>` | 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)
|
||||
|
|
|
|||
90
ROADMAP.md
90
ROADMAP.md
|
|
@ -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
|
||||
331
SECURITY.md
331
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: <brief>`.
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
188
THREAT_MODEL.md
188
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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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 <sac@securityops.co> 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):
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<service name="tar_scm" mode="manual">
|
||||
<param name="url">https://github.com/cristiancmoises/zupt</param>
|
||||
<param name="scm">git</param>
|
||||
<param name="revision">v4.0.0</param>
|
||||
<param name="revision">v4.1.0</param>
|
||||
<param name="versionformat">@PARENT_TAG@</param>
|
||||
<param name="versionrewrite-pattern">v(.*)</param>
|
||||
<param name="submodules">enable</param>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,28 @@
|
|||
-------------------------------------------------------------------
|
||||
Tue Jul 7 12:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
|
||||
|
||||
- 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 <cabelo@opensuse.org>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Binary file not shown.
25
src/vv_ans.c
25
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;
|
||||
|
|
|
|||
|
|
@ -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 <stdio.h>
|
||||
|
|
@ -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 <stdio.h>
|
||||
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -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 <stdio.h>
|
||||
|
|
@ -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 <stdio.h>
|
||||
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -319,14 +319,19 @@ static void huff_lut(const uint8_t *lengths, int ns, hlut_t *lut) {
|
|||
int sz = 1<<LZH_MAX_CODELEN;
|
||||
for(int i=0;i<sz;i++){lut[i].sym=-1;lut[i].len=0;}
|
||||
|
||||
/* SECURITY: code lengths index lc[]/nc[] (size LZH_MAX_CODELEN+1) and
|
||||
* drive the shift 1<<(LZH_MAX_CODELEN-bits); a value > 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;i<ns;i++) if(lengths[i]>0) lc[lengths[i]]++;
|
||||
for(int i=0;i<ns;i++) if(lengths[i]>0 && 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;i<ns;i++){
|
||||
if(lengths[i]==0) continue;
|
||||
if(lengths[i]==0 || lengths[i]>LZH_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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
1
vendor/pqvaptvupt/libpqvaptvupt.so
vendored
1
vendor/pqvaptvupt/libpqvaptvupt.so
vendored
|
|
@ -1 +0,0 @@
|
|||
libpqvaptvupt.so.0.6.0
|
||||
1
vendor/pqvaptvupt/libpqvaptvupt.so.0
vendored
1
vendor/pqvaptvupt/libpqvaptvupt.so.0
vendored
|
|
@ -1 +0,0 @@
|
|||
libpqvaptvupt.so.0.6.0
|
||||
BIN
vendor/pqvaptvupt/libpqvaptvupt.so.0.6.0
vendored
BIN
vendor/pqvaptvupt/libpqvaptvupt.so.0.6.0
vendored
Binary file not shown.
1
vendor/zuptsdk/libzuptsdk.so
vendored
1
vendor/zuptsdk/libzuptsdk.so
vendored
|
|
@ -1 +0,0 @@
|
|||
libzuptsdk.so.2.0.0
|
||||
1
vendor/zuptsdk/libzuptsdk.so.2
vendored
1
vendor/zuptsdk/libzuptsdk.so.2
vendored
|
|
@ -1 +0,0 @@
|
|||
libzuptsdk.so.2.0.0
|
||||
BIN
vendor/zuptsdk/libzuptsdk.so.2.0.0
vendored
BIN
vendor/zuptsdk/libzuptsdk.so.2.0.0
vendored
Binary file not shown.
Loading…
Reference in a new issue