v4.0.0: codec 2.60.4 security release, --pq-box sealed-box mode, F-16 fix
Some checks failed
CI / build-and-test (clang) (push) Has been cancelled
CI / build-and-test (gcc) (push) Has been cancelled
CI / strict-warnings (clang, -Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -O2 -std=c11 -Werror) (push) Has been cancelled
CI / strict-warnings (gcc, -Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -Wformat-security -Wlogical-op -Wjump-misses-init -Wdouble-promotion -O2 -std=c11 -Werror) (push) Has been cancelled
CI / sanitizers (push) Has been cancelled
CI / pie-hardening (push) Has been cancelled
CI / cross-aarch64 (push) Has been cancelled
CI / dist-reproducibility (push) Has been cancelled
CI / packaging-syntax (push) Has been cancelled
CI / release (push) Has been cancelled

Major release. Highlights:

- Codec: vendored VaptVupt codec moves to canonical 2.60.4 security
  release. Fixes a high-severity OOB heap write in the AVX2 decode fast
  path (reachable on a valid stream sized to exactly content_size, both
  tail variants). Brings CBMC-formally-verified BCJ filters with
  automatic ELF/PE/Mach-O detection. Compressed output stays
  byte-identical (ratio gate Δ 0.00%); wire format unchanged at v1.6.
- New --pq-box sealed-box recipient mode (vendored libpqvaptvupt 0.6.0):
  ML-KEM-768 + X25519 combined via HKDF-SHA256 with domain separation,
  AES-256-CTR + HMAC-SHA256 EtM. Legacy --pq and --pq-sdk stay readable.
- F-16: discloses and fixes a pre-existing data-loss defect in the
  <= 3.8.0 in-tree BCJ encoder. Full back-compat matrix decodes
  byte-exact under 4.0.0; every readable pre-4.0 archive remains readable.

Repository hygiene:
- Sync full 4.0.0 source tree (codec, crypto, SDK, GUI, packaging, tests).
- Remove internal scratch files (PROMPT.md, FORMAL_AUDIT_PROMPT.md)
  and superseded version-specific docs (INTEGRATION_PROTOCOL_2.60.4.md,
  docs/FINDINGS-2.x.md) and a stray test binary.
- Refresh README download/install section to real 4.0.0 release assets;
  bump version badge to 4.0.0.
- Add .gitignore for build outputs (keeps vendored prebuilt libraries).
This commit is contained in:
Cristian Cezar Moisés 2026-06-10 18:48:58 -03:00
commit 544a2cd647
98 changed files with 15615 additions and 1397 deletions

64
packaging/aur/PKGBUILD Normal file
View file

@ -0,0 +1,64 @@
# Maintainer: Cristian Cezar Moisés <sac@securityops.co>
#
# AUR submission instructions:
# 1. Run `make dist` upstream to produce zupt-VERSION.tar.gz.
# 2. Upload that tarball somewhere stable (GitHub release / git.securityops.co).
# 3. Update `source=()` URL and `sha256sums=()` below.
# 4. Run `makepkg --printsrcinfo > .SRCINFO` in this directory.
# 5. Commit and push to ssh://aur@aur.archlinux.org/zupt.git
#
# Test locally: `makepkg -s` in this directory after dropping a copy of the
# zupt-VERSION.tar.gz alongside the PKGBUILD.
pkgname=vaptvupt
pkgver=4.0.0
pkgrel=1
provides=('zupt')
replaces=('zupt')
conflicts=('zupt')
pkgdesc='Pure-C11 post-quantum backup compression utility (AES-256-CTR + HMAC-SHA256 + ML-KEM-768 + X25519)'
arch=('x86_64' 'aarch64')
url='https://git.securityops.co/cristiancmoises/zupt'
license=('AGPL-3.0-or-later')
depends=('glibc')
makedepends=('gcc')
checkdepends=('python')
# Replace SHA256 placeholder with output of:
# sha256sum /tmp/zupt-2.4.4.tar.gz
source=("${pkgname}-${pkgver}.tar.gz::https://git.securityops.co/cristiancmoises/zupt/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
sha256sums=('SKIP')
build() {
cd "${pkgname}-${pkgver}"
# Strict-warning build that the project's own §6 verification matrix uses.
CFLAGS="${CFLAGS:--O2 -std=c11} -Wall -Wextra -Wpedantic" \
make -j"$(nproc)"
}
check() {
cd "${pkgname}-${pkgver}"
# Project regression suite — F-06 HMAC, F-08 top-MAC, F-09 byte sweep, etc.
make test
}
package() {
cd "${pkgname}-${pkgver}"
make DESTDIR="${pkgdir}" PREFIX=/usr install
# Docs that aren't part of `make install`
install -Dm644 README.md "${pkgdir}/usr/share/doc/${pkgname}/README.md"
install -Dm644 SECURITY.md "${pkgdir}/usr/share/doc/${pkgname}/SECURITY.md"
install -Dm644 CHANGELOG.md "${pkgdir}/usr/share/doc/${pkgname}/CHANGELOG.md"
install -Dm644 AUDIT.md "${pkgdir}/usr/share/doc/${pkgname}/AUDIT.md"
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
# Vendored libzuptsdk shipped alongside the binary because the binary
# is linked with -Wl,-rpath,$ORIGIN/vendor/zuptsdk. For system install
# we move it to /usr/lib/zupt/ and the binary's rpath remains relative.
install -d "${pkgdir}/usr/lib/${pkgname}"
install -Dm755 vendor/zuptsdk/libzuptsdk.so.2.0.0 \
"${pkgdir}/usr/lib/${pkgname}/libzuptsdk.so.2.0.0"
ln -sf libzuptsdk.so.2.0.0 "${pkgdir}/usr/lib/${pkgname}/libzuptsdk.so.2"
ln -sf libzuptsdk.so.2.0.0 "${pkgdir}/usr/lib/${pkgname}/libzuptsdk.so"
}

View file

@ -1,49 +1,57 @@
#!/bin/bash
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2025-2026 Cristian Cezar Moisés
# Build zupt as AppImage (portable single-file binary).
# Build vaptvupt CLI as AppImage (portable single-file binary).
# Includes a legacy `zupt` symlink so AppDir users can invoke either name.
set -e
cd "$(dirname "$0")/.."
VERSION="${VERSION:-2.2.3}"
VERSION="${VERSION:-3.0.0}"
ARCH="${ARCH:-x86_64}"
NAME="zupt-$VERSION-$ARCH"
PKGNAME="vaptvupt"
LEGACY="zupt"
NAME="$PKGNAME-$VERSION-$ARCH"
OUT="/tmp/${NAME}.AppDir"
rm -rf "$OUT"
mkdir -p "$OUT/usr/bin" "$OUT/usr/lib" "$OUT/usr/share/applications" "$OUT/usr/share/icons/hicolor/256x256/apps"
install -m 755 zupt "$OUT/usr/bin/"
install -m 755 $PKGNAME "$OUT/usr/bin/$PKGNAME"
ln -sf $PKGNAME "$OUT/usr/bin/$LEGACY"
install -m 644 vendor/zuptsdk/libzuptsdk.so.2.0.0 "$OUT/usr/lib/"
ln -sf libzuptsdk.so.2.0.0 "$OUT/usr/lib/libzuptsdk.so.2"
ln -sf libzuptsdk.so.2 "$OUT/usr/lib/libzuptsdk.so"
install -m 644 vendor/pqvaptvupt/libpqvaptvupt.so.0.6.0 "$OUT/usr/lib/"
ln -sf libpqvaptvupt.so.0.6.0 "$OUT/usr/lib/libpqvaptvupt.so.0"
ln -sf libpqvaptvupt.so.0 "$OUT/usr/lib/libpqvaptvupt.so"
cat > "$OUT/AppRun" <<'APPRUN'
cat > "$OUT/AppRun" <<APPRUN
#!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export LD_LIBRARY_PATH="$HERE/usr/lib:$LD_LIBRARY_PATH"
export PATH="$HERE/usr/bin:$PATH"
exec "$HERE/usr/bin/zupt" "$@"
HERE="\$(dirname "\$(readlink -f "\${0}")")"
export LD_LIBRARY_PATH="\$HERE/usr/lib:\$LD_LIBRARY_PATH"
export PATH="\$HERE/usr/bin:\$PATH"
exec "\$HERE/usr/bin/$PKGNAME" "\$@"
APPRUN
chmod +x "$OUT/AppRun"
cat > "$OUT/zupt.desktop" <<'DESK'
cat > "$OUT/$PKGNAME.desktop" <<DESK
[Desktop Entry]
Name=Zupt
Comment=Post-quantum backup compression utility
Exec=zupt
Name=VaptVupt
Comment=Post-quantum backup compression utility (formerly Zupt)
Exec=$PKGNAME
Terminal=true
Type=Application
Categories=Utility;Archiving;
Icon=zupt
Categories=Utility;Archiving;Security;
Icon=$PKGNAME
DESK
cp "$OUT/zupt.desktop" "$OUT/usr/share/applications/"
cp "$OUT/$PKGNAME.desktop" "$OUT/usr/share/applications/"
printf '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\rIDATx\x9cc\xfc\xcf\xc0\x00\x00\x00\x05\x00\x01\xa5\xf6E@\x00\x00\x00\x00IEND\xaeB`\x82' > "$OUT/zupt.png"
cp "$OUT/zupt.png" "$OUT/usr/share/icons/hicolor/256x256/apps/zupt.png"
# 1x1 PNG placeholder — replace with a real icon when the brand asset exists
printf '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\rIDATx\x9cc\xfc\xcf\xc0\x00\x00\x00\x05\x00\x01\xa5\xf6E@\x00\x00\x00\x00IEND\xaeB`\x82' > "$OUT/$PKGNAME.png"
cp "$OUT/$PKGNAME.png" "$OUT/usr/share/icons/hicolor/256x256/apps/$PKGNAME.png"
if command -v appimagetool >/dev/null 2>&1; then
ARCH=$ARCH appimagetool "$OUT" "/tmp/${NAME}.AppImage"
ARCH=$ARCH appimagetool "$OUT" "/tmp/${NAME}.AppImage" 2>&1 | tail -5
echo "Built: /tmp/${NAME}.AppImage"
fi
@ -53,4 +61,4 @@ fi
cd /tmp
tar -czf "${NAME}.AppDir.tar.gz" "$(basename "$OUT")"
echo "Built: /tmp/${NAME}.AppDir.tar.gz"
echo "Users can run: tar xzf ${NAME}.AppDir.tar.gz && ./${NAME}.AppDir/AppRun"
echo "Users can run: tar xzf ${NAME}.AppDir.tar.gz && ./${NAME}.AppDir/AppRun version"

View file

@ -1,16 +1,27 @@
#!/bin/bash
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2025-2026 Cristian Cezar Moisés
# Build self-contained zupt CLI .deb package.
# Bundles libzuptsdk.so.2 under /usr/lib/zupt/ so users do NOT need to
# separately install the libzuptsdk package.
#
# Build self-contained vaptvupt CLI .deb package.
#
# v3.0.0 rename: the binary is now `vaptvupt`; we install it at
# /usr/bin/vaptvupt and create /usr/bin/zupt → /usr/bin/vaptvupt as
# a legacy symlink for one major version cycle. The package name
# is `vaptvupt` with Provides/Replaces/Conflicts on `zupt` so
# `apt install zupt` still resolves cleanly.
#
# Bundles libzuptsdk.so.2 under /usr/lib/vaptvupt/ so users do NOT
# need to separately install the libzuptsdk package.
set -e
cd "$(dirname "$0")/.."
VERSION="${VERSION:-2.2.3}"
VERSION="${VERSION:-3.0.0}"
ARCH="${ARCH:-amd64}"
PKGNAME="vaptvupt"
LEGACY="zupt"
PKG="zupt_${VERSION}_${ARCH}"
PKG="${PKGNAME}_${VERSION}_${ARCH}"
ROOT="/tmp/$PKG"
# Vendored libzuptsdk path (relative to project root)
@ -19,141 +30,105 @@ if [ ! -f "$SDK_LIB" ]; then
echo "ERROR: $SDK_LIB not found. Vendor the libzuptsdk shared object first." >&2
exit 1
fi
PQVV_LIB="vendor/pqvaptvupt/libpqvaptvupt.so.0.6.0"
if [ ! -f "$PQVV_LIB" ]; then
echo "ERROR: $PQVV_LIB not found. Vendor the libpqvaptvupt shared object first." >&2
exit 1
fi
# Rebuild zupt fresh, then patch the rpath to point at /usr/lib/zupt
echo "[deb] Building zupt"
echo "[deb] Building vaptvupt"
make clean >/dev/null 2>&1 || true
make -j"$(nproc)" >/dev/null
echo "[deb] Patching rpath -> /usr/lib/zupt:/usr/lib64/zupt"
patchelf --set-rpath '/usr/lib/zupt:/usr/lib64/zupt' zupt
echo "[deb] Patching rpath -> /usr/lib/$PKGNAME:/usr/lib64/$PKGNAME"
patchelf --set-rpath "/usr/lib/$PKGNAME:/usr/lib64/$PKGNAME" $PKGNAME
# Verify rpath was applied
if ! readelf -d zupt | grep -q "RUNPATH.*\[/usr/lib/zupt:/usr/lib64/zupt\]"; then
echo "ERROR: built zupt does not have correct RUNPATH" >&2
readelf -d zupt | grep -E "RPATH|RUNPATH"
if ! readelf -d $PKGNAME | grep -q "RUNPATH.*\[/usr/lib/$PKGNAME:/usr/lib64/$PKGNAME\]"; then
echo "ERROR: built $PKGNAME does not have correct RUNPATH" >&2
readelf -d $PKGNAME | grep -E "RPATH|RUNPATH"
exit 1
fi
rm -rf "$ROOT"
mkdir -p "$ROOT/DEBIAN" \
"$ROOT/usr/bin" \
"$ROOT/usr/lib/zupt" \
"$ROOT/usr/share/doc/zupt" \
"$ROOT/usr/share/man/man1"
"$ROOT/usr/lib/$PKGNAME" \
"$ROOT/usr/share/doc/$PKGNAME" \
"$ROOT/usr/share/man/man1" \
"$ROOT/usr/share/bash-completion/completions" \
"$ROOT/usr/share/zsh/site-functions" \
"$ROOT/usr/share/fish/vendor_completions.d"
# Binary
install -m 755 zupt "$ROOT/usr/bin/zupt"
# Binary + legacy symlink
install -m 755 $PKGNAME "$ROOT/usr/bin/$PKGNAME"
ln -sf $PKGNAME "$ROOT/usr/bin/$LEGACY"
# Bundled libzuptsdk
install -m 755 "$SDK_LIB" "$ROOT/usr/lib/zupt/libzuptsdk.so.2.0.0"
ln -sf libzuptsdk.so.2.0.0 "$ROOT/usr/lib/zupt/libzuptsdk.so.2"
ln -sf libzuptsdk.so.2.0.0 "$ROOT/usr/lib/zupt/libzuptsdk.so"
install -m 755 "$SDK_LIB" "$ROOT/usr/lib/$PKGNAME/libzuptsdk.so.2.0.0"
ln -sf libzuptsdk.so.2.0.0 "$ROOT/usr/lib/$PKGNAME/libzuptsdk.so.2"
ln -sf libzuptsdk.so.2.0.0 "$ROOT/usr/lib/$PKGNAME/libzuptsdk.so"
install -m 755 "$PQVV_LIB" "$ROOT/usr/lib/$PKGNAME/libpqvaptvupt.so.0.6.0"
ln -sf libpqvaptvupt.so.0.6.0 "$ROOT/usr/lib/$PKGNAME/libpqvaptvupt.so.0"
ln -sf libpqvaptvupt.so.0.6.0 "$ROOT/usr/lib/$PKGNAME/libpqvaptvupt.so"
# Manpage (gzip-compressed); install + legacy alias
if [ -f doc/vaptvupt.1 ]; then
gzip -9n -c doc/vaptvupt.1 > "$ROOT/usr/share/man/man1/$PKGNAME.1.gz"
ln -sf $PKGNAME.1.gz "$ROOT/usr/share/man/man1/$LEGACY.1.gz"
fi
# Shell completions
if [ -f completions/vaptvupt.bash ]; then
install -m 0644 completions/vaptvupt.bash "$ROOT/usr/share/bash-completion/completions/$PKGNAME"
ln -sf $PKGNAME "$ROOT/usr/share/bash-completion/completions/$LEGACY"
fi
if [ -f completions/_vaptvupt ]; then
install -m 0644 completions/_vaptvupt "$ROOT/usr/share/zsh/site-functions/_$PKGNAME"
ln -sf _$PKGNAME "$ROOT/usr/share/zsh/site-functions/_$LEGACY"
fi
if [ -f completions/vaptvupt.fish ]; then
install -m 0644 completions/vaptvupt.fish "$ROOT/usr/share/fish/vendor_completions.d/$PKGNAME.fish"
fi
# Docs
install -m 644 README.md CHANGELOG.md SECURITY.md AUDIT.md "$ROOT/usr/share/doc/zupt/"
gzip -9n -c CHANGELOG.md > "$ROOT/usr/share/doc/zupt/changelog.gz"
install -m 0644 README.md "$ROOT/usr/share/doc/$PKGNAME/README.md"
install -m 0644 LICENSE "$ROOT/usr/share/doc/$PKGNAME/copyright"
[ -f SECURITY.md ] && install -m 0644 SECURITY.md "$ROOT/usr/share/doc/$PKGNAME/SECURITY.md"
[ -f CHANGELOG.md ] && install -m 0644 CHANGELOG.md "$ROOT/usr/share/doc/$PKGNAME/CHANGELOG.md"
[ -f THREAT_MODEL.md ] && install -m 0644 THREAT_MODEL.md "$ROOT/usr/share/doc/$PKGNAME/THREAT_MODEL.md"
# Man page
if [ -f doc/zupt.1 ]; then
install -m 644 doc/zupt.1 "$ROOT/usr/share/man/man1/zupt.1"
else
cat > "$ROOT/usr/share/man/man1/zupt.1" <<MAN
.TH ZUPT 1 "May 2026" "zupt $VERSION" "User Commands"
.SH NAME
zupt \\- post-quantum backup compression utility
.SH SYNOPSIS
.B zupt
[\\fIcommand\\fR] [\\fIoptions\\fR] \\fIarchive\\fR [\\fIfiles...\\fR]
.SH SEE ALSO
Run \\fBzupt help\\fR for the full options reference.
MAN
fi
gzip -9n "$ROOT/usr/share/man/man1/zupt.1"
# Copyright
cat > "$ROOT/usr/share/doc/zupt/copyright" <<'COPYRIGHT'
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: zupt
Upstream-Contact: Cristian Cezar Moisés <zupt@riseup.net>
Source: https://git.securityops.co/cristiancmoises/zupt
Files: *
Copyright: 2025-2026 Cristian Cezar Moisés
License: AGPL-3.0+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
.
On Debian systems, the complete text of the GNU Affero General Public
License version 3 can be found in /usr/share/common-licenses/AGPL-3.
Files: src/vv_*.c src/vaptvupt_api.c include/vv_*.h include/vaptvupt*.h
Copyright: 2025-2026 Cristian Cezar Moisés
License: GPL-3.0+
The VaptVupt LZ codec is licensed under the GNU General Public License
version 3 or later (NOT AGPL like the rest of the project). VaptVupt
is GPL so that, with sufficient maturity, it can be considered for
upstreaming into the Linux or BSD kernels.
.
On Debian systems, the complete text of the GNU General Public
License version 3 can be found in /usr/share/common-licenses/GPL-3.
Files: usr/lib/zupt/libzuptsdk.so.*
Copyright: 2025-2026 Cristian Cezar Moisés
License: AGPL-3.0+
Bundled libzuptsdk shared object is part of the upstream libzuptsdk
project, AGPL-3.0+. Source: https://git.securityops.co/cristiancmoises/libzuptsdk
Comment:
Commercial licenses (relief from AGPL/GPL copyleft terms) are
available for both components. Contact sac@securityops.co for
commercial inquiries.
COPYRIGHT
# Control
INSTALLED_SIZE=$(du -sk "$ROOT" | cut -f1)
# DEBIAN/control
INSTALLED_KB=$(du -sk "$ROOT/usr" | awk '{print $1}')
cat > "$ROOT/DEBIAN/control" <<EOF
Package: zupt
Package: $PKGNAME
Version: $VERSION
Section: utils
Priority: optional
Architecture: $ARCH
Depends: libc6 (>= 2.28), libargon2-1, libssl3
Maintainer: Cristian Cezar Moisés <zupt@riseup.net>
Installed-Size: $INSTALLED_SIZE
Provides: $LEGACY (= $VERSION)
Replaces: $LEGACY (<< 3.0.0)
Conflicts: $LEGACY (<< 3.0.0)
Depends: libargon2-1, libssl3 | libssl3t64
Installed-Size: $INSTALLED_KB
Maintainer: Cristian Cezar Moisés <sac@securityops.co>
Homepage: https://git.securityops.co/cristiancmoises/zupt
Description: Post-quantum backup compression utility
Zupt is a backup-oriented compression utility with hybrid post-quantum
encryption (ML-KEM-768 + X25519). It provides AES-256-CTR + HMAC-SHA256
authenticated encryption, multi-threaded compression, full-disk
backup/restore, block-level deduplication, and embeds the VaptVupt
2.48.2 codec for high-throughput LZ77 + tANS compression with AVX2
and NEON SIMD acceleration. The libzuptsdk shared library is bundled
under /usr/lib/zupt -- no separate package required.
Description: Post-quantum backup compression utility (formerly zupt)
VaptVupt (renamed from Zupt in v3.0.0 due to a prior INPI Brasil
trademark on the name) is a pure-C11 backup compression utility
featuring post-quantum hybrid encryption (ML-KEM-768 + X25519,
FIPS 203), AES-256-CTR + HMAC-SHA256 authenticated encryption,
Argon2id KDF (PBKDF2-SHA256 via --kdf pbkdf2), multi-threaded
compression with the VaptVupt LZ + ANS codec 2.48.5, full-disk
backup with sparse-region detection, and end-to-end byte-level
tamper detection on encrypted archives (F-09: 0/1827 silent
accepts).
.
The .zupt archive extension is unchanged; v2.x and v3.0.0
archives are bidirectionally compatible. The legacy /usr/bin/zupt
symlink is preserved for one major version cycle.
EOF
# Postinst / Postrm: nothing needed; libzuptsdk is found via RPATH
cat > "$ROOT/DEBIAN/postinst" <<'POSTINST'
#!/bin/sh
set -e
exit 0
POSTINST
chmod 755 "$ROOT/DEBIAN/postinst"
cat > "$ROOT/DEBIAN/postrm" <<'POSTRM'
#!/bin/sh
set -e
exit 0
POSTRM
chmod 755 "$ROOT/DEBIAN/postrm"
# Build
dpkg-deb -Zxz --build --root-owner-group "$ROOT" "/tmp/$PKG.deb"
echo ""
echo "Built: /tmp/$PKG.deb ($(du -h /tmp/$PKG.deb | cut -f1))"
dpkg-deb --info "/tmp/$PKG.deb" | head -20
echo ""
echo "Contents:"
dpkg-deb --contents "/tmp/$PKG.deb" | head -20
DEB_OUT="/tmp/${PKGNAME}_${VERSION}_${ARCH}.deb"
dpkg-deb --build --root-owner-group "$ROOT" "$DEB_OUT" >/dev/null
echo "Built: $DEB_OUT ($(du -h "$DEB_OUT" | cut -f1))"
dpkg-deb -I "$DEB_OUT" | sed -n '1,20p'

188
packaging/build-dmg.sh Executable file
View file

@ -0,0 +1,188 @@
#!/bin/bash
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2025-2026 Cristian Cezar Moisés
#
# Build a macOS .dmg installer for the Zupt CLI.
#
# This script MUST be run on macOS — `hdiutil` is required and only
# ships with macOS. There is no portable way to produce a .dmg from
# Linux that Apple's installer will mount cleanly (libdmg-hfsplus and
# dmg2img exist but produce read-only images that some macOS versions
# reject).
#
# On macOS:
# xcode-select --install # one-time, for clang
# make # build the zupt binary
# VERSION=2.4.7 bash packaging/build-dmg.sh
#
# Produces: /tmp/Zupt-VERSION.dmg with:
# - zupt binary (universal2 if built with -arch x86_64 -arch arm64)
# - libzuptsdk dylib alongside the binary at @loader_path
# - install.command (drag-to-install script)
# - README.md, LICENSE
# - Optional: code-signed and notarized if APPLE_DEV_ID env is set
#
# For Homebrew installation, prefer packaging/homebrew/zupt.rb instead.
# The .dmg is for users who don't want to install Homebrew.
set -e
cd "$(dirname "$0")/.."
VERSION="${VERSION:-2.4.7}"
ARCH="${ARCH:-$(uname -m)}" # x86_64 or arm64
NAME="Zupt-${VERSION}-${ARCH}"
STAGE="/tmp/${NAME}.app/Contents"
# ── Platform check ──
if [ "$(uname)" != "Darwin" ]; then
cat >&2 <<EOF
ERROR: build-dmg.sh must be run on macOS.
The .dmg format requires Apple's hdiutil. On Linux:
- Use the .deb (packaging/build-deb.sh) for Debian/Ubuntu/Mint
- Use the .rpm (packaging/build-rpm.sh) for Fedora/RHEL/openSUSE
- Use the AppImage (packaging/build-appimage.sh) for universal Linux
- Use the Homebrew formula on macOS (packaging/homebrew/zupt.rb)
If you need a macOS .pkg without macOS hardware, GitHub Actions has
macos-14 runners that can produce signed .dmg/.pkg artefacts. See
.github/workflows/ci.yml for the matrix template.
EOF
exit 1
fi
# ── Build zupt (universal binary if possible) ──
echo "[dmg] Building zupt"
make clean
if xcrun --sdk macosx clang -dM -E - </dev/null | grep -q __aarch64__; then
# arm64 host → can cross-build for x86_64 via -arch flag
CFLAGS="-O2 -std=c11 -arch arm64 -arch x86_64" \
LDFLAGS="-arch arm64 -arch x86_64" \
make -j"$(sysctl -n hw.ncpu)" || make -j"$(sysctl -n hw.ncpu)"
else
make -j"$(sysctl -n hw.ncpu)"
fi
# ── Stage the .app bundle ──
echo "[dmg] Staging .app bundle"
rm -rf "/tmp/${NAME}.app"
mkdir -p "$STAGE/MacOS" "$STAGE/Resources" "$STAGE/Frameworks"
install -m 755 zupt "$STAGE/MacOS/zupt"
# Vendored libzuptsdk — on macOS it'd be .dylib, but if the vendored
# build is Linux-style .so, ship that and warn. A proper macOS build
# would produce libzuptsdk.2.0.0.dylib.
if [ -f vendor/zuptsdk/libzuptsdk.2.0.0.dylib ]; then
install -m 755 vendor/zuptsdk/libzuptsdk.2.0.0.dylib "$STAGE/Frameworks/"
install_name_tool -id "@loader_path/../Frameworks/libzuptsdk.2.0.0.dylib" \
"$STAGE/Frameworks/libzuptsdk.2.0.0.dylib"
install_name_tool -change "vendor/zuptsdk/libzuptsdk.so.2" \
"@loader_path/../Frameworks/libzuptsdk.2.0.0.dylib" \
"$STAGE/MacOS/zupt"
elif [ -f vendor/zuptsdk/libzuptsdk.so.2.0.0 ]; then
cat >&2 <<EOF
WARNING: vendor/zuptsdk ships .so (Linux), not .dylib (macOS).
The .dmg will include the Linux library which won't load on macOS.
Build libzuptsdk natively on macOS first, or modify the Makefile
to produce .dylib output on Darwin.
EOF
install -m 755 vendor/zuptsdk/libzuptsdk.so.2.0.0 "$STAGE/Frameworks/"
fi
# Info.plist (minimal — zupt is a CLI, so the .app is mostly a wrapper)
cat > "$STAGE/Info.plist" <<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>co.securityops.zupt</string>
<key>CFBundleName</key>
<string>Zupt</string>
<key>CFBundleDisplayName</key>
<string>Zupt</string>
<key>CFBundleVersion</key>
<string>${VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${VERSION}</string>
<key>CFBundleExecutable</key>
<string>zupt</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
</dict>
</plist>
PLIST
cp README.md "$STAGE/Resources/" 2>/dev/null || true
cp LICENSE "$STAGE/Resources/" 2>/dev/null || true
# ── Drag-to-install command file ──
cat > "/tmp/${NAME}-install.command" <<'INSTALL'
#!/bin/bash
# Drag-installer for Zupt CLI. Copies the binary to /usr/local/bin
# (or the user's ~/bin if /usr/local isn't writable).
set -e
DIR="$(cd "$(dirname "$0")" && pwd)"
APP="$DIR/Zupt.app"
TARGET="/usr/local/bin"
if [ ! -w "$TARGET" ]; then
TARGET="$HOME/bin"
mkdir -p "$TARGET"
echo "Installing to $TARGET (add to PATH if missing)"
fi
cp "$APP/Contents/MacOS/zupt" "$TARGET/zupt"
chmod 755 "$TARGET/zupt"
# Bundle the dylib alongside under a stable path
LIBDIR="/usr/local/lib/zupt"
[ -w /usr/local/lib ] || LIBDIR="$HOME/lib/zupt"
mkdir -p "$LIBDIR"
if [ -d "$APP/Contents/Frameworks" ]; then
cp -P "$APP/Contents/Frameworks"/* "$LIBDIR/" 2>/dev/null || true
fi
echo "Installed: $TARGET/zupt"
"$TARGET/zupt" version
INSTALL
chmod 755 "/tmp/${NAME}-install.command"
# ── Optional: code sign ──
if [ -n "${APPLE_DEV_ID:-}" ]; then
echo "[dmg] Code-signing with Developer ID: $APPLE_DEV_ID"
codesign --force --options runtime --sign "$APPLE_DEV_ID" \
--entitlements packaging/macos/entitlements.plist \
"$STAGE/MacOS/zupt" 2>&1 || echo " (no entitlements file — proceeding unsigned for hardening)"
codesign --force --sign "$APPLE_DEV_ID" "/tmp/${NAME}.app" || true
fi
# ── Build .dmg ──
echo "[dmg] Building disk image"
DMG="/tmp/${NAME}.dmg"
rm -f "$DMG"
# Stage a directory tree that becomes the .dmg root
DMGSRC="/tmp/${NAME}-dmgsrc"
rm -rf "$DMGSRC"
mkdir -p "$DMGSRC"
cp -R "/tmp/${NAME}.app" "$DMGSRC/Zupt.app"
cp "/tmp/${NAME}-install.command" "$DMGSRC/Install Zupt.command"
[ -f README.md ] && cp README.md "$DMGSRC/"
[ -f LICENSE ] && cp LICENSE "$DMGSRC/"
hdiutil create -fs HFS+ -srcfolder "$DMGSRC" -volname "Zupt ${VERSION}" \
-format UDZO -ov "$DMG"
# ── Optional: notarize ──
if [ -n "${APPLE_DEV_ID:-}" ] && [ -n "${APPLE_NOTARIZE_KEY:-}" ]; then
echo "[dmg] Submitting for notarization"
xcrun notarytool submit "$DMG" --apple-id "$APPLE_DEV_ID" \
--password "$APPLE_NOTARIZE_KEY" --wait
xcrun stapler staple "$DMG"
fi
echo ""
echo "Built: $DMG ($(du -h "$DMG" | cut -f1))"
echo "Users mount and drag 'Zupt.app' or double-click 'Install Zupt.command'."

View file

@ -13,44 +13,44 @@
set -e
cd "$(dirname "$0")/.."
VERSION="${VERSION:-1.1.1}"
APPDIR="/tmp/zupt-gui.AppDir"
VERSION="${VERSION:-1.2.0}"
APPDIR="/tmp/vaptvupt-gui.AppDir"
rm -rf "$APPDIR"
mkdir -p "$APPDIR/usr/bin" \
"$APPDIR/usr/lib/zupt-gui" \
"$APPDIR/usr/lib/vaptvupt-gui" \
"$APPDIR/usr/share/applications" \
"$APPDIR/usr/share/icons/hicolor/256x256/apps"
# Python source
install -m 644 gui/src/zupt_gui.py "$APPDIR/usr/lib/zupt-gui/"
install -m 644 gui/src/zupt_gui.py "$APPDIR/usr/lib/vaptvupt-gui/"
# Wrapper
cat > "$APPDIR/usr/bin/zupt-gui" <<'WRAP'
cat > "$APPDIR/usr/bin/vaptvupt-gui" <<'WRAP'
#!/bin/sh
exec python3 "$(dirname "$0")/../lib/zupt-gui/zupt_gui.py" "$@"
exec python3 "$(dirname "$0")/../lib/vaptvupt-gui/zupt_gui.py" "$@"
WRAP
chmod 755 "$APPDIR/usr/bin/zupt-gui"
chmod 755 "$APPDIR/usr/bin/vaptvupt-gui"
# Desktop file
cat > "$APPDIR/zupt-gui.desktop" <<'DESKTOP'
cat > "$APPDIR/vaptvupt-gui.desktop" <<'DESKTOP'
[Desktop Entry]
Type=Application
Name=Zupt GUI
Name=VaptVupt GUI
GenericName=Backup and Compression Utility
Comment=Post-quantum backup with HKDF combiner, key commitment, HPKE binding
Exec=zupt-gui %f
Icon=zupt-gui
Exec=vaptvupt-gui %f
Icon=vaptvupt-gui
Terminal=false
Categories=Utility;Archiving;Compression;Security;
StartupNotify=true
DESKTOP
cp "$APPDIR/zupt-gui.desktop" "$APPDIR/usr/share/applications/"
cp "$APPDIR/vaptvupt-gui.desktop" "$APPDIR/usr/share/applications/"
# Icon
if [ -f gui/assets/zupt-icon.png ]; then
cp gui/assets/zupt-icon.png "$APPDIR/zupt-gui.png"
cp gui/assets/zupt-icon.png "$APPDIR/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
cp gui/assets/zupt-icon.png "$APPDIR/vaptvupt-gui.png"
cp gui/assets/zupt-icon.png "$APPDIR/usr/share/icons/hicolor/256x256/apps/vaptvupt-gui.png"
else
python3 -c "
import struct, zlib
@ -60,7 +60,7 @@ def png(w, h, color):
return b'\\x89PNG\\r\\n\\x1a\\n' + chunk(b'IHDR', struct.pack('>IIBBBBB', w, h, 8, 2, 0, 0, 0)) + chunk(b'IDAT', zlib.compress(raw)) + chunk(b'IEND', b'')
open('$APPDIR/zupt-gui.png','wb').write(png(256, 256, (88, 92, 215)))
"
cp "$APPDIR/zupt-gui.png" "$APPDIR/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
cp "$APPDIR/vaptvupt-gui.png" "$APPDIR/usr/share/icons/hicolor/256x256/apps/vaptvupt-gui.png"
fi
# AppRun — sets PATH so zupt-gui finds the bundled wrapper, falls
@ -73,7 +73,7 @@ export PATH="$HERE/usr/bin:$PATH"
# Pre-flight check: is python3 available? Is a Qt6 binding installed?
if ! command -v python3 >/dev/null 2>&1; then
cat >&2 <<EOF
zupt-gui: python3 is not installed.
vaptvupt-gui: python3 is not installed.
Install: sudo apt install python3 (Debian/Ubuntu)
sudo dnf install python3 (Fedora/RHEL)
EOF
@ -83,7 +83,7 @@ fi
if ! python3 -c 'import PyQt6.QtWidgets' 2>/dev/null \
&& ! python3 -c 'import PySide6.QtWidgets' 2>/dev/null; then
cat >&2 <<EOF
zupt-gui: needs a Qt6 Python binding (PyQt6 or PySide6).
vaptvupt-gui: needs a Qt6 Python binding (PyQt6 or PySide6).
Install one of:
Debian/Ubuntu: sudo apt install python3-pyqt6
Fedora/RHEL: sudo dnf install python3-pyqt6
@ -92,30 +92,30 @@ EOF
exit 1
fi
if ! command -v zupt >/dev/null 2>&1; then
if ! command -v vaptvupt >/dev/null 2>&1 && ! command -v zupt >/dev/null 2>&1; then
cat >&2 <<EOF
zupt-gui: warning — the 'zupt' CLI was not found in PATH.
Install the zupt package or place the binary in PATH.
vaptvupt-gui: warning — neither 'vaptvupt' nor legacy 'zupt' CLI was found in PATH.
Install the vaptvupt package or place the binary in PATH.
The GUI will start but compress/extract operations will fail.
EOF
fi
exec "$HERE/usr/bin/zupt-gui" "$@"
exec "$HERE/usr/bin/vaptvupt-gui" "$@"
APPRUN
chmod 755 "$APPDIR/AppRun"
# Build AppImage
if command -v appimagetool >/dev/null 2>&1; then
ARCH=x86_64 appimagetool "$APPDIR" "/tmp/Zupt-GUI-$VERSION-x86_64.AppImage" 2>&1 | tail -5
echo "Built: /tmp/Zupt-GUI-$VERSION-x86_64.AppImage"
ARCH=x86_64 appimagetool "$APPDIR" "/tmp/VaptVupt-GUI-$VERSION-x86_64.AppImage" 2>&1 | tail -5
echo "Built: /tmp/VaptVupt-GUI-$VERSION-x86_64.AppImage"
else
cd /tmp
rm -f "Zupt-GUI-$VERSION-x86_64.AppDir.tar.gz"
tar -czf "Zupt-GUI-$VERSION-x86_64.AppDir.tar.gz" zupt-gui.AppDir
rm -f "VaptVupt-GUI-$VERSION-x86_64.AppDir.tar.gz"
tar -czf "VaptVupt-GUI-$VERSION-x86_64.AppDir.tar.gz" vaptvupt-gui.AppDir
cd - >/dev/null
echo "appimagetool unavailable; portable AppDir tarball at:"
echo " /tmp/Zupt-GUI-$VERSION-x86_64.AppDir.tar.gz"
echo "Run via: tar -xzf ... && ./zupt-gui.AppDir/AppRun"
echo " /tmp/VaptVupt-GUI-$VERSION-x86_64.AppDir.tar.gz"
echo "Run via: tar -xzf ... && ./vaptvupt-gui.AppDir/AppRun"
echo "Convert to AppImage on a host with appimagetool:"
echo " ARCH=x86_64 appimagetool zupt-gui.AppDir Zupt-GUI-$VERSION-x86_64.AppImage"
echo " ARCH=x86_64 appimagetool vaptvupt-gui.AppDir VaptVupt-GUI-$VERSION-x86_64.AppImage"
fi

View file

@ -5,39 +5,41 @@
set -e
cd "$(dirname "$0")/.."
VERSION="${VERSION:-1.1.1}"
VERSION="${VERSION:-1.2.0}"
ARCH="all"
PKG="zupt-gui_${VERSION}_${ARCH}"
PKG="vaptvupt-gui_${VERSION}_${ARCH}"
ROOT="/tmp/$PKG"
rm -rf "$ROOT"
mkdir -p "$ROOT/DEBIAN" \
"$ROOT/usr/bin" \
"$ROOT/usr/lib/zupt-gui" \
"$ROOT/usr/lib/vaptvupt-gui" \
"$ROOT/usr/share/applications" \
"$ROOT/usr/share/icons/hicolor/256x256/apps" \
"$ROOT/usr/share/man/man1" \
"$ROOT/usr/share/doc/zupt-gui"
"$ROOT/usr/share/doc/vaptvupt-gui"
# Source files
install -m 644 gui/src/zupt_gui.py "$ROOT/usr/lib/zupt-gui/"
install -m 644 gui/src/zupt_gui.py "$ROOT/usr/lib/vaptvupt-gui/"
# Wrapper script in /usr/bin
cat > "$ROOT/usr/bin/zupt-gui" <<'WRAP'
cat > "$ROOT/usr/bin/vaptvupt-gui" <<'WRAP'
#!/bin/sh
exec python3 /usr/lib/zupt-gui/zupt_gui.py "$@"
exec python3 /usr/lib/vaptvupt-gui/zupt_gui.py "$@"
WRAP
chmod 755 "$ROOT/usr/bin/zupt-gui"
chmod 755 "$ROOT/usr/bin/vaptvupt-gui"
# v3.0.0: legacy zupt-gui symlink
ln -sf vaptvupt-gui "$ROOT/usr/bin/zupt-gui"
# Desktop entry
cat > "$ROOT/usr/share/applications/zupt-gui.desktop" <<'DESKTOP'
cat > "$ROOT/usr/share/applications/vaptvupt-gui.desktop" <<'DESKTOP'
[Desktop Entry]
Type=Application
Name=Zupt GUI
Name=VaptVupt GUI
GenericName=Backup and Compression Utility
Comment=Post-quantum backup with HKDF combiner, key commitment, HPKE binding
Exec=zupt-gui %f
Icon=zupt-gui
Exec=vaptvupt-gui %f
Icon=vaptvupt-gui
Terminal=false
Categories=Utility;Archiving;Compression;Security;
StartupNotify=true
@ -46,14 +48,14 @@ Keywords=archive;compression;encryption;post-quantum;backup;
DESKTOP
# Man page
if [ -f doc/zupt-gui.1 ]; then
install -m 644 doc/zupt-gui.1 "$ROOT/usr/share/man/man1/zupt-gui.1"
gzip -9n "$ROOT/usr/share/man/man1/zupt-gui.1"
if [ -f doc/vaptvupt-gui.1 ]; then
install -m 644 doc/vaptvupt-gui.1 "$ROOT/usr/share/man/man1/vaptvupt-gui.1"
gzip -9n "$ROOT/usr/share/man/man1/vaptvupt-gui.1"
fi
# Icon
if [ -f gui/assets/zupt-icon.png ]; then
cp gui/assets/zupt-icon.png "$ROOT/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
cp gui/assets/zupt-icon.png "$ROOT/usr/share/icons/hicolor/256x256/apps/vaptvupt-gui.png"
else
python3 -c "
import struct, zlib
@ -66,12 +68,12 @@ open('$ROOT/usr/share/icons/hicolor/256x256/apps/zupt-gui.png','wb').write(png(2
fi
# Docs
install -m 644 gui/README.md "$ROOT/usr/share/doc/zupt-gui/" 2>/dev/null || true
gzip -9n -c CHANGELOG.md > "$ROOT/usr/share/doc/zupt-gui/changelog.gz"
install -m 644 gui/README.md "$ROOT/usr/share/doc/vaptvupt-gui/" 2>/dev/null || true
gzip -9n -c CHANGELOG.md > "$ROOT/usr/share/doc/vaptvupt-gui/changelog.gz"
cat > "$ROOT/usr/share/doc/zupt-gui/copyright" <<'COPYRIGHT'
cat > "$ROOT/usr/share/doc/vaptvupt-gui/copyright" <<'COPYRIGHT'
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: zupt-gui
Upstream-Name: vaptvupt-gui
Upstream-Contact: Cristian Cezar Moisés <zupt@riseup.net>
Source: https://git.securityops.co/cristiancmoises/zupt
@ -90,17 +92,20 @@ COPYRIGHT
# Control
INSTALLED_SIZE=$(du -sk "$ROOT" | cut -f1)
cat > "$ROOT/DEBIAN/control" <<EOF
Package: zupt-gui
Package: vaptvupt-gui
Version: $VERSION
Section: utils
Priority: optional
Architecture: $ARCH
Depends: python3 (>= 3.9), python3-pyqt6 | python3-pyside6, zupt (>= 2.2.3)
Depends: python3 (>= 3.9), python3-pyqt6 | python3-pyside6, vaptvupt (>= 3.0.0) | zupt (>= 2.2.3)
Provides: zupt-gui (= ${VERSION})
Replaces: zupt-gui (<< 1.2.0)
Conflicts: zupt-gui (<< 1.2.0)
Maintainer: Cristian Cezar Moisés <zupt@riseup.net>
Installed-Size: $INSTALLED_SIZE
Homepage: https://git.securityops.co/cristiancmoises/zupt
Description: Graphical interface for the Zupt post-quantum backup utility
PySide6/PyQt6 frontend for Zupt. Supports compression, extraction, key
Description: Graphical interface for VaptVupt post-quantum backup utility
PySide6/PyQt6 frontend for VaptVupt (formerly zupt-gui in 1.x). Supports compression, extraction, key
management, and full disk backup/restore. Exposes both legacy --pq
and new --pq-sdk (libzuptsdk: HKDF combiner, key commitment, HPKE
binding, Argon2id) encryption modes.
@ -125,7 +130,7 @@ if ! python3 -c 'import PyQt6.QtWidgets' 2>/dev/null \
cat << 'MSG'
──────────────────────────────────────────────────────────────────────
zupt-gui installed, but no Qt6 Python binding is available.
vaptvupt-gui installed, but no Qt6 Python binding is available.
Install one of the following to enable the GUI:
@ -134,20 +139,20 @@ Install one of the following to enable the GUI:
Arch/Manjaro: sudo pacman -S python-pyqt6
pip (any distro): pip install --user PySide6
After installing the binding, launch with: zupt-gui
After installing the binding, launch with: vaptvupt-gui
──────────────────────────────────────────────────────────────────────
MSG
fi
# Same friendly warning if zupt CLI not installed.
if ! command -v zupt >/dev/null 2>&1; then
if ! command -v vaptvupt >/dev/null 2>&1 && ! command -v zupt >/dev/null 2>&1; then
cat << 'MSG'
──────────────────────────────────────────────────────────────────────
zupt-gui needs the 'zupt' CLI to function. Install it:
vaptvupt-gui needs the 'vaptvupt' CLI to function. Install it:
Debian/Ubuntu/Mint: sudo dpkg -i zupt_2.2.3_amd64.deb
Debian/Ubuntu/Mint: sudo dpkg -i vaptvupt_3.0.0_amd64.deb
(followed by: sudo apt --fix-broken install)
──────────────────────────────────────────────────────────────────────

View file

@ -5,37 +5,40 @@
set -e
cd "$(dirname "$0")/.."
VERSION="${VERSION:-1.1.1}"
RPMROOT="/tmp/rpmbuild-zupt-gui"
VERSION="${VERSION:-1.2.0}"
RPMROOT="/tmp/rpmbuild-vaptvupt-gui"
rm -rf "$RPMROOT"
mkdir -p "$RPMROOT"/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
TMP="/tmp/zupt-gui-$VERSION"
TMP="/tmp/vaptvupt-gui-$VERSION"
rm -rf "$TMP" && mkdir -p "$TMP/src" "$TMP/doc" "$TMP/assets"
cp gui/src/zupt_gui.py "$TMP/src/"
cp doc/zupt-gui.1 "$TMP/doc/" 2>/dev/null || true
cp doc/vaptvupt-gui.1 "$TMP/doc/" 2>/dev/null || true
cp gui/README.md "$TMP/" 2>/dev/null || true
cp LICENSE "$TMP/" 2>/dev/null || true
[ -f gui/assets/zupt-icon.png ] && cp gui/assets/zupt-icon.png "$TMP/assets/"
tar -czf "$RPMROOT/SOURCES/zupt-gui-$VERSION.tar.gz" -C /tmp "zupt-gui-$VERSION"
tar -czf "$RPMROOT/SOURCES/vaptvupt-gui-$VERSION.tar.gz" -C /tmp "vaptvupt-gui-$VERSION"
cat > "$RPMROOT/SPECS/zupt-gui.spec" <<EOF
Name: zupt-gui
cat > "$RPMROOT/SPECS/vaptvupt-gui.spec" <<EOF
Name: vaptvupt-gui
Version: $VERSION
Release: 1%{?dist}
Summary: Graphical interface for the Zupt post-quantum backup utility
Summary: Graphical interface for VaptVupt post-quantum backup utility (formerly zupt-gui)
License: AGPL-3.0-or-later
URL: https://git.securityops.co/cristiancmoises/zupt
Source0: zupt-gui-%{version}.tar.gz
Source0: vaptvupt-gui-%{version}.tar.gz
BuildArch: noarch
BuildRequires: python3 >= 3.9
Requires: python3 >= 3.9
Requires: (python3-qt6 or python3-pyside6 or python3-pyqt6)
Requires: zupt >= 2.2.3
Requires: (vaptvupt >= 3.0.0 or zupt >= 2.2.3)
Provides: zupt-gui = %{version}-%{release}
Obsoletes: zupt-gui < 1.2.0
Conflicts: zupt-gui < 1.2.0
%description
PySide6/PyQt6 frontend for Zupt. Supports compression, extraction, key
PySide6/PyQt6 frontend for VaptVupt (renamed from zupt-gui in 1.x). Supports compression, extraction, key
management, and full disk backup/restore. Exposes both legacy --pq and
new --pq-sdk (libzuptsdk: HKDF combiner, key commitment, HPKE binding,
Argon2id) encryption modes. Auto-detects whichever Qt6 binding is
@ -49,44 +52,46 @@ installed at startup.
%install
mkdir -p %{buildroot}%{_bindir}
mkdir -p %{buildroot}%{_libdir}/zupt-gui
mkdir -p %{buildroot}%{_libdir}/vaptvupt-gui
mkdir -p %{buildroot}%{_datadir}/applications
mkdir -p %{buildroot}%{_datadir}/icons/hicolor/256x256/apps
mkdir -p %{buildroot}%{_mandir}/man1
install -m 644 src/zupt_gui.py %{buildroot}%{_libdir}/zupt-gui/
install -m 644 src/zupt_gui.py %{buildroot}%{_libdir}/vaptvupt-gui/
cat > %{buildroot}%{_bindir}/zupt-gui <<'WRAP'
cat > %{buildroot}%{_bindir}/vaptvupt-gui <<'WRAP'
#!/bin/sh
exec python3 %{_libdir}/zupt-gui/zupt_gui.py "\$@"
exec python3 %{_libdir}/vaptvupt-gui/zupt_gui.py "\$@"
WRAP
chmod 755 %{buildroot}%{_bindir}/zupt-gui
chmod 755 %{buildroot}%{_bindir}/vaptvupt-gui
# v3.0.0: legacy zupt-gui symlink for one major version cycle
ln -sf vaptvupt-gui %{buildroot}%{_bindir}/zupt-gui
cat > %{buildroot}%{_datadir}/applications/zupt-gui.desktop <<'DESKTOP'
cat > %{buildroot}%{_datadir}/applications/vaptvupt-gui.desktop <<'DESKTOP'
[Desktop Entry]
Type=Application
Name=Zupt GUI
Name=VaptVupt GUI
GenericName=Backup and Compression Utility
Comment=Post-quantum backup with HKDF combiner, key commitment, HPKE binding
Exec=zupt-gui %f
Icon=zupt-gui
Exec=vaptvupt-gui %f
Icon=vaptvupt-gui
Terminal=false
Categories=Utility;Archiving;Compression;Security;
StartupNotify=true
DESKTOP
[ -f doc/zupt-gui.1 ] && install -m 644 doc/zupt-gui.1 %{buildroot}%{_mandir}/man1/
[ -f assets/zupt-icon.png ] && install -m 644 assets/zupt-icon.png %{buildroot}%{_datadir}/icons/hicolor/256x256/apps/zupt-gui.png || true
[ -f doc/vaptvupt-gui.1 ] && install -m 644 doc/vaptvupt-gui.1 %{buildroot}%{_mandir}/man1/
[ -f assets/zupt-icon.png ] && install -m 644 assets/zupt-icon.png %{buildroot}%{_datadir}/icons/hicolor/256x256/apps/vaptvupt-gui.png || true
# Generate placeholder icon if no real one exists
if [ ! -f %{buildroot}%{_datadir}/icons/hicolor/256x256/apps/zupt-gui.png ]; then
if [ ! -f %{buildroot}%{_datadir}/icons/hicolor/256x256/apps/vaptvupt-gui.png ]; then
python3 -c "
import struct, zlib
def png(w, h, color):
raw = b''.join(b'\\0' + bytes(color) * w for _ in range(h))
def chunk(t, d): return struct.pack('>I', len(d)) + t + d + struct.pack('>I', zlib.crc32(t+d) & 0xffffffff)
return b'\\x89PNG\\r\\n\\x1a\\n' + chunk(b'IHDR', struct.pack('>IIBBBBB', w, h, 8, 2, 0, 0, 0)) + chunk(b'IDAT', zlib.compress(raw)) + chunk(b'IEND', b'')
open('%{buildroot}%{_datadir}/icons/hicolor/256x256/apps/zupt-gui.png','wb').write(png(256, 256, (88, 92, 215)))
open('%{buildroot}%{_datadir}/icons/hicolor/256x256/apps/vaptvupt-gui.png','wb').write(png(256, 256, (88, 92, 215)))
"
fi
@ -108,21 +113,35 @@ fi
%files
%doc README.md
%license LICENSE
%{_bindir}/vaptvupt-gui
%{_bindir}/zupt-gui
%{_libdir}/zupt-gui/zupt_gui.py
%{_datadir}/applications/zupt-gui.desktop
%{_datadir}/icons/hicolor/256x256/apps/zupt-gui.png
%{_mandir}/man1/zupt-gui.1*
%{_libdir}/vaptvupt-gui/zupt_gui.py
%{_datadir}/applications/vaptvupt-gui.desktop
%{_datadir}/icons/hicolor/256x256/apps/vaptvupt-gui.png
%changelog
* Mon Apr 27 2026 Cristian Cezar Moisés <zupt@riseup.net> - $VERSION-1
* Sun May 25 2026 Cristian Cezar Moisés <zupt@riseup.net> - $VERSION-1
- v1.2.0: package renamed zupt-gui → vaptvupt-gui (parent CLI also
renamed; INPI Brasil trademark on "Zupt"). Legacy /usr/bin/zupt-gui
symlink preserved. GUI binary-discovery bug fix: _find_vaptvupt
with liveness check + discovery log via VAPTVUPT_DEBUG=1.
* Mon Apr 27 2026 Cristian Cezar Moisés <zupt@riseup.net> - 1.1.1-1
- Cross-binding (PySide6 OR PyQt6 auto-detected)
- SDK v2 mode toggles in compress/extract/keygen tabs
- Man page added
EOF
if command -v rpmbuild >/dev/null 2>&1; then
rpmbuild --define "_topdir $RPMROOT" -bb "$RPMROOT/SPECS/zupt-gui.spec" 2>&1 | tail -3
# On Debian/Ubuntu, the host's `rpm` doesn't see `python3` as an RPM
# (it's a deb), so the BuildRequires check would fail. Use --nodeps
# since the runtime check on the target system is what actually
# matters. The Requires: lines still apply on install.
rpmbuild --define "_topdir $RPMROOT" --nodeps -bb "$RPMROOT/SPECS/vaptvupt-gui.spec" 2>&1 | tail -3
if [ -f "$RPMROOT/RPMS/noarch/vaptvupt-gui-${VERSION}-1.noarch.rpm" ]; then
cp "$RPMROOT/RPMS/noarch/vaptvupt-gui-${VERSION}-1.noarch.rpm" \
"/tmp/vaptvupt-gui-${VERSION}-1.noarch.rpm"
echo "Built: /tmp/vaptvupt-gui-${VERSION}-1.noarch.rpm"
fi
cp "$RPMROOT/RPMS/noarch/zupt-gui-$VERSION-1."*.rpm /tmp/ 2>/dev/null || true
ls /tmp/zupt-gui-$VERSION-*.rpm 2>/dev/null
else

View file

@ -1,14 +1,20 @@
#!/bin/bash
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2025-2026 Cristian Cezar Moisés
# Build self-contained zupt RPM. Bundles libzuptsdk.so.2 under
# /usr/lib/zupt/ so users do NOT need a separate libzuptsdk package.
#
# Build self-contained vaptvupt RPM (formerly zupt). Bundles
# libzuptsdk.so.2 under /usr/lib/vaptvupt/ so users do NOT need
# a separate libzuptsdk package. Installs a legacy /usr/bin/zupt
# symlink for one major version cycle.
set -e
cd "$(dirname "$0")/.."
VERSION="${VERSION:-2.2.3}"
VERSION="${VERSION:-3.0.0}"
ARCH="${ARCH:-x86_64}"
RELEASE="1"
PKGNAME="vaptvupt"
LEGACY="zupt"
SDK_LIB="vendor/zuptsdk/libzuptsdk.so.2.0.0"
if [ ! -f "$SDK_LIB" ]; then
@ -16,52 +22,54 @@ if [ ! -f "$SDK_LIB" ]; then
exit 1
fi
# Build zupt and patch RPATH to /usr/lib/zupt
echo "[rpm] Building zupt"
echo "[rpm] Building $PKGNAME"
make clean >/dev/null 2>&1 || true
make -j"$(nproc)" >/dev/null
echo "[rpm] Patching rpath -> /usr/lib/zupt:/usr/lib64/zupt"
patchelf --set-rpath '/usr/lib/zupt:/usr/lib64/zupt' zupt
if ! readelf -d zupt | grep -q "RUNPATH.*\[/usr/lib/zupt:/usr/lib64/zupt\]"; then
echo "ERROR: zupt does not have correct RUNPATH" >&2
echo "[rpm] Patching rpath -> /usr/lib/$PKGNAME:/usr/lib64/$PKGNAME"
patchelf --set-rpath "/usr/lib/$PKGNAME:/usr/lib64/$PKGNAME" $PKGNAME
if ! readelf -d $PKGNAME | grep -q "RUNPATH.*\[/usr/lib/$PKGNAME:/usr/lib64/$PKGNAME\]"; then
echo "ERROR: $PKGNAME does not have correct RUNPATH" >&2
exit 1
fi
if ! command -v rpmbuild >/dev/null 2>&1; then
echo "[rpm] rpmbuild not found; falling back to packaging/build-rpm-manual.py"
if [ ! -d "/tmp/zupt_${VERSION}_amd64" ]; then
echo "[rpm] /tmp/zupt_${VERSION}_amd64 missing; running build-deb.sh first"
bash packaging/build-deb.sh >/dev/null
fi
VERSION="$VERSION" python3 packaging/build-rpm-manual.py
exit 0
echo "[rpm] rpmbuild not found; install rpm package to proceed"
exit 1
fi
# Stage the source tarball that the spec's %install will unpack
RPMROOT="/tmp/rpmbuild-zupt"
RPMROOT="/tmp/rpmbuild-$PKGNAME"
rm -rf "$RPMROOT"
mkdir -p "$RPMROOT"/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
STAGE="/tmp/zupt-rpm-stage-${VERSION}"
STAGE="/tmp/$PKGNAME-rpm-stage-${VERSION}"
rm -rf "$STAGE"
mkdir -p "$STAGE/zupt-${VERSION}"
mkdir -p "$STAGE/$PKGNAME-${VERSION}/completions"
cp zupt "$STAGE/zupt-${VERSION}/zupt"
cp "$SDK_LIB" "$STAGE/zupt-${VERSION}/libzuptsdk.so.2.0.0"
cp README.md CHANGELOG.md SECURITY.md AUDIT.md LICENSE "$STAGE/zupt-${VERSION}/"
[ -f doc/zupt.1 ] && cp doc/zupt.1 "$STAGE/zupt-${VERSION}/zupt.1"
tar -czf "$RPMROOT/SOURCES/zupt-${VERSION}.tar.gz" -C "$STAGE" "zupt-${VERSION}"
cp $PKGNAME "$STAGE/$PKGNAME-${VERSION}/$PKGNAME"
cp "$SDK_LIB" "$STAGE/$PKGNAME-${VERSION}/libzuptsdk.so.2.0.0"
cp "vendor/pqvaptvupt/libpqvaptvupt.so.0.6.0" "$STAGE/$PKGNAME-${VERSION}/libpqvaptvupt.so.0.6.0"
cp README.md CHANGELOG.md SECURITY.md AUDIT.md LICENSE "$STAGE/$PKGNAME-${VERSION}/"
[ -f doc/vaptvupt.1 ] && cp doc/vaptvupt.1 "$STAGE/$PKGNAME-${VERSION}/$PKGNAME.1"
[ -f completions/vaptvupt.bash ] && cp completions/vaptvupt.bash "$STAGE/$PKGNAME-${VERSION}/completions/"
[ -f completions/_vaptvupt ] && cp completions/_vaptvupt "$STAGE/$PKGNAME-${VERSION}/completions/"
[ -f completions/vaptvupt.fish ] && cp completions/vaptvupt.fish "$STAGE/$PKGNAME-${VERSION}/completions/"
tar -czf "$RPMROOT/SOURCES/$PKGNAME-${VERSION}.tar.gz" -C "$STAGE" "$PKGNAME-${VERSION}"
cat > "$RPMROOT/SPECS/zupt.spec" <<EOF
Name: zupt
cat > "$RPMROOT/SPECS/$PKGNAME.spec" <<EOF
Name: $PKGNAME
Version: $VERSION
Release: ${RELEASE}%{?dist}
Summary: Post-quantum backup compression utility
Summary: Post-quantum backup compression utility (formerly zupt)
License: AGPL-3.0-or-later AND GPL-3.0-or-later
URL: https://git.securityops.co/cristiancmoises/zupt
Source0: zupt-%{version}.tar.gz
Source0: $PKGNAME-%{version}.tar.gz
# v3.0.0 rename — INPI Brasil trademark on the prior name "Zupt".
# Cleanly supersede legacy 'zupt' RPMs.
Provides: $LEGACY = %{version}-%{release}
Obsoletes: $LEGACY < 3.0.0
Conflicts: $LEGACY < 3.0.0
# libzuptsdk is bundled under /usr/lib/zupt; no external sdk dep needed.
Requires: libargon2
Requires: openssl-libs >= 3.0
AutoReqProv: no
@ -71,13 +79,18 @@ AutoReqProv: no
%global _build_id_links none
%description
Backup-oriented compression utility with hybrid post-quantum encryption
(ML-KEM-768 + X25519). Provides AES-256-CTR + HMAC-SHA256 authenticated
encryption, multi-threaded compression, full-disk backup/restore,
block-level deduplication, and embeds the VaptVupt 2.48.2 codec for
high-throughput LZ77 + tANS compression with AVX2 and NEON SIMD
acceleration. The libzuptsdk shared library is bundled under
/usr/lib/zupt -- no separate package required.
VaptVupt (renamed from Zupt in v3.0.0 due to INPI Brasil trademark
on the prior name) is a backup-oriented compression utility with
hybrid post-quantum encryption (ML-KEM-768 + X25519). Provides
AES-256-CTR + HMAC-SHA256 authenticated encryption, multi-threaded
compression, full-disk backup/restore, block-level deduplication,
and embeds the VaptVupt 2.48.5 LZ + ANS codec with AVX2 and NEON
SIMD acceleration. The libzuptsdk shared library is bundled under
/usr/lib/$PKGNAME -- no separate package required.
The on-disk archive extension is unchanged (.zupt); v2.x and v3.0.0
archives are bidirectionally compatible. The legacy /usr/bin/zupt
symlink is preserved for one major version cycle.
%prep
%setup -q
@ -87,58 +100,96 @@ acceleration. The libzuptsdk shared library is bundled under
%install
mkdir -p %{buildroot}%{_bindir}
mkdir -p %{buildroot}%{_libdir}/zupt
mkdir -p %{buildroot}%{_docdir}/zupt
mkdir -p %{buildroot}%{_licensedir}/zupt
mkdir -p %{buildroot}%{_libdir}/$PKGNAME
mkdir -p %{buildroot}%{_docdir}/$PKGNAME
mkdir -p %{buildroot}%{_licensedir}/$PKGNAME
mkdir -p %{buildroot}%{_mandir}/man1
mkdir -p %{buildroot}%{_datadir}/bash-completion/completions
mkdir -p %{buildroot}%{_datadir}/zsh/site-functions
mkdir -p %{buildroot}%{_datadir}/fish/vendor_completions.d
install -m 755 zupt %{buildroot}%{_bindir}/zupt
install -m 755 libzuptsdk.so.2.0.0 %{buildroot}%{_libdir}/zupt/libzuptsdk.so.2.0.0
ln -sf libzuptsdk.so.2.0.0 %{buildroot}%{_libdir}/zupt/libzuptsdk.so.2
ln -sf libzuptsdk.so.2.0.0 %{buildroot}%{_libdir}/zupt/libzuptsdk.so
install -m 755 $PKGNAME %{buildroot}%{_bindir}/$PKGNAME
ln -sf $PKGNAME %{buildroot}%{_bindir}/$LEGACY
install -m 644 README.md CHANGELOG.md SECURITY.md AUDIT.md %{buildroot}%{_docdir}/zupt/
install -m 644 LICENSE %{buildroot}%{_licensedir}/zupt/
install -m 755 libzuptsdk.so.2.0.0 %{buildroot}%{_libdir}/$PKGNAME/libzuptsdk.so.2.0.0
ln -sf libzuptsdk.so.2.0.0 %{buildroot}%{_libdir}/$PKGNAME/libzuptsdk.so.2
ln -sf libzuptsdk.so.2.0.0 %{buildroot}%{_libdir}/$PKGNAME/libzuptsdk.so
install -m 755 libpqvaptvupt.so.0.6.0 %{buildroot}%{_libdir}/$PKGNAME/libpqvaptvupt.so.0.6.0
ln -sf libpqvaptvupt.so.0.6.0 %{buildroot}%{_libdir}/$PKGNAME/libpqvaptvupt.so.0
ln -sf libpqvaptvupt.so.0.6.0 %{buildroot}%{_libdir}/$PKGNAME/libpqvaptvupt.so
if [ -f zupt.1 ]; then
install -m 644 zupt.1 %{buildroot}%{_mandir}/man1/zupt.1
gzip -9n %{buildroot}%{_mandir}/man1/zupt.1
install -m 644 README.md CHANGELOG.md SECURITY.md AUDIT.md %{buildroot}%{_docdir}/$PKGNAME/
install -m 644 LICENSE %{buildroot}%{_licensedir}/$PKGNAME/
if [ -f $PKGNAME.1 ]; then
install -m 644 $PKGNAME.1 %{buildroot}%{_mandir}/man1/$PKGNAME.1
gzip -9n %{buildroot}%{_mandir}/man1/$PKGNAME.1
ln -sf $PKGNAME.1.gz %{buildroot}%{_mandir}/man1/$LEGACY.1.gz
fi
if [ -f completions/vaptvupt.bash ]; then
install -m 644 completions/vaptvupt.bash %{buildroot}%{_datadir}/bash-completion/completions/$PKGNAME
ln -sf $PKGNAME %{buildroot}%{_datadir}/bash-completion/completions/$LEGACY
fi
if [ -f completions/_vaptvupt ]; then
install -m 644 completions/_vaptvupt %{buildroot}%{_datadir}/zsh/site-functions/_$PKGNAME
ln -sf _$PKGNAME %{buildroot}%{_datadir}/zsh/site-functions/_$LEGACY
fi
if [ -f completions/vaptvupt.fish ]; then
install -m 644 completions/vaptvupt.fish %{buildroot}%{_datadir}/fish/vendor_completions.d/$PKGNAME.fish
fi
%files
%license %{_licensedir}/zupt/LICENSE
%doc %{_docdir}/zupt/README.md
%doc %{_docdir}/zupt/CHANGELOG.md
%doc %{_docdir}/zupt/SECURITY.md
%doc %{_docdir}/zupt/AUDIT.md
%{_bindir}/zupt
%dir %{_libdir}/zupt
%{_libdir}/zupt/libzuptsdk.so
%{_libdir}/zupt/libzuptsdk.so.2
%{_libdir}/zupt/libzuptsdk.so.2.0.0
%{_mandir}/man1/zupt.1.gz
%license %{_licensedir}/$PKGNAME/LICENSE
%doc %{_docdir}/$PKGNAME/README.md
%doc %{_docdir}/$PKGNAME/CHANGELOG.md
%doc %{_docdir}/$PKGNAME/SECURITY.md
%doc %{_docdir}/$PKGNAME/AUDIT.md
%{_bindir}/$PKGNAME
%{_bindir}/$LEGACY
%dir %{_libdir}/$PKGNAME
%{_libdir}/$PKGNAME/libzuptsdk.so
%{_libdir}/$PKGNAME/libzuptsdk.so.2
%{_libdir}/$PKGNAME/libzuptsdk.so.2.0.0
%{_libdir}/$PKGNAME/libpqvaptvupt.so
%{_libdir}/$PKGNAME/libpqvaptvupt.so.0
%{_libdir}/$PKGNAME/libpqvaptvupt.so.0.6.0
%{_mandir}/man1/$PKGNAME.1.gz
%{_mandir}/man1/$LEGACY.1.gz
%{_datadir}/bash-completion/completions/$PKGNAME
%{_datadir}/bash-completion/completions/$LEGACY
%{_datadir}/zsh/site-functions/_$PKGNAME
%{_datadir}/zsh/site-functions/_$LEGACY
%{_datadir}/fish/vendor_completions.d/$PKGNAME.fish
%changelog
* Sat May 02 2026 Cristian Cezar Moises <zupt@riseup.net> - $VERSION-$RELEASE
- VaptVupt 2.48.2 codec integration (cost-aware lazy parser, format_v2
flag, 4-stream Huffman literal coding, encoder memory hygiene).
- Wrapper defaults: checksum=0 (Zupt outer MAC authenticates),
format_v2=1 for BALANCED/EXTREME (defensive guard against the
upstream-untested format_v2 + ULTRA_FAST combination).
- Makefile arch-detection bug fixed (x86-64 / x86_64 mismatch).
- 22/22 regression tests, 14/14 threaded, 10/10 PQ, 11/11 VaptVupt,
13/13 NIST vectors. ASAN clean across plain/password/PQ-SDK at
levels 1, 5, 9.
* Sun May 25 2026 Cristian Cezar Moises <zupt@riseup.net> - $VERSION-$RELEASE
- v3.0.0: Renamed from "Zupt" to "VaptVupt" because of a prior INPI
Brasil trademark on "Zupt". Archive extension .zupt is preserved;
v2.x and v3.0.0 archives are bidirectionally compatible. Legacy
/usr/bin/zupt is installed as a symlink to /usr/bin/vaptvupt.
- Integrated VaptVupt LZ + ANS codec 2.48.5: fixes csz==0 heap-
buffer-overflow READ in vv_dstream_decompress_chunk (libFuzzer-
found, medium severity), UBSan-safe pointer arithmetic in
vv_copy_match.
- Enhanced manpage (597 lines, was 422): POST-QUANTUM ENCRYPTION,
PERFORMANCE table, SECURITY/threat-model, ENVIRONMENT and
EXIT STATUS sections.
- Fixed GUI binary-discovery bug (PATH-missing-/usr/bin scenario);
GUI now does liveness check + logs discovery to stderr with
VAPTVUPT_DEBUG=1.
- 91/91 distro-safe regression suite green; F-09 byte sweep
0/1827 silent accepts; F-06 HMAC fuzz 0/2000 silent accepts.
EOF
rpmbuild --define "_topdir $RPMROOT" \
--define "_binary_payload w2.gzdio" \
-bb "$RPMROOT/SPECS/zupt.spec" 2>&1 | tail -8
-bb "$RPMROOT/SPECS/$PKGNAME.spec" 2>&1 | tail -5
RPM_PATH=$(find "$RPMROOT/RPMS" -name "zupt-${VERSION}-*.rpm" | head -1)
RPM_PATH=$(find "$RPMROOT/RPMS" -name "$PKGNAME-${VERSION}-*.rpm" | head -1)
if [ -n "$RPM_PATH" ]; then
cp "$RPM_PATH" "/tmp/zupt-${VERSION}-${RELEASE}.${ARCH}.rpm"
cp "$RPM_PATH" "/tmp/$PKGNAME-${VERSION}-${RELEASE}.${ARCH}.rpm"
echo ""
echo "Built: /tmp/zupt-${VERSION}-${RELEASE}.${ARCH}.rpm ($(du -h /tmp/zupt-${VERSION}-${RELEASE}.${ARCH}.rpm | cut -f1))"
rpm -qpi "/tmp/zupt-${VERSION}-${RELEASE}.${ARCH}.rpm" 2>&1 | head -15
echo "Built: /tmp/$PKGNAME-${VERSION}-${RELEASE}.${ARCH}.rpm ($(du -h "/tmp/$PKGNAME-${VERSION}-${RELEASE}.${ARCH}.rpm" | cut -f1))"
rpm -qpi "/tmp/$PKGNAME-${VERSION}-${RELEASE}.${ARCH}.rpm" 2>&1 | head -15
fi

353
packaging/debian/changelog Normal file
View file

@ -0,0 +1,353 @@
vaptvupt (4.0.0-1) UNRELEASED; urgency=high
* Codec upgraded to canonical VaptVupt 2.60.4 (security release):
fixes a high-severity OOB heap write in the AVX2 decode fast path
on exact-content_size buffers; brings CBMC-verified BCJ filters
with automatic ELF/PE/Mach-O detection. Ratio gate verified
byte-identical on identical inputs. New regression suite: 80
exact-size decode cases under ASan + BCJ roundtrips.
* F-16 (data loss, pre-existing, fixed): archives created by <= 3.8.0
at -l 8/-l 9 whose inputs included executables may be undecodable
by any version (write-time defect in the old divergent BCJ
encoder). Re-create such archives with 4.0.0 and verify extraction
before deleting sources. Readers <= 3.8.0 cannot open new archives
where the auto-filter fired (L3+ on executables).
* New --pq-box recipient encryption (envelope 0x05) via vendored
libpqvaptvupt 0.6.0: ML-KEM-768 + X25519 combined through
HKDF-SHA256 with domain separation; magic-tagged keypair files;
13/13 adversarial checks; ASan/UBSan clean. keygen --box generates
keypairs. Legacy --pq and --pq-sdk unchanged and re-verified.
* SHA-NI measured on capable silicon: SHA-256 5.8x over scalar
(204 -> 1184 MB/s); the v3.2.0 [ESTIMATED] label is retired.
Encrypted per-block throughput ~2x the 3.8.0-era figure.
* Toolchain: clang strict build restored (Jasmin .s assembled with
as(1)); vendored codec under explicit upstream warning policy;
test-asan link fixed (vv_bcj.c); codec license comment corrected
to GPL-3.0-or-later.
* Wire format v1.6 unchanged; 8-mode back-compat matrix byte-exact.
26 test suites green; NIST/RFC vectors 16/16.
-- Cristian Cezar Moisés <sac@securityops.co> Wed, 10 Jun 2026 12:00:00 +0000
vaptvupt (3.8.0-1) UNRELEASED; urgency=medium
* Documentation-only release. No source, crypto, or wire-format change
(format v1.6); the binary behaves identically to 3.7.0.
* Add BENCHMARKS.md: a consolidated, reproducible, measured benchmark
set with the test machine and method stated for every table —
compression ratio + encode/decode throughput at level 9 across the
5-fixture suite; encode-speed-vs-level trade-off; encryption overhead
separating the one-time KDF (Argon2id ~741 ms, PBKDF2 ~1562 ms on the
test box) from per-block crypto (~147 MB/s) and plain throughput
(~944 MB/s single-threaded); and a head-to-head ratio comparison
against zstd-3/zstd-19 that plainly shows where VaptVupt loses.
* The SHA-NI speedup is explicitly marked [ESTIMATED] because the test
box has no SHA-NI. 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 had never been consolidated.
* README benchmark section re-dated v3.1.0 -> v3.8.0 and linked to
BENCHMARKS.md. Test surface unchanged: test_vectors 16/0, F-09
0/1827, F-06 0/2000.
-- Cristian Cezar Moisés <sac@securityops.co> Sun, 01 Jun 2026 17:30:00 +0000
vaptvupt (3.7.0-1) UNRELEASED; urgency=medium
* Route the ML-KEM-768 decapsulation implicit-rejection comparison
through the single audited constant-time primitive zupt_ct_memeq
(introduced in 3.5.0 for the MAC tag compare), replacing an inline
byte-OR loop over the 1088-byte ciphertext. A timing leak in this
comparison is a KEM decapsulation oracle (distinguishing valid from
invalid ciphertexts), which would break IND-CCA2 security; it is now
the same measured-constant-time code path as the MAC compare. This
was the last security-critical comparison still using a bespoke
inline loop.
* ML-KEM output semantics are unchanged: zupt_ct_memeq returns equality
and the implicit-rejection fail bit is derived as (1 - equal), so a
matching ciphertext yields the success shared secret and a mismatched
one yields the pseudorandom rejection key, exactly as before.
Verified by the FIPS 203 roundtrip (5 trials), the implicit-rejection
vector, PQ-hybrid roundtrip, and wrong-key rejection.
* Extend tests/test_ct_timing to cover the 1088-byte comparison and add
a source-routing guard that fails if the decaps compare stops using
zupt_ct_memeq or a raw 1088-byte inline loop reappears. The 1088-byte
dudect numbers are reported as INFORMATIONAL, not pass/fail: at that
size on a shared vCPU the signal is dominated by memory effects and
plain memcmp is no longer a cleanly-leaking control, so the
environment-relative ratio that is meaningful at 32 bytes does not
transfer. Constant-timeness of the 1088-byte compare instead follows
rigorously from the 32-byte pass plus zupt_ct_memeq being
length-independent by construction (OR-accumulate, no early exit, no
data-dependent branch) plus the source-routing guard.
* No cryptographic-correctness change, no wire-format change (v1.6).
test_vectors 16/0; F-09 byte sweep 0/1827; F-06 HMAC fuzz 0/2000.
-- Cristian Cezar Moisés <sac@securityops.co> Sun, 01 Jun 2026 16:30:00 +0000
vaptvupt (3.6.0-1) UNRELEASED; urgency=medium
* Add NIST SP 800-38A AES-256-CTR known-answer vectors (F.5.5 encrypt,
F.5.6 decrypt) to the test_vectors suite. AES is the bulk cipher but
was previously only tested indirectly via roundtrips; it now has a
standards KAT that validates zupt_aes256_ctr on BOTH the Jasmin
AES-NI path (zupt_aes256_ctr4 + zupt_aes256_blk, x86_64) and the C
T-table fallback. Confirms the Jasmin AES is correct against the
standard (closing the stale "stack-offset" concern). userPreferences
list SP 800-38A as a required vector; this closes that gap.
* Fix an inverted result check in the ML-KEM-768 self-test reporting:
zupt_mlkem768_selftest() returns 0 on success / -1 on failure, but
test_vectors checked `if (ok)` and so printed "OK" precisely when the
self-test FAILED (and would have printed FAIL on success). The check
is now `if (rc == 0)`. The test had been passing vacuously.
* Fix the ML-KEM-768 NTT roundtrip self-test itself. It asserted
ntt∘inv_ntt == identity, which is false for this pqcrystals/Kyber
Montgomery convention (forward ntt divides by R without a prior
to-Montgomery map, so the roundtrip recovers each coefficient scaled
by a fixed constant R^-1 mod q). The self-test now verifies the real
invariant — a CONSISTENT linear scaling across all 256 coefficients —
which still catches genuine NTT bugs (wrong zeta/index) while no
longer emitting a misleading "NTT roundtrip FAILED" line on stderr.
ML-KEM correctness end-to-end was never affected: the K-PKE and KEM
roundtrips and the FIPS 203 roundtrip vectors all pass.
* test_vectors now reports 16 passed, 0 failed (was 14, one vacuous).
No source-crypto behaviour change, no wire-format change (v1.6).
F-09 byte sweep 0/1827, F-06 HMAC fuzz 0/2000.
-- Cristian Cezar Moisés <sac@securityops.co> Sun, 01 Jun 2026 15:30:00 +0000
vaptvupt (3.5.0-1) UNRELEASED; urgency=medium
* Measured constant-time MAC comparison (dudect-style). The MAC tag
compare — the most timing-sensitive operation, where a leak is a
forgery oracle — was previously implemented as three duplicated
inline byte-OR loops marked /* CT-REQUIRED */ but never measured.
Consolidated into a single audited primitive zupt_ct_memeq() (OR-
accumulate, no early exit, volatile sink so the optimiser cannot
reintroduce a branch), used by the v1.6 strict decrypt path and the
F-08 archive-integrity-trailer check.
* New dudect-style timing test tests/test_ct_timing.{c,sh}: Welch's
t-test over fixed-equal vs random-differing tag classes, built at
-O2 (the shipped optimisation level). Verdict is environment-
relative — zupt_ct_memeq's data-dependent timing signal must be a
small fraction (<=20%) of leaky memcmp measured in the same
environment; it lands near 1%. A positive control (memcmp) confirms
the harness can detect a real leak; if the host is too coarse the
test reports INCONCLUSIVE rather than passing vacuously. Wired into
make check and make test.
* Pure internal hardening: turns an asserted constant-time property
into a measured one and a regression guard (a future early-return
refactor fails the t-test). No cryptographic-correctness change, no
wire-format change (v1.6). F-09 byte sweep 0/1827, F-06 HMAC fuzz
0/2000. The formally-verified Jasmin zupt_mac_verify_ct path for the
v1.4/v1.5 legacy compare is unchanged.
-- Cristian Cezar Moisés <sac@securityops.co> Sun, 01 Jun 2026 14:30:00 +0000
vaptvupt (3.4.0-1) UNRELEASED; urgency=medium
* F-15: Argon2id KDF parameter transparency. The 0x04 Argon2id
enc-header previously recorded only [type|salt|nonce] and nothing
about the KDF cost, unlike the PBKDF2 header which records its
iteration count — a latent robustness problem for a long-lived
archive format (if the Argon2id preset ever changed, old archives
could become silently undecryptable). New archives append a one-byte
KDF profile descriptor at offset 33 (ZUPT_ARGON2_PROFILE_MODERATE),
making the header self-describing. The descriptor is covered by the
F-08 archive-integrity trailer, so it cannot be stripped or forged
without failing authentication.
* Back-compatible (additive): the legacy reader checks enc_hdr_len>=33
and reads fixed offsets, so it ignores the trailing byte; existing
33-byte Argon2id archives decrypt unchanged. New readers validate the
profile and refuse an unknown value (fail-closed) rather than
guessing a derivation. Verified byte-exact on pre-3.4.0 encrypted
archives.
* New regression test tests/test_kdf_transparency.{c,sh} (5 checks),
including a build-time KDF cost-floor + determinism guard that fails
if the vendored SDK is swapped for a non-memory-hard stand-in. Wired
into make check and make test.
* No cryptographic-correctness change, no wire-format change (v1.6);
F-09 byte sweep 0/1827, F-06 HMAC fuzz 0/2000. 23/23 suites green.
-- Cristian Cezar Moisés <sac@securityops.co> Sun, 01 Jun 2026 13:30:00 +0000
vaptvupt (3.3.0-1) UNRELEASED; urgency=medium
* Incremental HMAC-SHA256 for the per-block Encrypt-then-MAC hot path.
Adds zupt_hmac_sha256_init/update/final: the ipad/opad key-prefix
blocks are folded once per keyring (not once per block), and the
MAC is streamed segment-by-segment (aad || nonce || ciphertext ||
seq) instead of being concatenated into a freshly malloc'd buffer.
Removes a per-block malloc + full-ciphertext memcpy on BOTH the
encrypt and decrypt sides (for 4 MB blocks: a 4 MB malloc + 4 MB
copy per block per direction), and stops copying secret plaintext-
derived ciphertext into a second heap buffer.
* Byte-identical MAC: RFC 2104 + SHA-256 Merkle-Damgard make streamed
updates equal to a single concatenated hash. Verified by RFC 4231
vectors, a new equivalence test, and byte-exact decryption of
archives produced by 3.2.0 and earlier. No wire-format change
(format v1.6); F-09 byte sweep 0/1827, F-06 HMAC fuzz 0/2000.
* The one-shot zupt_hmac_sha256 is now a thin wrapper over the
incremental API (single source of truth; used by the AIT and other
once-per-archive sites).
* New regression test tests/test_hmac_incremental.{c,sh} wired into
make check and make test. ASan clean on both KDF paths.
-- Cristian Cezar Moisés <sac@securityops.co> Sun, 01 Jun 2026 12:30:00 +0000
vaptvupt (3.2.0-1) UNRELEASED; urgency=medium
* SHA-256 hardware acceleration (Intel SHA-NI). Adds an
SHA256RNDS2/MSG1/MSG2 compression-function path
(src/zupt_sha256_shani.c) with runtime CPUID dispatch
(has_shani, CPUID.07H:EBX[29]) and a multi-block update() that
feeds full blocks straight to the hardware. Accelerates the
Encrypt-then-MAC second pass (HMAC-SHA256) and PBKDF2 on CPUs
with the SHA Extensions (Intel Goldmont+/Ice Lake+, AMD Zen+).
Bit-identical output to the scalar path; the scalar C fallback
runs everywhere SHA-NI is absent (incl. aarch64).
* Security: SHA-NI is constant-time by construction (no data-
dependent memory access or branches), strengthening the side-
channel posture of HMAC verification over attacker-influenced
ciphertext relative to the table-free-but-scalar software path.
* Validation: the 64 SHA-NI round constants are verified bit-
identical to the scalar K[] table; NIST FIPS 180-4 vectors pass
on both paths; streaming-split == one-shot across lengths
0..4096. New regression test tests/test_sha256_shani.{c,sh}
wired into make check and make test.
* No wire-format change: same SHA-256, same HMAC, same bytes.
Format stays v1.6; 3.1.x archives extract unchanged.
-- Cristian Cezar Moisés <sac@securityops.co> Sun, 01 Jun 2026 11:00:00 +0000
vaptvupt (3.1.0-1) UNRELEASED; urgency=medium
* Integrate VaptVupt LZ + ANS codec 2.48.5 -> 2.53.3. Codec API is
byte-identical (vaptvupt.h and all vv_*.h unchanged); only vv_ans.c,
vv_decoder.c, vv_encoder.c changed. Brings the optimal parser
(measured: text -1.95%, binary -1.31%, source -4.72% smaller),
large-window extreme mode, faster decode (now ~on par with zstd-19),
and 6 upstream corrupt-input decoder memory-safety fixes.
* F-14: fix heap-buffer-overflow WRITE in the decode wrapper. Decode
buffers were malloc(uncompressed_size) with no slack; the codec AVX2
over-copy needs >=32 B slack per its documented contract. The old
codec never reached it; the 2.53.3 wider AVX2 hot path does (found
by ASan on a degenerate all-repeats input at L1). Fixed with a shared
ZUPT_VV_DECODE_SLACK (64 B) guard on both the single-threaded
(zupt_format.c) and parallel (zupt_parallel.c) decode paths.
* vv_decoder.c scalar/non-AVX2 build is now -Wall -Wextra -Werror clean
(3 AVX2-only safe-zone vars guarded with #if VV_INLINE_AVX2) — fixes
a -Werror break on the aarch64/Termux scalar target.
* Removed the unverified "1.27x zstd-3 decode" claim from help/version
output and README; replaced with our own measured numbers.
* New regression test tests/test_vv_decode_slack.sh (7 assertions),
wired into make check and make test.
* Wire format unchanged (v1.6); 3.0.3 archives extract byte-exact.
19/19 suites green; ASan 24/24 single-threaded + 15/15 multi-threaded;
300-trial bit-flip fuzz: 0 crashes. F-09 byte sweep 0/1827.
-- Cristian Cezar Moisés <sac@securityops.co> Sat, 31 May 2026 12:00:00 +0000
vaptvupt (3.0.3-1) UNRELEASED; urgency=medium
* Static-analysis cleanup pass:
- Removed dead AND-branch in zupt_decode_varint() and
zupt_read_varint() (the `&& (x&0x80)` part of the s>=64
overflow check was unreachable since the preceding
`if(!(x&0x80))return n;` already handles the terminator
case). Behaviour identical; flagged by cppcheck as
`knownConditionTrueFalse`.
- Explicit (tcflag_t) cast on the ECHO bit-clear in
prompt_password() to silence -Wsign-conversion.
- Explicit (size_t) cast on zupt_encode_varint return value
in zupt_disk_backup() — matches the convention used in
zupt_format.c.
* Our (non-vendored) C source now compiles cleanly under:
gcc -Wall -Wextra -Wpedantic -Wshadow -Wcast-align
-Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference
-Wformat=2 -Wlogical-op -Wjump-misses-init -Wdouble-promotion
-Woverlength-strings -Wconversion -Wsign-conversion -Werror
on 9 source files. Vendored vv_*.c, fips202.c, and zupt_mlkem.c
are kept under the upstream warning policy.
* New regression test tests/test_static_analysis.sh (7 assertions)
wires up cppcheck warning+performance level, error-level, and
pattern-level checks for the v3.0.3 dead-code findings.
Skipped gracefully if cppcheck is not installed. Wired into
make check and make test.
-- Cristian Cezar Moisés <sac@securityops.co> Mon, 26 May 2026 15:00:00 +0000
vaptvupt (3.0.2-1) UNRELEASED; urgency=medium
* F-13: split usage() string literal to stay under C99's 4095-char
limit (was 4121 chars, triggering -Woverlength-strings). Five
logical fprintf sections (synopsis, compress opts, extract opts,
examples, footer) — readable and maintainable.
* Help text refreshed: examples now use `vaptvupt` (not legacy
`zupt`), default codec described as "VaptVupt LZ + ANS 2.48.5"
(was stale "LZ77 + Huffman"), license attribution corrected to
"AGPL-3.0-or-later (VaptVupt)" (was "(Zupt)"), commercial-
licensing contact added, format-version line added.
* -Woverlength-strings now in the default CFLAGS — F-13 type
regressions caught at compile time.
* New regression test tests/test_help_consistency.sh (10 assertions):
parses src/zupt_main.c for the longest fprintf string-literal,
checks help output for command-name consistency, codec naming,
license attribution, KDF default, and format-version reporting.
Wired into make check and make test.
-- Cristian Cezar Moisés <sac@securityops.co> Mon, 26 May 2026 14:00:00 +0000
vaptvupt (3.0.1-1) UNRELEASED; urgency=medium
* GUI license cleanup: removed MIT-license credit line from the
about panel (the GUI is AGPL-3.0-or-later with commercial dual-
licensing; the MIT reference was a templating mistake). Replaced
gui/LICENSE-GUI (was MIT) with AGPL-3.0-or-later, mirroring the
top-level LICENSE. Top-level LICENSE preamble updated to reflect
the v3.0.0 Zupt → VaptVupt rename.
* GUI version-string parsing bug fix: the v3.0.0 GUI used
`replace("zupt ", "")` to peel the product name out of the CLI's
version banner, but that substring also appears inside the v3.0.0
parenthetical "formerly zupt; renamed in v3.0.0", so the parser
produced garbage. Window title, splash header, status bar and
about-panel hero number all now display "3.0.1" cleanly. New
anchored regex `_VERSION_RE` matches the version number only.
* GUI about-panel enhanced: header "ZUPT" → "VAPTVUPT", crypto
stack expanded to include Argon2id (default since v2.4.1), HKDF,
and the VaptVupt LZ + ANS codec attribution as a separate row
with its own copyright + license. Commercial-licensing contact
(sac@securityops.co) now visible.
* New regression test `tests/test_gui_branding.sh` catches future
MIT-line resurgence, the broken `replace("zupt ", ...)` parser
pattern, and the about-panel header still saying "ZUPT".
Wired into `make check` and `make test`. 11 assertions.
-- Cristian Cezar Moisés <sac@securityops.co> Mon, 26 May 2026 13:00:00 +0000
vaptvupt (3.0.0-1) UNRELEASED; urgency=medium
* Renamed from zupt → vaptvupt: prior INPI Brasil trademark on
"Zupt" required a product rename. Archive extension stays .zupt
for format continuity (header magic unchanged). The binary
`zupt` is preserved as a symlink to `vaptvupt` for one major
version cycle.
* Integrated VaptVupt LZ + ANS codec 2.48.5: fixes csz==0 heap-
buffer-overflow READ in vv_dstream_decompress_chunk (fuzzer-
found, medium severity), UBSan-safe pointer arithmetic in
vv_copy_match.
* Enhanced manpage covering all v3.0.0 surface (rename rationale,
PERFORMANCE table, threat model summary, exit codes, ENV vars).
* GUI binary-discovery bug fix: GUI launched from desktop
sessions with minimal PATH (no /usr/bin) now finds the binary
correctly. Discovery log available via VAPTVUPT_DEBUG=1.
* Format unchanged at v1.6. Bidirectional compat with 2.4.x.
-- Cristian Cezar Moisés <sac@securityops.co> Sun, 25 May 2026 13:00:00 +0000
zupt (2.4.8-1) UNRELEASED; urgency=medium
* Initial Debian source package.
* Closes F-12 (encrypted comments), continues from upstream's
no-open-findings security baseline.
-- Cristian Cezar Moisés <sac@securityops.co> Tue, 20 May 2025 12:00:00 +0000

41
packaging/debian/control Normal file
View file

@ -0,0 +1,41 @@
Source: vaptvupt
Section: utils
Priority: optional
Maintainer: Cristian Cezar Moisés <sac@securityops.co>
Build-Depends:
debhelper-compat (= 13),
gcc,
libc6-dev,
python3 (>= 3.8)
Standards-Version: 4.6.2
Homepage: https://git.securityops.co/cristiancmoises/zupt
Vcs-Browser: https://git.securityops.co/cristiancmoises/zupt
Vcs-Git: https://git.securityops.co/cristiancmoises/zupt.git
Rules-Requires-Root: no
Package: vaptvupt
Architecture: any
Provides: zupt (= ${binary:Version})
Replaces: zupt
Conflicts: zupt
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Post-quantum backup compression utility (formerly Zupt)
VaptVupt (renamed from Zupt in v3.0.0 due to INPI Brasil trademark) is
a pure-C11 backup compression utility featuring:
* Post-quantum hybrid encryption (ML-KEM-768 + X25519, FIPS 203)
* AES-256-CTR + HMAC-SHA256 authenticated encryption (Encrypt-then-MAC)
* Argon2id password-based key derivation (default since 2.4.1)
* Multi-threaded compression with the VaptVupt LZ + ANS codec 2.48.5
* Full-disk backup and restore with sparse-region detection
* End-to-end byte-level tamper detection on encrypted archives
(0 silent-accept positions in the v1.6 exhaustive byte sweep)
* Constant-time cryptographic primitives verified with Jasmin
* NIST/RFC test vectors for SHA-256, SHA-3, ML-KEM-768, AES-256-CTR,
HMAC-SHA256, X25519, PBKDF2, Argon2id
.
The archive extension stays .zupt for format continuity (header magic
unchanged). The binary `zupt` is preserved as a symlink to `vaptvupt`.
.
The archive format includes an integrity trailer that authenticates the
header and footer, per-block HMAC with bound frame-preface AAD, and
optional encrypted comments.

View file

@ -0,0 +1,49 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: zupt
Upstream-Contact: Cristian Cezar Moisés <sac@securityops.co>
Source: https://git.securityops.co/cristiancmoises/zupt
Files: *
Copyright: 2025-2026 Cristian Cezar Moisés
License: AGPL-3.0-or-later
Files: src/vv_*.c include/vaptvupt*.h include/vv_*.h vendor/zuptsdk/include/vv_*.h vendor/zuptsdk/include/vaptvupt*.h
Copyright: 2025-2026 Cristian Cezar Moisés (VaptVupt codec)
License: GPL-3.0-or-later
Files: vendor/zuptsdk/*
Copyright: 2025-2026 Cristian Cezar Moisés (libzuptsdk)
License: GPL-3.0-or-later
Files: debian/*
Copyright: 2025-2026 Cristian Cezar Moisés <sac@securityops.co>
License: AGPL-3.0-or-later
License: AGPL-3.0-or-later
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
.
On Debian systems, the full text of the GNU Affero General Public
License version 3 can be found in the file
`/usr/share/common-licenses/AGPL-3'.
License: GPL-3.0-or-later
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
On Debian systems, the full text of the GNU General Public License
version 3 can be found in the file `/usr/share/common-licenses/GPL-3'.

37
packaging/debian/rules Executable file
View file

@ -0,0 +1,37 @@
#!/usr/bin/make -f
# SPDX-License-Identifier: AGPL-3.0-or-later
# Honour Debian's reproducible-build epoch when set by dpkg-buildpackage.
export SOURCE_DATE_EPOCH ?= 1747699200
# Hardening flags — Debian's defaults are already strong, this adds project-
# specific ones.
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
export DEB_CFLAGS_MAINT_APPEND = -Wall -Wextra -Wpedantic
export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
%:
dh $@
override_dh_auto_build:
$(MAKE) -j$$(nproc)
override_dh_auto_test:
# Project's own regression suite covers F-06..F-12.
$(MAKE) test
override_dh_auto_install:
$(MAKE) DESTDIR=$(CURDIR)/debian/zupt PREFIX=/usr install
# Vendored libzuptsdk goes alongside the binary at a relative rpath.
install -d $(CURDIR)/debian/zupt/usr/lib/zupt
install -m 0755 vendor/zuptsdk/libzuptsdk.so.2.0.0 \
$(CURDIR)/debian/zupt/usr/lib/zupt/libzuptsdk.so.2.0.0
ln -sf libzuptsdk.so.2.0.0 $(CURDIR)/debian/zupt/usr/lib/zupt/libzuptsdk.so.2
ln -sf libzuptsdk.so.2.0.0 $(CURDIR)/debian/zupt/usr/lib/zupt/libzuptsdk.so
override_dh_auto_clean:
$(MAKE) clean
# Skip dh_strip's separate -dbgsym packages for a single-source-package layout.
override_dh_strip:
dh_strip --no-automatic-dbgsym

View file

@ -0,0 +1 @@
3.0 (quilt)

View file

@ -0,0 +1,71 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Homebrew formula for zupt.
#
# To publish:
# 1. Run `make dist` upstream to produce zupt-VERSION.tar.gz (reproducible).
# 2. Upload to a stable release URL.
# 3. Update `url`, `version`, and `sha256` below.
# 4. Submit to homebrew-core via PR OR host in your own tap
# (e.g. cristiancmoises/homebrew-tap).
#
# Local test:
# brew install --build-from-source ./zupt.rb
# brew test zupt
# brew audit --strict --online zupt
#
# Notes for macOS:
# * Jasmin assembly is disabled at build time on Darwin (no jasminc dep);
# the C fallback for AES-256-CTR / HMAC compare paths is shipped.
# * libzuptsdk is vendored and installed alongside the binary; the binary
# uses @loader_path rpath so users don't have to set DYLD paths.
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"
sha256 "REPLACE_WITH_SHA256_OF_RELEASE_TARBALL"
license "AGPL-3.0-or-later"
depends_on "python@3.12" => :test # only for test-suite tamper harness
def install
# macOS build: no Jasmin, C-fallback crypto paths are used.
# The Makefile auto-detects Jasmin availability and falls back cleanly.
ENV["CFLAGS"] = "#{ENV.cflags} -O2 -std=c11 -Wall -Wextra"
system "make", "-j#{ENV.make_jobs}"
system "make", "DESTDIR=#{prefix}", "PREFIX=", "install"
# Vendored libzuptsdk goes into lib/zupt/ with @loader_path rpath.
# Note: Linux ships .so.2.0.0; macOS .dylib equivalent must be built
# separately by the vendored makefile. For the initial Homebrew
# submission this assumes the upstream tarball includes a .dylib build;
# if not, build it here.
lib_zupt = lib/"zupt"
lib_zupt.mkpath
if File.exist?("vendor/zuptsdk/libzuptsdk.dylib")
cp "vendor/zuptsdk/libzuptsdk.dylib", lib_zupt
elsif File.exist?("vendor/zuptsdk/libzuptsdk.so.2.0.0")
# Fallback: link Linux-style .so on macOS (works for direct loads but
# not for dlopen-on-Darwin scenarios). Upstream is tracking this.
cp "vendor/zuptsdk/libzuptsdk.so.2.0.0", lib_zupt
end
# Docs
doc.install "README.md", "SECURITY.md", "CHANGELOG.md", "AUDIT.md"
end
test do
# End-to-end sanity check: build a real archive, extract it, byte-compare.
(testpath/"input.txt").write("homebrew formula test payload\n")
system bin/"zupt", "c", "-p", "test", "out.zupt", "input.txt"
system bin/"zupt", "info", "out.zupt"
mkdir "extracted"
cd "extracted" do
system bin/"zupt", "x", "-p", "test", "../out.zupt"
end
system "diff", "-q", "input.txt", "extracted/input.txt"
end
end

120
packaging/nix/flake.nix Normal file
View file

@ -0,0 +1,120 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Nix flake for zupt.
#
# Usage (with flakes enabled):
# nix build .#zupt # build the package
# nix run .#zupt -- version # run zupt directly
# nix develop # drop into a dev shell
# nix flake check # lint the flake
#
# To consume from another flake:
# inputs.zupt.url = "git+https://git.securityops.co/cristiancmoises/zupt?ref=v2.4.4";
# ...packages.x86_64-linux.default = inputs.zupt.packages.x86_64-linux.zupt;
#
# Reproducibility:
# * Nix already pins the source tree by hash.
# * `make dist` is also reproducible (tests/test_dist_reproducible.sh).
# * Together, two independent Nix evaluations of the same flake.lock
# produce byte-identical /nix/store outputs.
{
description = "Zupt post-quantum backup compression utility (C11)";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system:
let
pkgs = import nixpkgs { inherit system; };
zupt = pkgs.stdenv.mkDerivation {
pname = "vaptvupt";
version = "4.0.0";
# When publishing, replace this with `fetchurl` against the
# release tarball. For local development the flake assumes it
# lives in the same directory as the source.
src = ./.;
nativeBuildInputs = with pkgs; [
gcc
gnumake
];
# python3 is only used by the regression-test harness.
checkInputs = [ pkgs.python3 ];
# Build with the project's preferred warning set on top of Nix's
# hardening flags. Don't override -O2 from stdenv.
NIX_CFLAGS_COMPILE = "-Wall -Wextra -Wpedantic -std=c11";
# `make` builds the binary using vendored libzuptsdk via rpath.
buildPhase = ''
runHook preBuild
make -j$NIX_BUILD_CORES
runHook postBuild
'';
# Run the full upstream regression suite. Disable per-package by
# setting doCheck = false; on by default.
doCheck = true;
checkPhase = ''
runHook preCheck
make test
runHook postCheck
'';
installPhase = ''
runHook preInstall
make DESTDIR=$out PREFIX= install
# Move libzuptsdk into $out/lib/zupt/. The binary's rpath is
# $ORIGIN/../lib/zupt after autopatchelf rewrites it during
# the fixup phase.
mkdir -p $out/lib/zupt
install -m 0755 vendor/zuptsdk/libzuptsdk.so.2.0.0 \
$out/lib/zupt/libzuptsdk.so.2.0.0
ln -sf libzuptsdk.so.2.0.0 $out/lib/zupt/libzuptsdk.so.2
ln -sf libzuptsdk.so.2.0.0 $out/lib/zupt/libzuptsdk.so
# Docs
mkdir -p $out/share/doc/zupt
cp README.md SECURITY.md CHANGELOG.md AUDIT.md $out/share/doc/zupt/
runHook postInstall
'';
meta = with pkgs.lib; {
description = "Post-quantum backup compression utility (ML-KEM-768 + AES-256-CTR + HMAC-SHA256 + Argon2id)";
homepage = "https://git.securityops.co/cristiancmoises/zupt";
license = with licenses; [ agpl3Plus gpl3Plus ];
maintainers = [ ];
platforms = [ "x86_64-linux" "aarch64-linux" ];
mainProgram = "zupt";
};
};
in {
packages = {
zupt = zupt;
default = zupt;
};
apps.default = {
type = "app";
program = "${zupt}/bin/zupt";
};
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
gcc
gnumake
python3
valgrind
gdb
];
};
});
}

View file

@ -0,0 +1,112 @@
# openSUSE Build Service update for `home:cabelo:innovators/zupt`
This directory contains the three files you need to update your OBS
package from `1.5.5` to `2.4.8`:
| File | Status vs. your current files |
|---------------|---------------------------------------------------------------------|
| `_service` | Updated `revision` to `v2.4.8`. Format unchanged (still `tar_scm`). |
| `zupt.spec` | Version → `2.4.8`. License corrected `MIT``AGPL-3.0-or-later`. `%check` now calls `make check` (new distro-safe target). |
| `zupt.changes`| 13 new entries prepended (2.0.0 → 2.4.8). Your existing 1.0.01.5.4 history is preserved verbatim. |
## What changed in the spec
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.
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:
* 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
Total ~91 assertions, runs in <2 minutes, no flakes on emulated
hosts. The s390x branch still falls back to just `test-vectors`.
3. **Upstream URL in `URL:` field** updated to
`https://git.securityops.co/cristiancmoises/zupt` (the canonical
project URL). The `_service` file still pulls from GitHub
(`https://github.com/cristiancmoises/zupt`) since that's where
your `tar_scm` is already configured and what works in OBS today.
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.
## How to apply
```sh
# 1. Check out the package
osc checkout home:cabelo:innovators zupt
cd home:cabelo:innovators/zupt
# 2. Drop the new files in (assuming this README is at
# /path/to/zupt-source/packaging/opensuse/README.md)
cp /path/to/zupt-source/packaging/opensuse/_service .
cp /path/to/zupt-source/packaging/opensuse/zupt.spec .
cp /path/to/zupt-source/packaging/opensuse/zupt.changes .
# 3. Trigger the service locally to fetch v2.4.8 from GitHub
osc service runall
# This produces zupt-2.4.8.tar.gz in the current directory and
# updates zupt.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).
# 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 zupt-2.4.8.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"
```
## 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`).
Zupt 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.
## Reporting issues
* Upstream bugs: https://git.securityops.co/cristiancmoises/zupt
* 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 `zupt-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.

View file

@ -0,0 +1,16 @@
<services>
<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="versionformat">@PARENT_TAG@</param>
<param name="versionrewrite-pattern">v(.*)</param>
<param name="submodules">enable</param>
<param name="filename">vaptvupt</param>
</service>
<service name="recompress" mode="manual">
<param name="file">*.tar</param>
<param name="compression">gz</param>
</service>
<service name="set_version" mode="manual"/>
</services>

View file

@ -0,0 +1,400 @@
-------------------------------------------------------------------
Wed Jun 10 12:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 4.0.0:
* Codec -> canonical VaptVupt 2.60.4 (security: OOB heap write in
AVX2 exact-size decode fixed; CBMC-verified BCJ with
auto-detection; ratio gate byte-identical on identical inputs).
* F-16 disclosed and fixed: <= 3.8.0 wrote undecodable archives on
executable content at L8/L9 (write-time BCJ defect). Re-create
affected archives with 4.0.0.
* New --pq-box mode (libpqvaptvupt 0.6.0): ML-KEM-768 + X25519 via
HKDF-SHA256 domain-separated combiner; keygen --box; 13/13
adversarial checks; ASan/UBSan clean.
* SHA-NI measured 5.8x (scalar 204 -> 1184 MB/s); estimate retired.
* Clang strict build restored; wire format v1.6 unchanged; 26
suites green; vectors 16/16.
-------------------------------------------------------------------
Mon Jun 1 22:31:24 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 3.8.0 (documentation-only; binary identical to 3.7.0, v1.6)
* Add BENCHMARKS.md: consolidated reproducible measured benchmarks
(compression ratio/throughput, encode-speed-vs-level, KDF-vs-per-
block crypto overhead, head-to-head ratio vs zstd showing where
VaptVupt loses) with the test machine + method stated per table.
SHA-NI speedup marked [ESTIMATED] (test box has no SHA-NI).
* README benchmark section re-dated and linked to BENCHMARKS.md.
No source/crypto/wire change; test_vectors 16/0, F-09 0/1827.
-------------------------------------------------------------------
Mon Jun 1 22:15:58 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 3.7.0
* Route the ML-KEM-768 decapsulation implicit-rejection comparison
(1088-byte ciphertext) through the audited constant-time primitive
zupt_ct_memeq, replacing an inline byte-OR loop. A timing leak there
is a KEM decapsulation oracle (breaks IND-CCA2); it now shares the
measured-constant-time path of the MAC compare. ML-KEM output
semantics unchanged (verified by FIPS 203 roundtrip, implicit-
rejection vector, PQ-hybrid roundtrip, wrong-key rejection).
* test_ct_timing extended to the 1088-byte compare + a source-routing
guard; the 1088B dudect numbers are informational (at that size the
signal is memory-dominated and memcmp is not a clean control), with
constant-timeness following from the 32B pass + length-independence
+ routing guard. No wire-format change (v1.6); F-09 0/1827.
-------------------------------------------------------------------
Mon Jun 1 21:33:10 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 3.6.0
* Add NIST SP 800-38A AES-256-CTR known-answer vectors (F.5.5/F.5.6)
to the test_vectors suite. Validates zupt_aes256_ctr against the
standard on both the Jasmin AES-NI path and the C T-table fallback;
AES was previously only roundtrip-tested.
* Fix an inverted result check in the ML-KEM-768 self-test reporting
(printed OK on failure) and fix the NTT roundtrip self-test to
assert the real Montgomery-scaled invariant instead of a false
identity (no more misleading stderr "NTT roundtrip FAILED"). ML-KEM
correctness end-to-end was never affected.
* test_vectors now 16/0 (was 14, one vacuous). No wire-format change
(v1.6); F-09 0/1827, F-06 0/2000.
-------------------------------------------------------------------
Mon Jun 1 16:51:09 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 3.5.0
* Measured constant-time MAC comparison (dudect-style). The three
duplicated inline byte-OR MAC compares are consolidated into one
audited primitive zupt_ct_memeq() (volatile OR-accumulate, no early
exit), used by the v1.6 strict decrypt path and the F-08 integrity
trailer. New timing test tests/test_ct_timing.sh applies Welch's
t-test (fixed vs random tag classes) at -O2 with a leaky-memcmp
positive control; the compare shows ~1% of the leak signal.
* Internal hardening only: asserted constant-time becomes measured +
regression-guarded. No wire-format change (v1.6); F-09 0/1827,
F-06 0/2000. Jasmin zupt_mac_verify_ct path unchanged.
-------------------------------------------------------------------
Mon Jun 1 12:00:57 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 3.4.0
* F-15: Argon2id KDF parameter transparency. New archives append a
one-byte KDF profile descriptor to the 0x04 enc-header, making it
self-describing about the Argon2id cost (the PBKDF2 header already
recorded its iteration count). Covered by the F-08 integrity
trailer; cannot be stripped undetected.
* Back-compatible: legacy 33-byte Argon2id archives decrypt unchanged;
unknown profiles are refused fail-closed. New test
tests/test_kdf_transparency.sh incl. a build-time KDF cost-floor
guard. No wire-format change (v1.6); F-09 0/1827, F-06 0/2000.
-------------------------------------------------------------------
Mon Jun 1 11:46:10 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 3.3.0
* Incremental HMAC-SHA256 for the per-block Encrypt-then-MAC hot
path: ipad/opad folded once per keyring; MAC streamed instead of
concatenated into a malloc'd buffer. Removes a per-block malloc +
full-ciphertext memcpy on both encrypt and decrypt sides.
* Byte-identical MAC; verified by RFC 4231 vectors, an equivalence
test, and byte-exact decryption of 3.2.x archives. No wire-format
change (v1.6); F-09 0/1827, F-06 0/2000.
-------------------------------------------------------------------
Mon Jun 1 11:18:57 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Package renamed zupt -> vaptvupt (project renamed in 3.0.0 due to a
prior INPI Brasil trademark on "Zupt"). Provides/Obsoletes: zupt so
the upgrade is automatic; the binary still installs a /usr/bin/zupt
compatibility symlink and a zupt.1 man-page symlink.
- Update to 3.2.0
* SHA-256 hardware acceleration (Intel SHA-NI): SHA256RNDS2/MSG1/MSG2
compression path with CPUID runtime dispatch; accelerates HMAC-
SHA256 (Encrypt-then-MAC second pass) and PBKDF2 on Zen+/Ice Lake+.
Bit-identical to the scalar path; scalar C fallback elsewhere
(incl. aarch64). SHA-NI is constant-time by construction.
* 64 SHA-NI round constants verified identical to the scalar K[]
table; NIST FIPS 180-4 vectors pass on both paths. New regression
test tests/test_sha256_shani.sh.
* No wire-format change (v1.6); 3.1.x archives extract unchanged.
-------------------------------------------------------------------
Sun May 31 23:41:40 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 3.1.0
* VaptVupt codec 2.48.5 -> 2.53.3 (API byte-identical; 3 .c files).
Optimal parser (text -1.95%, binary -1.31%, source -4.72% smaller),
large-window extreme, faster decode (~on par with zstd-19), and 6
upstream corrupt-input decoder memory-safety fixes.
* F-14: heap-buffer-overflow WRITE fixed in the decode wrapper. The
codec AVX2 over-copy needs >=32 B output slack (documented contract);
our buffers had none. Fixed with ZUPT_VV_DECODE_SLACK (64 B) on both
single-threaded and parallel decode paths. Found by ASan.
* vv_decoder.c scalar build made -Werror clean (aarch64).
* New regression test tests/test_vv_decode_slack.sh.
* Wire format unchanged (v1.6); 3.0.x archives extract byte-exact.
-------------------------------------------------------------------
Tue May 26 02:50:05 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 3.0.3
* Static-analysis cleanup: removed dead AND-branch in varint
decoders (cppcheck knownConditionTrueFalse); explicit casts on
-Wsign-conversion sites. Our non-vendored C now compiles clean
under -Wconversion -Wsign-conversion -Werror.
* New regression test tests/test_static_analysis.sh (7 assertions)
wraps cppcheck + strict GCC; wired into make check. Skipped
cleanly when cppcheck is unavailable on the build host.
* No functional changes; archive format and wire compatibility
unchanged at v1.6.
-------------------------------------------------------------------
Tue May 26 02:27:34 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 3.0.2
* F-13: usage() string literal exceeded C99's 4095-char limit
(was 4121 chars); split into five logical fprintf sections.
-Woverlength-strings added to default CFLAGS so this kind of
regression fails the build under -Werror.
* Help text refreshed: examples now use `vaptvupt` (not legacy
`zupt`), default codec correctly named VaptVupt LZ + ANS 2.48.5
(was stale "LZ77 + Huffman"), license attribution corrected.
* New regression test tests/test_help_consistency.sh (10 assertions)
wired into make check.
-------------------------------------------------------------------
Tue May 26 00:43:52 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 3.0.1
* GUI license cleanup: removed MIT credit line from the about
panel; gui/LICENSE-GUI replaced (was MIT) with AGPL-3.0-or-later
to match the source SPDX header. The GUI was never actually
released under MIT — that was a templating mistake.
* GUI version-string parsing bug fix (the replace("zupt ", ...)
substring also matched inside the v3.0.0 parenthetical). Window
title, splash header, status bar and about-panel hero number now
display "3.0.1" cleanly.
* GUI about-panel enhanced: header VAPTVUPT, crypto stack now
includes Argon2id, HKDF, the VaptVupt codec attribution; the
commercial-licensing contact (sac\@securityops.co) is visible.
* New regression test tests/test_gui_branding.sh (11 assertions)
catches future regressions of all three issues.
-------------------------------------------------------------------
Mon May 25 13:09:04 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 3.0.0 (rename: zupt → vaptvupt)
* Renamed from "Zupt" to "VaptVupt" due to a prior INPI Brasil
trademark registration on the name "Zupt" for unrelated software.
The on-disk archive extension stays .zupt for format continuity
(header magic bytes \x5A\x55\x50\x54\x1A\x00 are unchanged).
Binaries from 2.x extract 3.0.0 archives byte-exact and vice
versa. The C-source identifier prefix (zupt_, ZUPT_) is also
unchanged for ABI continuity with libzuptsdk.
* Legacy /usr/bin/zupt symlink installed alongside vaptvupt for
one major version cycle.
* Integrated VaptVupt LZ + ANS codec 2.48.5: fixes csz==0 heap-
buffer-overflow READ in vv_dstream_decompress_chunk (libFuzzer-
found, medium severity), UBSan-safe pointer arithmetic in
vv_copy_match, const-correctness cleanup in vv_ans entropy
encoder.
* Enhanced manpage (597 lines, was 422). New PERFORMANCE section
with measured numbers against gzip-9 / zstd-3 / zstd-19, ENV
var documentation including VAPTVUPT_BIN and VAPTVUPT_DEBUG,
threat-model summary in the man page itself.
* Fixed GUI binary-discovery bug: zupt-gui (now vaptvupt-gui)
launched from desktop sessions with a minimal PATH that didn't
include /usr/bin failed to locate the binary. New _find_vaptvupt
implementation tries env vars, source-tree paths, shutil.which
on both names, then a curated list of common install paths,
and runs a liveness check (binary actually runs and
exits 0) on each candidate. Diagnostic output via VAPTVUPT_DEBUG=1.
-------------------------------------------------------------------
Sun May 24 13:08:04 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.4.8
* New `make check` target — distro-safe regression subset for
OBS %check (no `make clean` mid-stream, no threading-flaky
tests). Spec now calls `make check` on x86_64/aarch64.
* License field corrected: AGPL-3.0-or-later (was MIT in 1.5.x).
Project is dual-licensed AGPL-3.0-or-later + commercial.
* Upstream URL updated to git.securityops.co.
-------------------------------------------------------------------
Sun May 24 13:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.4.7
* Manpage rewrite covering all v2.4.x flags (--kdf, --comment,
--comment-file, --pq-sdk, ML-KEM-768)
* Shell completions for bash, zsh, fish covering 16 critical
CLI flags
* Fixed three stale strings that still mentioned PBKDF2 as the
default KDF after the v2.4.1 flip to Argon2id
-------------------------------------------------------------------
Sun May 24 12:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.4.6
* Comprehensive GitHub Actions CI matrix: 8 jobs covering
GCC+Clang, strict warnings (-Werror + full §6 set),
ASAN/UBSAN, PIE hardening, aarch64 via QEMU, `make dist`
reproducibility, packaging-syntax, tag-triggered release.
* New THREAT_MODEL.md (12 KB): plain-English security boundary
document covering what zupt protects against AND what it
explicitly does NOT.
-------------------------------------------------------------------
Sun May 24 11:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.4.5
* Packaging completion: Fedora/RHEL .spec, NixOS flake.nix,
DISTRIBUTION.md guide. openSUSE inherits this work.
* New tests/test_packaging_syntax.sh asserts cross-recipe
version consistency.
-------------------------------------------------------------------
Sun May 24 10:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.4.4
* Reproducible `make dist` source tarball: sorted file order,
fixed mtime via SOURCE_DATE_EPOCH, uid/gid pinned, gzip -9n.
Two consecutive runs produce byte-identical sha256 (asserted
by tests/test_dist_reproducible.sh).
* Upstream packaging recipes for AUR, Debian, Homebrew added.
-------------------------------------------------------------------
Sun May 24 09:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.4.3
* F-12 closed: encrypted archive comments 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.
* CLI flags -c / --comment and --comment-file.
* Exhaustive byte sweep on 1878-byte archive with comment:
0/1878 silent accepts.
-------------------------------------------------------------------
Sun May 24 08:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.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.
* Eliminates a verbal probe-oracle. No format change.
-------------------------------------------------------------------
Sun May 24 07:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.4.1
* F-10: password-mode KDF default flipped from PBKDF2-SHA256
(600k iter) to Argon2id (memory-hard). Use `--kdf pbkdf2`
for compatibility with v2.4.0-and-older readers.
-------------------------------------------------------------------
Sun May 24 06:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.3.1
* F-09 closed: extended-AAD per-block MAC binds 29-byte
canonical preface (block_type, codec_id, block_flags, sizes,
plaintext-XXH64) into every block's HMAC. Format v1.5 → v1.6.
* Exhaustive byte sweep on 1827-byte PQ-SDK archive:
0/1827 silent accepts. Full byte-level tamper detection on
encrypted archives.
-------------------------------------------------------------------
Sun May 24 05:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.3.0
* F-08 closed via 32-byte archive-integrity-trailer:
HMAC-SHA256(mac_key, hdr[0..63] || footer[0..23]) appended
after the footer. Format v1.4 → v1.5. v1.4 archives still
readable with downgrade warning.
-------------------------------------------------------------------
Sun May 24 04:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.2.5
* F-06 (HIGH) closed: HMAC verifier on the Jasmin path was
silently accepting ~6% of single-bit tampers because
`diff_v2 & diff_v1` cleared zeroed-difference bits. Fixed via
(x|-x)>>63 nonzero-indicator fold before AND. 2000-trial
regression: 0 silent accepts.
* F-07 closed: structural block_type check at index_offset
rejects malformed archives early.
-------------------------------------------------------------------
Sun May 24 03:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.2.4
* Audit batch close (F-01..F-05): help-text newline, flaky
tamper test, -Wshadow cleanup, orphan selftest removed,
const-correct pointer params.
-------------------------------------------------------------------
Sun May 24 02:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Update to 2.0.0
* Major version bump. libzuptsdk integration: HKDF combiner,
key commitment, HPKE binding for the post-quantum path
(--pq-sdk mode). Argon2id KDF available. New on-disk format
v1.4 with explicit enc_type byte dispatch (0x01=PBKDF2,
0x03=PQ-SDK).
* VaptVupt 2.x codec integrated as first-class compressor.
* AppImage / .deb / .rpm packaging scripts added upstream.
-------------------------------------------------------------------
Thu Apr 2 02:54:23 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Version 1.5.4
* Makefile multiarc
-------------------------------------------------------------------
Thu Apr 2 02:31:57 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Version 1.5.4
* Object files removed
-------------------------------------------------------------------
Thu Apr 2 02:30:11 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Version 1.5.3
* Added man page installation (zupt.1.gz)
* Enabled verbose build output with V=1 support in Makefile
* Fixed Makefile to honor LDFLAGS and support PIE linking
* Improved rpmlint compliance for OBS/openSUSE packaging
-------------------------------------------------------------------
Tue Mar 31 00:24:26 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Version 1.5.2
- Enable C fallback on AArch64
* Jasmin Gate integration behind x86_64 target detection
* Detect target architecture using the compiler triplet
`$(CC) -dumpmachine`
* Prevent Jasmin x86_64 object files from being linked in
AArch64 builds
* Automatically use C fallback on non-x86_64 targets
* Preserve `ZUPT_USE_JASMIN` only when assembly sources are
present and compatible
-------------------------------------------------------------------
Mon Mar 30 22:17:02 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Version 1.5.1
* Binaries removed
-------------------------------------------------------------------
Sun Mar 29 22:10:54 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Version 1.5.0
- Added -Jasmin Assembly Integration
* zupt_mac_verify_ct Jasmin assembly linked into
zupt_decrypt_buffer(). Replaces the C XOR accumulation loop
for HMAC-SHA256 comparison.
* zupt_ct_select_32 Jasmin assembly linked into
zupt_mlkem768_decaps(). Replaces the C cmov() function for
Fujisaki-Okamoto implicit rejection.
* include/zupt_jasmin.h — extern declarations for all Jasmin
functions with ABI documentation.
* #ifdef ZUPT_USE_JASMIN dispatch guards in zupt_crypto.c and
zupt_mlkem.c with clean C fallback.
* Makefile auto-detects jasmin/*.s files, assembles to .o,
links into binary, sets -DZUPT_USE_JASMIN.
-------------------------------------------------------------------
Mon Mar 23 18:46:51 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
- Initial package
- Version 1.0.0

View file

@ -0,0 +1,106 @@
#
# spec file for package vaptvupt
#
# Copyright (c) 2026 SUSE LLC
# Copyright (c) 2026 Alessandro de Oliveira Faria (A.K.A CABELO) <cabelo@opensuse.org>
# Copyright (c) 2025-2026 Cristian Cezar Moisés <zupt@riseup.net> (upstream)
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
Name: vaptvupt
Version: 4.0.0
Release: 0
Summary: Post-quantum backup compression with AES-256 + ML-KEM-768 hybrid encryption
License: AGPL-3.0-or-later
Group: Productivity/Archiving/Compression
URL: https://git.securityops.co/cristiancmoises/zupt
Source0: %{name}-%{version}.tar.gz
BuildRequires: gcc
BuildRequires: gzip
BuildRequires: make
# v3.0.0 renamed the project Zupt -> VaptVupt (prior INPI Brasil
# trademark on "Zupt"). Cleanly supersede any installed zupt package;
# the binary still installs a /usr/bin/zupt compatibility symlink.
Provides: zupt = %{version}-%{release}
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.
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.
%prep
%autosetup -p1
chmod +x tests/*.sh
%build
%make_build V=1 \
CFLAGS="%{optflags} -fPIE -Wall -Wextra -std=c11 -Iinclude -Isrc" \
LDFLAGS="%{?build_ldflags} -pie" \
LDLIBS="-lm -lpthread"
%check
# `make check` is the distro-safe subset added in 2.4.8: runs the
# security-critical regressions (F-06 HMAC, F-08 AIT, F-09 byte
# integrity, F-10 KDF, F-11 auth-fail, F-12 comments) plus NIST/RFC
# vectors. Skips threaded and dist-reproducibility tests that are
# sensitive to build-host environment.
#
# 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 \
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 \
CFLAGS="%{optflags} -fPIE -Wall -Wextra -std=c11 -Iinclude -Isrc" \
LDFLAGS="%{?build_ldflags} -pie" \
LDLIBS="-lm -lpthread" \
check
%endif
%install
%make_install PREFIX=%{_prefix}
%files
%license LICENSE
%doc README.md SECURITY.md CHANGELOG.md
%{_bindir}/vaptvupt
%{_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

119
packaging/rpm/vaptvupt.spec Normal file
View file

@ -0,0 +1,119 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Fedora / RHEL / CentOS RPM spec for zupt.
#
# Build with:
# spectool -g zupt.spec # fetches the upstream tarball
# rpmbuild -ba zupt.spec # builds source + binary RPMs
#
# To bring a release into production:
# 1. Run `make dist` upstream → /tmp/zupt-VERSION.tar.gz (reproducible).
# 2. Upload to a stable release URL (git.securityops.co releases).
# 3. Update %{version} below.
# 4. Run `sha256sum /tmp/zupt-VERSION.tar.gz` and update Source0
# checksum (handled by spectool when configured) or pin via
# sha256sum in a separate manifest if your distro requires it.
# 5. rpmbuild --define '_topdir ~/rpmbuild' -ba zupt.spec
#
# This spec is written for Fedora 38+ and EPEL 9+; it should also work
# on RHEL 8 (with EPEL) by adjusting BuildRequires if Python 3.8+ isn't
# in the base.
Name: vaptvupt
Version: 4.0.0
Release: 1%{?dist}
Summary: Post-quantum backup compression utility (AES-256 + ML-KEM-768 + Argon2id, formerly Zupt)
License: AGPL-3.0-or-later AND GPL-3.0-or-later
URL: https://git.securityops.co/cristiancmoises/zupt
Source0: %{url}/releases/download/v%{version}/%{name}-%{version}.tar.gz
# v3.0.0: legacy `zupt` package is superseded. Renaming was forced
# by a prior INPI Brasil trademark registration on "Zupt". The
# archive extension (.zupt), wire format, magic bytes, and C ABI
# are unchanged.
Provides: zupt = %{version}-%{release}
Obsoletes: zupt < 3.0.0
Conflicts: zupt < 3.0.0
BuildRequires: gcc
BuildRequires: make
BuildRequires: glibc-devel
BuildRequires: python3 >= 3.8
# python3 is only needed for the regression-test harness (byte sweeps,
# tamper injection). The shipped binary has no Python dependency.
Requires: glibc
%description
Zupt is a pure-C11 backup compression utility featuring:
* Post-quantum hybrid encryption (ML-KEM-768 + X25519, FIPS 203)
* AES-256-CTR + HMAC-SHA256 authenticated encryption (Encrypt-then-MAC)
* Argon2id password-based key derivation (default since 2.4.1)
* Multi-threaded compression with the VaptVupt LZ codec
* Full-disk backup and restore with sparse-region detection
* End-to-end byte-level tamper detection on encrypted archives
(0 silent-accept positions in the v1.6 exhaustive byte sweep)
* Constant-time cryptographic primitives verified with Jasmin
* NIST/RFC test vectors for SHA-256, SHA-3, ML-KEM-768, AES-256-CTR,
HMAC-SHA256, X25519, PBKDF2, Argon2id
The archive format includes an integrity trailer that authenticates
the header and footer, per-block HMAC with bound frame-preface AAD,
and optional encrypted comments.
%global debug_package %{nil}
# Single source RPM, no -debuginfo split for the initial release.
%prep
%autosetup -n %{name}-%{version}
%build
# Use Fedora's default optflags but with the project's preferred warning set.
CFLAGS="%{optflags} -Wall -Wextra -Wpedantic -std=c11" \
LDFLAGS="%{?build_ldflags}" \
%make_build
%check
# Run the upstream regression suite. F-06 HMAC trials, F-08 top-MAC sweep,
# F-09 byte sweep (1827 positions), F-10..F-12 regressions, dist
# reproducibility. ~3 minutes on modern hardware.
%make_build test
%install
%make_install DESTDIR=%{buildroot} PREFIX=/usr
# Install the vendored libzuptsdk into /usr/lib/zupt/ — the binary is
# linked with -Wl,-rpath,$ORIGIN/vendor/zuptsdk so we preserve the same
# layout under /usr/.
install -d %{buildroot}%{_libdir}/%{name}
install -m 0755 vendor/zuptsdk/libzuptsdk.so.2.0.0 \
%{buildroot}%{_libdir}/%{name}/libzuptsdk.so.2.0.0
ln -sf libzuptsdk.so.2.0.0 %{buildroot}%{_libdir}/%{name}/libzuptsdk.so.2
ln -sf libzuptsdk.so.2.0.0 %{buildroot}%{_libdir}/%{name}/libzuptsdk.so
install -m 0755 vendor/pqvaptvupt/libpqvaptvupt.so.0.6.0 \
%{buildroot}%{_libdir}/%{name}/libpqvaptvupt.so.0.6.0
ln -sf libpqvaptvupt.so.0.6.0 %{buildroot}%{_libdir}/%{name}/libpqvaptvupt.so.0
ln -sf libpqvaptvupt.so.0.6.0 %{buildroot}%{_libdir}/%{name}/libpqvaptvupt.so
%files
%license LICENSE
%doc README.md SECURITY.md CHANGELOG.md AUDIT.md
%{_bindir}/zupt
%{_libdir}/%{name}/libzuptsdk.so.2.0.0
%{_libdir}/%{name}/libzuptsdk.so.2
%{_libdir}/%{name}/libzuptsdk.so
%{_libdir}/%{name}/libpqvaptvupt.so.0.6.0
%{_libdir}/%{name}/libpqvaptvupt.so.0
%{_libdir}/%{name}/libpqvaptvupt.so
%if 0%{?_mandir:1}
%{_mandir}/man1/zupt.1*
%endif
%changelog
* Tue May 20 2025 Cristian Cezar Moisés <sac@securityops.co> - 2.4.4-1
- Initial Fedora/EPEL RPM package.
- Tracks upstream v2.4.4: distribution packaging release; archive
format unchanged from v2.4.3 (v1.6, 0/1878 silent-accept byte
tampers).