Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8844f806a | ||
|
|
33eb90454d | ||
|
|
ebb9ab3aa1 | ||
|
|
f6bbb95eee | ||
|
|
4741d34783 | ||
|
|
a6e330827e | ||
|
|
6fd78e0515 | ||
|
|
cb60cf0218 | ||
|
|
af87e67a87 | ||
|
|
7a8e5c5fbd | ||
|
|
65bc5bd427 | ||
|
|
69fc26bfec | ||
|
|
7687cfa577 | ||
|
|
d8668e6d64 | ||
|
|
8cca841258 | ||
|
|
cb794e100c | ||
|
|
6d9f740a2b | ||
|
|
7821523942 | ||
|
|
0fb13c2b49 | ||
|
|
fcce1b5a4a | ||
|
|
c27e3d5528 | ||
|
|
353b649bec | ||
|
|
70f5233888 | ||
|
|
cdc08870de | ||
|
|
f623205f45 | ||
|
|
09dc804b3e | ||
|
|
8db676568b | ||
|
|
85c1366b58 | ||
|
|
0a9a63db66 | ||
|
|
ff99770bd0 | ||
|
|
5febc89764 |
||
|
|
74e393ba3e | ||
|
|
3f89719056 | ||
|
|
2512844bc4 | ||
|
|
fce2522aad | ||
|
|
51fa068f50 | ||
|
|
59f9ebc59e | ||
|
|
df534503ec | ||
|
|
7c062bc92c | ||
|
|
c42513e0db | ||
|
|
a2350dd0f6 | ||
|
|
df232f25b1 | ||
|
|
95d9a8279b | ||
|
|
92488d47b9 | ||
|
|
e15329a7c9 | ||
|
|
5cb04bf837 | ||
|
|
040b9326bc | ||
|
|
748001aa65 |
||
|
|
d198dbb205 | ||
|
|
ca54a19a27 | ||
|
|
26c0d4b4ea | ||
|
|
572031910c | ||
|
|
8e0eb1ee41 |
||
|
|
92c1a1b377 | ||
|
|
25494a1877 | ||
|
|
bb0a62ca76 | ||
|
|
a4aab7a1b6 | ||
|
|
288df8ea0b | ||
|
|
3e3da54173 |
||
|
|
1ee9f30ae8 | ||
|
|
5fcb9977ad | ||
|
|
2b68548e93 | ||
|
|
79213bdc7b | ||
|
|
3e8fd9a3a4 | ||
|
|
c42251f0a2 | ||
|
|
0e8e3b11c6 | ||
|
|
7388038404 | ||
|
|
f567d0de79 | ||
|
|
570a7023c9 | ||
|
|
4c5a2efe63 | ||
|
|
dfa52fd576 | ||
|
|
d9a249fb65 | ||
|
|
5bb280eb25 | ||
|
|
26e45b5c3e |
||
|
|
44a429213a | ||
|
|
372ceb3b6d |
||
|
|
763438d3ee | ||
|
|
77f3d5f58e |
||
|
|
8a429693ef |
||
|
|
fb7e071892 | ||
|
|
aaaedcffaf | ||
|
|
5f303b3693 | ||
|
|
19811e79e4 | ||
|
|
3c4fcf208c | ||
|
|
266f6e21b7 | ||
|
|
89f03aa6f9 | ||
|
|
58e2355e9a | ||
|
|
23e9613390 | ||
|
|
68182c221f |
||
|
|
cf6e635309 | ||
|
|
d585de91a9 | ||
|
|
6adf3ceb95 | ||
|
|
cbca9a44b2 | ||
|
|
4edcf3af5e | ||
|
|
bd15c72fc9 |
213 changed files with 24177 additions and 13486 deletions
27
.gitattributes
vendored
Normal file
27
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
* text=auto eol=lf
|
||||
|
||||
*.bat text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
*.ps1 text eol=crlf
|
||||
*.s text eol=lf
|
||||
*.S text eol=lf
|
||||
*.jazz text eol=lf
|
||||
|
||||
*.png binary
|
||||
*.ico binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.gif binary
|
||||
*.webp binary
|
||||
*.zip binary
|
||||
*.gz binary
|
||||
*.xz binary
|
||||
*.bz2 binary
|
||||
*.zst binary
|
||||
|
||||
# These downstream recipes pin the checksum of the release tarball itself.
|
||||
# Excluding them from `git archive` avoids a self-referential checksum while
|
||||
# keeping every recipe versioned in Git and available to its package manager.
|
||||
/packaging/aur/** export-ignore
|
||||
/packaging/homebrew/** export-ignore
|
||||
/packaging/guix/** export-ignore
|
||||
804
.github/workflows/ci.yml
vendored
804
.github/workflows/ci.yml
vendored
|
|
@ -1,254 +1,640 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
#
|
||||
# Zupt CI matrix.
|
||||
#
|
||||
# Mirrors the project's local-verification protocol from PROMPT.md §6:
|
||||
# 1. Plain GCC build
|
||||
# 2. Plain Clang build
|
||||
# 3. Strict GCC (full warning set)
|
||||
# 4. Strict Clang (full warning set)
|
||||
# 5. ASAN + UBSAN
|
||||
# 6. Full regression suite (12 suites: audit, dedup, path-traversal,
|
||||
# argument-order, block-swap, F-08, F-09 byte sweep, F-10, F-11,
|
||||
# F-12, packaging syntax, dist reproducibility)
|
||||
# 7. License header audit
|
||||
# 8. `make dist` reproducibility (two runs, sha256 must match)
|
||||
# 9. aarch64 cross-test via QEMU emulation
|
||||
# 10. Automatic release on git tag push
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
tags: ['v*']
|
||||
branches:
|
||||
- master
|
||||
- 'codex/**'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches: [master]
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name != 'workflow_dispatch' && !startsWith(github.ref, 'refs/tags/') }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# ─── Plain build + test, exactly as a user would do it ───
|
||||
source-policy:
|
||||
name: Source-only, license, shell and secret policy
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Check out all refs without LFS or submodules
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
lfs: false
|
||||
submodules: false
|
||||
|
||||
- name: Install audit tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
dpkg-dev file git-lfs libarchive-tools libxml2-utils make python3 ruby \
|
||||
shellcheck unzip
|
||||
|
||||
- name: Audit tracked files, worktree and HEAD archive
|
||||
run: bash scripts/check-source-only.sh
|
||||
|
||||
- name: Exercise positive and negative scanner fixtures
|
||||
run: bash tests/test_source_only.sh
|
||||
|
||||
- name: Audit license headers
|
||||
run: make WITH_SDK=0 WITH_PQBOX=0 audit-licenses
|
||||
|
||||
- name: Validate release packaging metadata
|
||||
run: bash tests/test_packaging_syntax.sh
|
||||
|
||||
- name: ShellCheck release and source-policy scripts
|
||||
run: |
|
||||
shellcheck \
|
||||
packaging/build-deb.sh \
|
||||
packaging/build-rpm.sh \
|
||||
packaging/build-appimage.sh \
|
||||
packaging/build-dmg.sh \
|
||||
packaging/build-gui-appimage.sh \
|
||||
packaging/build-gui-deb.sh \
|
||||
packaging/build-gui-rpm.sh \
|
||||
packaging/opensuse/source-audit.sh \
|
||||
scripts/check-source-only.sh \
|
||||
scripts/export-opensuse-package.sh \
|
||||
scripts/test-installed-zupt.sh \
|
||||
tests/test_atomic_archive_output.sh \
|
||||
tests/test_authenticated_dedup_reorder.sh \
|
||||
tests/test_benchmark_temp_safety.sh \
|
||||
tests/test_block_type_confusion.sh \
|
||||
tests/test_disk_device_capacity.sh \
|
||||
tests/test_f09_preface.sh \
|
||||
tests/test_key_files.sh \
|
||||
tests/test_legacy_disk_5_2_1.sh \
|
||||
tests/test_path_traversal.sh \
|
||||
tests/test_pqbox.sh \
|
||||
tests/test_sdk.sh \
|
||||
tests/test_source_only.sh
|
||||
|
||||
- name: Credential material audit (paths only)
|
||||
shell: bash
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
findings=$(git grep -Il -E -- \
|
||||
"-----BEGIN (RSA |OPENSSH |EC |DSA )?PRIVATE KEY-----|https?://[^/@[:space:]]+:[A-Za-z0-9_+=.-]{20,}@|gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{20,}|glpat-[A-Za-z0-9_-]{20,}|AKIA[A-Z0-9]{16}|xox[baprs]-[A-Za-z0-9-]{20,}|(FORGEJO_TOKEN|SECURITYOPS_TOKEN|GITHUB_TOKEN|CODEBERG_TOKEN)[[:space:]]*[:=][[:space:]]*['\\\"]?[A-Za-z0-9_+=./-]{20,}" \
|
||||
-- . || true)
|
||||
if [[ -n $findings ]]; then
|
||||
printf '%s\n' "$findings" >&2
|
||||
echo 'credential-like material found in tracked files' >&2
|
||||
exit 1
|
||||
fi
|
||||
echo 'No private-key block, named token assignment, or credential-bearing URL found.'
|
||||
|
||||
build-and-test:
|
||||
name: Build and full tests (${{ matrix.cc }})
|
||||
needs: source-policy
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cc: [gcc, clang]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install build deps
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
lfs: false
|
||||
submodules: false
|
||||
- name: Install build tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential clang dpkg-dev python3
|
||||
- name: Build (${{ matrix.cc }})
|
||||
run: make CC=${{ matrix.cc }} -j$(nproc)
|
||||
- name: zupt version
|
||||
run: ./zupt version
|
||||
- name: Full regression suite
|
||||
run: make test
|
||||
- name: License header audit
|
||||
run: make audit-licenses
|
||||
sudo apt-get install -y build-essential clang file libarchive-tools python3 unzip
|
||||
- name: Clean source-only build
|
||||
run: |
|
||||
make clean
|
||||
make -j"$(nproc)" CC=${{ matrix.cc }} V=1 WITH_SDK=0 WITH_PQBOX=0
|
||||
- name: Distribution checks
|
||||
run: make CC=${{ matrix.cc }} V=1 WITH_SDK=0 WITH_PQBOX=0 check
|
||||
- name: Extended upstream tests
|
||||
run: make CC=${{ matrix.cc }} V=1 WITH_SDK=0 WITH_PQBOX=0 test-all
|
||||
- name: In-tree SDK atomic key-save regression
|
||||
run: make CC=${{ matrix.cc }} V=1 sdk-test
|
||||
- name: Functional test of the built CLI
|
||||
run: bash scripts/test-installed-zupt.sh "$PWD/zupt"
|
||||
|
||||
# ─── Strict warning matrix — what the project's §6 protocol uses ───
|
||||
strict-warnings:
|
||||
name: Strict warnings (${{ matrix.cc }})
|
||||
needs: source-policy
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- cc: gcc
|
||||
cflags: "-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"
|
||||
flags: >-
|
||||
-O2 -g -std=c11 -Wall -Wextra -Wpedantic -Wshadow
|
||||
-Wcast-align -Wstrict-prototypes -Wmissing-prototypes
|
||||
-Wnull-dereference -Wformat=2 -Werror
|
||||
- cc: clang
|
||||
cflags: "-Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -O2 -std=c11 -Werror"
|
||||
flags: >-
|
||||
-O2 -g -std=c11 -Wall -Wextra -Wpedantic -Wshadow
|
||||
-Wcast-align -Wstrict-prototypes -Wmissing-prototypes
|
||||
-Wnull-dereference -Wformat=2 -Werror
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install build deps
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential clang
|
||||
- name: Strict ${{ matrix.cc }} build (warnings → errors)
|
||||
run: make CC=${{ matrix.cc }} CFLAGS="${{ matrix.cflags }}" -j$(nproc)
|
||||
|
||||
# ─── ASAN + UBSAN — catches memory bugs the warning matrix can't ───
|
||||
sanitizers:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install build deps
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential python3
|
||||
- name: Build with ASAN + UBSAN
|
||||
run: make test-asan
|
||||
- name: Native --pq byte-exact roundtrip under ASAN
|
||||
env:
|
||||
ASAN_OPTIONS: detect_leaks=0:abort_on_error=1
|
||||
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
|
||||
run: |
|
||||
# Native hybrid ML-KEM-768 + X25519 (the source-only default; --pq-sdk
|
||||
# needs a WITH_SDK=1 build and is unavailable here).
|
||||
./zupt_asan keygen -o /tmp/k.priv
|
||||
./zupt_asan keygen --pub -o /tmp/k.pub -k /tmp/k.priv
|
||||
./zupt_asan compress --pq /tmp/k.pub /tmp/a.zupt include/
|
||||
mkdir -p /tmp/extracted
|
||||
./zupt_asan extract --pq /tmp/k.priv -o /tmp/extracted /tmp/a.zupt
|
||||
diff -qr include /tmp/extracted/include
|
||||
|
||||
# ─── PIE hardening build — verifies no runtime breakage from -fPIE ───
|
||||
pie-hardening:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install build deps
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential
|
||||
- name: Build with PIE + hardening
|
||||
run: |
|
||||
make CFLAGS="-O2 -std=c11 -fPIE -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security" \
|
||||
LDFLAGS="-pie -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack" \
|
||||
-j$(nproc)
|
||||
- name: Verify binary is PIE
|
||||
run: |
|
||||
file ./zupt | grep -E "ELF .*executable.*pie|ELF .*shared object" || \
|
||||
{ file ./zupt; echo "binary is not PIE"; exit 1; }
|
||||
- name: Smoke test
|
||||
run: |
|
||||
echo "test" > /tmp/in.txt
|
||||
./zupt c -p secret /tmp/a.zupt /tmp/in.txt
|
||||
mkdir /tmp/out
|
||||
(cd /tmp/out && ./../../home/runner/work/zupt/zupt/zupt x -p secret /tmp/a.zupt) || \
|
||||
{ cd /tmp/out && "$GITHUB_WORKSPACE/zupt" x -p secret /tmp/a.zupt; }
|
||||
diff -q /tmp/in.txt /tmp/out/in.txt
|
||||
|
||||
# ─── aarch64 cross-build via QEMU emulation ───
|
||||
cross-aarch64:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
platforms: arm64
|
||||
- name: Build + test inside aarch64 container
|
||||
run: |
|
||||
docker run --rm --platform linux/arm64 \
|
||||
-v "$PWD":/src -w /src \
|
||||
ubuntu:24.04 \
|
||||
bash -c '
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq build-essential python3
|
||||
make -j$(nproc)
|
||||
./zupt version
|
||||
make test
|
||||
'
|
||||
|
||||
# ─── make dist reproducibility ───
|
||||
dist-reproducibility:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install build deps
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential python3
|
||||
- name: First dist build
|
||||
run: make dist
|
||||
- name: Capture sha256 (run 1)
|
||||
id: sha1
|
||||
run: |
|
||||
VER=$(grep '^#define ZUPT_VERSION_STRING' include/zupt.h | awk -F'"' '{print $2}')
|
||||
SHA=$(sha256sum /tmp/vaptvupt-$VER.tar.gz | awk '{print $1}')
|
||||
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
|
||||
echo "ver=$VER" >> "$GITHUB_OUTPUT"
|
||||
echo "Run 1: $SHA"
|
||||
- name: Second dist build (must produce identical sha256)
|
||||
run: make dist
|
||||
- name: Verify reproducibility
|
||||
run: |
|
||||
VER="${{ steps.sha1.outputs.ver }}"
|
||||
SHA2=$(sha256sum /tmp/vaptvupt-$VER.tar.gz | awk '{print $1}')
|
||||
if [ "$SHA2" != "${{ steps.sha1.outputs.sha }}" ]; then
|
||||
echo "::error::make dist is NOT reproducible"
|
||||
echo " run 1: ${{ steps.sha1.outputs.sha }}"
|
||||
echo " run 2: $SHA2"
|
||||
exit 1
|
||||
fi
|
||||
echo "Reproducible ✓ ($SHA2)"
|
||||
- name: Upload reproducible source tarball
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: zupt-source-tarball
|
||||
path: /tmp/vaptvupt-*.tar.gz
|
||||
|
||||
# ─── Packaging-recipe syntax (cross-distro) ───
|
||||
packaging-syntax:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install validators
|
||||
persist-credentials: false
|
||||
- name: Install compilers
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential dpkg-dev ruby rpm
|
||||
- name: Build (for include/zupt.h to exist; not strictly needed for syntax test)
|
||||
run: make -j$(nproc)
|
||||
- name: Run packaging syntax test
|
||||
run: bash tests/test_packaging_syntax.sh
|
||||
sudo apt-get install -y build-essential clang
|
||||
- name: Compile with warnings as errors
|
||||
run: |
|
||||
make clean
|
||||
make -j"$(nproc)" CC=${{ matrix.cc }} V=1 WITH_SDK=0 WITH_PQBOX=0 \
|
||||
CFLAGS="${{ matrix.flags }}"
|
||||
|
||||
# ─── Automatic GitHub release on git tag push ───
|
||||
release:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs: [build-and-test, strict-warnings, sanitizers, dist-reproducibility, packaging-syntax]
|
||||
sanitizers:
|
||||
name: ASan, LSan and UBSan
|
||||
needs: source-policy
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install build deps
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential python3
|
||||
- name: Build reproducible source tarball
|
||||
run: make dist
|
||||
- name: Get version
|
||||
id: ver
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install compiler and test tools
|
||||
run: |
|
||||
VER=$(grep '^#define ZUPT_VERSION_STRING' include/zupt.h | awk -F'"' '{print $2}')
|
||||
echo "version=$VER" >> "$GITHUB_OUTPUT"
|
||||
- name: Verify tag matches version
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential file python3
|
||||
- name: Instrumented functional tests
|
||||
env:
|
||||
ASAN_OPTIONS: detect_leaks=1:abort_on_error=1
|
||||
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
|
||||
run: make V=1 WITH_SDK=0 WITH_PQBOX=0 test-asan-run
|
||||
- name: Mutation smoke under sanitizers
|
||||
env:
|
||||
ASAN_OPTIONS: detect_leaks=1:abort_on_error=1
|
||||
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
|
||||
run: make V=1 WITH_SDK=0 WITH_PQBOX=0 fuzz-format-run
|
||||
|
||||
static-analysis:
|
||||
name: GCC static analyzer
|
||||
needs: source-policy
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install GCC
|
||||
run: |
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
EXPECTED="v${{ steps.ver.outputs.version }}"
|
||||
if [ "$TAG" != "$EXPECTED" ]; then
|
||||
echo "::error::tag $TAG doesn't match include/zupt.h $EXPECTED"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential
|
||||
- name: Analyze every source translation unit
|
||||
run: |
|
||||
make clean
|
||||
make -j"$(nproc)" CC=gcc V=1 WITH_SDK=0 WITH_PQBOX=0 \
|
||||
CFLAGS="-O1 -g -std=c11 -Wall -Wextra -Werror -fanalyzer"
|
||||
|
||||
source-archive:
|
||||
name: Reproducible audited source archive
|
||||
needs: source-policy
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
lfs: false
|
||||
submodules: false
|
||||
- name: Install archive audit tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y file libarchive-tools python3 unzip
|
||||
- name: Build the source archive twice
|
||||
run: |
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
mkdir -p "$RUNNER_TEMP/dist-one" "$RUNNER_TEMP/dist-two" \
|
||||
"$RUNNER_TEMP/release-source"
|
||||
make DIST_TARBALL="$RUNNER_TEMP/dist-one/zupt-$version.tar.gz" dist
|
||||
make DIST_TARBALL="$RUNNER_TEMP/dist-two/zupt-$version.tar.gz" dist
|
||||
cmp "$RUNNER_TEMP/dist-one/zupt-$version.tar.gz" \
|
||||
"$RUNNER_TEMP/dist-two/zupt-$version.tar.gz"
|
||||
cp "$RUNNER_TEMP/dist-one/zupt-$version.tar.gz" \
|
||||
"$RUNNER_TEMP/release-source/"
|
||||
(cd "$RUNNER_TEMP/release-source" && sha256sum "zupt-$version.tar.gz" > \
|
||||
"zupt-$version.tar.gz.sha256")
|
||||
bash scripts/check-source-only.sh --archive \
|
||||
"$RUNNER_TEMP/release-source/zupt-$version.tar.gz"
|
||||
- name: Match downstream recipe checksums to the tagged source archive
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
source_tar="$RUNNER_TEMP/release-source/zupt-$version.tar.gz"
|
||||
actual_sha=$(sha256sum "$source_tar" | awk '{print $1}')
|
||||
aur_sha=$(awk -F"'" '/^sha256sums=/ { print $2; exit }' packaging/aur/PKGBUILD)
|
||||
homebrew_sha=$(awk -F'"' '/^[[:space:]]*sha256 / { print $2; exit }' packaging/homebrew/zupt.rb)
|
||||
guix_base32=$(sed -n 's/^[[:space:]]*(base32 "\([^"]*\)").*/\1/p' \
|
||||
packaging/guix/zupt.scm | head -n 1)
|
||||
actual_base32=$(python3 - "$source_tar" <<'PY'
|
||||
import hashlib
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
alphabet = "0123456789abcdfghijklmnpqrsvwxyz"
|
||||
digest = hashlib.sha256(pathlib.Path(sys.argv[1]).read_bytes()).digest()
|
||||
value = int.from_bytes(digest, "little")
|
||||
length = (len(digest) * 8 + 4) // 5
|
||||
print("".join(alphabet[(value >> (5 * index)) & 31]
|
||||
for index in range(length - 1, -1, -1)))
|
||||
PY
|
||||
)
|
||||
[[ $aur_sha == "$actual_sha" && $homebrew_sha == "$actual_sha" ]] || {
|
||||
echo 'AUR or Homebrew checksum does not match the source archive' >&2
|
||||
exit 1
|
||||
}
|
||||
[[ $guix_base32 == "$actual_base32" ]] || {
|
||||
echo 'Guix checksum does not match the source archive' >&2
|
||||
exit 1
|
||||
}
|
||||
- name: Upload source and checksum
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: release-source
|
||||
path: ${{ runner.temp }}/release-source/*
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
debian-package:
|
||||
name: Debian/Ubuntu source-built package
|
||||
needs: [source-policy, build-and-test]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
- name: Install Debian package tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential binutils dpkg-dev file git libarchive-tools python3 python3-pyqt6 unzip
|
||||
- name: Build and extract-test the DEB
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/release-deb"
|
||||
DIST_DIR="$RUNNER_TEMP/release-deb" RUN_CHECKS=1 bash packaging/build-deb.sh
|
||||
- name: Build and content-test the GUI DEB
|
||||
run: |
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
test -n "$version"
|
||||
mkdir -p "$RUNNER_TEMP/release-gui-deb"
|
||||
DIST_DIR="$RUNNER_TEMP/release-gui-deb" bash packaging/build-gui-deb.sh
|
||||
gui_deb="$RUNNER_TEMP/release-gui-deb/zupt-gui_${version}_all.deb"
|
||||
test -s "$gui_deb"
|
||||
test "$(dpkg-deb -f "$gui_deb" Package)" = zupt-gui
|
||||
test "$(dpkg-deb -f "$gui_deb" Version)" = "$version"
|
||||
test "$(dpkg-deb -f "$gui_deb" Architecture)" = all
|
||||
- name: Install, functionally test and uninstall the DEBs
|
||||
run: |
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
test -n "$version"
|
||||
deb=$(find "$RUNNER_TEMP/release-deb" -maxdepth 1 -type f -name '*.deb' -print -quit)
|
||||
gui_deb="$RUNNER_TEMP/release-gui-deb/zupt-gui_${version}_all.deb"
|
||||
test -n "$deb" && test -s "$gui_deb"
|
||||
sudo apt-get install -y "$deb" "$gui_deb"
|
||||
bash scripts/test-installed-zupt.sh /usr/bin/zupt
|
||||
QT_QPA_PLATFORM=offscreen zupt-gui --version | grep -Fx "zupt-gui $version"
|
||||
test ! -e /usr/bin/vaptvupt
|
||||
sudo apt-get purge -y zupt-gui zupt
|
||||
test ! -e /usr/bin/zupt-gui
|
||||
test ! -e /usr/bin/zupt
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: release-deb
|
||||
path: ${{ runner.temp }}/release-deb/*.deb
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: release-gui-deb
|
||||
path: ${{ runner.temp }}/release-gui-deb/*.deb
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
tumbleweed-rpm:
|
||||
name: openSUSE Tumbleweed x86_64 RPM gate
|
||||
needs: [source-policy, build-and-test]
|
||||
runs-on: ubuntu-24.04
|
||||
container: opensuse/tumbleweed:latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Bootstrap Git before checkout
|
||||
run: |
|
||||
zypper --non-interactive --gpg-auto-import-keys refresh
|
||||
zypper --non-interactive install --no-recommends \
|
||||
bash ca-certificates git-core
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
lfs: false
|
||||
submodules: false
|
||||
- name: Trust the exact checked-out workspace
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
- name: Install native openSUSE tooling
|
||||
run: |
|
||||
if rpm -q busybox-gawk >/dev/null 2>&1; then
|
||||
zypper --non-interactive remove busybox-gawk
|
||||
fi
|
||||
zypper --non-interactive install --no-recommends \
|
||||
bash binutils cpio coreutils diffutils file findutils gawk gcc git-core grep gzip \
|
||||
libxml2-tools make osc obs-service-obs_scm obs-service-recompress \
|
||||
obs-service-tar python3-base rpm-build rpmlint sed \
|
||||
shadow spec-cleaner tar unzip util-linux
|
||||
- name: Confirm the Factory architecture gate
|
||||
run: test "$(uname -m)" = x86_64
|
||||
- name: Validate OBS service and spec syntax
|
||||
run: |
|
||||
xmllint --noout packaging/opensuse/_service
|
||||
test -x /usr/lib/obs/service/obs_scm
|
||||
test -x /usr/lib/obs/service/tar
|
||||
test -x /usr/lib/obs/service/recompress
|
||||
rpmspec -P packaging/opensuse/zupt.spec >/dev/null
|
||||
spec-cleaner --no-copyright packaging/opensuse/zupt.spec \
|
||||
> "$RUNNER_TEMP/zupt.spec.cleaned"
|
||||
diff -u packaging/opensuse/zupt.spec \
|
||||
"$RUNNER_TEMP/zupt.spec.cleaned"
|
||||
- name: Exercise pinned OBS source service chain on release tags
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: |
|
||||
service_dir=$RUNNER_TEMP/obs-service
|
||||
mkdir -p "$service_dir"
|
||||
cp packaging/opensuse/_service "$service_dir/"
|
||||
# `osc service runall` additionally requires OBS working-copy metadata.
|
||||
# Use osc's installed service executor to validate this standalone,
|
||||
# repository-owned _service file with the exact same local services.
|
||||
python3 - "$service_dir" <<'PY'
|
||||
import os
|
||||
import sys
|
||||
from xml.etree import ElementTree
|
||||
from osc.obs_scm.serviceinfo import Serviceinfo
|
||||
|
||||
service_dir = sys.argv[1]
|
||||
os.chdir(service_dir)
|
||||
service_info = Serviceinfo()
|
||||
service_info.read(ElementTree.parse(f"{service_dir}/_service").getroot())
|
||||
raise SystemExit(service_info.execute(service_dir, "all", verbose=True))
|
||||
PY
|
||||
mapfile -t service_archives < <(find "$service_dir" -maxdepth 1 \
|
||||
-type f -name 'zupt-*.tar.gz' -print)
|
||||
test "${#service_archives[@]}" -eq 1
|
||||
bash scripts/check-source-only.sh --archive "${service_archives[0]}"
|
||||
- name: Build source and binary RPMs with real checks
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/release-rpm"
|
||||
DIST_DIR="$RUNNER_TEMP/release-rpm" bash packaging/build-rpm.sh
|
||||
- name: Run rpmlint without suppressions
|
||||
shell: bash
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
rpmlint "$RUNNER_TEMP"/release-rpm/*.rpm 2>&1 \
|
||||
| tee "$RUNNER_TEMP/rpmlint.log"
|
||||
if grep -Eq ': E:' "$RUNNER_TEMP/rpmlint.log"; then
|
||||
echo 'rpmlint reported one or more errors' >&2
|
||||
exit 1
|
||||
fi
|
||||
- name: Compute sha256
|
||||
id: sha
|
||||
- name: Install, functionally test and uninstall the RPM
|
||||
run: |
|
||||
VER="${{ steps.ver.outputs.version }}"
|
||||
SHA=$(sha256sum /tmp/vaptvupt-$VER.tar.gz | awk '{print $1}')
|
||||
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
|
||||
echo "$SHA vaptvupt-$VER.tar.gz" > /tmp/vaptvupt-$VER.tar.gz.sha256
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@v2
|
||||
rpm_file=$(find "$RUNNER_TEMP/release-rpm" -maxdepth 1 -type f \
|
||||
-name '*.rpm' ! -name '*.src.rpm' -print -quit)
|
||||
test -n "$rpm_file"
|
||||
zypper --non-interactive install --allow-unsigned-rpm "$rpm_file"
|
||||
test_home=/tmp/zupt-ci-user
|
||||
useradd --create-home --home-dir "$test_home" --shell /bin/bash zupt-ci
|
||||
runuser -u zupt-ci -- env HOME="$test_home" TMPDIR="$test_home" \
|
||||
bash "$GITHUB_WORKSPACE/scripts/test-installed-zupt.sh" \
|
||||
/usr/bin/zupt
|
||||
test ! -e /usr/bin/vaptvupt
|
||||
zypper --non-interactive remove zupt
|
||||
test ! -e /usr/bin/zupt
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
files: |
|
||||
/tmp/vaptvupt-${{ steps.ver.outputs.version }}.tar.gz
|
||||
/tmp/vaptvupt-${{ steps.ver.outputs.version }}.tar.gz.sha256
|
||||
body: |
|
||||
## Zupt v${{ steps.ver.outputs.version }}
|
||||
name: release-rpm
|
||||
path: ${{ runner.temp }}/release-rpm/*.rpm
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
Reproducible source tarball.
|
||||
gui-rpm-package:
|
||||
name: Fedora noarch GUI RPM and SRPM gate
|
||||
needs: [source-policy, build-and-test]
|
||||
runs-on: ubuntu-24.04
|
||||
container: fedora:latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Bootstrap checkout dependencies
|
||||
run: dnf install -y ca-certificates git
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
lfs: false
|
||||
submodules: false
|
||||
- name: Trust the exact checked-out workspace
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
- name: Install native build, package, audit and GUI runtime tools
|
||||
run: |
|
||||
dnf install -y \
|
||||
binutils cpio file findutils gcc git-core gzip libarchive make \
|
||||
python3 python3-pyside6 rpm-build rpmdevtools tar unzip
|
||||
- name: Build and content-test the GUI RPM and source RPM
|
||||
run: |
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
test -n "$version"
|
||||
mkdir -p "$RUNNER_TEMP/release-gui-rpm"
|
||||
DIST_DIR="$RUNNER_TEMP/release-gui-rpm" bash packaging/build-gui-rpm.sh
|
||||
gui_rpm="$RUNNER_TEMP/release-gui-rpm/zupt-gui-$version-1.noarch.rpm"
|
||||
gui_srpm="$RUNNER_TEMP/release-gui-rpm/zupt-gui-$version-1.src.rpm"
|
||||
test -s "$gui_rpm"
|
||||
test -s "$gui_srpm"
|
||||
test "$(rpm -qp --qf '%{NAME}' "$gui_rpm")" = zupt-gui
|
||||
test "$(rpm -qp --qf '%{VERSION}-%{RELEASE}' "$gui_rpm")" = "$version-1"
|
||||
test "$(rpm -qp --qf '%{ARCH}' "$gui_rpm")" = noarch
|
||||
rpm -qp --requires "$gui_rpm" | grep -Fx "zupt >= $version"
|
||||
test "$(rpm -qp --qf '%{NAME}' "$gui_srpm")" = zupt-gui
|
||||
test "$(rpm -qp --qf '%{VERSION}-%{RELEASE}' "$gui_srpm")" = "$version-1"
|
||||
test "$(rpm -qp --qf '%{SOURCEPACKAGE}' "$gui_srpm")" = 1
|
||||
test "$(rpm -qp --qf '%{SOURCERPM}' "$gui_srpm")" = '(none)'
|
||||
test "$(rpm -qpl "$gui_srpm" | wc -l)" -eq 2
|
||||
rpm -qpl "$gui_srpm" | grep -Fx "zupt-gui-$version.tar.gz"
|
||||
rpm -qpl "$gui_srpm" | grep -Fx zupt-gui.spec
|
||||
- name: Build the matching Fedora CLI RPM
|
||||
run: |
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
core_top="$RUNNER_TEMP/core-rpmbuild"
|
||||
mkdir -p "$core_top"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
||||
make DIST_TARBALL="$core_top/SOURCES/zupt-$version.tar.gz" dist
|
||||
rpmbuild --define "_topdir $core_top" -ba packaging/rpm/zupt.spec
|
||||
- name: Install and functionally test the GUI with the packaged CLI
|
||||
run: |
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
test -n "$version"
|
||||
core_rpm=$(find "$RUNNER_TEMP/core-rpmbuild/RPMS" -type f \
|
||||
-name "zupt-$version-1.*.rpm" ! -name '*-debuginfo-*' \
|
||||
! -name '*-debugsource-*' -print -quit)
|
||||
gui_rpm="$RUNNER_TEMP/release-gui-rpm/zupt-gui-$version-1.noarch.rpm"
|
||||
test -n "$core_rpm" && test -s "$gui_rpm"
|
||||
dnf install -y "$core_rpm" "$gui_rpm"
|
||||
bash scripts/test-installed-zupt.sh /usr/bin/zupt
|
||||
QT_QPA_PLATFORM=offscreen zupt-gui --version | grep -Fx "zupt-gui $version"
|
||||
test ! -e /usr/bin/vaptvupt
|
||||
dnf remove -y zupt-gui zupt
|
||||
test ! -e /usr/bin/zupt-gui
|
||||
test ! -e /usr/bin/zupt
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: release-gui-rpm
|
||||
path: ${{ runner.temp }}/release-gui-rpm/*.rpm
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
```
|
||||
sha256: ${{ steps.sha.outputs.sha }}
|
||||
```
|
||||
linux-portable:
|
||||
name: Linux x86_64 notice-bearing CLI tar.xz gate
|
||||
needs: [source-policy, build-and-test]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
lfs: false
|
||||
submodules: false
|
||||
- name: Install build and archive tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential binutils file python3 xz-utils
|
||||
- name: Build and audit the native executable
|
||||
run: |
|
||||
test "$(uname -m)" = x86_64
|
||||
make clean
|
||||
make -j"$(nproc)" V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0
|
||||
make V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 check
|
||||
bash scripts/test-installed-zupt.sh "$PWD/zupt"
|
||||
if readelf -d zupt | grep -Eq '(RPATH|RUNPATH)'; then
|
||||
echo 'Linux portable binary contains RPATH/RUNPATH' >&2
|
||||
exit 1
|
||||
fi
|
||||
mapfile -t needed < <(readelf -d zupt | sed -n 's/.*Shared library: \[\([^]]*\)\].*/\1/p')
|
||||
((${#needed[@]} > 0))
|
||||
for library in "${needed[@]}"; do
|
||||
case $library in
|
||||
libc.so.6|libm.so.6|libpthread.so.0) ;;
|
||||
*) echo "unexpected Linux runtime dependency: $library" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
if ldd zupt | grep -Fq 'not found'; then
|
||||
echo 'Linux portable binary has an unresolved runtime dependency' >&2
|
||||
exit 1
|
||||
fi
|
||||
- name: Assemble and extracted-package-test the tar.xz
|
||||
run: |
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
epoch=$(<.source-date-epoch)
|
||||
root="$RUNNER_TEMP/linux-work/zupt-$version-linux-x86_64"
|
||||
output="$RUNNER_TEMP/release-linux-x86_64/zupt-$version-linux-x86_64.tar.xz"
|
||||
mkdir -p "$root" "$(dirname "$output")"
|
||||
install -m 0755 zupt "$root/zupt"
|
||||
install -m 0644 README.md CHANGELOG.md SECURITY.md THREAT_MODEL.md \
|
||||
LICENSE LICENSE-AGPL-3.0 LICENSE-GPL-3.0 LICENSE-BSD-2-Clause \
|
||||
LICENSE-BSD-3-Clause LICENSE-CC0-1.0 NOTICE \
|
||||
THIRD-PARTY-NOTICES.md "$root/"
|
||||
tar --sort=name --mtime="@$epoch" --owner=0 --group=0 --numeric-owner \
|
||||
-C "$(dirname "$root")" -cJf "$output" "$(basename "$root")"
|
||||
extract=$(mktemp -d)
|
||||
tar -xJf "$output" -C "$extract"
|
||||
bash scripts/test-installed-zupt.sh \
|
||||
"$extract/$(basename "$root")/zupt"
|
||||
test "$(find "$extract/$(basename "$root")" -maxdepth 1 -type f | wc -l)" -eq 13
|
||||
sha256sum "$output"
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: release-linux-x86_64
|
||||
path: ${{ runner.temp }}/release-linux-x86_64/*.tar.xz
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
See CHANGELOG.md for release notes.
|
||||
gui-portable:
|
||||
name: Source-only GUI portable ZIP gate
|
||||
needs: [source-policy, build-and-test]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
lfs: false
|
||||
submodules: false
|
||||
- name: Install GUI smoke-test and archive tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential file python3 python3-pyqt6 unzip zip
|
||||
- name: Assemble, audit and execute the portable GUI source bundle
|
||||
run: |
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
make -j"$(nproc)" V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0
|
||||
root="$RUNNER_TEMP/gui-work/zupt-gui-$version-portable"
|
||||
output="$RUNNER_TEMP/release-gui-portable/zupt-gui-$version-portable.zip"
|
||||
mkdir -p "$root/assets" "$(dirname "$output")"
|
||||
install -m 0644 gui/src/zupt_gui.py "$root/zupt_gui.py"
|
||||
install -m 0755 packaging/portable/zupt-gui.sh \
|
||||
packaging/portable/zupt-gui.command "$root/"
|
||||
install -m 0644 packaging/portable/zupt-gui.bat "$root/"
|
||||
install -m 0644 packaging/portable/README.txt "$root/README.txt"
|
||||
install -m 0644 gui/assets/zupt-icon.png gui/assets/zupt.ico "$root/assets/"
|
||||
install -m 0644 LICENSE-AGPL-3.0 gui/LICENSE-GUI CHANGELOG.md "$root/"
|
||||
install -m 0644 gui/assets/README.md "$root/ASSET-PROVENANCE.md"
|
||||
bash scripts/check-source-only.sh --tree "$root"
|
||||
QT_QPA_PLATFORM=offscreen PATH="$PWD:$PATH" \
|
||||
"$root/zupt-gui.sh" --version | grep -Fx "zupt-gui $version"
|
||||
epoch=$(<.source-date-epoch)
|
||||
find "$root" -exec touch -d "@$epoch" {} +
|
||||
(cd "$(dirname "$root")" && zip -X -9 -r "$output" "$(basename "$root")")
|
||||
extract=$(mktemp -d)
|
||||
unzip -q "$output" -d "$extract"
|
||||
bash scripts/check-source-only.sh --tree "$extract/$(basename "$root")"
|
||||
QT_QPA_PLATFORM=offscreen PATH="$PWD:$PATH" \
|
||||
"$extract/$(basename "$root")/zupt-gui.sh" --version | \
|
||||
grep -Fx "zupt-gui $version"
|
||||
sha256sum "$output"
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: release-gui-portable
|
||||
path: ${{ runner.temp }}/release-gui-portable/*.zip
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
### Verifying the tarball
|
||||
|
||||
```sh
|
||||
sha256sum -c zupt-${{ steps.ver.outputs.version }}.tar.gz.sha256
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
```sh
|
||||
tar xzf zupt-${{ steps.ver.outputs.version }}.tar.gz
|
||||
cd zupt-${{ steps.ver.outputs.version }}
|
||||
make
|
||||
make test
|
||||
sudo make install
|
||||
```
|
||||
target-packages:
|
||||
name: Windows and macOS release gates
|
||||
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
|
||||
needs:
|
||||
- source-policy
|
||||
- build-and-test
|
||||
- strict-warnings
|
||||
- sanitizers
|
||||
- static-analysis
|
||||
- source-archive
|
||||
- debian-package
|
||||
- tumbleweed-rpm
|
||||
- gui-rpm-package
|
||||
- linux-portable
|
||||
- gui-portable
|
||||
uses: ./.github/workflows/cross-platform.yml
|
||||
permissions:
|
||||
contents: read
|
||||
|
|
|
|||
393
.github/workflows/cross-platform.yml
vendored
393
.github/workflows/cross-platform.yml
vendored
|
|
@ -1,180 +1,265 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
#
|
||||
# Cross-platform GUI + CLI binaries, built on REAL Windows and macOS runners.
|
||||
#
|
||||
# Why a dedicated workflow: the GUI is a PySide6/PyQt6 app and the CLI is
|
||||
# portable C11, but self-contained native installers (Windows .exe/.msi,
|
||||
# macOS .app/.dmg) can only be produced on the target OS. This workflow builds
|
||||
# them on GitHub's windows-latest and macos-latest runners and attaches them to
|
||||
# the GitHub release on a `v*` tag. Run it manually with "Run workflow"
|
||||
# (workflow_dispatch) to smoke-test the build before tagging.
|
||||
#
|
||||
# Artifacts produced:
|
||||
# Windows: vaptvupt.exe (CLI, mingw), vaptvupt-gui.exe (PyInstaller onefile),
|
||||
# VaptVupt-Setup-<ver>.exe (Inno Setup installer)
|
||||
# macOS: vaptvupt (CLI, universal where possible), VaptVupt-<ver>.dmg
|
||||
# All: vaptvupt-gui-<ver>-portable.zip (Python GUI + launchers)
|
||||
|
||||
name: cross-platform
|
||||
name: target release packages
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# ─────────────────────────── Windows ───────────────────────────
|
||||
windows:
|
||||
windows-x86_64:
|
||||
name: Windows x86_64 package and smoke test
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run: { shell: msys2 {0} }
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up MSYS2 (mingw gcc + make)
|
||||
uses: msys2/setup-msys2@v2
|
||||
- name: Check out the audited source
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
lfs: false
|
||||
submodules: false
|
||||
|
||||
- name: Install the Windows C toolchain
|
||||
uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0
|
||||
with:
|
||||
msystem: UCRT64
|
||||
update: true
|
||||
install: >-
|
||||
mingw-w64-ucrt-x86_64-binutils
|
||||
mingw-w64-ucrt-x86_64-gcc
|
||||
make
|
||||
bsdtar
|
||||
coreutils
|
||||
- name: Build CLI (vaptvupt.exe, source-only, C fallback crypto)
|
||||
run: |
|
||||
make CC=gcc WITH_SDK=0 -j2
|
||||
./vaptvupt.exe version || ./vaptvupt version
|
||||
cp "$(ls vaptvupt.exe vaptvupt 2>/dev/null | head -1)" vaptvupt.exe 2>/dev/null || true
|
||||
- name: Set up Python
|
||||
shell: pwsh
|
||||
run: |
|
||||
# Use the runner's native Python (not MSYS) for PyInstaller so the
|
||||
# produced .exe targets the standard Windows Python ABI.
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install PySide6 pyinstaller
|
||||
- name: Get version
|
||||
id: ver
|
||||
shell: pwsh
|
||||
run: |
|
||||
$ver = (Select-String -Path include/zupt.h -Pattern '^#define ZUPT_VERSION_STRING "([^"]+)"').Matches.Groups[1].Value
|
||||
"version=$ver" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
||||
- name: Bundle GUI with PyInstaller (vaptvupt-gui.exe)
|
||||
shell: pwsh
|
||||
run: |
|
||||
# onefile GUI that carries the CLI beside it via --add-binary.
|
||||
pyinstaller --noconfirm --onefile --windowed `
|
||||
--name vaptvupt-gui `
|
||||
--icon gui/assets/zupt-icon.png `
|
||||
--add-binary "vaptvupt.exe;." `
|
||||
--add-data "gui/assets/zupt-icon.png;assets" `
|
||||
gui/src/zupt_gui.py
|
||||
- name: Build Inno Setup installer
|
||||
shell: pwsh
|
||||
run: |
|
||||
choco install innosetup --no-progress -y
|
||||
& "$env:ChocolateyInstall\bin\ISCC.exe" `
|
||||
"/DAppVersion=${{ steps.ver.outputs.version }}" `
|
||||
packaging/windows/vaptvupt-gui.iss
|
||||
- name: Collect artifacts
|
||||
shell: pwsh
|
||||
run: |
|
||||
$v = "${{ steps.ver.outputs.version }}"
|
||||
New-Item -ItemType Directory -Force out | Out-Null
|
||||
Copy-Item vaptvupt.exe "out/vaptvupt-$v-windows-x86_64.exe"
|
||||
Copy-Item dist/vaptvupt-gui.exe "out/vaptvupt-gui-$v-windows-x86_64.exe"
|
||||
if (Test-Path "packaging/windows/Output") {
|
||||
Copy-Item packaging/windows/Output/*.exe "out/" -ErrorAction SilentlyContinue
|
||||
}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows
|
||||
path: out/*
|
||||
- name: Attach to release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: out/*
|
||||
diffutils
|
||||
file
|
||||
findutils
|
||||
git
|
||||
gzip
|
||||
make
|
||||
python
|
||||
tar
|
||||
unzip
|
||||
zip
|
||||
|
||||
# ─────────────────────────── macOS ───────────────────────────
|
||||
macos:
|
||||
- name: Audit source before building
|
||||
run: bash scripts/check-source-only.sh
|
||||
|
||||
- name: Build from source
|
||||
run: |
|
||||
test "$(uname -m)" = x86_64
|
||||
make clean
|
||||
make -j2 CC=gcc V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0
|
||||
|
||||
- name: Run the source-only distribution checks on Windows
|
||||
run: make CC=gcc V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 check
|
||||
|
||||
- name: Native CLI smoke and round-trip
|
||||
run: |
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
if [[ -x ./zupt.exe ]]; then
|
||||
exe=$PWD/zupt.exe
|
||||
elif [[ -x ./zupt ]]; then
|
||||
exe=$PWD/zupt
|
||||
else
|
||||
echo 'ZUPT executable was not produced' >&2
|
||||
exit 1
|
||||
fi
|
||||
version_output=$("$exe" --version)
|
||||
version_line=${version_output%%$'\n'*}
|
||||
read -r product reported_version _ <<< "$version_line"
|
||||
if [[ $product != zupt || $reported_version != "$version" ]]; then
|
||||
echo 'native Windows executable version does not match include/zupt.h' >&2
|
||||
exit 1
|
||||
fi
|
||||
"$exe" --help >/dev/null
|
||||
if "$exe" --definitely-invalid-option >/dev/null 2>&1; then
|
||||
echo 'invalid option returned success' >&2
|
||||
exit 1
|
||||
fi
|
||||
test_root=$(mktemp -d)
|
||||
trap 'chmod -R u+rwX "$test_root" 2>/dev/null || true; rm -rf -- "$test_root"' EXIT
|
||||
output_dir="$test_root/saída-安全"
|
||||
archive="$test_root/cópia-安全.zupt"
|
||||
emoji_name=$'emoji-\xF0\x9F\x98\x80.bin'
|
||||
mkdir -p "$test_root/input/subdir" "$output_dir"
|
||||
printf 'Windows release smoke test\n' > "$test_root/input/café.txt"
|
||||
printf 'UTF-8: café-安全\n' > "$test_root/input/subdir/ação-安全.txt"
|
||||
printf 'emoji filename\n' > "$test_root/input/subdir/$emoji_name"
|
||||
: > "$test_root/input/empty"
|
||||
dd if=/dev/urandom of="$test_root/input/subdir/random.bin" bs=4096 count=4 2>/dev/null
|
||||
(cd "$test_root" && "$exe" compress "$archive" input)
|
||||
"$exe" test "$archive"
|
||||
"$exe" list "$archive" > "$test_root/list.txt" 2>&1
|
||||
"$exe" extract -o "$output_dir" "$archive"
|
||||
diff -r "$test_root/input" "$output_dir/input"
|
||||
python3 - "$test_root/list.txt" <<'PY'
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
listing = pathlib.Path(sys.argv[1]).read_bytes()
|
||||
expected = {
|
||||
"Latin-1": bytes.fromhex("636166c3a92e747874"),
|
||||
"BMP": bytes.fromhex("61c3a7c3a36f2de5ae89e585a82e747874"),
|
||||
"non-BMP": bytes.fromhex("656d6f6a692df09f98802e62696e"),
|
||||
}
|
||||
missing = [label for label, name in expected.items() if name not in listing]
|
||||
if missing:
|
||||
raise SystemExit("list output is missing exact UTF-8 names: " +
|
||||
", ".join(missing))
|
||||
PY
|
||||
objdump -p "$exe" > "$test_root/imports.txt"
|
||||
if grep -Eqi '(vendor[/\\]|libvuptsdk|libpqvaptvupt|libgcc_s|libstdc\+\+|libwinpthread|msys-2[.]0|cygwin1)[^[:space:]]*[.]dll' \
|
||||
"$test_root/imports.txt"; then
|
||||
echo 'Windows binary imports a non-system or vendored runtime' >&2
|
||||
exit 1
|
||||
fi
|
||||
version_output=$(env PATH='/c/Windows/System32:/c/Windows' "$exe" --version)
|
||||
version_line=${version_output%%$'\n'*}
|
||||
read -r product reported_version _ <<< "$version_line"
|
||||
if [[ $product != zupt || $reported_version != "$version" ]]; then
|
||||
echo 'restricted-PATH Windows executable version does not match include/zupt.h' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Assemble Windows release files
|
||||
run: |
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
bundle="out/work/zupt-$version-windows-x86_64"
|
||||
mkdir -p out "$bundle"
|
||||
if [[ -x ./zupt.exe ]]; then source_exe=./zupt.exe; else source_exe=./zupt; fi
|
||||
install -m 0755 "$source_exe" "$bundle/zupt.exe"
|
||||
install -m 0644 README.md CHANGELOG.md LICENSE LICENSE-AGPL-3.0 LICENSE-GPL-3.0 \
|
||||
LICENSE-BSD-2-Clause LICENSE-BSD-3-Clause LICENSE-CC0-1.0 \
|
||||
NOTICE THIRD-PARTY-NOTICES.md "$bundle/"
|
||||
toolchain_prefix=${MINGW_PREFIX:-/ucrt64}
|
||||
install -m 0644 \
|
||||
"$toolchain_prefix/share/licenses/crt/COPYING" \
|
||||
"$bundle/MINGW-CRT-COPYING.txt"
|
||||
install -m 0644 \
|
||||
"$toolchain_prefix/share/licenses/crt/COPYING.MinGW-w64-runtime.txt" \
|
||||
"$bundle/COPYING.MinGW-w64-runtime.txt"
|
||||
install -m 0644 \
|
||||
"$toolchain_prefix/share/licenses/crt/COPYING.MinGW-w64.txt" \
|
||||
"$bundle/COPYING.MinGW-w64.txt"
|
||||
install -m 0644 \
|
||||
"$toolchain_prefix/share/licenses/gcc-libs/COPYING3" \
|
||||
"$bundle/GCC-COPYING3.txt"
|
||||
install -m 0644 \
|
||||
"$toolchain_prefix/share/licenses/gcc-libs/COPYING.RUNTIME" \
|
||||
"$bundle/GCC-RUNTIME-LIBRARY-EXCEPTION.txt"
|
||||
zip_path=$PWD/out/zupt-$version-windows-x86_64.zip
|
||||
(cd out/work && zip -9 -r "$zip_path" \
|
||||
"zupt-$version-windows-x86_64")
|
||||
|
||||
- name: Extract and functionally test the Windows ZIP
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
zip_path=$PWD/out/zupt-$version-windows-x86_64.zip
|
||||
unzip -t "$zip_path"
|
||||
extract_root=$(mktemp -d)
|
||||
cleanup() {
|
||||
chmod -R u+rwX "$extract_root" 2>/dev/null || true
|
||||
rm -rf -- "$extract_root"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
unzip -q "$zip_path" -d "$extract_root"
|
||||
for notice in MINGW-CRT-COPYING.txt COPYING.MinGW-w64-runtime.txt \
|
||||
COPYING.MinGW-w64.txt GCC-COPYING3.txt \
|
||||
GCC-RUNTIME-LIBRARY-EXCEPTION.txt; do
|
||||
test -s "$extract_root/zupt-$version-windows-x86_64/$notice"
|
||||
done
|
||||
packaged_exe=$extract_root/zupt-$version-windows-x86_64/zupt.exe
|
||||
test -x "$packaged_exe"
|
||||
version_output=$(env PATH='/c/Windows/System32:/c/Windows' \
|
||||
"$packaged_exe" --version)
|
||||
version_line=${version_output%%$'\n'*}
|
||||
read -r product reported_version _ <<< "$version_line"
|
||||
if [[ $product != zupt || $reported_version != "$version" ]]; then
|
||||
echo 'Windows ZIP executable version does not match include/zupt.h' >&2
|
||||
exit 1
|
||||
fi
|
||||
env PATH='/c/Windows/System32:/c/Windows' "$packaged_exe" --help >/dev/null
|
||||
mkdir -p "$extract_root/smoke/input" "$extract_root/smoke/saída-安全"
|
||||
printf 'Windows ZIP package test\n' > "$extract_root/smoke/input/payload-ação-😀.txt"
|
||||
(
|
||||
cd "$extract_root/smoke"
|
||||
env PATH='/c/Windows/System32:/c/Windows' \
|
||||
"$packaged_exe" compress cópia-安全.zupt input
|
||||
env PATH='/c/Windows/System32:/c/Windows' \
|
||||
"$packaged_exe" test cópia-安全.zupt
|
||||
env PATH='/c/Windows/System32:/c/Windows' \
|
||||
"$packaged_exe" extract -o saída-安全 cópia-安全.zupt
|
||||
)
|
||||
cmp "$extract_root/smoke/input/payload-ação-😀.txt" \
|
||||
"$extract_root/smoke/saída-安全/input/payload-ação-😀.txt"
|
||||
|
||||
- name: Upload tested Windows files
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: release-windows-x86_64
|
||||
path: out/*.zip
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
macos-native:
|
||||
name: macOS native DMG and installed-image test
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build CLI (vaptvupt, clang)
|
||||
run: |
|
||||
make CC=clang WITH_SDK=0 -j3
|
||||
./vaptvupt version
|
||||
- uses: actions/setup-python@v5
|
||||
with: { python-version: '3.12' }
|
||||
- name: Install GUI build deps
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install PySide6 pyinstaller
|
||||
brew install create-dmg || true
|
||||
- name: Get version
|
||||
id: ver
|
||||
run: echo "version=$(awk -F'\"' '/^#define ZUPT_VERSION_STRING/{print $2}' include/zupt.h)" >> "$GITHUB_OUTPUT"
|
||||
- name: Bundle GUI (.app) with PyInstaller
|
||||
run: |
|
||||
pyinstaller --noconfirm --windowed \
|
||||
--name "VaptVupt" \
|
||||
--add-binary "vaptvupt:." \
|
||||
--add-data "gui/assets/zupt-icon.png:assets" \
|
||||
gui/src/zupt_gui.py
|
||||
- name: Build .dmg
|
||||
run: |
|
||||
V="${{ steps.ver.outputs.version }}"
|
||||
create-dmg --volname "VaptVupt $V" --window-size 500 300 \
|
||||
--app-drop-link 350 120 --icon "VaptVupt.app" 150 120 \
|
||||
"VaptVupt-$V.dmg" "dist/VaptVupt.app" || \
|
||||
{ mkdir -p dmgroot && cp -R dist/VaptVupt.app dmgroot/ && \
|
||||
hdiutil create -volname "VaptVupt $V" -srcfolder dmgroot -ov -format UDZO "VaptVupt-$V.dmg"; }
|
||||
- name: Collect artifacts
|
||||
run: |
|
||||
V="${{ steps.ver.outputs.version }}"
|
||||
mkdir -p out
|
||||
cp vaptvupt "out/vaptvupt-$V-macos"
|
||||
cp "VaptVupt-$V.dmg" out/
|
||||
- uses: actions/upload-artifact@v4
|
||||
- name: Check out the audited source
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
name: macos
|
||||
path: out/*
|
||||
- name: Attach to release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: out/*
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
lfs: false
|
||||
submodules: false
|
||||
|
||||
# ─────────────── Portable GUI (works on every OS) ───────────────
|
||||
portable:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Get version
|
||||
id: ver
|
||||
run: echo "version=$(awk -F'\"' '/^#define ZUPT_VERSION_STRING/{print $2}' include/zupt.h)" >> "$GITHUB_OUTPUT"
|
||||
- name: Assemble portable package
|
||||
- name: Audit source before building
|
||||
run: bash scripts/check-source-only.sh
|
||||
|
||||
- name: Build and validate the native DMG
|
||||
run: |
|
||||
V="${{ steps.ver.outputs.version }}"
|
||||
D="vaptvupt-gui-$V-portable"
|
||||
mkdir -p "$D/assets"
|
||||
cp gui/src/zupt_gui.py "$D/"
|
||||
cp gui/assets/zupt-icon.png "$D/assets/"
|
||||
cp packaging/portable/vaptvupt-gui.bat "$D/"
|
||||
cp packaging/portable/vaptvupt-gui.command "$D/"
|
||||
cp packaging/portable/vaptvupt-gui.sh "$D/"
|
||||
cp packaging/portable/README.txt "$D/"
|
||||
chmod +x "$D/vaptvupt-gui.command" "$D/vaptvupt-gui.sh"
|
||||
zip -r "$D.zip" "$D"
|
||||
- uses: actions/upload-artifact@v4
|
||||
mkdir -p out
|
||||
DIST_DIR="$PWD/out" RUN_CHECKS=1 bash packaging/build-dmg.sh
|
||||
|
||||
- name: Mount and functionally test the packaged binary
|
||||
run: |
|
||||
dmg=$(find out -maxdepth 1 -type f -name '*.dmg' -print -quit)
|
||||
test -n "$dmg"
|
||||
mount_point=$(mktemp -d)
|
||||
cleanup() {
|
||||
hdiutil detach "$mount_point" >/dev/null 2>&1 || true
|
||||
chmod -R u+rwX "$mount_point" 2>/dev/null || true
|
||||
rm -rf -- "$mount_point"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
hdiutil attach -nobrowse -readonly -mountpoint "$mount_point" "$dmg" >/dev/null
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' \
|
||||
include/zupt.h)
|
||||
packaged_binary=$mount_point/ZUPT.app/Contents/MacOS/zupt
|
||||
version_output=$("$packaged_binary" --version)
|
||||
version_line=${version_output%%$'\n'*}
|
||||
read -r product reported_version _ <<< "$version_line"
|
||||
if [[ $product != zupt || $reported_version != "$version" ]]; then
|
||||
echo 'mounted macOS executable version does not match include/zupt.h' >&2
|
||||
exit 1
|
||||
fi
|
||||
bash packaging/build-dmg.sh --test-binary \
|
||||
"$packaged_binary"
|
||||
hdiutil detach "$mount_point"
|
||||
trap - EXIT HUP INT TERM
|
||||
rmdir "$mount_point"
|
||||
|
||||
- name: Upload tested macOS DMG
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: portable
|
||||
path: vaptvupt-gui-*-portable.zip
|
||||
- name: Attach to release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: vaptvupt-gui-*-portable.zip
|
||||
name: release-macos-native
|
||||
path: out/*.dmg
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
|
|
|||
656
.github/workflows/promote-release.yml
vendored
Normal file
656
.github/workflows/promote-release.yml
vendored
Normal file
|
|
@ -0,0 +1,656 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
|
||||
name: Promote a tested release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
source_run_id:
|
||||
description: Successful manually dispatched CI run that produced the assets
|
||||
required: true
|
||||
type: number
|
||||
tag:
|
||||
description: Existing annotated release tag, for example v5.2.8
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: promote-release-${{ inputs.tag }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
promote:
|
||||
name: Promote tested assets to the canonical GitHub release
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 45
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
steps:
|
||||
- name: Validate the tag and source CI run through the GitHub API
|
||||
id: provenance
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
SOURCE_RUN_ID: ${{ inputs.source_run_id }}
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
set +x
|
||||
umask 077
|
||||
[[ $SOURCE_RUN_ID =~ ^[1-9][0-9]*$ ]] || {
|
||||
echo 'source_run_id must be a positive integer' >&2
|
||||
exit 1
|
||||
}
|
||||
[[ $RELEASE_TAG =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || {
|
||||
echo 'tag must have the form vX.Y.Z' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
tag_ref_api="repos/$GITHUB_REPOSITORY/git/ref/tags/$RELEASE_TAG"
|
||||
tag_object_type=$(gh api "$tag_ref_api" --jq '.object.type')
|
||||
tag_object_sha=$(gh api "$tag_ref_api" --jq '.object.sha')
|
||||
[[ $tag_object_type == tag && $tag_object_sha =~ ^[0-9a-f]{40}$ ]] || {
|
||||
echo 'GitHub release ref is not an annotated tag' >&2
|
||||
exit 1
|
||||
}
|
||||
tag_object_api="repos/$GITHUB_REPOSITORY/git/tags/$tag_object_sha"
|
||||
target_type=$(gh api "$tag_object_api" --jq '.object.type')
|
||||
peeled_sha=$(gh api "$tag_object_api" --jq '.object.sha')
|
||||
[[ $target_type == commit && $peeled_sha =~ ^[0-9a-f]{40}$ ]] || {
|
||||
echo 'annotated tag does not point directly to a commit' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
run_api="repos/$GITHUB_REPOSITORY/actions/runs/$SOURCE_RUN_ID"
|
||||
run_status=$(gh api "$run_api" --jq '.status')
|
||||
run_conclusion=$(gh api "$run_api" --jq '.conclusion')
|
||||
run_event=$(gh api "$run_api" --jq '.event')
|
||||
run_head_branch=$(gh api "$run_api" --jq '.head_branch // ""')
|
||||
run_workflow_id=$(gh api "$run_api" --jq '.workflow_id')
|
||||
run_sha=$(gh api "$run_api" --jq '.head_sha')
|
||||
run_repository=$(gh api "$run_api" --jq '.head_repository.full_name // ""')
|
||||
workflow_path=$(gh api \
|
||||
"repos/$GITHUB_REPOSITORY/actions/workflows/$run_workflow_id" \
|
||||
--jq '.path')
|
||||
[[ $run_status == completed && $run_conclusion == success ]] || {
|
||||
echo 'source CI run is not completed successfully' >&2
|
||||
exit 1
|
||||
}
|
||||
[[ $run_event == workflow_dispatch ]] || {
|
||||
echo 'source CI run must have been started with workflow_dispatch' >&2
|
||||
exit 1
|
||||
}
|
||||
[[ $run_head_branch == "$RELEASE_TAG" ]] || {
|
||||
echo 'source CI run must have been dispatched from the release tag' >&2
|
||||
exit 1
|
||||
}
|
||||
[[ $workflow_path == .github/workflows/ci.yml ]] || {
|
||||
echo 'source run did not execute .github/workflows/ci.yml' >&2
|
||||
exit 1
|
||||
}
|
||||
[[ $run_repository == "$GITHUB_REPOSITORY" ]] || {
|
||||
echo 'source CI run belongs to a different head repository' >&2
|
||||
exit 1
|
||||
}
|
||||
[[ $run_sha =~ ^[0-9a-f]{40}$ && $run_sha == "$peeled_sha" ]] || {
|
||||
echo 'source CI head SHA does not match the peeled release tag' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
artifact_json=$RUNNER_TEMP/source-run-artifacts.json
|
||||
gh api "$run_api/artifacts?per_page=100" > "$artifact_json"
|
||||
python3 - "$artifact_json" <<'PY'
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
payload = json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))
|
||||
expected = {
|
||||
"release-source",
|
||||
"release-deb",
|
||||
"release-rpm",
|
||||
"release-gui-deb",
|
||||
"release-gui-rpm",
|
||||
"release-linux-x86_64",
|
||||
"release-gui-portable",
|
||||
"release-windows-x86_64",
|
||||
"release-macos-native",
|
||||
}
|
||||
artifacts = payload.get("artifacts", [])
|
||||
names = [artifact.get("name", "") for artifact in artifacts]
|
||||
if payload.get("total_count") != len(expected):
|
||||
raise SystemExit("source CI run artifact count mismatch")
|
||||
if set(names) != expected or len(names) != len(set(names)):
|
||||
raise SystemExit("source CI run artifact-name allowlist mismatch")
|
||||
if any(artifact.get("expired") for artifact in artifacts):
|
||||
raise SystemExit("one or more source CI artifacts have expired")
|
||||
PY
|
||||
{
|
||||
printf 'head_sha=%s\n' "$peeled_sha"
|
||||
printf 'tag_object_sha=%s\n' "$tag_object_sha"
|
||||
printf 'tag=%s\n' "$RELEASE_TAG"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Check out the exact tested commit without persisted credentials
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: ${{ steps.provenance.outputs.head_sha }}
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
lfs: false
|
||||
submodules: false
|
||||
|
||||
- name: Confirm the local annotated tag and source version
|
||||
id: release
|
||||
env:
|
||||
RELEASE_TAG: ${{ steps.provenance.outputs.tag }}
|
||||
EXPECTED_SHA: ${{ steps.provenance.outputs.head_sha }}
|
||||
EXPECTED_TAG_OBJECT: ${{ steps.provenance.outputs.tag_object_sha }}
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
[[ $(git rev-parse HEAD) == "$EXPECTED_SHA" ]] || {
|
||||
echo 'checked-out commit differs from the validated source run' >&2
|
||||
exit 1
|
||||
}
|
||||
[[ $(git cat-file -t "refs/tags/$RELEASE_TAG") == tag ]] || {
|
||||
echo 'checked-out release ref is not an annotated tag' >&2
|
||||
exit 1
|
||||
}
|
||||
[[ $(git rev-parse "refs/tags/$RELEASE_TAG") == "$EXPECTED_TAG_OBJECT" ]] || {
|
||||
echo 'local annotated tag object differs from the validated GitHub tag' >&2
|
||||
exit 1
|
||||
}
|
||||
[[ $(git rev-parse "$RELEASE_TAG^{commit}") == "$EXPECTED_SHA" ]] || {
|
||||
echo 'local peeled tag does not match the tested commit' >&2
|
||||
exit 1
|
||||
}
|
||||
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' \
|
||||
include/zupt.h)
|
||||
[[ -n $version && $RELEASE_TAG == "v$version" ]] || {
|
||||
echo 'tag does not match include/zupt.h' >&2
|
||||
exit 1
|
||||
}
|
||||
printf 'version=%s\n' "$version" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Install validation tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y file libarchive-tools python3 python3-pyqt6 rpm unzip xz-utils
|
||||
|
||||
- name: Download the exact source artifact from the validated run
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: release-source
|
||||
path: ${{ runner.temp }}/incoming/release-source
|
||||
repository: ${{ github.repository }}
|
||||
run-id: ${{ inputs.source_run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Download the exact DEB artifact from the validated run
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: release-deb
|
||||
path: ${{ runner.temp }}/incoming/release-deb
|
||||
repository: ${{ github.repository }}
|
||||
run-id: ${{ inputs.source_run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Download the exact RPM artifact from the validated run
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: release-rpm
|
||||
path: ${{ runner.temp }}/incoming/release-rpm
|
||||
repository: ${{ github.repository }}
|
||||
run-id: ${{ inputs.source_run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Download the exact GUI DEB artifact from the validated run
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: release-gui-deb
|
||||
path: ${{ runner.temp }}/incoming/release-gui-deb
|
||||
repository: ${{ github.repository }}
|
||||
run-id: ${{ inputs.source_run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Download the exact GUI RPM artifacts from the validated run
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: release-gui-rpm
|
||||
path: ${{ runner.temp }}/incoming/release-gui-rpm
|
||||
repository: ${{ github.repository }}
|
||||
run-id: ${{ inputs.source_run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Download the exact Linux tar.xz artifact from the validated run
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: release-linux-x86_64
|
||||
path: ${{ runner.temp }}/incoming/release-linux-x86_64
|
||||
repository: ${{ github.repository }}
|
||||
run-id: ${{ inputs.source_run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Download the exact portable GUI source bundle
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: release-gui-portable
|
||||
path: ${{ runner.temp }}/incoming/release-gui-portable
|
||||
repository: ${{ github.repository }}
|
||||
run-id: ${{ inputs.source_run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Download the exact Windows artifact from the validated run
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: release-windows-x86_64
|
||||
path: ${{ runner.temp }}/incoming/release-windows-x86_64
|
||||
repository: ${{ github.repository }}
|
||||
run-id: ${{ inputs.source_run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Download the exact macOS artifact from the validated run
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: release-macos-native
|
||||
path: ${{ runner.temp }}/incoming/release-macos-native
|
||||
repository: ${{ github.repository }}
|
||||
run-id: ${{ inputs.source_run_id }}
|
||||
github-token: ${{ github.token }}
|
||||
|
||||
- name: Enforce the allowlist and validate every release format
|
||||
env:
|
||||
RELEASE_TAG: ${{ steps.provenance.outputs.tag }}
|
||||
VERSION: ${{ steps.release.outputs.version }}
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
umask 077
|
||||
export LC_ALL=C
|
||||
incoming=$RUNNER_TEMP/incoming
|
||||
asset_dir=$RUNNER_TEMP/release-assets
|
||||
mkdir -p "$asset_dir"
|
||||
|
||||
artifact_names=(
|
||||
release-source
|
||||
release-deb
|
||||
release-rpm
|
||||
release-gui-deb
|
||||
release-gui-rpm
|
||||
release-linux-x86_64
|
||||
release-gui-portable
|
||||
release-windows-x86_64
|
||||
release-macos-native
|
||||
)
|
||||
expected_dirs=$RUNNER_TEMP/artifact-dirs.expected
|
||||
actual_dirs=$RUNNER_TEMP/artifact-dirs.actual
|
||||
printf '%s\0' "${artifact_names[@]}" | LC_ALL=C sort -z > "$expected_dirs"
|
||||
find "$incoming" -mindepth 1 -maxdepth 1 -type d -printf '%f\0' | \
|
||||
LC_ALL=C sort -z > "$actual_dirs"
|
||||
cmp "$expected_dirs" "$actual_dirs" || {
|
||||
echo 'downloaded artifact directory allowlist mismatch' >&2
|
||||
exit 1
|
||||
}
|
||||
if find "$incoming" -mindepth 1 -maxdepth 1 ! -type d -print -quit | \
|
||||
grep -q .; then
|
||||
echo 'unexpected non-directory entry in artifact download root' >&2
|
||||
exit 1
|
||||
fi
|
||||
if find "$incoming" -mindepth 2 ! -type f -print -quit | grep -q .; then
|
||||
echo 'artifact contains a directory, symlink, or special file' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source_name="zupt-$VERSION.tar.gz"
|
||||
source_sidecar="$source_name.sha256"
|
||||
deb_name="zupt_${VERSION}_amd64.deb"
|
||||
rpm_name="zupt-$VERSION-0.x86_64.rpm"
|
||||
srpm_name="zupt-$VERSION-0.src.rpm"
|
||||
gui_deb_name="zupt-gui_${VERSION}_all.deb"
|
||||
gui_rpm_name="zupt-gui-$VERSION-1.noarch.rpm"
|
||||
gui_srpm_name="zupt-gui-$VERSION-1.src.rpm"
|
||||
linux_tar_name="zupt-$VERSION-linux-x86_64.tar.xz"
|
||||
gui_portable_name="zupt-gui-$VERSION-portable.zip"
|
||||
windows_zip_name="zupt-$VERSION-windows-x86_64.zip"
|
||||
dmg_relative=()
|
||||
for arch in x86_64 arm64; do
|
||||
candidate="release-macos-native/ZUPT-$VERSION-macOS-$arch.dmg"
|
||||
[[ ! -f $incoming/$candidate || -L $incoming/$candidate ]] || \
|
||||
dmg_relative+=("$candidate")
|
||||
done
|
||||
((${#dmg_relative[@]} == 1)) || {
|
||||
echo 'expected exactly one native macOS DMG' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
expected_relative=(
|
||||
"release-source/$source_name"
|
||||
"release-source/$source_sidecar"
|
||||
"release-deb/$deb_name"
|
||||
"release-rpm/$rpm_name"
|
||||
"release-rpm/$srpm_name"
|
||||
"release-gui-deb/$gui_deb_name"
|
||||
"release-gui-rpm/$gui_rpm_name"
|
||||
"release-gui-rpm/$gui_srpm_name"
|
||||
"release-linux-x86_64/$linux_tar_name"
|
||||
"release-gui-portable/$gui_portable_name"
|
||||
"release-windows-x86_64/$windows_zip_name"
|
||||
"${dmg_relative[0]}"
|
||||
)
|
||||
expected_relative_list=$RUNNER_TEMP/artifact-files.expected
|
||||
actual_relative_list=$RUNNER_TEMP/artifact-files.actual
|
||||
printf '%s\0' "${expected_relative[@]}" | LC_ALL=C sort -z \
|
||||
> "$expected_relative_list"
|
||||
find "$incoming" -mindepth 2 -type f -printf '%P\0' | LC_ALL=C sort -z \
|
||||
> "$actual_relative_list"
|
||||
cmp "$expected_relative_list" "$actual_relative_list" || {
|
||||
echo 'downloaded file allowlist mismatch' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
expected_assets=()
|
||||
for relative in "${expected_relative[@]}"; do
|
||||
name=${relative#*/}
|
||||
cp -- "$incoming/$relative" "$asset_dir/$name"
|
||||
expected_assets+=("$name")
|
||||
done
|
||||
expected_list=$RUNNER_TEMP/release-assets.expected
|
||||
printf '%s\0' "${expected_assets[@]}" | LC_ALL=C sort -z > "$expected_list"
|
||||
|
||||
source_tar=$asset_dir/$source_name
|
||||
sidecar=$asset_dir/$source_sidecar
|
||||
actual_source_sha=$(sha256sum "$source_tar" | awk '{print $1}')
|
||||
[[ $(<"$sidecar") == "$actual_source_sha $source_name" ]] || {
|
||||
echo 'source archive sidecar is not the exact expected SHA-256 record' >&2
|
||||
exit 1
|
||||
}
|
||||
(cd "$asset_dir" && sha256sum -c -- "$source_sidecar")
|
||||
file "$source_tar" | grep -Eqi 'gzip compressed data'
|
||||
tar -tzf "$source_tar" >/dev/null
|
||||
bash scripts/check-source-only.sh --archive "$source_tar"
|
||||
archive_version=$(tar -xOf "$source_tar" \
|
||||
"zupt-$VERSION/include/zupt.h" | sed -n \
|
||||
's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p')
|
||||
[[ $archive_version == "$VERSION" && $RELEASE_TAG == "v$archive_version" ]] || {
|
||||
echo 'source archive version does not match the release tag' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
deb=$asset_dir/$deb_name
|
||||
dpkg-deb --info "$deb" >/dev/null
|
||||
[[ $(dpkg-deb -f "$deb" Package) == zupt ]]
|
||||
[[ $(dpkg-deb -f "$deb" Version) == "$VERSION" ]]
|
||||
[[ $(dpkg-deb -f "$deb" Architecture) == amd64 ]]
|
||||
|
||||
rpm_file=$asset_dir/$rpm_name
|
||||
[[ $(rpm -qp --qf '%{NAME}' "$rpm_file") == zupt ]]
|
||||
[[ $(rpm -qp --qf '%{VERSION}' "$rpm_file") == "$VERSION" ]]
|
||||
[[ $(rpm -qp --qf '%{RELEASE}' "$rpm_file") == 0 ]]
|
||||
[[ $(rpm -qp --qf '%{ARCH}' "$rpm_file") == x86_64 ]]
|
||||
[[ $(rpm -qp --qf '%{SOURCEPACKAGE}' "$rpm_file") == '(none)' ]]
|
||||
[[ $(rpm -qp --qf '%{SOURCERPM}' "$rpm_file") == "$srpm_name" ]]
|
||||
srpm=$asset_dir/$srpm_name
|
||||
[[ $(rpm -qp --qf '%{NAME}' "$srpm") == zupt ]]
|
||||
[[ $(rpm -qp --qf '%{VERSION}' "$srpm") == "$VERSION" ]]
|
||||
[[ $(rpm -qp --qf '%{RELEASE}' "$srpm") == 0 ]]
|
||||
[[ $(rpm -qp --qf '%{SOURCEPACKAGE}' "$srpm") == 1 ]]
|
||||
[[ $(rpm -qp --qf '%{SOURCERPM}' "$srpm") == '(none)' ]]
|
||||
[[ $(rpm -qpl "$srpm" | wc -l) -eq 2 ]]
|
||||
rpm -qpl "$srpm" | grep -Fx "zupt-$VERSION.tar.gz"
|
||||
rpm -qpl "$srpm" | grep -Fx zupt.spec
|
||||
|
||||
gui_deb=$asset_dir/$gui_deb_name
|
||||
dpkg-deb --info "$gui_deb" >/dev/null
|
||||
[[ $(dpkg-deb -f "$gui_deb" Package) == zupt-gui ]]
|
||||
[[ $(dpkg-deb -f "$gui_deb" Version) == "$VERSION" ]]
|
||||
[[ $(dpkg-deb -f "$gui_deb" Architecture) == all ]]
|
||||
|
||||
gui_rpm=$asset_dir/$gui_rpm_name
|
||||
[[ $(rpm -qp --qf '%{NAME}' "$gui_rpm") == zupt-gui ]]
|
||||
[[ $(rpm -qp --qf '%{VERSION}' "$gui_rpm") == "$VERSION" ]]
|
||||
[[ $(rpm -qp --qf '%{RELEASE}' "$gui_rpm") == 1 ]]
|
||||
[[ $(rpm -qp --qf '%{ARCH}' "$gui_rpm") == noarch ]]
|
||||
[[ $(rpm -qp --qf '%{SOURCEPACKAGE}' "$gui_rpm") == '(none)' ]]
|
||||
[[ $(rpm -qp --qf '%{SOURCERPM}' "$gui_rpm") == "$gui_srpm_name" ]]
|
||||
rpm -qp --requires "$gui_rpm" | grep -Fx "zupt >= $VERSION"
|
||||
gui_srpm=$asset_dir/$gui_srpm_name
|
||||
[[ $(rpm -qp --qf '%{NAME}' "$gui_srpm") == zupt-gui ]]
|
||||
[[ $(rpm -qp --qf '%{VERSION}' "$gui_srpm") == "$VERSION" ]]
|
||||
[[ $(rpm -qp --qf '%{RELEASE}' "$gui_srpm") == 1 ]]
|
||||
[[ $(rpm -qp --qf '%{SOURCEPACKAGE}' "$gui_srpm") == 1 ]]
|
||||
[[ $(rpm -qp --qf '%{SOURCERPM}' "$gui_srpm") == '(none)' ]]
|
||||
[[ $(rpm -qpl "$gui_srpm" | wc -l) -eq 2 ]]
|
||||
rpm -qpl "$gui_srpm" | grep -Fx "zupt-gui-$VERSION.tar.gz"
|
||||
rpm -qpl "$gui_srpm" | grep -Fx zupt-gui.spec
|
||||
|
||||
linux_tar=$asset_dir/$linux_tar_name
|
||||
python3 - "$linux_tar" "zupt-$VERSION-linux-x86_64" <<'PY'
|
||||
import pathlib
|
||||
import sys
|
||||
import tarfile
|
||||
|
||||
archive = pathlib.Path(sys.argv[1])
|
||||
root = sys.argv[2]
|
||||
expected_files = {
|
||||
"zupt", "README.md", "CHANGELOG.md", "SECURITY.md",
|
||||
"THREAT_MODEL.md", "LICENSE", "LICENSE-AGPL-3.0",
|
||||
"LICENSE-GPL-3.0", "LICENSE-BSD-2-Clause",
|
||||
"LICENSE-BSD-3-Clause", "LICENSE-CC0-1.0", "NOTICE",
|
||||
"THIRD-PARTY-NOTICES.md",
|
||||
}
|
||||
with tarfile.open(archive, "r:xz") as package:
|
||||
members = package.getmembers()
|
||||
names = [member.name for member in members]
|
||||
if len(names) != len(set(names)):
|
||||
raise SystemExit("duplicate Linux tar member")
|
||||
actual_files = set()
|
||||
for member in members:
|
||||
path = pathlib.PurePosixPath(member.name)
|
||||
if (path.is_absolute() or ".." in path.parts or not path.parts or
|
||||
path.parts[0] != root or member.issym() or member.islnk() or
|
||||
not (member.isdir() or member.isfile())):
|
||||
raise SystemExit("unsafe Linux tar member")
|
||||
if member.isfile():
|
||||
actual_files.add("/".join(path.parts[1:]))
|
||||
if actual_files != expected_files:
|
||||
raise SystemExit("Linux tar member allowlist mismatch")
|
||||
PY
|
||||
linux_extract=$RUNNER_TEMP/linux-package
|
||||
mkdir -p "$linux_extract"
|
||||
tar -xJf "$linux_tar" -C "$linux_extract"
|
||||
linux_binary="$linux_extract/zupt-$VERSION-linux-x86_64/zupt"
|
||||
file "$linux_binary" | grep -Eqi 'ELF.*executable'
|
||||
bash scripts/test-installed-zupt.sh "$linux_binary"
|
||||
|
||||
gui_portable=$asset_dir/$gui_portable_name
|
||||
python3 - "$gui_portable" "zupt-gui-$VERSION-portable" <<'PY'
|
||||
import pathlib
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
archive = pathlib.Path(sys.argv[1])
|
||||
root = sys.argv[2]
|
||||
expected = {
|
||||
f"{root}/", f"{root}/assets/", f"{root}/zupt_gui.py",
|
||||
f"{root}/zupt-gui.sh", f"{root}/zupt-gui.command",
|
||||
f"{root}/zupt-gui.bat", f"{root}/README.txt",
|
||||
f"{root}/assets/zupt-icon.png", f"{root}/assets/zupt.ico",
|
||||
f"{root}/LICENSE-AGPL-3.0", f"{root}/LICENSE-GUI",
|
||||
f"{root}/ASSET-PROVENANCE.md", f"{root}/CHANGELOG.md",
|
||||
}
|
||||
with zipfile.ZipFile(archive) as package:
|
||||
names = package.namelist()
|
||||
if len(names) != len(set(names)) or set(names) != expected:
|
||||
raise SystemExit("portable GUI ZIP member allowlist mismatch")
|
||||
for name in names:
|
||||
path = pathlib.PurePosixPath(name)
|
||||
if path.is_absolute() or ".." in path.parts or path.parts[0] != root:
|
||||
raise SystemExit("unsafe portable GUI ZIP member")
|
||||
PY
|
||||
bash scripts/check-source-only.sh --archive "$gui_portable"
|
||||
gui_extract=$RUNNER_TEMP/gui-portable
|
||||
mkdir -p "$gui_extract"
|
||||
unzip -q "$gui_portable" -d "$gui_extract"
|
||||
QT_QPA_PLATFORM=offscreen ZUPT_BIN="$linux_binary" \
|
||||
"$gui_extract/zupt-gui-$VERSION-portable/zupt-gui.sh" --version | \
|
||||
grep -Fx "zupt-gui $VERSION"
|
||||
|
||||
windows_zip=$asset_dir/$windows_zip_name
|
||||
unzip -t "$windows_zip" >/dev/null
|
||||
python3 - "$windows_zip" "zupt-$VERSION-windows-x86_64" <<'PY'
|
||||
import pathlib
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
archive = pathlib.Path(sys.argv[1])
|
||||
root = sys.argv[2]
|
||||
with zipfile.ZipFile(archive) as package:
|
||||
names = package.namelist()
|
||||
if len(names) != len(set(names)):
|
||||
raise SystemExit("duplicate Windows ZIP member")
|
||||
expected = {
|
||||
f"{root}/",
|
||||
f"{root}/zupt.exe",
|
||||
f"{root}/README.md",
|
||||
f"{root}/CHANGELOG.md",
|
||||
f"{root}/LICENSE",
|
||||
f"{root}/LICENSE-AGPL-3.0",
|
||||
f"{root}/LICENSE-GPL-3.0",
|
||||
f"{root}/LICENSE-BSD-2-Clause",
|
||||
f"{root}/LICENSE-BSD-3-Clause",
|
||||
f"{root}/LICENSE-CC0-1.0",
|
||||
f"{root}/NOTICE",
|
||||
f"{root}/THIRD-PARTY-NOTICES.md",
|
||||
f"{root}/MINGW-CRT-COPYING.txt",
|
||||
f"{root}/COPYING.MinGW-w64-runtime.txt",
|
||||
f"{root}/COPYING.MinGW-w64.txt",
|
||||
f"{root}/GCC-COPYING3.txt",
|
||||
f"{root}/GCC-RUNTIME-LIBRARY-EXCEPTION.txt",
|
||||
}
|
||||
if set(names) != expected:
|
||||
raise SystemExit("Windows ZIP member allowlist mismatch")
|
||||
for name in names:
|
||||
path = pathlib.PurePosixPath(name)
|
||||
if (path.is_absolute() or "\\" in name or ".." in path.parts or
|
||||
not path.parts or path.parts[0] != root):
|
||||
raise SystemExit("unsafe or unexpected Windows ZIP member")
|
||||
executable = f"{root}/zupt.exe"
|
||||
if names.count(executable) != 1:
|
||||
raise SystemExit("Windows ZIP executable is missing or duplicated")
|
||||
for notice in (
|
||||
f"{root}/MINGW-CRT-COPYING.txt",
|
||||
f"{root}/COPYING.MinGW-w64-runtime.txt",
|
||||
f"{root}/COPYING.MinGW-w64.txt",
|
||||
f"{root}/GCC-COPYING3.txt",
|
||||
f"{root}/GCC-RUNTIME-LIBRARY-EXCEPTION.txt",
|
||||
):
|
||||
if not package.read(notice):
|
||||
raise SystemExit("Windows toolchain notice is empty")
|
||||
PY
|
||||
unzip -p "$windows_zip" \
|
||||
"zupt-$VERSION-windows-x86_64/zupt.exe" \
|
||||
> "$RUNNER_TEMP/windows-zip-zupt.exe"
|
||||
python3 - "$RUNNER_TEMP/windows-zip-zupt.exe" <<'PY'
|
||||
import pathlib
|
||||
import struct
|
||||
import sys
|
||||
|
||||
executable = pathlib.Path(sys.argv[1])
|
||||
with executable.open("rb") as stream:
|
||||
header = stream.read(64)
|
||||
if len(header) != 64 or header[:2] != b"MZ":
|
||||
raise SystemExit("Windows ZIP executable lacks MZ magic")
|
||||
pe_offset = struct.unpack_from("<I", header, 0x3C)[0]
|
||||
stream.seek(pe_offset)
|
||||
if stream.read(4) != b"PE\0\0":
|
||||
raise SystemExit("Windows ZIP executable lacks PE signature")
|
||||
PY
|
||||
|
||||
dmg_name=${dmg_relative[0]#*/}
|
||||
dmg=$asset_dir/$dmg_name
|
||||
python3 - "$dmg" <<'PY'
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
image = pathlib.Path(sys.argv[1])
|
||||
with image.open("rb") as stream:
|
||||
stream.seek(-512, 2)
|
||||
if stream.read(4) != b"koly":
|
||||
raise SystemExit("DMG lacks the UDIF trailer magic")
|
||||
PY
|
||||
|
||||
checksum_tmp=$RUNNER_TEMP/SHA256SUMS
|
||||
(cd "$asset_dir" && xargs -0 sha256sum < "$expected_list") \
|
||||
> "$checksum_tmp"
|
||||
mv "$checksum_tmp" "$asset_dir/SHA256SUMS"
|
||||
(cd "$asset_dir" && sha256sum -c SHA256SUMS)
|
||||
cp "$expected_list" "$RUNNER_TEMP/release-assets.list"
|
||||
printf 'SHA256SUMS\0' >> "$RUNNER_TEMP/release-assets.list"
|
||||
echo 'All downloaded release assets match the exact allowlist and formats.'
|
||||
|
||||
- name: Refuse to mutate an existing GitHub release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RELEASE_TAG: ${{ steps.provenance.outputs.tag }}
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
set +x
|
||||
umask 077
|
||||
existing_tags=$RUNNER_TEMP/github-release-tags
|
||||
gh api --paginate "repos/$GITHUB_REPOSITORY/releases" \
|
||||
--jq '.[].tag_name' > "$existing_tags"
|
||||
if grep -Fxq -- "$RELEASE_TAG" "$existing_tags"; then
|
||||
echo 'GitHub release already exists; refusing to replace or add assets' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Publish the already-tested byte-identical asset set
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RELEASE_TAG: ${{ steps.provenance.outputs.tag }}
|
||||
RELEASE_COMMIT: ${{ steps.provenance.outputs.head_sha }}
|
||||
VERSION: ${{ steps.release.outputs.version }}
|
||||
SOURCE_RUN_ID: ${{ inputs.source_run_id }}
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
set +x
|
||||
umask 077
|
||||
asset_dir=$RUNNER_TEMP/release-assets
|
||||
(cd "$asset_dir" && sha256sum -c SHA256SUMS)
|
||||
mapfile -d '' -t asset_names < "$RUNNER_TEMP/release-assets.list"
|
||||
release_assets=()
|
||||
for name in "${asset_names[@]}"; do
|
||||
path=$asset_dir/$name
|
||||
[[ -f $path && ! -L $path ]] || {
|
||||
printf 'validated release asset disappeared or changed type: %q\n' \
|
||||
"$name" >&2
|
||||
exit 1
|
||||
}
|
||||
release_assets+=("$path")
|
||||
done
|
||||
cat > "$RUNNER_TEMP/release-notes.md" <<EOF
|
||||
ZUPT $VERSION was built and tested by manually dispatched CI run
|
||||
https://github.com/$GITHUB_REPOSITORY/actions/runs/$SOURCE_RUN_ID
|
||||
for annotated tag $RELEASE_TAG at commit $RELEASE_COMMIT. The job
|
||||
definitions and logs in that run record the runner images,
|
||||
architectures, toolchains, results, and explicit skips.
|
||||
|
||||
The attached source archive, CLI DEB/RPM/source RPM, GUI
|
||||
DEB/RPM/source RPM, notice-bearing Linux x86_64 tar.xz, source-only
|
||||
portable GUI ZIP, Windows CLI ZIP, and native macOS CLI DMG are the
|
||||
exact artifacts validated by that run. SHA256SUMS records every
|
||||
attached payload asset. GitHub is the canonical upstream release.
|
||||
|
||||
Binary packages are release-page assets only. The Git tree and source
|
||||
archive remain source-only, built with WITH_SDK=0 and WITH_PQBOX=0.
|
||||
AppImage and bare executables are intentionally excluded: the former
|
||||
lacks an audited runtime source/relink handoff, while the latter does
|
||||
not carry the required license and notice payload beside the program.
|
||||
EOF
|
||||
gh release create "$RELEASE_TAG" "${release_assets[@]}" \
|
||||
--repo "$GITHUB_REPOSITORY" --draft --verify-tag \
|
||||
--title "ZUPT $VERSION" \
|
||||
--notes-file "$RUNNER_TEMP/release-notes.md"
|
||||
gh release edit "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --draft=false
|
||||
44
.gitignore
vendored
44
.gitignore
vendored
|
|
@ -1,39 +1,65 @@
|
|||
# Build outputs
|
||||
/vaptvupt
|
||||
/zupt
|
||||
# Optional compatibility symlink produced by legacy-alias builds.
|
||||
/vaptvupt
|
||||
*.o
|
||||
*.obj
|
||||
*.a
|
||||
*.la
|
||||
*.so
|
||||
*.so.*
|
||||
*.dll
|
||||
*.dylib
|
||||
*.exe
|
||||
*.obj
|
||||
*.com
|
||||
*.class
|
||||
*.jar
|
||||
*.war
|
||||
*.wasm
|
||||
*.lib
|
||||
*.pdb
|
||||
*.out
|
||||
*.bin
|
||||
*.elf
|
||||
*.ko
|
||||
*.mod
|
||||
|
||||
# Python bytecode caches
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
|
||||
# Coverage / profiling
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.gcov
|
||||
*.profraw
|
||||
*.profdata
|
||||
/coverage/
|
||||
|
||||
# CMake / out-of-tree build dirs
|
||||
/build/
|
||||
/dist/
|
||||
/out/
|
||||
/target/
|
||||
/sdk/build/
|
||||
/cmake-build-*/
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
|
||||
# Distribution tarballs and packages (published as release assets, not committed)
|
||||
/*.tar.gz
|
||||
/*.tar.xz
|
||||
/*.zip
|
||||
/*.deb
|
||||
/*.rpm
|
||||
/*.AppImage
|
||||
*.tar.gz
|
||||
*.tar.xz
|
||||
*.zip
|
||||
*.deb
|
||||
*.rpm
|
||||
*.AppImage
|
||||
*.msi
|
||||
*.apk
|
||||
*.ipa
|
||||
*.dmg
|
||||
*.AppDir/
|
||||
SHA256SUMS
|
||||
SHA256SUMS.txt
|
||||
|
||||
# Test/scratch binaries
|
||||
|
|
@ -43,6 +69,8 @@ SHA256SUMS.txt
|
|||
|
||||
# Editor / OS noise
|
||||
*.swp
|
||||
*.tmp
|
||||
*.log
|
||||
*~
|
||||
.DS_Store
|
||||
.idea/
|
||||
|
|
|
|||
1
.source-date-epoch
Normal file
1
.source-date-epoch
Normal file
|
|
@ -0,0 +1 @@
|
|||
1788134400
|
||||
416
AUDIT.md
416
AUDIT.md
|
|
@ -1,85 +1,357 @@
|
|||
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
||||
# VaptVupt — Security Audit
|
||||
# ZUPT 5.2.8 audit guide and finding history
|
||||
|
||||
This document records the security review of VaptVupt: what is checked, how, the
|
||||
findings and their resolutions, and how to reproduce the checks. It complements
|
||||
[SECURITY.md](SECURITY.md) (policy + primitives) and
|
||||
[THREAT_MODEL.md](THREAT_MODEL.md) (what is and isn't defended).
|
||||
This document describes review surfaces and reproducible checks. It is an
|
||||
upstream self-review, not an independent audit, certification, or guarantee.
|
||||
`SECURITY.md` defines reporting policy and `THREAT_MODEL.md` defines the
|
||||
security boundary.
|
||||
|
||||
Scope: the pure-C11 CLI (`src/`, `include/`) and the PySide6/PyQt6 GUI
|
||||
(`gui/src/zupt_gui.py`). Out of scope: the optional, separately distributed
|
||||
`libzuptsdk` / `libpqvaptvupt` binaries (only present in a `make WITH_SDK=1`
|
||||
build); the shipped source-only build contains no vendored binaries.
|
||||
## 5.2.8 scope
|
||||
|
||||
> **Not independently certified.** This is the project's own structured review,
|
||||
> not a third-party accredited audit. Treat it as "reviewed, with reproducible
|
||||
> evidence" and do your own review for high-assurance use.
|
||||
The baseline scope is the source-only CLI and its bundled source codec:
|
||||
|
||||
## Methodology
|
||||
- first-party C and headers under `src/` and `include/`;
|
||||
- textual architecture-specific source under `jasmin/`, distinguishing
|
||||
compiler-generated output from separately identified hand-written assembly;
|
||||
- VaptVupt codec source at release 2.65.3, with provenance and licensing in
|
||||
`THIRD-PARTY-NOTICES.md`;
|
||||
- CLI tests, source scanner, build system, CI, and packaging recipes;
|
||||
- the Python GUI source as a caller of the CLI.
|
||||
|
||||
| Technique | What it covers | Where |
|
||||
|---|---|---|
|
||||
| **Cryptographic conformance vs an independent reference** | ML-KEM-768 is validated byte-for-byte against **OpenSSL 3.5's FIPS 203 ML-KEM-768** — deterministic keygen `ek` equality plus shared-secret agreement in both cross-decapsulation directions. | `tests/test_mlkem_fips203.sh`, in `make check` |
|
||||
| **NIST/RFC known-answer vectors** | SHA-256 (FIPS 180-4), SHA-3/SHAKE (FIPS 202), AES-256-CTR (SP 800-38A F.5.5/F.5.6), HMAC-SHA256 (RFC 4231), X25519 (RFC 7748), ML-KEM-768, PBKDF2. | `tests/test_vectors.c` |
|
||||
| **Byte-level tamper sweep** | Every byte position of a representative archive is flipped and re-opened; zero silent-accepts required (F-09). | `tests/` byte-sweep |
|
||||
| **Authenticated-encryption fuzzing** | HMAC / integrity-trailer fuzz over many trials (F-06, F-08). | `tests/` |
|
||||
| **Constant-time measurement** | dudect-style Welch t-test on the MAC-tag compare and the ML-KEM FO implicit-rejection compare (the two decapsulation-oracle-sensitive paths). | `tests/test_ct_timing.*` |
|
||||
| **Memory-safety sanitizers** | ASan + UBSan builds; exact-size decode cases; crafted-input decode. | `make test-asan` |
|
||||
| **Static analysis** | cppcheck (warning/style/performance) on the first-party sources; strict `-Wall -Wextra -Wpedantic -Werror` gcc + clang matrix. | CI |
|
||||
| **Formal annotations** | Frama-C/ACSL contracts on memory-safety-critical functions; 5 Jasmin-verified constant-time assembly routines (x86_64). | `include/zupt_acsl.h`, `jasmin/` |
|
||||
| **Adversarial multi-agent review** | Independent reviewers per dimension (crypto, parser/memory-safety, CLI, GUI↔CLI contract, packaging), each finding then adversarially refuted before it is accepted. | manual, per release |
|
||||
The baseline is built with `WITH_SDK=0 WITH_PQBOX=0`. The optional system
|
||||
`libvuptsdk` and `libpqvaptvupt` implementations are outside this scope unless
|
||||
their exact source packages and versions are added to an assessment. Assembly
|
||||
under `jasmin/` is disabled by default and is a separate `WITH_JASMIN=1` build
|
||||
choice on supported x86_64 compiler targets. Generated files must record their
|
||||
compiler provenance; hand-written files must not be represented as compiler
|
||||
output.
|
||||
|
||||
## Cryptographic conformance
|
||||
## Source-only review
|
||||
|
||||
- **ML-KEM-768 — genuine FIPS 203 (v5.0.0).** Earlier releases shipped round-3
|
||||
CRYSTALS-Kyber under a "FIPS 203" label; it was self-consistent and secure as
|
||||
an IND-CCA2 KEM but **not interoperable** with a compliant ML-KEM. Validating
|
||||
against OpenSSL 3.5 revealed three deviations — a transposed matrix-`Â`
|
||||
sampling convention (in both K-PKE.KeyGen and K-PKE.Encrypt), the round-3 final
|
||||
KDF, and the implicit-rejection domain. All three were fixed and the result is
|
||||
now byte-for-byte interoperable with OpenSSL in both directions. A permanent
|
||||
conformance test guards against regression. This changed the shared secret, so
|
||||
it is a wire-breaking change for `--pq`/`--pq-only` archives (see CHANGELOG
|
||||
5.0.0 BREAKING).
|
||||
- **Hybrid is the flagship.** `--pq` combines ML-KEM-768 with X25519 through a
|
||||
SHA3-512 combiner; the archive key is secure if **either** primitive holds —
|
||||
the strongest real-world posture and the recommended default. `--pq-only`
|
||||
offers pure ML-KEM-768 for single-primitive compliance mandates.
|
||||
- **Envelope.** AES-256-CTR with a **fresh random 128-bit nonce per block**
|
||||
(the dedup keystream-reuse bug is fixed and regression-tested), HMAC-SHA256
|
||||
Encrypt-then-MAC verified before any decryption, and an archive-integrity
|
||||
trailer over the header/footer.
|
||||
The 5.2.8 baseline retains the source-only boundary introduced in 5.2.2, which
|
||||
removed incomplete SDK/PQBOX header snapshots and local precompiled-library
|
||||
expectations. Git and new upstream source
|
||||
archives are intended to contain no compiled executable, object, shared/static
|
||||
library, distribution package, unsafe symlink, or unresolved Git LFS pointer.
|
||||
|
||||
## Notable findings and resolutions (recent)
|
||||
|
||||
| Sev | Finding | Resolution |
|
||||
|---|---|---|
|
||||
| High | ML-KEM-768 not FIPS 203-conformant / not interoperable | Fixed (transpose + KDF); validated vs OpenSSL; permanent conformance test |
|
||||
| High | `compress -p out.zupt f1 f2` overwrote an input file (data loss, exit 0) | Refuse to overwrite an existing non-`.zupt` output without `-y/--force`; self-overwrite guard |
|
||||
| High | `compress out.zupt dir -p pw` wrote an **unencrypted** archive (exit 0) | Error on a misplaced option after the archive (`--` escape available) |
|
||||
| Critical | AES-CTR keystream reuse across `--dedup` blocks (many-time-pad) | Fresh random per-block nonce; regression test |
|
||||
| Medium | Heap OOB read in the AVX2 decoder fast path on crafted input | Bound the 2-/3-byte offset read like the scalar tail path |
|
||||
| Medium | GUI defaulted to SDK modes absent from the source-only build (unusable) | Reworked to native `--pq`/`--pq-only`; SDK shown only when supported |
|
||||
| Low | Hybrid-decrypt did not wipe secret buffers on key-read failure | Wipe on the error path (matches the pq-only path) |
|
||||
| Low | Untruthful banner (Argon2id-default / `/zupt` URL) on source-only builds | Build-aware, accurate `version`/`help` output |
|
||||
| Critical* | Packaging (`debian/rules`, `aur`, `nix`, `homebrew`, `opensuse`) would fail a source-only build | Removed vendored-lib/`AUDIT.md` steps, fixed URLs, added completions |
|
||||
|
||||
\* build-time failure, not a runtime security issue.
|
||||
|
||||
## Known limitations / non-goals
|
||||
|
||||
- No protection against a compromised endpoint, a weak password, or key
|
||||
custody failures (see THREAT_MODEL.md).
|
||||
- Metadata (total archive size, block count) is observable.
|
||||
- The review is reproducible but not third-party certified.
|
||||
|
||||
## Reproducing
|
||||
Run the same scanner over each representation:
|
||||
|
||||
```sh
|
||||
make check # vectors, tamper sweep, FIPS 203 conformance, guards
|
||||
make test-asan # ASan + UBSan
|
||||
bash tests/test_mlkem_fips203.sh # FIPS 203 interop vs OpenSSL (needs openssl 3.5+)
|
||||
# tracked files and working tree
|
||||
scripts/check-source-only.sh
|
||||
|
||||
# committed Git tree or immutable tag
|
||||
scripts/check-source-only.sh --tag HEAD
|
||||
scripts/check-source-only.sh --tag v5.2.8
|
||||
|
||||
# generated source archive
|
||||
scripts/check-source-only.sh --archive /path/to/zupt-5.2.8.tar.gz
|
||||
```
|
||||
|
||||
FIPS 203 conformance needs an ML-KEM-capable OpenSSL (3.5+); the test skips
|
||||
gracefully otherwise (e.g. inside a distro package build).
|
||||
The scanner checks extensions and magic bytes, nested archives, symlink targets,
|
||||
LFS pointers, generated compiler output, and stale vendor-library references.
|
||||
It reports paths without printing file contents. Its negative tests include
|
||||
renamed ELF, ar, PE/MZ, versioned `.so`, RPM/DEB/AppImage, escaping symlinks,
|
||||
and LFS pointers; textual assembly is a permitted source type.
|
||||
|
||||
Archive inspection must also fail closed at bounded recursion depth, member
|
||||
count, individual expanded size, and total expanded size so a nested archive or
|
||||
decompression bomb cannot turn the release scanner into an unbounded resource
|
||||
consumer. On committed Linux candidate `ff99770`, this hardening and its
|
||||
adversarial fixtures passed all 39 source-only scanner cases, including GNU
|
||||
thin-archive and safe-diagnostic-path cases.
|
||||
|
||||
An unknown `.bin` fails by default. A necessary binary data fixture can be
|
||||
declared only through `--data-manifest`, with four tab-separated fields for
|
||||
path, purpose, provenance, and SPDX license. That manifest does not override a
|
||||
compiled/executable magic finding.
|
||||
|
||||
An artifact is not clean merely because it has a harmless extension. Conversely,
|
||||
binary image data is not executable code: the documented GUI icon assets are
|
||||
necessary data and are reviewed separately for purpose, provenance, and license.
|
||||
|
||||
## Reproducible project checks
|
||||
|
||||
The baseline gates are:
|
||||
|
||||
```sh
|
||||
make clean
|
||||
make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf 1)" \
|
||||
WITH_SDK=0 WITH_PQBOX=0 V=1
|
||||
make WITH_SDK=0 WITH_PQBOX=0 check
|
||||
make WITH_SDK=0 WITH_PQBOX=0 test-all
|
||||
make sdk-test
|
||||
```
|
||||
|
||||
Relevant review layers include:
|
||||
|
||||
| Layer | Evidence source | Interpretation |
|
||||
|---|---|---|
|
||||
| Source boundary | `scripts/check-source-only.sh`, `tests/test_source_only.sh` | Fails on prohibited artifacts or unsafe source layout |
|
||||
| Primitive vectors | `tests/test_vectors.c` | Known-answer regression for implemented primitives |
|
||||
| ML-KEM interoperability | `tests/test_mlkem_fips203.sh` | Runs only with an ML-KEM-capable OpenSSL 3.5+; otherwise `SKIP` |
|
||||
| Archive behavior | quick/regression, traversal, argument-order, block-swap, nonce, and exact-size tests | Exercises current parser, integrity, and round-trip properties |
|
||||
| Password sources | `tests/test_password_sources.sh` | Exercises password-file, inherited-descriptor and explicit-prompt rejection paths without logging password contents |
|
||||
| Key files | native key regressions | Exercises no-replace private-file creation, POSIX mode `0600`/Windows current-user-only DACL, failed-partial behavior, checksum, and exact ZKEY/ZPQK version/flags/reserved/size/role validation |
|
||||
| SDK key publication | `make sdk-test` | Exercises atomic descriptor/handle-backed key copies, POSIX private/public modes, and symlink/hardlink target preservation; this now runs in `release-check` and hosted GCC/Clang Linux CI |
|
||||
| Terminal output | archive-comment regression | Requires displayed untrusted comments to contain no raw terminal-control sequence |
|
||||
| Prompt cleanup | PTY signal regression | Requires handled POSIX interruption to restore the saved terminal state |
|
||||
| Sanitizers | `make test-asan-run` | Builds and executes separate ASan/UBSan/LSan evidence where supported; not a substitute for normal tests |
|
||||
| Static analysis | compiler analyzer, cppcheck, scan-build, clang-tidy where installed | Tool-specific findings must be reviewed, not suppressed globally |
|
||||
| Shell and metadata | shellcheck, SPDX/license checks, packaging syntax checks | Applies only when the named tool actually executed |
|
||||
| Source reproducibility | two `make dist` runs with identical committed input and epoch | Requires equal SHA-256 digests and clean archive scans |
|
||||
| Installed package | target-native package inspection and `scripts/test-installed-zupt.sh` | Applies only to the exact OS/release/architecture tested |
|
||||
|
||||
This table identifies evidence layers rather than results. Missing tools, OBS
|
||||
access, other architectures, Leap, and SLE must not be reported as passing
|
||||
without evidence.
|
||||
|
||||
## Prior 5.2.2 committed-candidate local Linux evidence
|
||||
|
||||
The following upstream self-audit results apply only to the 5.2.2 candidate at
|
||||
commit `ff99770` on the recorded local Linux environments. The immutable 5.2.2
|
||||
tag was not promoted after post-tag CI integration failures. These results are
|
||||
not independent certification, a 5.2.8 result, or evidence that release assets
|
||||
were published.
|
||||
|
||||
| Gate | Result | Recorded evidence |
|
||||
|---|---|---|
|
||||
| Full project gate | PASS | `make release-check` completed successfully on `ff99770`, including the late key-file, terminal-comment, password-prompt, explicit-Bash, and scanner-limit regressions. |
|
||||
| Packaging policy/syntax | PASS | `PASS=49 FAIL=0 SKIP=0`. |
|
||||
| Source-only scanner adversarial suite | PASS | 39/39, including GNU thin archives, bounded archive expansion, and safe diagnostic cases. |
|
||||
| Strict compilers and compiler analyzer | PASS | GCC and Clang strict builds passed; GCC `-fanalyzer` passed. |
|
||||
| Static-analysis suite | PASS | 9/9 in the full tool-enabled run. A separate reduced-environment `release-check` run completed six available checks and reported `cppcheck` unavailable; unavailable tooling was not relabelled as a pass. |
|
||||
| Dynamic analysis | PASS | ASan, UBSan, and LSan runs passed. |
|
||||
| Mutation fuzzing | PASS | 1,000 mutation iterations completed without a sanitizer-detected crash. |
|
||||
|
||||
An earlier off-screen GUI smoke run remains supporting evidence, but is not
|
||||
represented as an exact-`ff99770` GUI-package result. The immutable 5.2.3
|
||||
candidate was not promoted because its source-policy test assumed LF for a
|
||||
Windows `.bat` checkout that correctly used CRLF.
|
||||
|
||||
## Prior 5.2.4 exact-tag integration evidence
|
||||
|
||||
GitHub Actions exact-tag run `33431386002` completed 12 jobs successfully. Its
|
||||
sole failed job was the openSUSE gate: the standalone `Serviceinfo` harness did
|
||||
not change into the directory containing `_service` before executing the
|
||||
service chain. Dependent native Windows and macOS jobs were therefore skipped,
|
||||
and v5.2.4 was not promoted. The tag and its record remain immutable.
|
||||
|
||||
A separate local openSUSE Tumbleweed reproduction resolved the explicit
|
||||
`refs/tags/v5.2.4` revision to the tagged commit and, after
|
||||
`os.chdir(service_dir)`, completed `obs_scm`, `tar`, and `recompress`. It
|
||||
produced exactly one `zupt-5.2.4.tar.gz`, which passed the source-only scanner.
|
||||
This isolates a release/test harness defect; it is not evidence of a product,
|
||||
archive-format, cryptographic, codec, or SDK ABI change. It also does not turn
|
||||
the skipped native jobs into passes or transfer any result to 5.2.8.
|
||||
|
||||
## Prior 5.2.5 exact-tag native-gate evidence
|
||||
|
||||
The immutable `v5.2.5` candidate was not promoted. Exact-tag GitHub Actions run
|
||||
`33434986357` completed 13 jobs successfully, while its native Windows and
|
||||
macOS jobs failed. The Windows regression did not preserve every requested
|
||||
hostile path byte across its command-line boundary. The macOS gate exposed both
|
||||
an unavailable `explicit_bzero` assumption and Bash 3.2 empty-array behavior in
|
||||
the source scanner exercised by `make check`.
|
||||
|
||||
The 5.2.6 corrections select the existing compiler-resistant volatile wipe on
|
||||
Darwin and NetBSD, guard every relevant scanner array, and make the Windows
|
||||
fixture accept explicit hexadecimal bytes, verify the full requested path in
|
||||
the archive, and reject each dangerous raw byte fragment anywhere in diagnostic
|
||||
output. These changes do not alter the archive format, cryptography, bundled
|
||||
codec, or SDK ABI.
|
||||
|
||||
A separate local compatibility run executed the corrected scanner with genuine
|
||||
GNU Bash 3.2.57 in a clean clone. All four exercised modes completed: the
|
||||
repository audit reported 609 files and one archive; `--tree` reported 204/0;
|
||||
`--archive` reported 201/1; and `--root` plus `--tag v5.2.5` reported 810/2.
|
||||
This is targeted scanner compatibility evidence only, not exact-v5.2.6 or
|
||||
v5.2.8 hosted CI, package, native-platform, or promotion evidence.
|
||||
|
||||
## Prior 5.2.6 exact-tag native-gate evidence
|
||||
|
||||
The immutable `v5.2.6` candidate was not promoted. Exact-tag GitHub Actions run
|
||||
`33442264243` completed 13 jobs successfully and failed two native jobs. On
|
||||
macOS arm64, the strict SHA-NI regression build diagnosed x86-only helper
|
||||
declarations as unused under `-Werror`. On Windows, argv transcoding of the safe
|
||||
printable UTF-8 fixture caused the path regression to abort before its intended
|
||||
archive and diagnostic assertions.
|
||||
|
||||
The 5.2.7 changes scope those helper declarations to supported x86 builds and
|
||||
carry the safe UTF-8 fixture across the Windows argument boundary without
|
||||
locale-dependent byte conversion. These are test/release integration changes,
|
||||
not archive-format, cryptographic, codec, or SDK ABI changes.
|
||||
|
||||
## Prior 5.2.7 exact-tag native-gate evidence
|
||||
|
||||
The immutable `v5.2.7` candidate was not promoted. Exact-tag GitHub Actions run
|
||||
`33445470664` concluded `cancelled` at `2026-08-31T23:11:19Z`, with 13
|
||||
successful jobs, one failed macOS job, and one cancelled Windows job. The macOS
|
||||
runner filesystem rejected creation of the
|
||||
raw-C1 filename fixture with `EILSEQ`. The hosted Windows job stalled in `make
|
||||
check`; a MinGW/Wine reproduction isolated the cause to
|
||||
`test --password-prompt ... </dev/null` entering `_getch`, and the remaining
|
||||
native job was cancelled.
|
||||
|
||||
The 5.2.8 fixture treats that creation refusal as an explicit `SKIP`; on a
|
||||
filesystem that accepts the byte, the scanner must still reject the compiled
|
||||
magic and render the path without emitting the raw C1 byte. No result from the
|
||||
immutable tag transfers automatically to 5.2.8. The Windows prompt correction
|
||||
rejects a redirected/non-console standard input before entering `_getch` and
|
||||
adds a native EOF regression; it also requires fresh exact-candidate evidence.
|
||||
|
||||
## 5.2.8 CodeQL High path-race corrections
|
||||
|
||||
CodeQL High #5 identified SDK key-copy publication that reopened the output
|
||||
path and then applied `chmod` to that mutable name. The copy now uses the core
|
||||
atomic publisher, sets POSIX mode on the open temporary descriptor, checks
|
||||
read/close/publication failures, and replaces only the destination directory
|
||||
entry. `sdk-test` covers symlink and hardlink sentinels and expected key modes.
|
||||
|
||||
CodeQL High #6 identified the POSIX disk-restore `lstat`-then-`open` sequence.
|
||||
Restore now opens once with `O_NOFOLLOW` and without truncation, classifies that
|
||||
descriptor with `fstat`, and retains the same raw-device descriptor through
|
||||
capacity checks and writes. Regular-file destinations retain the existing
|
||||
atomic publisher.
|
||||
|
||||
CodeQL High #7 identified benchmark cleanup's `lstat`-then-recursive-path
|
||||
sequence. POSIX cleanup now pins each component with `openat` and removes leaves
|
||||
with `unlinkat`; Windows pins ancestors/children, opens reparse points without
|
||||
following them, recurses only into a plain directory held against rename, and
|
||||
after emptying a directory reopens it relative to the pinned parent, verifies
|
||||
its volume and file index against the traversal handle, and marks only that
|
||||
identity-checked handle for deletion. The live-workspace
|
||||
regression injects a directory symlink and verifies that its external sentinel
|
||||
survives. These are reviewed fixes and regression coverage, not independent
|
||||
certification by themselves; the exact-tag evidence is recorded below.
|
||||
|
||||
The C/C++ default-branch analysis of commit `69fc26b` closed #5, #6, and #7,
|
||||
then reported High #8, #9, and #10 solely in the newly added SDK regression:
|
||||
its sentinel and mode checks used `stat`/`lstat` before later path operations.
|
||||
Each individual content or metadata check now opens without following links
|
||||
and uses `fstat` or reads through that already-open descriptor. A static guard
|
||||
rejects a return to path-level `stat`/`lstat` in this test. The subsequent
|
||||
C/C++ default-branch scan run `33452563116` completed successfully at commit
|
||||
`7a8e5c5`; alerts #5 through #10 are fixed, and the authenticated
|
||||
code-scanning API reported zero open alerts.
|
||||
|
||||
Manual pre-tag CI run `33452602634` at the same functional commit completed 14
|
||||
of 15 jobs successfully. Linux compilers, analyzers, sanitizers, source policy,
|
||||
reproducibility, DEB, RPM/SRPM, portable bundles, and the native macOS DMG gate
|
||||
passed. On Windows, source audit, build, and `make check` passed; the subsequent
|
||||
smoke failed only when the old MSYS `grep` tried to match a literal non-BMP
|
||||
filename after ZUPT had compressed and verified all five inputs. Independent
|
||||
MinGW/Wine reproduction confirmed that the redirected ZUPT listing contained
|
||||
the exact UTF-8 bytes. The gate now creates the non-BMP name through byte
|
||||
escapes, verifies Latin-1/BMP/non-BMP listing bytes without locale-sensitive
|
||||
matching, and requires extraction plus a full tree diff. The path-confinement
|
||||
regression independently constructs the BMP/non-BMP archive name from ASCII
|
||||
hex and requires byte-exact listing and extraction.
|
||||
|
||||
Because that pre-tag run failed, it remains diagnostic evidence rather than
|
||||
release approval. The immutable `v5.2.8` tag at
|
||||
`ebb9ab3aa1d42c50030ca02883f6162dc4771fe1` repeated the complete suite in
|
||||
manually dispatched run `33456209269`: all 15 jobs passed, including native
|
||||
Windows/macOS, the pinned local OBS service chain, source reproducibility,
|
||||
DEB/RPM/SRPM, installed-package, sanitizer, analyzer, and source-only gates.
|
||||
The canonical source archive was reproduced at 798296 bytes with SHA-256
|
||||
`378b9506211545b9594cf0d38ac8955d9b1cac34eb6b379ae0ec26b84edb65f7`.
|
||||
|
||||
Initial promotion run `33457344882` stopped before release creation because
|
||||
its validator incorrectly assumed that an SRPM's `%{ARCH}` must be `src`.
|
||||
Both artifacts were genuine source packages: `%{SOURCEPACKAGE}` was `1`,
|
||||
`%{SOURCERPM}` was absent, each payload was exactly its Source0 plus spec, and
|
||||
each binary RPM referenced the matching SRPM. Commit `33eb904` changed the
|
||||
gate to those canonical metadata and payload checks. Corrected promotion run
|
||||
`33457868306` then validated the same tag-bound artifacts and published exactly
|
||||
13 assets. No asset was rebuilt to pass promotion. These are reproducible
|
||||
project records, not independent certification.
|
||||
|
||||
## Cryptographic review boundary
|
||||
|
||||
The repository includes NIST/RFC known-answer tests and a conditional OpenSSL
|
||||
ML-KEM interoperability check. These establish specific functional outputs in
|
||||
the environments where they pass; they do not prove implementation security,
|
||||
constant-time execution, or resistance to every malformed input.
|
||||
|
||||
Portable C is the default. Sensitive comparison/select code is written to avoid
|
||||
secret-dependent branching, but compiler output remains platform-dependent.
|
||||
Optional generated Jasmin functions have a narrower assurance scope and do not
|
||||
formally verify the parser, codec, key management, or whole application. The C
|
||||
AES implementation has documented cache-timing risk on hostile shared hardware.
|
||||
|
||||
## Historical findings
|
||||
|
||||
The following entries are retained as release history. Their regression tests
|
||||
should be rerun, but the historical resolution does not itself constitute a
|
||||
5.2.8 test result.
|
||||
|
||||
| First corrected | Severity | Finding | Resolution recorded at the time |
|
||||
|---|---|---|---|
|
||||
| 4.2.0 | Critical | AES-CTR nonce reuse across encrypted `--dedup` blocks | Changed to fresh random per-block nonces; older affected archives should be re-encrypted |
|
||||
| 5.0.0 | High | Native ML-KEM used round-3 CRYSTALS-Kyber semantics while labelled FIPS 203 | Corrected matrix/KDF/rejection behavior and added OpenSSL interoperability regression; native PQ compatibility changed |
|
||||
| 5.0.0 | High | A malformed password-option ordering could overwrite an input | Added output/self-overwrite and argument-order guards |
|
||||
| 5.0.0 | High | A misplaced option could cause plaintext output when encryption was intended | Reject misplaced options unless explicitly escaped |
|
||||
| 5.0.0 | Medium | AVX2 codec offset read could exceed a crafted input tail | Added the scalar-equivalent bound and exact-size regression |
|
||||
| 5.2.2 | Build/supply chain | Build and packaging paths expected local precompiled optional libraries | Removed incomplete vendor trees; optional integrations now require explicit system development packages |
|
||||
| 5.2.2 | High | Extraction used mutable string paths and could follow hostile parent/leaf links or publish partially verified output | Added strict index-path validation, descriptor/handle-relative traversal, no-replace atomic publication, exact size/hash validation, and hostile-archive regressions |
|
||||
| 5.2.2 | High | Compression or disk backup could name its own input through an alternate spelling, hardlink, or symlink | Compare open-file identity before creating the output in normal, solid, and disk-image writers; `--force` cannot bypass the guard |
|
||||
| 5.2.2 | High | Disk restore validated a pathname before destructively consuming it and did not prove raw-device capacity before writing | Snapshot the measured archive privately before target open, restore from the same stream, and fail closed on unknown or insufficient device capacity |
|
||||
| 5.2.2 | High | Some decoders did not require DATA at every payload position, and legacy encrypted+dedup disk references used a different AAD sequence | Enforce frame types in serial, threaded, solid, test, and disk readers; reconstruct the exact v5.2.1 disk AAD sequence with an actual encrypted DATA/DATA/REF/DATA fixture |
|
||||
| 5.2.2 | Medium | Benchmark scratch paths were predictable from the process ID | Create one random private scratch directory and clean it without following links |
|
||||
| 5.2.2 | High | Native private-key output and ZKEY/ZPQK readers did not uniformly enforce no-replace private permissions and every structural field | Create without replacement using POSIX mode `0600` or a Windows current-user-only DACL; validate checksum, version, flags, reserved bytes, exact size, and public/private role before use; leave a failed exclusive partial for manual removal rather than risk unlinking a pathname replacement |
|
||||
| 5.2.2 | Medium | An authenticated archive comment could still inject terminal control sequences when displayed | Render untrusted comment bytes in a terminal-safe form without changing the authenticated archive value |
|
||||
| 5.2.2 | Medium | A signal during an interactive POSIX password prompt could leave terminal echo/state altered | Restore saved terminal settings on handled interruptions; cover the behavior with a PTY regression |
|
||||
| 5.2.2 | Test reliability | `tests/regression.sh` used Bash syntax without making the interpreter contract explicit | Execute the suite explicitly with Bash and keep syntax/interpreter checks in release gates |
|
||||
| 5.2.2 | Documentation/licensing | Current documentation incorrectly denied historical MIT grants visible in published Git history | Added a factual erratum: current source follows current SPDX notices, while earlier grants and immutable tags remain valid and unmodified |
|
||||
| 5.2.8 | High | CodeQL #5: SDK key copies changed permissions through a re-resolved destination path | Publish through the core atomic output object and apply permissions to its open descriptor; run link-target/mode regressions through `sdk-test` |
|
||||
| 5.2.8 | High | CodeQL #6: POSIX disk restore classified a pathname before reopening it destructively | Open without truncation or symlink following, classify with `fstat`, and retain the same device descriptor through write |
|
||||
| 5.2.8 | High | CodeQL #7: benchmark cleanup classified entries before recursively resolving their path | Traverse pinned descriptors/handles, refuse link/reparse traversal, remove entries relative to pinned parents, and verify Windows directory identity before handle deletion |
|
||||
| 5.2.8 | High (test-only) | CodeQL #8/#9/#10: the new SDK regression inspected paths before later reads or cleanup | Open each fixture without following links, inspect/read through `fstat` and the same descriptor, and reject path-level `stat`/`lstat` in the static gate |
|
||||
|
||||
See `CHANGELOG.md` for the complete per-release history and compatibility notes.
|
||||
Old tags remain immutable and may contain artifacts or build assumptions removed
|
||||
from current branches and new tags.
|
||||
|
||||
## Packaging review
|
||||
|
||||
The openSUSE package must build the immutable source tag with
|
||||
`WITH_SDK=0 WITH_PQBOX=0`, preserve distribution flags and debuginfo, run a real
|
||||
`%check`, install through `DESTDIR`, and omit the renamed-era `vaptvupt` alias from
|
||||
the main package. Review the built RPM for dependencies, paths, permissions,
|
||||
RPATH/RUNPATH, hardening, debug information, licenses, and unowned files, then
|
||||
test it after installation in a disposable target environment.
|
||||
|
||||
Release-page DEB, binary RPM, SRPM, notice-bearing Linux tar.xz, Windows ZIP,
|
||||
and macOS DMG packages are separate outputs, never members of Git or source
|
||||
archives. Publish only formats built and tested for their stated target and
|
||||
include SHA-256 checksums. The gated GUI set adds the architecture-independent
|
||||
DEB, noarch/source RPM, and source-only portable GUI ZIP. Package gates include
|
||||
exact payload/dependency and installed off-screen integration checks; the
|
||||
portable ZIP additionally receives source scans, an exact safe-member allowlist,
|
||||
and an extracted launcher test. An AppImage is not promoted by the 5.2.8
|
||||
policy; AppDir and Flatpak bundles, GUI platform installers, and bare
|
||||
Linux/Windows executables are also excluded. Windows ZIP and macOS DMG outputs
|
||||
remain CLI-only.
|
||||
|
||||
No Wine result is retained as release evidence for 5.2.8. Cross-compilation
|
||||
does not establish native-Windows behavior. Extended-length/device namespace
|
||||
paths, raw UNC output roots, and mapped/network-drive output are unsupported;
|
||||
the native Windows workflow remains a publication gate for the ZIP containing
|
||||
the executable.
|
||||
|
||||
## Known limitations
|
||||
|
||||
- No independent security audit or certification has been performed.
|
||||
- Fuzzing and sanitizers sample behavior; they cannot prove absence of memory or
|
||||
parser defects.
|
||||
- Static analysis is tool-, configuration-, and path-dependent.
|
||||
- Optional SDK/PQBOX code is outside the default assessment.
|
||||
- Side-channel behavior is compiler-, CPU-, OS-, and workload-dependent.
|
||||
- A clean source archive does not by itself establish that a binary was built
|
||||
reproducibly or on a trusted runner.
|
||||
- Passing on one OS or architecture is not evidence for another.
|
||||
|
||||
Record exact commands, tool versions, target, exit status, and non-sensitive
|
||||
logs for every release gate. Never convert an unavailable or unexecuted check
|
||||
from `SKIP` to `PASS`.
|
||||
|
|
|
|||
707
CHANGELOG.md
707
CHANGELOG.md
|
|
@ -1,4 +1,621 @@
|
|||
# VaptVupt Changelog
|
||||
# ZUPT Changelog
|
||||
|
||||
## [5.2.8] — 2026-08-31 — Path-race hardening and native-fixture correction
|
||||
|
||||
Corrective successor to the immutable, unpromoted `v5.2.7` candidate.
|
||||
Exact-tag GitHub Actions run `33445470664` reached a macOS failure because the
|
||||
runner filesystem rejected creation of the raw-C1 filename fixture with
|
||||
`EILSEQ`. The workflow concluded `cancelled` at `2026-08-31T23:11:19Z`, with
|
||||
13 successful jobs, one failed macOS job, and one cancelled Windows job.
|
||||
The hosted Windows job stalled in `make check`; a MinGW/Wine reproduction
|
||||
isolated the cause to `test --password-prompt ... </dev/null` entering
|
||||
`_getch` despite redirected standard input. The tag and its evidence remain
|
||||
unchanged.
|
||||
|
||||
- Close CodeQL High #5 in SDK key saving by copying into the core atomic
|
||||
publisher's already-open private object, applying POSIX key permissions with
|
||||
`fchmod` on that descriptor, checking read/close/publication failures, and
|
||||
replacing only the requested directory entry. Symlink and hardlink target
|
||||
sentinels and private/public key modes are covered by the SDK regression.
|
||||
- Close CodeQL High #6 in POSIX disk restore by opening the target once without
|
||||
truncation or final-symlink following, classifying that descriptor with
|
||||
`fstat`, and retaining the same device descriptor through capacity checks and
|
||||
writes. Regular-file restores continue to use atomic publication.
|
||||
- Close CodeQL High #7 in benchmark cleanup by resolving POSIX components with
|
||||
`openat(..., O_NOFOLLOW)`, deleting relative to pinned descriptors with
|
||||
`unlinkat`, and using pinned, reparse-point-aware handles for Windows
|
||||
traversal. The regression injects a directory symlink into a live workspace
|
||||
and verifies that cleanup does not visit its target.
|
||||
- The first default-branch rescan closed #5, #6, and #7 and then identified
|
||||
test-only path checks as High #8, #9, and #10 in the new SDK regression.
|
||||
Replace every test-side `stat`/`lstat` sequence with one no-follow `open`
|
||||
followed by `fstat` and descriptor reads, and retain that boundary in the
|
||||
static regression gate. Default-branch run `33452563116` completed
|
||||
successfully with #5 through #10 fixed and zero open code-scanning alerts.
|
||||
- Treat inability to create the raw-C1 scanner filename as an explicit fixture
|
||||
skip on filesystems that reject the byte; when creation succeeds, the unsafe
|
||||
diagnostic-escaping assertions still run unchanged. Normalize Bash 3.2's
|
||||
sign-extended `%d` character conversion to an unsigned octet so raw and
|
||||
UTF-8 C1 diagnostics retain their canonical `\\xNN`/`\\uNNNN` form.
|
||||
- Reject redirected or otherwise non-console Windows password prompts before
|
||||
entering `_getch`, handle console EOF as an error, and cover the native
|
||||
redirected-input path so it cannot hang a release gate.
|
||||
- On Windows, validate private-key confinement as the protected,
|
||||
current-user-only DACL that the implementation creates; do not treat MSYS's
|
||||
synthetic `stat` mode as a POSIX `0600` result. POSIX continues to require
|
||||
the real descriptor mode `0600` under multiple umasks.
|
||||
- Replace the old MSYS `grep` non-BMP boundary exposed after 14 successful jobs
|
||||
in pre-tag run `33452602634` with byte-exact Python checks for Latin-1, BMP,
|
||||
and non-BMP UTF-8 listing output. Construct the emoji fixture from ASCII byte
|
||||
escapes and require list, extract, and full-tree equality; the independent
|
||||
path regression also creates its BMP/non-BMP name from ASCII hex.
|
||||
- Run `sdk-test` from both `release-check` and the hosted GCC/Clang Linux job so
|
||||
the atomic key-save regression cannot silently fall outside release gates.
|
||||
- Carry the v5.2.7 archive format, cryptography, bundled codec release, and SDK
|
||||
ABI forward unchanged. These are implementation and test-integration
|
||||
corrections, not a wire-format or API change.
|
||||
- Realign current code, package, workflow, artifact, and documentation
|
||||
references to 5.2.8, and pin the AUR/Homebrew SHA-256 and Guix content hash
|
||||
to the final reproducible source archive before tagging.
|
||||
- Exact-tag run `33456209269` passed all 15 source, package, analyzer,
|
||||
sanitizer, OBS, Windows, and macOS jobs at `ebb9ab3`. Promotion initially
|
||||
stopped before publication because its validator treated `%{ARCH}` as the
|
||||
SRPM discriminator; the genuine SRPMs instead correctly carry
|
||||
`%{SOURCEPACKAGE}=1`. Commit `33eb904` corrected and strengthened that gate,
|
||||
and run `33457868306` published the exact 13 tested assets without rebuilding
|
||||
them. No v5.2.7 result transfers to this evidence.
|
||||
|
||||
## [5.2.7] — 2026-08-31 — Native test-harness portability corrections
|
||||
|
||||
Corrective successor to the immutable, unpromoted `v5.2.6` candidate. Exact-tag
|
||||
GitHub Actions run `33442264243` completed 13 jobs successfully, but the native
|
||||
Windows and macOS jobs failed, so no 5.2.6 assets were promoted. The tag and its
|
||||
recorded evidence remain unchanged.
|
||||
|
||||
- Scope the SHA-NI regression helper declarations to the supported x86 build
|
||||
path so macOS arm64 strict compilation does not diagnose unused declarations
|
||||
under `-Werror`.
|
||||
- Carry the safe printable UTF-8 path fixture through an explicit
|
||||
byte-stable Windows argument representation so argv transcoding cannot abort
|
||||
the regression before its intended archive and diagnostic assertions.
|
||||
- Carry the 5.2.6 source-only and security baseline forward without changing
|
||||
the archive format, cryptography, bundled codec release, or SDK ABI.
|
||||
- Realign current source, package, workflow, artifact, and tag references to
|
||||
5.2.7, then pin the final reproducible release-archive checksum and content
|
||||
hash in the downstream recipes before tagging.
|
||||
- Require fresh exact-`v5.2.7` source, checksum, hosted CI, native-platform,
|
||||
package, OBS, and promotion evidence. No prior candidate result transfers
|
||||
automatically, and this entry does not claim those gates passed.
|
||||
|
||||
## [5.2.6] — 2026-08-31 — Native release-gate portability corrections
|
||||
|
||||
Corrective successor to the immutable, unpromoted `v5.2.5` candidate. Exact-tag
|
||||
GitHub Actions run `33434986357` completed 13 jobs successfully, but the native
|
||||
Windows and macOS jobs failed, so no 5.2.5 assets were promoted. The tag and its
|
||||
recorded evidence remain unchanged.
|
||||
|
||||
- Use the compiler-resistant volatile wipe fallback on macOS and NetBSD instead
|
||||
of assuming that their C libraries export `explicit_bzero`; supported glibc,
|
||||
FreeBSD, and OpenBSD paths retain their existing selection.
|
||||
- Make the source-only scanner's empty-array handling compatible with the
|
||||
system Bash 3.2 shipped by macOS, including repository, tag, standalone-tree,
|
||||
standalone-archive, and path-component traversal paths.
|
||||
- Make hostile archive-path fixtures accept explicit hexadecimal bytes, verify
|
||||
the requested path bytes in the generated archive, and reject the dangerous
|
||||
raw byte fragment anywhere in diagnostic output, so the Windows regression
|
||||
does not depend on command-line conversion or benign path prefixes.
|
||||
- Carry the 5.2.5 source-only and security baseline forward without changing
|
||||
the archive format, cryptography, bundled codec release, or SDK ABI.
|
||||
- Realign current package, workflow, artifact, and tag references to 5.2.6;
|
||||
leave release-archive checksums explicitly pending until the final source
|
||||
archive is generated.
|
||||
- Require fresh exact-`v5.2.6` source, checksum, hosted CI, native-platform,
|
||||
package, OBS, and promotion evidence. No prior candidate result transfers
|
||||
automatically, and this changelog entry does not claim those gates passed.
|
||||
|
||||
## [5.2.5] — 2026-08-31 — OBS service working-directory correction
|
||||
|
||||
Corrective successor to the immutable `v5.2.4` candidate. GitHub Actions run
|
||||
`33431386002` did not promote that tag: 12 jobs passed, the openSUSE Tumbleweed
|
||||
RPM gate failed because its standalone `Serviceinfo` executor inherited the
|
||||
repository working directory instead of the isolated service directory, and
|
||||
the dependent Windows/macOS gate was skipped. The tag and its recorded
|
||||
checksums remain unchanged.
|
||||
|
||||
- Run the standalone OBS `obs_scm` → `tar` → `recompress` chain from its
|
||||
isolated service directory so downstream services can find `.obsinfo`.
|
||||
- Add a packaging-policy regression for the OBS executor working directory
|
||||
while retaining the immutable `refs/tags/v5.2.5` service revision assertion.
|
||||
- Record the successful local Tumbleweed reproduction of the corrected service
|
||||
chain; it produced one source archive that passed the source-only scanner.
|
||||
- Carry the 5.2.4 security and source-only baseline forward without changing
|
||||
the archive format, cryptography, bundled codec release, or SDK ABI.
|
||||
- Realign current package, workflow, artifact, and tag references to 5.2.5;
|
||||
leave AUR, Homebrew, and Guix hashes pending the final source archive.
|
||||
- Require fresh exact-`v5.2.5` source, checksum, hosted CI, native-platform,
|
||||
package, and promotion evidence; no v5.2.4 result transfers automatically.
|
||||
|
||||
## [5.2.4] — 2026-08-31 — Source-policy line-ending correction
|
||||
|
||||
Corrective successor to the immutable `v5.2.3` candidate. That candidate was
|
||||
not promoted because its source-policy test assumed LF bytes for a Windows
|
||||
`.bat` file that Git correctly materializes as CRLF according to
|
||||
`.gitattributes`; the tag and its historical record remain unchanged.
|
||||
|
||||
- Correct the release/source-policy integration so the required CRLF checkout
|
||||
form is validated without treating it as source drift.
|
||||
- Carry the 5.2.3 release-integration work and the 5.2.2 security/source-only
|
||||
baseline forward without an archive-format, cryptographic, codec, or SDK ABI
|
||||
change.
|
||||
- Realign current documentation, package, workflow, artifact, and tag references
|
||||
to 5.2.4 while keeping generated binaries outside Git and source archives.
|
||||
- Correct three internal regression-test headers so pre-3.0 product releases
|
||||
retain the historical ZUPT name; VaptVupt remains only the bundled codec and
|
||||
compatibility-facing identifier where applicable.
|
||||
- Require fresh exact-`v5.2.4` source, checksum, hosted CI, native-platform,
|
||||
package, and promotion evidence; no 5.2.3 result transfers automatically.
|
||||
- Keep authenticated OBS/Factory validation pending and retain the unresolved,
|
||||
unsuppressed openSUSE automatic `debugsource` rpmlint `no-binary` finding.
|
||||
|
||||
## [5.2.3] — 2026-08-31 — Corrective release integration
|
||||
|
||||
Corrective successor to the immutable `v5.2.2` candidate. Post-tag CI
|
||||
integration failures prevented 5.2.2 asset promotion; the tag and its historical
|
||||
record remain unchanged.
|
||||
|
||||
- Carry the 5.2.2 source-only, security, naming, format, and compatibility work
|
||||
forward without a new archive-format version, codec release, or SDK ABI.
|
||||
- Realign current source, package, workflow, documentation, artifact, and tag
|
||||
version references to 5.2.3. Release binaries remain outside Git and source
|
||||
archives.
|
||||
- Make `zupt-gui --version` emit the stable machine-readable line
|
||||
`zupt-gui 5.2.3`, and cover that contract in GUI/package regressions.
|
||||
- Derive Debian and Fedora GUI integration paths, package metadata checks, and
|
||||
CLI dependency checks from the authoritative version header instead of a
|
||||
prior-release literal.
|
||||
- Repair native RPM container gates: persist the exact checked-out workspace as
|
||||
a Git safe directory, and remove Tumbleweed's conflicting `busybox-gawk`
|
||||
before installing the native `gawk`/RPM toolchain.
|
||||
- Require a new exact-`v5.2.3` source archive and checksum set plus fresh hosted
|
||||
CI, native Windows/macOS, package, and promotion evidence. Prior local 5.2.2
|
||||
results do not transfer automatically.
|
||||
- Keep authenticated OBS/Factory validation pending. The automatic openSUSE
|
||||
`debugsource` rpmlint `no-binary` finding remains unresolved and unsuppressed.
|
||||
- Continue to exclude AppImage/AppDir/Flatpak, GUI platform installers, and bare
|
||||
executables from the promoted set; retain the notice-bearing CLI archives and
|
||||
source-only portable GUI ZIP policy.
|
||||
|
||||
|
||||
## [5.2.2] — 2026-08-31 — ZUPT identity, source-only upstream tree, and openSUSE packaging
|
||||
|
||||
This maintenance release keeps the `.zupt` extension, the v1.6 version byte,
|
||||
and the bundled VaptVupt codec at 2.65.3. It adds flag-gated 5.2.2 encodings for
|
||||
authenticated encrypted-dedup references and disk-image integrity/index
|
||||
metadata. The 5.2.2 reader retains a narrow legacy v5.2.1 plain disk-index
|
||||
path; older readers are not claimed to accept every archive written by 5.2.2.
|
||||
|
||||
### Product identity and compatibility
|
||||
|
||||
- Restored the original **ZUPT** product name and `zupt` command across the
|
||||
application, GUI, packages, documentation, and release artifacts.
|
||||
- Moved the canonical project location to
|
||||
`https://github.com/cristiancmoises/zupt`.
|
||||
- Kept the `.zupt` extension, format v1.6, `ZUPT` magic bytes, codec IDs,
|
||||
`zupt_*`/`ZUPT_*` identifiers, and `zuptsdk_*` ABI unchanged. This is a
|
||||
product-identity change, not an archive or cryptographic format change.
|
||||
- Retained the name VaptVupt where it identifies the bundled codec, its
|
||||
`vv_*` API/wire format, the `--vv`/`--vaptvupt` codec selector, or an external
|
||||
compatibility contract. An optional `vaptvupt` command alias can support
|
||||
scripts written for versions 3.0.0 through 5.2.1.
|
||||
|
||||
### Source and build
|
||||
|
||||
- Removed the incomplete `vendor/vuptsdk/` and `vendor/pqvaptvupt/` header
|
||||
snapshots and every build expectation that a precompiled local `.so`, `.a`,
|
||||
or `.o` is available. Git and newly generated source archives contain source
|
||||
and necessary data only.
|
||||
- `WITH_SDK` and `WITH_PQBOX` are disabled by default. When requested, they
|
||||
resolve separately installed development libraries through `pkg-config` (or
|
||||
explicit packager-supplied flags) and fail clearly when unavailable. The
|
||||
build never downloads dependencies or falls back to a binary under `vendor/`.
|
||||
- Reworked the Makefile to honor `CC`, `CPPFLAGS`, `CFLAGS`, `LDFLAGS`,
|
||||
`LDLIBS`, `AR`, `RANLIB`, `STRIP`, `DESTDIR`, `PREFIX`, and install-directory
|
||||
overrides. Architecture detection follows the compiler target. Baseline
|
||||
builds no longer apply AVX2 to the whole program, add a private-library
|
||||
RPATH, or strip distribution binaries.
|
||||
- `make install DESTDIR=... PREFIX=/usr` supports staged package builds. The
|
||||
`vaptvupt` command is an optional compatibility install, not part of the
|
||||
openSUSE main package.
|
||||
- Added `--password-prompt`, `--pass-file`, and `--pass-fd` to every CLI path
|
||||
that accepts a password. These explicit sources avoid the historical
|
||||
optional-argument ambiguity of `-p`; file/descriptor input rejects empty,
|
||||
NUL-containing, and overlong values.
|
||||
- Hardened native key handling: private outputs use no-replace creation with
|
||||
POSIX mode `0600` or a Windows current-user-only DACL, and ZKEY/ZPQK inputs
|
||||
must pass checksum, version, flags, reserved-byte, exact-size, and
|
||||
public/private-role validation. A write/flush/close failure leaves its
|
||||
incomplete or durability-uncertain exclusive file for manual review and
|
||||
removal instead of risking an unlink-after-close race against a replacement
|
||||
pathname.
|
||||
- Added signal-aware POSIX password-prompt cleanup that restores saved terminal
|
||||
state on handled interruption, with a PTY regression in the final gate.
|
||||
- Corrected bundled-code provenance: pq-crystals/kyber-derived ML-KEM portions
|
||||
now carry the upstream CC0-1.0 option and complete license text; the x86 BCJ
|
||||
state machine is identified as an adaptation of Igor Pavlov's public-domain
|
||||
LZMA SDK source instead of making an unsupported clean-room claim; and
|
||||
curve25519-donna-derived X25519 portions retain the conservative upstream
|
||||
BSD-3-Clause notice. The SHA-NI path now records its immutable
|
||||
public-domain SHA-Intrinsics reference.
|
||||
- Removed unsupported `JASMIN-VERIFIED` labels. The repository retains
|
||||
Jasmin textual sources and generated/hand-written assembly plus runtime
|
||||
tests, but no reproducible formal-proof certificate or log for these paths;
|
||||
historical changelog claims below are qualified accordingly.
|
||||
- Treat older “constant-time by construction” and formal-verification wording
|
||||
as historical design claims unless a current reproducible proof artifact is
|
||||
named. Source review and timing regressions do not prove the behavior of
|
||||
every compiler, CPU, or final binary.
|
||||
|
||||
### Archive integrity and path security
|
||||
|
||||
- Reject archive entry names containing absolute roots, `.` or `..`
|
||||
components, control characters, NTFS alternate-stream syntax, trailing
|
||||
dot/space components, or reserved DOS device names. Empty, overlong, and
|
||||
embedded-NUL index paths are rejected during parsing.
|
||||
- Create output below a pinned destination. POSIX systems traverse parents with
|
||||
`openat()`/`mkdirat()` and `O_NOFOLLOW` after resolving the user-selected root
|
||||
to a physical path once; Windows traverses and creates each component with
|
||||
handle-relative `NtCreateFile` and publishes with a handle-relative
|
||||
`FileRenameInfo`. Neither implementation re-resolves a checked archive path
|
||||
through a mutable parent.
|
||||
- Write to a private, newly created temporary file and publish it atomically
|
||||
only after the decoded size, per-file checksum, archive integrity checks, and
|
||||
close/flush operations succeed. Existing regular files, hardlinks, symlinks,
|
||||
or reparse points are never overwritten.
|
||||
- Normal, solid, and disk-image compression now use the same private-temporary
|
||||
discipline in the destination directory. Publication replaces only the
|
||||
requested directory entry, so an output symlink or hardlink cannot truncate
|
||||
its target; any input, encryption, or write failure preserves the previous
|
||||
archive and removes the temporary. Disk-image indices now use the canonical
|
||||
varint encoding and pass the normal `list` and `test` parsers. Disk backup
|
||||
measures and reads the source through one open descriptor, rejects a size
|
||||
change, and cannot be redirected by exchanging the source pathname. Before
|
||||
creating the temporary output, normal compression and disk backup also reject
|
||||
an output that identifies the same file as the input through an alternate
|
||||
spelling, hardlink, or symlink; `--force` does not bypass this guard.
|
||||
- Disk restore now copies the measured compacted archive into one private,
|
||||
auto-deleted scratch file before opening the destructive destination. Its
|
||||
preflight and restore phases consume that same snapshot, so exchanging the
|
||||
source pathname cannot change the bytes after validation. `ZUPT_TMPDIR`
|
||||
selects an existing scratch directory and fails without fallback when it is
|
||||
invalid or lacks space. Linux, macOS, and FreeBSD raw block-device capacity
|
||||
is queried before the first write; an unknown or undersized device fails
|
||||
closed. Regular-file restore retains atomic publication.
|
||||
- Bind every encrypted DATA frame to its logical file/block position (or disk
|
||||
block position), including when deduplication is enabled. An authenticated
|
||||
DEDUP_REF is bound to its own logical position and carries the authenticated
|
||||
source position needed to verify the referenced DATA frame. Swapping either
|
||||
kind of frame therefore fails authentication instead of relying on the former
|
||||
archive-wide dedup AAD sentinel. Generic `test` and byte-exact `disk restore`
|
||||
regressions cover encrypted `disk backup --dedup`; they remain part of the
|
||||
exact final-candidate gate described below.
|
||||
- Require both XXH64 and an independent SHA-256/128 digest match before
|
||||
emitting a dedup reference, and authenticate reference offsets in new
|
||||
encrypted archives.
|
||||
- Require a valid archive-integrity trailer in the `extract`, `list`, `test`,
|
||||
and `disk restore` paths by default, regardless of unauthenticated header
|
||||
flags. `--allow-legacy-no-ait` is an explicit recovery-only override for
|
||||
those commands when given a known, trusted pre-AIT archive; it emits a
|
||||
downgrade warning. Writers never use the override or create a no-AIT archive.
|
||||
`info` remains an unauthenticated framing inspection: it reports AIT presence
|
||||
but does not validate the trailer or archive contents.
|
||||
- Authenticate the encrypted disk index and record a chained whole-image XXH64
|
||||
content hash in new disk archives. Generic `test` and `disk restore` verify
|
||||
block count, restored size, reference targets, and that content hash. XXH64
|
||||
remains a non-cryptographic corruption check in a plain archive.
|
||||
- Retain compatibility parsers for the fixed little-endian disk index and the
|
||||
linear encrypted-dedup AAD sequence published through 5.2.1. The regression
|
||||
fixture is an actual v5.2.1 password-encrypted DATA/DATA/REF/DATA disk archive,
|
||||
stored as hexadecimal text with its source tag/commit, password, input hash,
|
||||
and archive hash. The 5.2.2 candidate lists, tests, extracts, and restores it byte-exact;
|
||||
the legacy index has no whole-image hash and produces an explicit warning.
|
||||
The full local Linux gate passed on commit `ff99770`; no broader historical
|
||||
compatibility claim is made.
|
||||
- Serialize fixed-width archive/header, footer, index and PBKDF iteration
|
||||
fields explicitly in little-endian order. Varint readers now reject overlong
|
||||
encodings and values wider than 64 bits instead of accepting an ambiguous or
|
||||
wrapped scalar.
|
||||
- Require a DATA frame wherever a decoder consumes file or disk payload. The
|
||||
multithreaded and serial readers, solid reader, generic `test`, and disk
|
||||
restore now reject type-confused frames rather than decoding COMMENT or INDEX
|
||||
payload as ordinary data.
|
||||
- Add structurally valid hostile-archive fixtures covering traversal, absolute
|
||||
and Windows-special paths, destination leaf/ancestor links, a relative user
|
||||
output root, corrupt payload cleanup, nested UTF-8 paths, and safe separator
|
||||
normalization.
|
||||
- Scope the Windows output boundary to normal local Win32 paths. Cross-build
|
||||
and Wine results are not native-Windows evidence; the native package workflow,
|
||||
including its Unicode round trip, remains a release gate.
|
||||
Extended-length/device namespace paths, raw UNC output roots, and
|
||||
mapped/network-drive output are unsupported in 5.2.2.
|
||||
- Create benchmark corpora, archives, extraction outputs, and concatenation
|
||||
inputs below a randomly generated private temporary directory and clean it
|
||||
recursively without following links. This replaces the predictable
|
||||
process-ID-only scratch path used by earlier builds.
|
||||
- Render archive comments as untrusted terminal data: control bytes are shown
|
||||
safely rather than emitted as raw terminal-control sequences. The stored and
|
||||
authenticated comment bytes are unchanged.
|
||||
|
||||
### Licensing and provenance erratum
|
||||
|
||||
- Corrected prior documentation that incorrectly denied all historical MIT
|
||||
grants. Commit `d4660e6539c8b6eeba81751c018217d978fdd618` distributed the
|
||||
then-current first-party application and GUI with MIT license files, and the
|
||||
immutable `v2.2.2` tag contains an MIT-form `gui/LICENSE-GUI` alongside an
|
||||
AGPL SPDX notice in the GUI source. Those records are preserved and their
|
||||
historical permissions are not revoked or reinterpreted by 5.2.2.
|
||||
- Current source follows its current per-file SPDX notices: the application,
|
||||
GUI, cryptographic tool, build, test, and documentation code are
|
||||
AGPL-3.0-or-later, while the identified bundled codec source is
|
||||
GPL-3.0-or-later. The erratum corrects the record; it does not rewrite old
|
||||
tags or change the license of a historical copy.
|
||||
- Distinguished Jasmin compiler output from hand-written textual assembly.
|
||||
`zupt_aes_ctr4.s` is a hand-written production implementation corresponding
|
||||
to an algorithm-only `.jazz` description; generated files retain their
|
||||
available compiler provenance.
|
||||
- Corrected the stale claim that the adapted XXH64 code was public domain.
|
||||
Both derived implementations now preserve Yann Collet's BSD-2-Clause
|
||||
copyright, conditions, disclaimer, and compound SPDX scope; binary package
|
||||
metadata and license payloads include BSD-2-Clause.
|
||||
|
||||
### Audit, tests, packaging, and release
|
||||
|
||||
- Added a reusable source-only scanner for tracked files, the working tree,
|
||||
`git archive`, release archives, nested archives, unsafe symlinks, Git LFS
|
||||
pointers, compiler output, executable magic, and stale vendor-library
|
||||
references. Positive and negative regression tests cover renamed ELF, ar,
|
||||
PE/MZ, versioned `.so`, RPM/DEB/AppImage, escaping symlinks, and LFS pointers
|
||||
while permitting textual assembly. Necessary non-code `.bin` data requires a
|
||||
manifest entry with purpose, provenance, and SPDX license; compiled magic is
|
||||
never allowlisted. Nested inspection now has fail-closed recursion, member,
|
||||
per-entry expansion, and total-expansion limits with decompression-bomb
|
||||
regressions.
|
||||
- Make `tests/regression.sh`'s Bash interpreter requirement explicit so running
|
||||
it through a non-Bash `/bin/sh` cannot masquerade as a product regression.
|
||||
- Added upstream openSUSE/OBS packaging under `packaging/opensuse/`, built with
|
||||
`WITH_SDK=0 WITH_PQBOX=0`, real `%check` execution, staged installation, and
|
||||
no installed `vaptvupt` alias. The OBS service tracks the canonical ZUPT
|
||||
repository and an immutable release tag; no acceptance by OBS or Factory is
|
||||
implied by files being present upstream.
|
||||
- Updated CI and release checks around clean source builds, tests, source
|
||||
archive inspection, packaging metadata, licenses, and secret hygiene.
|
||||
- The gated release path is defined to produce the audited source archive plus
|
||||
an Ubuntu amd64 CLI DEB, openSUSE Tumbleweed x86_64 CLI binary/source RPMs,
|
||||
a notice-bearing Linux x86_64 CLI tar.xz, an architecture-independent GUI
|
||||
DEB, a noarch GUI RPM and matching source RPM, a source-only portable GUI
|
||||
ZIP, a CLI Windows x86_64 ZIP containing the executable and notices, and a
|
||||
native-architecture CLI macOS DMG.
|
||||
Each binary format is built separately from the tagged source and may be
|
||||
published only after its target-specific package and functional gates pass;
|
||||
`SHA256SUMS` covers the promoted assets. Packages never enter Git or the
|
||||
source archive.
|
||||
- Exclude AppImage, AppDir and Flatpak bundles, GUI platform installers, and
|
||||
bare executables from the 5.2.2 release set: the inspected type-2 runtime's
|
||||
static dependency notice omitted mimalloc and the available inputs did not
|
||||
provide a complete LGPL source/relink handoff. The offline helper now
|
||||
requires runtime-specific compliance material from its operator.
|
||||
- Publish the Windows executable only inside its ZIP with AGPL, GPL,
|
||||
BSD-2-Clause, BSD-3-Clause, CC0-1.0, toolchain-runtime, NOTICE, and
|
||||
third-party notices; no bare EXE is promoted.
|
||||
- Limit the 5.2.2 GUI artifact promise to `zupt-gui_5.2.2_all.deb`,
|
||||
`zupt-gui-5.2.2-1.noarch.rpm`, its matching source RPM, and
|
||||
`zupt-gui-5.2.2-portable.zip`. Package artifacts have exact dependency and
|
||||
installed off-screen GUI/CLI tests. The portable ZIP contains source and
|
||||
launchers only and passes source scans, an exact safe-member allowlist, and an
|
||||
extracted off-screen launcher test. Windows and macOS artifacts remain
|
||||
CLI-only.
|
||||
- Remove the standalone GUI `setup.py` sdist/wheel route, whose outputs omitted
|
||||
the complete AGPL and artwork-provenance payload. Reviewed GUI installers and
|
||||
package helpers preserve those notices with every included icon.
|
||||
- Updated the README, installation, distribution, security, audit, GUI, and
|
||||
manual-page documentation for the 5.2.2 source-only workflow.
|
||||
|
||||
The committed Linux candidate `ff99770` passed the full local
|
||||
`make release-check`. Recorded results include packaging
|
||||
`PASS=49 FAIL=0 SKIP=0`; 39/39 source-only scanner cases, including GNU thin
|
||||
archives and safe diagnostics; strict GCC and Clang; GCC `-fanalyzer`; 9/9 in
|
||||
the full tool-enabled static-analysis run; ASan/UBSan/LSan; and 1,000
|
||||
mutation-fuzz iterations without a sanitizer-detected crash. An earlier
|
||||
off-screen GUI smoke run remains supporting rather than exact-candidate package
|
||||
evidence.
|
||||
|
||||
These are upstream self-audit results, not independent certification or a
|
||||
published-release claim. Native Windows and macOS, hosted GitHub CI and release
|
||||
promotion, authenticated OBS, and the openSUSE automatic `debugsource` rpmlint
|
||||
`no-binary` finding remain pending. Missing or unexecuted gates remain `SKIP`,
|
||||
not `PASS`.
|
||||
|
||||
|
||||
## [5.2.1] — 2026-07-12 — GUI Verify/Extract robustness; refreshed comparison + audit tables
|
||||
|
||||
Backward-compatible; no format, key, or codec change (codec stays 2.65.3, wire
|
||||
format v1.6). GUI-only fix + documentation.
|
||||
|
||||
### GUI — Verify and Extract no longer error on a mis-set mode / missing credential
|
||||
|
||||
- Verifying an encrypted archive required picking the correct PQ mode from a
|
||||
dropdown and supplying the matching key; a wrong pick produced a raw CLI
|
||||
decrypt error ("Error: Archive uses post-quantum encryption. Use --pq" /
|
||||
"Authentication failed"), and Verify ran synchronously on the GUI thread
|
||||
(freezing the window on a large archive).
|
||||
- New `_detect_archive_enc()` reads the archive header via `info` (no credential)
|
||||
and classifies it none / password / pq / pqonly / sdk. The Verify tab now
|
||||
auto-detects the encryption, uses the matching decrypt flag automatically (a
|
||||
wrong-mode mismatch is impossible — the PQ-mode dropdown was removed), and if
|
||||
the required password or key is missing it shows a clear instruction instead
|
||||
of a raw error. Verify runs through the async job runner (own progress bar),
|
||||
so it never freezes; results print "All checksums passed." / "Verification
|
||||
failed."
|
||||
- The same header-detection + missing-credential guidance is applied to the
|
||||
Extract tab (it had the identical footgun).
|
||||
|
||||
### Documentation
|
||||
|
||||
- Refreshed the README **Compression comparison** tables (ratio + throughput vs
|
||||
zstd/gzip/lz4) on codec 2.65.3, every round-trip byte-exact.
|
||||
- Added an **Audit status** table: `make check` 16/16, NIST/RFC KAT 16/16,
|
||||
ML-KEM-768 FIPS 203 conformance 3/3, path-traversal 5/5, block-swap 6/6,
|
||||
dedup-nonce 1/1, arg-order 8/8, decode-slack 7/7, SHA-NI + incremental HMAC
|
||||
9/9, exact-size decode 80/80, GUI branding 11/11.
|
||||
|
||||
### Validation
|
||||
|
||||
Full GUI function matrix on a real X display with thread-safety instrumentation
|
||||
(zero cross-thread widget access): compress/extract (hybrid + full-PQ + password,
|
||||
byte-exact), Verify (plain/password/hybrid/pq-only pass; missing password and
|
||||
missing PQ key each produce guidance; wrong credential and non-archive fail
|
||||
cleanly), Info, Disk backup+restore, two concurrent jobs, close-mid-job.
|
||||
|
||||
|
||||
|
||||
## [5.2.0] — 2026-07-12 — GUI compress-crash fix; codec 2.65.3; libvuptsdk
|
||||
|
||||
Backward-compatible with 5.0.x/5.1.0: `.zupt` wire format unchanged (v1.6),
|
||||
archives interoperate in both directions, and `--pq` / `--pq-only` keys keep
|
||||
working.
|
||||
|
||||
### GUI — the compress crash / corruption / "nothing happens" is fixed
|
||||
|
||||
The 5.1.0 progress-bar work introduced a cross-thread bug that made compress
|
||||
crash the app ("app closes"), hang ("nothing happens"), or leave a truncated
|
||||
(corrupt) archive — worst on the full-PQ path.
|
||||
|
||||
- Root cause: `run_async` connected plain Python CLOSURES (`finish`/`on_pct`/
|
||||
`release`) to signals emitted from the worker `QThread`. PySide6 runs a
|
||||
plain-closure slot in the EMITTING thread regardless of the requested
|
||||
connection type — even an explicit `Qt.QueuedConnection` — because a bare
|
||||
functor has no receiver QObject to give it GUI-thread affinity. Those closures
|
||||
then called `QProgressBar.setValue/setRange/hide`, `QPushButton.setEnabled`
|
||||
and `QTextEdit.append` from the worker thread. Cross-thread QWidget access is
|
||||
undefined behaviour and crashed the app under real X11/Wayland rendering; it
|
||||
only survived offscreen tests, so prior automated runs missed it.
|
||||
- Fix: a `_Job(QObject)` controller parented to a GUI-thread widget, so every
|
||||
slot is a bound method Qt auto-marshals to the GUI thread.
|
||||
- Verified on a real X display (window shown, progress bar rendering) with a
|
||||
QProgressBar/QPushButton instrumentation that flags any worker-thread call:
|
||||
zero cross-thread calls after the fix, and hybrid + full-PQ + password
|
||||
compress/extract all byte-exact round-trip; Verify/Info/Disk backup+restore/
|
||||
two-concurrent-jobs/close-mid-job all pass.
|
||||
|
||||
### Codec — VaptVupt 2.65.3
|
||||
|
||||
Byte-identical output to 2.65.0 (same ratio, wire format v1.6) but extreme-mode
|
||||
encode is ~1.6–2× faster (Sprint 132) and the extreme prepass window
|
||||
reservation is capped at 8 MiB instead of up to 128 MiB (Sprint 133 memory
|
||||
hygiene). Our AVX2 decoder offset-read guard is now upstream; the ANS
|
||||
safe-zone reserve patch is re-applied on top.
|
||||
|
||||
### SDK — libvuptsdk (renamed from libzuptsdk)
|
||||
|
||||
`libvuptsdk` (git.securityops.co/cristiancmoises/libvuptsdk) is the renamed
|
||||
`libzuptsdk`; only the shared-object filename/SONAME changed
|
||||
(`libzuptsdk.so.2` → `libvuptsdk.so.2`), the C API is unchanged. A `WITH_SDK=1`
|
||||
build now links `-lvuptsdk` and enables `--pq-sdk` + the Argon2id password KDF.
|
||||
`--pq-box` needs the SEPARATE `libpqvaptvupt`, which `libvuptsdk` does not
|
||||
provide, so it is now behind its own `WITH_PQBOX=1` flag rather than folded into
|
||||
`WITH_SDK`. The default distributed build stays source-only (`WITH_SDK=0`):
|
||||
native `--pq` / `--pq-only` + PBKDF2, no external libraries. Validated:
|
||||
`WITH_SDK=1` links libvuptsdk + libcrypto + libargon2, `keygen --sdk` and
|
||||
`--pq-sdk` encrypt/decrypt round-trip byte-exact.
|
||||
|
||||
### Validation
|
||||
|
||||
`make check` 16/16 (source-only); codec KAT 16/16; ML-KEM-768 FIPS 203
|
||||
conformance 3/3; full GUI function matrix on real X with zero cross-thread
|
||||
access; SDK round-trip on a `WITH_SDK=1` build.
|
||||
|
||||
|
||||
|
||||
## [5.1.0] — 2026-07-11 — codec 2.65.0; large ratio gains; GUI compress fixes
|
||||
|
||||
Backward-compatible with 5.0.0: the `.zupt` wire format is unchanged (v1.6) and
|
||||
archives interoperate in both directions. `--pq` / `--pq-only` keys and archives
|
||||
from 5.0.0 continue to work — no key regeneration needed.
|
||||
|
||||
### Compression — much better ratio (two settings were leaving it on the table)
|
||||
|
||||
- **Vendored codec upgraded 2.60.4 → 2.65.0** (`git.securityops.co/cristiancmoises/vaptvupt-codec`,
|
||||
tag v2.65.0): faster balanced encoder and the Sprint 124–130 extreme-mode
|
||||
literal-pricing work. The two in-tree audit patches (ANS decode safe-zone
|
||||
`2*SAFEZONE_MAX_RUN` reserve against a crafted-sequence heap overflow, and the
|
||||
AVX2 offset-read bound in `vv_decoder.c`) are re-applied on top; the safe-zone
|
||||
fix is not yet upstream.
|
||||
- **`format_v2` is no longer forced** in the integration wrapper (`src/vaptvupt_api.c`).
|
||||
Forcing the binary-oriented `format_v2`/min_match=3 path on *every* input routed
|
||||
text through the greedy parser and **halved the extreme-mode ratio on text**
|
||||
(7.6× → 3.7× at the codec level). Since codec v2.61.0 the encoder auto-enables
|
||||
`format_v2` for binary-detected input and keeps the optimal parser for text, so
|
||||
the wrapper now leaves it on auto — text gets the optimal parser, binary still
|
||||
gets v2.
|
||||
- **Block size scales with level** (`auto_block_size` in `src/zupt_format.c`). The
|
||||
block is the codec's LZ window; the old flat 512 KiB extreme block meant the
|
||||
"large-window extreme" parser could never match beyond 512 KiB. New defaults:
|
||||
≤2 → 128 KiB, ≤4 → 1 MiB, ≤6 → 2 MiB, 7 (balanced) → 4 MiB, ≥8 (extreme) → 8 MiB.
|
||||
- **`--dedup` keeps a small block automatically** (256 KiB). Block size also sets
|
||||
dedup granularity, and a large block almost never finds a byte-exact duplicate,
|
||||
so dedup + large-window are mutually exclusive; `--dedup` now picks the small
|
||||
block regardless of level, restoring dedup ratios that the block bump broke.
|
||||
|
||||
Measured, level 9 (extreme), 20–25 MB per class, single thread (full tables in
|
||||
README → *Compression comparison*):
|
||||
|
||||
| Data class | 5.0.0 | 5.1.0 | Change |
|
||||
|---|--:|--:|--:|
|
||||
| Text (docs, Markdown) | 3.77× | 5.98× | +58% |
|
||||
| Server logs | 7.21× | 9.07× | +26% |
|
||||
| JSON (structured records) | 8.25× | 9.38× | +14% |
|
||||
| Source code (C / headers) | 4.93× | 5.63× | +14% |
|
||||
|
||||
Extreme mode trades encode speed for the larger window (its optimal DP now runs
|
||||
over a bigger block); balanced (`-l 7`, the default) also improves and stays
|
||||
fast. Decode speed and memory are unaffected by block size; peak RSS at extreme
|
||||
is ~30 MB per thread.
|
||||
|
||||
### GUI — "app closes / gets stuck when I compress" fixed
|
||||
|
||||
- **Crash on every job completion.** `run_async` dropped its `QThread`/`Worker`
|
||||
references immediately after `quit()`; Python's cyclic GC then collected the
|
||||
still-running `QThread` and Qt aborted the process ("QThread: Destroyed while
|
||||
thread is still running"). References are now released from a slot on
|
||||
`QThread.finished` after `wait()`.
|
||||
- **"App stuck" during compress.** The CLI paints live progress as `
|
||||
` frames
|
||||
(no newline until 100%); the worker read line-by-line and so emitted nothing
|
||||
for the whole job — the window looked frozen on any file larger than one block.
|
||||
The worker now parses `
|
||||
` progress frames and drives the progress bar, and
|
||||
runs the child with `stdin=/dev/null` so a prompt can never block it.
|
||||
- **Window never appeared on Wayland** (Sway 1.12 + Qt 6.9): the toolkit never
|
||||
sent the initial `wl_surface.commit`, so the compositor never mapped the
|
||||
surface. The GUI now watches for its first expose and, if none arrives, relaunches
|
||||
itself on XWayland (`QT_QPA_PLATFORM=xcb`). Earlier: a Wayland-launch SIGSEGV
|
||||
from self-`raise()`/`activateWindow()` (gated to X11 now).
|
||||
- Worker exceptions can no longer strand a job (catch-all → failure report;
|
||||
`errors="replace"` on pipes); closing the window mid-job asks for confirmation
|
||||
then aborts cleanly; added `vaptvupt-gui --version` / `--help` / `--selftest`
|
||||
for headless launch verification.
|
||||
|
||||
### Validation
|
||||
|
||||
- `make check` 16/16; codec KAT vectors 16/16; ML-KEM-768 FIPS 203 conformance
|
||||
3/3 (still byte-exact vs OpenSSL 3.5); path-traversal, block-swap, dedup-nonce,
|
||||
arg-order, decode-slack suites green.
|
||||
- Cross-version interop: 5.0.0 ↔ 5.1.0 archives (password + `--pq` hybrid +
|
||||
`--pq-only`) extract byte-identically in both directions, all levels.
|
||||
- Full GUI function matrix (keygen / compress with PQ key / password / pq-only /
|
||||
extract all modes with byte-identical round-trips / verify / info / concurrent
|
||||
jobs / close-mid-job) 16/16 on offscreen and X11; big-file (300 MB) extreme
|
||||
round-trips clean, peak RSS 761 MB at 24 threads.
|
||||
|
||||
|
||||
|
||||
## [5.0.0] — 2026-07-10 — genuine FIPS 203 ML-KEM-768; GUI + CLI hardening
|
||||
|
|
@ -273,9 +890,10 @@ later + commercial; its own suite: 66/66):
|
|||
pristine upstream files.
|
||||
- `VV_SOURCES` gained `vv_bcj.c` (the `test-asan` target could not link
|
||||
since BCJ arrived).
|
||||
- Corrected a Makefile comment that misstated the codec license as
|
||||
"Apache-2.0 / MIT" — the codec is **GPL-3.0-or-later**; the tool is
|
||||
**AGPL-3.0-or-later** (never MIT).
|
||||
- Corrected a Makefile comment that misstated the current codec license as
|
||||
"Apache-2.0 / MIT" — the current codec is **GPL-3.0-or-later** and the
|
||||
current tool is **AGPL-3.0-or-later**. See the 5.2.2 licensing erratum for
|
||||
preserved historical MIT grants.
|
||||
|
||||
### Compatibility summary
|
||||
|
||||
|
|
@ -1330,10 +1948,10 @@ The v3.0.0 GUI's about panel had a credit line:
|
|||
zupt Cristian Cezar Moises MIT
|
||||
```
|
||||
|
||||
That was false. The GUI's SPDX header has always been
|
||||
`AGPL-3.0-or-later`, the top-level `LICENSE` is AGPL, and the project
|
||||
policy is **AGPL-3.0-or-later with commercial dual-licensing forever**.
|
||||
The MIT line was a templating mistake inherited from an early scaffold.
|
||||
That entry described the intended license of the then-current GUI, but its
|
||||
historical conclusion was incorrect. Earlier published repository revisions
|
||||
did contain MIT license notices, and those grants cannot be retroactively
|
||||
denied. See the 5.2.2 licensing erratum above.
|
||||
|
||||
Removed. The CREDITS block now has two correctly-attributed rows:
|
||||
|
||||
|
|
@ -1342,10 +1960,10 @@ Removed. The CREDITS block now has two correctly-attributed rows:
|
|||
|
||||
Both rows carry the commercial-licensing contact `sac@securityops.co`.
|
||||
|
||||
`gui/LICENSE-GUI` was an actual MIT license file. Replaced with the
|
||||
AGPL-3.0-or-later text + commercial-dual-licensing note + a historical
|
||||
note explaining the prior MIT mistake (so anyone with an old tarball
|
||||
can't legitimately claim to have received an MIT grant).
|
||||
`gui/LICENSE-GUI` was changed from an MIT-form file to an
|
||||
AGPL-3.0-or-later notice for the then-current source. That change did not revoke
|
||||
MIT permissions already conveyed for historical material. The current
|
||||
`gui/LICENSE-GUI` records both the current notice and the factual erratum.
|
||||
|
||||
Top-level `LICENSE` preamble updated to reflect the Zupt → VaptVupt
|
||||
rename.
|
||||
|
|
@ -1393,9 +2011,10 @@ All call sites updated.
|
|||
|
||||
### New regression test: `tests/test_gui_branding.sh`
|
||||
|
||||
11 assertions covering exactly the bugs we just fixed:
|
||||
- No MIT references in the GUI source (excluding the explanatory comment)
|
||||
- `gui/LICENSE-GUI` is AGPL-licensed and does not start with "MIT License"
|
||||
Assertions covering the current branding and license presentation:
|
||||
- No claim in the GUI source that the current GUI is MIT-only
|
||||
- `gui/LICENSE-GUI` presents the current AGPL notice first and preserves the
|
||||
evidenced historical MIT grant (see the 5.2.2 erratum)
|
||||
- GUI source SPDX header is `AGPL-3.0-or-later`
|
||||
- No `replace("zupt ", ...)` parser in code
|
||||
- An anchored `_VERSION_RE` regex is present
|
||||
|
|
@ -1879,7 +2498,7 @@ bump and packaging-syntax test expansion; archive format unchanged.
|
|||
### `.github/workflows/ci.yml` — 8-job CI matrix
|
||||
|
||||
Replaces the prior 4-job CI with a comprehensive matrix that mirrors
|
||||
the project's local-verification protocol from `PROMPT.md §6`:
|
||||
the project's historical local-verification protocol:
|
||||
|
||||
| Job | What it does |
|
||||
|---|---|
|
||||
|
|
@ -1907,7 +2526,8 @@ Covers, with appropriate plain-language honesty:
|
|||
(encrypted modes), byte-level tamper detection (0 silent accepts
|
||||
in v1.6 sweep), authentication-failure indistinguishability
|
||||
(F-11), post-quantum forward secrecy in `--pq-sdk`, side-channel
|
||||
resistance on Jasmin-proven hot paths
|
||||
resistance on hot paths then described as Jasmin-proven (5.2.2 records that
|
||||
no reproducible formal-proof artifact was retained)
|
||||
- **What Zupt does NOT protect against**: compromised endpoints,
|
||||
key compromise (no forward secrecy across archives, no rotation
|
||||
feature), weak passwords (with concrete brute-force numbers),
|
||||
|
|
@ -2601,7 +3221,7 @@ size to run. It needs to be a sprint-protocol step, not a one-off.
|
|||
|
||||
### What changed
|
||||
|
||||
**`PROMPT.md` — Prompt v2.**
|
||||
**Historical sprint instructions — version 2.**
|
||||
|
||||
- **NEW §3.5: The exhaustive byte-sweep mandate.** Every
|
||||
format-touching change runs the full byte sweep before claiming
|
||||
|
|
@ -2621,7 +3241,7 @@ size to run. It needs to be a sprint-protocol step, not a one-off.
|
|||
with the regression-test names that catch each one. The table is
|
||||
the canonical "things that have shipped and must never recur"
|
||||
reference; keeping it current is part of every sprint.
|
||||
- **Footer stamp**: Prompt v2, 2026-05-20.
|
||||
- **Footer stamp**: historical instruction revision 2, 2026-05-20.
|
||||
|
||||
**`Makefile`.** The help-target banner version is now derived
|
||||
from `include/zupt.h` via a `grep | awk` substitution:
|
||||
|
|
@ -2632,7 +3252,7 @@ help:
|
|||
include/zupt.h | awk -F'"' '{print $$2}') build targets:"
|
||||
```
|
||||
|
||||
This closes a recurring bug noted in `PROMPT.md §6 step 9` —
|
||||
This closes a recurring bug noted in the historical sprint checklist —
|
||||
prior sprints (2.3.0, 2.3.1) left the banner stale even after the
|
||||
sprint protocol said to bump it. Making it auto-derived removes
|
||||
the drift opportunity entirely.
|
||||
|
|
@ -2663,7 +3283,6 @@ the drift opportunity entirely.
|
|||
### Files touched
|
||||
|
||||
```
|
||||
PROMPT.md (§3.5 NEW, §6 step renumber, §10 kickoff, §11 rows, v2 stamp)
|
||||
Makefile (help banner auto-derives version from header)
|
||||
include/zupt.h (version 2.3.1 → 2.4.0)
|
||||
CHANGELOG.md (this entry)
|
||||
|
|
@ -2847,7 +3466,7 @@ the footer.
|
|||
|
||||
- **Encrypted modes**: AIT = `HMAC-SHA256(mac_key, hdr[0..63] || footer[0..23])`
|
||||
- **Plaintext modes**: AIT = `XXH64(...)` in the first 8 bytes, zeros in
|
||||
the rest. Best-effort (`OPAQUE`-class per PROMPT.md §5).
|
||||
the rest. Best-effort (`OPAQUE` structural-integrity class).
|
||||
|
||||
The MAC input deliberately excludes `footer[24..31]` = `"ZEND" || u32 version`.
|
||||
Both are structurally validated by the read path (`locate_footer_v15`
|
||||
|
|
@ -3037,7 +3656,7 @@ so flipping it (e.g. from `0x02 INDEX` to `0x03 ENC_HEADER`) did
|
|||
not cause auth failure; the downstream parser was tolerant.
|
||||
Severity: low. Fix at `src/zupt_format.c` adds the structural
|
||||
check immediately after `read_block`. This makes the byte
|
||||
`OPAQUE`-class per PROMPT.md §5 (tamper detected by parser, not by
|
||||
`OPAQUE` structural-integrity class (tamper detected by parser, not by
|
||||
MAC).
|
||||
|
||||
**F-02b — RECLASSIFIED.** The 2.2.4 hypothesis that the archive
|
||||
|
|
@ -3074,7 +3693,7 @@ other v2.3.0 changes than as a standalone patch.
|
|||
### Verification
|
||||
|
||||
- `make` — clean on plain GCC and Clang.
|
||||
- `make` with strict GCC flags (full set from PROMPT.md §6) — clean.
|
||||
- `make` with the historical strict GCC warning set — clean.
|
||||
- `make` with strict Clang flags — clean.
|
||||
- `make test` — **61/61 passing**.
|
||||
- `make test-vectors` — **14/14 passing**.
|
||||
|
|
@ -3110,8 +3729,8 @@ SECURITY.md (integrity statement reaffirmed)
|
|||
|
||||
Patch release. No format changes, no feature changes, no on-disk
|
||||
compatibility impact. Five findings closed against the v2.2.3 baseline
|
||||
under the methodology in the new top-level `PROMPT.md` (continuous
|
||||
improvement prompt) and tracked in `docs/FINDINGS-2.x.md` (durable
|
||||
under the methodology in the then-current audit instructions and tracked in
|
||||
`docs/FINDINGS-2.x.md` (durable
|
||||
numbered ledger that survives between work sessions).
|
||||
|
||||
### Findings closed
|
||||
|
|
@ -3183,9 +3802,9 @@ preserved.
|
|||
|
||||
### New process artefacts
|
||||
|
||||
- **`PROMPT.md`** — top-level "god-tier" continuous-improvement prompt.
|
||||
Designed to be pasted verbatim into a fresh chat alongside the latest
|
||||
source tarball. Encodes the methodology that produced this release:
|
||||
- **Historical continuous-improvement instructions** — removed from the
|
||||
current source tree after their durable material was consolidated into the
|
||||
audit and security documents. They encoded the methodology that produced this release:
|
||||
three-line workflow (survey → fix-with-test → ship), explicit
|
||||
authentication-coverage invariant (every archive byte covered by
|
||||
per-block HMAC OR a separate index MAC OR a footer MAC — no third
|
||||
|
|
@ -3233,7 +3852,7 @@ preserved.
|
|||
### Files touched
|
||||
|
||||
```
|
||||
PROMPT.md (new, top-level)
|
||||
[historical sprint-instruction file] (removed from the current tree)
|
||||
docs/FINDINGS-2.x.md (new)
|
||||
tests/test_audit_flake.sh (new)
|
||||
src/zupt_main.c (F-01)
|
||||
|
|
@ -3345,20 +3964,20 @@ Two `make test` runs back-to-back, both clean. Cumulative test count:
|
|||
|
||||
### Documentation cleanup
|
||||
|
||||
Four design/audit-prompt documents that were sprint-internal scratch
|
||||
Four design/audit-instruction documents that were sprint-internal scratch
|
||||
have been removed from the source tree (consolidated into the
|
||||
remaining permanent docs):
|
||||
|
||||
| Removed | Where the content lives now |
|
||||
|---|---|
|
||||
| `AUDIT_PROMPT.md` | superseded by `FORMAL_AUDIT_PROMPT.md` |
|
||||
| Two historical audit-instruction files | consolidated into the permanent audit and security documents |
|
||||
| `ROOT_CAUSE_ANALYSIS.md` | reproducible-bug postmortems are now per-release entries in `CHANGELOG.md` |
|
||||
| `COMPAT.md` | the table moved into `README.md` § "Architecture & platform support" |
|
||||
| `DONATIONS.md` | one-liner moved into `README.md` § "Supporting Zupt" |
|
||||
|
||||
Surviving canonical docs: `README.md`, `CHANGELOG.md` (this file),
|
||||
`SECURITY.md`, `INSTALL.md`, `LICENSE`, `THIRD-PARTY-NOTICES.md`,
|
||||
`AUDIT.md`, `FORMAL_AUDIT_PROMPT.md`, `ROADMAP.md`.
|
||||
`AUDIT.md` and the then-current roadmap.
|
||||
|
||||
|
||||
## [2.2.2-final2] — 2026-05-01 — CLI help, man pages, deb copyright
|
||||
|
|
@ -3468,10 +4087,9 @@ yml, and a few packaging files had no SPDX line at all. Added:
|
|||
it: `vv_ans.c`, `vv_decoder.c`, `vv_encoder.c`, `vv_huffman.c`,
|
||||
`vv_simd.c`, `vv_xxh64.c`, `vv_ans.h`, `vv_huffman.h`)
|
||||
|
||||
The 5 Jasmin `.jazz` source files previously declared "MIT License" in
|
||||
their headers as a copy-paste artifact from an earlier draft. They have
|
||||
been **relicensed to AGPL-3.0-or-later** (sole-author relicensing — no
|
||||
external contributor's work was relicensed).
|
||||
The current headers of five Jasmin `.jazz` source files were changed from MIT
|
||||
notices to AGPL-3.0-or-later notices. This describes the current revision; it
|
||||
does not revoke the MIT permissions attached to exact historical material.
|
||||
|
||||
The 5 VaptVupt headers in `vendor/zuptsdk/include/` (vaptvupt.h,
|
||||
vaptvupt_api.h, vv_ans.h, vv_huffman.h, vv_platform.h) were tagged
|
||||
|
|
@ -3537,7 +4155,7 @@ the github-old → github-new migration sprint) deleted as obsolete.
|
|||
|
||||
## [2.2.2] god-tier audit — bug #16 (block-swap attack) fix
|
||||
|
||||
Independent formal cryptographic audit (per FORMAL_AUDIT_PROMPT.md two-pass
|
||||
Independent formal cryptographic audit (using the then-current two-pass
|
||||
methodology) discovered a critical authenticated-encryption flaw in the
|
||||
shipped 2.2.2 binary. Investigation, root-cause, fix, regression test, and
|
||||
final verification documented below.
|
||||
|
|
@ -3736,7 +4354,7 @@ Compile-tested with `-Wpedantic` under GCC. Win32 code paths verified via
|
|||
- `AUDIT.md`: 2026-04-27 formal audit entry with cumulative test table
|
||||
- `README.md`: Security section bumped with audit confirmation
|
||||
- `doc/zupt.1`: SECURITY section mentions path-traversal protection
|
||||
- `FORMAL_AUDIT_PROMPT.md`: methodology document at repo root for future audits
|
||||
- the then-current formal-audit methodology document
|
||||
|
||||
## [2.2.2] — 2026-04-27
|
||||
|
||||
|
|
@ -3952,7 +4570,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|||
|
||||
### Added — Block-Level Deduplication (`--dedup`)
|
||||
|
||||
- **New `--dedup` / `-D` flag** for `zupt compress` and `zupt disk backup`. Eliminates redundant data blocks before compression using XXH64 fingerprinting with full content verification on match.
|
||||
- **New `--dedup` / `-D` flag** for `zupt compress` and `zupt disk backup`. Eliminates redundant data blocks before compression using XXH64 fingerprinting (strengthened with an independent SHA-256/128 match in 5.2.2).
|
||||
- **New block type `ZUPT_BLOCK_DEDUP_REF` (0x04)**: Reference blocks store an 8-byte offset to the original data block instead of the full block payload. A 4MB duplicate block becomes 8 bytes.
|
||||
- **Hash table index**: Open-addressing with linear probing, capped at 2M entries (~48MB RAM). 75% load factor limit. Secure wipe on free.
|
||||
- **Content verification**: XXH64 fingerprint match is verified by block size comparison to prevent hash-collision corruption.
|
||||
|
|
@ -4127,8 +4745,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|||
## [1.5.0] — 2026-03-28
|
||||
|
||||
### Added — Jasmin Assembly Integration (Sprint 1)
|
||||
- **`zupt_mac_verify_ct`** Jasmin assembly linked into `zupt_decrypt_buffer()`. Replaces the C XOR accumulation loop for HMAC-SHA256 comparison. 4×u64 unrolled XOR, proven constant-time by Jasmin type system. Symbol confirmed active via `nm`: `T zupt_mac_verify_ct`.
|
||||
- **`zupt_ct_select_32`** Jasmin assembly linked into `zupt_mlkem768_decaps()`. Replaces the C `cmov()` function for Fujisaki-Okamoto implicit rejection key selection. 4×u64 masked select, proven constant-time. Symbol confirmed active via `nm`: `T zupt_ct_select_32`.
|
||||
- **`zupt_mac_verify_ct`** Jasmin assembly linked into `zupt_decrypt_buffer()`. Replaces the C XOR accumulation loop for HMAC-SHA256 comparison. 4×u64 unrolled XOR, then described as proven constant-time; 5.2.2 records that no reproducible proof artifact was retained. Symbol confirmed active via `nm`: `T zupt_mac_verify_ct`.
|
||||
- **`zupt_ct_select_32`** Jasmin assembly linked into `zupt_mlkem768_decaps()`. Replaces the C `cmov()` function for Fujisaki-Okamoto implicit rejection key selection. 4×u64 masked select, with the same historical proof qualification above. Symbol confirmed active via `nm`: `T zupt_ct_select_32`.
|
||||
- **`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`.
|
||||
|
|
@ -4160,8 +4778,9 @@ All 4 `.jazz` files rewritten to fix compilation errors:
|
|||
|
||||
### Changed
|
||||
- Removed all `-CT` flag references (does not exist in jasminc 2026.03.0).
|
||||
- CT enforced by Jasmin type system during normal compilation.
|
||||
- Safety: `jasminc -arch x86-64 -checksafety`.
|
||||
- The release claimed CT enforcement by the Jasmin type system during normal
|
||||
compilation and use of `jasminc -arch x86-64 -checksafety`; no reproducible
|
||||
certificate/log for those claims was retained (documented in 5.2.2).
|
||||
- All compound expressions split into separate register operations.
|
||||
- All output parameters changed from `reg ptr` to `reg u64` raw pointers.
|
||||
- Byte-level access avoided: 4×u64 instead of 32×u8.
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
project(zupt VERSION 0.4.0 LANGUAGES C)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(SOURCES
|
||||
src/zupt_main.c src/zupt_format.c src/zupt_lz.c src/zupt_lzh.c src/zupt_xxh.c
|
||||
src/zupt_sha256.c src/zupt_aes256.c src/zupt_crypto.c src/zupt_predict.c)
|
||||
add_executable(zupt ${SOURCES})
|
||||
target_include_directories(zupt PRIVATE include)
|
||||
target_link_libraries(zupt m)
|
||||
if(MSVC)
|
||||
target_compile_options(zupt PRIVATE /W4 /D_CRT_SECURE_NO_WARNINGS)
|
||||
else()
|
||||
target_compile_options(zupt PRIVATE -Wall -Wextra -O2)
|
||||
endif()
|
||||
install(TARGETS zupt DESTINATION bin)
|
||||
469
DISTRIBUTION.md
469
DISTRIBUTION.md
|
|
@ -1,258 +1,327 @@
|
|||
# Distributing VaptVupt
|
||||
# Distributing ZUPT 5.2.8
|
||||
|
||||
This document describes the upstream packaging recipes shipped under `packaging/` and the path from a local source tree to an installable package.
|
||||
This document describes the packaging material maintained in the ZUPT
|
||||
source repository. A recipe in `packaging/` is not evidence that a package has
|
||||
been accepted by a distribution or that every target platform has been tested.
|
||||
Record each build and test result separately; an unexecuted target is a skip.
|
||||
|
||||
Real submission to AUR / Debian / Fedora / Homebrew / NixOS / openSUSE is operational work outside this repository.
|
||||
The canonical repository is:
|
||||
|
||||
## Producing a reproducible source tarball
|
||||
|
||||
Every packaging recipe expects an upstream tarball `vaptvupt-VERSION.tar.gz` produced by the project's `make dist` target. The tarball is byte-reproducible:
|
||||
|
||||
```sh
|
||||
make dist
|
||||
# → /tmp/vaptvupt-5.0.0.tar.gz
|
||||
```text
|
||||
https://github.com/cristiancmoises/zupt
|
||||
```
|
||||
|
||||
Re-running `make dist` on the same source tree produces an identical sha256 (verified by `tests/test_dist_reproducible.sh`, wired into `make test`). This lets distros pin a stable hash in their recipes.
|
||||
GitHub is the canonical source and release host. Packaging must never fetch
|
||||
`zupt-web` or substitute an asset from another project.
|
||||
|
||||
The reproducibility properties:
|
||||
The `v5.2.2`, `v5.2.3`, `v5.2.4`, `v5.2.5`, `v5.2.6`, and `v5.2.7` tags are
|
||||
immutable non-promoted candidates.
|
||||
The v5.2.3 source-policy test assumed LF for a Windows `.bat` file that Git
|
||||
correctly checks out as CRLF. Exact-tag GitHub Actions run `33431386002` then
|
||||
recorded 12 successful v5.2.4 jobs, one openSUSE service-harness failure caused
|
||||
by its working directory, and skipped dependent Windows/macOS jobs. A local
|
||||
Tumbleweed reproduction confirmed that `refs/tags/v5.2.4` is valid and that
|
||||
entering the service directory completes the source-service chain. Corrective
|
||||
working-directory integration was carried by v5.2.5, whose exact-tag GitHub
|
||||
Actions run `33434986357` completed 13 jobs successfully but failed the native
|
||||
Windows and macOS jobs. Its v5.2.6 corrections reached exact-tag run
|
||||
`33442264243`, where 13 jobs succeeded but macOS arm64 failed on unused x86
|
||||
SHA-NI test-helper declarations under `-Werror`, and Windows aborted during safe
|
||||
UTF-8 fixture argv transcoding. Version 5.2.7 corrected those failures, but
|
||||
exact-tag run `33445470664` ended with 13 successful jobs, a macOS raw-C1
|
||||
fixture failure with `EILSEQ`, and a cancelled Windows job after the hosted job
|
||||
stalled in `make check`; a MinGW/Wine reproduction isolated a non-console
|
||||
password-prompt hang in `_getch`. Manual 5.2.8 pre-tag run `33452602634`
|
||||
subsequently passed 14 of 15 jobs, including native macOS and the complete
|
||||
Windows distribution checks, before an old MSYS `grep` non-BMP pattern failed
|
||||
in the later smoke. ZUPT's redirected listing was byte-correct; the corrected
|
||||
gate uses byte-exact, locale-independent checks and requires extraction plus a
|
||||
full tree diff. The failed run is diagnostic evidence only.
|
||||
Exact-tag run `33456209269` subsequently passed all 15 jobs at
|
||||
`ebb9ab3aa1d42c50030ca02883f6162dc4771fe1`, including the pinned local OBS
|
||||
source-service chain, native
|
||||
Windows/macOS, and every package gate. Promotion run `33457868306` published
|
||||
the exact tested 13-file set; the source archive SHA-256 is
|
||||
`378b9506211545b9594cf0d38ac8955d9b1cac34eb6b379ae0ec26b84edb65f7`.
|
||||
Corrective packages and release assets use `v5.2.8`; never move or
|
||||
overwrite an earlier tag or checksum, and never transfer prior evidence
|
||||
automatically. Version 5.2.8 corrects those native test boundaries, hardens
|
||||
three path-race boundaries, and adds the SDK regression to release/hosted Linux
|
||||
gates. The archive format, cryptography, codec, and SDK ABI remain unchanged.
|
||||
|
||||
- Files sorted by name (deterministic order across filesystems)
|
||||
- mtime fixed to `SOURCE_DATE_EPOCH` (default `1747699200`; override via env)
|
||||
- uid/gid pinned to root (0/0) via `--owner=0 --group=0 --numeric-owner`
|
||||
- gzip wrapped with `-9n` (no embedded timestamp or filename)
|
||||
- Source-only — no `.o`, no built binaries, no `.git/` tree
|
||||
## Source-only boundary
|
||||
|
||||
The tree is source-only. The default `make` build needs only a C compiler, make, libm, and pthread — no external crypto library, and it installs no `.so`. The optional SDK-backed modes (`--pq-sdk`, `--pq-box`) and the Argon2id KDF are built only with `make WITH_SDK=1` against the separately distributed `libzuptsdk` / `libpqvaptvupt` libraries.
|
||||
Git, `git archive`, and the upstream source tarball contain source code,
|
||||
textual assembly, documentation, packaging metadata, and necessary data files.
|
||||
They do not contain compiled objects or executables, shared or static libraries,
|
||||
or DEB/RPM/AppImage packages.
|
||||
|
||||
To force a specific epoch (for distro release-day pinning):
|
||||
The default build is deliberately independent of the optional SDK and PQBOX
|
||||
libraries:
|
||||
|
||||
```sh
|
||||
SOURCE_DATE_EPOCH=1727740800 make dist # 2024-10-01 UTC
|
||||
make clean
|
||||
make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf 1)" \
|
||||
WITH_SDK=0 WITH_PQBOX=0
|
||||
make WITH_SDK=0 WITH_PQBOX=0 check
|
||||
```
|
||||
|
||||
## Recipes shipped
|
||||
`WITH_SDK=1` and `WITH_PQBOX=1` use separately installed system development
|
||||
libraries. They never load a library committed under `vendor/`, never download
|
||||
a dependency during build or test, and fail explicitly when their development
|
||||
metadata is unavailable. Distribution builds should keep both options at `0`
|
||||
unless the corresponding source-built system packages are declared as build
|
||||
requirements.
|
||||
|
||||
| Distro / Platform | Path | Format |
|
||||
|-------------------|---------------------------------|----------------|
|
||||
| Arch Linux | `packaging/aur/PKGBUILD` | AUR PKGBUILD |
|
||||
| Debian / Ubuntu | `packaging/debian/` | Source package (`3.0 (quilt)`) |
|
||||
| Fedora / RHEL | `packaging/rpm/vaptvupt.spec` | RPM .spec |
|
||||
| openSUSE | `packaging/opensuse/` | RPM .spec (OBS) |
|
||||
| macOS | `packaging/homebrew/vaptvupt.rb`| Homebrew formula |
|
||||
| NixOS / Nix flake | `packaging/nix/flake.nix` | Nix flake |
|
||||
|
||||
All recipes:
|
||||
|
||||
- Install the binary to `$PREFIX/bin/vaptvupt` (default `/usr/bin/vaptvupt`)
|
||||
- Install manpage to `$PREFIX/share/man/man1/vaptvupt.1.gz`
|
||||
- Install docs (README, SECURITY, CHANGELOG) to `$PREFIX/share/doc/vaptvupt/`
|
||||
- Run the full upstream regression suite (`make test`) during build when the distro's package guidelines allow check-phase execution
|
||||
|
||||
## Arch Linux (AUR)
|
||||
|
||||
Maintainer flow:
|
||||
Audit the current tree or a generated archive with:
|
||||
|
||||
```sh
|
||||
# 1. Produce the upstream tarball
|
||||
make dist
|
||||
# → /tmp/vaptvupt-5.0.0.tar.gz
|
||||
|
||||
# 2. Upload to a stable URL (e.g. git.securityops.co releases)
|
||||
|
||||
# 3. Update packaging/aur/PKGBUILD:
|
||||
# - Set pkgver=5.0.0
|
||||
# - Set sha256sums=("$(sha256sum /tmp/vaptvupt-5.0.0.tar.gz | awk '{print $1}')")
|
||||
|
||||
# 4. Generate .SRCINFO
|
||||
cd packaging/aur && makepkg --printsrcinfo > .SRCINFO
|
||||
|
||||
# 5. Test locally
|
||||
makepkg -s
|
||||
|
||||
# 6. Push to AUR
|
||||
git clone ssh://aur@aur.archlinux.org/vaptvupt.git aur-vaptvupt
|
||||
cp packaging/aur/PKGBUILD packaging/aur/.SRCINFO aur-vaptvupt/
|
||||
cd aur-vaptvupt && git add -A && git commit -m "v5.0.0" && git push
|
||||
scripts/check-source-only.sh
|
||||
scripts/check-source-only.sh --archive /path/to/zupt-5.2.8.tar.gz
|
||||
```
|
||||
|
||||
User install:
|
||||
The scanner reports paths, not file contents, and exits nonzero on a violation.
|
||||
|
||||
## Reproducible source archive
|
||||
|
||||
`make dist` verifies committed `HEAD` and exports its tree object, normalizes
|
||||
member order, timestamps, owner/group metadata, and gzip metadata, and audits
|
||||
the result before moving it to its destination. Exporting the tree rather than
|
||||
the commit omits Git's commit-ID PAX header:
|
||||
|
||||
```sh
|
||||
yay -S vaptvupt # or paru, pikaur, etc.
|
||||
make DIST_TARBALL=/tmp/zupt-5.2.8.tar.gz dist
|
||||
sha256sum /tmp/zupt-5.2.8.tar.gz
|
||||
```
|
||||
|
||||
## Shell completions
|
||||
The canonical release uses the tracked `.source-date-epoch`; an explicit
|
||||
`SOURCE_DATE_EPOCH` override intentionally creates a different archive. With
|
||||
identical committed input and epoch, repeated exports must have the same
|
||||
SHA-256 digest. Do not generate a release tarball from uncommitted working-tree
|
||||
files.
|
||||
|
||||
`make install` automatically installs Bash, zsh, and fish completion files alongside the binary and manpage:
|
||||
The AUR, Homebrew, and Guix recipes pin the checksum of this tarball. They are
|
||||
marked `export-ignore` in `.gitattributes` so their own checksum fields do not
|
||||
make the archive self-referential. A commit changing only those ignored recipes
|
||||
therefore leaves the fixed-epoch archive byte-identical. The recipes remain
|
||||
versioned in Git and must be updated after the final source archive checksum is
|
||||
known.
|
||||
|
||||
| Shell | Path |
|
||||
|---|---|
|
||||
| Bash | `$PREFIX/share/bash-completion/completions/vaptvupt` |
|
||||
| zsh | `$PREFIX/share/zsh/site-functions/_vaptvupt` |
|
||||
| fish | `$PREFIX/share/fish/vendor_completions.d/vaptvupt.fish` |
|
||||
Do not commit the generated tarball or checksum file. Host them as immutable
|
||||
release assets after the release tag is published.
|
||||
|
||||
The source files live under `completions/` in the project tree. Distros that prefer a different install location should override the relevant paths in their `make install` invocation.
|
||||
## Staged installation
|
||||
|
||||
For per-user installation without root:
|
||||
Packagers should preserve distribution flags and install into a package root:
|
||||
|
||||
```sh
|
||||
# Bash
|
||||
cp completions/vaptvupt.bash ~/.local/share/bash-completion/completions/vaptvupt
|
||||
|
||||
# zsh (somewhere in $fpath; add the directory to ~/.zshrc if needed)
|
||||
cp completions/_vaptvupt ~/.zsh/completion/_vaptvupt
|
||||
|
||||
# fish
|
||||
cp completions/vaptvupt.fish ~/.config/fish/completions/vaptvupt.fish
|
||||
make -j"${JOBS:-1}" WITH_SDK=0 WITH_PQBOX=0 \
|
||||
CPPFLAGS="$CPPFLAGS" CFLAGS="$CFLAGS" \
|
||||
LDFLAGS="$LDFLAGS" LDLIBS="$LDLIBS"
|
||||
make WITH_SDK=0 WITH_PQBOX=0 check
|
||||
make DESTDIR="$pkgroot" PREFIX=/usr \
|
||||
WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 install
|
||||
```
|
||||
|
||||
Completions cover every CLI flag the binary actually parses (`--kdf`, `--comment`, `--comment-file`, `--pq`, `--dedup`, etc.) and are validated on every CI run via `tests/test_completions_manpage.sh`.
|
||||
`INSTALL_LEGACY_ALIAS=0` installs only `zupt`. The `vaptvupt`
|
||||
command can be requested explicitly with `INSTALL_LEGACY_ALIAS=1`, but it is
|
||||
not installed by default and is not part of the openSUSE main package. This
|
||||
keeps the canonical package surface limited to ZUPT and `zupt`.
|
||||
|
||||
## Debian / Ubuntu
|
||||
The Makefile accepts the usual `BINDIR`, `LIBDIR`, `INCLUDEDIR`, `MANDIR`, and
|
||||
completion-directory overrides. It does not strip package builds or add a
|
||||
private-library RPATH.
|
||||
|
||||
The `packaging/debian/` tree is a Debian source-package layout. Maintainer flow:
|
||||
## Packaging material
|
||||
|
||||
| Target | Maintained path | Intended output |
|
||||
|---|---|---|
|
||||
| openSUSE / OBS | `packaging/opensuse/` | source and binary RPM through OBS |
|
||||
| Debian / Ubuntu | `packaging/debian/`, `packaging/build-deb.sh` | Debian metadata and binary DEB after the target gate |
|
||||
| RPM release artifact | `packaging/opensuse/zupt.spec`, `packaging/build-rpm.sh` | source and binary RPM after the target gate |
|
||||
| GUI DEB | `packaging/build-gui-deb.sh` | `zupt-gui_5.2.8_all.deb` after payload/dependency and installed integration gates |
|
||||
| GUI RPM | `packaging/build-gui-rpm.sh` | `zupt-gui-5.2.8-1.noarch.rpm` and matching `.src.rpm` after package and installed integration gates |
|
||||
| Linux CLI archive | `.github/workflows/ci.yml` | `zupt-5.2.8-linux-x86_64.tar.xz` with notices after dependency, member, and extracted functional gates |
|
||||
| Portable GUI source | `packaging/portable/`, `.github/workflows/ci.yml` | `zupt-gui-5.2.8-portable.zip` after source scan, member allowlist, and extracted off-screen integration gate |
|
||||
| Fedora / RPM-based systems | `packaging/rpm/zupt.spec` | downstream RPM starting point |
|
||||
| AppImage helper | `packaging/build-appimage.sh` | downstream-only helper; no 5.2.8 AppImage is promoted |
|
||||
| Windows | `.github/workflows/cross-platform.yml` | native ZIP (executable plus notices) after the required native gate |
|
||||
| macOS | `packaging/build-dmg.sh` | native-architecture DMG after the native gate |
|
||||
| Arch Linux | `packaging/aur/PKGBUILD` | AUR package recipe |
|
||||
| Homebrew | `packaging/homebrew/zupt.rb` | formula-built package |
|
||||
| Guix | `packaging/guix/zupt.scm` | Guix package definition |
|
||||
| Nix | `packaging/nix/flake.nix` | flake-built package |
|
||||
|
||||
These files are upstream starting points. Use each distribution's isolated
|
||||
builder and current policy checks; do not claim support based only on parsing a
|
||||
recipe.
|
||||
|
||||
### openSUSE / OBS
|
||||
|
||||
The authoritative instructions, tested matrix, and outstanding gates are in
|
||||
`packaging/opensuse/README.md`. The normal local flow is:
|
||||
|
||||
```sh
|
||||
# 1. Produce the upstream tarball with the standard Debian
|
||||
# orig.tar.gz naming convention:
|
||||
make dist
|
||||
cp /tmp/vaptvupt-5.0.0.tar.gz /tmp/vaptvupt_5.0.0.orig.tar.gz
|
||||
|
||||
# 2. Unpack and overlay the debian/ tree:
|
||||
cd /tmp && tar xzf vaptvupt_5.0.0.orig.tar.gz && cd vaptvupt-5.0.0
|
||||
cp -a /path/to/vaptvupt/packaging/debian ./debian
|
||||
|
||||
# 3. Build the source package:
|
||||
dpkg-buildpackage -S -us -uc # source-only
|
||||
dpkg-buildpackage -b -us -uc # binary
|
||||
|
||||
# 4. Lint:
|
||||
lintian vaptvupt_5.0.0-1_*.deb
|
||||
|
||||
# 5. Submit via the standard Debian mentors process:
|
||||
# https://mentors.debian.net/intro-maintainers/
|
||||
cd packaging/opensuse
|
||||
xmllint --noout _service
|
||||
osc service manualrun
|
||||
rpmspec -P zupt.spec >/dev/null
|
||||
osc build openSUSE_Tumbleweed x86_64 zupt.spec
|
||||
```
|
||||
|
||||
User install (after the package lands in Debian unstable / Ubuntu):
|
||||
Run `rpmlint` on all produced RPMs and install the binary RPM in a disposable
|
||||
environment for `--version`, `--help`, and archive round-trip tests. Presence of
|
||||
the OBS files upstream does not mean the package has been submitted or accepted
|
||||
by openSUSE Factory.
|
||||
|
||||
### Debian and RPM release artifacts
|
||||
|
||||
The release helper scripts build from this source tree, stage into temporary
|
||||
directories, run their format and installed-binary checks, and place only their
|
||||
final outputs in an explicitly selected directory. Run them from an exact
|
||||
checkout of the immutable tag inside a clean target container, chroot, or VM:
|
||||
|
||||
```sh
|
||||
sudo apt install vaptvupt
|
||||
release_dir=$(mktemp -d)
|
||||
|
||||
# Native Debian/Ubuntu binary package
|
||||
DIST_DIR="$release_dir" RUN_CHECKS=1 packaging/build-deb.sh
|
||||
|
||||
# Source and binary RPM using the openSUSE spec
|
||||
DIST_DIR="$release_dir" packaging/build-rpm.sh
|
||||
|
||||
# Architecture-independent GUI DEB and noarch/source GUI RPM
|
||||
DIST_DIR="$release_dir" packaging/build-gui-deb.sh
|
||||
DIST_DIR="$release_dir" packaging/build-gui-rpm.sh
|
||||
```
|
||||
|
||||
## Fedora / RHEL / CentOS
|
||||
`packaging/build-deb.sh` creates a native binary DEB; it does not claim to
|
||||
create a Debian source package. The files in `packaging/debian/` are Debian
|
||||
source-package metadata and must be staged as the source package's top-level
|
||||
`debian/` directory before using `dpkg-buildpackage`. Running
|
||||
`dpkg-buildpackage` directly at the ZUPT repository root is not the
|
||||
documented release-artifact path.
|
||||
|
||||
`packaging/build-rpm.sh` creates its audited Source0 archive, builds both the
|
||||
binary RPM and source RPM, inspects the installed payload, and copies both
|
||||
outputs to `DIST_DIR`. The separate `packaging/rpm/zupt.spec` is a
|
||||
Fedora-family downstream starting point; build and lint it only after staging
|
||||
Source0 in a normal RPM build tree.
|
||||
|
||||
Run the target's metadata and lint tools in addition to the script gates. A
|
||||
package built for one distribution release or architecture is not evidence for
|
||||
another.
|
||||
|
||||
The GUI helpers package Python/Qt source rather than compiled application code.
|
||||
They validate exact version, payload, dependency, ownership and legacy-alias
|
||||
expectations, then test the installed launcher off-screen against the matching
|
||||
`zupt` CLI. A successful GUI DEB gate does not imply an RPM gate, or vice versa.
|
||||
|
||||
### Portable and native release artifacts
|
||||
|
||||
The Linux x86_64 gate packages the tested `zupt` executable as
|
||||
`zupt-5.2.8-linux-x86_64.tar.xz` beside README, changelog, security guidance,
|
||||
and every applicable public license and notice. Its dynamic-library allowlist,
|
||||
archive member allowlist, and extracted CLI functional suite must pass.
|
||||
|
||||
The `zupt-gui-5.2.8-portable.zip` artifact is source-only: it contains the GUI
|
||||
Python source, shell/macOS/Windows launchers, icons, provenance, changelog, and
|
||||
licenses, but no Python, Qt, CLI, or compiled runtime. The gate scans both the
|
||||
assembled and extracted trees, verifies an exact safe member allowlist, and
|
||||
runs the extracted launcher off-screen against the tested CLI.
|
||||
|
||||
AppImage creation is deliberately offline and is not a 5.2.8 release gate.
|
||||
Supply a locally verified `appimagetool`, type-2 runtime, and the complete
|
||||
license/source-relink compliance notice for those exact runtime bytes; the
|
||||
helper never downloads any input:
|
||||
|
||||
```sh
|
||||
# 1. Produce the tarball
|
||||
make dist
|
||||
cp /tmp/vaptvupt-5.0.0.tar.gz ~/rpmbuild/SOURCES/
|
||||
|
||||
# 2. Drop the .spec into the SPECS directory:
|
||||
cp packaging/rpm/vaptvupt.spec ~/rpmbuild/SPECS/
|
||||
|
||||
# 3. Build source + binary RPMs:
|
||||
cd ~/rpmbuild && rpmbuild -ba SPECS/vaptvupt.spec
|
||||
|
||||
# 4. Lint:
|
||||
rpmlint RPMS/x86_64/vaptvupt-5.0.0-1.fc*.rpm
|
||||
|
||||
# 5. Submit via the Fedora new-package review process:
|
||||
# https://docs.fedoraproject.org/en-US/package-maintainers/Package_Review_Process/
|
||||
# EPEL automatically inherits Fedora packages.
|
||||
DIST_DIR="$release_dir" RUN_CHECKS=1 \
|
||||
APPIMAGETOOL=/verified/path/appimagetool \
|
||||
APPIMAGE_RUNTIME_FILE=/verified/path/runtime-x86_64 \
|
||||
APPIMAGE_RUNTIME_COMPLIANCE_FILE=/verified/path/runtime-compliance.txt \
|
||||
packaging/build-appimage.sh
|
||||
```
|
||||
|
||||
User install (after the package lands in Fedora / EPEL):
|
||||
The runtime inspected while preparing 5.2.2 omitted a linked component from
|
||||
its notice and did not provide the complete LGPL source/relink handoff required
|
||||
by this release policy. No AppImage produced by this helper is promoted by the
|
||||
upstream 5.2.8 workflow. AppDir and Flatpak bundles and GUI platform installers
|
||||
are also excluded. Bare Linux and Windows executables are not promoted; their
|
||||
CLI programs appear only inside notice-bearing archives. The Windows ZIP and
|
||||
macOS DMG remain CLI-only.
|
||||
|
||||
Run `packaging/build-dmg.sh` only on a native macOS host. It records the host
|
||||
architecture in the filename and tests the binary before and after packaging:
|
||||
|
||||
```sh
|
||||
sudo dnf install vaptvupt # Fedora
|
||||
sudo dnf install epel-release vaptvupt # RHEL/CentOS via EPEL
|
||||
DIST_DIR="$release_dir" RUN_CHECKS=1 packaging/build-dmg.sh
|
||||
```
|
||||
|
||||
## openSUSE
|
||||
The Windows ZIP (including its executable and notices) must be built and tested
|
||||
by the Windows job in `.github/workflows/cross-platform.yml`; it is not a
|
||||
cross-compiled release claim from a Linux build. No Wine result is retained as
|
||||
5.2.8 release evidence. Extended-length/device namespace paths, raw UNC output
|
||||
roots, and mapped/network-drive output are not supported in 5.2.8. Publish the
|
||||
exact architecture recorded by the native job.
|
||||
These helpers create binary distribution artifacts for the release page, not
|
||||
content to be committed to Git or included in the source archive.
|
||||
|
||||
The `packaging/opensuse/` tree carries an RPM `.spec` suited to the Open Build Service (OBS).
|
||||
### AUR, Homebrew, Guix, and Nix
|
||||
|
||||
```sh
|
||||
# 1. Produce the tarball
|
||||
make dist
|
||||
After calculating the final reproducible source archive, but before creating or
|
||||
publishing the immutable tag, update each recipe to version 5.2.8 and to the
|
||||
exact digest or content hash expected by its package manager. These recipe
|
||||
directories are excluded from the source archive, so this does not create a
|
||||
checksum cycle. Commit the pinned recipes in the tagged tree, then build and
|
||||
test with each package manager before publishing its recipe. Keep build inputs
|
||||
offline-capable: the check phase must not fetch source or dependencies
|
||||
dynamically.
|
||||
|
||||
# 2. In an OBS package checkout (osc), stage the sources and spec:
|
||||
cp /tmp/vaptvupt-5.0.0.tar.gz .
|
||||
cp /path/to/vaptvupt/packaging/opensuse/vaptvupt.spec .
|
||||
## Release-page artifacts
|
||||
|
||||
# 3. Build locally against a target repository:
|
||||
osc build openSUSE_Tumbleweed x86_64
|
||||
The source-only policy applies to Git and upstream source archives. A release
|
||||
page may also carry CLI/GUI DEB and RPM artifacts, the notice-bearing Linux CLI
|
||||
tar.xz, source-only portable GUI ZIP, CLI Windows ZIP, or CLI macOS DMG when
|
||||
each is built from the tagged source in its target environment and passes its
|
||||
format-specific tests. These are separate outputs, never inputs to a source
|
||||
build.
|
||||
|
||||
# 4. Commit to OBS once the build and check phase pass:
|
||||
osc addremove && osc commit
|
||||
```
|
||||
For every published artifact:
|
||||
|
||||
User install (after the package lands in a distribution or OBS repository):
|
||||
1. start from the immutable `v5.2.8` tag;
|
||||
2. keep `WITH_SDK=0 WITH_PQBOX=0` unless system dependencies are declared;
|
||||
3. record the exact OS, distribution release, architecture, and toolchain;
|
||||
4. run format validation plus installed `--version`, `--help`, and archive
|
||||
round-trip tests;
|
||||
5. publish a SHA-256 checksum;
|
||||
6. scan the source inputs and ensure no credential or build path is embedded;
|
||||
7. label an unbuilt or untested target `SKIP`, never `PASS`.
|
||||
|
||||
```sh
|
||||
sudo zypper install vaptvupt
|
||||
```
|
||||
Do not infer multi-architecture compatibility from portable source. Do not add
|
||||
precompiled optional libraries to make a package build.
|
||||
|
||||
## macOS (Homebrew)
|
||||
The 13 gated assets are published at the
|
||||
[canonical GitHub release](https://github.com/cristiancmoises/zupt/releases/tag/v5.2.8).
|
||||
If an expected asset is absent or has a different checksum, report that target
|
||||
as unpublished rather than redirecting consumers to an unverified file.
|
||||
|
||||
```sh
|
||||
# 1. Produce the tarball and upload to a stable release URL.
|
||||
## Downstream checklist
|
||||
|
||||
# 2. Update packaging/homebrew/vaptvupt.rb:
|
||||
# - Set url to the release URL
|
||||
# - Set sha256 to the upstream tarball sha256
|
||||
|
||||
# 3. Test locally:
|
||||
brew install --build-from-source ./packaging/homebrew/vaptvupt.rb
|
||||
brew test vaptvupt
|
||||
brew audit --strict --online vaptvupt
|
||||
|
||||
# 4. Submit to homebrew-core (preferred, requires popularity threshold):
|
||||
# https://docs.brew.sh/Adding-Software-to-Homebrew
|
||||
#
|
||||
# OR host in your own tap:
|
||||
# https://docs.brew.sh/How-to-Create-and-Maintain-a-Tap
|
||||
```
|
||||
|
||||
User install (after submission lands):
|
||||
|
||||
```sh
|
||||
brew install vaptvupt
|
||||
# OR from a custom tap:
|
||||
brew install cristiancmoises/tap/vaptvupt
|
||||
```
|
||||
|
||||
## NixOS / Nix flake
|
||||
|
||||
```sh
|
||||
# 1. Build directly from the flake (no central submission needed):
|
||||
nix build github:cristiancmoises/vaptvupt#vaptvupt
|
||||
nix run github:cristiancmoises/vaptvupt#vaptvupt -- version
|
||||
|
||||
# 2. To consume from another flake:
|
||||
# inputs.vaptvupt.url = "github:cristiancmoises/vaptvupt?ref=v5.0.0";
|
||||
# packages.x86_64-linux.default = inputs.vaptvupt.packages.x86_64-linux.vaptvupt;
|
||||
|
||||
# 3. To submit to nixpkgs (https://github.com/NixOS/nixpkgs):
|
||||
# - Adapt packaging/nix/flake.nix's `vaptvupt` derivation into a
|
||||
# pkgs/by-name/va/vaptvupt/package.nix using fetchurl and a hash.
|
||||
# - Follow the nixpkgs contribution guide:
|
||||
# https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md
|
||||
```
|
||||
|
||||
## Submitting upstream — checklist
|
||||
|
||||
Before pushing any recipe to a distro repository:
|
||||
|
||||
- [ ] `make dist` produces a reproducible tarball (verified by `tests/test_dist_reproducible.sh` on every `make test`)
|
||||
- [ ] The tarball is uploaded to a stable, immutable URL
|
||||
- [ ] The recipe's checksum field is updated to match `sha256sum /tmp/vaptvupt-VERSION.tar.gz`
|
||||
- [ ] The recipe builds and tests pass in a clean chroot/container
|
||||
- [ ] The CHANGELOG mentions distro-relevant changes since the last release
|
||||
- [ ] The license metadata is correct (AGPL-3.0-or-later for VaptVupt core; GPL-3.0-or-later for the vendored VaptVupt codec)
|
||||
|
||||
## Security posture for downstream
|
||||
|
||||
Every packaging recipe runs `make test` during build (`check()` for AUR, `override_dh_auto_test` for Debian, `%check` for RPM and openSUSE, `checkPhase` for Nix, `test` block for Homebrew). The test suite runs in each recipe's check phase, including the tamper/integrity regressions and the `make dist` byte-identical reproducibility check.
|
||||
|
||||
A build that doesn't pass `make test` will fail at distro check time — the recipes don't paper over regressions.
|
||||
- [ ] The source URL resolves to the immutable `v5.2.8` tag.
|
||||
- [ ] The source archive passes `scripts/check-source-only.sh --archive`.
|
||||
- [ ] The recipe checksum matches the downloaded source exactly.
|
||||
- [ ] `WITH_SDK=0 WITH_PQBOX=0` is explicit, or system dependencies are complete.
|
||||
- [ ] Distribution compiler and linker flags are preserved.
|
||||
- [ ] The real upstream `check` target runs without network access.
|
||||
- [ ] Installation uses `DESTDIR` and does not write under `/usr/local`.
|
||||
- [ ] The main package installs `zupt`; any `vaptvupt` alias is explicitly documented as compatibility-only.
|
||||
- [ ] Licenses include AGPL-3.0-or-later for the application,
|
||||
GPL-3.0-or-later for the bundled source codec, and BSD-2-Clause for the
|
||||
xxHash-derived XXH64 routines, plus CC0-1.0 for the
|
||||
pq-crystals/kyber-derived ML-KEM portions and BSD-3-Clause for the
|
||||
curve25519-donna-derived X25519 portions.
|
||||
- [ ] Package contents, dependencies, hardening, RPATH/RUNPATH, and debug info
|
||||
have been inspected with target-native tools.
|
||||
- [ ] Installed-package smoke and round-trip tests pass.
|
||||
- [ ] Only tested target artifacts are attached to the release.
|
||||
|
|
|
|||
427
INSTALL.md
427
INSTALL.md
|
|
@ -1,232 +1,255 @@
|
|||
# VaptVupt + VaptVupt GUI — Install Guide for Linux
|
||||
# Installing ZUPT 5.2.8
|
||||
|
||||
If you're seeing the error:
|
||||
This guide covers the ZUPT command-line program and the optional Python GUI.
|
||||
The canonical source repository is
|
||||
`https://github.com/cristiancmoises/zupt`.
|
||||
|
||||
```
|
||||
vaptvupt-gui depende de python3-pyqt6 | python3-pyside6; porém:
|
||||
Pacote python3-pyqt6 não está instalado.
|
||||
vaptvupt-gui depende de vaptvupt (>= 5.0.0); porém:
|
||||
Versão de vaptvupt no sistema é 2.1.7-1.
|
||||
```
|
||||
## Choosing an installation method
|
||||
|
||||
This is correct behavior. The `vaptvupt-gui` deb requires:
|
||||
- Python 3 with **PyQt6** or **PySide6** (the GUI toolkit)
|
||||
- The **vaptvupt CLI 5.0.0** or newer
|
||||
- Build from the immutable source tag when you want the upstream source-only
|
||||
path described below.
|
||||
- Use a distribution package only when it matches your distribution release
|
||||
and architecture.
|
||||
- Release-page DEB, RPM, Linux tar.xz, portable GUI ZIP, Windows ZIP, and macOS
|
||||
files are separate artifacts. Their presence does not make them part of the
|
||||
Git tree or upstream source archive. Use only artifacts whose release notes
|
||||
record a successful format-specific test for your target.
|
||||
|
||||
## The fastest fix — one command (Linux Mint, Ubuntu, Debian)
|
||||
The immutable `v5.2.2` candidate was not promoted after CI integration
|
||||
failures. The immutable `v5.2.3` candidate was not promoted because its
|
||||
source-policy test assumed LF for a Windows `.bat` file checked out as the
|
||||
required CRLF. The immutable `v5.2.4` candidate was not promoted after exact-tag
|
||||
GitHub Actions run `33431386002`: 12 jobs succeeded, the sole openSUSE
|
||||
service-harness job failed because its executor did not enter the service
|
||||
directory, and dependent Windows/macOS jobs were skipped. A local Tumbleweed
|
||||
reproduction confirmed both the explicit tag ref and the corrected
|
||||
working-directory contract. This is release/test integration only; the product,
|
||||
archive format, cryptography, codec, and SDK ABI are unchanged. The immutable
|
||||
`v5.2.5` candidate was likewise not promoted: exact-tag GitHub Actions run
|
||||
`33434986357` recorded 13 successful jobs and failed native Windows/macOS jobs.
|
||||
The immutable `v5.2.6` candidate was not promoted after run `33442264243`
|
||||
recorded 13 successful jobs and two native failures: unused x86 SHA-NI helper
|
||||
declarations on macOS arm64 under `-Werror`, and early Windows abortion while
|
||||
argv-transcoding a safe UTF-8 fixture. Version 5.2.7 corrected those boundaries
|
||||
but was not promoted after exact-tag run `33445470664`: 13 jobs succeeded,
|
||||
macOS failed because its filesystem rejected the raw-C1 filename fixture with
|
||||
`EILSEQ`, and Windows was cancelled after the hosted job stalled in `make
|
||||
check`; a MinGW/Wine reproduction isolated the cause to the non-console
|
||||
password-prompt test entering `_getch`. Version 5.2.8 makes both
|
||||
fixtures portable, hardens the three CodeQL High path-race boundaries described
|
||||
in the security documents, and adds `sdk-test` to release and hosted Linux
|
||||
gates. Exact-tag run `33456209269` passed all 15 jobs, and promotion run
|
||||
`33457868306` published the exact tested set. Do not treat any prior candidate's
|
||||
artifacts or evidence as 5.2.8 packages or validation.
|
||||
|
||||
Put all the downloaded files in the same folder, then:
|
||||
The published 5.2.8 package set is exactly these 13 gated assets:
|
||||
|
||||
```bash
|
||||
sudo bash install-zupt-gui.sh
|
||||
```
|
||||
|
||||
This script auto-detects your distribution and installs everything in
|
||||
the right order.
|
||||
|
||||
## Manual fix — three commands (if you prefer)
|
||||
|
||||
### Linux Mint / Ubuntu / Debian / Pop!_OS
|
||||
|
||||
```bash
|
||||
# 1. Install the Qt6 Python binding
|
||||
sudo apt update
|
||||
sudo apt install -y python3-pyqt6
|
||||
|
||||
# 2. Upgrade vaptvupt CLI to 5.0.0
|
||||
sudo dpkg -i vaptvupt_5.0.0_amd64.deb
|
||||
|
||||
# 3. Install the GUI
|
||||
sudo dpkg -i vaptvupt-gui_1.3.0_all.deb
|
||||
```
|
||||
|
||||
If step 3 still complains about deps, run:
|
||||
|
||||
```bash
|
||||
sudo apt --fix-broken install
|
||||
```
|
||||
|
||||
### Fedora / RHEL / Rocky / AlmaLinux
|
||||
|
||||
```bash
|
||||
sudo dnf install -y python3-pyqt6
|
||||
sudo dnf install -y vaptvupt-5.0.0-1.x86_64.rpm vaptvupt-gui-1.3.0-1.noarch.rpm
|
||||
```
|
||||
|
||||
(Or build the RPM from the SRPM tarball with `rpmbuild -bb SPECS/vaptvupt.spec`)
|
||||
|
||||
### openSUSE Leap / Tumbleweed
|
||||
|
||||
```bash
|
||||
sudo zypper install python3-pyqt6
|
||||
# Build the RPM from the source tarball — see the SRPM .tar.gz
|
||||
```
|
||||
|
||||
### Arch Linux / Manjaro / EndeavourOS
|
||||
|
||||
```bash
|
||||
sudo pacman -S python-pyqt6
|
||||
# Build vaptvupt from the source tarball
|
||||
```
|
||||
|
||||
### Anything else (or no apt/dnf/pacman handy)
|
||||
|
||||
Use the AppImage — no install needed:
|
||||
|
||||
```bash
|
||||
tar xzf VaptVupt-GUI-1.3.0-x86_64.AppDir.tar.gz
|
||||
cd vaptvupt-gui.AppDir
|
||||
./AppRun
|
||||
```
|
||||
|
||||
The AppImage still needs Python 3 + Qt6 binding on the host. For a
|
||||
fully standalone executable with no Python dependency, use a future
|
||||
PyInstaller-built version (not in this release).
|
||||
|
||||
## Why does the GUI need Qt6?
|
||||
|
||||
The VaptVupt GUI is written in Python, using either PyQt6 or PySide6 (it
|
||||
auto-detects whichever is installed). These are bindings to the Qt 6
|
||||
graphical toolkit — they're how the GUI draws windows, buttons, and
|
||||
dialogs.
|
||||
|
||||
PyQt6 is in the default repositories of major Linux distributions, so
|
||||
installing it is one apt/dnf/zypper/pacman command away. We don't bundle
|
||||
Qt6 inside the deb because:
|
||||
|
||||
- It's already on most modern systems
|
||||
- Bundling would make the deb 80 MB+ instead of 35 KB
|
||||
- Distribution-managed Qt gets security updates automatically
|
||||
|
||||
## Why does the GUI need vaptvupt 5.0.0?
|
||||
|
||||
The GUI calls `vaptvupt --pq` and `vaptvupt keygen` for native
|
||||
post-quantum encryption (ML-KEM-768 + X25519, in-tree implementation).
|
||||
Older CLI versions lack these flags, so the GUI's compress/extract will
|
||||
fail against them.
|
||||
|
||||
## After installing — verify
|
||||
|
||||
```bash
|
||||
vaptvupt version # should show: 5.0.0
|
||||
vaptvupt-gui # should launch the GUI window
|
||||
```
|
||||
|
||||
## If the GUI window still doesn't appear
|
||||
|
||||
```bash
|
||||
# Run from terminal to see error messages
|
||||
vaptvupt-gui
|
||||
|
||||
# If you see "ImportError: No module named 'PyQt6'":
|
||||
# The GUI fell back through both PyQt6 and PySide6 imports.
|
||||
# Re-check: python3 -c 'import PyQt6.QtWidgets'
|
||||
|
||||
# If you see "DISPLAY not set":
|
||||
# You're on SSH without X forwarding. Use ssh -X or run locally.
|
||||
|
||||
# If you see "qt.qpa.plugin: Could not load the Qt platform plugin":
|
||||
# Missing Qt platform plugin. On Mint/Ubuntu:
|
||||
# sudo apt install qt6-qpa-plugins
|
||||
```
|
||||
|
||||
## Reporting issues
|
||||
|
||||
If you've tried the above and vaptvupt-gui still won't work, open an issue
|
||||
at https://git.securityops.co/cristiancmoises/vaptvupt/issues with:
|
||||
|
||||
1. Output of `lsb_release -a` (or `cat /etc/os-release`)
|
||||
2. Output of `python3 --version`
|
||||
3. Output of `python3 -c 'import PyQt6; print(PyQt6.__version__)' 2>&1`
|
||||
4. Output of `vaptvupt version`
|
||||
5. Output of `vaptvupt-gui` (the error message it printed to terminal)
|
||||
|
||||
---
|
||||
|
||||
## Building from source
|
||||
|
||||
If you want to build VaptVupt from the source tarball instead of installing
|
||||
the pre-built `.deb` / `.rpm` packages, you'll need only a C compiler and
|
||||
make. The default build has NO external crypto dependency and installs no
|
||||
shared library.
|
||||
|
||||
### Build dependencies (default build)
|
||||
|
||||
| Component | Why needed |
|
||||
| Component | Gated artifacts |
|
||||
|---|---|
|
||||
| `gcc` ≥ 7 or `clang` ≥ 10 | C11 compiler |
|
||||
| `make` | build driver |
|
||||
| libm, pthread | math and threading (part of the standard C library/toolchain) |
|
||||
| Source and checksums | `zupt-5.2.8.tar.gz`, `zupt-5.2.8.tar.gz.sha256`, and `SHA256SUMS` |
|
||||
| CLI | `zupt_5.2.8_amd64.deb`, `zupt-5.2.8-0.x86_64.rpm`, `zupt-5.2.8-0.src.rpm`, `zupt-5.2.8-linux-x86_64.tar.xz`, `zupt-5.2.8-windows-x86_64.zip`, and exactly one `ZUPT-5.2.8-macOS-{x86_64\|arm64}.dmg` |
|
||||
| GUI | `zupt-gui_5.2.8_all.deb`, `zupt-gui-5.2.8-1.noarch.rpm`, `zupt-gui-5.2.8-1.src.rpm`, and `zupt-gui-5.2.8-portable.zip` |
|
||||
|
||||
The default build uses PBKDF2-SHA256 (600k iterations) for password KDF
|
||||
and the in-tree native `--pq` mode (ML-KEM-768 + X25519) for post-quantum
|
||||
encryption. No `libzuptsdk`, no OpenSSL, no libargon2 is required.
|
||||
The GUI packages require the matching `zupt` CLI package and must pass exact
|
||||
payload/dependency checks plus an installed off-screen GUI/CLI integration
|
||||
test. The source-only portable GUI ZIP bundles launchers, notices, and GUI
|
||||
source, but not Python, Qt, or the CLI. The Linux tar.xz carries the tested CLI
|
||||
beside the complete public license/notice payload. AppImage, AppDir, Flatpak
|
||||
bundles, GUI platform installers, and bare Linux/Windows executables are not
|
||||
promoted for 5.2.8. The Windows ZIP and macOS DMG contain the CLI only. Exact
|
||||
target boundaries are listed in `README.md`.
|
||||
The release's `SHA256SUMS` and validation notes, not the mere presence of a
|
||||
download link, identify an artifact that completed its gate.
|
||||
|
||||
### Install build dependencies (Debian/Ubuntu/Mint)
|
||||
Do not install a package for a different distribution or CPU architecture.
|
||||
|
||||
```bash
|
||||
sudo apt install build-essential
|
||||
## Build requirements
|
||||
|
||||
The default CLI build requires:
|
||||
|
||||
- a C11 compiler;
|
||||
- GNU make;
|
||||
- the platform C library, math library, and threading support;
|
||||
- standard build utilities including `gzip` for installation and source export.
|
||||
|
||||
It does not need a vendored binary, OpenSSL, libargon2, `libvuptsdk`, or
|
||||
`libpqvaptvupt`. Dependencies must be installed before the build; `make` does
|
||||
not download anything.
|
||||
|
||||
Typical package-manager commands are:
|
||||
|
||||
```sh
|
||||
# Debian / Ubuntu
|
||||
sudo apt install build-essential gzip
|
||||
|
||||
# Fedora / RHEL family
|
||||
sudo dnf install gcc make gzip
|
||||
|
||||
# openSUSE
|
||||
sudo zypper install gcc make gzip
|
||||
|
||||
# Arch Linux
|
||||
sudo pacman -S base-devel gzip
|
||||
```
|
||||
|
||||
### Install build dependencies (Fedora/RHEL/openSUSE)
|
||||
Package names can differ by distribution release. These commands are examples,
|
||||
not a statement that 5.2.8 has been accepted into each distribution repository.
|
||||
|
||||
```bash
|
||||
sudo dnf install gcc make # Fedora/RHEL
|
||||
sudo zypper install gcc make # openSUSE
|
||||
## Build and test from source
|
||||
|
||||
Verify the checkout or extracted archive, then use the source-only feature set:
|
||||
|
||||
```sh
|
||||
scripts/check-source-only.sh
|
||||
make clean
|
||||
make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf 1)" \
|
||||
WITH_SDK=0 WITH_PQBOX=0 V=1
|
||||
make WITH_SDK=0 WITH_PQBOX=0 check
|
||||
./zupt --version
|
||||
./zupt --help
|
||||
```
|
||||
|
||||
### Build VaptVupt itself
|
||||
From a release archive, run the scanner as follows before extraction or from a
|
||||
trusted checkout after download:
|
||||
|
||||
```bash
|
||||
tar -xzf vaptvupt-5.0.0-source.tar.gz
|
||||
cd vaptvupt-5.0.0
|
||||
|
||||
make # build the `./vaptvupt` binary
|
||||
sudo make install # install to /usr/local/bin (override with PREFIX=/usr)
|
||||
|
||||
./vaptvupt version # verify
|
||||
```sh
|
||||
scripts/check-source-only.sh --archive /path/to/zupt-5.2.8.tar.gz
|
||||
```
|
||||
|
||||
The `make` step takes 10-30 seconds. The build emits the binary as
|
||||
`./vaptvupt`. The default install prefix is `/usr/local`; override with
|
||||
`PREFIX=/usr` for system-wide install.
|
||||
The default build provides the native password, ML-KEM-768 + X25519 hybrid
|
||||
`--pq`, and ML-KEM-768 `--pq-only` paths. See `SECURITY.md` and
|
||||
`THREAT_MODEL.md` before selecting an encryption mode.
|
||||
|
||||
### Run the test suite
|
||||
For password encryption, prefer one of the explicit non-argv inputs:
|
||||
|
||||
```bash
|
||||
make test
|
||||
```sh
|
||||
# Interactive, without terminal echo; compress confirms the password.
|
||||
zupt compress --password-prompt backup.zupt files/
|
||||
|
||||
# Read the first line of a mode-0600 file.
|
||||
zupt test --pass-file /secure/path/password.txt backup.zupt
|
||||
|
||||
# Read the first line from an inherited descriptor.
|
||||
zupt extract --pass-fd 3 -o restored backup.zupt 3</secure/path/password.txt
|
||||
```
|
||||
|
||||
Covers roundtrip, multi-file, cross-block, dedup property, path-traversal,
|
||||
argument-order, and block-swap regression. Each suite reports its own
|
||||
pass/fail count.
|
||||
`-p/--password PASSWORD` remains available for compatibility, but the password
|
||||
can be visible in shell history and process listings. `--pass-file` and
|
||||
`--pass-fd` reject empty, NUL-containing, or overlong input and remove the
|
||||
line-ending delimiter.
|
||||
|
||||
### Cross-compilation
|
||||
`make check` is the downstream-safe test gate. `make test-all` runs the broader
|
||||
upstream suite. Optional tests remain conditional on their corresponding
|
||||
system-built dependencies and must be reported as skipped when unavailable.
|
||||
|
||||
VaptVupt builds on x86_64, aarch64, armhf, ppc64le, s390x, and riscv64. To
|
||||
cross-compile:
|
||||
## Install
|
||||
|
||||
```bash
|
||||
make CC=aarch64-linux-gnu-gcc # for AArch64
|
||||
make CC=arm-linux-gnueabihf-gcc # for ARMv7 (Raspberry Pi 32-bit)
|
||||
The upstream default prefix is `/usr/local`:
|
||||
|
||||
```sh
|
||||
sudo make WITH_SDK=0 WITH_PQBOX=0 install
|
||||
zupt --version
|
||||
```
|
||||
|
||||
The Makefile auto-detects target architecture via `$(CC) -dumpmachine`
|
||||
and selects the appropriate SIMD flags (NEON on AArch64, SSE4/AVX2 on
|
||||
x86_64).
|
||||
For a distribution-style `/usr` installation, or when building a package:
|
||||
|
||||
### Optional: WITH_SDK=1 build
|
||||
|
||||
The SDK-backed modes — `--pq-sdk`, `--pq-box` (sealed-box), and the
|
||||
Argon2id KDF — are optional. They are not in the default build and require
|
||||
building against the separately distributed `libzuptsdk` / `libpqvaptvupt`
|
||||
libraries:
|
||||
|
||||
```bash
|
||||
make WITH_SDK=1
|
||||
```sh
|
||||
make DESTDIR="$pkgroot" PREFIX=/usr \
|
||||
WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 install
|
||||
```
|
||||
|
||||
This build additionally needs the SDK development package and its runtime
|
||||
dependencies (OpenSSL libcrypto, libargon2), which ship with the SDK
|
||||
distribution. Without `WITH_SDK=1`, the `--pq-sdk` and `--pq-box` flags are
|
||||
unavailable; use the native `--pq` mode instead.
|
||||
`DESTDIR` stages the files below a package root; it is not embedded in installed
|
||||
paths. `PREFIX`, `BINDIR`, `LIBDIR`, `INCLUDEDIR`, and `MANDIR` can be overridden
|
||||
without replacing packager-supplied compiler or linker flags.
|
||||
|
||||
The default installation provides `zupt`. To install the `vaptvupt` command
|
||||
and manual-page compatibility aliases for versions 3.0.0 through 5.2.1, use:
|
||||
|
||||
```sh
|
||||
sudo make INSTALL_LEGACY_ALIAS=1 install
|
||||
```
|
||||
|
||||
The openSUSE main package installs `/usr/bin/zupt` as the primary command and
|
||||
does not need the optional compatibility alias.
|
||||
|
||||
To remove an installation made with the same prefix:
|
||||
|
||||
```sh
|
||||
sudo make PREFIX=/usr/local uninstall
|
||||
```
|
||||
|
||||
## Optional system integrations
|
||||
|
||||
The SDK and PQBOX integrations are independent and disabled by default:
|
||||
|
||||
```sh
|
||||
# Requires a system libvuptsdk development package or explicit SDK_* flags
|
||||
make WITH_SDK=1 WITH_PQBOX=0
|
||||
|
||||
# Requires a system libpqvaptvupt development package or explicit PQBOX_* flags
|
||||
make WITH_SDK=0 WITH_PQBOX=1
|
||||
|
||||
# Enable both only when both system dependencies are installed
|
||||
make WITH_SDK=1 WITH_PQBOX=1
|
||||
```
|
||||
|
||||
The Makefile normally obtains flags from `pkg-config`. A packager may provide
|
||||
`SDK_CPPFLAGS`/`SDK_LDLIBS` or `PQBOX_CPPFLAGS`/`PQBOX_LDLIBS` explicitly. A
|
||||
missing dependency is an error: there is no download and no fallback to a local
|
||||
precompiled library.
|
||||
|
||||
Textual assembly under `jasmin/` can be selected separately with
|
||||
`WITH_JASMIN=1` on a supported x86_64 compiler target. The directory contains
|
||||
Jasmin-generated outputs and a separately identified hand-written production
|
||||
unit; it is off by default and the portable C implementations are the baseline
|
||||
build. Do not infer that an architecture is supported until that target has
|
||||
actually built and passed its tests.
|
||||
|
||||
## GUI
|
||||
|
||||
The GUI invokes the `zupt` CLI; it does not replace the CLI or implement
|
||||
archive cryptography in Python. Install and verify the CLI first:
|
||||
|
||||
```sh
|
||||
zupt --version
|
||||
python3 -m venv ~/.local/share/zupt-gui-venv
|
||||
~/.local/share/zupt-gui-venv/bin/pip install PySide6
|
||||
~/.local/share/zupt-gui-venv/bin/python gui/src/zupt_gui.py
|
||||
```
|
||||
|
||||
The GUI can use PySide6 or PyQt6. Prefer a distribution-managed Qt binding when
|
||||
available. A package-specific installer may provide launchers and desktop
|
||||
integration; consult its release notes instead of assuming a particular GUI
|
||||
package version or filename.
|
||||
|
||||
For a headless sanity check:
|
||||
|
||||
```sh
|
||||
python3 gui/src/zupt_gui.py --version
|
||||
python3 gui/src/zupt_gui.py --selftest
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the CLI is not found, inspect the selected prefix:
|
||||
|
||||
```sh
|
||||
command -v zupt
|
||||
printf '%s\n' "$PATH"
|
||||
```
|
||||
|
||||
If the GUI cannot find it, install the CLI in a directory on `PATH` or set
|
||||
`ZUPT_BIN` to its absolute path. `VAPTVUPT_BIN` remains a compatibility
|
||||
fallback. For Qt import failures, verify the same
|
||||
Python interpreter that starts the GUI:
|
||||
|
||||
```sh
|
||||
python3 -c 'import PySide6.QtWidgets'
|
||||
```
|
||||
|
||||
For build failures, rerun with `V=1` and include the compiler target, full build
|
||||
command, and first error in the issue report. Do not attach credentials,
|
||||
private keys, passwords, or sensitive archives.
|
||||
|
||||
Report issues at:
|
||||
`https://github.com/cristiancmoises/zupt/issues`.
|
||||
|
|
|
|||
121
LICENSE
121
LICENSE
|
|
@ -1,92 +1,65 @@
|
|||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
ZUPT licensing summary
|
||||
======================
|
||||
|
||||
Copyright (C) 2026 Cristian Cezar Moisés <zupt@riseup.net>
|
||||
Copyright (C) 2025-2026 Cristian Cezar Moisés
|
||||
|
||||
VaptVupt (formerly Zupt; renamed in v3.0.0 due to a prior INPI Brasil
|
||||
trademark registration of "Zupt" for unrelated software) 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.
|
||||
The ZUPT application, command-line interface, graphical interface,
|
||||
cryptographic tool code, build files, and documentation identified by the
|
||||
following SPDX expression are free software under:
|
||||
|
||||
VaptVupt 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.
|
||||
AGPL-3.0-or-later
|
||||
|
||||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program. If not, see:
|
||||
The integrated VaptVupt compression codec is a separately identified component.
|
||||
The codec files carry this SPDX expression:
|
||||
|
||||
https://www.gnu.org/licenses/agpl-3.0.txt
|
||||
https://www.gnu.org/licenses/agpl-3.0.html
|
||||
GPL-3.0-or-later
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
The two source files derived from Yann Collet's xxHash implementation carry an
|
||||
additional BSD-2-Clause obligation:
|
||||
|
||||
─────────────────────────────────────────────────────────────────────
|
||||
src/zupt_xxh.c
|
||||
src/vv_xxh64.c
|
||||
|
||||
ABOUT THIS LICENSE
|
||||
Portions of the native ML-KEM implementation were adapted from the
|
||||
pq-crystals/kyber reference implementation. Upstream offers that code under
|
||||
CC0-1.0 or Apache-2.0; this distribution uses the CC0-1.0 option for those
|
||||
portions:
|
||||
|
||||
The GNU Affero General Public License v3 (AGPLv3) is a copyleft
|
||||
license designed for software that may be run as a network service.
|
||||
It is identical to the GNU General Public License v3, with one
|
||||
additional requirement (Section 13): if you modify libzuptsdk and
|
||||
make the modified version available to users over a computer network,
|
||||
you must offer those users access to the corresponding modified
|
||||
source code.
|
||||
src/zupt_mlkem.c
|
||||
|
||||
This protects Zupt against being adopted by SaaS providers as
|
||||
a private fork without contributing back, while keeping it freely
|
||||
usable by individuals, small businesses, and the broader open-source
|
||||
community.
|
||||
Portions of the native X25519 implementation were adapted from
|
||||
curve25519-donna and conservatively retain its repository BSD-3-Clause terms:
|
||||
|
||||
If you write a separate program that is distributed alongside
|
||||
Zupt (for example, statically linking it into your own
|
||||
application), the AGPL requires you to license that combined work
|
||||
under the AGPL as well — which means you must publish the source.
|
||||
If this is not acceptable for your use case, please contact the
|
||||
author for commercial licensing options:
|
||||
src/zupt_x25519.c
|
||||
|
||||
sac@securityops.co
|
||||
https://git.securityops.co/cristiancmoises/zupt
|
||||
The codec scope consists of src/vv_*.c, src/vaptvupt_api.c,
|
||||
include/vaptvupt*.h, and include/vv_*.h. Per-file SPDX notices are
|
||||
authoritative if a file falls outside this summary.
|
||||
|
||||
─────────────────────────────────────────────────────────────────────
|
||||
GPL-3.0-or-later and AGPL-3.0-or-later code may be combined under section 13 of
|
||||
the licenses. Distribution of this repository therefore needs to preserve both
|
||||
license scopes and their notices. The unmodified license texts are provided in:
|
||||
|
||||
The full text of the GNU Affero General Public License version 3
|
||||
should accompany this distribution as a separate file (or you may
|
||||
download it from the URLs above). It is approximately 35 KB / 619
|
||||
lines of plain text.
|
||||
LICENSE-AGPL-3.0
|
||||
LICENSE-GPL-3.0
|
||||
LICENSE-BSD-2-Clause
|
||||
LICENSE-BSD-3-Clause
|
||||
LICENSE-CC0-1.0
|
||||
|
||||
─────────────────────────────────────────────────────────────────────
|
||||
ZUPT is distributed without warranty; see the applicable license text for
|
||||
the complete terms.
|
||||
|
||||
NOTE ON VAPTVUPT (GPL, NOT AGPL)
|
||||
Historical licensing note: published repository history includes earlier
|
||||
first-party application and GUI material distributed with MIT license notices.
|
||||
Those permissions remain applicable to the exact material distributed under
|
||||
them; the current license summary does not revoke or reinterpret an earlier
|
||||
grant. The 5.2.2 erratum in CHANGELOG.md identifies the known repository
|
||||
evidence. Current files follow their current per-file SPDX notices.
|
||||
|
||||
The VaptVupt LZ + tANS codec, located in:
|
||||
The applicable copyright holder may separately offer commercial terms for
|
||||
first-party rights that the holder controls. LICENSE-COMMERCIAL is only a
|
||||
licensing inquiry and scope notice; it is not a commercial license grant and
|
||||
does not relicense third-party or separately noticed material.
|
||||
|
||||
src/vv_*.c
|
||||
src/vaptvupt_api.c
|
||||
include/vaptvupt*.h
|
||||
include/vv_*.h
|
||||
|
||||
is licensed under the GNU General Public License version 3 or later
|
||||
(GPL-3.0-or-later), NOT the AGPL. This deliberate licensing choice is
|
||||
made so that, with sufficient maturity, VaptVupt can be considered
|
||||
for upstreaming into the Linux or BSD kernels (which require GPL-
|
||||
compatible licensing).
|
||||
|
||||
The standalone repository for VaptVupt is at:
|
||||
|
||||
https://git.securityops.co/cristiancmoises/vaptvupt
|
||||
|
||||
The combination of GPL-licensed VaptVupt with AGPL-licensed Zupt is
|
||||
explicitly intended by the author and consistent with the rights
|
||||
retained by sole-authorship.
|
||||
|
||||
─────────────────────────────────────────────────────────────────────
|
||||
|
||||
COMMERCIAL LICENSING
|
||||
|
||||
Both AGPL and GPL components may be commercially relicensed by the
|
||||
author. If you require relief from copyleft terms, contact:
|
||||
|
||||
sac@securityops.co
|
||||
Commercial licensing contact: sac@securityops.co
|
||||
Canonical repository: https://github.com/cristiancmoises/zupt
|
||||
|
|
|
|||
661
LICENSE-AGPL-3.0
Normal file
661
LICENSE-AGPL-3.0
Normal file
|
|
@ -0,0 +1,661 @@
|
|||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
26
LICENSE-BSD-2-Clause
Normal file
26
LICENSE-BSD-2-Clause
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
xxHash Library
|
||||
Copyright (c) 2012-2021 Yann Collet
|
||||
All rights reserved.
|
||||
|
||||
BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
46
LICENSE-BSD-3-Clause
Normal file
46
LICENSE-BSD-3-Clause
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
Copyright 2008, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
curve25519-donna: Curve25519 elliptic curve, public key function
|
||||
|
||||
http://code.google.com/p/curve25519-donna/
|
||||
|
||||
Adam Langley <agl@imperialviolet.org>
|
||||
|
||||
Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
|
||||
|
||||
More information about curve25519 can be found here
|
||||
http://cr.yp.to/ecdh.html
|
||||
|
||||
djb's sample implementation of curve25519 is written in a special assembly
|
||||
language called qhasm and uses the floating point registers.
|
||||
|
||||
This is, almost, a clean room reimplementation from the curve25519 paper. It
|
||||
uses many of the tricks described therein. Only the crecip function is taken
|
||||
from the sample implementation.
|
||||
121
LICENSE-CC0-1.0
Normal file
121
LICENSE-CC0-1.0
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
22
LICENSE-COMMERCIAL
Normal file
22
LICENSE-COMMERCIAL
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
ZUPT COMMERCIAL LICENSING NOTICE
|
||||
|
||||
First-party ZUPT code is publicly licensed under the per-file terms:
|
||||
AGPL-3.0-or-later for the application/GUI/cryptographic tool code and
|
||||
GPL-3.0-or-later for the separately identified VaptVupt compression codec.
|
||||
|
||||
The applicable copyright holder may also offer those first-party rights under
|
||||
a separate written commercial agreement executed with the licensee.
|
||||
|
||||
THIS FILE IS NOT A COMMERCIAL LICENSE GRANT. It provides no permission outside
|
||||
the applicable AGPL or GPL public option. Commercial-option rights, including
|
||||
any proprietary redistribution right, exist only in an executed agreement that
|
||||
identifies its exact files, version, use, and licensee. This notice promises no
|
||||
support, warranty, patent, indemnification, trademark, pricing, or sublicensing
|
||||
term.
|
||||
|
||||
Vendored, generated, contributed, and separately noticed material is not
|
||||
relicensed by this option unless the executed agreement expressly covers
|
||||
rights owned or controlled by the licensor. See THIRD-PARTY-NOTICES.md and all
|
||||
per-file SPDX notices.
|
||||
|
||||
Commercial licensing inquiries: sac@securityops.co
|
||||
674
LICENSE-GPL-3.0
Normal file
674
LICENSE-GPL-3.0
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
39
NOTICE
Normal file
39
NOTICE
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
ZUPT notices
|
||||
============
|
||||
|
||||
Copyright remains with the holders identified by per-file notices and repository
|
||||
history.
|
||||
|
||||
Public source-license scopes:
|
||||
|
||||
- application, GUI, cryptographic tool, build and documentation code:
|
||||
AGPL-3.0-or-later;
|
||||
- integrated VaptVupt compression codec files identified in LICENSE:
|
||||
GPL-3.0-or-later.
|
||||
- xxHash-derived routines in `src/zupt_xxh.c` and `src/vv_xxh64.c`:
|
||||
BSD-2-Clause in addition to their application/codec license.
|
||||
- pq-crystals/kyber-derived portions in `src/zupt_mlkem.c`:
|
||||
CC0-1.0 in addition to the application license.
|
||||
- curve25519-donna-derived portions in `src/zupt_x25519.c`:
|
||||
BSD-3-Clause in addition to the application license; Copyright 2008,
|
||||
Google Inc.
|
||||
- the x86 BCJ state machine in `src/vv_bcj.c` is adapted from Igor Pavlov's
|
||||
public-domain LZMA SDK source.
|
||||
- the SHA-NI compression path in `src/zupt_sha256_shani.c` is adapted from
|
||||
Jeffrey Walton's public-domain SHA-Intrinsics reference.
|
||||
|
||||
The corresponding unmodified texts are LICENSE-AGPL-3.0,
|
||||
LICENSE-GPL-3.0, LICENSE-BSD-2-Clause, LICENSE-BSD-3-Clause, and
|
||||
LICENSE-CC0-1.0. Preserve THIRD-PARTY-NOTICES.md and all per-file SPDX and
|
||||
copyright notices when redistributing the source.
|
||||
|
||||
Published historical revisions contain MIT notices for some first-party
|
||||
application and GUI material. Those historical permissions remain attached to
|
||||
the exact material distributed under them; see the 5.2.2 licensing erratum in
|
||||
CHANGELOG.md. The current source scopes above do not revoke an earlier grant.
|
||||
|
||||
LICENSE-COMMERCIAL describes a possible separately executed commercial
|
||||
agreement for controlled first-party rights. It grants no additional permission
|
||||
by itself and does not alter the public licenses.
|
||||
|
||||
Commercial licensing contact: sac@securityops.co
|
||||
646
SECURITY.md
646
SECURITY.md
|
|
@ -1,326 +1,404 @@
|
|||
# Security Policy — VaptVupt 5.0.0
|
||||
# Security Policy — ZUPT 5.2.8
|
||||
|
||||
## Reporting Vulnerabilities
|
||||
## Reporting vulnerabilities
|
||||
|
||||
Report privately by email to **zupt@riseup.net** with `[security]` in the
|
||||
subject. Do not open a public issue on the project's git server.
|
||||
Report suspected vulnerabilities privately to **zupt@riseup.net** with
|
||||
`[security]` in the subject. Do not open a public issue before coordinated
|
||||
disclosure.
|
||||
|
||||
Include:
|
||||
Include the output of `zupt --version`, operating system and architecture,
|
||||
impact, and the smallest safe reproducer. Remove passwords, keys, tokens,
|
||||
personal data, and confidential archive contents.
|
||||
|
||||
- Version (`vaptvupt --version`) and platform.
|
||||
- Description, impact assessment, and a reproduction (a minimal archive or
|
||||
a code snippet).
|
||||
The project aims to acknowledge reports within five business days and to target
|
||||
high-severity fixes within 30 days, with the disclosure timeline agreed case by
|
||||
case. These are targets, not a warranty.
|
||||
|
||||
Disclosure SLA: acknowledgement within 5 business days; target fix within
|
||||
30 days for high-severity issues. Coordinated disclosure preferred; the
|
||||
timeline is discussed case by case. A PGP key is on the project's
|
||||
keyserver entry.
|
||||
ZUPT has not had an independent third-party security audit or certification.
|
||||
Treat the in-repository review and tests as reproducible project evidence, not
|
||||
as external assurance.
|
||||
|
||||
The project has not had an external independent audit. For high-stakes
|
||||
deployments, treat it as "reviewed but unaudited" and do your own review.
|
||||
## Supported security modes
|
||||
|
||||
---
|
||||
| Mode | CLI | Key establishment / derivation | Payload protection |
|
||||
|---|---|---|---|
|
||||
| Plain | no encryption option | none | compression checksums only |
|
||||
| Password | `-p/--password`, `--password-prompt`, `--pass-file`, or `--pass-fd` | PBKDF2-SHA256, 600,000 iterations | AES-256-CTR + HMAC-SHA256 |
|
||||
| Native hybrid PQ | `--pq` | ML-KEM-768 + X25519, SHA3-512 combiner | AES-256-CTR + HMAC-SHA256 |
|
||||
| Native PQ only | `--pq-only` | ML-KEM-768, SHA3-512 derivation | AES-256-CTR + HMAC-SHA256 |
|
||||
|
||||
## Encryption Modes
|
||||
The native hybrid mode is the recommended post-quantum mode unless a policy
|
||||
forbids a classical component. `--pq-only` removes the X25519 fallback: a break
|
||||
of ML-KEM-768 alone would then compromise key establishment. Password security
|
||||
is bounded by password entropy; PBKDF2 cannot make a short or reused password
|
||||
safe against offline guessing.
|
||||
|
||||
| Mode | CLI Flag | Algorithm | PQ-Safe? | Use Case |
|
||||
|------|----------|-----------|----------|----------|
|
||||
| Password | `-p` | PBKDF2-SHA256 → AES-256-CTR + HMAC-SHA256 | No | Short-term backups, personal use |
|
||||
| PQ Hybrid | `--pq` | ML-KEM-768 + X25519 → AES-256-CTR + HMAC-SHA256 | Yes | Long-term archives, high-value data (**recommended**) |
|
||||
| PQ Only | `--pq-only` | ML-KEM-768 only → AES-256-CTR + HMAC-SHA256 | Yes | "PQ-only" compliance postures (no classical KEM) |
|
||||
| None | (default) | No encryption (compression only) | N/A | Non-sensitive data |
|
||||
The `-p/--password PASSWORD` argument form can be visible to process-list users
|
||||
and shell history. Prefer `--password-prompt`, `--pass-file` with restrictive
|
||||
permissions, or `--pass-fd` with a descriptor inherited from a trusted caller.
|
||||
The file/descriptor forms read one line, remove LF and an optional preceding CR,
|
||||
and reject empty, NUL-containing, or overlong input. ZUPT does not enforce
|
||||
password-file ownership or mode; the caller remains responsible for creating,
|
||||
protecting, and deleting that file. The descriptor form duplicates the supplied
|
||||
descriptor and does not close the caller's original descriptor.
|
||||
The duplicate shares the same underlying stream and offset, and buffered input
|
||||
may consume beyond the password line. Pass a descriptor dedicated to this one
|
||||
password read; do not reuse it as a multi-record protocol channel.
|
||||
|
||||
Password mode (`-p`) is not quantum-safe. For protection against "harvest
|
||||
now, decrypt later" quantum attacks, use `--pq` — the recommended
|
||||
post-quantum mode. `--pq` is native and in-tree; it needs no external
|
||||
library.
|
||||
On POSIX terminals, the explicit prompt saves terminal state and installs
|
||||
signal-aware cleanup so a handled interruption restores echo and other changed
|
||||
settings before termination. This behavior is covered by a PTY regression and
|
||||
passed in the full local Linux gate for commit `ff99770`.
|
||||
On Windows, the prompt requires a real console input handle before entering
|
||||
`_getch`; redirected input and console EOF fail instead of blocking a native
|
||||
release gate.
|
||||
|
||||
`--pq-only` (envelope type `0x06`) uses ML-KEM-768 as the *sole* key
|
||||
mechanism, with no classical X25519 component. It exists for compliance
|
||||
postures that mandate a single NIST-standardised PQ primitive with no
|
||||
classical KEM in the envelope (CNSA 2.0-style "PQ-only"). **This is a
|
||||
deliberate reduction in defence-in-depth:** unlike `--pq`, there is no
|
||||
classical fallback, so a future cryptanalytic break of ML-KEM-768 alone is
|
||||
sufficient to break the archive. Under `--pq`, an attacker must break *both*
|
||||
ML-KEM-768 and X25519. **Unless a policy forbids the classical component,
|
||||
prefer `--pq`.** Both modes are native, in-tree, and need no external
|
||||
library.
|
||||
## Native key files
|
||||
|
||||
Optional SDK modes (`--pq-sdk`, `--pq-box`) are available only in an
|
||||
upstream `make WITH_SDK=1` build linked against the separately distributed
|
||||
libzuptsdk / libpqvaptvupt libraries. They are not part of the default
|
||||
build and are not defaults.
|
||||
Native private keys use no-replace creation: POSIX files are mode `0600` and
|
||||
Windows files receive a current-user-only DACL. An existing destination is
|
||||
never truncated. If write, flush/fsync, or close fails, ZUPT deliberately leaves
|
||||
the exclusively created incomplete or durability-uncertain file at that path
|
||||
for the user to inspect and remove. It does not unlink by pathname after close,
|
||||
which avoids deleting a replacement installed during a race. Public keys may be
|
||||
shared deliberately and are not treated as secret. ZKEY and ZPQK readers
|
||||
validate the checksum, format version, flags, reserved bytes, exact encoded
|
||||
size, and public/private role before using any key material. A truncated,
|
||||
extended, structurally invalid, or role-confused key is rejected rather than
|
||||
partially accepted.
|
||||
|
||||
---
|
||||
### Optional integrations
|
||||
|
||||
## Cryptographic Algorithms
|
||||
The 5.2.8 default is `WITH_SDK=0 WITH_PQBOX=0`:
|
||||
|
||||
| Component | Algorithm | Standard | Key Size | Security Level |
|
||||
|-----------|-----------|----------|----------|---------------|
|
||||
| Symmetric encryption | AES-256-CTR | FIPS 197 | 256-bit | 128-bit post-quantum (Grover) |
|
||||
| Authentication | HMAC-SHA256 | RFC 2104 | 256-bit | 128-bit post-quantum (Grover) |
|
||||
| Password KDF (default) | PBKDF2-SHA256 | RFC 8018 | 600K iterations | Password-dependent |
|
||||
| Password KDF (WITH_SDK=1 option) | Argon2id | RFC 9106 | OWASP minimums | Password-dependent, memory-hard |
|
||||
| Post-quantum KEM | ML-KEM-768 | FIPS 203 (validated vs OpenSSL 3.5) | 1184B ek / 2400B dk | NIST Level 3 |
|
||||
| Classical KEM | X25519 | RFC 7748 | 32B scalar | ~128-bit classical |
|
||||
| Hybrid KDF (`--pq`) | SHA3-512 | FIPS 202 | 512-bit output | Secure if either KEM holds |
|
||||
| PQ-only KDF (`--pq-only`) | SHA3-512 | FIPS 202 | 512-bit output | Secure if ML-KEM-768 holds (no classical fallback) |
|
||||
| Integrity | XXH64 | xxHash spec | 64-bit checksum | Non-cryptographic |
|
||||
| Hashing | SHA3-256, SHA3-512 | FIPS 202 | 256/512-bit | Standard |
|
||||
| Random | OS CSPRNG | getrandom(2) / RtlGenRandom | N/A | Hard fail if unavailable |
|
||||
- `WITH_SDK=1` enables libvuptsdk-backed features, including the SDK PQ mode
|
||||
and Argon2id support, using a separately installed system development package.
|
||||
- `WITH_PQBOX=1` independently enables the libpqvaptvupt sealed-box mode using
|
||||
its separately installed system development package.
|
||||
|
||||
The default build uses PBKDF2-SHA256 (600k iterations) for password mode.
|
||||
Argon2id is available only in a `make WITH_SDK=1` build.
|
||||
Neither library is committed as a precompiled artifact, and no build path
|
||||
downloads it. A missing requested dependency is a build error. Security
|
||||
properties of these optional libraries are outside the source-only CLI audit
|
||||
unless their exact source package and version are reviewed separately.
|
||||
|
||||
---
|
||||
The in-repository SDK adapter saves copied keys through the core atomic output
|
||||
publisher. POSIX permissions are applied to its already-open temporary
|
||||
descriptor, and publication replaces only the requested directory entry after
|
||||
copy/close checks succeed. `make sdk-test` exercises private/public modes and
|
||||
preservation of pre-existing symlink and hardlink targets. This covers the
|
||||
adapter boundary; it does not certify the separately installed SDK library.
|
||||
|
||||
## Security Architecture
|
||||
## Cryptographic construction
|
||||
|
||||
### Per-Block Authenticated Encryption
|
||||
Encrypted blocks use a fresh 128-bit nonce, AES-256-CTR, and HMAC-SHA256. The
|
||||
MAC binds the encrypted payload, canonical block metadata, and the frame's
|
||||
logical position, and is checked before restored data is accepted. In 5.2.2,
|
||||
this positional AAD applies to DATA and DEDUP_REF frames. An authenticated
|
||||
reference is bound to its own position and carries the authenticated source
|
||||
position needed to verify the referenced DATA frame.
|
||||
|
||||
```
|
||||
For each data block:
|
||||
An archive-integrity trailer (AIT) covers global metadata. The 5.2.2
|
||||
`extract`, `list`, `test`, and `disk restore` paths refuse a no-AIT layout by
|
||||
default, without trusting the archive's unauthenticated `ENCRYPTED` bit to
|
||||
decide whether that check matters. `--allow-legacy-no-ait` is accepted only by
|
||||
those commands; it is a recovery-only opt-in for a known, trusted pre-AIT
|
||||
archive and emits a downgrade warning. Never use it for an archive from
|
||||
untrusted or attacker-writable storage. `info` only reports unauthenticated
|
||||
framing and apparent AIT presence; it does not validate the trailer or archive
|
||||
contents. Plain archives use non-cryptographic checksums and do not provide
|
||||
protection against an attacker who can rewrite the archive.
|
||||
|
||||
nonce = CSPRNG(16) [16 bytes, fresh per block]
|
||||
ciphertext = AES-256-CTR(enc_key, nonce, plaintext)
|
||||
mac = HMAC-SHA256(mac_key, aad ‖ nonce ‖ ciphertext) [32 bytes]
|
||||
stored = nonce ‖ ciphertext ‖ mac
|
||||
Archive comments are authenticated according to the archive mode, but they are
|
||||
still untrusted display data. ZUPT renders control bytes safely when showing a
|
||||
comment and does not emit raw terminal-control sequences. This prevents a valid
|
||||
or attacker-supplied comment from rewriting terminal output; it does not make a
|
||||
plain archive cryptographically authentic.
|
||||
|
||||
In new 5.2.2 encrypted+dedup archives, each reference offset is included in
|
||||
the authenticated reference payload. New encrypted disk archives also
|
||||
authenticate their index; the index binds the image size, block count, and a
|
||||
chained XXH64 hash of the complete restored byte stream. The writer
|
||||
additionally requires an XXH64 and SHA-256/128 match before emitting a dedup
|
||||
reference, but that SHA-256/128 digest is an in-memory collision guard and is
|
||||
not stored as an on-disk integrity claim. XXH64 is non-cryptographic: in a
|
||||
plain archive it
|
||||
detects accidental corruption but can be recomputed by an attacker.
|
||||
|
||||
The native hybrid derivation implemented by 5.2.2 is:
|
||||
|
||||
```text
|
||||
ml_ss = ML-KEM-768 shared secret
|
||||
x25519_ss = X25519 shared secret
|
||||
hybrid_ikm = ml_ss XOR x25519_ss
|
||||
archive_key = SHA3-512(hybrid_ikm || ml_ct || ephemeral_pk ||
|
||||
"ZUPT-HYBRID-v1")
|
||||
```
|
||||
|
||||
The nonce is a **fresh 128-bit random value per block**, stored in the block
|
||||
prefix and bound into the block MAC. The block sequence number is bound into
|
||||
the MAC AAD (not into the nonce), so reordering, splicing, or replaying blocks
|
||||
is still detected.
|
||||
`--pq-only` derives the archive key as
|
||||
`SHA3-512(ml_ss || ml_ct || "ZUPT-PQ-ONLY-v1")`.
|
||||
|
||||
> **History (fixed in 4.2.0):** earlier releases derived the nonce as
|
||||
> `base_nonce XOR pad_le(block_seq, 8)`. In `--dedup` mode every data block is
|
||||
> assigned sequence 0 (the sentinel that keeps cross-file dedup references
|
||||
> authenticating consistently), so the nonce collapsed to a single value across
|
||||
> all dedup blocks — reusing the AES-CTR keystream across distinct plaintexts
|
||||
> (a many-time-pad). Switching to a fresh random per-block nonce closes this.
|
||||
> Regression test: `tests/test_dedup_nonce.sh`. Re-encrypt any `--dedup` +
|
||||
> encrypted archives written by ≤ 4.1.0.
|
||||
The native modes are at-rest archive encryption. They do not provide protocol
|
||||
session forward secrecy: later compromise of the relevant long-term private key
|
||||
can compromise archives encrypted to it.
|
||||
|
||||
### Encrypt-then-MAC
|
||||
## Constant-time and side-channel scope
|
||||
|
||||
HMAC is computed over `nonce ‖ ciphertext` and verified **before** any
|
||||
decryption. This prevents:
|
||||
Portable C is the 5.2.8 default. Sensitive comparisons and selections use
|
||||
branchless helpers, but generated machine-code behavior remains dependent on
|
||||
the compiler and platform. This is not a formal whole-program constant-time
|
||||
claim. The C AES implementation uses table lookups and is unsuitable for a
|
||||
claim of cache-timing resistance on hostile shared hardware.
|
||||
|
||||
- Chosen-ciphertext attacks
|
||||
- Padding oracle attacks
|
||||
- Processing of tampered data
|
||||
Sensitive VaptVupt working buffers are cleared through a compiler-resistant
|
||||
wipe helper. Platforms with a guaranteed libc `explicit_bzero` use it; macOS
|
||||
and NetBSD use the portable volatile-write fallback because the supported
|
||||
deployment targets do not guarantee that symbol. This source-level choice
|
||||
resists ordinary dead-store elimination but is not a formal claim about every
|
||||
compiler binary.
|
||||
|
||||
### Hybrid Post-Quantum KEM (`--pq`)
|
||||
Textual assembly under `jasmin/` can be enabled explicitly with
|
||||
`WITH_JASMIN=1` on a supported x86_64 compiler target. The directory contains
|
||||
Jasmin-generated output and separately identified hand-written assembly; all of
|
||||
it is disabled by default and its inclusion must be confirmed in the exact
|
||||
binary being assessed. Its availability does not imply formal verification of
|
||||
the archive parser, compression codec, or the whole program.
|
||||
|
||||
> **FIPS 203 conformance (v5.0.0).** The ML-KEM-768 implementation is validated
|
||||
> byte-for-byte against OpenSSL 3.5's FIPS 203 ML-KEM-768: deterministic keygen
|
||||
> produces an identical `ek`, and the shared secret agrees in both
|
||||
> cross-decapsulation directions (our encaps ↔ OpenSSL decaps, and vice-versa).
|
||||
> This is checked on every `make check` by `tests/test_mlkem_fips203.sh`.
|
||||
> Releases ≤ 4.2.1 used round-3 CRYSTALS-Kyber (secure, but not interoperable);
|
||||
> 5.0.0's `--pq`/`--pq-only` archives are therefore not backward-compatible.
|
||||
## Security boundary and limitations
|
||||
|
||||
```
|
||||
Encapsulation:
|
||||
ML-KEM-768.Encaps(pk) → ml_ct[1088], ml_ss[32]
|
||||
eph_sk ← CSPRNG(32)
|
||||
eph_pk = X25519(eph_sk, basepoint)
|
||||
x25519_ss = X25519(eph_sk, recipient_pk)
|
||||
hybrid_ikm = ml_ss XOR x25519_ss
|
||||
archive_key = SHA3-512(hybrid_ikm ‖ ml_ct ‖ eph_pk ‖ "ZUPT-HYBRID-v1")
|
||||
enc_key = archive_key[0:32]
|
||||
mac_key = archive_key[32:64]
|
||||
ZUPT is designed for backups created and restored on trusted endpoints. It
|
||||
does not protect against:
|
||||
|
||||
- malware, keyloggers, memory inspection, or a compromised user account on the
|
||||
machine handling plaintext or keys;
|
||||
- disclosure of a password or private key;
|
||||
- denial of service from arbitrarily large or adversarial input;
|
||||
- traffic analysis from archive size and visible framing metadata;
|
||||
- hiding that a file is a ZUPT archive;
|
||||
- compression-length side channels when attacker-controlled and secret data are
|
||||
compressed together and an attacker can observe output length;
|
||||
- network transport attacks, multi-party access control, threshold recovery, or
|
||||
key rotation;
|
||||
- every compiler-, microarchitecture-, power-, or speculative-execution side
|
||||
channel.
|
||||
|
||||
Archive entry paths reject traversal, absolute paths, control characters,
|
||||
Windows alternate streams/device names, and ambiguous trailing dot/space
|
||||
components. POSIX extraction resolves each parent relative to a pinned file
|
||||
descriptor with no-follow semantics after canonicalizing the user-selected
|
||||
output root once; symlinks below that root remain forbidden. Windows resolves
|
||||
each parent and temporary
|
||||
file relative to a directory handle and performs the final no-replace rename by
|
||||
handle, so a checked path is not looked up again through a mutable junction or
|
||||
reparse point. An existing destination leaf is never overwritten.
|
||||
|
||||
Decoded bytes first go to a private, exclusively created temporary file. The
|
||||
final name is published only after the expected decoded size and chained
|
||||
checksum match and the stream closes successfully; failures remove the
|
||||
temporary by descriptor/handle. These controls reduce traversal, link, race,
|
||||
and partial-output risks, but they do not make privileged extraction
|
||||
appropriate. Extract untrusted archives as a dedicated unprivileged user into
|
||||
a new empty directory, inspect the result before moving it, and apply OS
|
||||
sandboxing where available.
|
||||
|
||||
Benchmark workspaces are random private directories. POSIX cleanup opens each
|
||||
directory component without following links and removes entries relative to
|
||||
pinned descriptors. Windows retains no-delete-sharing handles for the resolved
|
||||
ancestors and refuses reparse-point recursion. After emptying a directory, it
|
||||
reopens that entry relative to the pinned parent, verifies the volume and file
|
||||
index against the traversal handle, and marks only the identity-checked handle
|
||||
for deletion. An injected link is removed as a link rather than traversed to
|
||||
its target.
|
||||
|
||||
Disk restore has a separate destructive-device boundary. It measures and
|
||||
copies the compacted archive to an exclusively created, auto-deleted private
|
||||
scratch file, then performs validation and restoration from that same snapshot.
|
||||
`ZUPT_TMPDIR` is an explicit existing scratch-directory override; an invalid
|
||||
override fails without fallback. On POSIX, the target is opened once without
|
||||
truncation or final-symlink following, classified with `fstat`, and—when it is
|
||||
a supported Linux, macOS, or FreeBSD raw block device—the same descriptor is
|
||||
retained through capacity checks and writes. Regular files continue through
|
||||
atomic publication. Unknown device capacity, an undersized device, a
|
||||
source/destination identity match, or a snapshot failure stops before the
|
||||
first target write. These checks do not
|
||||
make raw-device restore non-destructive: verify both operands and keep recovery
|
||||
media before proceeding.
|
||||
|
||||
These changes address the three 5.2.8 CodeQL High reports: #5 at SDK key
|
||||
publication, #6 at POSIX disk-target classification/use, and #7 at benchmark
|
||||
workspace cleanup. The regressions and source review are project evidence, not
|
||||
an independent certification. Exact-tag run `33456209269` subsequently passed
|
||||
all 15 jobs at `ebb9ab3aa1d42c50030ca02883f6162dc4771fe1`.
|
||||
|
||||
The C/C++ default-branch analysis of commit `69fc26b` closed #5, #6, and #7,
|
||||
then opened test-only High #8, #9, and #10 because the new SDK regression used
|
||||
path-level `stat`/`lstat` before later reads or cleanup. Each individual test
|
||||
check now uses a no-follow descriptor with `fstat` or descriptor reads; the
|
||||
static gate rejects reintroduction of path-level metadata checks there. The
|
||||
subsequent C/C++ default-branch scan run `33452563116` completed successfully at
|
||||
commit `7a8e5c5`; alerts #5 through #10 are fixed, and the authenticated
|
||||
code-scanning API reported zero open alerts.
|
||||
The final release-commit CodeQL run `33456049125` also completed successfully;
|
||||
the authenticated API again reported zero open alerts, with #5 through #10
|
||||
recorded as fixed rather than dismissed.
|
||||
|
||||
The Windows handle-relative implementation is scoped to normal local Win32
|
||||
paths. Win32 extended-length and device-namespace paths, raw UNC output roots,
|
||||
and mapped/network-drive output are not supported in 5.2.8. Cross-build and
|
||||
Wine results are not native-Windows evidence; the `windows-latest` package gate
|
||||
must pass its Unicode round trip before Windows assets are published. Restore
|
||||
to a normal local directory first and move verified output to network storage
|
||||
afterward.
|
||||
|
||||
## Historical compatibility and fixes
|
||||
|
||||
These statements are historical release records, not claims that every current
|
||||
gate was rerun on every platform:
|
||||
|
||||
- In 4.2.0, encrypted deduplication changed from a repeated derived nonce to a
|
||||
fresh random per-block nonce. Re-encrypt encrypted `--dedup` archives written
|
||||
by releases through 4.1.0.
|
||||
- In 5.0.0, native ML-KEM was corrected from round-3 CRYSTALS-Kyber semantics
|
||||
to FIPS 203 ML-KEM-768. Native `--pq` and `--pq-only` keys and archives from
|
||||
releases through 4.2.1 are not compatible with the corrected mode. Password
|
||||
and plain archive paths were not affected by that KEM change.
|
||||
- Releases predating the archive-integrity trailer may have a structurally
|
||||
valid no-AIT layout. Such an archive now fails closed unless the caller uses
|
||||
`--allow-legacy-no-ait` on a supported read command. This option permits
|
||||
recovery of trusted old media; it is not a general compatibility mode and
|
||||
does not make unauthenticated header/footer metadata safe.
|
||||
- Readers since 5.2.2 accept the fixed-width disk index and encrypted-dedup linear
|
||||
AAD sequence published through 5.2.1 and warns that the legacy index has no
|
||||
whole-image content hash. Its regression fixture is an actual v5.2.1
|
||||
password-encrypted DATA/DATA/REF/DATA disk archive stored as hexadecimal text with
|
||||
source and hash provenance. The candidate lists, tests, extracts, and restores it
|
||||
byte-exact; the full local Linux gate passed on commit `ff99770`. This does not
|
||||
claim that 5.2.1 readers accept the new flag-gated 5.2.2 records or that every
|
||||
historical encrypted+dedup combination was validated.
|
||||
|
||||
The repository contains NIST/RFC vector tests and an OpenSSL 3.5 ML-KEM
|
||||
interoperability test. The OpenSSL test can only execute when the environment
|
||||
provides an ML-KEM-capable OpenSSL; otherwise it must be reported as skipped.
|
||||
|
||||
## Source and release integrity
|
||||
|
||||
Git and upstream source archives contain no compiled executable, object,
|
||||
shared/static library, or distribution package. Audit them with:
|
||||
|
||||
```sh
|
||||
scripts/check-source-only.sh
|
||||
scripts/check-source-only.sh --archive /path/to/zupt-5.2.8.tar.gz
|
||||
```
|
||||
|
||||
Security model: secure if EITHER ML-KEM-768 (post-quantum, NIST Level 3)
|
||||
OR X25519 (classical, ~128-bit) remains unbroken. Both must be compromised
|
||||
simultaneously to recover the archive key. Same approach as Signal
|
||||
(PQXDH), Apple iMessage (PQ3), and OpenSSH 9.0+.
|
||||
Nested archive inspection is required to enforce bounded recursion, member
|
||||
count, per-entry expanded size, and total expanded size, and to fail closed on
|
||||
limit violations. On commit `ff99770`, the source-only scanner suite passed
|
||||
39/39, including GNU thin archives, resource-limit cases, and safe diagnostics.
|
||||
|
||||
The `--pq-sdk` mode (WITH_SDK=1 only) uses an HKDF-SHA3-256 combiner, a
|
||||
32-byte key commitment tag, HPKE-style context binding (RFC 9180 §5),
|
||||
anti-fault double decapsulation, and XChaCha20-Poly1305 AEAD.
|
||||
DEB, binary RPM, SRPM, notice-bearing Linux tar.xz, source-only portable GUI
|
||||
ZIP, Windows ZIP, and macOS DMG release assets are separate outputs. An
|
||||
AppImage is not promoted for 5.2.8. A bare Linux or Windows executable is also
|
||||
excluded; executables are distributed only inside their notice-bearing
|
||||
archives. Trust an artifact only when its exact format has a recorded build,
|
||||
content/metadata inspection, extracted or installed smoke test, and applicable
|
||||
archive round trip. Never treat an unexecuted platform as passing.
|
||||
|
||||
### Full Post-Quantum KEM (`--pq-only`)
|
||||
The gated 5.2.8 set is the CLI package/archive set plus the exact GUI DEB,
|
||||
noarch/source RPM, and source-only portable ZIP documented in the README. The
|
||||
portable GUI ZIP contains no compiled runtime and is scanned as source before
|
||||
and after extraction. Other GUI packages, AppImage, AppDir and Flatpak bundles,
|
||||
and GUI platform installers are excluded. Windows ZIP and macOS DMG artifacts
|
||||
remain CLI-only.
|
||||
|
||||
```
|
||||
Encapsulation:
|
||||
ML-KEM-768.Encaps(pk) → ml_ct[1088], ml_ss[32]
|
||||
archive_key = SHA3-512(ml_ss ‖ ml_ct ‖ "ZUPT-PQ-ONLY-v1")
|
||||
enc_key = archive_key[0:32]
|
||||
mac_key = archive_key[32:64]
|
||||
## Reproducing project checks
|
||||
|
||||
Start with the baseline source-only build:
|
||||
|
||||
```sh
|
||||
make clean
|
||||
make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf 1)" \
|
||||
WITH_SDK=0 WITH_PQBOX=0 V=1
|
||||
make WITH_SDK=0 WITH_PQBOX=0 check
|
||||
make WITH_SDK=0 WITH_PQBOX=0 test-all
|
||||
make sdk-test
|
||||
```
|
||||
|
||||
Security model: secure if ML-KEM-768 (post-quantum, NIST Level 3) remains
|
||||
unbroken. **There is no classical component**, so — unlike `--pq` — a break of
|
||||
ML-KEM-768 alone is sufficient to compromise the archive key. This mode exists
|
||||
only for compliance postures that mandate a single NIST-standardised PQ
|
||||
primitive with no classical KEM in the envelope (CNSA 2.0-style "PQ-only").
|
||||
Decapsulation uses ML-KEM Fujisaki-Okamoto implicit rejection: a wrong or
|
||||
tampered `ml_ct` yields a pseudorandom shared secret, so decryption fails
|
||||
closed at the HMAC check rather than leaking a decapsulation-validity oracle.
|
||||
**Unless a policy forbids the classical component, prefer `--pq`.**
|
||||
Where the compiler supports them, run the sanitizer target separately:
|
||||
|
||||
---
|
||||
|
||||
## Constant-Time Guarantees
|
||||
|
||||
### Jasmin-Verified (assembly linked into binary)
|
||||
|
||||
| Function | Purpose | Proof |
|
||||
|----------|---------|-------|
|
||||
| `zupt_mac_verify_ct` | HMAC comparison (32 bytes) | Jasmin type system: no branch on diff value |
|
||||
| `zupt_ct_select_32` | ML-KEM FO implicit rejection | Jasmin type system: no branch on cond value |
|
||||
|
||||
These functions are compiled from Jasmin source to x86-64 assembly. The
|
||||
Jasmin compiler enforces that no secret-typed variable flows into branch
|
||||
conditions or memory addresses. This guarantee holds at the machine code
|
||||
level — no C compiler optimization can introduce timing leaks.
|
||||
|
||||
### C Constant-Time (branchless, compiler-dependent)
|
||||
|
||||
| Function | Method | Risk |
|
||||
|----------|--------|------|
|
||||
| X25519 `fe_cswap` | Masked XOR (`mask & (a ^ b)`) | Low — branchless but compiler may optimize |
|
||||
| ML-KEM NTT/basemul | Montgomery reduction (no branches) | Low |
|
||||
| ML-KEM CBD sampling | Bitwise operations only | Low |
|
||||
| Key wipe (`zupt_secure_wipe`) | `explicit_bzero` / volatile | Low |
|
||||
|
||||
### NOT Constant-Time (documented risks)
|
||||
|
||||
| Function | Risk | Mitigation |
|
||||
|----------|------|------------|
|
||||
| AES-256 block encrypt | HIGH on shared hardware — S-box table lookups leak via cache timing | Jasmin AES-NI path planned; do not use on multi-tenant VMs |
|
||||
| SHA-256 | Low — table constants are public, not indexed by secret data | Accepted |
|
||||
|
||||
---
|
||||
|
||||
## Threat Model
|
||||
|
||||
### What VaptVupt Protects
|
||||
|
||||
| Asset | Protection |
|
||||
|-------|-----------|
|
||||
| File contents | AES-256-CTR encryption |
|
||||
| File names, sizes, structure | Encrypted in central index block, HMAC-protected |
|
||||
| Archive integrity (payloads + index) | Per-block HMAC-SHA256 |
|
||||
| Archive integrity (header + footer metadata) | v1.5+ archives: 32-byte archive-integrity-trailer HMAC-SHA256 over `hdr ‖ ft[0..23]`. v1.4 archives: not covered, downgrade warning on extract. |
|
||||
| Against stolen backups | AES-256 requires key/password to read |
|
||||
| Against tampering of file contents, names, sizes, offsets | HMAC detects any modification |
|
||||
| Against tampering of per-block frame preface bytes (codec_id, block_flags, varints, plaintext-XXH64) | v1.6: per-block MAC binds the canonical preface AAD; encryption-header block validated structurally |
|
||||
| Against tampering of archive comment (when present) | Comment block goes through the same per-block AEAD pipeline as data (AES-256-CTR + HMAC-SHA256 + preface AAD); `hdr.comment_offset` pointer is in the AIT-signed region |
|
||||
| Against block-swap (reorder) attacks | MAC binds an 8-byte position AAD; a block moved to another position fails verification and its partial output is unlinked. Dedup refs use sentinel seq=0 and rely on plaintext XXH64 for per-block integrity. |
|
||||
| Against malicious archive entries (Zip Slip / path traversal) | `zupt_path_is_safe()` rejects `..`, absolute paths, Windows drive/UNC paths, embedded NULs |
|
||||
| Against symlink at extract target (TOCTOU) | `zupt_safe_fopen_output()` uses `O_NOFOLLOW` on POSIX. Windows relies on directory ACLs (documented limitation). |
|
||||
| Against quantum adversary | `--pq` mode: ML-KEM-768 (NIST Level 3) hybridized with X25519 |
|
||||
|
||||
The wire/on-disk format is v1.6. See CHANGELOG.md for the per-release
|
||||
finding history behind these protections.
|
||||
|
||||
### What VaptVupt Does NOT Protect Against
|
||||
|
||||
| Threat | Reason | Mitigation Path |
|
||||
|--------|--------|----------------|
|
||||
| Attacker who knows the password or has the private key | Fundamental to encryption | Use strong passwords (12+ chars); protect key files |
|
||||
| Endpoint compromise (keylogger, malware on the host) | Outside the archive's trust boundary | Secure the machine where you type the password or hold the key |
|
||||
| Cache-timing side channels (C AES) | Table-based S-box lookups | Build with Jasmin AES-NI when available; avoid multi-tenant VMs |
|
||||
| Memory forensics during operation | Keys on stack during compress/extract | `zupt_secure_wipe()` on completion; `mlock()` planned |
|
||||
| Deniability | Archive header identifies format | `.zupt` magic bytes visible; ENCRYPTED flag in header |
|
||||
| Weak passwords | PBKDF2-SHA256 (600k) is the default KDF; Argon2id (memory-hard) is available in a WITH_SDK=1 build | Use `--pq` mode for critical data — keys are random, not derived from a password |
|
||||
| Traffic analysis / metadata | Archive size reveals data volume; file list, sizes, mtimes not padded | Outside VaptVupt's scope |
|
||||
| File permission/ownership | Not stored in archive | Documented in README.md |
|
||||
| Spectre-class side channels in callers | Below the constant-time primitive layer | Host OS / compiler mitigations |
|
||||
|
||||
### Quantum Threat Analysis
|
||||
|
||||
Scenario: adversary captures an encrypted archive today, stores it, and
|
||||
attempts decryption when a cryptographically-relevant quantum computer is
|
||||
available.
|
||||
|
||||
| Mode | Classical Security | Quantum Security | Verdict |
|
||||
|------|-------------------|-----------------|---------|
|
||||
| Password (`-p`) | Password-dependent + 256-bit AES | ~128-bit (Grover on AES), PBKDF2 accelerated | Vulnerable — use `--pq` |
|
||||
| PQ Hybrid (`--pq`) | ~128-bit (X25519) | NIST Level 3 (ML-KEM-768) | Protected |
|
||||
|
||||
In `--pq` mode: even if Shor's algorithm breaks X25519, ML-KEM-768
|
||||
protects the archive; even if a novel classical attack breaks ML-KEM,
|
||||
X25519 still provides ~128-bit security. The hybrid design is secure if
|
||||
either component holds.
|
||||
|
||||
### Extracting untrusted archives — operational guidance
|
||||
|
||||
The in-binary defenses are the primary control; the following are defense
|
||||
in depth:
|
||||
|
||||
1. Extract into a dedicated empty directory (not `~/Downloads` or `/tmp`).
|
||||
2. Audit symlinks in the target directory before extraction.
|
||||
3. Run extraction as a low-privilege user, never root.
|
||||
4. On Windows, pre-create the target directory with restrictive ACLs
|
||||
(the `O_NOFOLLOW` defense is POSIX-only).
|
||||
|
||||
### Out of scope
|
||||
|
||||
- External independent audit.
|
||||
- Side-channel testing on production hardware (timing leaks).
|
||||
- Formal verification beyond the Jasmin constant-time primitives.
|
||||
|
||||
---
|
||||
|
||||
## CSPRNG Policy
|
||||
|
||||
| Platform | Primary Source | Fallback | Failure Mode |
|
||||
|----------|---------------|----------|--------------|
|
||||
| Linux | `getrandom(2)` | `/dev/urandom` | Hard exit — no encryption without CSPRNG |
|
||||
| macOS | `/dev/urandom` | None | Hard exit |
|
||||
| Windows | `RtlGenRandom` | None | Hard exit |
|
||||
|
||||
There is no `rand()`, `srand()`, or any weak PRNG fallback anywhere in the
|
||||
codebase. If the OS CSPRNG is unavailable, VaptVupt exits with an error.
|
||||
This is a deliberate design choice — weak random keys are worse than no
|
||||
encryption.
|
||||
|
||||
---
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
| Platform | Compiler | Threading | CSPRNG | Status |
|
||||
|----------|----------|-----------|--------|--------|
|
||||
| Linux x86-64 | GCC 5+ / Clang 3.5+ | pthreads | `getrandom(2)` | Primary |
|
||||
| Linux ARM64 | GCC 5+ | pthreads | `getrandom(2)` | Tested |
|
||||
| macOS x86-64/ARM64 | Apple Clang | pthreads | `/dev/urandom` | Tested |
|
||||
| Windows x86-64 | MinGW / MSVC 2015+ | Win32 threads | `RtlGenRandom` | Tested |
|
||||
| FreeBSD | GCC / Clang | pthreads | `/dev/urandom` | Untested (expected to work) |
|
||||
|
||||
---
|
||||
|
||||
## Verification Commands
|
||||
|
||||
Anyone can verify the security claims. The default build needs only a C
|
||||
compiler + make (plus libm/pthread); no external crypto library.
|
||||
|
||||
```bash
|
||||
# Build
|
||||
make
|
||||
|
||||
# Functional tests
|
||||
make test-all
|
||||
|
||||
# Memory safety
|
||||
```sh
|
||||
make test-asan
|
||||
|
||||
# NIST/RFC test vectors
|
||||
make test-vectors && ./test_vectors
|
||||
|
||||
# Verify Jasmin symbols are active
|
||||
nm vaptvupt | grep "zupt_mac_verify_ct\|zupt_ct_select_32"
|
||||
# Expected: T zupt_mac_verify_ct
|
||||
# T zupt_ct_select_32
|
||||
|
||||
# Verify Jasmin compilation (requires jasminc)
|
||||
jasminc -arch x86-64 -o /dev/null jasmin/zupt_mac_verify.jazz
|
||||
jasminc -arch x86-64 -o /dev/null jasmin/zupt_mlkem_select.jazz
|
||||
make test-asan-run
|
||||
```
|
||||
|
||||
---
|
||||
The first command builds the sanitizer configuration; the second executes its
|
||||
test suite. Neither substitutes for the normal optimized build and tests.
|
||||
|
||||
© 2026 Cristian Cezar Moisés — AGPL-3.0-or-later (dual-licensed AGPL + commercial)
|
||||
The full local Linux `make release-check` passed on the immutable, non-promoted
|
||||
5.2.2 candidate at `ff99770`. Its recorded evidence includes packaging
|
||||
`PASS=49 FAIL=0 SKIP=0`,
|
||||
the 39/39 source-only scanner suite, strict GCC and Clang builds, GCC
|
||||
`-fanalyzer`, 9/9 static analysis in a tool-enabled run, ASan/UBSan/LSan, and
|
||||
1,000 mutation-fuzz iterations without a sanitizer-detected crash. An earlier
|
||||
off-screen GUI smoke run is supporting evidence, not an exact-candidate package
|
||||
result.
|
||||
|
||||
Post-tag CI integration failures prevented 5.2.2 promotion. Those upstream
|
||||
self-audit results are not independent certification and do not transfer to
|
||||
5.2.8. The immutable 5.2.3 candidate was not promoted because its source-policy
|
||||
test assumed LF for a Windows `.bat` file checked out as CRLF. The immutable
|
||||
v5.2.4 candidate was not promoted after exact-tag GitHub Actions run
|
||||
`33431386002`: 12 jobs succeeded, the sole openSUSE job failed in its
|
||||
standalone source-service harness because it did not enter the service
|
||||
directory, and dependent Windows and macOS jobs were skipped. A local
|
||||
Tumbleweed reproduction confirmed that `refs/tags/v5.2.4` is valid and that
|
||||
`os.chdir(service_dir)` lets `obs_scm`, `tar`, and `recompress` complete with a
|
||||
source-scanned archive. This was a release/test integration defect, not a
|
||||
product, archive, cryptographic, codec, or SDK ABI change, and its evidence does
|
||||
not transfer automatically to 5.2.8. The immutable v5.2.5 candidate was also
|
||||
not promoted: exact-tag GitHub Actions run `33434986357` recorded 13 successful
|
||||
jobs and failed native Windows/macOS jobs. Its Windows fixture-byte and macOS
|
||||
secure-wipe/Bash 3.2 defects were corrected for 5.2.6. A targeted clean-clone
|
||||
run of the corrected scanner under genuine GNU Bash 3.2.57 passed repository, standalone
|
||||
tree, standalone archive, and root-plus-tag modes; that local compatibility
|
||||
result does not transfer to any other gate. Exact-tag v5.2.6 run `33442264243`
|
||||
then completed 13 jobs successfully but failed native macOS because x86 SHA-NI
|
||||
test helpers were unused on arm64 under `-Werror`, and failed native Windows
|
||||
when argv transcoding aborted the safe UTF-8 fixture. Those are test-harness
|
||||
integration defects, not product, archive, cryptographic, codec, or SDK ABI
|
||||
changes; v5.2.6 remained unpromoted, so its results did not transfer to the
|
||||
required 5.2.8 suite. The immutable v5.2.7 candidate was likewise not
|
||||
promoted: exact-tag run `33445470664` concluded `cancelled` at
|
||||
`2026-08-31T23:11:19Z`, with 13 successful jobs, one failed macOS job after
|
||||
raw-C1 fixture creation returned `EILSEQ`, and one cancelled Windows job after
|
||||
the hosted job stalled in `make check`; a MinGW/Wine reproduction isolated the
|
||||
cause to a redirected password prompt entering `_getch`. Version 5.2.8 makes
|
||||
both test boundaries fail or skip without hanging. Manual pre-tag run
|
||||
`33452602634` subsequently passed 14 of 15 jobs, including the native macOS
|
||||
DMG and the Windows source audit, build, and full distribution checks. The
|
||||
remaining Windows smoke failure was an old MSYS `grep` non-BMP pattern boundary
|
||||
after ZUPT had compressed and verified all inputs; MinGW/Wine confirmed ZUPT's
|
||||
byte-exact UTF-8 listing. The corrected gate validates Latin-1, BMP, and
|
||||
non-BMP listing bytes without locale-sensitive matching, then requires
|
||||
extraction and a full tree diff. The failed run is not exact-candidate
|
||||
evidence. Exact-tag run `33456209269` then completed 15/15 jobs successfully,
|
||||
including native Windows/macOS, the pinned local OBS service chain, package
|
||||
installation/round trips, source-only checks, analyzers, and sanitizers.
|
||||
Promotion run `33457868306` published the exact 13-file allowlist after
|
||||
format, metadata, payload, and checksum validation. An unavailable or
|
||||
unexecuted environment remains `SKIP`, never `PASS`; successful project CI is
|
||||
still not independent security certification.
|
||||
|
||||
Run target-native static analyzers and package checks as additional evidence.
|
||||
Do not infer x86_64, aarch64, ppc64le, s390x, riscv64, macOS, Windows, Leap, or
|
||||
SLE success from these commands unless that exact environment produced a
|
||||
successful recorded result.
|
||||
|
||||
ZUPT application code is distributed under AGPL-3.0-or-later. The bundled
|
||||
VaptVupt codec source is GPL-3.0-or-later. The two xxHash-derived XXH64 units
|
||||
also carry BSD-2-Clause. The pq-crystals/kyber-derived portions of native
|
||||
ML-KEM carry CC0-1.0 in addition to the application license, and the x86 BCJ
|
||||
state machine is adapted from public-domain LZMA SDK source. Native X25519
|
||||
portions adapted from curve25519-donna conservatively retain BSD-3-Clause. See
|
||||
`LICENSE`, `LICENSE-GPL-3.0`, `LICENSE-BSD-2-Clause`, `LICENSE-BSD-3-Clause`,
|
||||
`LICENSE-CC0-1.0`, `NOTICE`, and `THIRD-PARTY-NOTICES.md`. Historical license
|
||||
grants for exact earlier material are recorded in the 5.2.2 licensing erratum;
|
||||
the current notices do not revoke them.
|
||||
|
|
|
|||
|
|
@ -1,110 +1,179 @@
|
|||
THIRD-PARTY NOTICES
|
||||
===================
|
||||
# Third-party and bundled-component notices
|
||||
|
||||
This document records VaptVupt's runtime dependencies and build-time
|
||||
tools. If you redistribute VaptVupt, you must preserve this attribution
|
||||
document along with the LICENSE file.
|
||||
This file records bundled source, generated textual source and optional system
|
||||
dependencies. Preserve it with LICENSE, NOTICE, and the applicable license
|
||||
texts.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
Licensing
|
||||
-------------------------------------------------------------------------
|
||||
## Bundled VaptVupt codec
|
||||
|
||||
**Note on VaptVupt LZ codec licensing**: the VaptVupt LZ codec
|
||||
(src/vv_*.c, src/vaptvupt_api.c, include/vaptvupt*.h) is licensed
|
||||
GPL-3.0-or-later (not AGPL like the rest of the project) so that, with
|
||||
sufficient maturity, it can be considered for upstreaming into the Linux
|
||||
or BSD kernels, which require GPL-compatible licenses. The author retains
|
||||
the right to dual-license the codec under other terms for commercial use;
|
||||
contact sac@securityops.co for inquiries.
|
||||
The compression codec in src/vv_*.c, src/vaptvupt_api.c,
|
||||
include/vaptvupt*.h, and include/vv_*.h is bundled as source and licensed
|
||||
GPL-3.0-or-later.
|
||||
|
||||
The rest of the project (vaptvupt CLI, Jasmin source, GUI) is licensed
|
||||
AGPL-3.0-or-later. Commercial licenses (relief from the AGPL network-use
|
||||
clause) are available; contact sac@securityops.co.
|
||||
- Recorded codec release: 2.65.3
|
||||
- Recorded upstream tag: v2.65.3
|
||||
- Standalone upstream: https://git.securityops.co/cristiancmoises/vaptvupt-codec
|
||||
- Integration commit in this repository:
|
||||
59f9ebc59ea13c6edf1d199ca795cdbf00e62226
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
Build-time tool (not redistributed)
|
||||
-------------------------------------------------------------------------
|
||||
The integration commit records an in-tree ANS safe-zone reserve applied on top
|
||||
of that tag. Earlier integration commit a2350dd also records wrapper-default
|
||||
changes used by the CLI. This repository did not retain the standalone tag
|
||||
object hash, so the tag name and the immutable integration commits are the
|
||||
provenance evidence available here; no unverified external hash is asserted.
|
||||
|
||||
**jasminc** — the Jasmin language compiler
|
||||
The openSUSE package truthfully declares
|
||||
bundled(vaptvupt-codec) = 2.65.3. No compiled codec object or library is
|
||||
distributed in the source tree or source archive.
|
||||
|
||||
The constant-time cryptographic primitives in jasmin/*.jazz are
|
||||
compiled to native assembly (jasmin/*.s) using the external `jasminc`
|
||||
compiler. The jasminc tool is not bundled with VaptVupt; the AGPL .jazz
|
||||
source files and their AGPL-licensed .s assembly output are bundled.
|
||||
## Jasmin and textual assembly
|
||||
|
||||
Upstream: https://github.com/jasmin-lang/jasmin
|
||||
License: MIT (the compiler itself; not relevant to VaptVupt's licensing)
|
||||
Used by: VaptVupt's build system, only when re-generating jasmin/*.s
|
||||
from jasmin/*.jazz (most users won't need to do this —
|
||||
pre-built .s files ship in this repo).
|
||||
Files under `jasmin/` include AGPL-licensed `.jazz` source or algorithm
|
||||
descriptions and textual GNU assembly `.s`. The assembly is source, not an
|
||||
object file. Provenance is recorded per production unit rather than treating
|
||||
every `.s` file as generated:
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
Runtime system libraries (linked from the OS, never bundled)
|
||||
-------------------------------------------------------------------------
|
||||
- `zupt_mac_verify.s`, `zupt_mlkem_select.s`, and `zupt_x25519_fe.s` identify
|
||||
themselves as output of Jasmin Compiler 2026.03.0;
|
||||
- `zupt_aes_ctr.s` is recorded in its file header as `jasminc` output, but the
|
||||
exact compiler version was not retained in that file, so no version stronger
|
||||
than the repository record is asserted;
|
||||
- `zupt_aes_ctr4.s` is hand-written production assembly matching the algorithm
|
||||
documented by `zupt_aes_ctr4.jazz`; that `.jazz` file is not compiled.
|
||||
|
||||
These are standard system libraries provided by the operating system's
|
||||
package manager (apt, dnf, pacman, etc.). They are dynamically linked
|
||||
at runtime and are NOT redistributed as part of VaptVupt.
|
||||
Regeneration of files identified as compiler output uses the external
|
||||
`jasminc` compiler:
|
||||
|
||||
**libargon2** — Argon2id password hashing function (RFC 9106)
|
||||
- Upstream: https://github.com/jasmin-lang/jasmin
|
||||
- Compiler license: MIT
|
||||
|
||||
Required only for: the optional `make WITH_SDK=1` build. The default
|
||||
build uses native PBKDF2-SHA256 and does not link
|
||||
libargon2.
|
||||
Linked at runtime: libargon2.so.1
|
||||
Version expected: 1.0+ (Debian/Ubuntu: libargon2-1)
|
||||
Upstream: https://github.com/P-H-C/phc-winner-argon2
|
||||
License: Apache-2.0 OR CC0-1.0 (dual)
|
||||
Copyright: (c) 2015 The Argon2 Authors
|
||||
Used by: Argon2id password-derived encryption mode
|
||||
The compiler itself is not bundled or redistributed. Hand-written assembly
|
||||
must not be represented as generated or formally verified merely because a
|
||||
corresponding `.jazz` description exists.
|
||||
|
||||
**OpenSSL libcrypto** — AES, SHA-256, AES-NI hardware backends
|
||||
## Optional system libraries
|
||||
|
||||
Linked at runtime: libcrypto.so.3
|
||||
Version expected: 3.0+
|
||||
Upstream: https://www.openssl.org
|
||||
License: Apache-2.0
|
||||
Copyright: (c) 1998-2026 The OpenSSL Project
|
||||
Used by: AES-256-CTR, SHA-256, hardware-accelerated paths
|
||||
The default WITH_SDK=0 WITH_PQBOX=0 build uses the operating system's C runtime,
|
||||
math and threading libraries and does not bundle a shared library.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
Compatibility with public standards
|
||||
-------------------------------------------------------------------------
|
||||
WITH_SDK=1 and WITH_PQBOX=1 are opt-in integrations. They use only headers and
|
||||
libraries supplied by the system/toolchain configuration and fail explicitly
|
||||
when those dependencies are unavailable:
|
||||
|
||||
Where VaptVupt implements public standards, it does so independently from
|
||||
any reference implementation. Other projects in the post-quantum hybrid
|
||||
encryption space (libsodium, age, Tink, rustls, etc.) were referenced as
|
||||
prior art during design, but no code was copied from any external
|
||||
project. Standards followed:
|
||||
- libvuptsdk: enables --pq-sdk and the Argon2id-backed SDK path;
|
||||
- libpqvaptvupt: enables --pq-box.
|
||||
|
||||
- FIPS 197 (AES)
|
||||
- FIPS 202 (Keccak / SHA-3)
|
||||
- FIPS 203 (ML-KEM)
|
||||
- RFC 5297 (AES-SIV)
|
||||
- RFC 5869 (HKDF)
|
||||
- RFC 7748 (X25519)
|
||||
- RFC 8032 (Ed25519)
|
||||
- RFC 8439 (ChaCha20-Poly1305)
|
||||
- RFC 9106 (Argon2)
|
||||
- RFC 9180 (HPKE)
|
||||
The former vendor/vuptsdk and vendor/pqvaptvupt header snapshots and all
|
||||
fallbacks to local precompiled libraries were removed. No download occurs in
|
||||
make, packaging build, or package checks.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
Reporting attribution issues
|
||||
-------------------------------------------------------------------------
|
||||
## xxHash-derived source
|
||||
|
||||
If you believe VaptVupt redistributes code from a project not listed here,
|
||||
or if attribution information is incomplete, please email:
|
||||
`src/zupt_xxh.c` and `src/vv_xxh64.c` contain adapted XXH64 routines based on
|
||||
xxHash by Yann Collet. xxHash is BSD-2-Clause, not public domain. The upstream
|
||||
copyright, conditions, and disclaimer are preserved in
|
||||
`LICENSE-BSD-2-Clause`; those obligations apply in addition to the AGPL or GPL
|
||||
scope identified by each source file.
|
||||
|
||||
sac@securityops.co
|
||||
- Upstream: https://github.com/Cyan4973/xxHash
|
||||
- Upstream license: https://github.com/Cyan4973/xxHash/blob/dev/LICENSE
|
||||
|
||||
with the subject "[third-party]" and details of the issue.
|
||||
## pq-crystals/kyber-derived ML-KEM source
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
License summary
|
||||
-------------------------------------------------------------------------
|
||||
`src/zupt_mlkem.c` contains portions adapted from the pq-crystals/kyber
|
||||
reference implementation, including its NTT, base multiplication, Montgomery
|
||||
conversion, and related representation conventions. The upstream project
|
||||
offers that reference code under either CC0-1.0 or Apache-2.0; ZUPT elects
|
||||
the CC0-1.0 option for those portions. Local integration and modifications
|
||||
remain under AGPL-3.0-or-later, as recorded by the compound per-file SPDX
|
||||
identifier.
|
||||
|
||||
VaptVupt CLI, Jasmin source, GUI: AGPL-3.0-or-later
|
||||
VaptVupt LZ codec: GPL-3.0-or-later
|
||||
Commercial license (any component): contact sac@securityops.co
|
||||
- Upstream: https://github.com/pq-crystals/kyber
|
||||
- Upstream license record: https://github.com/pq-crystals/kyber/blob/main/LICENSE
|
||||
- Local introduction commit: c80332778fb10364a606bf0380f440dc7be66ced
|
||||
- Local FIPS 203 correction commit: 862f4a2df6c756ebd0369e176ea68b5ac506f422
|
||||
|
||||
Project home: https://git.securityops.co/cristiancmoises/vaptvupt
|
||||
The repository did not retain an immutable upstream Kyber revision for the
|
||||
original adaptation. No unverified upstream commit is asserted. The complete
|
||||
CC0-1.0 legal text is in `LICENSE-CC0-1.0`.
|
||||
|
||||
## curve25519-donna-derived X25519 source
|
||||
|
||||
`src/zupt_x25519.c` contains portions adapted from the 5x51-bit
|
||||
curve25519-donna implementation, including its field representation, packing,
|
||||
constant-time swap, and inversion-chain approach. The upstream source file
|
||||
describes the code as public domain, while the repository preserves a
|
||||
BSD-3-Clause notice. This distribution conservatively retains that complete
|
||||
BSD-3-Clause notice in `LICENSE-BSD-3-Clause`; local integration and
|
||||
modifications remain AGPL-3.0-or-later under the compound per-file SPDX
|
||||
identifier.
|
||||
|
||||
- Upstream: https://github.com/agl/curve25519-donna
|
||||
- Upstream license record: https://github.com/agl/curve25519-donna/blob/master/LICENSE.md
|
||||
- Upstream copyright: Copyright 2008, Google Inc.
|
||||
- Upstream author record: Adam Langley
|
||||
- Local introduction commit: c80332778fb10364a606bf0380f440dc7be66ced
|
||||
|
||||
The repository did not retain an immutable upstream revision for the original
|
||||
adaptation. No unverified upstream commit is asserted, and the historical
|
||||
reference to libsodium is treated as an implementation comparison rather than
|
||||
an unsupported claim that libsodium was the copied source.
|
||||
|
||||
## LZMA SDK x86 BCJ source
|
||||
|
||||
The x86 state machine in `src/vv_bcj.c` is adapted from Igor Pavlov's
|
||||
`C/Bra86.c` in the LZMA SDK. The official LZMA SDK is placed in the public
|
||||
domain. The AArch64 filter in the same file is separately documented local
|
||||
code and is not represented as LZMA SDK source.
|
||||
|
||||
- Upstream: https://www.7-zip.org/sdk.html
|
||||
- Upstream author: Igor Pavlov
|
||||
- Upstream status: public domain
|
||||
|
||||
The exact SDK version or revision used by the original integration was not
|
||||
retained, so none is asserted. The former `clean-room` description was removed
|
||||
because repository evidence cannot establish that development process.
|
||||
|
||||
## SHA-Intrinsics SHA-NI source
|
||||
|
||||
The SHA-NI compression path in `src/zupt_sha256_shani.c` is adapted from
|
||||
Jeffrey Walton's public-domain `SHA-Intrinsics/sha256-x86.c` reference, which
|
||||
records that it is based on Intel and Sean Gulley's miTLS material. The
|
||||
immutable upstream reference below explicitly places the code in the public
|
||||
domain; it therefore adds no separate package-license term. Local integration
|
||||
and modifications remain AGPL-3.0-or-later.
|
||||
|
||||
- Upstream: https://github.com/noloader/SHA-Intrinsics
|
||||
- Audited source revision: d03795497f3e4576083fc2cd8fe0b924f24d0bb2
|
||||
- Upstream source: https://github.com/noloader/SHA-Intrinsics/blob/d03795497f3e4576083fc2cd8fe0b924f24d0bb2/sha256-x86.c
|
||||
- Upstream author: Jeffrey Walton
|
||||
- Upstream status: public domain
|
||||
- Local introduction commit: 544a2cd64758478690e33a923b2ab75347122f51
|
||||
|
||||
## GUI image data
|
||||
|
||||
The PNG and ICO files under gui/assets/ are non-executable first-party GUI data.
|
||||
Their purpose, Git provenance and license scope, including the historical MIT
|
||||
grant attached to their unchanged Git blobs, are recorded in
|
||||
`gui/assets/README.md`.
|
||||
|
||||
## AppImage type-2 runtime
|
||||
|
||||
No AppImage is a promised or promoted 5.2.8 release asset. The upstream
|
||||
type-2 runtime inspected during the 5.2.2 review statically linked musl, libfuse,
|
||||
squashfuse, zstd, zlib, and mimalloc, but its own license notice did not list
|
||||
mimalloc and the available release inputs did not provide a complete
|
||||
LGPL-compatible source/relink handoff. ZUPT therefore does not
|
||||
redistribute that runtime.
|
||||
|
||||
`packaging/build-appimage.sh` remains an offline downstream helper. It accepts
|
||||
no network input and requires the operator to supply both a locally verified
|
||||
runtime and `APPIMAGE_RUNTIME_COMPLIANCE_FILE`, containing the license notices,
|
||||
source correspondence or offer, and relink information applicable to those
|
||||
exact runtime bytes. An artifact produced independently with that helper is
|
||||
not covered by the 5.2.8 upstream release gates.
|
||||
|
||||
## Reporting attribution issues
|
||||
|
||||
Report incomplete or incorrect attribution to sac@securityops.co with the
|
||||
subject [third-party].
|
||||
|
|
|
|||
682
THREAT_MODEL.md
682
THREAT_MODEL.md
|
|
@ -1,328 +1,362 @@
|
|||
# VaptVupt threat model
|
||||
|
||||
Plain-English description of what VaptVupt protects against, what it
|
||||
doesn't, and what assumptions you're making when you use it.
|
||||
|
||||
This document is for users and downstream packagers. Read it before
|
||||
trusting VaptVupt with anything you can't afford to lose.
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
VaptVupt is designed for at-rest backup encryption by someone who
|
||||
controls the machine doing the encryption and the machine doing the
|
||||
extraction. It is not a network protocol, a multi-party scheme, or
|
||||
a substitute for full-disk encryption.
|
||||
|
||||
| Use case | VaptVupt is appropriate? |
|
||||
|---|---|
|
||||
| Backing up files to an untrusted cloud (S3, Backblaze, Google Drive) | Yes |
|
||||
| Backing up a disk image to external media you might lose | Yes |
|
||||
| Long-term archival of personal/business data | Yes |
|
||||
| Sharing an encrypted archive with someone you trust to handle the key | Yes, with care (see "Key distribution" below) |
|
||||
| Real-time encrypted communication | No (use Signal, age, or TLS) |
|
||||
| Multi-party access (n-of-m) | No (no threshold scheme) |
|
||||
| Hiding the existence of an archive (steganography) | No (archive header has fixed magic bytes) |
|
||||
| Protecting against a hostile machine you're encrypting on | No (a compromised host can read plaintext before encryption) |
|
||||
|
||||
---
|
||||
|
||||
## Modes referenced in this document
|
||||
|
||||
- `-p` / password mode: symmetric encryption with a key derived from a
|
||||
password. The default build derives the key with PBKDF2-SHA256
|
||||
(600k iterations). Argon2id is available only in an upstream
|
||||
`make WITH_SDK=1` build against the separately distributed
|
||||
libraries.
|
||||
- `--pq`: native post-quantum **hybrid** mode (ML-KEM-768 + X25519), the
|
||||
recommended PQ mode in the default build. The ML-KEM-768 implementation
|
||||
is in-tree.
|
||||
- `--pq-only`: native **full/pure** post-quantum mode (ML-KEM-768 only, no
|
||||
X25519), also in the default build. For compliance postures that mandate a
|
||||
single NIST-standardised PQ primitive with no classical KEM in the envelope.
|
||||
Its threat profile differs from `--pq` in exactly one axis: it has no
|
||||
classical fallback, so a break of ML-KEM-768 alone breaks the archive
|
||||
(see §5 and "Cryptographic assumptions").
|
||||
- `--pq-sdk` / `--pq-box`: optional post-quantum modes backed by the
|
||||
separately distributed `libzuptsdk` / `libpqvaptvupt` libraries.
|
||||
Available only in a `make WITH_SDK=1` build. Key files for these
|
||||
modes are produced by `vaptvupt keygen --sdk`, also SDK-only.
|
||||
|
||||
---
|
||||
|
||||
## What VaptVupt protects against
|
||||
|
||||
### 1. Confidentiality of archive contents (encrypted mode)
|
||||
|
||||
An attacker with read access to the archive bytes cannot recover
|
||||
plaintext file contents, file names, file sizes, file modes, or
|
||||
embedded comments without the key/password, assuming:
|
||||
|
||||
- The chosen mode is one of the encrypted modes (`-p`, `--pq`, or the
|
||||
optional `--pq-sdk` / `--pq-box`)
|
||||
- The password is strong enough to resist offline brute-force
|
||||
(see "Password strength" below)
|
||||
- The key file (for `--pq-sdk` / `--pq-box`) was not compromised at
|
||||
generation time
|
||||
|
||||
### 2. Integrity of every byte of an encrypted archive
|
||||
|
||||
If any single bit of the on-disk archive bytes is flipped, the
|
||||
extraction fails with an authentication error. Coverage layers:
|
||||
|
||||
- Per-block HMAC-SHA256 with frame-preface AAD (F-09): every data
|
||||
block carries an HMAC over its ciphertext and over the canonical
|
||||
29-byte preface (block_type, codec_id, block_flags, sizes,
|
||||
plaintext-XXH64)
|
||||
- Archive Integrity Trailer (F-08): HMAC-SHA256 over the 64-byte
|
||||
header and 24 bytes of footer, appended after the footer
|
||||
- Strict structural validation of the encryption-header block (F-09):
|
||||
codec must be `STORE`, flags must be 0, csz must equal usz, the
|
||||
plaintext XXH64 must match
|
||||
|
||||
### 3. Tamper detection on plaintext archives (best-effort)
|
||||
|
||||
Plaintext archives (no `-p`, no `--pq*`) are protected by XXH64
|
||||
plaintext checksums per block plus structural validation. This is
|
||||
not cryptographic integrity — a determined attacker with write access
|
||||
can produce a tampered plaintext archive that passes the checksum
|
||||
(XXH64 is not collision-resistant). It does catch accidental
|
||||
corruption and naive tampering.
|
||||
|
||||
Use an encrypted mode if you need cryptographic integrity.
|
||||
|
||||
### 4. Authentication failure indistinguishability (F-11)
|
||||
|
||||
The default error message for "wrong password", "wrong PQ key",
|
||||
and "actual header tamper" is the same single line:
|
||||
|
||||
> `Error: Authentication failed (wrong key, wrong password, or tampered archive).`
|
||||
|
||||
This prevents an attacker who can issue extraction attempts from
|
||||
learning which check failed first via the stderr output. Timing is
|
||||
also constant (HMAC is always run, branchless return).
|
||||
|
||||
The detailed cause is available via `--verbose` for debugging on
|
||||
machines under the user's own control.
|
||||
|
||||
### 5. Post-quantum forward secrecy (`--pq`, `--pq-only`, and optional `--pq-sdk`)
|
||||
|
||||
The native `--pq` mode uses ML-KEM-768 (FIPS 203 — validated byte-for-byte
|
||||
against OpenSSL 3.5's ML-KEM-768; see AUDIT.md) hybridized with X25519 via an
|
||||
HKDF combiner. Archives encrypted today cannot be decrypted by a future quantum
|
||||
adversary holding only the ciphertext, assuming:
|
||||
|
||||
- ML-KEM-768 retains its claimed security level (NIST Category 3,
|
||||
192-bit classical / 96-bit quantum strength)
|
||||
- X25519 hybridization protects against an unforeseen ML-KEM break
|
||||
- The recipient's private key is not later compromised
|
||||
|
||||
The native `--pq-only` mode (envelope type `0x06`) provides the same
|
||||
harvest-now-decrypt-later protection using ML-KEM-768 as the *sole* key
|
||||
mechanism. It exists for compliance postures that mandate a single
|
||||
NIST-standardised PQ primitive with no classical KEM in the envelope
|
||||
(CNSA 2.0-style "PQ-only"). **The trade-off is a loss of the second
|
||||
assumption above:** there is no X25519 hybridization, so an unforeseen
|
||||
break of ML-KEM-768 alone is sufficient to recover the archive key. For
|
||||
that reason `--pq` (hybrid) is the recommended default, and `--pq-only`
|
||||
should be used only when a policy forbids the classical component.
|
||||
|
||||
The optional `--pq-sdk` mode provides the same hybrid guarantee as
|
||||
`--pq` via the separately distributed SDK libraries.
|
||||
|
||||
### 6. Side-channel resistance for cryptographic primitives
|
||||
|
||||
The hot crypto paths (AES-256-CTR, HMAC-SHA256 comparison, X25519
|
||||
field operations, ML-KEM polynomial arithmetic) are implemented in
|
||||
Jasmin and proved constant-time at the assembly level on x86_64.
|
||||
Non-Jasmin platforms (aarch64, fallback x86_64) use C implementations
|
||||
that avoid secret-dependent branches and memory accesses where
|
||||
feasible — but without formal proof.
|
||||
|
||||
---
|
||||
|
||||
## What VaptVupt does NOT protect against
|
||||
|
||||
### 1. Compromised endpoints
|
||||
|
||||
VaptVupt cannot protect against:
|
||||
|
||||
- Malware on the machine doing the encryption (it sees plaintext
|
||||
before any crypto is applied)
|
||||
- Malware on the machine doing the extraction (it sees plaintext
|
||||
after decryption)
|
||||
- A hardware keylogger capturing the password
|
||||
- A compromised user account that can read your files or
|
||||
`~/.zupt-key` directly
|
||||
- Cold-boot attacks on running machines
|
||||
|
||||
If you don't trust the machine, VaptVupt cannot help.
|
||||
|
||||
### 2. Key compromise
|
||||
|
||||
If the password or `~/.zupt-key` is leaked:
|
||||
|
||||
- All archives encrypted with that key are decryptable
|
||||
- VaptVupt has no forward secrecy across archives — each archive
|
||||
is encrypted under a single static key derived from the password
|
||||
or stored in the key file
|
||||
- There is no key-rotation feature; rotate by re-encrypting
|
||||
archives under a new password/key and securely deleting the old
|
||||
password/key
|
||||
|
||||
For high-value, long-term archives, treat the key file as you
|
||||
would a master password: store it offline, encrypt it under
|
||||
another layer (e.g. on an encrypted USB), and rotate periodically.
|
||||
|
||||
### 3. Password strength
|
||||
|
||||
Password mode derives the key with PBKDF2-SHA256 (600k iterations)
|
||||
in the default build, or Argon2id in a `make WITH_SDK=1` build. A
|
||||
key derivation function slows offline guessing but does not make a
|
||||
short, common password safe: a determined attacker with GPU clusters
|
||||
or cloud compute can still exhaust a weak password.
|
||||
|
||||
Use a long, high-entropy password — a multi-word diceware passphrase
|
||||
or a random 16+ character string with a full alphabet. For critical
|
||||
data, use a key-file mode (native `--pq`, or the optional `--pq-sdk`
|
||||
with a random key file from `vaptvupt keygen --sdk`) so the key is
|
||||
CSPRNG output, not derived from human-typed text.
|
||||
|
||||
### 4. Metadata leakage from archive structure
|
||||
|
||||
Even with encryption, an attacker who can see the archive bytes
|
||||
can infer:
|
||||
|
||||
- Approximate file count (from `total_blocks` in the footer)
|
||||
- Total archive size (file size on disk)
|
||||
- Whether the archive is encrypted at all (`ZUPT_FLAG_ENCRYPTED`
|
||||
in the global flags is visible)
|
||||
- Whether the archive is solid or per-file mode (visible flag)
|
||||
- Whether post-quantum mode is in use (visible flag)
|
||||
- Approximate file size distribution (block sizes are visible
|
||||
even when block payloads are encrypted)
|
||||
- Archive creation time (a 64-bit timestamp in the header)
|
||||
- A random 16-byte UUID per archive (no information leak, but
|
||||
globally identifies the archive across copies)
|
||||
|
||||
If metadata privacy matters, layer VaptVupt under another tool that
|
||||
hides bulk metadata (e.g., put the `.zupt` file inside a fixed-size
|
||||
encrypted container).
|
||||
|
||||
### 5. Network attacks
|
||||
|
||||
VaptVupt is not a network protocol. There is no:
|
||||
|
||||
- Forward-secure session establishment (use TLS or Noise)
|
||||
- Mutual authentication of remote parties (use signed messages or
|
||||
TLS client certs)
|
||||
- Replay protection across sessions (archives can be replayed by
|
||||
an attacker who can write to the destination)
|
||||
- Network-layer encryption (use TLS to transport `.zupt` files)
|
||||
|
||||
### 6. Multi-party schemes
|
||||
|
||||
There is no threshold cryptography, no n-of-m sharing, no
|
||||
multi-party computation, no proxy re-encryption. Each archive
|
||||
has exactly one decryption credential (one password OR one
|
||||
recipient key). To give two people access to the same archive,
|
||||
they must share the password or the key file.
|
||||
|
||||
### 7. Plausible deniability / hidden volumes
|
||||
|
||||
VaptVupt archives have a fixed 6-byte magic `\x90\x5a\x55\x50\x54\x01`
|
||||
at offset 0. Anyone scanning the bytes can see it's a VaptVupt
|
||||
archive. VaptVupt has no hidden-volume or duress-password feature.
|
||||
|
||||
### 8. Side channels we don't claim to address
|
||||
|
||||
- Power analysis (relevant for embedded targets, not commodity desktops)
|
||||
- Electromagnetic emanation
|
||||
- Acoustic side channels
|
||||
- Network timing of upload patterns
|
||||
- Filesystem-level metadata (mtime/atime of the `.zupt` file)
|
||||
|
||||
### 9. Trusted setup of post-quantum primitives
|
||||
|
||||
The in-tree ML-KEM-768 implementation was not independently audited
|
||||
at the time of writing. We use NIST KAT vectors for correctness
|
||||
verification but have not formally proven constant-time properties
|
||||
for every PQ code path.
|
||||
|
||||
For maximum assurance, treat the post-quantum layer as a hedge — it
|
||||
does not replace the X25519 layer; both must be broken for an
|
||||
attacker to recover plaintext.
|
||||
|
||||
### 10. Format extension attacks
|
||||
|
||||
The format is versioned (v1.6). Older readers may accept newer
|
||||
archives in unexpected ways. We try to maintain forward
|
||||
compatibility, but a careful attacker who can produce
|
||||
malformed-but-just-valid archives may find parser-state issues that
|
||||
don't rise to the level of a CVE. The fuzzing harness
|
||||
(`make fuzz-format`) is the primary mitigation; report bugs.
|
||||
|
||||
### 11. Compression-side-channel attacks (CRIME / BREACH style)
|
||||
|
||||
VaptVupt compresses before encryption. If an attacker can:
|
||||
|
||||
- Influence part of the plaintext (e.g. inject a known prefix)
|
||||
- Observe the resulting archive size precisely
|
||||
|
||||
then they can use the compression ratio to learn information about
|
||||
the rest of the plaintext — this is the classic CRIME/BREACH attack
|
||||
against TLS compression.
|
||||
|
||||
VaptVupt is designed for offline backup, where attacker-controlled
|
||||
plaintext injection is rare. If your threat model includes
|
||||
attacker-chosen plaintext mixed with secret plaintext in the same
|
||||
archive, use `--no-compress` (codec 0 = STORE) to disable the
|
||||
LZ codec and eliminate this side channel.
|
||||
|
||||
---
|
||||
|
||||
## Cryptographic assumptions
|
||||
|
||||
VaptVupt's security rests on the following standard assumptions:
|
||||
|
||||
| Assumption | What breaks if it fails |
|
||||
|---|---|
|
||||
| AES-256-CTR is a secure stream cipher | All encrypted archives become readable |
|
||||
| HMAC-SHA256 is a secure PRF / MAC | Tamper detection fails; integrity can be forged |
|
||||
| PBKDF2-SHA256 (or Argon2id, WITH_SDK) is a secure password KDF | Password-mode archives become brute-forceable faster |
|
||||
| ML-KEM-768 retains NIST Category 3 security | `--pq` / `--pq-sdk` reduce to the X25519 layer; **`--pq-only` has no fallback and is broken** |
|
||||
| X25519 retains 128-bit security (no quantum) | Hybrid PQ modes reduce to the ML-KEM layer; `--pq-only` and classical password mode unaffected |
|
||||
| HKDF-SHA256 is a secure key-derivation construction | Combined PQ + classical keys may be predictable |
|
||||
| SHA3 / SHAKE retain pre-image and collision resistance | Auxiliary protocol bindings may be forged |
|
||||
|
||||
If you don't trust one of these primitives, VaptVupt cannot protect
|
||||
you. We rely on the same primitives the broader cryptographic
|
||||
community has standardized.
|
||||
|
||||
---
|
||||
# ZUPT 5.2.8 threat model
|
||||
|
||||
This document defines the security boundary of the ZUPT archive tool. It is
|
||||
not a certification, a guarantee against every hostile input, or a substitute
|
||||
for reviewing the exact source and binary used for important data.
|
||||
|
||||
## Intended use
|
||||
|
||||
ZUPT is intended for at-rest backup archives created and restored on
|
||||
machines controlled by the user. It can be used when the storage provider or
|
||||
physical medium is not trusted, provided encryption is enabled and credentials
|
||||
remain secret.
|
||||
|
||||
It is not a network protocol, a full-disk encryption system, a multi-party or
|
||||
threshold scheme, a password manager, or a way to make an archive's existence
|
||||
plausibly deniable.
|
||||
|
||||
## Baseline considered here
|
||||
|
||||
The upstream baseline is built from the 5.2.8 source with:
|
||||
|
||||
```sh
|
||||
make WITH_SDK=0 WITH_PQBOX=0
|
||||
```
|
||||
|
||||
It contains the native password, ML-KEM-768 + X25519 hybrid `--pq`, and
|
||||
ML-KEM-768-only `--pq-only` modes. It does not load a precompiled library from
|
||||
the repository and does not download a dependency while building.
|
||||
|
||||
`WITH_SDK=1` and `WITH_PQBOX=1` add separately installed system libraries and
|
||||
change the assessed code boundary. The SDK and PQBOX integrations must be
|
||||
reviewed with their exact packaged source and version; success of the baseline
|
||||
tests is not evidence for them.
|
||||
|
||||
Textual assembly under `jasmin/` is a separate `WITH_JASMIN=1` option for
|
||||
supported x86_64 compiler targets. The directory contains both generated and
|
||||
separately identified hand-written assembly. Portable C is the default.
|
||||
Architecture portability is a source property, not evidence that an unexecuted
|
||||
architecture passed.
|
||||
|
||||
## Assets
|
||||
|
||||
The assets ZUPT tries to protect are:
|
||||
|
||||
- archived file contents and encrypted index data;
|
||||
- the integrity and ordering of encrypted archive blocks and current global
|
||||
metadata covered by the archive integrity trailer;
|
||||
- private keys, passwords, and derived encryption/MAC keys while held by the
|
||||
trusted caller;
|
||||
- safe placement of extracted entries within the requested destination.
|
||||
|
||||
The archive's existence, total byte length, magic, encryption/framing flags, and
|
||||
some size/structure information are observable. Plain archives provide
|
||||
corruption detection, not cryptographic protection against an active attacker.
|
||||
|
||||
## Adversaries considered
|
||||
|
||||
The design considers an adversary who can read, copy, truncate, reorder, or
|
||||
modify stored archive bytes but cannot read the encryption endpoint's memory or
|
||||
credentials. It also considers accidental corruption and malicious archive
|
||||
entry paths during extraction.
|
||||
|
||||
The following adversaries are outside the protection boundary:
|
||||
|
||||
- malware, a keylogger, or an administrator on the source or restore endpoint;
|
||||
- an attacker who obtains the password or matching private key;
|
||||
- a malicious compiler, kernel, CPU, firmware, or random-number generator;
|
||||
- an attacker with unrestricted side-channel observation of a shared machine;
|
||||
- an attacker allowed unbounded CPU, memory, or storage denial of service.
|
||||
|
||||
## Security properties
|
||||
|
||||
### Encrypted archive confidentiality
|
||||
|
||||
Password and native PQ modes encrypt blocks with AES-256-CTR and authenticate
|
||||
them with HMAC-SHA256. Confidentiality depends on unique nonces, correct
|
||||
implementations, OS randomness, and credential secrecy. In password mode it
|
||||
also depends on password entropy; PBKDF2-SHA256 slows but cannot prevent offline
|
||||
guessing of a weak password.
|
||||
|
||||
Prefer `--password-prompt`, `--pass-file`, or `--pass-fd`. A password supplied
|
||||
through `-p/--password` can be visible through process inspection or shell
|
||||
history. A password file is protected only by the caller's filesystem choices;
|
||||
ZUPT does not validate its ownership or permission bits. A descriptor is
|
||||
trusted input inherited from the caller. Both non-interactive forms read one
|
||||
line and reject empty, NUL-containing, or overlong values. The descriptor form
|
||||
duplicates but shares the underlying stream/offset and may buffer beyond the
|
||||
line, so callers should provide a descriptor dedicated to that password read.
|
||||
On POSIX, handled prompt interruptions restore the saved terminal state before
|
||||
termination; an exact-candidate PTY regression is required before release.
|
||||
On Windows, a prompt is entered only for a real console input handle;
|
||||
redirected input and console EOF fail instead of blocking in `_getch`.
|
||||
|
||||
Native private-key generation uses no-replace creation with POSIX mode `0600`
|
||||
or a Windows current-user-only DACL. A failed write, flush/fsync, or close leaves
|
||||
the incomplete or durability-uncertain exclusive file for manual review and
|
||||
removal instead of risking an unlink-after-close race against a replacement
|
||||
pathname. ZKEY and ZPQK inputs
|
||||
are accepted only after checksum, version, flags, reserved bytes, exact size,
|
||||
and public/private role validation. This prevents role confusion and
|
||||
partial/trailing-key acceptance; it does not protect a key after endpoint or
|
||||
account compromise.
|
||||
|
||||
When the optional system SDK is enabled, the in-repository adapter copies a key
|
||||
through the core atomic publisher, applies POSIX mode through the already-open
|
||||
temporary descriptor, and publishes only after copy/close checks succeed. Its
|
||||
`sdk-test` regression preserves existing symlink/hardlink targets and verifies
|
||||
private/public modes. This narrows the adapter boundary; it does not extend the
|
||||
baseline assessment to the external SDK implementation.
|
||||
|
||||
### Encrypted archive integrity
|
||||
|
||||
Current encrypted archives authenticate ciphertext, canonical block metadata,
|
||||
and each frame's logical position. DATA and DEDUP_REF frames both receive this
|
||||
positional AAD. A reference is authenticated at its own position and carries
|
||||
the authenticated source position needed to verify the referenced DATA frame,
|
||||
so exchanging otherwise equivalent frames is not accepted.
|
||||
|
||||
Current archives carry an archive-integrity trailer for global metadata. The
|
||||
`extract`, `list`, `test`, and `disk restore` paths refuse any no-AIT layout by
|
||||
default without relying on an unauthenticated header flag.
|
||||
`--allow-legacy-no-ait` is a narrowly scoped, warning-producing recovery option
|
||||
for those commands when the caller already trusts a pre-AIT archive. Selecting
|
||||
it for attacker-controlled storage removes the header/footer authentication
|
||||
assumption and is outside this threat model. `info` is an unauthenticated
|
||||
framing inspection that reports apparent AIT presence but validates neither the
|
||||
trailer nor archive contents. These checks do not prevent deletion of the
|
||||
entire archive, rollback to an older valid archive, or storage-layer replay.
|
||||
|
||||
Archive comments remain untrusted presentation data even when they are
|
||||
authenticated. Display paths render control bytes without emitting raw terminal
|
||||
control sequences, limiting terminal-output injection while leaving the stored
|
||||
and authenticated comment bytes unchanged.
|
||||
|
||||
New 5.2.2 encrypted+dedup archives authenticate each reference offset. New
|
||||
encrypted disk archives also authenticate an index that binds image size,
|
||||
block count, and a chained XXH64 hash of the complete restored stream. The
|
||||
writer's additional SHA-256/128 comparison is only an in-memory collision guard
|
||||
before deduplication; it is not an on-disk cryptographic hash. XXH64 is not
|
||||
cryptographic, so a writer who controls a plain archive can recompute it.
|
||||
|
||||
Plain archives use non-cryptographic checksums. A writer who controls a plain
|
||||
archive can recompute them.
|
||||
|
||||
### Native hybrid post-quantum mode
|
||||
|
||||
The `--pq` mode combines an ML-KEM-768 shared secret and an X25519 shared secret
|
||||
as implemented in 5.2.2:
|
||||
|
||||
```text
|
||||
hybrid_ikm = ml_ss XOR x25519_ss
|
||||
archive_key = SHA3-512(hybrid_ikm || ml_ct || ephemeral_pk ||
|
||||
"ZUPT-HYBRID-v1")
|
||||
```
|
||||
|
||||
Its goal is harvest-now/decrypt-later resistance if ML-KEM-768 remains secure,
|
||||
with X25519 as a classical hedge under the combiner assumptions. This is not
|
||||
session forward secrecy: compromise of the recipient's long-term private key
|
||||
can compromise previously captured archives encrypted to it.
|
||||
|
||||
The native `--pq-only` mode removes X25519 and derives a key from ML-KEM-768
|
||||
alone. Use it only when a policy specifically excludes the classical component;
|
||||
it loses the hybrid hedge.
|
||||
|
||||
The in-tree ML-KEM code has project tests, including known-answer vectors and a
|
||||
conditional OpenSSL 3.5 interoperability test. It has not been independently
|
||||
audited or formally verified as a whole implementation.
|
||||
|
||||
### Extraction containment
|
||||
|
||||
The reader rejects absolute paths, traversal components, control characters,
|
||||
ambiguous trailing dot/space components, NTFS alternate-stream syntax, and
|
||||
reserved Windows device names. POSIX extraction resolves every parent below a
|
||||
pinned destination descriptor with no-follow operations after canonicalizing
|
||||
the user-selected root once. Windows extraction
|
||||
uses handle-relative traversal, rejects reparse-point parents, and publishes the
|
||||
final name by handle without replacing an existing leaf. A checked path is not
|
||||
re-resolved through a mutable parent.
|
||||
|
||||
Decoded bytes are first written to a private, exclusively created temporary
|
||||
file. The final name is published only after the expected decoded size and
|
||||
chained checksum match and the stream closes successfully; failures remove the
|
||||
temporary through its descriptor or handle. These controls reduce traversal,
|
||||
link, race, and partial-output risks, but do not establish that no parser or
|
||||
filesystem bug can exist.
|
||||
|
||||
Benchmark scratch data lives in a random private directory. Cleanup resolves
|
||||
POSIX components without following links and deletes relative to pinned
|
||||
descriptors. On Windows it retains no-delete-sharing ancestor handles, refuses
|
||||
reparse-point recursion, then reopens each emptied directory relative to its
|
||||
pinned parent and verifies its filesystem identity before handle-based
|
||||
deletion. An attacker who inserts a link can cause cleanup failure, but the
|
||||
cleanup must not traverse to the link target.
|
||||
|
||||
The Windows handle-relative boundary in 5.2.8 covers normal local Win32 paths.
|
||||
Win32 extended-length and device-namespace paths, raw UNC output roots, and
|
||||
mapped/network-drive output are not supported. Cross-build and Wine results are
|
||||
not a substitute for the required native `windows-latest` Unicode package
|
||||
gate. Restore locally before moving verified output to network storage.
|
||||
|
||||
Disk restore copies the measured compacted archive into one exclusively
|
||||
created, auto-deleted scratch file. Preflight and restoration consume that same
|
||||
open snapshot. An explicit `ZUPT_TMPDIR` selects an existing scratch directory;
|
||||
failure there does not fall back to consuming the mutable source pathname. On
|
||||
POSIX, the destination is opened once without truncation or final-symlink
|
||||
following, classified with `fstat`, and the same raw-device descriptor is
|
||||
retained for supported Linux, macOS, and FreeBSD capacity checks and writes.
|
||||
Regular-file output retains atomic publication. A raw target is rejected before
|
||||
writing if its capacity is unknown or smaller than the image. These controls
|
||||
reduce source exchange, target exchange, and immediate overrun risk but do not
|
||||
protect against a compromised kernel/device, a wrongly selected sufficiently
|
||||
large device, power loss, or hardware failure.
|
||||
|
||||
The SDK publication, POSIX disk-target, and benchmark-cleanup changes address
|
||||
CodeQL High #5, #6, and #7 respectively. Their source review and regressions
|
||||
alone are project evidence, not independent certification. Exact-tag run
|
||||
`33456209269` subsequently passed all 15 hosted jobs at
|
||||
`ebb9ab3aa1d42c50030ca02883f6162dc4771fe1`; final release-commit CodeQL run
|
||||
`33456049125` completed successfully with zero open alerts.
|
||||
|
||||
For an untrusted archive:
|
||||
|
||||
1. use a new empty destination outside sensitive trees;
|
||||
2. run as a dedicated unprivileged user, never root;
|
||||
3. apply a container, sandbox, resource limits, and a storage quota when
|
||||
available;
|
||||
4. inspect extracted paths, types, permissions, and content before moving them;
|
||||
5. never restore a disk image to a device without independently confirming both
|
||||
source and destination.
|
||||
|
||||
## Non-goals and residual risks
|
||||
|
||||
ZUPT does not claim to provide:
|
||||
|
||||
- resistance to cache, power, EM, acoustic, speculative-execution, or all
|
||||
compiler-introduced timing side channels;
|
||||
- bounded resource consumption for every malformed archive;
|
||||
- confidentiality of archive size or complete framing metadata;
|
||||
- protection against compression-length oracles when secret and
|
||||
attacker-controlled data are compressed together;
|
||||
- rollback detection across multiple valid versions of a backup;
|
||||
- forward-secure sessions, remote authentication, replay protection, or secure
|
||||
transport;
|
||||
- automatic key rotation, recovery, escrow, threshold access, or secure
|
||||
deletion;
|
||||
- preservation of every operating-system ACL, ownership attribute, extended
|
||||
attribute, or special-file semantic;
|
||||
- safe operation on a compromised host.
|
||||
|
||||
## Credential handling
|
||||
|
||||
- Generate PQ keys on a trusted system using the OS CSPRNG.
|
||||
- Keep private keys separate from the archive and from release/package inputs.
|
||||
- Store an offline recovery copy and test recovery before relying on a backup.
|
||||
- Use a distinct high-entropy credential where compromise isolation matters.
|
||||
- Re-encrypt under a new credential after suspected disclosure; there is no
|
||||
in-place key rotation.
|
||||
- Never include credentials or sensitive archives in bug reports or CI logs.
|
||||
|
||||
## Supply-chain boundary
|
||||
|
||||
Git and upstream source archives are source-only. They must pass
|
||||
`scripts/check-source-only.sh` and must not contain executable code artifacts,
|
||||
objects, shared/static libraries, distribution packages, unsafe symlinks, or Git
|
||||
LFS pointers.
|
||||
|
||||
Nested inspection is itself an untrusted-input boundary. The release scanner
|
||||
must cap recursion depth, archive members, per-entry expansion, and total
|
||||
expanded bytes and fail closed when a cap is reached. Commit `ff99770` passed
|
||||
all 39 source-only scanner cases, including GNU thin archives, scanner-bomb
|
||||
limits, and safe diagnostic cases.
|
||||
|
||||
DEB, binary RPM, SRPM, notice-bearing Linux tar.xz, source-only portable GUI
|
||||
ZIP, Windows ZIP, and macOS DMG files can be published separately from the
|
||||
tagged source. Each artifact extends the trust boundary to its builder,
|
||||
toolchain, runner image, and packaging scripts. Treat it as validated only when
|
||||
the exact target has a recorded build, content/package inspection, extracted or
|
||||
installed smoke test, and applicable archive round trip. An AppImage is not
|
||||
promoted for 5.2.8; bare Linux and Windows executables are also excluded.
|
||||
|
||||
For 5.2.8, that gated artifact scope covers the CLI files plus the exact GUI
|
||||
DEB, noarch/source RPM, and source-only portable ZIP named in the README. The
|
||||
portable ZIP contains no compiled runtime and crosses the release boundary only
|
||||
after source scans and an exact safe-member check. AppDir and Flatpak bundles
|
||||
and GUI platform installers remain excluded; Windows ZIP and macOS DMG outputs
|
||||
remain CLI-only.
|
||||
|
||||
The immutable, non-promoted 5.2.2 candidate at `ff99770` passed the full local
|
||||
`make release-check`: packaging reported `PASS=49 FAIL=0 SKIP=0`; strict GCC,
|
||||
strict Clang, GCC `-fanalyzer`, the 9/9 tool-enabled static-analysis run,
|
||||
ASan/UBSan/LSan, and 1,000 mutation-fuzz iterations passed. Earlier off-screen
|
||||
GUI smoke evidence is retained separately. Post-tag CI integration failures
|
||||
prevented 5.2.2 promotion. This upstream self-review is not an independent
|
||||
certification and is not 5.2.8 evidence. The immutable 5.2.3 candidate was not
|
||||
promoted because its source-policy test assumed LF for a Windows `.bat` checkout
|
||||
that correctly used CRLF. The immutable v5.2.4 candidate was not promoted after
|
||||
exact-tag GitHub Actions run `33431386002`: 12 jobs succeeded, the sole openSUSE
|
||||
service-harness job failed because its standalone executor did not enter the
|
||||
service directory, and dependent Windows/macOS jobs were skipped. A local
|
||||
Tumbleweed reproduction established that the explicit `refs/tags/v5.2.4`
|
||||
revision works and that `os.chdir(service_dir)` completes the source-service
|
||||
chain. This narrows the failure to release/test integration; it changes no
|
||||
product, archive, cryptographic, codec, or SDK ABI boundary and supplies no
|
||||
automatic 5.2.8 evidence. The immutable v5.2.5 candidate was not promoted after
|
||||
exact-tag GitHub Actions run `33434986357`: 13 jobs succeeded, but native
|
||||
Windows and macOS failed on fixture-byte preservation and Darwin/Bash 3.2
|
||||
portability respectively. The corresponding 5.2.6 corrections were followed by
|
||||
exact-tag run `33442264243`: 13 jobs succeeded, while native macOS failed on
|
||||
x86-only SHA-NI helper declarations unused on arm64 under `-Werror`, and native
|
||||
Windows aborted during safe UTF-8 fixture argv transcoding. The v5.2.6 tag was
|
||||
not promoted. Version 5.2.7 corrected those two boundaries, but its exact-tag
|
||||
run `33445470664` concluded `cancelled` at `2026-08-31T23:11:19Z`, with 13
|
||||
successful jobs, one failed macOS job after raw-C1 filename creation returned
|
||||
`EILSEQ`, and one cancelled Windows job after the hosted job stalled in `make
|
||||
check`; a MinGW/Wine reproduction isolated the cause to a redirected password
|
||||
prompt entering `_getch`.
|
||||
The corresponding 5.2.8 fixture and prompt corrections alone did not establish
|
||||
a result. Exact-tag run `33456209269` then passed 15/15 jobs, including
|
||||
`sdk-test`, native Windows/macOS, the pinned local OBS source-service chain,
|
||||
and the package/source gates. Promotion run `33457868306` published the exact
|
||||
13 tested assets. Official authenticated OBS/Factory acceptance, the full
|
||||
automatic debug-package result, and untested architectures remain unclaimed;
|
||||
the earlier `debugsource` rpmlint `no-binary` finding remains unresolved and
|
||||
unsuppressed.
|
||||
|
||||
## Historical compatibility notes
|
||||
|
||||
These are historical facts about earlier releases, retained to support recovery:
|
||||
|
||||
- Releases through 4.1.0 could reuse an AES-CTR nonce in encrypted `--dedup`
|
||||
archives. Release 4.2.0 changed to fresh random per-block nonces. Re-encrypt
|
||||
affected older archives.
|
||||
- Releases through 4.2.1 used round-3 CRYSTALS-Kyber semantics in the native PQ
|
||||
path. Release 5.0.0 corrected the implementation to FIPS 203 ML-KEM-768,
|
||||
changing native PQ key/archive compatibility. See `CHANGELOG.md` before
|
||||
planning cross-version restoration.
|
||||
- Pre-AIT archive layouts now fail closed by default. The explicit
|
||||
`--allow-legacy-no-ait` read option is only for recovery from a known, trusted
|
||||
historical archive and leaves its header/footer metadata outside the current
|
||||
authenticated boundary.
|
||||
- The 5.2.2 reader retains compatibility parsers for the fixed-width disk index
|
||||
and encrypted-dedup linear AAD sequence published through 5.2.1. An actual
|
||||
v5.2.1 password-encrypted DATA/DATA/REF/DATA disk fixture is stored as hexadecimal
|
||||
text with source and hash provenance. The candidate lists, tests, extracts, and restores
|
||||
it byte-exact, with a warning that the legacy index has no whole-image hash;
|
||||
the full local Linux gate passed on commit `ff99770`. Older readers are not
|
||||
claimed to accept new flag-gated 5.2.2 records, and untested historical mode
|
||||
combinations remain unclaimed.
|
||||
|
||||
Historical test counts in the changelog describe those releases. They do not
|
||||
automatically become 5.2.8 results; current outcomes belong in the release
|
||||
validation record, with unavailable environments marked `SKIP`. In particular,
|
||||
runs made before the final positional-AAD and mandatory-AIT changes are not
|
||||
final release gates for the resulting candidate.
|
||||
|
||||
## Reporting security issues
|
||||
|
||||
Email `sac@securityops.co` with the subject `VaptVupt security report`.
|
||||
PGP key available on request.
|
||||
Email **zupt@riseup.net** with `[security]` in the subject. Include the version,
|
||||
platform, impact, and a minimal non-sensitive reproducer. Do not disclose the
|
||||
issue publicly until a coordinated timeline has been agreed.
|
||||
|
||||
We will:
|
||||
|
||||
- Acknowledge receipt within 7 days
|
||||
- Investigate and publish a CVE / advisory if warranted
|
||||
- Credit you in the CHANGELOG if you wish
|
||||
|
||||
Please don't open public issues for security reports until we've
|
||||
coordinated disclosure. For non-security bugs (parser edge cases,
|
||||
documentation typos, performance issues), open a public issue
|
||||
normally.
|
||||
|
||||
---
|
||||
|
||||
## Document version
|
||||
|
||||
This threat model covers archive format v1.6 as shipped in VaptVupt
|
||||
5.0.0. It is part of the source tree (`THREAT_MODEL.md`) and
|
||||
versioned with the project; this section will be updated as the
|
||||
format evolves.
|
||||
Document version: 5.2.8, 2026-08-31.
|
||||
|
|
|
|||
21
build.bat
21
build.bat
|
|
@ -1,21 +0,0 @@
|
|||
@echo off
|
||||
echo Zupt v0.4 Build Script for Windows
|
||||
where gcc >nul 2>nul
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
gcc -Wall -Wextra -O2 -std=c11 -Iinclude ^
|
||||
src\zupt_main.c src\zupt_format.c src\zupt_lz.c src\zupt_lzh.c src\zupt_xxh.c ^
|
||||
src\zupt_sha256.c src\zupt_aes256.c src\zupt_crypto.c src\zupt_predict.c ^
|
||||
-lm -o zupt.exe
|
||||
if %ERRORLEVEL% EQU 0 (echo [OK] zupt.exe) else (echo [FAIL])
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
where cl >nul 2>nul
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
cl /nologo /W4 /O2 /Iinclude /D_CRT_SECURE_NO_WARNINGS ^
|
||||
src\zupt_main.c src\zupt_format.c src\zupt_lz.c src\zupt_lzh.c src\zupt_xxh.c ^
|
||||
src\zupt_sha256.c src\zupt_aes256.c src\zupt_crypto.c src\zupt_predict.c ^
|
||||
/Fe:zupt.exe & del *.obj 2>nul
|
||||
exit /b 0
|
||||
)
|
||||
echo No C compiler found.
|
||||
exit /b 1
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
#compdef vaptvupt zupt
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
#
|
||||
# Install:
|
||||
# sudo install -m 644 completions/_zupt /usr/share/zsh/site-functions/_zupt
|
||||
# or for a single user (anywhere in $fpath):
|
||||
# cp completions/_zupt ~/.zsh/completion/_zupt
|
||||
# # then in ~/.zshrc:
|
||||
# # fpath=(~/.zsh/completion $fpath)
|
||||
# # autoload -U compinit && compinit
|
||||
|
||||
_zupt_levels() {
|
||||
_values 'compression level' \
|
||||
'1[fastest, smallest window]' \
|
||||
'2[fast]' \
|
||||
'3[balanced (low)]' \
|
||||
'4[balanced]' \
|
||||
'5[balanced (high)]' \
|
||||
'6[high compression]' \
|
||||
'7[default; high]' \
|
||||
'8[maximum, 1MB window]' \
|
||||
'9[maximum, deep search]'
|
||||
}
|
||||
|
||||
_zupt_kdf() {
|
||||
_values 'KDF' \
|
||||
'argon2id[memory-hard, default since v2.4.1]' \
|
||||
'pbkdf2[legacy 600k-iter PBKDF2-SHA256]'
|
||||
}
|
||||
|
||||
_zupt_threads() {
|
||||
_values 'threads' '0[auto]' '1' '2' '4' '8' '16' '32' '64'
|
||||
}
|
||||
|
||||
_zupt_compress_opts() {
|
||||
_arguments \
|
||||
'(-l --level)'{-l,--level}'[compression level]:level:_zupt_levels' \
|
||||
'(-b --block)'{-b,--block}'[block size in bytes]:size:' \
|
||||
'(-s --store)'{-s,--store}'[store without compression]' \
|
||||
'(-f --fast)'{-f,--fast}'[use fast LZ codec]' \
|
||||
'(--vv --vaptvupt)'{--vv,--vaptvupt}'[use VaptVupt codec]' \
|
||||
'--lzhp[use Zupt-LZHP codec (LZ77+Huffman, no SIMD)]' \
|
||||
'(-p --password)'{-p,--password}'[encrypt with password]:password:' \
|
||||
'--kdf[password KDF]:kdf:_zupt_kdf' \
|
||||
'(-c --comment)'{-c,--comment}'[embed archive comment]:text:' \
|
||||
'--comment-file[read comment from file]:file:_files' \
|
||||
'--pq[legacy PQ encryption]:pubkey:_files' \
|
||||
'--pq-sdk[PQ encryption via libzuptsdk]:pubkey:_files' \
|
||||
'(-D --dedup)'{-D,--dedup}'[block-level deduplication]' \
|
||||
'--solid[solid mode: single stream]' \
|
||||
'(-v --verbose)'{-v,--verbose}'[verbose output]' \
|
||||
'(-q --quiet)'{-q,--quiet}'[suppress non-error output]' \
|
||||
'(-t --threads)'{-t,--threads}'[thread count]:threads:_zupt_threads' \
|
||||
'*:files:_files'
|
||||
}
|
||||
|
||||
_zupt_extract_opts() {
|
||||
_arguments \
|
||||
'(-o --output)'{-o,--output}'[output directory]:directory:_directories' \
|
||||
'(-p --password)'{-p,--password}'[decryption password]:password:' \
|
||||
'--pq[legacy PQ decryption]:privkey:_files' \
|
||||
'--pq-sdk[PQ decryption via libzuptsdk]:privkey:_files' \
|
||||
'(-v --verbose)'{-v,--verbose}'[verbose output]' \
|
||||
'(-t --threads)'{-t,--threads}'[thread count]:threads:_zupt_threads' \
|
||||
'*:archive:_files -g "*.zupt"'
|
||||
}
|
||||
|
||||
_zupt() {
|
||||
local context curcontext="$curcontext" state line
|
||||
local -a subcommands
|
||||
|
||||
subcommands=(
|
||||
'compress:create an archive'
|
||||
'c:create an archive (alias)'
|
||||
'extract:extract an archive'
|
||||
'x:extract an archive (alias)'
|
||||
'list:list archive entries'
|
||||
'l:list archive entries (alias)'
|
||||
'test:verify archive integrity'
|
||||
't:verify archive integrity (alias)'
|
||||
'info:archive metadata (no key needed)'
|
||||
'bench:benchmark levels 1-9'
|
||||
'disk:full-disk backup/restore'
|
||||
'keygen:generate a key file'
|
||||
'version:print version info'
|
||||
'help:print help'
|
||||
)
|
||||
|
||||
_arguments -C \
|
||||
'(-): :->command' \
|
||||
'(-)*:: :->args'
|
||||
|
||||
case $state in
|
||||
command)
|
||||
_describe -t commands 'zupt subcommand' subcommands
|
||||
;;
|
||||
args)
|
||||
case $line[1] in
|
||||
compress|c)
|
||||
_zupt_compress_opts
|
||||
;;
|
||||
extract|x)
|
||||
_zupt_extract_opts
|
||||
;;
|
||||
list|l|test|t)
|
||||
_arguments \
|
||||
'(-p --password)'{-p,--password}'[password]:password:' \
|
||||
'--pq[legacy PQ privkey]:privkey:_files' \
|
||||
'--pq-sdk[PQ privkey]:privkey:_files' \
|
||||
'(-v --verbose)'{-v,--verbose}'[verbose]' \
|
||||
'*:archive:_files -g "*.zupt"'
|
||||
;;
|
||||
info)
|
||||
_arguments '*:archive:_files -g "*.zupt"'
|
||||
;;
|
||||
disk)
|
||||
_values 'disk action' 'backup' 'restore'
|
||||
;;
|
||||
keygen)
|
||||
_arguments \
|
||||
'--sdk[generate SDK v2 keypair]' \
|
||||
'--pq-sdk[same as --sdk]' \
|
||||
'-o[output keyfile]:file:_files' \
|
||||
'--pub[export public key from -k]' \
|
||||
'-k[source private key for --pub]:file:_files'
|
||||
;;
|
||||
bench)
|
||||
_arguments '*:files:_files'
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_zupt "$@"
|
||||
132
completions/_zupt
Normal file
132
completions/_zupt
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#compdef zupt
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
local context state state_descr line
|
||||
local -a _zupt_disk_legacy_options
|
||||
typeset -A opt_args
|
||||
|
||||
_zupt_password_options=(
|
||||
'(-p --password)'{-p,--password}'[password in process arguments]:password:'
|
||||
'--password-prompt[read password interactively without echo]'
|
||||
'--pass-file[read password from first line of file]:password file:_files'
|
||||
'--pass-fd[read password from inherited file descriptor]:file descriptor:'
|
||||
)
|
||||
|
||||
_zupt_pq_options=(
|
||||
'--pq[native ML-KEM-768 + X25519 hybrid key]:key file:_files'
|
||||
'--pq-only[native ML-KEM-768-only key]:key file:_files'
|
||||
'--pq-sdk[optional system libvuptsdk key]:key file:_files'
|
||||
'--pq-box[optional system libpqvaptvupt key]:key file:_files'
|
||||
)
|
||||
|
||||
_zupt_read_options=(
|
||||
"${_zupt_password_options[@]}"
|
||||
"${_zupt_pq_options[@]}"
|
||||
'(-v --verbose)'{-v,--verbose}'[additional diagnostics]'
|
||||
'--allow-legacy-no-ait[recover a trusted old archive without an integrity trailer]'
|
||||
)
|
||||
|
||||
_arguments -C \
|
||||
'1:command:->command' \
|
||||
'*::argument:->arguments'
|
||||
|
||||
case $state in
|
||||
command)
|
||||
_values 'command' \
|
||||
'compress:create an archive' 'c:create an archive' \
|
||||
'extract:extract an archive' 'x:extract an archive' \
|
||||
'list:list archive entries' 'l:list archive entries' \
|
||||
'test:verify archive integrity' 't:verify archive integrity' \
|
||||
'info:show framing metadata' 'i:show framing metadata' \
|
||||
'bench:benchmark compression levels' 'b:benchmark compression levels' \
|
||||
'disk:back up or restore a disk image' \
|
||||
'keygen:generate or export a recipient key' \
|
||||
'version:show version and build information' \
|
||||
'help:show command help' '--version:show version and build information' \
|
||||
'-V:show version and build information' '--help:show command help' \
|
||||
'-h:show command help'
|
||||
;;
|
||||
arguments)
|
||||
case ${line[1]} in
|
||||
compress|c)
|
||||
_arguments \
|
||||
'(-l --level)'{-l,--level}'[compression level]:level:(1 2 3 4 5 6 7 8 9)' \
|
||||
'(-b --block)'{-b,--block}'[block size in bytes]:bytes:' \
|
||||
'(-s --store)'{-s,--store}'[store without compression]' \
|
||||
'(-f --fast)'{-f,--fast}'[use fast LZ codec]' \
|
||||
'(--vv --vaptvupt)'{--vv,--vaptvupt}'[force VaptVupt LZ + ANS codec]' \
|
||||
'--lzhp[force portable LZHP codec]' \
|
||||
"${_zupt_password_options[@]}" \
|
||||
'--kdf[password KDF]:KDF:(pbkdf2 argon2id)' \
|
||||
'(-c --comment)'{-c,--comment}'[archive comment]:comment:' \
|
||||
'--comment-file[read archive comment from file]:comment file:_files' \
|
||||
"${_zupt_pq_options[@]}" \
|
||||
'(-D --dedup)'{-D,--dedup}'[block-level deduplication]' \
|
||||
'(-S --solid)'{-S,--solid}'[solid single-stream compression]' \
|
||||
'(-y --force)'{-y,--force}'[overwrite an existing non-.zupt output]' \
|
||||
'(-v --verbose)'{-v,--verbose}'[additional progress output]' \
|
||||
'(-t --threads)'{-t,--threads}'[compression thread count]:threads:' \
|
||||
'1:output archive:_files -g "*.zupt"' \
|
||||
'*:input file or directory:_files'
|
||||
;;
|
||||
extract|x)
|
||||
_arguments \
|
||||
'(-o --output)'{-o,--output}'[output directory]:directory:_directories' \
|
||||
"${_zupt_read_options[@]}" \
|
||||
'(-t --threads)'{-t,--threads}'[decompression thread count]:threads:' \
|
||||
'1:archive:_files -g "*.zupt"'
|
||||
;;
|
||||
list|l|test|t)
|
||||
_arguments \
|
||||
"${_zupt_read_options[@]}" \
|
||||
'1:archive:_files -g "*.zupt"'
|
||||
;;
|
||||
info|i)
|
||||
_arguments '1:archive:_files -g "*.zupt"'
|
||||
;;
|
||||
bench|b)
|
||||
_arguments '--compare[compare available external compressors]' \
|
||||
'*:input file or directory:_files'
|
||||
;;
|
||||
disk)
|
||||
_zupt_disk_legacy_options=()
|
||||
if [[ ${line[2]-} == restore ]]; then
|
||||
_zupt_disk_legacy_options=(
|
||||
'--allow-legacy-no-ait[recover a trusted old disk archive without an integrity trailer]'
|
||||
)
|
||||
fi
|
||||
_arguments -C \
|
||||
'1:disk command:(backup restore)' \
|
||||
'(-l --level)'{-l,--level}'[compression level]:level:(1 2 3 4 5 6 7 8 9)' \
|
||||
'(-b --block)'{-b,--block}'[block size in bytes]:bytes:' \
|
||||
'(-s --store)'{-s,--store}'[store without compression]' \
|
||||
'(--vv --vaptvupt)'{--vv,--vaptvupt}'[force VaptVupt LZ + ANS codec]' \
|
||||
'--lzhp[force portable LZHP codec]' \
|
||||
"${_zupt_password_options[@]}" \
|
||||
'--kdf[password KDF]:KDF:(pbkdf2 argon2id)' \
|
||||
'(-c --comment)'{-c,--comment}'[archive comment]:comment:' \
|
||||
'--comment-file[read archive comment from file]:comment file:_files' \
|
||||
'--pq[native hybrid key]:key file:_files' \
|
||||
'--pq-only[native ML-KEM-768-only key]:key file:_files' \
|
||||
'(-D --dedup)'{-D,--dedup}'[block-level deduplication]' \
|
||||
'(-v --verbose)'{-v,--verbose}'[additional progress output]' \
|
||||
'(-t --threads)'{-t,--threads}'[thread count]:threads:' \
|
||||
"${_zupt_disk_legacy_options[@]}" \
|
||||
'2:archive:_files' \
|
||||
'3:device or file:_files'
|
||||
;;
|
||||
keygen)
|
||||
_arguments \
|
||||
'(-o --output)'{-o,--output}'[output key file]:output file:_files' \
|
||||
'--pub[export a public key]' \
|
||||
'(-k --key)'{-k,--key}'[source private key]:private key:_files' \
|
||||
'(--pq-only --pqonly)'{--pq-only,--pqonly}'[native ML-KEM-768-only key format]' \
|
||||
'(--sdk --pq-sdk)'{--sdk,--pq-sdk}'[optional system libvuptsdk key format]' \
|
||||
'(--box --pq-box)'{--box,--pq-box}'[optional system libpqvaptvupt key format]'
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
unset _zupt_password_options _zupt_pq_options _zupt_read_options
|
||||
unset _zupt_disk_legacy_options
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
# bash completion for vaptvupt (with `zupt` legacy alias)
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
#
|
||||
# Install (system-wide):
|
||||
# sudo install -m 644 completions/vaptvupt.bash /usr/share/bash-completion/completions/vaptvupt
|
||||
# sudo ln -sf vaptvupt /usr/share/bash-completion/completions/zupt
|
||||
# or for a single user:
|
||||
# cp completions/vaptvupt.bash ~/.local/share/bash-completion/completions/vaptvupt
|
||||
#
|
||||
# Reload your shell or `source` the file to pick up changes.
|
||||
|
||||
_vaptvupt() {
|
||||
local cur prev words cword
|
||||
_init_completion -n = 2>/dev/null || {
|
||||
# _init_completion missing on this host; fall back to manual setup.
|
||||
local IFS=$' \t\n'
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
cword=$COMP_CWORD
|
||||
words=("${COMP_WORDS[@]}")
|
||||
}
|
||||
|
||||
local subcommands="compress c extract x list l test t info bench disk keygen version help"
|
||||
local global_opts="-v --verbose -q --quiet -t --threads -h --help"
|
||||
|
||||
# First positional → subcommand
|
||||
if [ "$cword" -eq 1 ]; then
|
||||
COMPREPLY=( $(compgen -W "$subcommands" -- "$cur") )
|
||||
return 0
|
||||
fi
|
||||
|
||||
local subcmd="${words[1]}"
|
||||
|
||||
case "$prev" in
|
||||
-p|--password)
|
||||
# Don't complete passwords from filesystem
|
||||
COMPREPLY=()
|
||||
return 0
|
||||
;;
|
||||
-l|--level)
|
||||
COMPREPLY=( $(compgen -W "1 2 3 4 5 6 7 8 9" -- "$cur") )
|
||||
return 0
|
||||
;;
|
||||
--kdf)
|
||||
COMPREPLY=( $(compgen -W "argon2id pbkdf2" -- "$cur") )
|
||||
return 0
|
||||
;;
|
||||
-t|--threads)
|
||||
COMPREPLY=( $(compgen -W "0 1 2 4 8 16 32" -- "$cur") )
|
||||
return 0
|
||||
;;
|
||||
-b|--block)
|
||||
COMPREPLY=( $(compgen -W "65536 131072 262144 524288 1048576" -- "$cur") )
|
||||
return 0
|
||||
;;
|
||||
-o|--output)
|
||||
_filedir -d
|
||||
return 0
|
||||
;;
|
||||
--pq|--pq-sdk)
|
||||
# Key files (no extension constraint)
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
--comment-file)
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
-c|--comment)
|
||||
# Free-form text; no useful completion
|
||||
COMPREPLY=()
|
||||
return 0
|
||||
;;
|
||||
-k)
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$subcmd" in
|
||||
compress|c)
|
||||
if [[ "$cur" == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W "
|
||||
-l --level -b --block -s --store -f --fast
|
||||
--vv --vaptvupt --lzhp
|
||||
-p --password --kdf
|
||||
-c --comment --comment-file
|
||||
--pq --pq-sdk
|
||||
--dedup -D --solid
|
||||
-v --verbose -q --quiet -t --threads
|
||||
$global_opts
|
||||
" -- "$cur") )
|
||||
else
|
||||
_filedir
|
||||
fi
|
||||
;;
|
||||
extract|x)
|
||||
if [[ "$cur" == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W "
|
||||
-o --output -p --password
|
||||
--pq --pq-sdk
|
||||
-v --verbose -t --threads
|
||||
$global_opts
|
||||
" -- "$cur") )
|
||||
else
|
||||
_filedir 'zupt'
|
||||
fi
|
||||
;;
|
||||
list|l|test|t)
|
||||
if [[ "$cur" == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W "
|
||||
-p --password --pq --pq-sdk
|
||||
-v --verbose
|
||||
$global_opts
|
||||
" -- "$cur") )
|
||||
else
|
||||
_filedir 'zupt'
|
||||
fi
|
||||
;;
|
||||
info)
|
||||
_filedir 'zupt'
|
||||
;;
|
||||
disk)
|
||||
if [ "$cword" -eq 2 ]; then
|
||||
COMPREPLY=( $(compgen -W "backup restore" -- "$cur") )
|
||||
elif [[ "$cur" == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W "
|
||||
-p --password --pq --pq-sdk
|
||||
--kdf -c --comment --comment-file
|
||||
-v --verbose
|
||||
" -- "$cur") )
|
||||
else
|
||||
_filedir
|
||||
fi
|
||||
;;
|
||||
keygen)
|
||||
if [[ "$cur" == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W "--sdk --pq-sdk -o --pub -k" -- "$cur") )
|
||||
else
|
||||
_filedir
|
||||
fi
|
||||
;;
|
||||
bench)
|
||||
_filedir
|
||||
;;
|
||||
version|help)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
complete -F _vaptvupt vaptvupt
|
||||
# v3.0.0: legacy `zupt` name retained as an alias.
|
||||
complete -F _vaptvupt zupt
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
# Fish completions for zupt
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
#
|
||||
# Install:
|
||||
# sudo install -m 644 completions/zupt.fish /usr/share/fish/vendor_completions.d/
|
||||
# or for a single user:
|
||||
# cp completions/zupt.fish ~/.config/fish/completions/
|
||||
|
||||
# ─── Subcommands ───
|
||||
complete -c vaptvupt -f -n '__fish_use_subcommand' -a 'compress c' -d 'Create an archive'
|
||||
complete -c zupt -f -n '__fish_use_subcommand' -a 'compress c' -d 'Create an archive'
|
||||
complete -c vaptvupt -f -n '__fish_use_subcommand' -a 'extract x' -d 'Extract an archive'
|
||||
complete -c zupt -f -n '__fish_use_subcommand' -a 'extract x' -d 'Extract an archive'
|
||||
complete -c vaptvupt -f -n '__fish_use_subcommand' -a 'list l' -d 'List archive entries'
|
||||
complete -c zupt -f -n '__fish_use_subcommand' -a 'list l' -d 'List archive entries'
|
||||
complete -c vaptvupt -f -n '__fish_use_subcommand' -a 'test t' -d 'Verify archive integrity'
|
||||
complete -c zupt -f -n '__fish_use_subcommand' -a 'test t' -d 'Verify archive integrity'
|
||||
complete -c vaptvupt -f -n '__fish_use_subcommand' -a 'info' -d 'Archive metadata (no key needed)'
|
||||
complete -c zupt -f -n '__fish_use_subcommand' -a 'info' -d 'Archive metadata (no key needed)'
|
||||
complete -c vaptvupt -f -n '__fish_use_subcommand' -a 'bench' -d 'Benchmark compression levels'
|
||||
complete -c zupt -f -n '__fish_use_subcommand' -a 'bench' -d 'Benchmark compression levels'
|
||||
complete -c vaptvupt -f -n '__fish_use_subcommand' -a 'disk' -d 'Full-disk backup/restore'
|
||||
complete -c zupt -f -n '__fish_use_subcommand' -a 'disk' -d 'Full-disk backup/restore'
|
||||
complete -c vaptvupt -f -n '__fish_use_subcommand' -a 'keygen' -d 'Generate a key file'
|
||||
complete -c zupt -f -n '__fish_use_subcommand' -a 'keygen' -d 'Generate a key file'
|
||||
complete -c vaptvupt -f -n '__fish_use_subcommand' -a 'version' -d 'Print version info'
|
||||
complete -c zupt -f -n '__fish_use_subcommand' -a 'version' -d 'Print version info'
|
||||
complete -c vaptvupt -f -n '__fish_use_subcommand' -a 'help' -d 'Print help'
|
||||
complete -c zupt -f -n '__fish_use_subcommand' -a 'help' -d 'Print help'
|
||||
|
||||
# Helper predicates
|
||||
function __fish_zupt_using_subcommand
|
||||
set -l cmd (commandline -opc)
|
||||
if test (count $cmd) -gt 1
|
||||
contains -- $cmd[2] $argv
|
||||
return $status
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
# ─── Compress options ───
|
||||
set -l compress_cmds compress c
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -s l -l level -d 'Compression level (1-9, default 7)' -x -a '1 2 3 4 5 6 7 8 9'
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -s l -l level -d 'Compression level (1-9, default 7)' -x -a '1 2 3 4 5 6 7 8 9'
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -s b -l block -d 'Block size in bytes' -x
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -s b -l block -d 'Block size in bytes' -x
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -s s -l store -d 'Store without compression'
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -s s -l store -d 'Store without compression'
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -s f -l fast -d 'Use fast LZ codec'
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -s f -l fast -d 'Use fast LZ codec'
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -l vv -l vaptvupt -d 'Use VaptVupt codec'
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -l vv -l vaptvupt -d 'Use VaptVupt codec'
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -l lzhp -d 'Use Zupt-LZHP codec (no SIMD needed)'
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -l lzhp -d 'Use Zupt-LZHP codec (no SIMD needed)'
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -s p -l password -d 'Encrypt with password (prompted if empty)' -x
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -s p -l password -d 'Encrypt with password (prompted if empty)' -x
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -l kdf -d 'Password KDF' -x -a 'argon2id pbkdf2'
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -l kdf -d 'Password KDF' -x -a 'argon2id pbkdf2'
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -s c -l comment -d 'Embed archive comment (UTF-8, ≤4096 B)' -x
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -s c -l comment -d 'Embed archive comment (UTF-8, ≤4096 B)' -x
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -l comment-file -d 'Read comment from file' -r
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -l comment-file -d 'Read comment from file' -r
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -l pq -d 'Legacy PQ public key' -r
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -l pq -d 'Legacy PQ public key' -r
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -l pq-sdk -d 'PQ public key (libzuptsdk)' -r
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -l pq-sdk -d 'PQ public key (libzuptsdk)' -r
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -s D -l dedup -d 'Block-level deduplication'
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -s D -l dedup -d 'Block-level deduplication'
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -l solid -d 'Solid mode (single stream)'
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -l solid -d 'Solid mode (single stream)'
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -s v -l verbose -d 'Verbose output'
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -s v -l verbose -d 'Verbose output'
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -s q -l quiet -d 'Suppress non-error output'
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -s q -l quiet -d 'Suppress non-error output'
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $compress_cmds" -s t -l threads -d 'Thread count (0=auto)' -x
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $compress_cmds" -s t -l threads -d 'Thread count (0=auto)' -x
|
||||
|
||||
# ─── Extract / List / Test options ───
|
||||
set -l rw_cmds extract x list l test t
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $rw_cmds" -s o -l output -d 'Output directory' -x -a '(__fish_complete_directories)'
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $rw_cmds" -s o -l output -d 'Output directory' -x -a '(__fish_complete_directories)'
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $rw_cmds" -s p -l password -d 'Decryption password' -x
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $rw_cmds" -s p -l password -d 'Decryption password' -x
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $rw_cmds" -l pq -d 'Legacy PQ private key' -r
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $rw_cmds" -l pq -d 'Legacy PQ private key' -r
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $rw_cmds" -l pq-sdk -d 'PQ private key (libzuptsdk)' -r
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $rw_cmds" -l pq-sdk -d 'PQ private key (libzuptsdk)' -r
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $rw_cmds" -s v -l verbose -d 'Verbose output (surfaces top-MAC/SDK details on failure)'
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $rw_cmds" -s v -l verbose -d 'Verbose output (surfaces top-MAC/SDK details on failure)'
|
||||
complete -c vaptvupt -n "__fish_zupt_using_subcommand $rw_cmds" -s t -l threads -d 'Thread count' -x
|
||||
complete -c zupt -n "__fish_zupt_using_subcommand $rw_cmds" -s t -l threads -d 'Thread count' -x
|
||||
|
||||
# ─── Disk subcommand ───
|
||||
complete -c vaptvupt -f -n "__fish_zupt_using_subcommand disk; and not __fish_seen_subcommand_from backup restore" \
|
||||
complete -c zupt -f -n "__fish_zupt_using_subcommand disk; and not __fish_seen_subcommand_from backup restore" \
|
||||
-a 'backup' -d 'Read a block device into an archive'
|
||||
complete -c vaptvupt -f -n "__fish_zupt_using_subcommand disk; and not __fish_seen_subcommand_from backup restore" \
|
||||
complete -c zupt -f -n "__fish_zupt_using_subcommand disk; and not __fish_seen_subcommand_from backup restore" \
|
||||
-a 'restore' -d 'Write an archive to a block device'
|
||||
|
||||
# ─── Keygen options ───
|
||||
complete -c vaptvupt -n '__fish_zupt_using_subcommand keygen' -l sdk -d 'Generate SDK v2 keypair'
|
||||
complete -c zupt -n '__fish_zupt_using_subcommand keygen' -l sdk -d 'Generate SDK v2 keypair'
|
||||
complete -c vaptvupt -n '__fish_zupt_using_subcommand keygen' -l pq-sdk -d 'Same as --sdk'
|
||||
complete -c zupt -n '__fish_zupt_using_subcommand keygen' -l pq-sdk -d 'Same as --sdk'
|
||||
complete -c vaptvupt -n '__fish_zupt_using_subcommand keygen' -s o -d 'Output keyfile path' -r
|
||||
complete -c zupt -n '__fish_zupt_using_subcommand keygen' -s o -d 'Output keyfile path' -r
|
||||
complete -c vaptvupt -n '__fish_zupt_using_subcommand keygen' -l pub -d 'Export public key from -k'
|
||||
complete -c zupt -n '__fish_zupt_using_subcommand keygen' -l pub -d 'Export public key from -k'
|
||||
complete -c vaptvupt -n '__fish_zupt_using_subcommand keygen' -s k -d 'Source private keyfile' -r
|
||||
complete -c zupt -n '__fish_zupt_using_subcommand keygen' -s k -d 'Source private keyfile' -r
|
||||
112
completions/zupt.bash
Normal file
112
completions/zupt.bash
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# bash completion for ZUPT
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
_zupt()
|
||||
{
|
||||
local cur prev command disk_command
|
||||
COMPREPLY=()
|
||||
cur=${COMP_WORDS[COMP_CWORD]}
|
||||
prev=${COMP_WORDS[COMP_CWORD-1]}
|
||||
command=${COMP_WORDS[1]-}
|
||||
disk_command=${COMP_WORDS[2]-}
|
||||
|
||||
case $prev in
|
||||
-l|--level)
|
||||
COMPREPLY=( $(compgen -W '1 2 3 4 5 6 7 8 9' -- "$cur") )
|
||||
return
|
||||
;;
|
||||
-b|--block|-t|--threads|--pass-fd|-p|--password|-c|--comment)
|
||||
return
|
||||
;;
|
||||
--kdf)
|
||||
COMPREPLY=( $(compgen -W 'pbkdf2 argon2id' -- "$cur") )
|
||||
return
|
||||
;;
|
||||
-o|--output|-k|--key|--pass-file|--comment-file|--pq|--pq-only|--pq-sdk|--pq-box)
|
||||
COMPREPLY=( $(compgen -f -- "$cur") )
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
if (( COMP_CWORD == 1 )); then
|
||||
COMPREPLY=( $(compgen -W \
|
||||
'compress c extract x list l test t info i bench b disk keygen version help --version -V --help -h' \
|
||||
-- "$cur") )
|
||||
return
|
||||
fi
|
||||
|
||||
local password_options='-p --password --password-prompt --pass-file --pass-fd'
|
||||
local pq_options='--pq --pq-only --pq-sdk --pq-box'
|
||||
local legacy_read_option='--allow-legacy-no-ait'
|
||||
local common_read_options="-v --verbose $password_options $pq_options $legacy_read_option"
|
||||
|
||||
case $command in
|
||||
compress|c)
|
||||
if [[ $cur == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W \
|
||||
"-l --level -b --block -s --store -f --fast --vv --vaptvupt --lzhp
|
||||
$password_options --kdf -c --comment --comment-file $pq_options
|
||||
-D --dedup -S --solid -y --force -v --verbose -t --threads" \
|
||||
-- "$cur") )
|
||||
else
|
||||
COMPREPLY=( $(compgen -f -- "$cur") )
|
||||
fi
|
||||
;;
|
||||
extract|x)
|
||||
if [[ $cur == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W \
|
||||
"-o --output $common_read_options -t --threads" -- "$cur") )
|
||||
else
|
||||
COMPREPLY=( $(compgen -f -X '!*.zupt' -- "$cur") )
|
||||
fi
|
||||
;;
|
||||
list|l|test|t)
|
||||
if [[ $cur == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W "$common_read_options" -- "$cur") )
|
||||
else
|
||||
COMPREPLY=( $(compgen -f -X '!*.zupt' -- "$cur") )
|
||||
fi
|
||||
;;
|
||||
info|i)
|
||||
COMPREPLY=( $(compgen -f -X '!*.zupt' -- "$cur") )
|
||||
;;
|
||||
bench|b)
|
||||
if [[ $cur == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W '--compare' -- "$cur") )
|
||||
else
|
||||
COMPREPLY=( $(compgen -f -- "$cur") )
|
||||
fi
|
||||
;;
|
||||
disk)
|
||||
if (( COMP_CWORD == 2 )); then
|
||||
COMPREPLY=( $(compgen -W 'backup restore' -- "$cur") )
|
||||
elif [[ $cur == -* ]]; then
|
||||
local disk_options
|
||||
disk_options="-l --level -b --block -s --store --vv --vaptvupt --lzhp
|
||||
$password_options --kdf -c --comment --comment-file
|
||||
--pq --pq-only -D --dedup -v --verbose -t --threads"
|
||||
if [[ $disk_command == restore ]]; then
|
||||
disk_options+=" $legacy_read_option"
|
||||
fi
|
||||
COMPREPLY=( $(compgen -W \
|
||||
"$disk_options" \
|
||||
-- "$cur") )
|
||||
elif [[ $disk_command == restore ]]; then
|
||||
COMPREPLY=( $(compgen -f -X '!*.zupt' -- "$cur") )
|
||||
else
|
||||
COMPREPLY=( $(compgen -f -- "$cur") )
|
||||
fi
|
||||
;;
|
||||
keygen)
|
||||
if [[ $cur == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W \
|
||||
'-o --output --pub -k --key --pq-only --pqonly --sdk --pq-sdk --box --pq-box' \
|
||||
-- "$cur") )
|
||||
else
|
||||
COMPREPLY=( $(compgen -f -- "$cur") )
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
complete -F _zupt zupt
|
||||
163
completions/zupt.fish
Normal file
163
completions/zupt.fish
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
# fish completion for ZUPT
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
function __fish_zupt_needs_command
|
||||
set -l tokens (commandline -opc)
|
||||
test (count $tokens) -eq 1
|
||||
end
|
||||
|
||||
function __fish_zupt_using_command
|
||||
set -l tokens (commandline -opc)
|
||||
test (count $tokens) -gt 1; and contains -- $tokens[2] $argv
|
||||
end
|
||||
|
||||
function __fish_zupt_disk_needs_command
|
||||
set -l tokens (commandline -opc)
|
||||
test (count $tokens) -eq 2; and test "$tokens[2]" = disk
|
||||
end
|
||||
|
||||
function __fish_zupt_disk_using_command
|
||||
set -l tokens (commandline -opc)
|
||||
test (count $tokens) -gt 2; and test "$tokens[2]" = disk; and contains -- $tokens[3] $argv
|
||||
end
|
||||
|
||||
complete -c zupt -f -n __fish_zupt_needs_command \
|
||||
-a 'compress c' -d 'Create an archive'
|
||||
complete -c zupt -f -n __fish_zupt_needs_command \
|
||||
-a 'extract x' -d 'Extract an archive'
|
||||
complete -c zupt -f -n __fish_zupt_needs_command \
|
||||
-a 'list l' -d 'List archive entries'
|
||||
complete -c zupt -f -n __fish_zupt_needs_command \
|
||||
-a 'test t' -d 'Verify archive integrity'
|
||||
complete -c zupt -f -n __fish_zupt_needs_command \
|
||||
-a 'info i' -d 'Show archive framing metadata'
|
||||
complete -c zupt -f -n __fish_zupt_needs_command \
|
||||
-a 'bench b' -d 'Benchmark compression levels'
|
||||
complete -c zupt -f -n __fish_zupt_needs_command \
|
||||
-a disk -d 'Back up or restore a disk image'
|
||||
complete -c zupt -f -n __fish_zupt_needs_command \
|
||||
-a keygen -d 'Generate or export a recipient key'
|
||||
complete -c zupt -f -n __fish_zupt_needs_command \
|
||||
-a version -d 'Show version and build information'
|
||||
complete -c zupt -f -n __fish_zupt_needs_command \
|
||||
-a help -d 'Show command help'
|
||||
complete -c zupt -f -n __fish_zupt_needs_command \
|
||||
-a '--version -V' -d 'Show version and build information'
|
||||
complete -c zupt -f -n __fish_zupt_needs_command \
|
||||
-a '--help -h' -d 'Show command help'
|
||||
|
||||
set -l compress_condition '__fish_zupt_using_command compress c'
|
||||
complete -c zupt -n "$compress_condition" -s l -l level \
|
||||
-d 'Compression level' -x -a '1 2 3 4 5 6 7 8 9'
|
||||
complete -c zupt -n "$compress_condition" -s b -l block \
|
||||
-d 'Block size in bytes' -x
|
||||
complete -c zupt -n "$compress_condition" -s s -l store \
|
||||
-d 'Store without compression'
|
||||
complete -c zupt -n "$compress_condition" -s f -l fast \
|
||||
-d 'Use fast LZ codec'
|
||||
complete -c zupt -n "$compress_condition" -l vv -l vaptvupt \
|
||||
-d 'Force VaptVupt LZ + ANS codec'
|
||||
complete -c zupt -n "$compress_condition" -l lzhp \
|
||||
-d 'Force portable LZHP codec'
|
||||
complete -c zupt -n "$compress_condition" -l kdf \
|
||||
-d 'Password KDF' -x -a 'pbkdf2 argon2id'
|
||||
complete -c zupt -n "$compress_condition" -s c -l comment \
|
||||
-d 'Store an archive comment' -x
|
||||
complete -c zupt -n "$compress_condition" -l comment-file \
|
||||
-d 'Read archive comment from file' -r
|
||||
complete -c zupt -n "$compress_condition" -s D -l dedup \
|
||||
-d 'Enable block-level deduplication'
|
||||
complete -c zupt -n "$compress_condition" -s S -l solid \
|
||||
-d 'Use a solid single compression stream'
|
||||
complete -c zupt -n "$compress_condition" -s y -l force \
|
||||
-d 'Overwrite an existing non-.zupt output'
|
||||
complete -c zupt -n "$compress_condition" -s t -l threads \
|
||||
-d 'Compression thread count' -x
|
||||
|
||||
set -l read_condition '__fish_zupt_using_command compress c extract x list l test t'
|
||||
complete -c zupt -n "$read_condition" -s p -l password \
|
||||
-d 'Password in process arguments' -x
|
||||
complete -c zupt -n "$read_condition" -l password-prompt \
|
||||
-d 'Read password interactively without echo'
|
||||
complete -c zupt -n "$read_condition" -l pass-file \
|
||||
-d 'Read password from first line of file' -r
|
||||
complete -c zupt -n "$read_condition" -l pass-fd \
|
||||
-d 'Read password from inherited file descriptor' -x
|
||||
complete -c zupt -n "$read_condition" -l pq \
|
||||
-d 'Native ML-KEM-768 + X25519 hybrid key' -r
|
||||
complete -c zupt -n "$read_condition" -l pq-only \
|
||||
-d 'Native ML-KEM-768-only key' -r
|
||||
complete -c zupt -n "$read_condition" -l pq-sdk \
|
||||
-d 'Optional system libvuptsdk key' -r
|
||||
complete -c zupt -n "$read_condition" -l pq-box \
|
||||
-d 'Optional system libpqvaptvupt key' -r
|
||||
complete -c zupt -n "$read_condition" -s v -l verbose \
|
||||
-d 'Additional progress or diagnostic output'
|
||||
|
||||
set -l legacy_read_condition '__fish_zupt_using_command extract x list l test t'
|
||||
complete -c zupt -n "$legacy_read_condition" -l allow-legacy-no-ait \
|
||||
-d 'Recover a trusted old archive without an integrity trailer'
|
||||
|
||||
complete -c zupt -n '__fish_zupt_using_command extract x' \
|
||||
-s o -l output -d 'Output directory' -r
|
||||
complete -c zupt -n '__fish_zupt_using_command extract x' \
|
||||
-s t -l threads -d 'Decompression thread count' -x
|
||||
complete -c zupt -n '__fish_zupt_using_command bench b' \
|
||||
-l compare -d 'Compare available external compressors'
|
||||
|
||||
complete -c zupt -f -n __fish_zupt_disk_needs_command \
|
||||
-a backup -d 'Create a disk-image archive'
|
||||
complete -c zupt -f -n __fish_zupt_disk_needs_command \
|
||||
-a restore -d 'Restore a disk-image archive'
|
||||
set -l disk_condition '__fish_zupt_using_command disk'
|
||||
complete -c zupt -n "$disk_condition" -s l -l level \
|
||||
-d 'Compression level' -x -a '1 2 3 4 5 6 7 8 9'
|
||||
complete -c zupt -n "$disk_condition" -s b -l block \
|
||||
-d 'Block size in bytes' -x
|
||||
complete -c zupt -n "$disk_condition" -s s -l store \
|
||||
-d 'Store without compression'
|
||||
complete -c zupt -n "$disk_condition" -l vv -l vaptvupt \
|
||||
-d 'Force VaptVupt LZ + ANS codec'
|
||||
complete -c zupt -n "$disk_condition" -l lzhp \
|
||||
-d 'Force portable LZHP codec'
|
||||
complete -c zupt -n "$disk_condition" -s p -l password \
|
||||
-d 'Password in process arguments' -x
|
||||
complete -c zupt -n "$disk_condition" -l password-prompt \
|
||||
-d 'Read password interactively without echo'
|
||||
complete -c zupt -n "$disk_condition" -l pass-file \
|
||||
-d 'Read password from first line of file' -r
|
||||
complete -c zupt -n "$disk_condition" -l pass-fd \
|
||||
-d 'Read password from inherited file descriptor' -x
|
||||
complete -c zupt -n "$disk_condition" -l kdf \
|
||||
-d 'Password KDF' -x -a 'pbkdf2 argon2id'
|
||||
complete -c zupt -n "$disk_condition" -s c -l comment \
|
||||
-d 'Store an archive comment' -x
|
||||
complete -c zupt -n "$disk_condition" -l comment-file \
|
||||
-d 'Read archive comment from file' -r
|
||||
complete -c zupt -n "$disk_condition" -l pq \
|
||||
-d 'Native ML-KEM-768 + X25519 hybrid key' -r
|
||||
complete -c zupt -n "$disk_condition" -l pq-only \
|
||||
-d 'Native ML-KEM-768-only key' -r
|
||||
complete -c zupt -n "$disk_condition" -s D -l dedup \
|
||||
-d 'Enable block-level deduplication'
|
||||
complete -c zupt -n "$disk_condition" -s v -l verbose \
|
||||
-d 'Additional progress or diagnostic output'
|
||||
complete -c zupt -n "$disk_condition" -s t -l threads \
|
||||
-d 'Thread count' -x
|
||||
complete -c zupt -n '__fish_zupt_disk_using_command restore' \
|
||||
-l allow-legacy-no-ait \
|
||||
-d 'Recover a trusted old disk archive without an integrity trailer'
|
||||
|
||||
set -l keygen_condition '__fish_zupt_using_command keygen'
|
||||
complete -c zupt -n "$keygen_condition" -s o -l output \
|
||||
-d 'Output key file' -r
|
||||
complete -c zupt -n "$keygen_condition" -l pub \
|
||||
-d 'Export a public key'
|
||||
complete -c zupt -n "$keygen_condition" -s k -l key \
|
||||
-d 'Source private key' -r
|
||||
complete -c zupt -n "$keygen_condition" -l pq-only -l pqonly \
|
||||
-d 'Native ML-KEM-768-only key format'
|
||||
complete -c zupt -n "$keygen_condition" -l sdk -l pq-sdk \
|
||||
-d 'Optional system libvuptsdk key format'
|
||||
complete -c zupt -n "$keygen_condition" -l box -l pq-box \
|
||||
-d 'Optional system libpqvaptvupt key format'
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
.\" Manpage for vaptvupt-gui (formerly zupt-gui; INPI Brasil trademark rename in v3.0.0)
|
||||
.\" SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
.\" Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
.TH VAPTVUPT-GUI 1 "2026-06-11" "vaptvupt-gui 1.3.0" "User Commands"
|
||||
.SH NAME
|
||||
vaptvupt-gui \- graphical interface for the VaptVupt post-quantum backup utility
|
||||
.SH SYNOPSIS
|
||||
.B vaptvupt-gui
|
||||
.RI [ ARCHIVE ]
|
||||
.SH DESCRIPTION
|
||||
.B vaptvupt-gui
|
||||
is a graphical frontend for
|
||||
.BR vaptvupt (1).
|
||||
It provides tabs for compression, extraction, key management, and
|
||||
full-disk backup. Both PQ encryption modes are exposed:
|
||||
.B legacy --pq
|
||||
and
|
||||
.B SDK v2 --pq-sdk
|
||||
(HKDF combiner, key commitment, HPKE binding, Argon2id).
|
||||
|
||||
The legacy command name
|
||||
.B zupt-gui
|
||||
is preserved as a symlink for backward compatibility; both invocations
|
||||
behave identically.
|
||||
|
||||
If
|
||||
.I ARCHIVE
|
||||
is given on the command line, the GUI opens directly on the
|
||||
extract tab with that archive preloaded.
|
||||
|
||||
.B vaptvupt-gui
|
||||
uses Qt 6. It works with either of the following Python Qt bindings,
|
||||
auto-detected at startup in this order:
|
||||
.IP \(bu 2
|
||||
PySide6 (Qt for Python)
|
||||
.IP \(bu 2
|
||||
PyQt6
|
||||
.PP
|
||||
If neither is installed, the GUI prints an instructive error and exits.
|
||||
|
||||
.SH TABS
|
||||
.TP
|
||||
.B Compress
|
||||
Select files or directories, choose codec, level, password and/or PQ
|
||||
key. The
|
||||
.B Mode
|
||||
panel controls whether the SDK v2 path or the legacy path is used.
|
||||
.TP
|
||||
.B Extract
|
||||
Open a .zupt archive, select output directory, provide password
|
||||
and/or PQ private key.
|
||||
.TP
|
||||
.B Keygen
|
||||
Generate ML-KEM-768 + X25519 keypair. The
|
||||
.B SDK v2 format
|
||||
checkbox controls whether the keypair is generated via
|
||||
.B vaptvupt keygen --sdk
|
||||
(producing
|
||||
.IR file
|
||||
and
|
||||
.IR file.pub
|
||||
in one step) or via the legacy
|
||||
.BR "vaptvupt keygen" .
|
||||
.TP
|
||||
.B Disk
|
||||
Full-disk backup and restore. Enumerates block devices with
|
||||
human-readable sizes. Same encryption mode controls as Compress.
|
||||
|
||||
.SH FILES
|
||||
.TP
|
||||
.I /usr/bin/vaptvupt-gui
|
||||
Wrapper script that invokes the Python entry point (and the symlinked
|
||||
legacy
|
||||
.IR /usr/bin/zupt-gui ).
|
||||
.TP
|
||||
.I /usr/lib/vaptvupt-gui/zupt_gui.py
|
||||
Main Python source.
|
||||
.TP
|
||||
.I /usr/share/applications/vaptvupt-gui.desktop
|
||||
Desktop entry for menu integration.
|
||||
.TP
|
||||
.I /usr/share/icons/hicolor/256x256/apps/vaptvupt-gui.png
|
||||
Application icon.
|
||||
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
.B VAPTVUPT_BIN
|
||||
Override the path to the
|
||||
.B vaptvupt
|
||||
binary (default: search
|
||||
.IR PATH ).
|
||||
The legacy name
|
||||
.B ZUPT_BIN
|
||||
is also honoured.
|
||||
.TP
|
||||
.B VAPTVUPT_DEBUG
|
||||
Enable binary-discovery debug logging on stderr. The legacy name
|
||||
.B ZUPT_DEBUG
|
||||
is also honoured.
|
||||
|
||||
.SH BUGS
|
||||
Report at
|
||||
.UR https://git.securityops.co/cristiancmoises/vaptvupt/issues
|
||||
.UE .
|
||||
|
||||
.SH AUTHOR
|
||||
Cristian Cezar Moisés
|
||||
.MT zupt@riseup.net
|
||||
.ME
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR vaptvupt (1).
|
||||
|
||||
.SH LICENSE
|
||||
.PP
|
||||
vaptvupt-gui is licensed under the
|
||||
.B GNU Affero General Public License version 3 or later
|
||||
(AGPL-3.0-or-later). Commercial license available for relief from
|
||||
copyleft terms; contact
|
||||
.MT sac@securityops.co
|
||||
.ME .
|
||||
|
||||
.SH PROJECT
|
||||
.PP
|
||||
Home page:
|
||||
.UR https://git.securityops.co/cristiancmoises/vaptvupt
|
||||
.UE
|
||||
730
doc/vaptvupt.1
730
doc/vaptvupt.1
|
|
@ -1,730 +0,0 @@
|
|||
.\" Manpage for vaptvupt (formerly zupt; INPI Brasil trademark rename in v3.0.0)
|
||||
.\" SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
.\" Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
.TH VAPTVUPT 1 "July 2026" "vaptvupt 5.0.0" "User Commands"
|
||||
|
||||
.SH NAME
|
||||
vaptvupt \- post-quantum backup compression utility (formerly zupt)
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B vaptvupt compress
|
||||
.RI [ options ]
|
||||
.I out.zupt
|
||||
.I files...
|
||||
.br
|
||||
.B vaptvupt extract
|
||||
.RI [ options ]
|
||||
.I archive.zupt
|
||||
.br
|
||||
.B vaptvupt list
|
||||
.RI [ options ]
|
||||
.I archive.zupt
|
||||
.br
|
||||
.B vaptvupt test
|
||||
.RI [ options ]
|
||||
.I archive.zupt
|
||||
.br
|
||||
.B vaptvupt info
|
||||
.I archive.zupt
|
||||
.br
|
||||
.B vaptvupt bench
|
||||
.I files/dirs...
|
||||
.br
|
||||
.B vaptvupt disk backup
|
||||
.RI [ options ]
|
||||
.I out.zupt
|
||||
.I device
|
||||
.br
|
||||
.B vaptvupt disk restore
|
||||
.RI [ options ]
|
||||
.I archive.zupt
|
||||
.I device
|
||||
.br
|
||||
.B vaptvupt keygen
|
||||
.RI [ options ]
|
||||
.br
|
||||
.B vaptvupt version
|
||||
.br
|
||||
.B vaptvupt help
|
||||
|
||||
.PP
|
||||
The legacy command name
|
||||
.B zupt
|
||||
is preserved as an alias for backward compatibility; both invocations
|
||||
behave identically.
|
||||
|
||||
.SH DESCRIPTION
|
||||
.B vaptvupt
|
||||
compresses and encrypts files, directories, and whole block devices
|
||||
into self-contained, authenticated archives with the
|
||||
.B .zupt
|
||||
extension. It targets long-lived backup storage where:
|
||||
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
the archive is written once and restored under time pressure many years later;
|
||||
.IP \(bu 2
|
||||
the encryption envelope must remain secure against a future cryptographically-relevant quantum computer (ML-KEM-768);
|
||||
.IP \(bu 2
|
||||
every byte of the archive — header, footer, per-block metadata, comments — is authenticated, and a single bit-flip is rejected at restore time.
|
||||
.RE
|
||||
|
||||
.PP
|
||||
The compression layer is the
|
||||
.B VaptVupt LZ + ANS
|
||||
codec (version 2.60.4), which prioritises decode speed and ratio over
|
||||
encode speed. Aggregate decode throughput on this build is 1.27\(mu
|
||||
zstd\-3; encode throughput is 0.2\(mu\(en0.5\(mu zstd\-3 depending on
|
||||
content. See
|
||||
.B PERFORMANCE
|
||||
below.
|
||||
|
||||
.PP
|
||||
The on-disk format is v1.6 and has been wire-compatible since release
|
||||
v2.3.1. The product was renamed from
|
||||
.B Zupt
|
||||
to
|
||||
.B VaptVupt
|
||||
in v3.0.0 because of a prior INPI Brasil trademark registration of the
|
||||
name "Zupt" for unrelated software. The
|
||||
.B .zupt
|
||||
file extension and the
|
||||
.B ZUPT
|
||||
header magic bytes are unchanged: archives produced by any v2.x release
|
||||
extract cleanly under v3.0.0 and vice versa.
|
||||
|
||||
.SH COMMANDS
|
||||
|
||||
.TP
|
||||
.B compress
|
||||
Create an archive. Default codec is VaptVupt (level 7). Compression
|
||||
is multi-threaded; one worker per detected CPU by default.
|
||||
|
||||
.TP
|
||||
.B extract
|
||||
Decompress an archive into the current directory (or
|
||||
.BR -o " " \fIdir\fR ).
|
||||
Refuses to write outside the destination directory (path-traversal
|
||||
defence). Files are written with their original permissions and
|
||||
mtime preserved.
|
||||
|
||||
.TP
|
||||
.B list
|
||||
Print archive metadata: per-file path, size, mtime, mode, compressed
|
||||
size, codec. With
|
||||
.B --verbose
|
||||
also prints per-block sizes and HMAC tags (first 8 bytes).
|
||||
|
||||
.TP
|
||||
.B test
|
||||
Decompress all blocks in memory and verify HMAC tags + archive
|
||||
integrity trailer. Does not write any files. Use to validate an
|
||||
archive without restoring it. Exit code is non-zero on any failure.
|
||||
|
||||
.TP
|
||||
.B info
|
||||
Print archive header metadata without requiring the decryption key.
|
||||
Reports: format version, codec, encryption type (none / PBKDF2 /
|
||||
Argon2id / ML-KEM-768+X25519 hybrid / ML-KEM-768 pure-PQ), KDF
|
||||
iteration count, file count,
|
||||
creation timestamp, archive UUID, AIT presence. Safe to run on an
|
||||
untrusted archive.
|
||||
|
||||
.TP
|
||||
.B bench
|
||||
Compare compression levels 1\(en9 on the supplied files; reports
|
||||
ratio and encode/decode throughput per level. Useful when picking
|
||||
the right
|
||||
.B -l
|
||||
for a given workload.
|
||||
|
||||
.TP
|
||||
.B disk backup
|
||||
Read a block device and write a sparse-aware archive. Detects
|
||||
all-zero regions and records them as runs rather than compressing
|
||||
them.
|
||||
|
||||
.TP
|
||||
.B disk restore
|
||||
Inverse of
|
||||
.BR "disk backup" .
|
||||
Writes the archive's contents back to a block device. Verifies
|
||||
target device size before writing; refuses if the target is smaller
|
||||
than the archived size. With
|
||||
.B --sync
|
||||
issues
|
||||
.BR fsync (2)
|
||||
after each block.
|
||||
|
||||
.TP
|
||||
.B keygen
|
||||
Generate a key file. With no PQ flag, writes a 32-byte raw key for
|
||||
keyfile-mode encryption. With
|
||||
.B --pq
|
||||
generates a native ML-KEM-768 + X25519 \fBhybrid\fR keypair for
|
||||
.B --pq
|
||||
encryption (in-tree crypto; no external library). With
|
||||
.B --pq-only
|
||||
generates a native pure ML-KEM-768 keypair (magic
|
||||
.BR ZPQK )
|
||||
for
|
||||
.B --pq-only
|
||||
encryption. With
|
||||
.B --sdk
|
||||
generates a keypair for the optional
|
||||
.B --pq-sdk
|
||||
mode, and with
|
||||
.B --box
|
||||
a libpqvaptvupt sealed-box keypair for
|
||||
.B --pq-box
|
||||
(both need a
|
||||
.B WITH_SDK=1
|
||||
build). With
|
||||
.B --pub
|
||||
extracts the public key from an existing private key (combine with the
|
||||
matching PQ flag, e.g.
|
||||
.BR "keygen --pub --pq-only" ).
|
||||
|
||||
.SH GLOBAL OPTIONS
|
||||
|
||||
.TP
|
||||
.BR -v ", " --verbose
|
||||
Print per-file and per-block details during compress/extract/list/test.
|
||||
|
||||
.TP
|
||||
.BR -q ", " --quiet
|
||||
Suppress non-error output.
|
||||
|
||||
.TP
|
||||
.BR -j " " \fIN\fR ", " --jobs " " \fIN\fR
|
||||
Worker thread count for parallel compression. Default: number of
|
||||
online CPUs.
|
||||
|
||||
.SH COMPRESS OPTIONS
|
||||
|
||||
.TP
|
||||
.BR -l " " \fI1..9\fR ", " --level " " \fI1..9\fR
|
||||
Compression level. 1\(en2 = ultra-fast (~80 MB/s encode on typical
|
||||
hardware, lower ratio). 3\(en7 = balanced (default 7). 8\(en9 =
|
||||
extreme (optimal parsing, ~5\(en10\(mu slower encode, best ratio).
|
||||
|
||||
.TP
|
||||
.B --codec \fIid\fR
|
||||
Force a specific codec by id. Accepted values:
|
||||
.BR store " (0x0000), "
|
||||
.BR vaptvupt-lz " (0x0008), "
|
||||
.BR vaptvupt-lzh " (0x0009), "
|
||||
.BR vaptvupt-lzhp " (0x000A), "
|
||||
.BR vaptvupt " (0x0010 — default), "
|
||||
.BR auto " (0xFFFF — pick at runtime)."
|
||||
|
||||
.TP
|
||||
.BR -p " " \fIpassword\fR
|
||||
Enable password-based encryption (PBKDF2-SHA256 KDF; Argon2id is available
|
||||
only in a WITH_SDK=1 build via \fB--kdf argon2id\fR).
|
||||
Reading the password from a flag exposes it in
|
||||
.BR ps (1)
|
||||
output; prefer
|
||||
.B --pass-file
|
||||
or interactive prompt.
|
||||
|
||||
.TP
|
||||
.B --pass-file \fIpath\fR
|
||||
Read password from the first line of the file. The file's permission
|
||||
bits should be 0600.
|
||||
|
||||
.TP
|
||||
.B --pass-fd \fIN\fR
|
||||
Read password from file descriptor N.
|
||||
|
||||
.TP
|
||||
.B --kdf \fIalgo\fR
|
||||
Choose key-derivation function for password mode. In the default source-only
|
||||
build the only KDF is
|
||||
.BR pbkdf2 " (SHA-256, 600 000 iter), which is the default;"
|
||||
.BR argon2id " (memory-hard) is available only in a " WITH_SDK=1 " build."
|
||||
|
||||
.TP
|
||||
.B --keyfile \fIpath\fR
|
||||
Use a 32-byte raw key file (generated with
|
||||
.BR "vaptvupt keygen" ).
|
||||
|
||||
.TP
|
||||
.B --pq \fIpub\fR
|
||||
Enable native post-quantum \fBhybrid\fR encryption (envelope type 0x02,
|
||||
recommended). Combines ML-KEM-768 (FIPS 203) with X25519 (RFC 7748) so
|
||||
the archive key is secure unless \fBboth\fR the lattice KEM and the
|
||||
elliptic-curve exchange are broken. Uses the in-tree crypto only — no
|
||||
external library, always available. The
|
||||
.I pub
|
||||
argument is the recipient's public-key file from
|
||||
.BR "vaptvupt keygen" .
|
||||
On extraction, pass the secret key:
|
||||
.B --pq
|
||||
\fIpriv\fR.
|
||||
|
||||
.TP
|
||||
.B --pq-only \fIpub\fR
|
||||
Enable native \fBfull\fR (pure) post-quantum encryption (envelope type
|
||||
0x06). ML-KEM-768 is the \fIsole\fR key-establishment mechanism — no
|
||||
X25519 component. Choose this only when a policy mandates a single
|
||||
NIST-standardised PQ primitive with no classical KEM in the envelope
|
||||
(e.g. CNSA 2.0-style "PQ-only" postures). The archive key is
|
||||
SHA3-512(ml_ss || ml_ct || "ZUPT-PQ-ONLY-v1"). Note the deliberate
|
||||
trade-off: unlike
|
||||
.BR --pq ,
|
||||
a future weakness in ML-KEM-768 alone is sufficient to break the
|
||||
envelope, because there is no classical KEM to fall back on. When in
|
||||
doubt use
|
||||
.B --pq
|
||||
(hybrid). Keys are generated with
|
||||
.BR "vaptvupt keygen --pq-only" ;
|
||||
the private and public key files (magic
|
||||
.BR ZPQK )
|
||||
are not interchangeable with hybrid
|
||||
.B --pq
|
||||
keys. On extraction, pass the secret key:
|
||||
.B --pq-only
|
||||
\fIpriv\fR.
|
||||
|
||||
.TP
|
||||
.B --pq-box \fIpub\fR
|
||||
Enable post-quantum sealed-box encryption via libpqvaptvupt (envelope
|
||||
type 0x05). \fBRequires an optional\fR \fBWITH_SDK=1\fR \fBbuild\fR: the
|
||||
default source-only tree ships no vendored library, so this mode is
|
||||
absent unless you build against libpqvaptvupt yourself. Prefer the
|
||||
native
|
||||
.B --pq
|
||||
or
|
||||
.B --pq-only
|
||||
modes, which need no external library. ML-KEM-768 + X25519 shared secrets are
|
||||
combined through HKDF-SHA256 with a domain-separating info string
|
||||
("pqvv-seal-v1"); the box carries AES-256-CTR + HMAC-SHA256
|
||||
Encrypt-then-MAC. The
|
||||
.I pub
|
||||
argument is the recipient's public-key file from
|
||||
.B keygen --box
|
||||
(magic-tagged; public and secret key files are not interchangeable).
|
||||
On extraction, pass the secret key:
|
||||
.B --pq-box
|
||||
\fIpriv\fR.
|
||||
.TP
|
||||
.B --pq-sdk \fIpub\fR
|
||||
Enable post-quantum hybrid encryption via libzuptsdk (envelope type
|
||||
0x03). \fBRequires an optional\fR \fBWITH_SDK=1\fR \fBbuild\fR and is
|
||||
absent from the default source-only tree; use the native
|
||||
.B --pq
|
||||
instead, which provides the same ML-KEM-768 + X25519 hybrid with no
|
||||
external dependency. Uses ML-KEM-768 + X25519 with HKDF combiner, HPKE
|
||||
binding, and key commitment. The
|
||||
.I pub
|
||||
argument is the recipient's public-key file generated by
|
||||
.BR "vaptvupt keygen --sdk" .
|
||||
|
||||
.TP
|
||||
.B -c \fItext\fR ", " --comment " " \fItext\fR
|
||||
Embed an encrypted UTF-8 comment in the archive (up to 4096 bytes).
|
||||
The comment is bound to the archive's frame-preface AAD; tampering
|
||||
is detected at extract time.
|
||||
|
||||
.TP
|
||||
.B --comment-file \fIpath\fR
|
||||
Read the comment from a file rather than the command line.
|
||||
|
||||
.TP
|
||||
.B -b \fIsize\fR ", " --block-size " " \fIsize\fR
|
||||
Compression block size. Default 4 MiB. Smaller blocks improve
|
||||
random-access decode but lose some ratio.
|
||||
|
||||
.SH EXTRACT, LIST, TEST OPTIONS
|
||||
|
||||
.TP
|
||||
.BR -o " " \fIdir\fR ", " --output " " \fIdir\fR
|
||||
Extract into
|
||||
.IR dir
|
||||
(created if it doesn't exist). Default: current directory.
|
||||
|
||||
.TP
|
||||
.B --no-mtime
|
||||
Do not restore archived modification times; use current time instead.
|
||||
|
||||
.TP
|
||||
.B --strip-components \fIN\fR
|
||||
Strip
|
||||
.I N
|
||||
leading path components from each entry, like
|
||||
.BR tar 's
|
||||
flag of the same name.
|
||||
|
||||
.SH POST-QUANTUM ENCRYPTION
|
||||
|
||||
.B vaptvupt
|
||||
offers two native post-quantum modes, both built entirely from the
|
||||
in-tree crypto (no external library):
|
||||
|
||||
.RS
|
||||
.IP "\fB--pq\fR (hybrid, recommended)" 4
|
||||
A hybrid KEM combining ML-KEM-768 (FIPS 203) with X25519 (RFC 7748).
|
||||
The archive key is derived as:
|
||||
.RS
|
||||
.nf
|
||||
|
||||
ss_pq = ML-KEM-768.decaps(sk_pq, ct_pq)
|
||||
ss_ec = X25519(sk_ec, pk_ec_peer)
|
||||
session = HKDF-SHA256(ss_pq || ss_ec,
|
||||
info = "vaptvupt-pq-sdk-v1",
|
||||
salt = archive_uuid)
|
||||
.fi
|
||||
.RE
|
||||
The hybrid combiner means the session key is at least as strong as the
|
||||
strongest of {ML-KEM-768, X25519}: an attacker must break \fBboth\fR to
|
||||
recover the key. This is the default recommendation and the right choice
|
||||
for almost every user, because it stays secure even if one primitive is
|
||||
later found weak.
|
||||
.IP "\fB--pq-only\fR (full / pure PQ)" 4
|
||||
ML-KEM-768 as the \fIsole\fR key-establishment mechanism, with no
|
||||
classical component. The archive key is derived as:
|
||||
.RS
|
||||
.nf
|
||||
|
||||
(ss_pq, ct_pq) = ML-KEM-768.encaps(pk_pq)
|
||||
archive_key = SHA3-512(ss_pq || ct_pq || "ZUPT-PQ-ONLY-v1")
|
||||
.fi
|
||||
.RE
|
||||
Use this only when a compliance posture requires a single
|
||||
NIST-standardised PQ primitive with no classical KEM in the envelope
|
||||
(for example CNSA 2.0-style "PQ-only" requirements). The deliberate
|
||||
trade-off is that the envelope has \fBno hybrid safety net\fR: a future
|
||||
cryptanalytic break of ML-KEM-768 alone breaks the archive, whereas
|
||||
under
|
||||
.B --pq
|
||||
the attacker would still have to break X25519 as well. Unless a policy
|
||||
forbids the classical component, prefer
|
||||
.BR --pq .
|
||||
.RE
|
||||
|
||||
.PP
|
||||
Both modes carry the same authenticated envelope as password mode:
|
||||
per-block AES-256-CTR with a fresh random 128-bit nonce, HMAC-SHA256
|
||||
Encrypt-then-MAC, and ML-KEM Fujisaki-Okamoto implicit rejection, so a
|
||||
wrong or tampered ciphertext is rejected rather than yielding garbage.
|
||||
|
||||
.PP
|
||||
.B Key commitment:
|
||||
the ciphertext is bound to the exact public key it was encrypted to
|
||||
via an HPKE-style derivation. An attacker cannot present a different
|
||||
public key that decrypts to the same plaintext (this defeats the
|
||||
"partitioning" attack class).
|
||||
|
||||
.PP
|
||||
.B Implementation notes:
|
||||
the ML-KEM-768 implementation is vendored from a clean reference and
|
||||
verified against the FIPS 203 KAT vectors. The X25519 implementation
|
||||
uses 4\(mu64-bit field arithmetic with Jasmin-verified constant-time
|
||||
field operations on x86_64. On other architectures the same routines
|
||||
run in pure C, also constant-time by construction.
|
||||
|
||||
.SH SECURITY
|
||||
|
||||
.SS Threat model
|
||||
|
||||
What
|
||||
.B vaptvupt
|
||||
.B protects against:
|
||||
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
Confidentiality of archived data at rest (AES-256-CTR with HMAC-SHA256 EtM, or AEAD via libzuptsdk on the
|
||||
.B --pq-sdk
|
||||
path).
|
||||
.IP \(bu 2
|
||||
End-to-end byte-level tamper detection on encrypted archives. The F-09 byte-sweep regression (1827 positions on a representative archive, 2000 trials, every run) shows zero silent-accept positions.
|
||||
.IP \(bu 2
|
||||
Wrong-password and tampered-archive indistinguishability at the user-visible message layer (F-11). The default error wording is identical for both cases; only
|
||||
.B --verbose
|
||||
prints the distinguishing detail. This closes the "verbal probe-oracle" attack class where the error string leaked which check failed first.
|
||||
.IP \(bu 2
|
||||
Post-quantum forward secrecy on archives encrypted with
|
||||
.B --pq-sdk
|
||||
(assuming ML-KEM-768 holds against future quantum attack).
|
||||
.IP \(bu 2
|
||||
Archive-header and footer authentication via a 32-byte HMAC-SHA256 trailer (F-08). Tampering with the file count, comment offset, or timestamp is detected at archive open time.
|
||||
.IP \(bu 2
|
||||
Path-traversal at extract time. Entries with absolute paths or
|
||||
.B ..
|
||||
components are refused or stripped.
|
||||
.RE
|
||||
|
||||
What it does
|
||||
.B NOT
|
||||
protect against:
|
||||
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
Compromise of the endpoint that creates or restores the archive. If the host is compromised, the password, key file, or plaintext is accessible.
|
||||
.IP \(bu 2
|
||||
Compromise of the key file or password. Key custody is the user's responsibility.
|
||||
.IP \(bu 2
|
||||
A weak password. Argon2id with default parameters needs ~256 MiB and ~1 s to derive a key on commodity hardware; a 4-character password is still trivially crackable.
|
||||
.IP \(bu 2
|
||||
Metadata leakage. File names, sizes, and modification times are encrypted, but the archive's total size and the count of compressed blocks are visible to an observer.
|
||||
.IP \(bu 2
|
||||
Side channels on the compression layer (CRIME/BREACH-style). If the same archive contains both attacker-controlled and secret data and the attacker can observe the compressed size, length-based oracles may be possible.
|
||||
.IP \(bu 2
|
||||
Denial-of-service via malformed input on the decoder. The decoder rejects malformed input cleanly (no crashes in the fuzz harness), but a very large compressed input can still consume CPU and memory proportional to its size.
|
||||
.RE
|
||||
|
||||
.SS Cryptographic primitives
|
||||
|
||||
.TS
|
||||
tab(|);
|
||||
l l.
|
||||
SHA-256 | FIPS 180-4
|
||||
SHA-3 / SHAKE | FIPS 202
|
||||
ML-KEM-768 | FIPS 203
|
||||
AES-256-CTR | NIST SP 800-38A
|
||||
HMAC-SHA256 | RFC 2104 / FIPS 198-1
|
||||
X25519 | RFC 7748
|
||||
HKDF-SHA256 | RFC 5869
|
||||
PBKDF2-SHA256 | RFC 8018
|
||||
Argon2id | RFC 9106
|
||||
XXH64 | non-cryptographic; used only inside the AEAD envelope
|
||||
.TE
|
||||
|
||||
.SS Constant-time guarantees
|
||||
|
||||
All secret-dependent comparisons and table lookups in the cryptographic
|
||||
core are constant-time. On x86_64 the hot paths (HMAC equality compare,
|
||||
ML-KEM Fujisaki-Okamoto implicit rejection) are implemented in Jasmin
|
||||
and assembled with
|
||||
.BR jasminc (1).
|
||||
On other architectures the same routines run in portable C; the
|
||||
constant-time property is preserved by source-level construction.
|
||||
|
||||
.SH FILES
|
||||
|
||||
.TP
|
||||
.I ~/.config/vaptvupt/
|
||||
Per-user configuration directory (reserved; not used in v3.0.0).
|
||||
|
||||
.TP
|
||||
.I /etc/vaptvupt/
|
||||
System-wide configuration directory (reserved; not used in v3.0.0).
|
||||
|
||||
.TP
|
||||
.I /usr/share/bash-completion/completions/vaptvupt
|
||||
Bash completion (and the symlinked legacy
|
||||
.IR /usr/share/bash-completion/completions/zupt ).
|
||||
|
||||
.TP
|
||||
.I /usr/share/zsh/site-functions/_vaptvupt
|
||||
zsh completion.
|
||||
|
||||
.TP
|
||||
.I /usr/share/fish/vendor_completions.d/vaptvupt.fish
|
||||
fish completion.
|
||||
|
||||
.SH ENVIRONMENT
|
||||
|
||||
.TP
|
||||
.B VAPTVUPT_BIN
|
||||
Override the binary path used by the GUI front-end. Legacy
|
||||
.B ZUPT_BIN
|
||||
is also honoured.
|
||||
|
||||
.TP
|
||||
.B VAPTVUPT_DEBUG
|
||||
If set to any non-empty value, the GUI front-end prints its binary-discovery log to stderr.
|
||||
|
||||
.SH EXIT STATUS
|
||||
|
||||
.TP
|
||||
.B 0
|
||||
Success.
|
||||
|
||||
.TP
|
||||
.B 1
|
||||
General error (bad arguments, file not found, etc.).
|
||||
|
||||
.TP
|
||||
.B 2
|
||||
Authentication failed. Wrong password, wrong key file, or the archive has been tampered with. Use
|
||||
.B --verbose
|
||||
to see the distinguishing detail (subject to F-11's threat model: detailed messages may leak which failure cause fired first).
|
||||
|
||||
.TP
|
||||
.B 3
|
||||
Archive-format error (wrong magic bytes, unsupported format version, corrupted header).
|
||||
|
||||
.TP
|
||||
.B 4
|
||||
I/O error (disk full, permission denied, network failure).
|
||||
|
||||
.TP
|
||||
.B 5
|
||||
Compressed-data integrity error (per-block HMAC mismatch detected mid-stream).
|
||||
|
||||
.SH PERFORMANCE
|
||||
|
||||
Numbers below are from the v3.0.0 release benchmark (May 2026), run
|
||||
on an Intel Xeon @ 2.8 GHz with the codec compiled with the
|
||||
distribution's default optimisation level.
|
||||
|
||||
.TS
|
||||
tab(|);
|
||||
l l l l l.
|
||||
\fBFixture\fR | \fBTool\fR | \fBRatio\fR | \fBEnc MB/s\fR | \fBDec MB/s\fR
|
||||
text 8 MB | vv-9 | 34.6% | 5.4 | 219
|
||||
text 8 MB | gzip-9 | 30.9% | 6.2 | 137
|
||||
text 8 MB | zstd-3 | 31.6% | 137 | 427
|
||||
text 8 MB | zstd-19 | 25.5% | 1.6 | 384
|
||||
source 670 KB | vv-9 | 25.1% | 11.4 | 128
|
||||
source 670 KB | gzip-9 | 23.2% | 10.0 | 107
|
||||
source 670 KB | zstd-3 | 24.1% | 91 | 160
|
||||
binary 2.4 MB | vv-9 | 44.7% | 7.7 | 153
|
||||
binary 2.4 MB | gzip-9 | 52.0% | 12.6 | 109
|
||||
binary 2.4 MB | zstd-3 | 77.3% | 164 | 382
|
||||
binary 2.4 MB | zstd-19 | 48.0% | 5.8 | 229
|
||||
random 5 MB | vv-9 | 100.0% | 11.1 | 477
|
||||
random 5 MB | zstd-3 | 100.0% | 397 | 681
|
||||
.TE
|
||||
|
||||
.PP
|
||||
Honest reading of these numbers:
|
||||
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
On binary-structured data (game saves, mmap'd structures, struct arrays),
|
||||
.B vaptvupt
|
||||
beats zstd-3 by a wide margin on ratio (44.7% vs 77.3%) at the cost of being ~20\(mu slower to encode. For write-once / restore-often workloads this is the right trade.
|
||||
.IP \(bu 2
|
||||
On text and source, zstd-19 beats
|
||||
.B vaptvupt
|
||||
on ratio. The fundamental codec difference is that zstd's reference encoder has had years of compiler-engineering attention that
|
||||
.B vaptvupt
|
||||
has not.
|
||||
.IP \(bu 2
|
||||
Encode throughput is
|
||||
.BR vaptvupt 's
|
||||
weak axis. If encode latency matters more than ratio, use
|
||||
.B -l 1
|
||||
or
|
||||
.BR -l 2 .
|
||||
.IP \(bu 2
|
||||
On random / already-compressed data, all codecs hit the incompressibility wall; comparing encode/decode throughput in that regime is mostly measuring memcpy speed plus framing overhead.
|
||||
.RE
|
||||
|
||||
.SH EXAMPLES
|
||||
|
||||
.PP
|
||||
Compress with default settings (PBKDF2-SHA256 password, VaptVupt level 7, multi-threaded):
|
||||
|
||||
.RS
|
||||
.nf
|
||||
$ vaptvupt compress -p secret backup.zupt ~/Documents
|
||||
.fi
|
||||
.RE
|
||||
|
||||
Compress with post-quantum hybrid encryption to a published public key:
|
||||
|
||||
.RS
|
||||
.nf
|
||||
$ vaptvupt keygen --sdk -o ~/.config/vaptvupt-mykey
|
||||
$ vaptvupt keygen -o ~/.config/vaptvupt-mykey
|
||||
$ vaptvupt keygen --pub -o mykey.pub -k ~/.config/vaptvupt-mykey
|
||||
$ vaptvupt compress --pq mykey.pub backup.zupt ~/Documents
|
||||
.fi
|
||||
.RE
|
||||
|
||||
Compress with full (pure) post-quantum encryption — ML-KEM-768 only,
|
||||
no classical component (compliance postures that mandate a single PQ
|
||||
primitive):
|
||||
|
||||
.RS
|
||||
.nf
|
||||
$ vaptvupt keygen --pq-only -o pqkey
|
||||
$ vaptvupt keygen --pub --pq-only -o pqkey.pub -k pqkey
|
||||
$ vaptvupt compress --pq-only pqkey.pub backup.zupt ~/Documents
|
||||
$ vaptvupt extract --pq-only pqkey -o restored backup.zupt
|
||||
.fi
|
||||
.RE
|
||||
|
||||
Backup a block device, sparse-aware:
|
||||
|
||||
.RS
|
||||
.nf
|
||||
$ sudo vaptvupt disk backup -p secret system.zupt /dev/nvme0n1p2
|
||||
.fi
|
||||
.RE
|
||||
|
||||
Verify an archive without restoring:
|
||||
|
||||
.RS
|
||||
.nf
|
||||
$ vaptvupt test -p secret backup.zupt
|
||||
.fi
|
||||
.RE
|
||||
|
||||
Print archive metadata without supplying a password:
|
||||
|
||||
.RS
|
||||
.nf
|
||||
$ vaptvupt info backup.zupt
|
||||
.fi
|
||||
.RE
|
||||
|
||||
Compare compression levels:
|
||||
|
||||
.RS
|
||||
.nf
|
||||
$ vaptvupt bench ~/Downloads/big-dataset.bin
|
||||
.fi
|
||||
.RE
|
||||
|
||||
Run the GUI from a desktop session where /usr/bin isn't on PATH (the bug fixed in v3.0.0):
|
||||
|
||||
.RS
|
||||
.nf
|
||||
$ VAPTVUPT_DEBUG=1 vaptvupt-gui 2> /tmp/discovery.log
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.SH STANDARDS
|
||||
|
||||
ISO C11; POSIX.1-2017 for I/O and threading. The cryptographic
|
||||
primitives implement the specifications listed in
|
||||
.BR SECURITY
|
||||
above. The on-disk archive format is documented in
|
||||
.B FORMAT.md
|
||||
in the source distribution.
|
||||
|
||||
.SH AUTHORS
|
||||
Cristian Cezar Moisés <zupt@riseup.net> — primary author and maintainer.
|
||||
|
||||
.SH BUGS
|
||||
Report bugs at https://git.securityops.co/cristiancmoises/vaptvupt/issues
|
||||
or by email to <zupt@riseup.net>.
|
||||
|
||||
.SH LICENSE
|
||||
AGPL-3.0-or-later for the application; GPL-3.0-or-later for the
|
||||
embedded VaptVupt codec. Dual-licensed: a commercial licence is
|
||||
available from <sac@securityops.co>.
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR vaptvupt-gui (1),
|
||||
.BR zstd (1),
|
||||
.BR gzip (1),
|
||||
.BR xz (1),
|
||||
.BR tar (1),
|
||||
.BR cryptsetup (8),
|
||||
.BR jasminc (1).
|
||||
.PP
|
||||
Project home: https://git.securityops.co/cristiancmoises/vaptvupt
|
||||
.br
|
||||
Threat model: see
|
||||
.B THREAT_MODEL.md
|
||||
in the source distribution.
|
||||
.br
|
||||
Archive format spec: see
|
||||
.B FORMAT.md
|
||||
in the source distribution.
|
||||
235
doc/zupt-gui.1
235
doc/zupt-gui.1
|
|
@ -1,127 +1,158 @@
|
|||
.\" Manpage for vaptvupt-gui (formerly zupt-gui; INPI Brasil trademark rename in v3.0.0)
|
||||
.\" SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
.\" Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
.TH VAPTVUPT-GUI 1 "2026-06-11" "vaptvupt-gui 1.3.0" "User Commands"
|
||||
.TH ZUPT-GUI 1 "2026-08-31" "ZUPT 5.2.8" "User Commands"
|
||||
.SH NAME
|
||||
vaptvupt-gui \- graphical interface for the VaptVupt post-quantum backup utility
|
||||
zupt-gui \- Qt interface for the ZUPT backup utility
|
||||
.SH SYNOPSIS
|
||||
.B vaptvupt-gui
|
||||
.RI [ ARCHIVE ]
|
||||
.SH DESCRIPTION
|
||||
.B vaptvupt-gui
|
||||
is a graphical frontend for
|
||||
.BR vaptvupt (1).
|
||||
It provides tabs for compression, extraction, key management, and
|
||||
full-disk backup. Both PQ encryption modes are exposed:
|
||||
.B legacy --pq
|
||||
and
|
||||
.B SDK v2 --pq-sdk
|
||||
(HKDF combiner, key commitment, HPKE binding, Argon2id).
|
||||
|
||||
The legacy command name
|
||||
.B zupt-gui
|
||||
is preserved as a symlink for backward compatibility; both invocations
|
||||
behave identically.
|
||||
|
||||
If
|
||||
.I ARCHIVE
|
||||
is given on the command line, the GUI opens directly on the
|
||||
extract tab with that archive preloaded.
|
||||
|
||||
.B vaptvupt-gui
|
||||
uses Qt 6. It works with either of the following Python Qt bindings,
|
||||
auto-detected at startup in this order:
|
||||
.IP \(bu 2
|
||||
PySide6 (Qt for Python)
|
||||
.IP \(bu 2
|
||||
PyQt6
|
||||
.RI [ ARCHIVE.zupt ]
|
||||
.br
|
||||
.B zupt-gui
|
||||
.BI --compress " FILE ..."
|
||||
.br
|
||||
.B zupt-gui
|
||||
.BI --extract " ARCHIVE.zupt"
|
||||
.br
|
||||
.B zupt-gui
|
||||
.RB [ --help | --version | --selftest ]
|
||||
.SH DESCRIPTION
|
||||
.B zupt-gui
|
||||
is a Python Qt 6 frontend for
|
||||
.BR zupt (1).
|
||||
It creates, inspects, verifies, and extracts archives by running the CLI as a
|
||||
child process. It can also request CLI disk backup and restore operations.
|
||||
The GUI does not implement an archive codec or cryptography itself.
|
||||
.PP
|
||||
If neither is installed, the GUI prints an instructive error and exits.
|
||||
|
||||
PySide6 is tried first and PyQt6 is used as a fallback. The selected
|
||||
.B zupt
|
||||
command is checked by executing
|
||||
.BR "zupt version" .
|
||||
The ZUPT command and environment variables are preferred; renamed-era names
|
||||
are accepted only for compatibility with an existing installation.
|
||||
.PP
|
||||
When paired with the source-only baseline CLI, the frontend uses a build with
|
||||
.B WITH_SDK=0
|
||||
and
|
||||
.BR WITH_PQBOX=0 .
|
||||
Native password,
|
||||
.B --pq
|
||||
(ML-KEM-768 plus X25519), and
|
||||
.B --pq-only
|
||||
(ML-KEM-768) modes remain available. The GUI parses the CLI's
|
||||
.B Build integrations:
|
||||
line and exposes
|
||||
.B --pq-sdk
|
||||
or
|
||||
.B --pq-box
|
||||
only when libvuptsdk or libpqvaptvupt is independently reported enabled.
|
||||
These two optional integrations are detected separately.
|
||||
.PP
|
||||
The gated 5.2.8 GUI release set is limited to the architecture-independent DEB,
|
||||
noarch/source RPM, and source-only portable ZIP named in the project README.
|
||||
Package gates require exact checks and installed off-screen GUI/CLI integration.
|
||||
The portable ZIP receives source scans, an exact safe-member allowlist, and an
|
||||
extracted launcher test; it bundles no Python, Qt, CLI, or compiled runtime.
|
||||
AppImage, AppDir and Flatpak bundles and Windows/macOS GUI installers are not
|
||||
promoted; the Windows ZIP and macOS DMG are CLI-only.
|
||||
.PP
|
||||
The GUI does not expose the CLI's recovery-only
|
||||
.B --allow-legacy-no-ait
|
||||
option. A known, trusted pre-AIT archive must be recovered explicitly with the
|
||||
CLI; untrusted trailerless archives must remain rejected.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B --compress
|
||||
Open the Compress tab with the remaining arguments selected as inputs.
|
||||
.TP
|
||||
.B --extract
|
||||
Open the Extract tab with the following archive selected.
|
||||
.TP
|
||||
.B --selftest
|
||||
Create the complete interface, run the event loop briefly, and exit. A display
|
||||
backend (or a suitable off-screen Qt backend) is still required.
|
||||
.TP
|
||||
.BR --version , " -V"
|
||||
Print the GUI, Qt binding, CLI version, and selected CLI path.
|
||||
.TP
|
||||
.BR --help , " -h"
|
||||
Print command-line usage.
|
||||
.SH TABS
|
||||
.TP
|
||||
.B Keys
|
||||
Generate and export recipient keys. Mode choices follow CLI capability
|
||||
detection, including independent SDK and PQ-box choices when enabled.
|
||||
.TP
|
||||
.B Compress
|
||||
Select files or directories, choose codec, level, password and/or PQ
|
||||
key. The
|
||||
.B Mode
|
||||
panel controls whether the SDK v2 path or the legacy path is used.
|
||||
Choose inputs, destination, codec options, password, and an optional recipient
|
||||
public key.
|
||||
.TP
|
||||
.B Extract
|
||||
Open a .zupt archive, select output directory, provide password
|
||||
and/or PQ private key.
|
||||
Choose an archive, output directory, and any required password or private key.
|
||||
The GUI uses
|
||||
.B zupt info
|
||||
to auto-detect supported archive protection modes. That framing inspection is
|
||||
unauthenticated and is only a mode-selection hint; the subsequent CLI extract
|
||||
or test operation performs the required AIT and content validation.
|
||||
.TP
|
||||
.B Keygen
|
||||
Generate ML-KEM-768 + X25519 keypair. The
|
||||
.B SDK v2 format
|
||||
checkbox controls whether the keypair is generated via
|
||||
.B vaptvupt keygen --sdk
|
||||
(producing
|
||||
.IR file
|
||||
and
|
||||
.IR file.pub
|
||||
in one step) or via the legacy
|
||||
.BR "vaptvupt keygen" .
|
||||
.B Verify
|
||||
Inspect an archive header or run the CLI integrity test with the detected
|
||||
credential type.
|
||||
.TP
|
||||
.B Disk
|
||||
Full-disk backup and restore. Enumerates block devices with
|
||||
human-readable sizes. Same encryption mode controls as Compress.
|
||||
|
||||
.SH FILES
|
||||
Request full-device or image backup and restore through the CLI. Raw devices
|
||||
may require operating-system privileges. Restore overwrites its selected target
|
||||
and requires explicit confirmation in the GUI.
|
||||
.TP
|
||||
.I /usr/bin/vaptvupt-gui
|
||||
Wrapper script that invokes the Python entry point (and the symlinked
|
||||
legacy
|
||||
.IR /usr/bin/zupt-gui ).
|
||||
.TP
|
||||
.I /usr/lib/vaptvupt-gui/zupt_gui.py
|
||||
Main Python source.
|
||||
.TP
|
||||
.I /usr/share/applications/vaptvupt-gui.desktop
|
||||
Desktop entry for menu integration.
|
||||
.TP
|
||||
.I /usr/share/icons/hicolor/256x256/apps/vaptvupt-gui.png
|
||||
Application icon.
|
||||
|
||||
.B About
|
||||
Show the detected CLI version and build information.
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
.B VAPTVUPT_BIN
|
||||
Override the path to the
|
||||
.B vaptvupt
|
||||
binary (default: search
|
||||
.IR PATH ).
|
||||
The legacy name
|
||||
.B ZUPT_BIN
|
||||
is also honoured.
|
||||
Absolute or executable path to the preferred
|
||||
.B zupt
|
||||
command. It must pass the CLI version liveness check.
|
||||
.TP
|
||||
.B VAPTVUPT_DEBUG
|
||||
Enable binary-discovery debug logging on stderr. The legacy name
|
||||
.B ZUPT_DEBUG
|
||||
is also honoured.
|
||||
|
||||
Print command-discovery diagnostics to standard error when non-empty.
|
||||
.TP
|
||||
.B ZUPT_NO_XCB_FALLBACK
|
||||
Disable the guarded XWayland relaunch used when a Wayland window is never
|
||||
exposed.
|
||||
.TP
|
||||
.BR VAPTVUPT_BIN , " VAPTVUPT_DEBUG" , " VAPTVUPT_NO_XCB_FALLBACK"
|
||||
Renamed-era compatibility aliases for the corresponding ZUPT variables.
|
||||
New integrations should use the ZUPT names.
|
||||
.SH FILES
|
||||
.TP
|
||||
.I /usr/bin/zupt-gui
|
||||
Installed launcher.
|
||||
.TP
|
||||
.I /usr/lib/zupt-gui/zupt_gui.py
|
||||
GUI source location used by the DEB or source installer. The noarch RPM may use
|
||||
an architecture-independent shared-data directory instead.
|
||||
.TP
|
||||
.I /usr/share/applications/zupt-gui.desktop
|
||||
Desktop entry.
|
||||
.PP
|
||||
Distribution packages do not install a
|
||||
.B vaptvupt-gui
|
||||
alias. The source installer can create one only with its explicit
|
||||
.B --legacy-alias
|
||||
option. The optional alias has no separate manual page.
|
||||
.SH BUGS
|
||||
Report at
|
||||
.UR https://git.securityops.co/cristiancmoises/vaptvupt/issues
|
||||
Report reproducible issues at
|
||||
.UR https://github.com/cristiancmoises/zupt/issues
|
||||
the ZUPT issue tracker
|
||||
.UE .
|
||||
|
||||
.SH AUTHOR
|
||||
Cristian Cezar Moisés
|
||||
.MT zupt@riseup.net
|
||||
.ME
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR vaptvupt (1).
|
||||
|
||||
.SH LICENSE
|
||||
.PP
|
||||
vaptvupt-gui is licensed under the
|
||||
.B GNU Affero General Public License version 3 or later
|
||||
(AGPL-3.0-or-later). Commercial license available for relief from
|
||||
copyleft terms; contact
|
||||
.MT sac@securityops.co
|
||||
.ME .
|
||||
|
||||
.SH PROJECT
|
||||
.PP
|
||||
Home page:
|
||||
.UR https://git.securityops.co/cristiancmoises/vaptvupt
|
||||
.UE
|
||||
The current integrated GUI source carries AGPL-3.0-or-later notices. Published
|
||||
historical revisions include MIT grants that remain applicable to the exact
|
||||
material distributed under them. See
|
||||
.I gui/LICENSE-GUI
|
||||
and the 5.2.2 licensing erratum in
|
||||
.I CHANGELOG.md
|
||||
for scope and repository evidence.
|
||||
.SH SEE ALSO
|
||||
.BR zupt (1)
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
vaptvupt.1
|
||||
636
doc/zupt.1
Normal file
636
doc/zupt.1
Normal file
|
|
@ -0,0 +1,636 @@
|
|||
.\" SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
.\" Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
.TH ZUPT 1 "2026-08-31" "ZUPT 5.2.8" "User Commands"
|
||||
.
|
||||
.SH NAME
|
||||
zupt \- source-built backup compression and authenticated-encryption utility
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.B zupt compress
|
||||
.RI [ options ]
|
||||
.I output.zupt input...
|
||||
.br
|
||||
.B zupt extract
|
||||
.RI [ options ]
|
||||
.I archive.zupt
|
||||
.br
|
||||
.B zupt list
|
||||
.RI [ options ]
|
||||
.I archive.zupt
|
||||
.br
|
||||
.B zupt test
|
||||
.RI [ options ]
|
||||
.I archive.zupt
|
||||
.br
|
||||
.B zupt info
|
||||
.I archive.zupt
|
||||
.br
|
||||
.B zupt bench
|
||||
.RB [ --compare ]
|
||||
.I input...
|
||||
.br
|
||||
.B zupt disk backup
|
||||
.RI [ options ]
|
||||
.I output.zupt device-or-file
|
||||
.br
|
||||
.B zupt disk restore
|
||||
.RI [ options ]
|
||||
.I archive.zupt target-device-or-file
|
||||
.br
|
||||
.B zupt keygen
|
||||
.RI [ key-options ]
|
||||
.B -o
|
||||
.I output
|
||||
.br
|
||||
.B zupt
|
||||
.RB { help | --help | -h | version | --version | -V }
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
.B zupt
|
||||
creates self-contained backup archives with the historical
|
||||
.B .zupt
|
||||
extension. Version 5.2.2 restores the original product name, ZUPT, and the
|
||||
primary installed command is
|
||||
.BR zupt .
|
||||
The archive extension and
|
||||
.B ZUPT
|
||||
format magic, codec identifiers, and archive compatibility remain unchanged.
|
||||
.
|
||||
.PP
|
||||
Plain archives provide compression checksums for accidental-corruption
|
||||
detection. They do not provide cryptographic authentication against an attacker
|
||||
who can rewrite an archive. Encrypted archives use AES-256-CTR with
|
||||
HMAC-SHA256 and authenticate current per-block framing, logical frame position,
|
||||
and archive metadata. The validating read paths require an archive-integrity
|
||||
trailer by default; a no-trailer archive fails closed unless the caller selects
|
||||
the explicit trusted-legacy override described below.
|
||||
See
|
||||
.B SECURITY.md
|
||||
and
|
||||
.B THREAT_MODEL.md
|
||||
for the exact boundary and historical-format limitations.
|
||||
.
|
||||
.PP
|
||||
The bundled compression codec is VaptVupt codec 2.65.3.
|
||||
Automatic codec selection uses VaptVupt where the supported AVX2 or NEON path
|
||||
is available and uses the portable LZHP codec otherwise. Use a codec-selection
|
||||
option only when a specific choice is required.
|
||||
.
|
||||
.PP
|
||||
The renamed-era
|
||||
.B vaptvupt
|
||||
command is an optional compatibility alias and is not installed by default.
|
||||
Distribution packages, including the openSUSE main package, expose
|
||||
.B zupt
|
||||
as the canonical command.
|
||||
.
|
||||
.PP
|
||||
Git and the upstream source tarball are source-only. Separately built CLI DEB,
|
||||
binary RPM, SRPM, notice-bearing Linux tar.xz, Windows ZIP, and macOS DMG assets
|
||||
may be published from the immutable tag only after their target-specific gates
|
||||
pass; they never enter Git or the source tarball. An AppImage is not promoted
|
||||
for 5.2.8; neither are AppDir/Flatpak bundles, GUI platform installers, or bare
|
||||
Linux/Windows executables. The Python/Qt frontend remains available as source;
|
||||
its gated architecture-independent DEB, noarch/source RPM, and source-only
|
||||
portable ZIP are included in the release claim. The portable ZIP contains no
|
||||
Python, Qt, CLI, or compiled runtime. Windows and macOS artifacts remain
|
||||
CLI-only.
|
||||
.
|
||||
.SH COMMANDS
|
||||
.TP
|
||||
.BR compress , " c"
|
||||
Create an archive from one or more files or directories. Directories are
|
||||
traversed recursively. All options must precede
|
||||
.IR output.zupt .
|
||||
A literal input name beginning with a hyphen can follow a
|
||||
.B --
|
||||
separator after the output name.
|
||||
.
|
||||
.TP
|
||||
.BR extract , " x"
|
||||
Extract regular-file contents below the current directory or the directory
|
||||
selected by
|
||||
.BR -o .
|
||||
Entry names are validated and resolved below a pinned destination. Existing
|
||||
destination files are not overwritten. Decoded data is published from a
|
||||
private temporary file only after its expected size and checksum are verified.
|
||||
ZUPT does not restore ownership, ACLs, extended attributes, original mode,
|
||||
or modification time.
|
||||
.
|
||||
.TP
|
||||
.BR list , " l"
|
||||
Open and verify the archive as required, then print its format, block and
|
||||
protection flags followed by each entry's path, original size, compressed size,
|
||||
and compression ratio. No file is extracted.
|
||||
.
|
||||
.TP
|
||||
.BR test , " t"
|
||||
Decode and verify the archive without writing extracted files. Exit status is
|
||||
nonzero on an authentication, integrity, format, or I/O failure.
|
||||
.
|
||||
.TP
|
||||
.BR info , " i"
|
||||
Read non-secret framing metadata without a password or private key. Current
|
||||
output includes archive size, format version, integrity-trailer type, UUID,
|
||||
creation timestamp, block count when a footer is found, encryption/PQ mode,
|
||||
selected global feature flags, and whether a comment is present. This command
|
||||
does not decrypt, list, or extract entries. It does not validate the trailer or
|
||||
archive contents: a reported trailer is framing information, and successful
|
||||
execution is not an integrity result. Its input must still be treated as
|
||||
untrusted.
|
||||
.
|
||||
.TP
|
||||
.BR bench , " b"
|
||||
Run the built-in compression-level benchmark on supplied input. With
|
||||
.BR --compare ,
|
||||
also compare external compressors available on the host. Results depend on the
|
||||
input, compiler, CPU, storage, and system load and are not release support
|
||||
claims.
|
||||
.
|
||||
.TP
|
||||
.B disk backup
|
||||
Read a device or regular file sequentially and create a sparse-aware archive.
|
||||
Zero regions are represented without storing their full contents.
|
||||
.
|
||||
.TP
|
||||
.B disk restore
|
||||
Restore a disk-image archive to a device or regular file. The target is written
|
||||
destructively; verify both operands and use the least privilege required. The
|
||||
archive is first copied to a private, auto-deleted scratch file, and validation
|
||||
and restoration consume that same snapshot before the target is opened. Set
|
||||
.B ZUPT_TMPDIR
|
||||
to an existing private scratch directory when needed; an invalid override fails
|
||||
without fallback. A raw block device is rejected before the first write when
|
||||
its capacity cannot be determined or is smaller than the restored image.
|
||||
.
|
||||
.TP
|
||||
.B keygen
|
||||
Generate a native ML-KEM-768 plus X25519 hybrid private key by default. Other
|
||||
key formats require the matching key-generation option and, for optional SDK or
|
||||
PQBOX modes, a build with the corresponding system integration enabled.
|
||||
.
|
||||
.TP
|
||||
.BR version , " --version" , " -V"
|
||||
Print the program, archive-format and codec versions, compiled optional
|
||||
integrations, runtime CPU acceleration, license scopes, and canonical project
|
||||
URL.
|
||||
.
|
||||
.TP
|
||||
.BR help , " --help" , " -h"
|
||||
Print command usage and the options compiled into the program.
|
||||
.
|
||||
.SH PASSWORD INPUT
|
||||
The password options are accepted by
|
||||
.BR compress ,
|
||||
.BR extract ,
|
||||
.BR list ,
|
||||
.BR test ,
|
||||
and both
|
||||
.B disk
|
||||
subcommands. For
|
||||
.B compress
|
||||
and
|
||||
.BR "disk backup" ,
|
||||
the interactive form asks for confirmation.
|
||||
.
|
||||
.TP
|
||||
.BR -p " " password , " --password " password
|
||||
Read a password directly from the next process argument. This is compatible
|
||||
with older command lines but can expose the password through shell history or
|
||||
process inspection. Prefer one of the non-argv forms below.
|
||||
.
|
||||
.TP
|
||||
.B --password-prompt
|
||||
Read the password from the terminal without echo. This explicit form avoids
|
||||
the optional-argument ambiguity of historical
|
||||
.BR -p .
|
||||
On POSIX, handled interruptions restore the terminal settings saved before the
|
||||
prompt.
|
||||
.
|
||||
.TP
|
||||
.BI --pass-file " file"
|
||||
Read the first line from
|
||||
.IR file .
|
||||
The trailing LF and an optional preceding CR are removed. Empty input, an
|
||||
embedded NUL, or an overlong password is rejected. Protect the file with
|
||||
restrictive permissions and remove it securely when it is no longer needed.
|
||||
.
|
||||
.TP
|
||||
.BI --pass-fd " fd"
|
||||
Read the first line from the inherited numeric file descriptor
|
||||
.IR fd .
|
||||
ZUPT duplicates the descriptor for reading and does not close the caller's
|
||||
original descriptor. The duplicate shares the underlying stream and offset;
|
||||
buffered input may consume beyond the password line, so dedicate the descriptor
|
||||
to this read. The same input validation as
|
||||
.B --pass-file
|
||||
applies.
|
||||
.
|
||||
.SH COMPRESSION OPTIONS
|
||||
These options are accepted by
|
||||
.B compress
|
||||
unless stated otherwise.
|
||||
.
|
||||
.TP
|
||||
.BR -l " " 1..9 , " --level " 1..9
|
||||
Set the compression level. The default is 7. Automatic block sizes are 128 KiB
|
||||
for levels 1-2, 1 MiB for levels 3-4, 2 MiB for levels 5-6, 4 MiB for level 7,
|
||||
and 8 MiB for levels 8-9. Deduplication uses a smaller automatic granularity.
|
||||
.
|
||||
.TP
|
||||
.BR -b " " bytes , " --block " bytes
|
||||
Set the block size in bytes. Values are constrained to the supported range of
|
||||
64 KiB through 256 MiB.
|
||||
.
|
||||
.TP
|
||||
.BR -s , " --store"
|
||||
Store data without compression.
|
||||
.
|
||||
.TP
|
||||
.BR -f , " --fast"
|
||||
Select the fast LZ codec.
|
||||
.
|
||||
.TP
|
||||
.BR --vv , " --vaptvupt"
|
||||
Force the bundled VaptVupt LZ plus ANS codec.
|
||||
.
|
||||
.TP
|
||||
.B --lzhp
|
||||
Force the portable LZHP codec.
|
||||
.
|
||||
.TP
|
||||
.BI --kdf " algorithm"
|
||||
Select the password KDF. The default source-only build supports and defaults to
|
||||
.BR pbkdf2 ,
|
||||
using PBKDF2-SHA256 with 600,000 iterations. A
|
||||
.B WITH_SDK=1
|
||||
build additionally supports
|
||||
.B argon2id
|
||||
and uses it by default. A build must reject a requested KDF that it cannot
|
||||
provide rather than silently changing algorithms.
|
||||
.
|
||||
.TP
|
||||
.BR -c " " text , " --comment " text
|
||||
Store a comment of up to 4095 CLI bytes. In an encrypted archive the comment is
|
||||
encrypted and authenticated with the archive; in a plain archive it receives
|
||||
only the plain archive's non-cryptographic integrity treatment.
|
||||
.
|
||||
.TP
|
||||
.BI --comment-file " file"
|
||||
Read the comment from
|
||||
.IR file .
|
||||
Trailing CR/LF characters are removed.
|
||||
.
|
||||
.TP
|
||||
.BR -D , " --dedup"
|
||||
Enable block-level deduplication. Encrypted deduplicated blocks still use fresh
|
||||
per-block nonces in current archives. DATA and DEDUP_REF frames are bound to
|
||||
their logical positions. An authenticated reference also carries the source
|
||||
position required to authenticate the referenced DATA frame.
|
||||
.
|
||||
.TP
|
||||
.BR -S , " --solid"
|
||||
Use one solid compression stream. Solid mode is single-threaded.
|
||||
.
|
||||
.TP
|
||||
.BR -y , " --force"
|
||||
Allow
|
||||
.B compress
|
||||
to overwrite an existing output whose name does not end in
|
||||
.BR .zupt .
|
||||
This does not relax extraction's no-overwrite policy.
|
||||
.
|
||||
.TP
|
||||
.BR -t " " count , " --threads " count
|
||||
Set the compression or extraction thread count. Zero selects automatic
|
||||
detection; explicit values are limited to 64. This option is accepted by
|
||||
.B compress
|
||||
and
|
||||
.BR extract ,
|
||||
and by disk backup/restore, but not by
|
||||
.B list
|
||||
or
|
||||
.BR test .
|
||||
.
|
||||
.TP
|
||||
.BR -v , " --verbose"
|
||||
Enable additional progress or diagnostic output where the selected command
|
||||
implements it. Authentication failures remain intentionally generic unless
|
||||
verbose diagnostics are requested, reducing the default verbal probe-oracle.
|
||||
.
|
||||
.SH POST-QUANTUM OPTIONS
|
||||
.TP
|
||||
.BI --pq " key"
|
||||
Use the native hybrid envelope: ML-KEM-768 plus X25519 with the archive-key
|
||||
combiner documented in
|
||||
.BR THREAT_MODEL.md .
|
||||
Use the recipient public key for creation and the matching private key for
|
||||
reading. This is the recommended native PQ mode.
|
||||
.
|
||||
.TP
|
||||
.BI --pq-only " key"
|
||||
Use native ML-KEM-768 without the X25519 hedge. Choose it only when a policy
|
||||
requires a single post-quantum KEM. Its keys are generated with
|
||||
.BR "zupt keygen --pq-only" .
|
||||
.
|
||||
.TP
|
||||
.BI --pq-sdk " key"
|
||||
Use the optional system
|
||||
.B libvuptsdk
|
||||
integration. This option is unavailable unless ZUPT was built with
|
||||
.BR WITH_SDK=1 .
|
||||
It is not enabled by the default source-only distribution build.
|
||||
.
|
||||
.TP
|
||||
.BI --pq-box " key"
|
||||
Use the optional system
|
||||
.B libpqvaptvupt
|
||||
sealed-box integration. This option is unavailable unless ZUPT was built
|
||||
with
|
||||
.BR WITH_PQBOX=1 .
|
||||
It is not enabled by the default source-only distribution build.
|
||||
.
|
||||
.SH EXTRACTION OPTIONS
|
||||
.TP
|
||||
.BR -o " " directory , " --output " directory
|
||||
Extract below
|
||||
.IR directory .
|
||||
The directory is created when needed. The default is the current directory.
|
||||
.
|
||||
.PP
|
||||
Extraction also accepts the password, PQ,
|
||||
.BR -v ,
|
||||
and
|
||||
.B -t
|
||||
options described above. Unlike compression, extract/list/test options may
|
||||
appear before or after the single archive operand.
|
||||
.
|
||||
.SH TRUSTED LEGACY READ OPTION
|
||||
.TP
|
||||
.B --allow-legacy-no-ait
|
||||
Permit recovery of a known, trusted historical archive that predates the
|
||||
archive-integrity trailer. By default the validating read commands reject an
|
||||
archive without an AIT, without trusting its unauthenticated header flags. This
|
||||
option emits a downgrade warning and is accepted only by
|
||||
.BR extract ,
|
||||
.BR list ,
|
||||
.BR test ,
|
||||
and
|
||||
.BR "disk restore" .
|
||||
It is rejected by compression and disk backup, which always write a current
|
||||
trailer.
|
||||
.
|
||||
.PP
|
||||
Do not use this option for an archive obtained from untrusted or
|
||||
attacker-writable storage. It does not authenticate the legacy header or footer.
|
||||
After recovery, verify the restored data and create a new current archive.
|
||||
.
|
||||
.SH DISK OPTIONS
|
||||
Disk options must precede the archive and device/file operands. Both disk
|
||||
subcommands accept
|
||||
.BR -l / --level ,
|
||||
.BR -b / --block ,
|
||||
.BR -s / --store ,
|
||||
.BR --vv / --vaptvupt ,
|
||||
.BR --lzhp ,
|
||||
the password input options,
|
||||
.BR --pq ,
|
||||
.BR --pq-only ,
|
||||
.BR -D / --dedup ,
|
||||
.BR -c / --comment ,
|
||||
.BR --comment-file ,
|
||||
.BR --kdf ,
|
||||
.BR -t / --threads ,
|
||||
and
|
||||
.BR -v / --verbose .
|
||||
The optional SDK/PQBOX envelope options and solid mode are not disk-command
|
||||
options.
|
||||
.
|
||||
.PP
|
||||
.B --allow-legacy-no-ait
|
||||
is accepted by disk restore only.
|
||||
.
|
||||
.SH KEY GENERATION OPTIONS
|
||||
.TP
|
||||
.BR -o " " file , " --output " file
|
||||
Write the generated private key or exported public key to
|
||||
.IR file .
|
||||
This option is required.
|
||||
.
|
||||
.TP
|
||||
.BR --pub
|
||||
Export a public key from the private key selected by
|
||||
.BR -k .
|
||||
Use the same mode option as the private key.
|
||||
.
|
||||
.TP
|
||||
.BR -k " " file , " --key " file
|
||||
Read the source private key used by
|
||||
.BR --pub .
|
||||
.
|
||||
.TP
|
||||
.BR --pq-only , " --pqonly"
|
||||
Generate or export a native ML-KEM-768-only key.
|
||||
.
|
||||
.TP
|
||||
.BR --sdk , " --pq-sdk"
|
||||
Generate an SDK-format key through the optional
|
||||
.B libvuptsdk
|
||||
integration.
|
||||
.
|
||||
.TP
|
||||
.BR --box , " --pq-box"
|
||||
Generate a sealed-box key through the optional
|
||||
.B libpqvaptvupt
|
||||
integration.
|
||||
.
|
||||
.PP
|
||||
With no mode option,
|
||||
.B keygen
|
||||
generates or exports the native ML-KEM-768 plus X25519 hybrid format used by
|
||||
.BR --pq .
|
||||
.PP
|
||||
Private-key output uses no-replace creation, POSIX mode 0600, or a Windows
|
||||
current-user-only DACL. A write, flush, or close failure leaves the invalid
|
||||
exclusive partial for manual removal rather than unlinking a possibly replaced
|
||||
pathname. Native ZKEY and ZPQK inputs must have a valid checksum, version,
|
||||
flags, reserved bytes, exact size, and public/private role; malformed or
|
||||
role-confused keys are rejected.
|
||||
.
|
||||
.SH SECURITY NOTES
|
||||
Use encrypted archives when an attacker may modify storage. Plain checksums are
|
||||
not authentication. Keep private keys separate from archives, use high-entropy
|
||||
passwords, and prefer
|
||||
.BR --password-prompt ,
|
||||
.BR --pass-file ,
|
||||
or
|
||||
.B --pass-fd
|
||||
over argv passwords.
|
||||
.PP
|
||||
Archive comments remain untrusted display data even when authenticated. ZUPT
|
||||
renders control bytes without emitting raw terminal-control sequences.
|
||||
.
|
||||
.PP
|
||||
Extract untrusted archives as a dedicated unprivileged user into a new empty
|
||||
local directory. POSIX builds canonicalize the user-selected output root once,
|
||||
then traverse below a pinned directory descriptor with no-follow operations.
|
||||
Windows builds use handle-relative traversal and
|
||||
no-replace publication for normal local Win32 destinations. Extended-length and
|
||||
device-namespace paths, raw UNC output roots, and mapped/network-drive output
|
||||
are not supported in 5.2.8. Cross-compilation and Wine results are not native
|
||||
Windows evidence; the native Windows package gate, including its Unicode round
|
||||
trip, is separate and mandatory before publication.
|
||||
.
|
||||
.PP
|
||||
Current encrypted archives protect ciphertext, canonical framing metadata,
|
||||
logical frame position, and current header/footer metadata. The default AIT
|
||||
requirement prevents a silent downgrade to a trailerless layout; the explicit
|
||||
legacy override intentionally leaves old header/footer metadata outside that
|
||||
authenticated boundary. ZUPT does not protect a compromised endpoint, a
|
||||
disclosed credential, archive rollback or deletion, traffic analysis,
|
||||
compression-length side channels, or every compiler and microarchitectural side
|
||||
channel. Native PQ archive encryption is at-rest encryption, not session
|
||||
forward secrecy.
|
||||
.
|
||||
.SH EXIT STATUS
|
||||
.TP
|
||||
.B 0
|
||||
The requested operation completed successfully.
|
||||
.
|
||||
.TP
|
||||
.B 1
|
||||
Invalid arguments or an operational, format, authentication, integrity, or I/O
|
||||
failure.
|
||||
.
|
||||
.SH FILES
|
||||
.TP
|
||||
.I /usr/bin/zupt
|
||||
Distribution-installed command.
|
||||
.
|
||||
.TP
|
||||
.I /usr/share/bash-completion/completions/zupt
|
||||
Bash completion for the current command.
|
||||
.
|
||||
.TP
|
||||
.I /usr/share/zsh/site-functions/_zupt
|
||||
zsh completion for the current command.
|
||||
.
|
||||
.TP
|
||||
.I /usr/share/fish/vendor_completions.d/zupt.fish
|
||||
fish completion for the current command.
|
||||
.
|
||||
.SH EXAMPLES
|
||||
Create a plain archive:
|
||||
.PP
|
||||
.RS
|
||||
.B zupt compress backup.zupt Documents/
|
||||
.RE
|
||||
.
|
||||
.PP
|
||||
Create and restore a password-protected archive without placing the password in
|
||||
argv:
|
||||
.PP
|
||||
.RS
|
||||
.B zupt compress --password-prompt secure.zupt Documents/
|
||||
.br
|
||||
.B zupt test --password-prompt secure.zupt
|
||||
.br
|
||||
.B zupt extract --password-prompt -o restored secure.zupt
|
||||
.RE
|
||||
.
|
||||
.PP
|
||||
Use a password supplied on file descriptor 3:
|
||||
.PP
|
||||
.RS
|
||||
.B zupt test --pass-fd 3 secure.zupt 3<password-file
|
||||
.RE
|
||||
.
|
||||
.PP
|
||||
Create a native hybrid recipient key and archive:
|
||||
.PP
|
||||
.RS
|
||||
.B zupt keygen -o recipient.key
|
||||
.br
|
||||
.B zupt keygen --pub -k recipient.key -o recipient.pub
|
||||
.br
|
||||
.B zupt compress --pq recipient.pub backup.zupt Documents/
|
||||
.br
|
||||
.B zupt extract --pq recipient.key -o restored backup.zupt
|
||||
.RE
|
||||
.
|
||||
.PP
|
||||
Inspect and verify an archive without extraction:
|
||||
.PP
|
||||
.RS
|
||||
.B zupt info backup.zupt
|
||||
.br
|
||||
.B zupt list backup.zupt
|
||||
.br
|
||||
.B zupt test backup.zupt
|
||||
.RE
|
||||
.
|
||||
.SH COMPATIBILITY
|
||||
The on-disk version byte remains v1.6, with flag-gated 5.2.2 encodings for
|
||||
positional authenticated dedup references and disk-image integrity metadata.
|
||||
Readers since 5.2.2 retain compatibility parsers for the fixed-width disk index
|
||||
and the encrypted-dedup linear AAD sequence published through 5.2.1. The narrow
|
||||
compatibility fixture is an actual v5.2.1 password-encrypted
|
||||
DATA/DATA/REF/DATA disk archive stored as hexadecimal text with source and hash
|
||||
provenance. The candidate lists, tests, extracts, and restores that fixture
|
||||
byte-exact. The exact 5.2.8 candidate must repeat the gate. It does not imply
|
||||
that a 5.2.1 reader
|
||||
accepts every new 5.2.2 archive or that every historical encrypted mode was
|
||||
retested.
|
||||
.
|
||||
.PP
|
||||
The FIPS 203 correction in VaptVupt 5.0.0 changed
|
||||
native
|
||||
.B --pq
|
||||
and
|
||||
.B --pq-only
|
||||
key/archive compatibility relative to releases through 4.2.1. Password and
|
||||
plain archives were not affected by that KEM correction. Consult
|
||||
.B CHANGELOG.md
|
||||
before relying on cross-version recovery, and test restoration before depending
|
||||
on a backup.
|
||||
.
|
||||
.SH LICENSE
|
||||
The ZUPT application and tool code are AGPL-3.0-or-later. The bundled
|
||||
VaptVupt codec source is GPL-3.0-or-later. The two xxHash-derived XXH64 units
|
||||
also carry BSD-2-Clause. The pq-crystals/kyber-derived portions of native
|
||||
ML-KEM also carry CC0-1.0; the x86 BCJ state machine is adapted from
|
||||
public-domain LZMA SDK source. Native X25519 portions adapted from
|
||||
curve25519-donna also carry BSD-3-Clause. Preserve
|
||||
.BR LICENSE ,
|
||||
.BR LICENSE-AGPL-3.0 ,
|
||||
.BR LICENSE-GPL-3.0 ,
|
||||
.BR LICENSE-BSD-2-Clause ,
|
||||
.BR LICENSE-BSD-3-Clause ,
|
||||
.BR LICENSE-CC0-1.0 ,
|
||||
.BR NOTICE ,
|
||||
and
|
||||
.B THIRD-PARTY-NOTICES.md
|
||||
when redistributing the source.
|
||||
Published historical revisions include MIT grants for exact material shipped
|
||||
with those notices; the current license summary does not revoke them. See the
|
||||
5.2.2 licensing erratum in
|
||||
.B CHANGELOG.md
|
||||
for repository evidence.
|
||||
.
|
||||
.SH AUTHORS
|
||||
Cristian Cezar Moisés is the primary upstream author and maintainer.
|
||||
Packaging credits belong in their applicable packaging history and do not
|
||||
imply authorship of the upstream program.
|
||||
.
|
||||
.SH REPORTING BUGS
|
||||
Project issues:
|
||||
.UR https://github.com/cristiancmoises/zupt/issues
|
||||
ZUPT issue tracker
|
||||
.UE
|
||||
.
|
||||
.PP
|
||||
Report security vulnerabilities privately as described in
|
||||
.BR SECURITY.md .
|
||||
.
|
||||
.SH SEE ALSO
|
||||
.BR zupt-gui (1)
|
||||
|
|
@ -3,14 +3,13 @@
|
|||
|
||||
Copyright (C) 2026 Cristian Cezar Moisés <zupt@riseup.net>
|
||||
|
||||
VaptVupt GUI (formerly Zupt GUI; parent application renamed in v3.0.0
|
||||
due to a prior INPI Brasil trademark registration of "Zupt") is free
|
||||
ZUPT GUI 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.
|
||||
|
||||
VaptVupt GUI is distributed in the hope that it will be useful, but
|
||||
ZUPT GUI 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.
|
||||
|
|
@ -23,21 +22,26 @@
|
|||
|
||||
─────────────────────────────────────────────────────────────────────
|
||||
|
||||
PRIOR LICENSE NOTE
|
||||
HISTORICAL LICENSE NOTE
|
||||
|
||||
Earlier copies of this file may have stated "MIT License" — that was
|
||||
a packaging mistake inherited from a template. The GUI source code's
|
||||
SPDX-License-Identifier header has always been AGPL-3.0-or-later;
|
||||
the file-level license here is corrected to match. There is no
|
||||
historical MIT-licensed release of VaptVupt GUI; do not assume MIT
|
||||
grant from any prior tarball that contained this file.
|
||||
Published repository history includes earlier copies of this file
|
||||
under the MIT License. In particular, commit d4660e6539c8b6eeba81751c018217d978fdd618
|
||||
and the v2.2.2 source tag contain an MIT-form gui/LICENSE-GUI. Those
|
||||
permissions remain applicable to the exact material distributed under
|
||||
them; this file does not revoke or reinterpret a historical grant.
|
||||
|
||||
The current GUI source carries AGPL-3.0-or-later SPDX notices. Apply
|
||||
the notices shipped with the exact source revision being used. See the
|
||||
5.2.2 licensing erratum in CHANGELOG.md for the repository record.
|
||||
|
||||
─────────────────────────────────────────────────────────────────────
|
||||
|
||||
COMMERCIAL LICENSING
|
||||
|
||||
The VaptVupt GUI may be commercially relicensed by the author. If
|
||||
you require relief from copyleft terms (proprietary derivatives,
|
||||
closed-source bundling, etc.), contact:
|
||||
The applicable copyright holder may offer controlled first-party
|
||||
rights under a separately executed commercial agreement. This notice
|
||||
is not a commercial license grant, does not revoke a public license,
|
||||
and cannot relicense rights that the licensor does not control. For
|
||||
inquiries, contact:
|
||||
|
||||
sac@securityops.co
|
||||
|
|
|
|||
228
gui/README.md
228
gui/README.md
|
|
@ -1,123 +1,149 @@
|
|||
# VaptVupt GUI
|
||||
# ZUPT GUI
|
||||
|
||||
Desktop application for [vaptvupt](https://git.securityops.co/cristiancmoises/vaptvupt) backup compression with ML-KEM-768 + X25519 post-quantum hybrid encryption.
|
||||
The ZUPT GUI is a Python/Qt front end for the ZUPT 5.2.8 command-line
|
||||
program. It starts the CLI as a subprocess; compression, archive parsing, and
|
||||
cryptography remain in the C program.
|
||||
|
||||
Works on GNU/Linux, BSD, macOS, and Windows.
|
||||
The canonical project repository is
|
||||
`https://github.com/cristiancmoises/zupt`.
|
||||
|
||||
## Install
|
||||
## Requirements
|
||||
|
||||
### Linux (recommended)
|
||||
- a working `zupt` CLI from the same release, available on `PATH` or through
|
||||
the `ZUPT_BIN` environment variable;
|
||||
- Python 3.9 or newer;
|
||||
- PySide6 or PyQt6;
|
||||
- a graphical session for normal use.
|
||||
|
||||
```bash
|
||||
tar xzf vaptvupt-gui.tar.gz && cd vaptvupt-gui
|
||||
./vaptvupt-gui # auto-creates venv, installs PySide6
|
||||
./install.sh --user # adds right-click menu integration
|
||||
Install and test the source-only CLI first:
|
||||
|
||||
```sh
|
||||
make clean
|
||||
make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf 1)" \
|
||||
WITH_SDK=0 WITH_PQBOX=0
|
||||
make WITH_SDK=0 WITH_PQBOX=0 check
|
||||
./zupt --version
|
||||
```
|
||||
|
||||
### Windows
|
||||
The GUI detects the capabilities reported by that binary. Native `--pq` and
|
||||
`--pq-only` are available in the default build. SDK and PQBOX controls are
|
||||
usable only when the CLI was built explicitly against the corresponding system
|
||||
development libraries; no precompiled optional library is shipped in Git.
|
||||
|
||||
**Option A — Installer (recommended):**
|
||||
## Run from the source tree
|
||||
|
||||
Download `VaptVuptGUI-1.3.0-Setup.exe` and run it. Installs to Program Files, adds Start Menu shortcut, desktop shortcut, right-click context menus, and .zupt file association. Includes uninstaller.
|
||||
Using a virtual environment keeps Python packages outside the repository:
|
||||
|
||||
**Option B — Build from source:**
|
||||
|
||||
```cmd
|
||||
cd packaging\windows
|
||||
build-windows.bat
|
||||
```sh
|
||||
python3 -m venv ~/.local/share/zupt-gui-venv
|
||||
~/.local/share/zupt-gui-venv/bin/pip install PySide6
|
||||
ZUPT_BIN="$PWD/zupt" \
|
||||
~/.local/share/zupt-gui-venv/bin/python gui/src/zupt_gui.py
|
||||
```
|
||||
|
||||
Requires Python 3.9+, NSIS 3.x, and a compiled `vaptvupt.exe`.
|
||||
Installing PySide6 can access the Python package index. Do that as an explicit
|
||||
setup step; upstream CLI builds, package builds, and checks do not download
|
||||
dependencies.
|
||||
|
||||
**Option C — Run directly:**
|
||||
For noninteractive checks:
|
||||
|
||||
```cmd
|
||||
pip install PySide6
|
||||
python src\zupt_gui.py
|
||||
```sh
|
||||
python3 gui/src/zupt_gui.py --version
|
||||
python3 gui/src/zupt_gui.py --selftest
|
||||
```
|
||||
|
||||
### macOS / BSD
|
||||
The first command does not prove that a full desktop session works. Test the
|
||||
actual windows and archive operations on every platform for which a GUI package
|
||||
is published.
|
||||
|
||||
```bash
|
||||
pip3 install PySide6
|
||||
python3 src/zupt_gui.py
|
||||
## Functions
|
||||
|
||||
| Area | Function |
|
||||
|---|---|
|
||||
| Keys | Generate keys supported by the selected CLI build |
|
||||
| Compress | Select input, compression settings, and an available encryption mode |
|
||||
| Extract | Detect archive encryption, request the needed credential, and extract |
|
||||
| Verify | Run archive integrity validation without extraction |
|
||||
| Info | Display metadata reported by the CLI |
|
||||
| Disk | Front end for the CLI disk backup and restore commands |
|
||||
|
||||
Disk operations can require additional operating-system privileges. Run only
|
||||
the specific CLI operation that needs them; do not run the whole desktop session
|
||||
as root.
|
||||
|
||||
## Desktop integration
|
||||
|
||||
`gui/install.sh --user` installs the integration supported by that script for
|
||||
the current user. Review the script and its destination paths before running
|
||||
it. File-manager menus and file associations differ across desktops and
|
||||
operating systems and must be tested on the target system.
|
||||
|
||||
## Packaged GUI builds
|
||||
|
||||
Release pages provide only these GUI artifacts after their separate package and
|
||||
installed off-screen GUI/CLI integration gates pass:
|
||||
|
||||
- `zupt-gui_5.2.8_all.deb`;
|
||||
- `zupt-gui-5.2.8-1.noarch.rpm`;
|
||||
- `zupt-gui-5.2.8-1.src.rpm`;
|
||||
- `zupt-gui-5.2.8-portable.zip`.
|
||||
|
||||
The DEB/RPM packages install the Python/Qt source and depend on the matching
|
||||
`zupt` CLI package. The portable ZIP contains source, launchers, icons, licenses,
|
||||
and provenance only; it bundles no Python, Qt, CLI, or compiled runtime. Its
|
||||
gate scans the assembled and extracted trees, enforces an exact safe-member
|
||||
allowlist, and runs the extracted launcher off-screen against the tested CLI.
|
||||
An absent artifact did not pass its gate and must not be inferred from another
|
||||
format's result.
|
||||
|
||||
GUI AppImage, AppDir and Flatpak bundles, and Windows/macOS GUI installers are
|
||||
not promoted by the upstream 5.2.8 release gates.
|
||||
`packaging/build-gui-appimage.sh` is a downstream-only helper and fails unless
|
||||
its operator supplies the exact verified runtime plus a complete
|
||||
license/source-relink notice through `APPIMAGE_RUNTIME_COMPLIANCE_FILE`; that
|
||||
material is included in the resulting AppDir.
|
||||
|
||||
The downstream Windows GUI helper similarly requires
|
||||
`ZUPT_WINDOWS_RUNTIME_NOTICES_DIR` with a `MANIFEST.txt` that identifies
|
||||
the exact Python, PyInstaller, Qt and PySide/PyQt runtime inputs and their
|
||||
notices. It fails unless the directory also has non-empty
|
||||
`PYTHON-NOTICE.txt`, `PYINSTALLER-NOTICE.txt`, `QT-NOTICE.txt`, and either
|
||||
`PYSIDE6-NOTICE.txt` or `PYQT6-NOTICE.txt`. The installer includes that
|
||||
directory together with every ZUPT license and notice. This requirement does
|
||||
not make the untested GUI installer a 5.2.8 release asset. The promoted Windows
|
||||
ZIP and macOS DMG are CLI-only.
|
||||
|
||||
Packaging recipes and scripts under `gui/packaging/` and `packaging/` are build
|
||||
inputs, not evidence that a package has been accepted by a distribution. They
|
||||
must build the CLI from the immutable source tag with
|
||||
`WITH_SDK=0 WITH_PQBOX=0` unless source-built system dependencies are declared.
|
||||
Generated packages, application bundles, and executables must remain outside
|
||||
Git and outside upstream source archives.
|
||||
|
||||
The former standalone `gui/setup.py` sdist/wheel route is intentionally absent:
|
||||
its outputs did not carry the complete project license payload. Use
|
||||
`gui/install.sh` or the reviewed distribution helpers so the AGPL text and
|
||||
artwork provenance are installed with the GUI.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Verify the exact interpreter and CLI used by the GUI:
|
||||
|
||||
```sh
|
||||
python3 -c 'import PySide6.QtWidgets'
|
||||
zupt --version
|
||||
ZUPT_BIN=/absolute/path/to/zupt \
|
||||
python3 gui/src/zupt_gui.py --selftest
|
||||
```
|
||||
|
||||
### AppImage (universal Linux)
|
||||
|
||||
```bash
|
||||
chmod +x VaptVupt-GUI-1.3.0-x86_64.AppImage
|
||||
./VaptVupt-GUI-1.3.0-x86_64.AppImage
|
||||
```
|
||||
|
||||
### Flatpak
|
||||
|
||||
```bash
|
||||
flatpak-builder --install build packaging/flatpak/dev.zupt.gui.yml
|
||||
flatpak run dev.zupt.gui
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
| Tab | Function |
|
||||
|-----|----------|
|
||||
| Keys | Generate ML-KEM-768 + X25519 hybrid keypairs |
|
||||
| Compress | All codecs, levels 1-9, dedup, solid, password, PQ keys |
|
||||
| Extract | Decrypt and extract .zupt archives |
|
||||
| Verify | Check block checksums, view archive metadata |
|
||||
| Disk | Full-disk backup and restore |
|
||||
| About | Version, cryptographic stack, credits |
|
||||
|
||||
All tabs support drag-and-drop. Drop a .zupt file anywhere on the window to extract it. Drop any other file to compress it.
|
||||
|
||||
## System Integration
|
||||
|
||||
### Linux (Nemo / Cinnamon)
|
||||
|
||||
After `./install.sh --user`:
|
||||
- Right-click any file: **Compress with VaptVupt**
|
||||
- Right-click .zupt file: **Extract with VaptVupt**
|
||||
- Double-click .zupt: opens in VaptVupt GUI
|
||||
|
||||
### Windows (after installer)
|
||||
|
||||
- Right-click any file: **Compress with VaptVupt**
|
||||
- Right-click any folder: **Compress with VaptVupt**
|
||||
- Double-click .zupt: opens in VaptVupt GUI
|
||||
- Right-click .zupt: **Verify Integrity**
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
VaptVupt GUI (PySide6, Python)
|
||||
|
|
||||
|-- subprocess.Popen() with streaming stderr
|
||||
|
|
||||
v
|
||||
vaptvupt CLI (Pure C11 binary)
|
||||
ML-KEM-768 + X25519 + AES-256-CTR
|
||||
VaptVupt / LZHP / Store codecs
|
||||
Block deduplication, full-disk backup
|
||||
```
|
||||
|
||||
The GUI calls the vaptvupt CLI binary — all cryptography runs in native C, not Python.
|
||||
|
||||
## Packaging
|
||||
|
||||
| Platform | Format | Tool |
|
||||
|----------|--------|------|
|
||||
| Any | pip | `pip install .` |
|
||||
| Debian/Ubuntu/Mint | .deb | `packaging/deb/control` |
|
||||
| Fedora/RHEL | .rpm | `rpmbuild -ba packaging/rpm/vaptvupt.spec` |
|
||||
| Universal Linux | .AppImage | `packaging/appimage/build-appimage.sh` |
|
||||
| Sandboxed Linux | .flatpak | `packaging/flatpak/dev.zupt.gui.yml` |
|
||||
| Windows | .exe installer | `packaging/windows/build-windows.bat` |
|
||||
| Windows | NSIS .exe | `packaging/windows/zupt-installer.nsi` |
|
||||
|
||||
## Credits
|
||||
|
||||
- **vaptvupt** v5.0.0 — Cristian Cezar Moisés ([github](https://git.securityops.co/cristiancmoises/vaptvupt))
|
||||
If no window appears, run the GUI from a terminal and check the display/Wayland
|
||||
or X11 error. When reporting a problem, include OS and desktop versions, Python
|
||||
and Qt binding versions, `zupt --version`, and the non-sensitive error
|
||||
message. Never attach passwords, private keys, tokens, or confidential archives.
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0-or-later
|
||||
The current GUI source is AGPL-3.0-or-later. Published historical revisions
|
||||
include MIT notices whose grants remain applicable to the exact material
|
||||
distributed under them. See `gui/LICENSE-GUI`, the 5.2.2 licensing erratum in
|
||||
`CHANGELOG.md`, and the repository-level license notices.
|
||||
|
|
|
|||
23
gui/assets/README.md
Normal file
23
gui/assets/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# GUI image assets
|
||||
|
||||
These files are runtime data used by the graphical interface and its packaging;
|
||||
they are not executable code or compiler output.
|
||||
|
||||
| File | Purpose |
|
||||
| --- | --- |
|
||||
| `zupt-icon.png` | Main 48 px application icon used by the GUI and packaging. |
|
||||
| `zupt-128.png` | 128 px application icon. |
|
||||
| `zupt.png` | 256 px application/AppDir icon. |
|
||||
| `zupt.ico` | Windows application icon container. |
|
||||
|
||||
Git provenance: all four files were first added by Cristian Cezar Moisés in
|
||||
ZUPT repository commit `d4660e6539c8b6eeba81751c018217d978fdd618`; the repository
|
||||
records no earlier or external origin. Their current Git blobs are byte-for-byte
|
||||
the same blobs present in that commit. That revision distributed them with MIT
|
||||
license notices, whose permissions remain available for those exact files.
|
||||
|
||||
The current GUI tree also carries AGPL-3.0-or-later notices; apply the license
|
||||
option appropriate to the exact material and revision being redistributed and
|
||||
preserve `gui/LICENSE-GUI` and the repository notices. Their historical
|
||||
filenames are retained because build and desktop-integration files refer to
|
||||
them.
|
||||
200
gui/install.sh
200
gui/install.sh
|
|
@ -1,90 +1,130 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# Install Zupt GUI + desktop integration
|
||||
# Run: sudo ./install.sh (or ./install.sh --user for per-user install)
|
||||
set -e
|
||||
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
USER_INSTALL=0
|
||||
[ "$1" = "--user" ] && USER_INSTALL=1
|
||||
# Install the integrated ZUPT GUI from the checked-out source tree.
|
||||
# This script never downloads Python modules or operating-system packages.
|
||||
|
||||
if [ "$USER_INSTALL" -eq 1 ]; then
|
||||
BIN="$HOME/.local/bin"
|
||||
APPS="$HOME/.local/share/applications"
|
||||
NEMO="$HOME/.local/share/nemo/actions"
|
||||
MIME="$HOME/.local/share/mime"
|
||||
NAUTILUS="$HOME/.local/share/nautilus/scripts"
|
||||
else
|
||||
BIN="/usr/local/bin"
|
||||
APPS="/usr/share/applications"
|
||||
NEMO="/usr/share/nemo/actions"
|
||||
MIME="/usr/share/mime"
|
||||
NAUTILUS=""
|
||||
set -Eeuo pipefail
|
||||
|
||||
die() {
|
||||
printf 'zupt-gui install: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: gui/install.sh [OPTIONS]
|
||||
|
||||
--user install below $HOME/.local
|
||||
--prefix DIR installation prefix (default: /usr/local)
|
||||
--destdir DIR staging root for package builds
|
||||
--legacy-alias install opt-in vaptvupt-gui compatibility symlink
|
||||
-h, --help show this help
|
||||
|
||||
Python 3.9+ and either PySide6 or PyQt6 must already be installed. The
|
||||
zupt CLI must also be installed or selected with ZUPT_BIN at runtime.
|
||||
VAPTVUPT_BIN remains a renamed-era compatibility fallback.
|
||||
EOF
|
||||
}
|
||||
|
||||
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)
|
||||
repo_root=$(cd -- "$script_dir/.." && pwd -P)
|
||||
prefix=/usr/local
|
||||
destdir=${DESTDIR:-}
|
||||
legacy_alias=0
|
||||
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
--user)
|
||||
[[ -n ${HOME:-} ]] || die 'HOME is unset; cannot use --user'
|
||||
prefix=$HOME/.local
|
||||
;;
|
||||
--prefix)
|
||||
(($# >= 2)) || die '--prefix requires a directory'
|
||||
prefix=$2
|
||||
shift
|
||||
;;
|
||||
--destdir)
|
||||
(($# >= 2)) || die '--destdir requires a directory'
|
||||
destdir=$2
|
||||
shift
|
||||
;;
|
||||
--legacy-alias) legacy_alias=1 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown option: $1" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[[ $prefix == /* ]] || die '--prefix must be an absolute path'
|
||||
[[ -z $destdir || $destdir == /* ]] || die '--destdir must be an absolute path'
|
||||
|
||||
bindir=${BINDIR:-$prefix/bin}
|
||||
libexecdir=${LIBEXECDIR:-$prefix/lib/zupt-gui}
|
||||
datadir=${DATADIR:-$prefix/share}
|
||||
|
||||
for source_file in \
|
||||
"$script_dir/src/zupt_gui.py" \
|
||||
"$script_dir/assets/zupt-icon.png" \
|
||||
"$script_dir/packaging/zupt-gui.desktop" \
|
||||
"$repo_root/doc/zupt-gui.1" \
|
||||
"$repo_root/LICENSE" \
|
||||
"$repo_root/LICENSE-AGPL-3.0" \
|
||||
"$script_dir/LICENSE-GUI" \
|
||||
"$script_dir/assets/README.md"; do
|
||||
[[ -f $source_file ]] || die "required source file is missing: $source_file"
|
||||
done
|
||||
|
||||
stage_bindir=$destdir$bindir
|
||||
stage_libexecdir=$destdir$libexecdir
|
||||
stage_datadir=$destdir$datadir
|
||||
install -d -- "$stage_bindir" "$stage_libexecdir" \
|
||||
"$stage_datadir/applications" \
|
||||
"$stage_datadir/icons/hicolor/256x256/apps" \
|
||||
"$stage_datadir/man/man1" \
|
||||
"$stage_datadir/licenses/zupt-gui"
|
||||
|
||||
install -m 0644 -- "$script_dir/src/zupt_gui.py" "$stage_libexecdir/zupt_gui.py"
|
||||
install -m 0644 -- "$script_dir/packaging/zupt-gui.desktop" \
|
||||
"$stage_datadir/applications/zupt-gui.desktop"
|
||||
install -m 0644 -- "$script_dir/assets/zupt-icon.png" \
|
||||
"$stage_datadir/icons/hicolor/256x256/apps/zupt-gui.png"
|
||||
install -m 0644 -- "$repo_root/doc/zupt-gui.1" \
|
||||
"$stage_datadir/man/man1/zupt-gui.1"
|
||||
install -m 0644 -- "$repo_root/LICENSE" \
|
||||
"$stage_datadir/licenses/zupt-gui/LICENSE"
|
||||
install -m 0644 -- "$repo_root/LICENSE-AGPL-3.0" \
|
||||
"$stage_datadir/licenses/zupt-gui/LICENSE-AGPL-3.0"
|
||||
install -m 0644 -- "$script_dir/LICENSE-GUI" \
|
||||
"$stage_datadir/licenses/zupt-gui/LICENSE-GUI"
|
||||
install -m 0644 -- "$script_dir/assets/README.md" \
|
||||
"$stage_datadir/licenses/zupt-gui/ASSET-PROVENANCE.md"
|
||||
|
||||
# Quote the installed module path for a POSIX shell without embedding DESTDIR.
|
||||
quoted_libexec=${libexecdir//\'/\'\\\'\'}
|
||||
launcher_tmp=$(mktemp "${TMPDIR:-/tmp}/zupt-gui-launcher.XXXXXXXX")
|
||||
trap 'rm -f -- "$launcher_tmp"' EXIT HUP INT TERM
|
||||
cat >"$launcher_tmp" <<EOF
|
||||
#!/bin/sh
|
||||
exec python3 '$quoted_libexec/zupt_gui.py' "\$@"
|
||||
EOF
|
||||
install -m 0755 -- "$launcher_tmp" "$stage_bindir/zupt-gui"
|
||||
|
||||
if ((legacy_alias)); then
|
||||
ln -s -- zupt-gui "$stage_bindir/vaptvupt-gui"
|
||||
fi
|
||||
|
||||
mkdir -p "$BIN" "$APPS"
|
||||
|
||||
# ── Install launcher ──
|
||||
cat > "$BIN/zupt-gui" << LAUNCHER
|
||||
#!/bin/bash
|
||||
DIR="$DIR"
|
||||
VENV="\$DIR/.venv"
|
||||
PY="\$VENV/bin/python3"
|
||||
[ ! -x "\$PY" ] && python3 -m venv "\$VENV" && "\$VENV/bin/pip" install PySide6 -q
|
||||
exec "\$PY" "\$DIR/src/zupt_gui.py" "\$@"
|
||||
LAUNCHER
|
||||
chmod +x "$BIN/zupt-gui"
|
||||
echo "Installed: $BIN/zupt-gui"
|
||||
|
||||
# ── Desktop entry ──
|
||||
cp "$DIR/packaging/zupt-gui.desktop" "$APPS/"
|
||||
echo "Installed: $APPS/zupt-gui.desktop"
|
||||
|
||||
# ── Nemo actions (Linux Mint / Cinnamon) ──
|
||||
if [ -d "$(dirname "$NEMO")" ] || [ "$USER_INSTALL" -eq 1 ]; then
|
||||
mkdir -p "$NEMO"
|
||||
cp "$DIR/packaging/desktop-integration/nemo/"*.nemo_action "$NEMO/" 2>/dev/null && \
|
||||
echo "Installed: Nemo right-click actions" || true
|
||||
fi
|
||||
|
||||
# ── Nautilus scripts (GNOME) ──
|
||||
if [ -n "$NAUTILUS" ]; then
|
||||
mkdir -p "$NAUTILUS"
|
||||
cat > "$NAUTILUS/Compress with Zupt" << 'NSCRIPT'
|
||||
#!/bin/bash
|
||||
zupt-gui --compress $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
|
||||
NSCRIPT
|
||||
chmod +x "$NAUTILUS/Compress with Zupt"
|
||||
echo "Installed: Nautilus script"
|
||||
fi
|
||||
|
||||
# ── MIME type for .zupt files ──
|
||||
MIME_XML="$MIME/packages/zupt.xml"
|
||||
if [ ! -f "$MIME_XML" ]; then
|
||||
mkdir -p "$(dirname "$MIME_XML")"
|
||||
cat > "$MIME_XML" << 'MIMEXML'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="application/x-zupt">
|
||||
<comment>Zupt Archive</comment>
|
||||
<glob pattern="*.zupt"/>
|
||||
<icon name="package-x-generic"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
MIMEXML
|
||||
if command -v update-mime-database >/dev/null; then
|
||||
update-mime-database "$MIME" 2>/dev/null
|
||||
if [[ -z $destdir ]]; then
|
||||
if command -v update-desktop-database >/dev/null 2>&1; then
|
||||
update-desktop-database "$datadir/applications" >/dev/null 2>&1 || true
|
||||
fi
|
||||
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
|
||||
gtk-update-icon-cache -q "$datadir/icons/hicolor" >/dev/null 2>&1 || true
|
||||
fi
|
||||
echo "Registered: .zupt MIME type"
|
||||
fi
|
||||
|
||||
# ── Associate .zupt files with zupt-gui ──
|
||||
if command -v xdg-mime >/dev/null; then
|
||||
xdg-mime default zupt-gui.desktop application/x-zupt 2>/dev/null
|
||||
echo "Associated: .zupt files open with Zupt GUI"
|
||||
printf 'Installed zupt-gui below %s%s\n' "$destdir" "$prefix"
|
||||
if ((!legacy_alias)); then
|
||||
printf 'Legacy vaptvupt-gui alias was not installed (use --legacy-alias to opt in).\n'
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Done. Right-click any file in your file manager to see Zupt options."
|
||||
echo "Double-click any .zupt file to open it in Zupt GUI."
|
||||
|
|
|
|||
|
|
@ -1,51 +1,7 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# Build Zupt GUI AppImage
|
||||
# Requires: appimagetool, python3, pip
|
||||
set -e
|
||||
|
||||
APP="zupt-gui"
|
||||
VERSION="1.0.0"
|
||||
APPDIR="${APP}.AppDir"
|
||||
|
||||
rm -rf "$APPDIR" "${APP}-${VERSION}-x86_64.AppImage"
|
||||
mkdir -p "$APPDIR/usr/bin" "$APPDIR/usr/share/zupt-gui" "$APPDIR/usr/share/applications" "$APPDIR/usr/share/icons/hicolor/256x256/apps"
|
||||
|
||||
# Install Python + deps into AppDir
|
||||
python3 -m venv "$APPDIR/usr/python"
|
||||
"$APPDIR/usr/python/bin/pip" install PySide6 --quiet
|
||||
|
||||
# Copy app
|
||||
cp ../../src/zupt_gui.py "$APPDIR/usr/share/zupt-gui/"
|
||||
cp ../../assets/zupt.png "$APPDIR/usr/share/icons/hicolor/256x256/apps/zupt-gui.png" 2>/dev/null || true
|
||||
|
||||
# Create launcher
|
||||
cat > "$APPDIR/AppRun" << 'APPRUN'
|
||||
#!/bin/bash
|
||||
HERE="$(dirname "$(readlink -f "$0")")"
|
||||
export PATH="$HERE/usr/bin:$HERE/usr/python/bin:$PATH"
|
||||
exec python3 "$HERE/usr/share/zupt-gui/zupt_gui.py" "$@"
|
||||
APPRUN
|
||||
chmod +x "$APPDIR/AppRun"
|
||||
|
||||
# Desktop file
|
||||
cat > "$APPDIR/${APP}.desktop" << DESKTOP
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Zupt GUI
|
||||
Comment=Post-Quantum Backup Utility
|
||||
Exec=zupt-gui
|
||||
Icon=zupt-gui
|
||||
Categories=Utility;Archiving;Security;
|
||||
Terminal=false
|
||||
DESKTOP
|
||||
|
||||
# Build AppImage
|
||||
if command -v appimagetool >/dev/null; then
|
||||
ARCH=x86_64 appimagetool "$APPDIR" "${APP}-${VERSION}-x86_64.AppImage"
|
||||
echo "Built: ${APP}-${VERSION}-x86_64.AppImage"
|
||||
else
|
||||
echo "appimagetool not found. Install from https://github.com/AppImage/AppImageKit"
|
||||
echo "AppDir ready at: $APPDIR/"
|
||||
fi
|
||||
# Compatibility entry point for the canonical source-only GUI builder.
|
||||
set -Eeuo pipefail
|
||||
repo_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../../.." && pwd -P)
|
||||
exec "$repo_root/packaging/build-gui-appimage.sh" "$@"
|
||||
|
|
|
|||
|
|
@ -1,94 +1,7 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# Build zupt-gui .deb package
|
||||
set -e
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
VERSION="${VERSION:-2.2.1}"
|
||||
ARCH="all"
|
||||
PKG="zupt-gui_${VERSION}_${ARCH}"
|
||||
ROOT="/tmp/$PKG"
|
||||
|
||||
rm -rf "$ROOT"
|
||||
mkdir -p "$ROOT/DEBIAN" \
|
||||
"$ROOT/usr/bin" \
|
||||
"$ROOT/usr/lib/python3/dist-packages" \
|
||||
"$ROOT/usr/share/applications" \
|
||||
"$ROOT/usr/share/icons/hicolor/256x256/apps" \
|
||||
"$ROOT/usr/share/doc/zupt-gui"
|
||||
|
||||
# Python module
|
||||
install -m 644 src/zupt_gui.py "$ROOT/usr/lib/python3/dist-packages/zupt_gui.py"
|
||||
|
||||
# Launcher
|
||||
cat > "$ROOT/usr/bin/zupt-gui" <<'LAUNCH'
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
sys.path.insert(0, "/usr/lib/python3/dist-packages")
|
||||
from zupt_gui import main
|
||||
sys.exit(main())
|
||||
LAUNCH
|
||||
chmod +x "$ROOT/usr/bin/zupt-gui"
|
||||
|
||||
# Desktop file
|
||||
install -m 644 packaging/zupt-gui.desktop "$ROOT/usr/share/applications/" 2>/dev/null || cat > "$ROOT/usr/share/applications/zupt-gui.desktop" <<'DESK'
|
||||
[Desktop Entry]
|
||||
Name=Zupt GUI
|
||||
GenericName=Post-Quantum Backup Utility
|
||||
Comment=Compress and encrypt files with hybrid PQ crypto
|
||||
Exec=zupt-gui
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;Archiving;Security;
|
||||
Icon=zupt-gui
|
||||
DESK
|
||||
|
||||
# Icon (use a real one if assets exist)
|
||||
if [ -f assets/zupt-256.png ]; then
|
||||
install -m 644 assets/zupt-256.png "$ROOT/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
|
||||
elif [ -d ../assets ] && [ -f ../assets/zupt-256.png ]; then
|
||||
install -m 644 ../assets/zupt-256.png "$ROOT/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
|
||||
else
|
||||
# 1×1 placeholder
|
||||
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' > "$ROOT/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
|
||||
fi
|
||||
|
||||
# Docs
|
||||
install -m 644 README.md "$ROOT/usr/share/doc/zupt-gui/"
|
||||
gzip -9n -c ../CHANGELOG.md > "$ROOT/usr/share/doc/zupt-gui/changelog.gz" 2>/dev/null || true
|
||||
|
||||
cat > "$ROOT/usr/share/doc/zupt-gui/copyright" <<'COPYRIGHT'
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: zupt-gui
|
||||
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+
|
||||
COPYRIGHT
|
||||
|
||||
INSTALLED_SIZE=$(du -sk "$ROOT" | cut -f1)
|
||||
cat > "$ROOT/DEBIAN/control" <<EOF
|
||||
Package: zupt-gui
|
||||
Version: $VERSION
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: $ARCH
|
||||
Depends: python3 (>= 3.9), python3-pyside6, zupt (>= 2.2.0)
|
||||
Maintainer: Cristian Cezar Moisés <zupt@riseup.net>
|
||||
Installed-Size: $INSTALLED_SIZE
|
||||
Homepage: https://git.securityops.co/cristiancmoises/zupt
|
||||
Description: Zupt GUI — Post-Quantum Backup Utility
|
||||
Cross-platform graphical interface for the zupt backup compression
|
||||
utility with post-quantum hybrid encryption (ML-KEM-768 + X25519).
|
||||
.
|
||||
v2.2+ uses libzuptsdk under the hood for HKDF-SHA3 hybrid combiner,
|
||||
32-byte key commitment, HPKE binding (RFC 9180), and anti-fault
|
||||
double-decapsulation. Supports legacy archives via auto-detection.
|
||||
EOF
|
||||
|
||||
dpkg-deb --build --root-owner-group "$ROOT" "/tmp/$PKG.deb"
|
||||
echo "Built: /tmp/$PKG.deb"
|
||||
dpkg-deb --info "/tmp/$PKG.deb" | head -12
|
||||
# Compatibility entry point for the canonical source-only GUI builder.
|
||||
set -Eeuo pipefail
|
||||
repo_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd -P)
|
||||
exec "$repo_root/packaging/build-gui-deb.sh" "$@"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
Package: zupt-gui
|
||||
Version: 1.0.0
|
||||
Version: 5.2.8
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Depends: python3 (>= 3.9), python3-pyside6, zupt (>= 2.1.6)
|
||||
Maintainer: Cristian Cezar Moises <cristian@zupt.dev>
|
||||
Depends: python3 (>= 3.9), python3-pyqt6 | python3-pyside6.qtwidgets, zupt (= 5.2.8)
|
||||
Maintainer: Cristian Cezar Moisés <sac@securityops.co>
|
||||
Homepage: https://github.com/cristiancmoises/zupt
|
||||
Description: Zupt GUI — Post-Quantum Backup Utility
|
||||
Cross-platform graphical interface for the zupt backup compression
|
||||
utility with post-quantum hybrid encryption (ML-KEM-768 + X25519),
|
||||
hardware-adaptive codecs, block-level deduplication, and full-disk
|
||||
backup/restore support.
|
||||
Description: Qt graphical interface for the ZUPT backup utility
|
||||
The GUI creates, inspects, verifies, and extracts .zupt archives through the
|
||||
separately packaged ZUPT command. Native post-quantum modes are available
|
||||
in the baseline build; SDK and PQ-box controls follow CLI capability detection.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
[Nemo Action]
|
||||
Name=Compress with Zupt
|
||||
Comment=Create encrypted .zupt archive
|
||||
Name=Compress with ZUPT
|
||||
Comment=Create a .zupt archive with ZUPT GUI
|
||||
Exec=zupt-gui --compress %F
|
||||
Icon-Name=package-x-generic
|
||||
Icon-Name=zupt-gui
|
||||
Selection=Any
|
||||
Extensions=any;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
[Nemo Action]
|
||||
Name=Extract with Zupt
|
||||
Comment=Decrypt and extract .zupt archive
|
||||
Name=Extract with ZUPT
|
||||
Comment=Extract a .zupt archive with ZUPT GUI
|
||||
Exec=zupt-gui --extract %F
|
||||
Icon-Name=package-x-generic
|
||||
Icon-Name=zupt-gui
|
||||
Selection=S
|
||||
Extensions=zupt;
|
||||
|
|
|
|||
|
|
@ -1,39 +1,42 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2026 Cristian Cezar Moisés
|
||||
|
||||
# Offline build manifest for the integrated source tree. The Qt/Python base and
|
||||
# SDK are resolved by Flatpak before the build; no command below uses pip or
|
||||
# downloads project dependencies.
|
||||
app-id: dev.zupt.gui
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: '24.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
runtime: org.kde.Platform
|
||||
runtime-version: '6.8'
|
||||
sdk: org.kde.Sdk
|
||||
base: io.qt.PySide.BaseApp
|
||||
base-version: '6.8'
|
||||
command: zupt-gui
|
||||
|
||||
finish-args:
|
||||
- --share=ipc
|
||||
- --socket=x11
|
||||
- --socket=fallback-x11
|
||||
- --socket=wayland
|
||||
- --filesystem=home
|
||||
- --device=all # For disk backup (block devices)
|
||||
|
||||
modules:
|
||||
- name: python3-pyside6
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --prefix=/app PySide6
|
||||
|
||||
- name: zupt
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- make
|
||||
- install -Dm755 zupt /app/bin/zupt
|
||||
sources:
|
||||
- type: git
|
||||
url: https://git.securityops.co/cristiancmoises/zupt
|
||||
tag: v2.1.6
|
||||
|
||||
- name: zupt-gui
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- install -Dm755 src/zupt_gui.py /app/bin/zupt-gui
|
||||
- install -Dm644 packaging/zupt-gui.desktop /app/share/applications/dev.zupt.gui.desktop
|
||||
- make -j${FLATPAK_BUILDER_N_JOBS} WITH_SDK=0 WITH_PQBOX=0
|
||||
- make WITH_SDK=0 WITH_PQBOX=0 check
|
||||
- make PREFIX=/app WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 install
|
||||
- install -Dm644 gui/src/zupt_gui.py /app/bin/zupt_gui.py
|
||||
- install -Dm755 packaging/portable/zupt-gui.sh /app/bin/zupt-gui
|
||||
- install -Dm644 gui/packaging/zupt-gui.desktop /app/share/applications/dev.zupt.gui.desktop
|
||||
- sed -i 's/^Icon=.*/Icon=dev.zupt.gui/' /app/share/applications/dev.zupt.gui.desktop
|
||||
- install -Dm644 gui/assets/zupt-icon.png /app/share/icons/hicolor/256x256/apps/dev.zupt.gui.png
|
||||
- install -Dm644 doc/zupt-gui.1 /app/share/man/man1/zupt-gui.1
|
||||
- install -d /app/share/licenses/zupt
|
||||
- install -m 0644 LICENSE LICENSE-AGPL-3.0 LICENSE-GPL-3.0 LICENSE-BSD-2-Clause LICENSE-BSD-3-Clause LICENSE-CC0-1.0 NOTICE THIRD-PARTY-NOTICES.md /app/share/licenses/zupt/
|
||||
- install -d /app/share/licenses/zupt-gui
|
||||
- install -m 0644 LICENSE-AGPL-3.0 /app/share/licenses/zupt-gui/LICENSE-AGPL-3.0
|
||||
- install -m 0644 gui/LICENSE-GUI /app/share/licenses/zupt-gui/LICENSE-GUI
|
||||
- install -m 0644 gui/assets/README.md /app/share/licenses/zupt-gui/ASSET-PROVENANCE.md
|
||||
sources:
|
||||
- type: dir
|
||||
path: .
|
||||
path: ../../..
|
||||
|
|
|
|||
|
|
@ -1,92 +1,102 @@
|
|||
@echo off
|
||||
REM ══════════════════════════════════════════════════
|
||||
REM Zupt GUI — Windows Build Script
|
||||
REM Creates: ZuptGUI-2.1.6-Setup.exe
|
||||
REM
|
||||
REM Prerequisites:
|
||||
REM 1. Python 3.9+ (python.org)
|
||||
REM 2. NSIS 3.x (nsis.sourceforge.io)
|
||||
REM 3. zupt.exe (compiled zupt CLI binary for Windows)
|
||||
REM
|
||||
REM Usage:
|
||||
REM cd packaging\windows
|
||||
REM build-windows.bat
|
||||
REM ══════════════════════════════════════════════════
|
||||
setlocal
|
||||
rem SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
rem Build the ZUPT GUI installer without downloading dependencies.
|
||||
rem
|
||||
rem Prerequisites must already be installed: Python 3.9+, PySide6, PyInstaller,
|
||||
rem Inno Setup 6, and a source-built/tested zupt.exe. The CLI path can be
|
||||
rem selected with ZUPT_CLI_EXE. Final output goes to ZUPT_DIST_DIR,
|
||||
rem which defaults to a directory below %%TEMP%% (outside the Git checkout).
|
||||
rem ZUPT_WINDOWS_RUNTIME_NOTICES_DIR is mandatory and must contain the
|
||||
rem license/notices for the exact Python, PyInstaller, Qt and PySide/PyQt
|
||||
rem runtime files embedded by this local build.
|
||||
|
||||
echo.
|
||||
echo Zupt GUI — Windows Build
|
||||
echo ════════════════════════
|
||||
echo.
|
||||
setlocal EnableExtensions
|
||||
for %%I in ("%~dp0\..\..\..") do set "REPO_ROOT=%%~fI"
|
||||
set "VERSION=%~1"
|
||||
if not defined VERSION set "VERSION=5.2.8"
|
||||
if not defined ZUPT_DIST_DIR set "ZUPT_DIST_DIR=%TEMP%\zupt-release"
|
||||
if not defined ZUPT_CLI_EXE set "ZUPT_CLI_EXE=%REPO_ROOT%\zupt.exe"
|
||||
set "WORK=%TEMP%\zupt-gui-build-%RANDOM%-%RANDOM%"
|
||||
set "RC=1"
|
||||
|
||||
REM ── Step 1: Install Python deps ──
|
||||
echo [1/4] Installing dependencies...
|
||||
pip install PySide6 pyinstaller --quiet --upgrade
|
||||
if errorlevel 1 (
|
||||
echo ERROR: pip install failed. Is Python in PATH?
|
||||
pause
|
||||
exit /b 1
|
||||
where pyinstaller >nul 2>nul || (
|
||||
echo ERROR: PyInstaller is required and is not downloaded by this script.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
where ISCC.exe >nul 2>nul || (
|
||||
echo ERROR: Inno Setup 6 ISCC.exe is required.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
if not exist "%ZUPT_CLI_EXE%" (
|
||||
echo ERROR: source-built CLI not found: %ZUPT_CLI_EXE%>&2
|
||||
goto :cleanup
|
||||
)
|
||||
if not defined ZUPT_WINDOWS_RUNTIME_NOTICES_DIR (
|
||||
echo ERROR: set ZUPT_WINDOWS_RUNTIME_NOTICES_DIR for the exact bundled runtime.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
if not exist "%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%\MANIFEST.txt" (
|
||||
echo ERROR: runtime notice directory must contain MANIFEST.txt.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
for %%I in ("%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%\MANIFEST.txt") do if %%~zI LEQ 0 (
|
||||
echo ERROR: runtime notice MANIFEST.txt must not be empty.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
for %%N in (PYTHON-NOTICE.txt PYINSTALLER-NOTICE.txt QT-NOTICE.txt) do (
|
||||
if not exist "%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%\%%N" (
|
||||
echo ERROR: runtime notice directory is missing %%N.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
for %%I in ("%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%\%%N") do if %%~zI LEQ 0 (
|
||||
echo ERROR: runtime notice %%N must not be empty.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
)
|
||||
set "QT_BINDING_NOTICE_FOUND="
|
||||
for %%N in (PYSIDE6-NOTICE.txt PYQT6-NOTICE.txt) do if exist "%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%\%%N" (
|
||||
for %%I in ("%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%\%%N") do if %%~zI GTR 0 set "QT_BINDING_NOTICE_FOUND=1"
|
||||
)
|
||||
if not defined QT_BINDING_NOTICE_FOUND (
|
||||
echo ERROR: runtime notices need non-empty PYSIDE6-NOTICE.txt or PYQT6-NOTICE.txt.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
|
||||
REM ── Step 2: Build .exe with PyInstaller ──
|
||||
echo [2/4] Building ZuptGUI.exe...
|
||||
if exist dist rmdir /s /q dist
|
||||
if exist build rmdir /s /q build
|
||||
mkdir "%WORK%" || goto :cleanup
|
||||
if not exist "%ZUPT_DIST_DIR%" mkdir "%ZUPT_DIST_DIR%" || goto :cleanup
|
||||
|
||||
pyinstaller --onefile --windowed ^
|
||||
--name "ZuptGUI" ^
|
||||
--icon "..\..\assets\zupt.ico" ^
|
||||
--add-data "..\..\assets\zupt.ico;assets" ^
|
||||
--add-data "..\..\assets\zupt.png;assets" ^
|
||||
"..\..\src\zupt_gui.py"
|
||||
|
||||
if not exist "dist\ZuptGUI.exe" (
|
||||
echo ERROR: PyInstaller build failed.
|
||||
pause
|
||||
exit /b 1
|
||||
"%ZUPT_CLI_EXE%" version >"%WORK%\cli-version.txt" 2>&1 || goto :cleanup
|
||||
findstr /b /c:"zupt %VERSION%" "%WORK%\cli-version.txt" >nul || (
|
||||
echo ERROR: CLI version does not match %VERSION%.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
echo Built: dist\ZuptGUI.exe
|
||||
"%ZUPT_CLI_EXE%" help >nul 2>&1 || goto :cleanup
|
||||
|
||||
REM ── Step 3: Check for zupt.exe ──
|
||||
echo [3/4] Checking for zupt.exe...
|
||||
if not exist "zupt.exe" (
|
||||
echo.
|
||||
echo WARNING: zupt.exe not found in this directory.
|
||||
echo The installer needs zupt.exe to bundle the CLI tool.
|
||||
echo Options:
|
||||
echo a) Copy zupt.exe here and re-run this script
|
||||
echo b) Build zupt from source with MSYS2/MinGW:
|
||||
echo pacman -S mingw-w64-x86_64-gcc make
|
||||
echo cd zupt-2.1.6 ^&^& make
|
||||
echo cp zupt.exe packaging/windows/
|
||||
echo.
|
||||
)
|
||||
pyinstaller --noconfirm --clean --onefile --windowed ^
|
||||
--name zupt-gui ^
|
||||
--icon "%REPO_ROOT%\gui\assets\zupt.ico" ^
|
||||
--add-data "%REPO_ROOT%\gui\assets\zupt.ico;assets" ^
|
||||
--add-data "%REPO_ROOT%\gui\assets\zupt-icon.png;assets" ^
|
||||
--distpath "%WORK%\dist" ^
|
||||
--workpath "%WORK%\build" ^
|
||||
--specpath "%WORK%" ^
|
||||
"%REPO_ROOT%\gui\src\zupt_gui.py" || goto :cleanup
|
||||
|
||||
REM ── Step 4: Build NSIS installer ──
|
||||
echo [4/4] Building installer...
|
||||
where makensis >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo NSIS not found. Install from: https://nsis.sourceforge.io
|
||||
echo Then run: makensis zupt-installer.nsi
|
||||
echo.
|
||||
echo Standalone exe ready at: dist\ZuptGUI.exe
|
||||
pause
|
||||
exit /b 0
|
||||
)
|
||||
set "GUI_EXE=%WORK%\dist\zupt-gui.exe"
|
||||
if not exist "%GUI_EXE%" goto :cleanup
|
||||
set "ZUPT_BIN=%ZUPT_CLI_EXE%"
|
||||
"%GUI_EXE%" --version >"%WORK%\gui-version.txt" 2>&1 || goto :cleanup
|
||||
findstr /b /c:"zupt-gui %VERSION%" "%WORK%\gui-version.txt" >nul || goto :cleanup
|
||||
|
||||
makensis zupt-installer.nsi
|
||||
if errorlevel 1 (
|
||||
echo ERROR: NSIS build failed.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
ISCC.exe "/DAppVersion=%VERSION%" "/DGuiExecutable=%GUI_EXE%" ^
|
||||
"/DCliExecutable=%ZUPT_CLI_EXE%" "/DBuildOutputDir=%ZUPT_DIST_DIR%" ^
|
||||
"/DRuntimeNoticesDir=%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%" ^
|
||||
"%REPO_ROOT%\packaging\windows\zupt-gui.iss" || goto :cleanup
|
||||
|
||||
echo.
|
||||
echo ════════════════════════════════════════════
|
||||
echo Build complete!
|
||||
echo Standalone: dist\ZuptGUI.exe
|
||||
echo Installer: ZuptGUI-2.1.6-Setup.exe
|
||||
echo ════════════════════════════════════════════
|
||||
echo.
|
||||
pause
|
||||
if not exist "%ZUPT_DIST_DIR%\ZUPT-Setup-%VERSION%.exe" goto :cleanup
|
||||
echo PASS: built %ZUPT_DIST_DIR%\ZUPT-Setup-%VERSION%.exe
|
||||
set "RC=0"
|
||||
|
||||
:cleanup
|
||||
if exist "%WORK%" rmdir /s /q "%WORK%"
|
||||
endlocal & exit /b %RC%
|
||||
|
|
|
|||
|
|
@ -1,132 +0,0 @@
|
|||
; Zupt GUI — NSIS Installer Script
|
||||
; Builds: ZuptGUI-Setup.exe
|
||||
;
|
||||
; Prerequisites on the build machine:
|
||||
; 1. NSIS 3.x installed (https://nsis.sourceforge.io)
|
||||
; 2. Run build-windows.bat first to create dist/ZuptGUI.exe
|
||||
; 3. Place zupt.exe in this directory
|
||||
; 4. Then: makensis zupt-installer.nsi
|
||||
|
||||
!include "MUI2.nsh"
|
||||
!include "FileFunc.nsh"
|
||||
|
||||
; ── Config ──
|
||||
!define APPNAME "Zupt"
|
||||
!define APPVERSION "2.1.6"
|
||||
!define GUIVERSION "1.0.0"
|
||||
!define PUBLISHER "Cristian Cezar Moises"
|
||||
!define HELPURL "https://github.com/cristiancmoises/zupt"
|
||||
!define EXE "ZuptGUI.exe"
|
||||
!define CLI "zupt.exe"
|
||||
|
||||
Name "${APPNAME} ${APPVERSION}"
|
||||
OutFile "ZuptGUI-${APPVERSION}-Setup.exe"
|
||||
InstallDir "$PROGRAMFILES\${APPNAME}"
|
||||
InstallDirRegKey HKLM "Software\${APPNAME}" "InstallDir"
|
||||
RequestExecutionLevel admin
|
||||
|
||||
; ── UI ──
|
||||
!define MUI_ICON "..\..\assets\zupt.ico"
|
||||
!define MUI_UNICON "..\..\assets\zupt.ico"
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_WELCOMEPAGE_TITLE "Install ${APPNAME} ${APPVERSION}"
|
||||
!define MUI_WELCOMEPAGE_TEXT "Post-quantum backup compression with ML-KEM-768 + X25519 hybrid encryption.$\r$\n$\r$\nThis will install the Zupt GUI and CLI tools."
|
||||
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!insertmacro MUI_PAGE_LICENSE "..\..\LICENSE"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
; ── Install ──
|
||||
Section "Install"
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
; Copy files
|
||||
File "dist\${EXE}"
|
||||
File "${CLI}"
|
||||
File "..\..\assets\zupt.ico"
|
||||
File "..\..\LICENSE"
|
||||
File "..\..\README.md"
|
||||
|
||||
; Write uninstaller
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
|
||||
; Start Menu
|
||||
CreateDirectory "$SMPROGRAMS\${APPNAME}"
|
||||
CreateShortcut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\${EXE}" "" "$INSTDIR\zupt.ico"
|
||||
CreateShortcut "$SMPROGRAMS\${APPNAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
|
||||
|
||||
; Desktop shortcut
|
||||
CreateShortcut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\${EXE}" "" "$INSTDIR\zupt.ico"
|
||||
|
||||
; Add to PATH (so zupt.exe is available system-wide)
|
||||
EnVar::AddValue "PATH" "$INSTDIR"
|
||||
|
||||
; Register .zupt file association
|
||||
WriteRegStr HKCR ".zupt" "" "ZuptArchive"
|
||||
WriteRegStr HKCR "ZuptArchive" "" "Zupt Archive"
|
||||
WriteRegStr HKCR "ZuptArchive\DefaultIcon" "" "$INSTDIR\zupt.ico"
|
||||
WriteRegStr HKCR "ZuptArchive\shell\open\command" "" '"$INSTDIR\${EXE}" --extract "%1"'
|
||||
WriteRegStr HKCR "ZuptArchive\shell\verify\command" "" '"$INSTDIR\${CLI}" test "%1"'
|
||||
WriteRegStr HKCR "ZuptArchive\shell\verify" "" "Verify Integrity"
|
||||
|
||||
; Right-click "Compress with Zupt" on any file
|
||||
WriteRegStr HKCR "*\shell\ZuptCompress" "" "Compress with Zupt"
|
||||
WriteRegStr HKCR "*\shell\ZuptCompress\Icon" "" "$INSTDIR\zupt.ico"
|
||||
WriteRegStr HKCR "*\shell\ZuptCompress\command" "" '"$INSTDIR\${EXE}" --compress "%1"'
|
||||
|
||||
; Right-click on directories
|
||||
WriteRegStr HKCR "Directory\shell\ZuptCompress" "" "Compress with Zupt"
|
||||
WriteRegStr HKCR "Directory\shell\ZuptCompress\Icon" "" "$INSTDIR\zupt.ico"
|
||||
WriteRegStr HKCR "Directory\shell\ZuptCompress\command" "" '"$INSTDIR\${EXE}" --compress "%1"'
|
||||
|
||||
; Add/Remove Programs entry
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME} — Post-Quantum Backup"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$INSTDIR\Uninstall.exe"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayIcon" "$INSTDIR\zupt.ico"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "Publisher" "${PUBLISHER}"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayVersion" "${APPVERSION}"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "URLInfoAbout" "${HELPURL}"
|
||||
|
||||
; Calculate installed size
|
||||
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||
IntFmt $0 "0x%08X" $0
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "EstimatedSize" "$0"
|
||||
SectionEnd
|
||||
|
||||
; ── Uninstall ──
|
||||
Section "Uninstall"
|
||||
; Remove files
|
||||
Delete "$INSTDIR\${EXE}"
|
||||
Delete "$INSTDIR\${CLI}"
|
||||
Delete "$INSTDIR\zupt.ico"
|
||||
Delete "$INSTDIR\LICENSE"
|
||||
Delete "$INSTDIR\README.md"
|
||||
Delete "$INSTDIR\Uninstall.exe"
|
||||
RMDir "$INSTDIR"
|
||||
|
||||
; Remove shortcuts
|
||||
Delete "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk"
|
||||
Delete "$SMPROGRAMS\${APPNAME}\Uninstall.lnk"
|
||||
RMDir "$SMPROGRAMS\${APPNAME}"
|
||||
Delete "$DESKTOP\${APPNAME}.lnk"
|
||||
|
||||
; Remove from PATH
|
||||
EnVar::DeleteValue "PATH" "$INSTDIR"
|
||||
|
||||
; Remove file associations
|
||||
DeleteRegKey HKCR ".zupt"
|
||||
DeleteRegKey HKCR "ZuptArchive"
|
||||
DeleteRegKey HKCR "*\shell\ZuptCompress"
|
||||
DeleteRegKey HKCR "Directory\shell\ZuptCompress"
|
||||
|
||||
; Remove Add/Remove Programs entry
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
|
||||
DeleteRegKey HKLM "Software\${APPNAME}"
|
||||
SectionEnd
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Zupt GUI
|
||||
GenericName=Post-Quantum Backup
|
||||
Comment=Compress, encrypt, and backup with quantum-resistant cryptography
|
||||
Exec=zupt-gui
|
||||
Name=ZUPT GUI
|
||||
GenericName=Backup and Compression Utility
|
||||
Comment=Create, inspect, verify, and extract ZUPT archives
|
||||
Exec=zupt-gui %f
|
||||
Icon=zupt-gui
|
||||
Categories=Utility;Archiving;Security;
|
||||
Keywords=backup;compress;encrypt;quantum;zupt;
|
||||
Categories=Utility;Archiving;Compression;
|
||||
Keywords=backup;archive;compression;encryption;post-quantum;zupt;
|
||||
Terminal=false
|
||||
StartupNotify=true
|
||||
MimeType=application/x-zupt;
|
||||
|
|
|
|||
34
gui/setup.py
34
gui/setup.py
|
|
@ -1,34 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="zupt-gui",
|
||||
version="1.1.1",
|
||||
description="Zupt GUI — Cross-Platform Post-Quantum Backup Utility",
|
||||
long_description=open("README.md").read(),
|
||||
long_description_content_type="text/markdown",
|
||||
author="Cristian Cezar Moisés",
|
||||
url="https://git.securityops.co/cristiancmoises/zupt",
|
||||
license="AGPL-3.0-or-later",
|
||||
packages=find_packages(where="src"),
|
||||
package_dir={"": "src"},
|
||||
py_modules=["zupt_gui"],
|
||||
python_requires=">=3.9",
|
||||
install_requires=["PySide6>=6.5"],
|
||||
entry_points={
|
||||
"console_scripts": ["zupt-gui=zupt_gui:main"],
|
||||
"gui_scripts": ["zupt-gui=zupt_gui:main"],
|
||||
},
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: X11 Applications :: Qt",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Security :: Cryptography",
|
||||
"Topic :: System :: Archiving :: Compression",
|
||||
],
|
||||
)
|
||||
File diff suppressed because it is too large
Load diff
65
gui/zupt-gui
65
gui/zupt-gui
|
|
@ -1,46 +1,31 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
VENV="$DIR/.venv"
|
||||
PY="$VENV/bin/python3"
|
||||
PIP="$VENV/bin/pip"
|
||||
GUI="$DIR/src/zupt_gui.py"
|
||||
# Source-tree launcher for ZUPT GUI.
|
||||
# It performs no package installation and never downloads dependencies.
|
||||
set -Eeuo pipefail
|
||||
|
||||
# ─── System deps (Qt xcb needs these on Debian/Mint/Ubuntu) ───
|
||||
NEED_APT=0
|
||||
for pkg in libxcb-cursor0 libxcb-xinerama0 libxkbcommon-x11-0 libegl1 python3-full; do
|
||||
dpkg -s "$pkg" >/dev/null 2>&1 || NEED_APT=1
|
||||
done
|
||||
if [ "$NEED_APT" -eq 1 ]; then
|
||||
echo "Installing system dependencies..."
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y python3-full python3-venv \
|
||||
libxcb-cursor0 libxcb-xinerama0 libxkbcommon-x11-0 libegl1 2>/dev/null
|
||||
fi
|
||||
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)
|
||||
gui=$script_dir/src/zupt_gui.py
|
||||
[[ -f $gui ]] || {
|
||||
printf 'zupt-gui: GUI source is missing: %s\n' "$gui" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ─── Venv ───
|
||||
if [ ! -x "$PY" ]; then
|
||||
rm -rf "$VENV"
|
||||
python3 -m venv "$VENV"
|
||||
fi
|
||||
if ! "$PY" -c "import PySide6" 2>/dev/null; then
|
||||
echo "Installing PySide6..."
|
||||
"$PIP" install --upgrade pip -q 2>/dev/null
|
||||
"$PIP" install PySide6 -q
|
||||
fi
|
||||
|
||||
# ─── Find zupt — local build FIRST, then system ───
|
||||
if [ -z "$ZUPT_BIN" ]; then
|
||||
# Check project tree first (gui/ is inside zupt-2.1.6/)
|
||||
for p in "$DIR/../zupt" "$DIR/../../zupt" "$DIR/zupt"; do
|
||||
[ -x "$p" ] && export ZUPT_BIN="$(readlink -f "$p")" && break
|
||||
done
|
||||
# Then system PATH
|
||||
if [ -z "$ZUPT_BIN" ]; then
|
||||
p="$(command -v zupt 2>/dev/null)"
|
||||
[ -x "$p" ] && export ZUPT_BIN="$p"
|
||||
if [[ -z ${ZUPT_BIN:-} ]]; then
|
||||
if [[ -n ${VAPTVUPT_BIN:-} ]]; then
|
||||
export ZUPT_BIN=$VAPTVUPT_BIN
|
||||
elif [[ -x $script_dir/../zupt ]]; then
|
||||
export ZUPT_BIN=$script_dir/../zupt
|
||||
elif command -v zupt >/dev/null 2>&1; then
|
||||
ZUPT_BIN=$(command -v zupt)
|
||||
export ZUPT_BIN
|
||||
elif [[ -x $script_dir/../vaptvupt ]]; then
|
||||
export ZUPT_BIN=$script_dir/../vaptvupt
|
||||
elif command -v vaptvupt >/dev/null 2>&1; then
|
||||
ZUPT_BIN=$(command -v vaptvupt)
|
||||
export ZUPT_BIN
|
||||
fi
|
||||
fi
|
||||
|
||||
exec "$PY" "$GUI" "$@"
|
||||
exec python3 "$gui" "$@"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* VaptVupt — Zupt Integration API
|
||||
* VaptVupt — VaptVupt Integration API
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* Copyright 2026 Cristian.
|
||||
*
|
||||
* ZUPT-COMPAT: This is the API that Zupt calls. It wraps the internal
|
||||
* EMBED-COMPAT: This is the API that a host application calls. It wraps the internal
|
||||
* VaptVupt API with sensible defaults for backup workloads:
|
||||
* - Checksum always enabled (data integrity is critical for backups)
|
||||
* - Adaptive window selection (auto-detect optimal wlog per file)
|
||||
|
|
|
|||
219
include/zupt.h
219
include/zupt.h
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Zupt — Backup-oriented compression with AES-256 encryption
|
||||
* ZUPT — Backup-oriented compression with AES-256 encryption
|
||||
* Copyright (c) 2026 Cristian Cezar Moisés
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
|
@ -15,12 +15,79 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
#include <wchar.h>
|
||||
#define ZUPT_PATH_SEP '\\'
|
||||
#define zupt_mkdir(p) _mkdir(p)
|
||||
|
||||
static inline wchar_t *zupt_win_utf8_to_wide_alloc(const char *text) {
|
||||
if (!text) return NULL;
|
||||
int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
||||
text, -1, NULL, 0);
|
||||
if (length <= 0) return NULL;
|
||||
wchar_t *wide = (wchar_t *)malloc((size_t)length * sizeof(wchar_t));
|
||||
if (!wide || !MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
||||
text, -1, wide, length)) {
|
||||
free(wide);
|
||||
return NULL;
|
||||
}
|
||||
return wide;
|
||||
}
|
||||
|
||||
static inline char *zupt_win_wide_to_utf8_alloc(const wchar_t *text) {
|
||||
if (!text) return NULL;
|
||||
int length = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
||||
text, -1, NULL, 0, NULL, NULL);
|
||||
if (length <= 0) return NULL;
|
||||
char *utf8 = (char *)malloc((size_t)length);
|
||||
if (!utf8 || !WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
||||
text, -1, utf8, length, NULL, NULL)) {
|
||||
free(utf8);
|
||||
return NULL;
|
||||
}
|
||||
return utf8;
|
||||
}
|
||||
|
||||
static inline FILE *zupt_win_fopen_utf8(const char *path, const char *mode) {
|
||||
wchar_t *wide_path = zupt_win_utf8_to_wide_alloc(path);
|
||||
wchar_t *wide_mode = zupt_win_utf8_to_wide_alloc(mode);
|
||||
if (!wide_path || !wide_mode) {
|
||||
free(wide_path);
|
||||
free(wide_mode);
|
||||
return NULL;
|
||||
}
|
||||
FILE *stream = _wfopen(wide_path, wide_mode);
|
||||
free(wide_path);
|
||||
free(wide_mode);
|
||||
return stream;
|
||||
}
|
||||
|
||||
static inline DWORD zupt_win_get_attributes_utf8(const char *path) {
|
||||
wchar_t *wide = zupt_win_utf8_to_wide_alloc(path);
|
||||
if (!wide) return INVALID_FILE_ATTRIBUTES;
|
||||
DWORD attributes = GetFileAttributesW(wide);
|
||||
free(wide);
|
||||
return attributes;
|
||||
}
|
||||
|
||||
static inline int zupt_win_mkdir_utf8(const char *path) {
|
||||
wchar_t *wide = zupt_win_utf8_to_wide_alloc(path);
|
||||
if (!wide) return -1;
|
||||
int result = _wmkdir(wide);
|
||||
free(wide);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Project path strings are UTF-8 on every platform. Call this wrapper
|
||||
* explicitly; never rewrite the C library's fopen in consumer code. */
|
||||
static inline FILE *zupt_fopen_path(const char *path, const char *mode) {
|
||||
return zupt_win_fopen_utf8(path, mode);
|
||||
}
|
||||
#define zupt_mkdir(p) zupt_win_mkdir_utf8(p)
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
|
@ -28,32 +95,38 @@
|
|||
#include <unistd.h>
|
||||
#define ZUPT_PATH_SEP '/'
|
||||
#define zupt_mkdir(p) mkdir(p, 0755)
|
||||
static inline FILE *zupt_fopen_path(const char *path, const char *mode) {
|
||||
return fopen(path, mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ─── Product identity ─────────────────────────────────────────────
|
||||
*
|
||||
* v3.0.0 (INPI Brasil trademark rename):
|
||||
* - Product name is now "VaptVupt" (was "Zupt"). The earlier name
|
||||
* conflicted with a software trademark already registered at INPI
|
||||
* Brasil under "Zupt".
|
||||
* - File extension stays `.zupt` for archive-format continuity:
|
||||
* v1.0–v2.4.x archives remain readable, the magic bytes
|
||||
* `\x5A\x55\x50\x54\x1A\x00` ("ZUPT" + sub-version) are unchanged.
|
||||
* - C identifier prefix stays `zupt_` / `ZUPT_` for ABI continuity
|
||||
* with libzuptsdk and existing callers. Only user-visible strings
|
||||
* (binary name, banner, help text, package names) change.
|
||||
* - The binary is now `vaptvupt`. Distro packages may ship a
|
||||
* compatibility symlink `zupt -> vaptvupt` for one major version.
|
||||
* v5.2.2 (product identity restored):
|
||||
* - The public product and primary command are again "ZUPT" and `zupt`.
|
||||
* - On-disk compatibility is deliberately unchanged: magic remains
|
||||
* "ZUPT", the archive extension remains .zupt, and format version
|
||||
* remains 1.6.
|
||||
* - Internal zupt_* symbols, SDK identifiers, codec IDs, and the bundled
|
||||
* VaptVupt codec ABI remain unchanged.
|
||||
* - Distributors may offer `vaptvupt -> zupt` only as an explicit
|
||||
* compatibility alias for scripts written for releases 3.0.0--5.2.1.
|
||||
*/
|
||||
#define ZUPT_PRODUCT_NAME "VaptVupt"
|
||||
#define ZUPT_PRODUCT_NAME_LC "vaptvupt" /* lowercase: binary name */
|
||||
#define ZUPT_PRODUCT_NAME "ZUPT"
|
||||
#define ZUPT_PRODUCT_NAME_LC "zupt" /* lowercase: binary name */
|
||||
#define ZUPT_PRODUCT_EXTENSION ".zupt" /* on-disk archive extension (kept stable) */
|
||||
#define ZUPT_PRODUCT_TAGLINE "Post-quantum backup compression"
|
||||
|
||||
#define ZUPT_VERSION_STRING "5.0.0"
|
||||
/* v5.2.3 corrects release packaging and CI; archive format remains v1.6. */
|
||||
/* v5.2.4 makes package metadata checks CRLF-safe; format remains v1.6. */
|
||||
/* v5.2.5 corrects the OBS service harness cwd; format remains v1.6. */
|
||||
/* v5.2.6 corrects native release-gate portability; format remains v1.6. */
|
||||
/* v5.2.7 corrects native test-harness portability; format remains v1.6. */
|
||||
/* v5.2.8 hardens three path-race boundaries; format remains v1.6. */
|
||||
#define ZUPT_VERSION_STRING "5.2.8"
|
||||
/* Vendored codec release (upstream tag) — single source for display strings.
|
||||
* The codec's own VV_VERSION_* is its internal API version, not the release. */
|
||||
#define ZUPT_CODEC_RELEASE "2.60.4"
|
||||
#define ZUPT_CODEC_RELEASE "2.65.3"
|
||||
#define ZUPT_FORMAT_MAJOR 1
|
||||
#define ZUPT_FORMAT_MINOR 6
|
||||
|
||||
|
|
@ -67,10 +140,13 @@
|
|||
* to keep the field stable across format-version transitions. Both bytes are
|
||||
* structurally validated by read_footer().
|
||||
*
|
||||
* Read path falls back to v1.4 layout (no trailer) when the footer magic is
|
||||
* found at EOF-32 instead of EOF-64. */
|
||||
* The reader can identify a legacy v1.4 layout at EOF-32, but refuses it by
|
||||
* default because the missing trailer is indistinguishable from an integrity
|
||||
* downgrade. Trusted old archives require --allow-legacy-no-ait. */
|
||||
#define ZUPT_AIT_SIZE 32
|
||||
#define ZUPT_AIT_MAC_INPUT_LEN (sizeof(zupt_archive_header_t) + 24)
|
||||
#define ZUPT_ARCHIVE_HEADER_SIZE 64u
|
||||
#define ZUPT_FOOTER_SIZE 32u
|
||||
#define ZUPT_AIT_MAC_INPUT_LEN (ZUPT_ARCHIVE_HEADER_SIZE + 24u)
|
||||
|
||||
#define ZUPT_MAGIC_0 0x5A
|
||||
#define ZUPT_MAGIC_1 0x55
|
||||
|
|
@ -83,6 +159,11 @@
|
|||
|
||||
#define ZUPT_MAX_PATH 4096
|
||||
#define ZUPT_MAX_FILES 2000000
|
||||
/* A decoded index entry contains a fixed-size path buffer. Cap aggregate
|
||||
* allocation independently of the wire count so a compact malicious index
|
||||
* cannot request several gigabytes of zeroed memory. */
|
||||
#define ZUPT_MAX_INDEX_ALLOC_BYTES (256u * 1024u * 1024u)
|
||||
#define ZUPT_MIN_INDEX_ENTRY_BYTES 47u
|
||||
#define ZUPT_DEFAULT_BLOCK_SZ (4 * 1024 * 1024)
|
||||
#define ZUPT_MIN_BLOCK_SZ (64 * 1024)
|
||||
#define ZUPT_MAX_BLOCK_SZ (256 * 1024 * 1024)
|
||||
|
|
@ -97,12 +178,14 @@
|
|||
#define ZUPT_FLAG_DEDUP (1u << 7) /* Block-level deduplication enabled */
|
||||
#define ZUPT_FLAG_AAD_SEQ (1u << 8) /* MAC binds block_seq as AAD (anti-reorder) */
|
||||
#define ZUPT_FLAG_AAD_PREFACE (1u << 9) /* v1.6: MAC also binds per-block frame preface (F-09) */
|
||||
#define ZUPT_FLAG_AUTH_DEDUP_REFS (1u << 10) /* Dedup offsets carry per-block authentication */
|
||||
#define ZUPT_FLAG_DISK_CONTENT_HASH (1u << 11) /* Disk index hashes restored bytes */
|
||||
|
||||
/* Encryption types (stored in encryption header block) */
|
||||
#define ZUPT_ENC_PBKDF2 0x01 /* Password-based: PBKDF2 → AES-256-CTR + HMAC */
|
||||
#define ZUPT_ENC_PQ_HYBRID 0x02 /* ML-KEM-768 + X25519 hybrid KEM (legacy XOR+SHA3) */
|
||||
#define ZUPT_ENC_PQ_SDK_V2 0x03 /* libzuptsdk v2 header: HKDF combiner + commitment + HPKE binding */
|
||||
#define ZUPT_ENC_PW_ARGON2 0x04 /* Password-based via libzuptsdk: Argon2id + XChaCha20-Poly1305 */
|
||||
#define ZUPT_ENC_PQ_SDK_V2 0x03 /* libvuptsdk v2 header: HKDF combiner + commitment + HPKE binding */
|
||||
#define ZUPT_ENC_PW_ARGON2 0x04 /* Password-based via libvuptsdk: Argon2id + XChaCha20-Poly1305 */
|
||||
#define ZUPT_ENC_PQ_BOX_V1 0x05 /* libpqvaptvupt sealed box: HKDF-SHA256 domain-separated combiner */
|
||||
#define ZUPT_ENC_PQ_ONLY 0x06 /* Full post-quantum: ML-KEM-768 only (no X25519), SHA3-512 KDF (v4.2.0) */
|
||||
|
||||
|
|
@ -123,12 +206,12 @@
|
|||
* descriptor is covered by the archive-integrity trailer (F-08), so it
|
||||
* cannot be stripped or forged without failing authentication.
|
||||
*
|
||||
* Profile 0 (implicit, absent byte) == the historical libzuptsdk
|
||||
* Profile 0 (implicit, absent byte) == the historical libvuptsdk
|
||||
* "MODERATE" Argon2id preset reached via zuptsdk_easy_derive_key.
|
||||
* Profile 1 is the same derivation with the descriptor made explicit so
|
||||
* future profiles (should the cost change) get distinct IDs. */
|
||||
#define ZUPT_ARGON2_PROFILE_LEGACY 0x00 /* implicit: pre-3.4.0, no descriptor byte */
|
||||
#define ZUPT_ARGON2_PROFILE_MODERATE 0x01 /* explicit: libzuptsdk MODERATE preset */
|
||||
#define ZUPT_ARGON2_PROFILE_MODERATE 0x01 /* explicit: libvuptsdk MODERATE preset */
|
||||
#define ZUPT_ARGON2_HDR_LEN_V1 33 /* [type|salt16|nonce16] */
|
||||
#define ZUPT_ARGON2_HDR_LEN_V2 34 /* + [profile1] */
|
||||
|
||||
|
|
@ -136,7 +219,7 @@
|
|||
#define ZUPT_BLOCK_DATA 0x00
|
||||
#define ZUPT_BLOCK_INDEX 0x02
|
||||
#define ZUPT_BLOCK_ENC_HEADER 0x03
|
||||
#define ZUPT_BLOCK_DEDUP_REF 0x04 /* Dedup reference: payload = 8B offset of original block */
|
||||
#define ZUPT_BLOCK_DEDUP_REF 0x04 /* Dedup reference; authenticated v5.2.2 payload also carries source AAD sequence */
|
||||
#define ZUPT_BLOCK_COMMENT 0x05 /* v2.4.3: free-form UTF-8 comment, encrypted same as data blocks */
|
||||
|
||||
#define ZUPT_MAX_COMMENT_LEN 4096 /* Maximum comment payload size (bytes). */
|
||||
|
|
@ -270,7 +353,7 @@ typedef struct {
|
|||
int level; uint32_t block_size; uint16_t codec_id;
|
||||
int verbose, encrypt, quiet, solid, threads;
|
||||
int pq_mode; /* 1 = post-quantum hybrid KEM mode */
|
||||
int sdk_mode; /* 1 = use libzuptsdk-backed v3 crypto (HKDF combiner + commitment + HPKE) */
|
||||
int sdk_mode; /* 1 = use libvuptsdk-backed v3 crypto (HKDF combiner + commitment + HPKE) */
|
||||
int box_mode; /* 1 = libpqvaptvupt sealed-box mode (ZUPT_ENC_PQ_BOX_V1) */
|
||||
int pqonly_mode; /* 1 = full post-quantum mode: ML-KEM-768 only (ZUPT_ENC_PQ_ONLY) */
|
||||
int dedup; /* 1 = block-level deduplication enabled */
|
||||
|
|
@ -345,7 +428,7 @@ static inline void zupt_secure_wipe(void *ptr, size_t len) {
|
|||
|
||||
static inline int zupt_is_regular_file(const char *path) {
|
||||
#ifdef _WIN32
|
||||
DWORD attr = GetFileAttributesA(path);
|
||||
DWORD attr = zupt_win_get_attributes_utf8(path);
|
||||
if (attr == INVALID_FILE_ATTRIBUTES) return 0;
|
||||
return !(attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE |
|
||||
FILE_ATTRIBUTE_REPARSE_POINT));
|
||||
|
|
@ -396,10 +479,11 @@ void zupt_hmac_sha256_init(zupt_hmac_ctx *c, const uint8_t *key, size_t klen);
|
|||
void zupt_hmac_sha256_update(zupt_hmac_ctx *c, const uint8_t *data, size_t dlen);
|
||||
void zupt_hmac_sha256_final(zupt_hmac_ctx *c, uint8_t mac[32]);
|
||||
|
||||
/* Constant-time buffer equality. Returns 1 if equal, 0 otherwise, in
|
||||
* time dependent only on n (not contents / mismatch position). The single
|
||||
* audited MAC-tag comparison primitive; timing-verified by the
|
||||
* dudect-style test in tests/test_ct_timing.c. CT-REQUIRED. */
|
||||
/* Constant-time-intended buffer equality. Returns 1 if equal, 0 otherwise.
|
||||
* The source has a fixed-length OR-accumulate loop without an intended
|
||||
* content-dependent exit or access. The dudect-style regression in
|
||||
* tests/test_ct_timing.c measures exact builds when its control is conclusive;
|
||||
* it is not a formal guarantee about compiler output. CT-REQUIRED. */
|
||||
int zupt_ct_memeq(const void *a, const void *b, size_t n);
|
||||
void zupt_pbkdf2_sha256(const uint8_t *pw, size_t pwlen, const uint8_t *salt, size_t slen, uint32_t iter, uint8_t *out, size_t olen);
|
||||
void zupt_aes256_ctr(const uint8_t key[32], const uint8_t nonce[16], const uint8_t *in, uint8_t *out, size_t len);
|
||||
|
|
@ -477,8 +561,20 @@ zupt_error_t zupt_compress_files(const char *out, const char **arc, const char *
|
|||
zupt_error_t zupt_extract_archive(const char *arc, const char *dir, zupt_options_t *opts);
|
||||
zupt_error_t zupt_list_archive(const char *arc, zupt_options_t *opts);
|
||||
zupt_error_t zupt_test_archive(const char *arc, zupt_options_t *opts);
|
||||
/* Internal stream variant used when a caller has pinned a private snapshot.
|
||||
* It never closes stream; the caller retains ownership. */
|
||||
zupt_error_t zupt_test_archive_stream(FILE *stream, zupt_options_t *opts);
|
||||
zupt_error_t zupt_open_archive_internal(FILE *stream, zupt_options_t *opts,
|
||||
zupt_archive_header_t *header,
|
||||
zupt_footer_t *footer,
|
||||
zupt_index_entry_t **entries,
|
||||
int *num_entries);
|
||||
|
||||
/* ─── Hybrid PQ KEM (ML-KEM-768 + X25519) ─── */
|
||||
/* Internal no-replace writer shared by the native and optional pq-box key
|
||||
* formats. Private material receives platform-specific restrictive access. */
|
||||
int zupt_keyfile_write_new(const char *path, const uint8_t *data, size_t length,
|
||||
int private_material);
|
||||
int zupt_hybrid_keygen(const char *keyfile);
|
||||
int zupt_hybrid_export_pubkey(const char *privfile, const char *pubfile);
|
||||
int zupt_hybrid_encrypt_init(zupt_keyring_t *kr, const char *pubkeyfile,
|
||||
|
|
@ -494,14 +590,14 @@ int zupt_pq_encrypt_init(zupt_keyring_t *kr, const char *pubkeyfile,
|
|||
int zupt_pq_decrypt_init(zupt_keyring_t *kr, const char *privkeyfile,
|
||||
const uint8_t *enc_hdr, size_t enc_hdr_len);
|
||||
|
||||
/* ─── SDK-backed crypto (zupt v2.2+, libzuptsdk under the hood) ─── */
|
||||
/* ─── SDK-backed crypto (zupt v2.2+, optional libvuptsdk) ─── */
|
||||
int zupt_sdk_hybrid_keygen(const char *privkeyfile, const char *pubkeyfile);
|
||||
int zupt_sdk_hybrid_encrypt_init(zupt_keyring_t *kr, const char *pubkeyfile,
|
||||
uint8_t *enc_hdr, size_t *enc_hdr_len);
|
||||
int zupt_sdk_hybrid_decrypt_init(zupt_keyring_t *kr, const char *privkeyfile,
|
||||
const uint8_t *enc_hdr, size_t enc_hdr_len);
|
||||
|
||||
/* pq-box mode (ZUPT_ENC_PQ_BOX_V1, vendored libpqvaptvupt) */
|
||||
/* pq-box mode (ZUPT_ENC_PQ_BOX_V1, optional system libpqvaptvupt) */
|
||||
int zupt_pqbox_keygen(const char *privkeyfile, const char *pubkeyfile);
|
||||
int zupt_pqbox_encrypt_init(zupt_keyring_t *kr, const char *pubkeyfile,
|
||||
uint8_t *enc_hdr, size_t *enc_hdr_len);
|
||||
|
|
@ -519,7 +615,7 @@ void zupt_format_size(uint64_t bytes, char *buf, size_t cap);
|
|||
|
||||
/* Resolve ZUPT_CODEC_AUTO to a concrete codec based on hardware.
|
||||
* On x86_64 with AVX2: VaptVupt (fast ANS+SIMD decode).
|
||||
* On all other arches: Zupt-LZHP (no SIMD dependency).
|
||||
* On all other arches: ZUPT-LZHP (no SIMD dependency).
|
||||
* Decompression of ALL codecs works on ALL architectures. */
|
||||
uint16_t zupt_resolve_auto_codec(void);
|
||||
|
||||
|
|
@ -538,18 +634,58 @@ zupt_error_t zupt_disk_restore(const char *archive_path, const char *target_path
|
|||
zupt_options_t *opts);
|
||||
|
||||
/* ─── Internal Block I/O (used by format + disk modules) ─── */
|
||||
typedef struct zupt_atomic_output zupt_atomic_output_t;
|
||||
|
||||
/* Create an archive in a private file next to OUTPUT_PATH. finish(..., 1)
|
||||
* atomically replaces only the final directory entry; it never follows a
|
||||
* symlink/reparse point at the leaf. finish(..., 0) removes the temporary. */
|
||||
zupt_atomic_output_t *zupt_atomic_output_open(const char *output_path,
|
||||
FILE **stream_out);
|
||||
int zupt_atomic_output_finish(zupt_atomic_output_t *output, int publish);
|
||||
|
||||
zupt_error_t read_block(FILE *f, zupt_block_t *b);
|
||||
zupt_error_t read_enc_header(FILE *f, zupt_archive_header_t *hdr, zupt_options_t *opts);
|
||||
zupt_error_t decompress_block(const zupt_block_t *b, const zupt_keyring_t *kr,
|
||||
uint64_t block_seq, uint8_t **out, size_t *olen);
|
||||
|
||||
/* Published 5.2.1 encrypted+dedup disk images bound DATA authentication to
|
||||
* each frame's linear sequence, while legacy references stored only offsets.
|
||||
* Readers build this private offset-to-sequence map before restoring them. */
|
||||
typedef struct {
|
||||
uint64_t offset;
|
||||
uint64_t aad_seq;
|
||||
} zupt_legacy_disk_aad_entry_t;
|
||||
|
||||
typedef struct {
|
||||
zupt_legacy_disk_aad_entry_t *entries;
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
} zupt_legacy_disk_aad_map_t;
|
||||
|
||||
zupt_error_t zupt_legacy_disk_aad_map_build(
|
||||
FILE *stream, uint64_t first_block_offset, uint32_t block_count,
|
||||
zupt_legacy_disk_aad_map_t *map);
|
||||
int zupt_legacy_disk_aad_map_lookup(
|
||||
const zupt_legacy_disk_aad_map_t *map, uint64_t offset,
|
||||
uint64_t *aad_seq);
|
||||
void zupt_legacy_disk_aad_map_free(zupt_legacy_disk_aad_map_t *map);
|
||||
|
||||
zupt_error_t write_enc_header(FILE *out, zupt_archive_header_t *hdr,
|
||||
zupt_options_t *opts);
|
||||
int zupt_w8(FILE *f, uint8_t v);
|
||||
int zupt_w16le(FILE *f, uint16_t v);
|
||||
int zupt_w64le(FILE *f, uint64_t v);
|
||||
void zupt_serialize_archive_header(const zupt_archive_header_t *header,
|
||||
uint8_t out[ZUPT_ARCHIVE_HEADER_SIZE]);
|
||||
void zupt_serialize_footer(const zupt_footer_t *footer,
|
||||
uint8_t out[ZUPT_FOOTER_SIZE]);
|
||||
int zupt_write_archive_header(FILE *stream,
|
||||
const zupt_archive_header_t *header);
|
||||
int zupt_write_footer(FILE *stream, const zupt_footer_t *footer);
|
||||
|
||||
/* ─── Block-Level Deduplication ─── */
|
||||
#define ZUPT_DEDUP_MAX_ENTRIES (2 * 1024 * 1024) /* 2M entries, ~48MB RAM */
|
||||
#define ZUPT_DEDUP_MAX_ENTRIES (2 * 1024 * 1024) /* 2M entries, ~80MB RAM */
|
||||
#define ZUPT_DEDUP_DIGEST_SIZE 16 /* SHA-256 prefix paired with XXH64 */
|
||||
|
||||
typedef struct zupt_dedup_ctx zupt_dedup_ctx_t;
|
||||
|
||||
|
|
@ -566,6 +702,17 @@ void zupt_dedup_stats(const zupt_dedup_ctx_t *ctx,
|
|||
uint64_t *bytes_saved);
|
||||
int zupt_dedup_write_ref(FILE *out, uint64_t ref_offset,
|
||||
uint32_t orig_size, uint64_t orig_checksum);
|
||||
int zupt_dedup_write_ref_secure(FILE *out, uint64_t ref_offset,
|
||||
uint32_t orig_size, uint64_t orig_checksum,
|
||||
uint64_t current_aad_seq,
|
||||
uint64_t referenced_aad_seq,
|
||||
const zupt_keyring_t *keyring);
|
||||
zupt_error_t zupt_dedup_read_ref(const zupt_block_t *block,
|
||||
const zupt_keyring_t *keyring,
|
||||
int require_authentication,
|
||||
uint64_t current_aad_seq,
|
||||
uint64_t *ref_offset,
|
||||
uint64_t *referenced_aad_seq);
|
||||
|
||||
/* ─── Archive Info (read-only metadata inspection) ─── */
|
||||
zupt_error_t zupt_archive_info(const char *path);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
* Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
* Zupt — ACSL Custom Predicates for Frama-C/WP
|
||||
* ZUPT — ACSL Custom Predicates for Frama-C/WP
|
||||
* Copyright (c) 2026 Cristian Cezar Moisés — AGPL-3.0-or-later
|
||||
*
|
||||
* Usage: frama-c -wp -wp-rte -wp-model Typed+Cast
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
* Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
* Zupt — CPU Feature Detection
|
||||
* ZUPT — CPU Feature Detection
|
||||
* Copyright (c) 2026 Cristian Cezar Moisés — AGPL-3.0-or-later
|
||||
*/
|
||||
#ifndef ZUPT_CPUID_H
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
* Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
* Zupt — Jasmin Verified Crypto Declarations
|
||||
* ZUPT — optional x86_64 crypto assembly declarations
|
||||
* Copyright (c) 2026 Cristian Cezar Moisés — AGPL-3.0-or-later
|
||||
*
|
||||
* Extern declarations for Jasmin-compiled assembly functions.
|
||||
* These replace C fallbacks when built with -DZUPT_USE_JASMIN.
|
||||
* Four declarations below correspond to checked-in jasminc output. The
|
||||
* zupt_aes256_ctr4 implementation is separately identified hand-written
|
||||
* assembly matching an algorithm-only .jazz description. These functions
|
||||
* replace C fallbacks when built with -DZUPT_USE_JASMIN.
|
||||
*
|
||||
* Calling convention: System V AMD64 ABI.
|
||||
* Pointer args passed in RDI, RSI, RDX, RCX, R8, R9.
|
||||
*
|
||||
* v2.0.0: All 4 Jasmin functions wired and active.
|
||||
* All five optional declarations are wired when the feature is enabled.
|
||||
*/
|
||||
#ifndef ZUPT_JASMIN_H
|
||||
#define ZUPT_JASMIN_H
|
||||
|
|
@ -18,24 +20,25 @@
|
|||
#ifdef ZUPT_USE_JASMIN
|
||||
#include <stdint.h>
|
||||
|
||||
/* JASMIN-VERIFIED: CT MAC comparison (4×u64 XOR accumulation).
|
||||
/* JASMIN PATH: CT-intended MAC comparison (4×u64 XOR accumulation).
|
||||
* Returns 0 if all 32 bytes match, nonzero if any differ.
|
||||
* Replaces XOR loop in zupt_decrypt_buffer(). */
|
||||
extern uint64_t zupt_mac_verify_ct(const void *expected, const void *actual);
|
||||
|
||||
/* JASMIN-VERIFIED: CT conditional select (4×u64 masked select).
|
||||
/* JASMIN PATH: CT-intended conditional select (4×u64 masked select).
|
||||
* if cond==0: copies a→out. if cond!=0: copies b→out.
|
||||
* Replaces cmov in zupt_mlkem768_decaps(). */
|
||||
extern void zupt_ct_select_32(void *out, const void *a,
|
||||
const void *b, uint64_t cond);
|
||||
|
||||
/* JASMIN-VERIFIED: CT conditional swap (4×u64 masked XOR swap).
|
||||
/* JASMIN PATH: CT-intended conditional swap (4×u64 masked XOR swap).
|
||||
* if cond==0: no-op. if cond==1: swaps a↔b in place.
|
||||
* Replaces fe_cswap in zupt_x25519.c.
|
||||
* NOTE: Requires 4×u64 field element layout (donna64). */
|
||||
* Operates on exactly four consecutive u64 values; the X25519 caller handles
|
||||
* its fifth 51-bit limb separately. */
|
||||
extern void zupt_fe_cswap(void *a, void *b, uint64_t cond);
|
||||
|
||||
/* JASMIN-VERIFIED: AES-256 single-block encrypt via AES-NI.
|
||||
/* JASMIN PATH: AES-256 single-block encrypt via AES-NI.
|
||||
* out = AES-256-ECB(key, ctr) XOR in.
|
||||
* FIX v2.0.0: Stack offset bug resolved — round keys at correct
|
||||
* 16-byte aligned offsets. Requires AES-NI (checked via CPUID).
|
||||
|
|
@ -49,7 +52,7 @@ extern void zupt_fe_cswap(void *a, void *b, uint64_t cond);
|
|||
extern void zupt_aes256_blk(void *out, const void *in,
|
||||
const void *key, const void *ctr);
|
||||
|
||||
/* JASMIN-VERIFIED: AES-256-CTR 4-block pipeline via AES-NI.
|
||||
/* HAND-WRITTEN ASSEMBLY PATH: AES-256-CTR 4-block pipeline via AES-NI.
|
||||
* Processes nblocks×16 bytes with 4-way interleaving.
|
||||
* Counter is updated in-place (big-endian increment in bytes [8..15]).
|
||||
* Requires AES-NI. Falls back to zupt_aes256_blk for remaining 1-3 blocks.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Zupt — Backup-oriented compression with AES-256 encryption
|
||||
* ZUPT — Backup-oriented compression with AES-256 encryption
|
||||
* Copyright (c) 2026 Cristian Cezar Moisés
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Zupt — Backup-oriented compression with AES-256 encryption
|
||||
* ZUPT — Backup-oriented compression with AES-256 encryption
|
||||
* Copyright (c) 2026 Cristian Cezar Moisés
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
|
|
@ -64,7 +64,7 @@ int zupt_mlkem768_decaps(uint8_t ss[MLKEM_SSBYTES],
|
|||
|
||||
/* Self-test: NTT/iNTT roundtrip + CBD-sampler range invariants.
|
||||
* Returns 1 on pass, 0 on fail. Called from test_vectors.c case 14
|
||||
* (F-04, Zupt 2.2.4). */
|
||||
* (F-04, ZUPT 2.2.4). */
|
||||
int zupt_mlkem768_selftest(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
/*
|
||||
* Zupt — Backup-oriented compression with AES-256 encryption
|
||||
* ZUPT — Backup-oriented compression with AES-256 encryption
|
||||
* Copyright (c) 2026 Cristian Cezar Moisés
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* X25519 Diffie-Hellman key agreement (RFC 7748).
|
||||
* Montgomery ladder — constant-time by construction.
|
||||
* Fixed-iteration Montgomery ladder, designed without secret-dependent
|
||||
* branches or table lookups; compiled timing remains platform-dependent.
|
||||
*/
|
||||
#ifndef ZUPT_X25519_H
|
||||
#define ZUPT_X25519_H
|
||||
|
|
@ -12,7 +13,8 @@
|
|||
#include <stdint.h>
|
||||
|
||||
/* X25519(scalar, point) → result. All inputs/outputs are 32 bytes.
|
||||
* CT-REQUIRED: Montgomery ladder is inherently constant-time. */
|
||||
* CT-REQUIRED: keep the ladder free of intended secret-dependent branches and
|
||||
* memory access. This source-level property is not a compiled timing proof. */
|
||||
void zupt_x25519(uint8_t out[32], const uint8_t scalar[32], const uint8_t point[32]);
|
||||
|
||||
/* X25519 with the standard basepoint (9).
|
||||
|
|
|
|||
34
install.sh
34
install.sh
|
|
@ -1,29 +1,33 @@
|
|||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# Fast Installer for VaptVupt - GNU/Linux
|
||||
# Fast installer for ZUPT - GNU/Linux
|
||||
|
||||
set -e
|
||||
set -Eeuo pipefail
|
||||
umask 077
|
||||
|
||||
echo "🔧 Installing VaptVupt..."
|
||||
VERSION=${VERSION:-5.2.8}
|
||||
PREFIX=${PREFIX:-/usr/local}
|
||||
|
||||
echo "🔧 Installing ZUPT..."
|
||||
|
||||
# Create temporary directory
|
||||
TMP_DIR=$(mktemp -d)
|
||||
TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/zupt-install.XXXXXXXX")
|
||||
trap 'chmod -R u+rwX "$TMP_DIR" 2>/dev/null || true; rm -rf -- "$TMP_DIR"' EXIT HUP INT TERM
|
||||
|
||||
# Clone and build
|
||||
git clone https://git.securityops.co/cristiancmoises/vaptvupt.git "$TMP_DIR/vaptvupt"
|
||||
cd "$TMP_DIR/vaptvupt"
|
||||
git clone --depth 1 --branch "v$VERSION" \
|
||||
https://github.com/cristiancmoises/zupt.git "$TMP_DIR/zupt"
|
||||
cd "$TMP_DIR/zupt"
|
||||
|
||||
make clean
|
||||
make
|
||||
make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf 2)" \
|
||||
WITH_SDK=0 WITH_PQBOX=0
|
||||
make WITH_SDK=0 WITH_PQBOX=0 check
|
||||
|
||||
# Install
|
||||
sudo make install
|
||||
sudo make PREFIX="$PREFIX" WITH_SDK=0 WITH_PQBOX=0 \
|
||||
INSTALL_LEGACY_ALIAS=0 install
|
||||
|
||||
echo "✅ VaptVupt successfully installed to /usr/local/bin/vaptvupt"
|
||||
echo "🔒 You can now run: vaptvupt (legacy 'zupt' symlink also installed)"
|
||||
|
||||
# Cleanup
|
||||
cd ~
|
||||
rm -rf "$TMP_DIR"
|
||||
echo "🧹 Cleanup completed"
|
||||
echo "✅ ZUPT $VERSION successfully installed to $PREFIX/bin/zupt"
|
||||
echo "🔒 You can now run: zupt"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
/* Zupt — AES-256 Single Block Encrypt via AES-NI (Jasmin)
|
||||
/* ZUPT — AES-256 Single Block Encrypt via AES-NI (Jasmin)
|
||||
* Copyright (c) 2026 Cristian Cezar Moisés
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* CT-REQUIRED: AES-NI has no data-dependent timing.
|
||||
* CT-REQUIRED: designed without intended secret-dependent branches or memory
|
||||
* access. Compiled and microarchitectural timing is not proven here.
|
||||
*
|
||||
* FIX v2.0.0: replaced `stack u128[15] rk` with 15 individual
|
||||
* `stack u128` variables. The array form uses byte-offset indexing
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
/* Zupt — AES-256-CTR 4-Block Pipeline via AES-NI (Jasmin)
|
||||
/* ZUPT — AES-256-CTR 4-Block Pipeline via AES-NI (Jasmin)
|
||||
* Copyright (c) 2026 Cristian Cezar Moisés
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* CT-REQUIRED: AES-NI has no data-dependent timing.
|
||||
* CT-REQUIRED: designed without intended secret-dependent branches or memory
|
||||
* access. Compiled and microarchitectural timing is not proven here.
|
||||
*
|
||||
* Interleaves 4 independent counter blocks through the AES round
|
||||
* pipeline. AES-NI has 4-cycle latency, 1-cycle throughput — so
|
||||
* 4 independent blocks saturate the pipeline for ~4× throughput.
|
||||
*
|
||||
* Expected: ~3.5 GB/s AES-256-CTR on modern x86-64 (Zen3/Alder Lake).
|
||||
*
|
||||
* Interface:
|
||||
* zupt_aes256_ctr4(out, in, key, ctr, nblocks)
|
||||
* Encrypts nblocks×16 bytes. Counter is incremented in the last 8 bytes
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2026 Cristian Cezar Moisés
|
||||
# Generated from jasmin/zupt_aes_ctr4.jazz by jasminc.
|
||||
# Hand-written production assembly matching the algorithm documented in
|
||||
# jasmin/zupt_aes_ctr4.jazz; this file is not jasminc output.
|
||||
.intel_syntax noprefix
|
||||
.text
|
||||
.p2align 5
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Zupt — Constant-Time MAC Comparison (Jasmin)
|
||||
/* ZUPT — Constant-Time MAC Comparison (Jasmin)
|
||||
* Copyright (c) 2026 Cristian Cezar Moisés
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Zupt — ML-KEM Constant-Time Select (Jasmin)
|
||||
/* ZUPT — ML-KEM Constant-Time Select (Jasmin)
|
||||
* Copyright (c) 2026 Cristian Cezar Moisés
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
/* Zupt — X25519 Constant-Time Conditional Swap (Jasmin)
|
||||
/* ZUPT — X25519 Constant-Time Conditional Swap (Jasmin)
|
||||
* Copyright (c) 2026 Cristian Cezar Moisés
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* CT-REQUIRED: fe_cswap must not leak cond via timing.
|
||||
* This is the only CT-critical field operation in X25519.
|
||||
* fe_add/fe_sub/fe_mul use C fallback (data-independent timing
|
||||
* on x86-64 — ADD/MUL have fixed latency).
|
||||
* fe_add/fe_sub/fe_mul use the C fallback. No fixed-latency claim is made for
|
||||
* every compiler, x86-64 CPU, or resulting binary.
|
||||
*
|
||||
* 4 × u64 limbs, pure register operations, no intrinsics needed.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,56 +1,60 @@
|
|||
# Maintainer: Cristian Cezar Moisés <sac@securityops.co>
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#
|
||||
# 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).
|
||||
# 2. Upload that tarball to the canonical GitHub release.
|
||||
# 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
|
||||
# 5. Commit and push to the separately maintained AUR package repository.
|
||||
#
|
||||
# Test locally: `makepkg -s` in this directory after dropping a copy of the
|
||||
# zupt-VERSION.tar.gz alongside the PKGBUILD.
|
||||
# Test locally with `makepkg -s` after the release archive is published.
|
||||
|
||||
pkgname=vaptvupt
|
||||
pkgver=5.0.0
|
||||
pkgname=zupt
|
||||
pkgver=5.2.8
|
||||
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/vaptvupt'
|
||||
license=('AGPL-3.0-or-later')
|
||||
arch=('x86_64')
|
||||
url='https://github.com/cristiancmoises/zupt'
|
||||
license=('AGPL-3.0-or-later AND GPL-3.0-or-later AND BSD-2-Clause AND BSD-3-Clause AND CC0-1.0')
|
||||
depends=('glibc')
|
||||
makedepends=('gcc')
|
||||
makedepends=('gcc' 'git' 'make')
|
||||
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/vaptvupt/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
sha256sums=('SKIP')
|
||||
source=("${pkgname}-${pkgver}.tar.gz::https://github.com/cristiancmoises/zupt/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
# Byte-reproducible upstream v5.2.8 source archive.
|
||||
sha256sums=('378b9506211545b9594cf0d38ac8955d9b1cac34eb6b379ae0ec26b84edb65f7')
|
||||
|
||||
build() {
|
||||
cd "${pkgname}-${pkgver}"
|
||||
# Source-only build (WITH_SDK=0) with the project's strict warning set.
|
||||
CFLAGS="${CFLAGS:--O2 -std=c11} -Wall -Wextra -Wpedantic" \
|
||||
make WITH_SDK=0 -j"$(nproc)"
|
||||
make WITH_SDK=0 WITH_PQBOX=0 -j"$(nproc)"
|
||||
}
|
||||
|
||||
check() {
|
||||
cd "${pkgname}-${pkgver}"
|
||||
# Distro-safe regression subset (F-06..F-12, dedup-nonce, NIST/RFC vectors).
|
||||
make WITH_SDK=0 check
|
||||
# Distro-safe quick, traversal, integrity, codec and NIST/RFC checks.
|
||||
make WITH_SDK=0 WITH_PQBOX=0 check
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "${pkgname}-${pkgver}"
|
||||
# Source-only build (no vendored libraries); `make install` places the
|
||||
# binary, the zupt symlink, the man pages and the shell completions.
|
||||
make DESTDIR="${pkgdir}" PREFIX=/usr WITH_SDK=0 install
|
||||
# binary, man page and shell completions under the public zupt name.
|
||||
make DESTDIR="${pkgdir}" PREFIX=/usr WITH_SDK=0 WITH_PQBOX=0 \
|
||||
INSTALL_LEGACY_ALIAS=0 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 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
||||
install -Dm644 LICENSE-AGPL-3.0 "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE-AGPL-3.0"
|
||||
install -Dm644 LICENSE-GPL-3.0 "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE-GPL-3.0"
|
||||
install -Dm644 LICENSE-BSD-2-Clause "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE-BSD-2-Clause"
|
||||
install -Dm644 LICENSE-BSD-3-Clause "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE-BSD-3-Clause"
|
||||
install -Dm644 LICENSE-CC0-1.0 "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE-CC0-1.0"
|
||||
install -Dm644 NOTICE "${pkgdir}/usr/share/licenses/${pkgname}/NOTICE"
|
||||
install -Dm644 THIRD-PARTY-NOTICES.md \
|
||||
"${pkgdir}/usr/share/licenses/${pkgname}/THIRD-PARTY-NOTICES.md"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,59 +1,165 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# 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:-4.2.1}"
|
||||
ARCH="${ARCH:-x86_64}"
|
||||
PKGNAME="vaptvupt"
|
||||
LEGACY="zupt"
|
||||
NAME="$PKGNAME-$VERSION-$ARCH"
|
||||
OUT="/tmp/${NAME}.AppDir"
|
||||
set -Eeuo pipefail
|
||||
|
||||
rm -rf "$OUT"
|
||||
mkdir -p "$OUT/usr/bin" "$OUT/usr/share/applications" "$OUT/usr/share/icons/hicolor/256x256/apps"
|
||||
umask 022
|
||||
export LC_ALL=C
|
||||
|
||||
# Source-only build: the binary links only libc/libm/pthread from the host,
|
||||
# so the AppDir ships no bundled libraries.
|
||||
install -m 755 $PKGNAME "$OUT/usr/bin/$PKGNAME"
|
||||
ln -sf $PKGNAME "$OUT/usr/bin/$LEGACY"
|
||||
die() {
|
||||
printf 'FAIL: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cat > "$OUT/AppRun" <<APPRUN
|
||||
#!/bin/bash
|
||||
HERE="\$(dirname "\$(readlink -f "\${0}")")"
|
||||
export PATH="\$HERE/usr/bin:\$PATH"
|
||||
exec "\$HERE/usr/bin/$PKGNAME" "\$@"
|
||||
APPRUN
|
||||
chmod +x "$OUT/AppRun"
|
||||
[[ $(uname -s) == Linux ]] || die 'AppImage packages must be built on Linux'
|
||||
|
||||
cat > "$OUT/$PKGNAME.desktop" <<DESK
|
||||
[Desktop Entry]
|
||||
Name=VaptVupt
|
||||
Comment=Post-quantum backup compression utility (formerly Zupt)
|
||||
Exec=$PKGNAME
|
||||
Terminal=true
|
||||
Type=Application
|
||||
Categories=Utility;Archiving;Security;
|
||||
Icon=$PKGNAME
|
||||
DESK
|
||||
cp "$OUT/$PKGNAME.desktop" "$OUT/usr/share/applications/"
|
||||
repo_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
cd -- "$repo_root"
|
||||
|
||||
# 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"
|
||||
header_version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
version=${VERSION:-$header_version}
|
||||
[[ -n $version && $version == "$header_version" ]] || \
|
||||
die "VERSION '$version' does not match include/zupt.h '$header_version'"
|
||||
[[ $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || die "invalid package version: $version"
|
||||
|
||||
if command -v appimagetool >/dev/null 2>&1; then
|
||||
ARCH=$ARCH appimagetool "$OUT" "/tmp/${NAME}.AppImage" 2>&1 | tail -5
|
||||
echo "Built: /tmp/${NAME}.AppImage"
|
||||
case $(uname -m) in
|
||||
x86_64|amd64) native_arch=x86_64 ;;
|
||||
aarch64|arm64) native_arch=aarch64 ;;
|
||||
*) die "unsupported native AppImage architecture: $(uname -m)" ;;
|
||||
esac
|
||||
case ${ARCH:-$native_arch} in
|
||||
x86_64|amd64) arch=x86_64 ;;
|
||||
aarch64|arm64) arch=aarch64 ;;
|
||||
*) die "unsupported AppImage architecture: ${ARCH:-$native_arch}" ;;
|
||||
esac
|
||||
[[ $arch == "$native_arch" ]] || \
|
||||
die "ARCH=$arch does not match the native build architecture $native_arch"
|
||||
|
||||
appimagetool=${APPIMAGETOOL:-appimagetool}
|
||||
if [[ $appimagetool == */* ]]; then
|
||||
[[ -x $appimagetool ]] || die "APPIMAGETOOL is not executable: $appimagetool"
|
||||
appimagetool=$(cd -- "$(dirname -- "$appimagetool")" && pwd -P)/$(basename -- "$appimagetool")
|
||||
else
|
||||
appimagetool=$(command -v -- "$appimagetool" || true)
|
||||
[[ -n $appimagetool ]] || die 'appimagetool not found; set APPIMAGETOOL to a verified local executable'
|
||||
fi
|
||||
runtime_file=${APPIMAGE_RUNTIME_FILE:-}
|
||||
[[ -n $runtime_file && -s $runtime_file ]] || \
|
||||
die 'set APPIMAGE_RUNTIME_FILE to a locally verified type-2 runtime (network downloads are not performed)'
|
||||
runtime_file=$(cd -- "$(dirname -- "$runtime_file")" && pwd -P)/$(basename -- "$runtime_file")
|
||||
runtime_compliance_file=${APPIMAGE_RUNTIME_COMPLIANCE_FILE:-}
|
||||
[[ -n $runtime_compliance_file && -s $runtime_compliance_file ]] || \
|
||||
die 'set APPIMAGE_RUNTIME_COMPLIANCE_FILE to the audited runtime license/source-compliance notice'
|
||||
runtime_compliance_file=$(cd -- "$(dirname -- "$runtime_compliance_file")" && pwd -P)/$(basename -- "$runtime_compliance_file")
|
||||
|
||||
dist_dir=${DIST_DIR:-${TMPDIR:-/tmp}/zupt-release}
|
||||
mkdir -p -- "$dist_dir"
|
||||
dist_dir=$(cd -- "$dist_dir" && pwd -P)
|
||||
output=$dist_dir/zupt-${version}-linux-${arch}.AppImage
|
||||
[[ ! -e $output ]] || die "refusing to overwrite existing output: $output"
|
||||
|
||||
for command_name in make readelf file sha256sum; do
|
||||
command -v -- "$command_name" >/dev/null 2>&1 || die "required command not found: $command_name"
|
||||
done
|
||||
run_checks=${RUN_CHECKS:-1}
|
||||
[[ $run_checks == 0 || $run_checks == 1 ]] || die 'RUN_CHECKS must be 0 or 1'
|
||||
if [[ $run_checks == 1 ]]; then
|
||||
command -v git >/dev/null 2>&1 || die 'git is required when RUN_CHECKS=1'
|
||||
fi
|
||||
|
||||
# Always produce the AppDir tarball as well -- some environments (no FUSE,
|
||||
# strict execve policies, etc.) cannot run the .AppImage directly. The
|
||||
# tarball is the universal fallback: extract and run AppRun.
|
||||
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 version"
|
||||
jobs=${JOBS:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf '1')}
|
||||
work=$(mktemp -d "${TMPDIR:-/tmp}/zupt-appimage.XXXXXXXX")
|
||||
appdir=$work/ZUPT.AppDir
|
||||
image_tmp=$work/$(basename -- "$output")
|
||||
|
||||
cleanup() {
|
||||
make -C "$repo_root" clean >/dev/null 2>&1 || true
|
||||
chmod -R u+rwX "$work" 2>/dev/null || true
|
||||
rm -rf -- "$work"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
printf '[AppImage] source-only build of ZUPT %s (%s)\n' "$version" "$arch"
|
||||
make clean
|
||||
make -j"$jobs" V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0
|
||||
if [[ $run_checks == 1 ]]; then
|
||||
make V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 check
|
||||
fi
|
||||
make DESTDIR="$appdir" PREFIX=/usr WITH_SDK=0 WITH_PQBOX=0 \
|
||||
INSTALL_LEGACY_ALIAS=0 install
|
||||
|
||||
binary=$appdir/usr/bin/zupt
|
||||
[[ -x $binary ]] || die 'AppDir executable is missing'
|
||||
[[ ! -e $appdir/usr/bin/vaptvupt ]] || die 'legacy vaptvupt alias must not be packaged'
|
||||
if readelf -d "$binary" 2>/dev/null | grep -Eq '(RPATH|RUNPATH)'; then
|
||||
readelf -d "$binary" | grep -E '(RPATH|RUNPATH)' >&2
|
||||
die 'AppDir executable contains RPATH/RUNPATH'
|
||||
fi
|
||||
if readelf -d "$binary" 2>/dev/null | grep -Eqi '(vendor/|libvuptsdk|libpqvaptvupt)'; then
|
||||
die 'AppDir executable references a vendored optional library'
|
||||
fi
|
||||
|
||||
mkdir -p -- "$appdir/usr/share/applications" \
|
||||
"$appdir/usr/share/doc/zupt" \
|
||||
"$appdir/usr/share/icons/hicolor/128x128/apps" \
|
||||
"$appdir/usr/share/licenses/zupt"
|
||||
install -m 0644 README.md CHANGELOG.md SECURITY.md THREAT_MODEL.md \
|
||||
"$appdir/usr/share/doc/zupt/"
|
||||
install -m 0644 LICENSE LICENSE-AGPL-3.0 LICENSE-GPL-3.0 \
|
||||
LICENSE-BSD-2-Clause LICENSE-BSD-3-Clause LICENSE-CC0-1.0 NOTICE \
|
||||
THIRD-PARTY-NOTICES.md "$appdir/usr/share/licenses/zupt/"
|
||||
install -m 0644 gui/LICENSE-GUI \
|
||||
"$appdir/usr/share/licenses/zupt/GUI-LICENSE.txt"
|
||||
install -m 0644 gui/assets/README.md \
|
||||
"$appdir/usr/share/licenses/zupt/GUI-ASSET-PROVENANCE.md"
|
||||
install -m 0644 "$runtime_compliance_file" \
|
||||
"$appdir/usr/share/licenses/zupt/AppImage-runtime-compliance.txt"
|
||||
install -m 0644 gui/assets/zupt-128.png \
|
||||
"$appdir/usr/share/icons/hicolor/128x128/apps/zupt.png"
|
||||
cp -- "$appdir/usr/share/icons/hicolor/128x128/apps/zupt.png" "$appdir/zupt.png"
|
||||
ln -s -- zupt.png "$appdir/.DirIcon"
|
||||
|
||||
desktop_file=dev.zupt.cli.desktop
|
||||
cat > "$appdir/$desktop_file" <<'EOF'
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=ZUPT
|
||||
Comment=Backup compression with authenticated and post-quantum encryption
|
||||
Exec=zupt
|
||||
Icon=zupt
|
||||
Terminal=true
|
||||
Categories=Utility;Archiving;
|
||||
EOF
|
||||
cp -- "$appdir/$desktop_file" "$appdir/usr/share/applications/$desktop_file"
|
||||
|
||||
cat > "$appdir/AppRun" <<'EOF'
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
appdir=$(CDPATH= cd -P "$(dirname "$0")" && pwd -P)
|
||||
exec "$appdir/usr/bin/zupt" "$@"
|
||||
EOF
|
||||
chmod 0755 "$appdir/AppRun"
|
||||
|
||||
forbidden=$(find "$appdir" -type f \( \
|
||||
-name '*.o' -o -name '*.obj' -o -name '*.a' -o -name '*.so' -o \
|
||||
-name '*.so.*' -o -name '*.dll' -o -name '*.dylib' \
|
||||
\) -print)
|
||||
[[ -z $forbidden ]] || {
|
||||
printf '%s\n' "$forbidden" >&2
|
||||
die 'compiled library or object found in AppDir'
|
||||
}
|
||||
|
||||
bash scripts/test-installed-zupt.sh "$appdir/AppRun"
|
||||
|
||||
export ARCH=$arch
|
||||
export VERSION=$version
|
||||
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
"$appimagetool" --runtime-file "$runtime_file" "$appdir" "$image_tmp"
|
||||
chmod 0755 "$image_tmp"
|
||||
file "$image_tmp" | grep -q 'ELF' || die 'generated AppImage does not have ELF magic'
|
||||
bash scripts/test-installed-zupt.sh "$image_tmp"
|
||||
|
||||
mv -- "$image_tmp" "$output"
|
||||
sha256sum "$output"
|
||||
printf 'PASS: built and executed-package-tested %s\n' "$output"
|
||||
|
|
|
|||
|
|
@ -1,134 +1,139 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
#
|
||||
# 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")/.."
|
||||
set -Eeuo pipefail
|
||||
|
||||
VERSION="${VERSION:-3.0.0}"
|
||||
ARCH="${ARCH:-amd64}"
|
||||
PKGNAME="vaptvupt"
|
||||
LEGACY="zupt"
|
||||
umask 022
|
||||
export LC_ALL=C
|
||||
|
||||
PKG="${PKGNAME}_${VERSION}_${ARCH}"
|
||||
ROOT="/tmp/$PKG"
|
||||
|
||||
# Vendored libzuptsdk path (relative to project root)
|
||||
SDK_LIB="vendor/zuptsdk/libzuptsdk.so.2.0.0"
|
||||
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
|
||||
die() {
|
||||
printf 'FAIL: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
repo_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
cd -- "$repo_root"
|
||||
|
||||
header_version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
version=${VERSION:-$header_version}
|
||||
[[ -n $version && $version == "$header_version" ]] || \
|
||||
die "VERSION '$version' does not match include/zupt.h '$header_version'"
|
||||
[[ $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || die "invalid package version: $version"
|
||||
|
||||
native_arch=$(dpkg --print-architecture)
|
||||
arch=${ARCH:-$native_arch}
|
||||
[[ $arch == "$native_arch" ]] || \
|
||||
die "ARCH=$arch does not match the native dpkg architecture $native_arch"
|
||||
dist_dir=${DIST_DIR:-${TMPDIR:-/tmp}/zupt-release}
|
||||
mkdir -p -- "$dist_dir"
|
||||
dist_dir=$(cd -- "$dist_dir" && pwd -P)
|
||||
output=$dist_dir/zupt_${version}_${arch}.deb
|
||||
[[ ! -e $output ]] || die "refusing to overwrite existing output: $output"
|
||||
|
||||
for command_name in make dpkg dpkg-deb readelf sha256sum; do
|
||||
command -v -- "$command_name" >/dev/null 2>&1 || die "required command not found: $command_name"
|
||||
done
|
||||
run_checks=${RUN_CHECKS:-1}
|
||||
[[ $run_checks == 0 || $run_checks == 1 ]] || die 'RUN_CHECKS must be 0 or 1'
|
||||
if [[ $run_checks == 1 ]]; then
|
||||
command -v git >/dev/null 2>&1 || die 'git is required when RUN_CHECKS=1'
|
||||
fi
|
||||
|
||||
echo "[deb] Building vaptvupt"
|
||||
make clean >/dev/null 2>&1 || true
|
||||
make -j"$(nproc)" >/dev/null
|
||||
jobs=${JOBS:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf '1')}
|
||||
work=$(mktemp -d "${TMPDIR:-/tmp}/zupt-deb.XXXXXXXX")
|
||||
stage=$work/stage
|
||||
extract=$work/extract
|
||||
|
||||
echo "[deb] Patching rpath -> /usr/lib/$PKGNAME:/usr/lib64/$PKGNAME"
|
||||
patchelf --set-rpath "/usr/lib/$PKGNAME:/usr/lib64/$PKGNAME" $PKGNAME
|
||||
cleanup() {
|
||||
make -C "$repo_root" clean >/dev/null 2>&1 || true
|
||||
chmod -R u+rwX "$work" 2>/dev/null || true
|
||||
rm -rf -- "$work"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
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
|
||||
printf '[deb] source-only build of ZUPT %s (%s)\n' "$version" "$arch"
|
||||
make clean
|
||||
make -j"$jobs" V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0
|
||||
if [[ $run_checks == 1 ]]; then
|
||||
make V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 check
|
||||
fi
|
||||
|
||||
rm -rf "$ROOT"
|
||||
mkdir -p "$ROOT/DEBIAN" \
|
||||
"$ROOT/usr/bin" \
|
||||
"$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"
|
||||
make DESTDIR="$stage" PREFIX=/usr WITH_SDK=0 WITH_PQBOX=0 \
|
||||
INSTALL_LEGACY_ALIAS=0 install
|
||||
|
||||
# Binary + legacy symlink
|
||||
install -m 755 $PKGNAME "$ROOT/usr/bin/$PKGNAME"
|
||||
ln -sf $PKGNAME "$ROOT/usr/bin/$LEGACY"
|
||||
binary=$stage/usr/bin/zupt
|
||||
[[ -x $binary ]] || die 'staged /usr/bin/zupt is missing'
|
||||
[[ ! -e $stage/usr/bin/vaptvupt ]] || die 'legacy /usr/bin/vaptvupt must not be packaged'
|
||||
|
||||
# Bundled libzuptsdk
|
||||
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"
|
||||
if readelf -d "$binary" 2>/dev/null | grep -Eq '(RPATH|RUNPATH)'; then
|
||||
readelf -d "$binary" | grep -E '(RPATH|RUNPATH)' >&2
|
||||
die 'staged executable contains RPATH/RUNPATH'
|
||||
fi
|
||||
if readelf -d "$binary" 2>/dev/null | grep -Eqi '(vendor/|libvuptsdk|libpqvaptvupt)'; then
|
||||
die 'staged executable references a vendored optional library'
|
||||
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"
|
||||
forbidden=$(find "$stage" -type f \( \
|
||||
-name '*.o' -o -name '*.obj' -o -name '*.a' -o -name '*.so' -o \
|
||||
-name '*.so.*' -o -name '*.dll' -o -name '*.dylib' -o -name '*.exe' \
|
||||
\) -print)
|
||||
[[ -z $forbidden ]] || {
|
||||
printf '%s\n' "$forbidden" >&2
|
||||
die 'compiled library or object found in package staging tree'
|
||||
}
|
||||
|
||||
docdir=$stage/usr/share/doc/zupt
|
||||
mkdir -p -- "$docdir"
|
||||
install -m 0644 README.md CHANGELOG.md SECURITY.md "$docdir/"
|
||||
for document in THREAT_MODEL.md NOTICE THIRD-PARTY-NOTICES.md LICENSE-AGPL-3.0 LICENSE-GPL-3.0 LICENSE-BSD-2-Clause LICENSE-BSD-3-Clause LICENSE-CC0-1.0; do
|
||||
[[ ! -f $document ]] || install -m 0644 "$document" "$docdir/"
|
||||
done
|
||||
install -m 0644 LICENSE "$docdir/copyright"
|
||||
|
||||
mkdir -p -- "$work/debian" "$stage/DEBIAN"
|
||||
if [[ -n ${DEB_DEPENDS:-} ]]; then
|
||||
depends=$DEB_DEPENDS
|
||||
else
|
||||
command -v dpkg-shlibdeps >/dev/null 2>&1 || \
|
||||
die 'dpkg-shlibdeps is required unless DEB_DEPENDS is explicitly set'
|
||||
printf 'Source: zupt\nPackage: zupt\n' > "$work/debian/control"
|
||||
shlib_line=$(cd -- "$work" && dpkg-shlibdeps -O -e"$binary")
|
||||
depends=${shlib_line#shlibs:Depends=}
|
||||
[[ -n $depends && $depends != "$shlib_line" ]] || \
|
||||
die 'dpkg-shlibdeps did not determine runtime dependencies'
|
||||
fi
|
||||
|
||||
# Docs
|
||||
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"
|
||||
|
||||
# DEBIAN/control
|
||||
INSTALLED_KB=$(du -sk "$ROOT/usr" | awk '{print $1}')
|
||||
cat > "$ROOT/DEBIAN/control" <<EOF
|
||||
Package: $PKGNAME
|
||||
Version: $VERSION
|
||||
installed_kib=$(du -sk "$stage/usr" | awk '{print $1}')
|
||||
cat > "$stage/DEBIAN/control" <<EOF
|
||||
Package: zupt
|
||||
Version: $version
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: $ARCH
|
||||
Provides: $LEGACY (= $VERSION)
|
||||
Replaces: $LEGACY (<< 3.0.0)
|
||||
Conflicts: $LEGACY (<< 3.0.0)
|
||||
Depends: libargon2-1, libssl3 | libssl3t64
|
||||
Installed-Size: $INSTALLED_KB
|
||||
Architecture: $arch
|
||||
Depends: $depends
|
||||
Installed-Size: $installed_kib
|
||||
Maintainer: Cristian Cezar Moisés <sac@securityops.co>
|
||||
Homepage: https://git.securityops.co/cristiancmoises/zupt
|
||||
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.
|
||||
Homepage: https://github.com/cristiancmoises/zupt
|
||||
Description: Backup compression with authenticated and post-quantum encryption
|
||||
ZUPT creates compressed backup archives with optional password encryption
|
||||
or ML-KEM-768 and X25519 hybrid key encapsulation. This package is built from
|
||||
source with the optional libvuptsdk and libpqvaptvupt integrations disabled.
|
||||
EOF
|
||||
|
||||
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'
|
||||
package_tmp=$work/$(basename -- "$output")
|
||||
dpkg-deb --build --root-owner-group "$stage" "$package_tmp" >/dev/null
|
||||
dpkg-deb --info "$package_tmp" >/dev/null
|
||||
dpkg-deb --contents "$package_tmp" > "$work/contents.txt"
|
||||
if grep -Eq '(/usr/bin/vaptvupt$|\.(o|obj|a|so|so\.[^/]+|dll|dylib)$)' "$work/contents.txt"; then
|
||||
cat "$work/contents.txt" >&2
|
||||
die 'forbidden alias or compiled library/object found in .deb contents'
|
||||
fi
|
||||
|
||||
mkdir -p -- "$extract"
|
||||
dpkg-deb --extract "$package_tmp" "$extract"
|
||||
bash scripts/test-installed-zupt.sh "$extract/usr/bin/zupt"
|
||||
|
||||
mv -- "$package_tmp" "$output"
|
||||
sha256sum "$output"
|
||||
printf 'PASS: built and extracted-package-tested %s\n' "$output"
|
||||
|
|
|
|||
|
|
@ -1,188 +1,228 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env 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")/.."
|
||||
set -Eeuo pipefail
|
||||
|
||||
VERSION="${VERSION:-2.4.7}"
|
||||
ARCH="${ARCH:-$(uname -m)}" # x86_64 or arm64
|
||||
NAME="Zupt-${VERSION}-${ARCH}"
|
||||
STAGE="/tmp/${NAME}.app/Contents"
|
||||
umask 022
|
||||
export LC_ALL=C
|
||||
|
||||
# ── 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
|
||||
die() {
|
||||
printf 'FAIL: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[[ $(uname -s) == Darwin ]] || die 'DMG packages must be built and tested on macOS'
|
||||
|
||||
test_macos_binary() (
|
||||
set -Eeuo pipefail
|
||||
|
||||
local candidate=$1 binary test_root archive_size
|
||||
if [[ $candidate == */* ]]; then
|
||||
[[ -x $candidate ]] || die "executable not found: $candidate"
|
||||
binary=$(cd "$(dirname "$candidate")" && pwd -P)/$(basename "$candidate")
|
||||
else
|
||||
binary=$(command -v "$candidate" || true)
|
||||
[[ -n $binary ]] || die "executable not found on PATH: $candidate"
|
||||
fi
|
||||
|
||||
for command_name in cmp dd diff find grep shasum sort; do
|
||||
command -v "$command_name" >/dev/null 2>&1 || \
|
||||
die "required smoke-test command not found: $command_name"
|
||||
done
|
||||
|
||||
test_root=$(mktemp -d "${TMPDIR:-/tmp}/zupt-macos-smoke.XXXXXX")
|
||||
trap 'chmod -R u+rwX "$test_root" 2>/dev/null || true; rm -rf "$test_root"' \
|
||||
EXIT HUP INT TERM
|
||||
mkdir -p "$test_root/input/subdir" "$test_root/output" \
|
||||
"$test_root/password-output" "$test_root/escape-output" "$test_root/outside"
|
||||
printf 'ZUPT macOS package smoke test\n' > "$test_root/input/text file.txt"
|
||||
printf 'conteúdo UTF-8\n' > "$test_root/input/subdir/café-安全.txt"
|
||||
: > "$test_root/input/empty file"
|
||||
dd if=/dev/urandom of="$test_root/input/subdir/random.bin" \
|
||||
bs=4096 count=8 >/dev/null 2>&1
|
||||
printf 'do-not-overwrite\n' > "$test_root/outside/sentinel"
|
||||
|
||||
"$binary" --version > "$test_root/version.log" 2>&1
|
||||
grep -q '^zupt ' "$test_root/version.log"
|
||||
"$binary" --help > "$test_root/help.log" 2>&1
|
||||
grep -q '^Usage:' "$test_root/help.log"
|
||||
if "$binary" --definitely-invalid-option >/dev/null 2>&1; then
|
||||
die 'invalid option returned success'
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$test_root"
|
||||
"$binary" compress plain.zupt input
|
||||
"$binary" test plain.zupt
|
||||
"$binary" extract -o output plain.zupt
|
||||
)
|
||||
diff -r "$test_root/input" "$test_root/output/input"
|
||||
(
|
||||
cd "$test_root/input"
|
||||
find . -type f -exec shasum -a 256 {} \; | sort
|
||||
) > "$test_root/original.sha256"
|
||||
(
|
||||
cd "$test_root/output/input"
|
||||
find . -type f -exec shasum -a 256 {} \; | sort
|
||||
) > "$test_root/extracted.sha256"
|
||||
cmp "$test_root/original.sha256" "$test_root/extracted.sha256"
|
||||
|
||||
(
|
||||
cd "$test_root"
|
||||
"$binary" compress -p 'ZUPT-test-password-2026!' \
|
||||
password.zupt 'input/text file.txt'
|
||||
"$binary" test -p 'ZUPT-test-password-2026!' password.zupt
|
||||
"$binary" extract -p 'ZUPT-test-password-2026!' \
|
||||
-o password-output password.zupt
|
||||
)
|
||||
cmp "$test_root/input/text file.txt" \
|
||||
"$test_root/password-output/input/text file.txt"
|
||||
if "$binary" extract -p incorrect-password -o "$test_root/wrong-password" \
|
||||
"$test_root/password.zupt" >/dev/null 2>&1; then
|
||||
die 'incorrect password returned success'
|
||||
fi
|
||||
|
||||
archive_size=$(wc -c < "$test_root/plain.zupt")
|
||||
((archive_size > 32)) || die 'archive unexpectedly small'
|
||||
dd if="$test_root/plain.zupt" of="$test_root/corrupt.zupt" bs=1 \
|
||||
count="$((archive_size - 17))" >/dev/null 2>&1
|
||||
if "$binary" test "$test_root/corrupt.zupt" >/dev/null 2>&1; then
|
||||
die 'truncated archive returned success'
|
||||
fi
|
||||
|
||||
ln -s "$test_root/outside" "$test_root/escape-output/input"
|
||||
"$binary" extract -o "$test_root/escape-output" \
|
||||
"$test_root/plain.zupt" >/dev/null 2>&1 || true
|
||||
[[ $(<"$test_root/outside/sentinel") == do-not-overwrite ]] || \
|
||||
die 'extraction overwrote outside sentinel'
|
||||
[[ ! -e $test_root/outside/text\ file.txt && ! -e $test_root/outside/subdir ]] || \
|
||||
die 'extraction escaped through a destination symlink'
|
||||
|
||||
[[ $(id -u) -ne 0 ]] || die 'macOS package smoke test unexpectedly ran as root'
|
||||
printf 'PASS: native macOS package functional test suite\n'
|
||||
)
|
||||
|
||||
if [[ ${1:-} == --test-binary ]]; then
|
||||
(($# == 2)) || die 'usage: build-dmg.sh --test-binary PATH'
|
||||
test_macos_binary "$2"
|
||||
exit 0
|
||||
elif (($# != 0)); then
|
||||
die 'usage: build-dmg.sh [--test-binary PATH]'
|
||||
fi
|
||||
|
||||
# ── Build zupt (universal binary if possible) ──
|
||||
echo "[dmg] Building zupt"
|
||||
repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
cd -- "$repo_root"
|
||||
|
||||
header_version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
version=${VERSION:-$header_version}
|
||||
[[ -n $version && $version == "$header_version" ]] || \
|
||||
die "VERSION '$version' does not match include/zupt.h '$header_version'"
|
||||
[[ $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || die "invalid package version: $version"
|
||||
native_arch=$(uname -m)
|
||||
arch=${ARCH:-$native_arch}
|
||||
[[ $arch == "$native_arch" ]] || \
|
||||
die "ARCH=$arch does not match the native macOS architecture $native_arch"
|
||||
|
||||
dist_dir=${DIST_DIR:-${TMPDIR:-/tmp}/zupt-release}
|
||||
mkdir -p "$dist_dir"
|
||||
dist_dir=$(cd "$dist_dir" && pwd -P)
|
||||
output=$dist_dir/ZUPT-${version}-macOS-${arch}.dmg
|
||||
[[ ! -e $output ]] || die "refusing to overwrite existing output: $output"
|
||||
|
||||
for command_name in make clang hdiutil otool plutil shasum; do
|
||||
command -v -- "$command_name" >/dev/null 2>&1 || die "required command not found: $command_name"
|
||||
done
|
||||
run_checks=${RUN_CHECKS:-1}
|
||||
[[ $run_checks == 0 || $run_checks == 1 ]] || die 'RUN_CHECKS must be 0 or 1'
|
||||
if [[ $run_checks == 1 ]]; then
|
||||
command -v git >/dev/null 2>&1 || die 'git is required when RUN_CHECKS=1'
|
||||
fi
|
||||
|
||||
jobs=${JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || printf '1')}
|
||||
work=$(mktemp -d "${TMPDIR:-/tmp}/zupt-dmg.XXXXXXXX")
|
||||
app=$work/ZUPT.app
|
||||
contents=$app/Contents
|
||||
dmg_root=$work/dmg-root
|
||||
dmg_tmp=$work/$(basename "$output")
|
||||
mkdir -p "$contents/MacOS" "$contents/Resources" "$dmg_root"
|
||||
|
||||
cleanup() {
|
||||
make -C "$repo_root" clean >/dev/null 2>&1 || true
|
||||
chmod -R u+rwX "$work" 2>/dev/null || true
|
||||
rm -rf "$work"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
printf '[dmg] source-only build of ZUPT %s (%s)\n' "$version" "$arch"
|
||||
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)"
|
||||
make -j"$jobs" CC=clang V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0
|
||||
if [[ $run_checks == 1 ]]; then
|
||||
make CC=clang V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 check
|
||||
fi
|
||||
test_macos_binary "$repo_root/zupt"
|
||||
|
||||
install -m 0755 zupt "$contents/MacOS/zupt"
|
||||
for document in README.md CHANGELOG.md LICENSE LICENSE-AGPL-3.0 LICENSE-GPL-3.0 LICENSE-BSD-2-Clause LICENSE-BSD-3-Clause LICENSE-CC0-1.0 NOTICE THIRD-PARTY-NOTICES.md; do
|
||||
[[ ! -f $document ]] || install -m 0644 "$document" "$contents/Resources/"
|
||||
done
|
||||
|
||||
if otool -l "$contents/MacOS/zupt" | grep -q 'cmd LC_RPATH'; then
|
||||
otool -l "$contents/MacOS/zupt" >&2
|
||||
die 'macOS executable contains LC_RPATH'
|
||||
fi
|
||||
if otool -L "$contents/MacOS/zupt" | grep -Eqi \
|
||||
'(vendor/|libvuptsdk|libpqvaptvupt|/home/|/Users/[^/]+/|/opt/(homebrew|local)/|/usr/local/)'; then
|
||||
otool -L "$contents/MacOS/zupt" >&2
|
||||
die 'macOS executable references a build path or vendored optional library'
|
||||
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
|
||||
cat > "$contents/Info.plist" <<EOF
|
||||
<?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">
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://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>
|
||||
<key>CFBundleIdentifier</key><string>dev.zupt.cli</string>
|
||||
<key>CFBundleName</key><string>ZUPT</string>
|
||||
<key>CFBundleDisplayName</key><string>ZUPT</string>
|
||||
<key>CFBundleExecutable</key><string>zupt</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>CFBundleVersion</key><string>$version</string>
|
||||
<key>CFBundleShortVersionString</key><string>$version</string>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
EOF
|
||||
plutil -lint "$contents/Info.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
|
||||
if [[ -n ${CODESIGN_IDENTITY:-} ]]; then
|
||||
codesign --force --options runtime --timestamp --sign "$CODESIGN_IDENTITY" "$app"
|
||||
codesign --verify --deep --strict "$app"
|
||||
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"
|
||||
cp -R "$app" "$dmg_root/ZUPT.app"
|
||||
cat > "$dmg_root/Install ZUPT.command" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
installer_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)
|
||||
source_binary=$installer_dir/ZUPT.app/Contents/MacOS/zupt
|
||||
target_dir=/usr/local/bin
|
||||
if [[ ! -d $target_dir || ! -w $target_dir ]]; then
|
||||
target_dir=${XDG_BIN_HOME:-$HOME/.local/bin}
|
||||
mkdir -p "$target_dir"
|
||||
fi
|
||||
install -m 0755 "$source_binary" "$target_dir/zupt"
|
||||
printf 'Installed %s\n' "$target_dir/zupt"
|
||||
"$target_dir/zupt" --version
|
||||
EOF
|
||||
chmod 0755 "$dmg_root/Install ZUPT.command"
|
||||
for document in README.md CHANGELOG.md LICENSE LICENSE-AGPL-3.0 LICENSE-GPL-3.0 LICENSE-BSD-2-Clause LICENSE-BSD-3-Clause LICENSE-CC0-1.0 NOTICE THIRD-PARTY-NOTICES.md; do
|
||||
[[ ! -f $document ]] || install -m 0644 "$document" "$dmg_root/"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Built: $DMG ($(du -h "$DMG" | cut -f1))"
|
||||
echo "Users mount and drag 'Zupt.app' or double-click 'Install Zupt.command'."
|
||||
hdiutil create -fs HFS+ -srcfolder "$dmg_root" -volname "ZUPT $version" \
|
||||
-format UDZO -ov "$dmg_tmp"
|
||||
hdiutil verify "$dmg_tmp"
|
||||
|
||||
mv "$dmg_tmp" "$output"
|
||||
shasum -a 256 "$output"
|
||||
printf 'PASS: built and native-binary-tested %s\n' "$output"
|
||||
|
|
|
|||
|
|
@ -1,121 +1,140 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# Build zupt-gui AppImage. Since the GUI is pure Python + Qt, the AppDir
|
||||
# bundles only the Python source and metadata; it relies on system
|
||||
# python3 + PyQt6/PySide6 at runtime. This keeps the AppImage tiny
|
||||
# (~50 KB) and lets it work on any Linux with Qt6 Python bindings.
|
||||
#
|
||||
# For a true self-contained AppImage with bundled Python interpreter,
|
||||
# use python-appimage (https://github.com/niess/python-appimage) on
|
||||
# the build host — it produces a ~80 MB AppImage. The portable variant
|
||||
# below is the better tradeoff for most distributions.
|
||||
set -e
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
VERSION="${VERSION:-1.2.0}"
|
||||
APPDIR="/tmp/vaptvupt-gui.AppDir"
|
||||
# Build a dependency-light GUI AppImage. The ZUPT CLI is compiled from this
|
||||
# tree and bundled; Python 3 plus PySide6 or PyQt6 remain host requirements.
|
||||
|
||||
rm -rf "$APPDIR"
|
||||
mkdir -p "$APPDIR/usr/bin" \
|
||||
"$APPDIR/usr/lib/vaptvupt-gui" \
|
||||
"$APPDIR/usr/share/applications" \
|
||||
"$APPDIR/usr/share/icons/hicolor/256x256/apps"
|
||||
set -Eeuo pipefail
|
||||
umask 022
|
||||
export LC_ALL=C
|
||||
|
||||
# Python source
|
||||
install -m 644 gui/src/zupt_gui.py "$APPDIR/usr/lib/vaptvupt-gui/"
|
||||
die() { printf 'FAIL: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
# Wrapper
|
||||
cat > "$APPDIR/usr/bin/vaptvupt-gui" <<'WRAP'
|
||||
[[ $(uname -s) == Linux ]] || die 'AppImage packages must be built on Linux'
|
||||
repo_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
cd -- "$repo_root"
|
||||
header_version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
version=${VERSION:-$header_version}
|
||||
[[ $version == "$header_version" && $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || \
|
||||
die "VERSION '$version' does not match include/zupt.h '$header_version'"
|
||||
|
||||
case $(uname -m) in
|
||||
x86_64|amd64) native_arch=x86_64 ;;
|
||||
aarch64|arm64) native_arch=aarch64 ;;
|
||||
*) die "unsupported native AppImage architecture: $(uname -m)" ;;
|
||||
esac
|
||||
case ${ARCH:-$native_arch} in
|
||||
x86_64|amd64) arch=x86_64 ;;
|
||||
aarch64|arm64) arch=aarch64 ;;
|
||||
*) die "unsupported AppImage architecture: ${ARCH:-$native_arch}" ;;
|
||||
esac
|
||||
[[ $arch == "$native_arch" ]] || \
|
||||
die "ARCH=$arch does not match the native build architecture $native_arch"
|
||||
|
||||
dist_dir=${DIST_DIR:-${TMPDIR:-/tmp}/zupt-release}
|
||||
mkdir -p -- "$dist_dir"
|
||||
dist_dir=$(cd -- "$dist_dir" && pwd -P)
|
||||
case $dist_dir/ in "$repo_root"/*) die 'DIST_DIR must be outside the repository' ;; esac
|
||||
output=$dist_dir/ZUPT-GUI-$version-linux-$arch.AppImage
|
||||
[[ ! -e $output ]] || die "refusing to overwrite existing output: $output"
|
||||
|
||||
appimagetool=${APPIMAGETOOL:-appimagetool}
|
||||
appimagetool=$(command -v -- "$appimagetool" 2>/dev/null || true)
|
||||
[[ -n $appimagetool ]] || die 'appimagetool not found; no network fallback is performed'
|
||||
runtime_file=${APPIMAGE_RUNTIME_FILE:-}
|
||||
[[ -n $runtime_file && -s $runtime_file ]] || \
|
||||
die 'set APPIMAGE_RUNTIME_FILE to a non-empty verified local type-2 runtime'
|
||||
runtime_file=$(cd -- "$(dirname -- "$runtime_file")" && pwd -P)/$(basename -- "$runtime_file")
|
||||
runtime_compliance_file=${APPIMAGE_RUNTIME_COMPLIANCE_FILE:-}
|
||||
[[ -n $runtime_compliance_file && -s $runtime_compliance_file ]] || \
|
||||
die 'set APPIMAGE_RUNTIME_COMPLIANCE_FILE to the audited runtime license/source-compliance notice'
|
||||
runtime_compliance_file=$(cd -- "$(dirname -- "$runtime_compliance_file")" && pwd -P)/$(basename -- "$runtime_compliance_file")
|
||||
|
||||
for command_name in make python3 readelf file sha256sum; do
|
||||
command -v -- "$command_name" >/dev/null 2>&1 || \
|
||||
die "required command not found: $command_name"
|
||||
done
|
||||
python3 -c 'import PySide6.QtWidgets' 2>/dev/null || \
|
||||
python3 -c 'import PyQt6.QtWidgets' 2>/dev/null || \
|
||||
die 'the build/test host needs PySide6 or PyQt6; the AppImage does not download it'
|
||||
|
||||
jobs=${JOBS:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf '1')}
|
||||
work=$(mktemp -d "${TMPDIR:-/tmp}/zupt-gui-appimage.XXXXXXXX")
|
||||
appdir=$work/ZUPT-GUI.AppDir
|
||||
image_tmp=$work/$(basename -- "$output")
|
||||
cleanup() {
|
||||
make -C "$repo_root" clean >/dev/null 2>&1 || true
|
||||
chmod -R u+rwX "$work" 2>/dev/null || true
|
||||
rm -rf -- "$work"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
make clean
|
||||
make -j"$jobs" V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0
|
||||
make V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 check
|
||||
make DESTDIR="$appdir" PREFIX=/usr WITH_SDK=0 WITH_PQBOX=0 \
|
||||
INSTALL_LEGACY_ALIAS=0 install
|
||||
|
||||
binary=$appdir/usr/bin/zupt
|
||||
[[ -x $binary ]] || die 'source-built CLI is missing from AppDir'
|
||||
[[ ! -e $appdir/usr/bin/vaptvupt ]] || die 'legacy vaptvupt alias must not be packaged'
|
||||
if readelf -d "$binary" 2>/dev/null | grep -Eq '(RPATH|RUNPATH|libvuptsdk|libpqvaptvupt|vendor/)'; then
|
||||
readelf -d "$binary" >&2
|
||||
die 'CLI has RPATH/RUNPATH or an optional-library reference'
|
||||
fi
|
||||
|
||||
install -Dm0644 gui/src/zupt_gui.py "$appdir/usr/lib/zupt-gui/zupt_gui.py"
|
||||
install -Dm0644 gui/assets/zupt-icon.png \
|
||||
"$appdir/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
|
||||
install -Dm0644 gui/packaging/zupt-gui.desktop \
|
||||
"$appdir/usr/share/applications/zupt-gui.desktop"
|
||||
install -d "$appdir/usr/share/licenses/zupt"
|
||||
install -m 0644 LICENSE LICENSE-AGPL-3.0 LICENSE-GPL-3.0 \
|
||||
LICENSE-BSD-2-Clause LICENSE-BSD-3-Clause LICENSE-CC0-1.0 NOTICE \
|
||||
THIRD-PARTY-NOTICES.md "$appdir/usr/share/licenses/zupt/"
|
||||
install -d "$appdir/usr/share/licenses/zupt-gui"
|
||||
install -m 0644 LICENSE-AGPL-3.0 \
|
||||
"$appdir/usr/share/licenses/zupt-gui/LICENSE-AGPL-3.0"
|
||||
install -m 0644 gui/LICENSE-GUI \
|
||||
"$appdir/usr/share/licenses/zupt-gui/LICENSE-GUI"
|
||||
install -m 0644 gui/assets/README.md \
|
||||
"$appdir/usr/share/licenses/zupt-gui/ASSET-PROVENANCE.md"
|
||||
install -Dm0644 "$runtime_compliance_file" \
|
||||
"$appdir/usr/share/licenses/zupt/AppImage-runtime-compliance.txt"
|
||||
cp -- "$appdir/usr/share/icons/hicolor/256x256/apps/zupt-gui.png" \
|
||||
"$appdir/zupt-gui.png"
|
||||
|
||||
cat >"$appdir/usr/bin/zupt-gui" <<'WRAP'
|
||||
#!/bin/sh
|
||||
exec python3 "$(dirname "$0")/../lib/vaptvupt-gui/zupt_gui.py" "$@"
|
||||
here=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P)
|
||||
export ZUPT_BIN=$here/bin/zupt
|
||||
exec python3 "$here/lib/zupt-gui/zupt_gui.py" "$@"
|
||||
WRAP
|
||||
chmod 755 "$APPDIR/usr/bin/vaptvupt-gui"
|
||||
|
||||
# Desktop file
|
||||
cat > "$APPDIR/vaptvupt-gui.desktop" <<'DESKTOP'
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=VaptVupt GUI
|
||||
GenericName=Backup and Compression Utility
|
||||
Comment=Post-quantum backup with HKDF combiner, key commitment, HPKE binding
|
||||
Exec=vaptvupt-gui %f
|
||||
Icon=vaptvupt-gui
|
||||
Terminal=false
|
||||
Categories=Utility;Archiving;Compression;Security;
|
||||
StartupNotify=true
|
||||
DESKTOP
|
||||
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/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
|
||||
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('$APPDIR/zupt-gui.png','wb').write(png(256, 256, (88, 92, 215)))
|
||||
"
|
||||
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
|
||||
# back to system zupt CLI if not present in /usr/bin alongside.
|
||||
cat > "$APPDIR/AppRun" <<'APPRUN'
|
||||
chmod 0755 "$appdir/usr/bin/zupt-gui"
|
||||
cat >"$appdir/AppRun" <<'APPRUN'
|
||||
#!/bin/sh
|
||||
HERE="$(dirname "$(readlink -f "$0")")"
|
||||
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
|
||||
vaptvupt-gui: python3 is not installed.
|
||||
Install: sudo apt install python3 (Debian/Ubuntu)
|
||||
sudo dnf install python3 (Fedora/RHEL)
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! python3 -c 'import PyQt6.QtWidgets' 2>/dev/null \
|
||||
&& ! python3 -c 'import PySide6.QtWidgets' 2>/dev/null; then
|
||||
cat >&2 <<EOF
|
||||
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
|
||||
pip (any): pip install --user PySide6
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v vaptvupt >/dev/null 2>&1 && ! command -v zupt >/dev/null 2>&1; then
|
||||
cat >&2 <<EOF
|
||||
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/vaptvupt-gui" "$@"
|
||||
appdir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
|
||||
exec "$appdir/usr/bin/zupt-gui" "$@"
|
||||
APPRUN
|
||||
chmod 755 "$APPDIR/AppRun"
|
||||
chmod 0755 "$appdir/AppRun"
|
||||
cp -- "$appdir/usr/share/applications/zupt-gui.desktop" "$appdir/"
|
||||
|
||||
# Build AppImage
|
||||
if command -v appimagetool >/dev/null 2>&1; then
|
||||
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 "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/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 vaptvupt-gui.AppDir VaptVupt-GUI-$VERSION-x86_64.AppImage"
|
||||
fi
|
||||
forbidden=$(find "$appdir" -type f \( \
|
||||
-name '*.o' -o -name '*.obj' -o -name '*.a' -o -name '*.so' -o \
|
||||
-name '*.so.*' -o -name '*.dll' -o -name '*.dylib' \
|
||||
\) -print)
|
||||
[[ -z $forbidden ]] || { printf '%s\n' "$forbidden" >&2; die 'compiled library/object in AppDir'; }
|
||||
|
||||
QT_QPA_PLATFORM=offscreen "$appdir/AppRun" --version | grep -Fq "zupt-gui $version" || \
|
||||
die 'AppDir GUI/CLI integration check failed'
|
||||
export ARCH=$arch APPIMAGE_EXTRACT_AND_RUN=1
|
||||
"$appimagetool" --runtime-file "$runtime_file" "$appdir" "$image_tmp"
|
||||
chmod 0755 "$image_tmp"
|
||||
file "$image_tmp" | grep -q ELF || die 'generated AppImage does not have ELF magic'
|
||||
QT_QPA_PLATFORM=offscreen "$image_tmp" --version | grep -Fq "zupt-gui $version" || \
|
||||
die 'generated AppImage execution check failed'
|
||||
|
||||
mv -- "$image_tmp" "$output"
|
||||
sha256sum "$output"
|
||||
printf 'PASS: built and execution-tested %s\n' "$output"
|
||||
|
|
|
|||
|
|
@ -1,181 +1,113 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# Build zupt-gui .deb (Python/Qt GUI). Works with PyQt6 OR PySide6.
|
||||
set -e
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
VERSION="${VERSION:-1.2.0}"
|
||||
ARCH="all"
|
||||
PKG="vaptvupt-gui_${VERSION}_${ARCH}"
|
||||
ROOT="/tmp/$PKG"
|
||||
# Build the architecture-independent GUI package from tracked source. The CLI
|
||||
# dependency is built and tested in baseline mode but is packaged separately.
|
||||
|
||||
rm -rf "$ROOT"
|
||||
mkdir -p "$ROOT/DEBIAN" \
|
||||
"$ROOT/usr/bin" \
|
||||
"$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/vaptvupt-gui"
|
||||
set -Eeuo pipefail
|
||||
umask 022
|
||||
export LC_ALL=C
|
||||
|
||||
# Source files
|
||||
install -m 644 gui/src/zupt_gui.py "$ROOT/usr/lib/vaptvupt-gui/"
|
||||
die() { printf 'FAIL: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
# Wrapper script in /usr/bin
|
||||
cat > "$ROOT/usr/bin/vaptvupt-gui" <<'WRAP'
|
||||
#!/bin/sh
|
||||
exec python3 /usr/lib/vaptvupt-gui/zupt_gui.py "$@"
|
||||
WRAP
|
||||
chmod 755 "$ROOT/usr/bin/vaptvupt-gui"
|
||||
# v3.0.0: legacy zupt-gui symlink
|
||||
ln -sf vaptvupt-gui "$ROOT/usr/bin/zupt-gui"
|
||||
repo_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
cd -- "$repo_root"
|
||||
|
||||
# Desktop entry
|
||||
cat > "$ROOT/usr/share/applications/vaptvupt-gui.desktop" <<'DESKTOP'
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=VaptVupt GUI
|
||||
GenericName=Backup and Compression Utility
|
||||
Comment=Post-quantum backup with HKDF combiner, key commitment, HPKE binding
|
||||
Exec=vaptvupt-gui %f
|
||||
Icon=vaptvupt-gui
|
||||
Terminal=false
|
||||
Categories=Utility;Archiving;Compression;Security;
|
||||
StartupNotify=true
|
||||
MimeType=application/x-zupt;
|
||||
Keywords=archive;compression;encryption;post-quantum;backup;
|
||||
DESKTOP
|
||||
header_version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
version=${VERSION:-$header_version}
|
||||
[[ $version == "$header_version" && $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || \
|
||||
die "VERSION '$version' does not match include/zupt.h '$header_version'"
|
||||
|
||||
# Man page
|
||||
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
|
||||
dist_dir=${DIST_DIR:-${TMPDIR:-/tmp}/zupt-release}
|
||||
mkdir -p -- "$dist_dir"
|
||||
dist_dir=$(cd -- "$dist_dir" && pwd -P)
|
||||
case $dist_dir/ in "$repo_root"/*) die 'DIST_DIR must be outside the repository' ;; esac
|
||||
output=$dist_dir/zupt-gui_${version}_all.deb
|
||||
[[ ! -e $output ]] || die "refusing to overwrite existing output: $output"
|
||||
|
||||
# Icon
|
||||
if [ -f gui/assets/zupt-icon.png ]; then
|
||||
cp gui/assets/zupt-icon.png "$ROOT/usr/share/icons/hicolor/256x256/apps/vaptvupt-gui.png"
|
||||
else
|
||||
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('$ROOT/usr/share/icons/hicolor/256x256/apps/zupt-gui.png','wb').write(png(256, 256, (88, 92, 215)))
|
||||
"
|
||||
fi
|
||||
for command_name in make python3 dpkg-deb gzip sha256sum; do
|
||||
command -v -- "$command_name" >/dev/null 2>&1 || \
|
||||
die "required command not found: $command_name"
|
||||
done
|
||||
|
||||
# Docs
|
||||
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"
|
||||
jobs=${JOBS:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf '1')}
|
||||
work=$(mktemp -d "${TMPDIR:-/tmp}/zupt-gui-deb.XXXXXXXX")
|
||||
stage=$work/stage
|
||||
extract=$work/extract
|
||||
cleanup() {
|
||||
make -C "$repo_root" clean >/dev/null 2>&1 || true
|
||||
chmod -R u+rwX "$work" 2>/dev/null || true
|
||||
rm -rf -- "$work"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
cat > "$ROOT/usr/share/doc/vaptvupt-gui/copyright" <<'COPYRIGHT'
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: vaptvupt-gui
|
||||
Upstream-Contact: Cristian Cezar Moisés <zupt@riseup.net>
|
||||
Source: https://git.securityops.co/cristiancmoises/zupt
|
||||
printf '[GUI deb] validating source-only CLI dependency %s\n' "$version"
|
||||
make clean
|
||||
make -j"$jobs" V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0
|
||||
make V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 check
|
||||
./zupt version | grep -Fq "zupt $version" || die 'CLI version check failed'
|
||||
|
||||
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.
|
||||
COPYRIGHT
|
||||
PYTHONDONTWRITEBYTECODE=1 python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
source = Path("gui/src/zupt_gui.py").read_text(encoding="utf-8")
|
||||
compile(source, "gui/src/zupt_gui.py", "exec")
|
||||
PY
|
||||
|
||||
# Control
|
||||
INSTALLED_SIZE=$(du -sk "$ROOT" | cut -f1)
|
||||
cat > "$ROOT/DEBIAN/control" <<EOF
|
||||
Package: vaptvupt-gui
|
||||
Version: $VERSION
|
||||
mkdir -p -- "$stage"
|
||||
bash gui/install.sh --destdir "$stage" --prefix /usr
|
||||
[[ ! -e $stage/usr/bin/vaptvupt-gui ]] || die 'legacy vaptvupt-gui alias must not be packaged'
|
||||
|
||||
install -d -- "$stage/usr/share/doc/zupt-gui" "$stage/DEBIAN"
|
||||
install -m 0644 -- gui/README.md "$stage/usr/share/doc/zupt-gui/README.md"
|
||||
gzip -9n -c CHANGELOG.md >"$stage/usr/share/doc/zupt-gui/changelog.gz"
|
||||
install -m 0644 -- LICENSE-AGPL-3.0 "$stage/usr/share/doc/zupt-gui/copyright"
|
||||
gzip -9n -- "$stage/usr/share/man/man1/zupt-gui.1"
|
||||
|
||||
installed_kib=$(du -sk "$stage/usr" | awk '{print $1}')
|
||||
cat >"$stage/DEBIAN/control" <<EOF
|
||||
Package: zupt-gui
|
||||
Version: $version
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: $ARCH
|
||||
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 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.
|
||||
Architecture: all
|
||||
Depends: python3 (>= 3.9), python3-pyqt6 | python3-pyside6.qtwidgets, zupt (= $version)
|
||||
Installed-Size: $installed_kib
|
||||
Maintainer: Cristian Cezar Moisés <sac@securityops.co>
|
||||
Homepage: https://github.com/cristiancmoises/zupt
|
||||
Description: Qt graphical interface for the ZUPT backup utility
|
||||
The GUI creates, inspects, verifies, and extracts .zupt archives through the
|
||||
separately packaged zupt command. Optional SDK and PQ-box controls are
|
||||
shown only when the installed command reports those integrations enabled.
|
||||
EOF
|
||||
|
||||
# Postinst: refresh icon cache + desktop database, print first-run guidance
|
||||
cat > "$ROOT/DEBIAN/postinst" <<'POSTINST'
|
||||
#!/bin/sh
|
||||
set -e
|
||||
if [ -x /usr/bin/update-desktop-database ]; then
|
||||
update-desktop-database -q /usr/share/applications || true
|
||||
fi
|
||||
if [ -x /usr/bin/gtk-update-icon-cache ]; then
|
||||
gtk-update-icon-cache -q /usr/share/icons/hicolor || true
|
||||
forbidden=$(find "$stage" -type f \( \
|
||||
-name '*.o' -o -name '*.obj' -o -name '*.a' -o -name '*.so' -o \
|
||||
-name '*.so.*' -o -name '*.dll' -o -name '*.dylib' -o -name '*.exe' \
|
||||
\) -print)
|
||||
[[ -z $forbidden ]] || { printf '%s\n' "$forbidden" >&2; die 'compiled artifact in GUI package'; }
|
||||
|
||||
package_tmp=$work/$(basename -- "$output")
|
||||
source_epoch=${SOURCE_DATE_EPOCH:-$(sed -n '1p' .source-date-epoch 2>/dev/null || true)}
|
||||
[[ $source_epoch =~ ^[0-9]+$ ]] || die 'SOURCE_DATE_EPOCH is not available'
|
||||
SOURCE_DATE_EPOCH=$source_epoch dpkg-deb -Zxz --build --root-owner-group \
|
||||
"$stage" "$package_tmp" >/dev/null
|
||||
dpkg-deb --info "$package_tmp" >/dev/null
|
||||
dpkg-deb --contents "$package_tmp" >"$work/contents.txt"
|
||||
grep -q './usr/bin/zupt-gui' "$work/contents.txt" || die 'GUI launcher missing from .deb'
|
||||
if grep -Eq '(/usr/bin/vaptvupt-gui|\.(o|obj|a|so|so\.[^/]+|dll|dylib|exe)$)' "$work/contents.txt"; then
|
||||
cat "$work/contents.txt" >&2
|
||||
die 'forbidden compatibility alias or compiled artifact in .deb'
|
||||
fi
|
||||
|
||||
# Friendly first-run check: warn the user if no Qt6 binding is installed.
|
||||
# We don't fail the install (deb deps already enforce this); we just print
|
||||
# clear guidance for users who saw "unmet dependencies" earlier.
|
||||
if ! python3 -c 'import PyQt6.QtWidgets' 2>/dev/null \
|
||||
&& ! python3 -c 'import PySide6.QtWidgets' 2>/dev/null; then
|
||||
cat << 'MSG'
|
||||
mkdir -p -- "$extract"
|
||||
dpkg-deb --extract "$package_tmp" "$extract"
|
||||
PYTHONDONTWRITEBYTECODE=1 python3 - <<PY
|
||||
from pathlib import Path
|
||||
p = Path("$extract/usr/lib/zupt-gui/zupt_gui.py")
|
||||
compile(p.read_text(encoding="utf-8"), str(p), "exec")
|
||||
PY
|
||||
|
||||
──────────────────────────────────────────────────────────────────────
|
||||
vaptvupt-gui installed, but no Qt6 Python binding is available.
|
||||
|
||||
Install one of the following to enable the GUI:
|
||||
|
||||
Debian/Ubuntu/Mint: sudo apt install python3-pyqt6
|
||||
Fedora/RHEL/Rocky: sudo dnf install python3-pyqt6
|
||||
Arch/Manjaro: sudo pacman -S python-pyqt6
|
||||
pip (any distro): pip install --user PySide6
|
||||
|
||||
After installing the binding, launch with: vaptvupt-gui
|
||||
──────────────────────────────────────────────────────────────────────
|
||||
|
||||
MSG
|
||||
fi
|
||||
|
||||
# Same friendly warning if zupt CLI not installed.
|
||||
if ! command -v vaptvupt >/dev/null 2>&1 && ! command -v zupt >/dev/null 2>&1; then
|
||||
cat << 'MSG'
|
||||
|
||||
──────────────────────────────────────────────────────────────────────
|
||||
vaptvupt-gui needs the 'vaptvupt' CLI to function. Install it:
|
||||
|
||||
Debian/Ubuntu/Mint: sudo dpkg -i vaptvupt_3.0.0_amd64.deb
|
||||
(followed by: sudo apt --fix-broken install)
|
||||
──────────────────────────────────────────────────────────────────────
|
||||
|
||||
MSG
|
||||
fi
|
||||
exit 0
|
||||
POSTINST
|
||||
chmod 755 "$ROOT/DEBIAN/postinst"
|
||||
|
||||
cat > "$ROOT/DEBIAN/postrm" <<'POSTRM'
|
||||
#!/bin/sh
|
||||
set -e
|
||||
if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
|
||||
if [ -x /usr/bin/update-desktop-database ]; then
|
||||
update-desktop-database -q /usr/share/applications || true
|
||||
fi
|
||||
if [ -x /usr/bin/gtk-update-icon-cache ]; then
|
||||
gtk-update-icon-cache -q /usr/share/icons/hicolor || true
|
||||
fi
|
||||
fi
|
||||
POSTRM
|
||||
chmod 755 "$ROOT/DEBIAN/postrm"
|
||||
|
||||
dpkg-deb -Zxz --build --root-owner-group "$ROOT" "/tmp/$PKG.deb"
|
||||
echo "Built: /tmp/$PKG.deb"
|
||||
dpkg-deb --info "/tmp/$PKG.deb" | head -12
|
||||
mv -- "$package_tmp" "$output"
|
||||
sha256sum "$output"
|
||||
printf 'PASS: built and content-validated %s\n' "$output"
|
||||
|
|
|
|||
|
|
@ -1,151 +1,175 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# Build zupt-gui RPM. Falls back to SRPM-equivalent tarball if rpmbuild absent.
|
||||
set -e
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
VERSION="${VERSION:-1.2.0}"
|
||||
RPMROOT="/tmp/rpmbuild-vaptvupt-gui"
|
||||
rm -rf "$RPMROOT"
|
||||
mkdir -p "$RPMROOT"/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||
# Build a real noarch RPM and source RPM. Run this in a native RPM build
|
||||
# environment; there is deliberately no --nodeps or tarball fallback.
|
||||
|
||||
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/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/vaptvupt-gui-$VERSION.tar.gz" -C /tmp "vaptvupt-gui-$VERSION"
|
||||
set -Eeuo pipefail
|
||||
umask 022
|
||||
export LC_ALL=C
|
||||
|
||||
cat > "$RPMROOT/SPECS/vaptvupt-gui.spec" <<EOF
|
||||
Name: vaptvupt-gui
|
||||
Version: $VERSION
|
||||
Release: 1%{?dist}
|
||||
Summary: Graphical interface for VaptVupt post-quantum backup utility (formerly zupt-gui)
|
||||
die() { printf 'FAIL: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
repo_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
cd -- "$repo_root"
|
||||
header_version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
version=${VERSION:-$header_version}
|
||||
[[ $version == "$header_version" && $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || \
|
||||
die "VERSION '$version' does not match include/zupt.h '$header_version'"
|
||||
|
||||
dist_dir=${DIST_DIR:-${TMPDIR:-/tmp}/zupt-release}
|
||||
mkdir -p -- "$dist_dir"
|
||||
dist_dir=$(cd -- "$dist_dir" && pwd -P)
|
||||
case $dist_dir/ in "$repo_root"/*) die 'DIST_DIR must be outside the repository' ;; esac
|
||||
|
||||
for command_name in make python3 rpmbuild rpm rpm2cpio cpio tar sha256sum; do
|
||||
command -v -- "$command_name" >/dev/null 2>&1 || \
|
||||
die "required command not found: $command_name"
|
||||
done
|
||||
|
||||
jobs=${JOBS:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf '1')}
|
||||
work=$(mktemp -d "${TMPDIR:-/tmp}/zupt-gui-rpm.XXXXXXXX")
|
||||
top=$work/rpmbuild
|
||||
tree=$work/zupt-gui-$version
|
||||
extract=$work/extract
|
||||
mkdir -p -- "$top"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} \
|
||||
"$tree"/{src,assets,doc} "$extract"
|
||||
cleanup() {
|
||||
make -C "$repo_root" clean >/dev/null 2>&1 || true
|
||||
chmod -R u+rwX "$work" 2>/dev/null || true
|
||||
rm -rf -- "$work"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
printf '[GUI rpm] validating source-only CLI dependency %s\n' "$version"
|
||||
make clean
|
||||
make -j"$jobs" V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0
|
||||
make V=1 WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 check
|
||||
./zupt version | grep -Fq "zupt $version" || die 'CLI version check failed'
|
||||
|
||||
install -m 0644 gui/src/zupt_gui.py "$tree/src/"
|
||||
install -m 0644 gui/assets/zupt-icon.png "$tree/assets/"
|
||||
install -m 0644 gui/packaging/zupt-gui.desktop "$tree/"
|
||||
install -m 0644 doc/zupt-gui.1 "$tree/doc/"
|
||||
install -m 0644 gui/README.md "$tree/README.md"
|
||||
install -m 0644 LICENSE LICENSE-AGPL-3.0 "$tree/"
|
||||
install -m 0644 gui/LICENSE-GUI "$tree/LICENSE-GUI"
|
||||
install -m 0644 gui/assets/README.md "$tree/ASSET-PROVENANCE.md"
|
||||
|
||||
source_epoch=${SOURCE_DATE_EPOCH:-$(sed -n '1p' .source-date-epoch 2>/dev/null || true)}
|
||||
[[ $source_epoch =~ ^[0-9]+$ ]] || die 'SOURCE_DATE_EPOCH is not available'
|
||||
source_tar=$top/SOURCES/zupt-gui-$version.tar.gz
|
||||
tar --sort=name --mtime="@$source_epoch" --owner=0 --group=0 --numeric-owner \
|
||||
-czf "$source_tar" -C "$work" "zupt-gui-$version"
|
||||
|
||||
cat >"$top/SPECS/zupt-gui.spec" <<EOF
|
||||
Name: zupt-gui
|
||||
Version: $version
|
||||
Release: 1
|
||||
Summary: Qt graphical interface for the ZUPT backup utility
|
||||
License: AGPL-3.0-or-later
|
||||
URL: https://git.securityops.co/cristiancmoises/zupt
|
||||
Source0: vaptvupt-gui-%{version}.tar.gz
|
||||
URL: https://github.com/cristiancmoises/zupt
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
BuildArch: noarch
|
||||
|
||||
BuildRequires: python3 >= 3.9
|
||||
Requires: python3 >= 3.9
|
||||
Requires: (python3-qt6 or python3-pyside6 or python3-pyqt6)
|
||||
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
|
||||
Requires: zupt >= %{version}
|
||||
|
||||
%description
|
||||
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
|
||||
installed at startup.
|
||||
ZUPT GUI creates, inspects, verifies, and extracts .zupt archives through
|
||||
the separately packaged zupt command. Optional SDK and PQ-box controls are
|
||||
shown only when that command reports the corresponding integration enabled.
|
||||
|
||||
%prep
|
||||
%autosetup
|
||||
|
||||
%build
|
||||
# nothing to build; pure Python
|
||||
|
||||
%check
|
||||
python3 -c 'from pathlib import Path; p=Path("src/zupt_gui.py"); compile(p.read_text(encoding="utf-8"), str(p), "exec")'
|
||||
|
||||
%install
|
||||
install -Dm0644 src/zupt_gui.py %{buildroot}%{_datadir}/zupt-gui/zupt_gui.py
|
||||
install -Dm0644 zupt-gui.desktop %{buildroot}%{_datadir}/applications/zupt-gui.desktop
|
||||
install -Dm0644 assets/zupt-icon.png %{buildroot}%{_datadir}/icons/hicolor/256x256/apps/zupt-gui.png
|
||||
install -Dm0644 doc/zupt-gui.1 %{buildroot}%{_mandir}/man1/zupt-gui.1
|
||||
mkdir -p %{buildroot}%{_bindir}
|
||||
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}/vaptvupt-gui/
|
||||
|
||||
cat > %{buildroot}%{_bindir}/vaptvupt-gui <<'WRAP'
|
||||
cat >%{buildroot}%{_bindir}/zupt-gui <<'WRAP'
|
||||
#!/bin/sh
|
||||
exec python3 %{_libdir}/vaptvupt-gui/zupt_gui.py "\$@"
|
||||
exec python3 %{_datadir}/zupt-gui/zupt_gui.py "\$@"
|
||||
WRAP
|
||||
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/vaptvupt-gui.desktop <<'DESKTOP'
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=VaptVupt GUI
|
||||
GenericName=Backup and Compression Utility
|
||||
Comment=Post-quantum backup with HKDF combiner, key commitment, HPKE binding
|
||||
Exec=vaptvupt-gui %f
|
||||
Icon=vaptvupt-gui
|
||||
Terminal=false
|
||||
Categories=Utility;Archiving;Compression;Security;
|
||||
StartupNotify=true
|
||||
DESKTOP
|
||||
|
||||
[ -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/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/vaptvupt-gui.png','wb').write(png(256, 256, (88, 92, 215)))
|
||||
"
|
||||
fi
|
||||
|
||||
%post
|
||||
if [ -x /usr/bin/update-desktop-database ]; then
|
||||
update-desktop-database -q /usr/share/applications || :
|
||||
fi
|
||||
if [ -x /usr/bin/gtk-update-icon-cache ]; then
|
||||
gtk-update-icon-cache -q /usr/share/icons/hicolor || :
|
||||
fi
|
||||
|
||||
%postun
|
||||
if [ \$1 -eq 0 ]; then
|
||||
if [ -x /usr/bin/update-desktop-database ]; then
|
||||
update-desktop-database -q /usr/share/applications || :
|
||||
fi
|
||||
fi
|
||||
chmod 0755 %{buildroot}%{_bindir}/zupt-gui
|
||||
|
||||
%files
|
||||
%doc README.md
|
||||
%license LICENSE
|
||||
%{_bindir}/vaptvupt-gui
|
||||
%license LICENSE LICENSE-AGPL-3.0 LICENSE-GUI
|
||||
%doc README.md ASSET-PROVENANCE.md
|
||||
%{_bindir}/zupt-gui
|
||||
%{_libdir}/vaptvupt-gui/zupt_gui.py
|
||||
%{_datadir}/applications/vaptvupt-gui.desktop
|
||||
%{_datadir}/icons/hicolor/256x256/apps/vaptvupt-gui.png
|
||||
%{_datadir}/zupt-gui/zupt_gui.py
|
||||
%{_datadir}/applications/zupt-gui.desktop
|
||||
%{_datadir}/icons/hicolor/256x256/apps/zupt-gui.png
|
||||
%{_mandir}/man1/zupt-gui.1*
|
||||
|
||||
%changelog
|
||||
* 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
|
||||
* Mon Aug 31 2026 Cristian Cezar Moisés <sac@securityops.co> - $version-1
|
||||
- Package the integrated GUI under its restored ZUPT identity.
|
||||
- Require the separately built source-only baseline CLI package.
|
||||
EOF
|
||||
|
||||
if command -v rpmbuild >/dev/null 2>&1; then
|
||||
# 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
|
||||
SRPM_TAR="/tmp/zupt-gui-$VERSION.srpm.tar.gz"
|
||||
tar -czf "$SRPM_TAR" -C "$RPMROOT" SPECS SOURCES
|
||||
echo "rpmbuild unavailable; SRPM-equivalent at: $SRPM_TAR"
|
||||
rpmbuild --define "_topdir $top" -ba "$top/SPECS/zupt-gui.spec"
|
||||
|
||||
mapfile -t main_rpms < <(find "$top/RPMS" -type f -name "zupt-gui-$version-*.noarch.rpm" -print | sort)
|
||||
mapfile -t source_rpms < <(find "$top/SRPMS" -type f -name "zupt-gui-$version-*.src.rpm" -print | sort)
|
||||
[[ ${#main_rpms[@]} -eq 1 ]] || die "expected one GUI RPM, found ${#main_rpms[@]}"
|
||||
[[ ${#source_rpms[@]} -eq 1 ]] || die "expected one GUI source RPM, found ${#source_rpms[@]}"
|
||||
|
||||
main_rpm=${main_rpms[0]}
|
||||
source_rpm=${source_rpms[0]}
|
||||
[[ $(rpm -qp --qf '%{NAME}' "$main_rpm") == zupt-gui ]] || \
|
||||
die 'GUI binary RPM name metadata is not zupt-gui'
|
||||
[[ $(rpm -qp --qf '%{VERSION}' "$main_rpm") == "$version" ]] || \
|
||||
die 'GUI binary RPM version metadata does not match the release'
|
||||
[[ $(rpm -qp --qf '%{RELEASE}' "$main_rpm") == 1 ]] || \
|
||||
die 'GUI binary RPM release metadata is not 1'
|
||||
[[ $(rpm -qp --qf '%{SOURCEPACKAGE}' "$main_rpm") == '(none)' ]] || \
|
||||
die 'GUI binary RPM is marked as a source package'
|
||||
[[ $(rpm -qp --qf '%{SOURCERPM}' "$main_rpm") == "$(basename -- "$source_rpm")" ]] || \
|
||||
die 'GUI binary RPM does not reference the matching source RPM'
|
||||
[[ $(rpm -qp --qf '%{NAME}' "$source_rpm") == zupt-gui ]] || \
|
||||
die 'GUI source RPM name metadata is not zupt-gui'
|
||||
[[ $(rpm -qp --qf '%{VERSION}' "$source_rpm") == "$version" ]] || \
|
||||
die 'GUI source RPM version metadata does not match the release'
|
||||
[[ $(rpm -qp --qf '%{RELEASE}' "$source_rpm") == 1 ]] || \
|
||||
die 'GUI source RPM release metadata is not 1'
|
||||
[[ $(rpm -qp --qf '%{SOURCEPACKAGE}' "$source_rpm") == 1 ]] || \
|
||||
die 'GUI source RPM is not marked as a source package'
|
||||
[[ $(rpm -qp --qf '%{SOURCERPM}' "$source_rpm") == '(none)' ]] || \
|
||||
die 'GUI source RPM unexpectedly references another source RPM'
|
||||
mapfile -t source_members < <(rpm -qpl "$source_rpm" | sort)
|
||||
expected_source_members=("zupt-gui-${version}.tar.gz" zupt-gui.spec)
|
||||
mapfile -t expected_source_members < <(printf '%s\n' "${expected_source_members[@]}" | sort)
|
||||
[[ ${#source_members[@]} -eq 2 && \
|
||||
${source_members[*]} == "${expected_source_members[*]}" ]] || \
|
||||
die 'GUI source RPM payload is not the exact Source0/spec pair'
|
||||
|
||||
rpm -qpl "$main_rpm" >"$work/contents.txt"
|
||||
grep -q '^/usr/bin/zupt-gui$' "$work/contents.txt" || die 'GUI launcher missing from RPM'
|
||||
if grep -Eq '(^/usr/bin/vaptvupt-gui$|\.(o|obj|a|so|so\.[^/]+|dll|dylib|exe)$)' "$work/contents.txt"; then
|
||||
cat "$work/contents.txt" >&2
|
||||
die 'forbidden compatibility alias or compiled artifact in GUI RPM'
|
||||
fi
|
||||
(cd -- "$extract" && rpm2cpio "$main_rpm" | cpio -idm --quiet)
|
||||
PYTHONDONTWRITEBYTECODE=1 python3 - <<PY
|
||||
from pathlib import Path
|
||||
p = Path("$extract/usr/share/zupt-gui/zupt_gui.py")
|
||||
compile(p.read_text(encoding="utf-8"), str(p), "exec")
|
||||
PY
|
||||
|
||||
for artifact in "$main_rpm" "$source_rpm"; do
|
||||
destination=$dist_dir/$(basename -- "$artifact")
|
||||
[[ ! -e $destination ]] || die "refusing to overwrite existing output: $destination"
|
||||
cp -- "$artifact" "$destination"
|
||||
sha256sum "$destination"
|
||||
done
|
||||
printf 'PASS: built and content-validated GUI RPM and source RPM in %s\n' "$dist_dir"
|
||||
|
|
|
|||
|
|
@ -1,339 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
"""
|
||||
Build a binary RPM for zupt without rpmbuild.
|
||||
Constructs an RPM-format file directly from the file tree we have for deb.
|
||||
|
||||
This is intentionally minimal but produces a valid RPM that:
|
||||
- Can be installed via `rpm -i` on RHEL/Fedora and other RPM-based distributions
|
||||
- Contains correct dependency info
|
||||
- Has working pre/post scripts
|
||||
- Includes the binary, library, headers, docs, license
|
||||
"""
|
||||
import struct, os, sys, hashlib, gzip, io, time, subprocess
|
||||
|
||||
VERSION = os.environ.get('VERSION', '2.2.3')
|
||||
RELEASE = '1'
|
||||
ARCH = 'x86_64'
|
||||
NAME = 'zupt'
|
||||
|
||||
# RPM tag values (from rpmtag.h)
|
||||
RPMTAG_NAME = 1000
|
||||
RPMTAG_VERSION = 1001
|
||||
RPMTAG_RELEASE = 1002
|
||||
RPMTAG_SUMMARY = 1004
|
||||
RPMTAG_DESCRIPTION = 1005
|
||||
RPMTAG_BUILDTIME = 1006
|
||||
RPMTAG_BUILDHOST = 1007
|
||||
RPMTAG_SIZE = 1009
|
||||
RPMTAG_DISTRIBUTION = 1010
|
||||
RPMTAG_VENDOR = 1011
|
||||
RPMTAG_LICENSE = 1014
|
||||
RPMTAG_PACKAGER = 1015
|
||||
RPMTAG_GROUP = 1016
|
||||
RPMTAG_URL = 1020
|
||||
RPMTAG_OS = 1021
|
||||
RPMTAG_ARCH = 1022
|
||||
RPMTAG_PREIN = 1023
|
||||
RPMTAG_POSTIN = 1024
|
||||
RPMTAG_PREUN = 1025
|
||||
RPMTAG_POSTUN = 1026
|
||||
RPMTAG_FILESIZES = 1028
|
||||
RPMTAG_FILEMODES = 1030
|
||||
RPMTAG_FILERDEVS = 1033
|
||||
RPMTAG_FILEMTIMES = 1034
|
||||
RPMTAG_FILEDIGESTS = 1035
|
||||
RPMTAG_FILELINKTOS = 1036
|
||||
RPMTAG_FILEFLAGS = 1037
|
||||
RPMTAG_FILEUSERNAME = 1039
|
||||
RPMTAG_FILEGROUPNAME = 1040
|
||||
RPMTAG_PROVIDENAME = 1047
|
||||
RPMTAG_REQUIREFLAGS = 1048
|
||||
RPMTAG_REQUIRENAME = 1049
|
||||
RPMTAG_REQUIREVERSION = 1050
|
||||
RPMTAG_BASENAMES = 1117
|
||||
RPMTAG_DIRNAMES = 1118
|
||||
RPMTAG_DIRINDEXES = 1116
|
||||
RPMTAG_PAYLOADFORMAT = 1124
|
||||
RPMTAG_PAYLOADCOMPRESSOR = 1125
|
||||
RPMTAG_FILEDIGESTALGO = 5011
|
||||
|
||||
# Type codes
|
||||
RPM_NULL_TYPE = 0
|
||||
RPM_CHAR_TYPE = 1
|
||||
RPM_INT8_TYPE = 2
|
||||
RPM_INT16_TYPE = 3
|
||||
RPM_INT32_TYPE = 4
|
||||
RPM_INT64_TYPE = 5
|
||||
RPM_STRING_TYPE = 6
|
||||
RPM_BIN_TYPE = 7
|
||||
RPM_STRING_ARRAY_TYPE = 8
|
||||
|
||||
class Header:
|
||||
def __init__(self):
|
||||
self.entries = [] # (tag, type, value)
|
||||
|
||||
def add(self, tag, typ, value):
|
||||
self.entries.append((tag, typ, value))
|
||||
|
||||
def serialize(self):
|
||||
# Build store + index
|
||||
store = bytearray()
|
||||
index = []
|
||||
for tag, typ, value in self.entries:
|
||||
if typ == RPM_STRING_TYPE:
|
||||
count = 1
|
||||
data = value.encode('utf-8') + b'\x00'
|
||||
offset = len(store)
|
||||
store.extend(data)
|
||||
elif typ == RPM_STRING_ARRAY_TYPE:
|
||||
count = len(value)
|
||||
data = b''.join(s.encode('utf-8') + b'\x00' for s in value)
|
||||
offset = len(store)
|
||||
store.extend(data)
|
||||
elif typ == RPM_INT32_TYPE:
|
||||
if not isinstance(value, list):
|
||||
value = [value]
|
||||
count = len(value)
|
||||
# align to 4
|
||||
while len(store) % 4: store.append(0)
|
||||
offset = len(store)
|
||||
for v in value:
|
||||
store.extend(struct.pack('>I', v & 0xFFFFFFFF))
|
||||
elif typ == RPM_INT16_TYPE:
|
||||
if not isinstance(value, list):
|
||||
value = [value]
|
||||
count = len(value)
|
||||
while len(store) % 2: store.append(0)
|
||||
offset = len(store)
|
||||
for v in value:
|
||||
store.extend(struct.pack('>H', v & 0xFFFF))
|
||||
elif typ == RPM_BIN_TYPE:
|
||||
count = len(value)
|
||||
offset = len(store)
|
||||
store.extend(value)
|
||||
elif typ == RPM_NULL_TYPE:
|
||||
count = 1
|
||||
offset = 0
|
||||
else:
|
||||
raise ValueError(f"Unsupported type {typ}")
|
||||
index.append(struct.pack('>IIII', tag, typ, offset, count))
|
||||
|
||||
index_bytes = b''.join(index)
|
||||
# Header magic + reserved + index count + store size
|
||||
out = struct.pack('>3sBI4sII', b'\x8e\xad\xe8', 1, 0, b'\x00\x00\x00\x00',
|
||||
len(self.entries), len(store))
|
||||
out += index_bytes + bytes(store)
|
||||
return out
|
||||
|
||||
def make_cpio(file_list, source_root, payload_size_out):
|
||||
"""Build a cpio archive (newc format) of the files."""
|
||||
out = io.BytesIO()
|
||||
inode = 1
|
||||
total = 0
|
||||
for arc_path, src_path, mode, is_dir, link_target in file_list:
|
||||
if is_dir:
|
||||
data = b''
|
||||
file_size = 0
|
||||
elif link_target is not None:
|
||||
data = link_target.encode('utf-8')
|
||||
file_size = len(data)
|
||||
else:
|
||||
with open(src_path, 'rb') as f:
|
||||
data = f.read()
|
||||
file_size = len(data)
|
||||
total += file_size
|
||||
|
||||
name = ('.' + arc_path).encode('utf-8') + b'\x00'
|
||||
# newc header: 110 bytes
|
||||
header = (
|
||||
b'070701'
|
||||
+ format(inode, '08x').encode('ascii')
|
||||
+ format(mode, '08x').encode('ascii')
|
||||
+ b'00000000' # uid
|
||||
+ b'00000000' # gid
|
||||
+ b'00000001' # nlink
|
||||
+ format(int(time.time()), '08x').encode('ascii')
|
||||
+ format(file_size, '08x').encode('ascii')
|
||||
+ b'00000000' * 4 # devmajor/minor + rdevmajor/minor
|
||||
+ format(len(name), '08x').encode('ascii')
|
||||
+ b'00000000' # check
|
||||
)
|
||||
out.write(header)
|
||||
out.write(name)
|
||||
# pad to 4
|
||||
pad = (4 - ((len(header) + len(name)) % 4)) % 4
|
||||
out.write(b'\x00' * pad)
|
||||
out.write(data)
|
||||
# pad data to 4
|
||||
pad = (4 - (file_size % 4)) % 4
|
||||
out.write(b'\x00' * pad)
|
||||
inode += 1
|
||||
|
||||
# Trailer
|
||||
trailer_name = b'TRAILER!!!\x00'
|
||||
out.write(b'070701' + b'0' * 8 + b'0' * 8 + b'0' * 8 + b'0' * 8 + b'00000001'
|
||||
+ b'0' * 8 + b'0' * 8 + b'0' * 8 + b'0' * 8 + b'0' * 8 + b'0' * 8
|
||||
+ format(len(trailer_name), '08x').encode('ascii') + b'0' * 8)
|
||||
out.write(trailer_name)
|
||||
pad = (4 - ((110 + len(trailer_name)) % 4)) % 4
|
||||
out.write(b'\x00' * pad)
|
||||
payload_size_out[0] = total
|
||||
return out.getvalue()
|
||||
|
||||
def main():
|
||||
# Files to include (source_path inside our deb tree)
|
||||
deb_root = f'/tmp/zupt_{VERSION}_amd64'
|
||||
files = [] # (arc_path, source_path, mode, is_dir, link_target)
|
||||
|
||||
for root, dirs, fnames in os.walk(deb_root):
|
||||
for d in sorted(dirs):
|
||||
full = os.path.join(root, d)
|
||||
arc = full[len(deb_root):]
|
||||
files.append((arc, full, 0o40755, True, None))
|
||||
for fn in sorted(fnames):
|
||||
full = os.path.join(root, fn)
|
||||
arc = full[len(deb_root):]
|
||||
if 'DEBIAN' in arc:
|
||||
continue
|
||||
if os.path.islink(full):
|
||||
files.append((arc, full, 0o120777, False, os.readlink(full)))
|
||||
else:
|
||||
mode = 0o100755 if os.access(full, os.X_OK) else 0o100644
|
||||
files.append((arc, full, mode, False, None))
|
||||
|
||||
# Sort and build basename/dirname/dirindex arrays
|
||||
files.sort(key=lambda x: x[0])
|
||||
|
||||
basenames = []
|
||||
dirnames_set = []
|
||||
dirname_to_idx = {}
|
||||
dirindexes = []
|
||||
filesizes = []
|
||||
filemodes = []
|
||||
filemtimes = []
|
||||
filedigests = []
|
||||
filelinktos = []
|
||||
filerdevs = []
|
||||
fileflags = []
|
||||
fileuser = []
|
||||
filegroup = []
|
||||
|
||||
for arc, src, mode, is_dir, link in files:
|
||||
d, b = os.path.split(arc)
|
||||
d = d + '/'
|
||||
if d not in dirname_to_idx:
|
||||
dirname_to_idx[d] = len(dirnames_set)
|
||||
dirnames_set.append(d)
|
||||
basenames.append(b or '.')
|
||||
dirindexes.append(dirname_to_idx[d])
|
||||
if is_dir:
|
||||
filesizes.append(0)
|
||||
filedigests.append('')
|
||||
filelinktos.append('')
|
||||
elif link:
|
||||
filesizes.append(len(link))
|
||||
filedigests.append('')
|
||||
filelinktos.append(link)
|
||||
else:
|
||||
filesizes.append(os.path.getsize(src))
|
||||
with open(src, 'rb') as f:
|
||||
filedigests.append(hashlib.sha256(f.read()).hexdigest())
|
||||
filelinktos.append('')
|
||||
filemodes.append(mode)
|
||||
filemtimes.append(int(time.time()))
|
||||
filerdevs.append(0)
|
||||
fileflags.append(0)
|
||||
fileuser.append('root')
|
||||
filegroup.append('root')
|
||||
|
||||
payload_size = [0]
|
||||
cpio_data = make_cpio(files, deb_root, payload_size)
|
||||
# Compress payload with gzip
|
||||
gz_payload = gzip.compress(cpio_data)
|
||||
|
||||
# Build main header
|
||||
h = Header()
|
||||
h.add(RPMTAG_NAME, RPM_STRING_TYPE, NAME)
|
||||
h.add(RPMTAG_VERSION, RPM_STRING_TYPE, VERSION)
|
||||
h.add(RPMTAG_RELEASE, RPM_STRING_TYPE, RELEASE)
|
||||
h.add(RPMTAG_SUMMARY, RPM_STRING_ARRAY_TYPE, ['Post-quantum backup compression utility'])
|
||||
h.add(RPMTAG_DESCRIPTION, RPM_STRING_ARRAY_TYPE, [
|
||||
'Zupt provides hybrid post-quantum encryption (ML-KEM-768 + X25519)\n'
|
||||
'with multi-threaded compression and full-disk backup support.\n'
|
||||
'Bundled with libzuptsdk for HKDF-SHA3 hybrid KDF, key commitment,\n'
|
||||
'HPKE binding, and anti-fault decapsulation.'
|
||||
])
|
||||
h.add(RPMTAG_BUILDTIME, RPM_INT32_TYPE, int(time.time()))
|
||||
h.add(RPMTAG_BUILDHOST, RPM_STRING_TYPE, 'localhost')
|
||||
h.add(RPMTAG_SIZE, RPM_INT32_TYPE, sum(filesizes))
|
||||
h.add(RPMTAG_LICENSE, RPM_STRING_TYPE, 'AGPL-3.0-or-later')
|
||||
h.add(RPMTAG_PACKAGER, RPM_STRING_TYPE, 'Cristian Cezar Moises <zupt@riseup.net>')
|
||||
h.add(RPMTAG_GROUP, RPM_STRING_ARRAY_TYPE, ['Applications/Archiving'])
|
||||
h.add(RPMTAG_URL, RPM_STRING_TYPE, 'https://git.securityops.co/cristiancmoises/zupt')
|
||||
h.add(RPMTAG_OS, RPM_STRING_TYPE, 'linux')
|
||||
h.add(RPMTAG_ARCH, RPM_STRING_TYPE, ARCH)
|
||||
h.add(RPMTAG_POSTIN, RPM_STRING_TYPE, '/sbin/ldconfig\n')
|
||||
h.add(RPMTAG_POSTUN, RPM_STRING_TYPE, '/sbin/ldconfig\n')
|
||||
h.add(RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE, basenames)
|
||||
h.add(RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE, dirnames_set)
|
||||
h.add(RPMTAG_DIRINDEXES, RPM_INT32_TYPE, dirindexes)
|
||||
h.add(RPMTAG_FILESIZES, RPM_INT32_TYPE, filesizes)
|
||||
h.add(RPMTAG_FILEMODES, RPM_INT16_TYPE, filemodes)
|
||||
h.add(RPMTAG_FILEMTIMES, RPM_INT32_TYPE, filemtimes)
|
||||
h.add(RPMTAG_FILEDIGESTS, RPM_STRING_ARRAY_TYPE, filedigests)
|
||||
h.add(RPMTAG_FILELINKTOS, RPM_STRING_ARRAY_TYPE, filelinktos)
|
||||
h.add(RPMTAG_FILEFLAGS, RPM_INT32_TYPE, fileflags)
|
||||
h.add(RPMTAG_FILERDEVS, RPM_INT16_TYPE, filerdevs)
|
||||
h.add(RPMTAG_FILEUSERNAME, RPM_STRING_ARRAY_TYPE, fileuser)
|
||||
h.add(RPMTAG_FILEGROUPNAME, RPM_STRING_ARRAY_TYPE, filegroup)
|
||||
h.add(RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE, [NAME])
|
||||
h.add(RPMTAG_REQUIRENAME, RPM_STRING_ARRAY_TYPE, ['libargon2.so.1()(64bit)', 'libcrypto.so.3()(64bit)', 'libc.so.6()(64bit)'])
|
||||
h.add(RPMTAG_REQUIREFLAGS, RPM_INT32_TYPE, [0, 0, 0])
|
||||
h.add(RPMTAG_REQUIREVERSION, RPM_STRING_ARRAY_TYPE, ['', '', ''])
|
||||
h.add(RPMTAG_PAYLOADFORMAT, RPM_STRING_TYPE, 'cpio')
|
||||
h.add(RPMTAG_PAYLOADCOMPRESSOR, RPM_STRING_TYPE, 'gzip')
|
||||
h.add(RPMTAG_FILEDIGESTALGO, RPM_INT32_TYPE, 8) # SHA-256
|
||||
|
||||
main_hdr = h.serialize()
|
||||
|
||||
# Signature header (minimal: just size of payload after sig hdr)
|
||||
sig = Header()
|
||||
sig_payload = main_hdr + gz_payload
|
||||
sig.add(1000, RPM_INT32_TYPE, len(sig_payload)) # SIZE
|
||||
sig.add(1004, RPM_BIN_TYPE, hashlib.md5(sig_payload).digest()) # MD5
|
||||
sig_bytes = sig.serialize()
|
||||
# Pad sig hdr to 8-byte boundary
|
||||
pad = (8 - (len(sig_bytes) % 8)) % 8
|
||||
sig_bytes += b'\x00' * pad
|
||||
|
||||
# Lead (96 bytes)
|
||||
lead = struct.pack('>4sBBhh66sHH16s',
|
||||
b'\xed\xab\xee\xdb', # magic
|
||||
3, 0, # major, minor
|
||||
0, # type (binary)
|
||||
1, # archnum
|
||||
NAME.encode().ljust(66, b'\x00'),
|
||||
1, # osnum
|
||||
5, # signature_type
|
||||
b'\x00' * 16)
|
||||
|
||||
out_path = f'/tmp/{NAME}-{VERSION}-{RELEASE}.{ARCH}.rpm'
|
||||
with open(out_path, 'wb') as f:
|
||||
f.write(lead)
|
||||
f.write(sig_bytes)
|
||||
f.write(main_hdr)
|
||||
f.write(gz_payload)
|
||||
|
||||
print(f'Built: {out_path} ({os.path.getsize(out_path)} bytes)')
|
||||
# Try rpm -Kvv to verify if rpm is installed
|
||||
try:
|
||||
result = subprocess.run(['rpm', '-qpi', out_path], capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0:
|
||||
print(result.stdout[:500])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,195 +1,154 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
#
|
||||
# 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")/.."
|
||||
set -Eeuo pipefail
|
||||
|
||||
VERSION="${VERSION:-3.0.0}"
|
||||
ARCH="${ARCH:-x86_64}"
|
||||
RELEASE="1"
|
||||
PKGNAME="vaptvupt"
|
||||
LEGACY="zupt"
|
||||
umask 022
|
||||
export LC_ALL=C
|
||||
|
||||
SDK_LIB="vendor/zuptsdk/libzuptsdk.so.2.0.0"
|
||||
if [ ! -f "$SDK_LIB" ]; then
|
||||
echo "ERROR: $SDK_LIB not found." >&2
|
||||
die() {
|
||||
printf 'FAIL: %s\n' "$*" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "[rpm] Building $PKGNAME"
|
||||
make clean >/dev/null 2>&1 || true
|
||||
make -j"$(nproc)" >/dev/null
|
||||
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
|
||||
repo_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
cd -- "$repo_root"
|
||||
|
||||
if ! command -v rpmbuild >/dev/null 2>&1; then
|
||||
echo "[rpm] rpmbuild not found; install rpm package to proceed"
|
||||
exit 1
|
||||
fi
|
||||
header_version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
|
||||
version=${VERSION:-$header_version}
|
||||
[[ -n $version && $version == "$header_version" ]] || \
|
||||
die "VERSION '$version' does not match include/zupt.h '$header_version'"
|
||||
[[ $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || die "invalid package version: $version"
|
||||
|
||||
RPMROOT="/tmp/rpmbuild-$PKGNAME"
|
||||
rm -rf "$RPMROOT"
|
||||
mkdir -p "$RPMROOT"/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||
spec=packaging/opensuse/zupt.spec
|
||||
[[ -f $spec ]] || die "spec file not found: $spec"
|
||||
spec_version=$(sed -n 's/^Version:[[:space:]]*//p' "$spec" | head -n 1)
|
||||
[[ $spec_version == "$version" ]] || die "spec version '$spec_version' does not match '$version'"
|
||||
|
||||
STAGE="/tmp/$PKGNAME-rpm-stage-${VERSION}"
|
||||
rm -rf "$STAGE"
|
||||
mkdir -p "$STAGE/$PKGNAME-${VERSION}/completions"
|
||||
dist_dir=${DIST_DIR:-${TMPDIR:-/tmp}/zupt-release}
|
||||
mkdir -p -- "$dist_dir"
|
||||
dist_dir=$(cd -- "$dist_dir" && pwd -P)
|
||||
|
||||
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}"
|
||||
for command_name in make git rpmbuild rpm rpm2cpio cpio date readelf sha256sum tar; do
|
||||
command -v -- "$command_name" >/dev/null 2>&1 || die "required command not found: $command_name"
|
||||
done
|
||||
|
||||
cat > "$RPMROOT/SPECS/$PKGNAME.spec" <<EOF
|
||||
Name: $PKGNAME
|
||||
Version: $VERSION
|
||||
Release: ${RELEASE}%{?dist}
|
||||
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: $PKGNAME-%{version}.tar.gz
|
||||
work=$(mktemp -d "${TMPDIR:-/tmp}/zupt-rpm.XXXXXXXX")
|
||||
top=$work/rpmbuild
|
||||
extract=$work/extract
|
||||
mkdir -p -- "$top/BUILD" "$top/BUILDROOT" "$top/RPMS" "$top/SOURCES" \
|
||||
"$top/SPECS" "$top/SRPMS" "$extract"
|
||||
|
||||
# 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
|
||||
cleanup() {
|
||||
chmod -R u+rwX "$work" 2>/dev/null || true
|
||||
rm -rf -- "$work"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
Requires: libargon2
|
||||
Requires: openssl-libs >= 3.0
|
||||
AutoReqProv: no
|
||||
source_tar=$top/SOURCES/zupt-${version}.tar.gz
|
||||
printf '[rpm] creating audited source archive for ZUPT %s\n' "$version"
|
||||
make DIST_TARBALL="$source_tar" WITH_SDK=0 WITH_PQBOX=0 dist
|
||||
archive_version=$(tar -xOf "$source_tar" "zupt-${version}/include/zupt.h" | \
|
||||
sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p')
|
||||
[[ $archive_version == "$version" ]] || die "source archive version is '$archive_version', expected '$version'"
|
||||
|
||||
%global debug_package %{nil}
|
||||
%global __os_install_post %{nil}
|
||||
%global _build_id_links none
|
||||
install -m 0644 "$spec" "$top/SPECS/zupt.spec"
|
||||
# OBS converts zupt.changes into RPM changelog metadata. Standalone
|
||||
# rpmbuild does not, so add an equivalent release entry only to the temporary
|
||||
# spec used for this release artifact.
|
||||
changelog_sections=$(grep -Ec '^%changelog[[:space:]]*$' "$top/SPECS/zupt.spec" || true)
|
||||
[[ $changelog_sections -eq 1 ]] || \
|
||||
die "expected exactly one %changelog section, found $changelog_sections"
|
||||
source_epoch=${SOURCE_DATE_EPOCH:-$(sed -n '1p' .source-date-epoch 2>/dev/null || true)}
|
||||
[[ $source_epoch =~ ^[0-9]+$ ]] || die 'SOURCE_DATE_EPOCH is not available'
|
||||
changelog_date=$(date -u --date="@$source_epoch" '+%a %b %d %Y')
|
||||
cat >> "$top/SPECS/zupt.spec" <<EOF
|
||||
|
||||
%description
|
||||
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
|
||||
|
||||
%build
|
||||
# Pre-built before rpmbuild was invoked; nothing to do.
|
||||
|
||||
%install
|
||||
mkdir -p %{buildroot}%{_bindir}
|
||||
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 $PKGNAME %{buildroot}%{_bindir}/$PKGNAME
|
||||
ln -sf $PKGNAME %{buildroot}%{_bindir}/$LEGACY
|
||||
|
||||
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
|
||||
|
||||
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}/$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
|
||||
* 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.
|
||||
* $changelog_date Cristian Cezar Moisés <sac@securityops.co> - $version-0
|
||||
- Build the release package from audited source with optional SDK and PQBOX
|
||||
features disabled.
|
||||
EOF
|
||||
rpmbuild --define "_topdir $top" -ba "$top/SPECS/zupt.spec"
|
||||
|
||||
rpmbuild --define "_topdir $RPMROOT" \
|
||||
--define "_binary_payload w2.gzdio" \
|
||||
-bb "$RPMROOT/SPECS/$PKGNAME.spec" 2>&1 | tail -5
|
||||
mapfile -t main_rpms < <(find "$top/RPMS" -type f -name "zupt-${version}-*.rpm" \
|
||||
! -name '*-debuginfo-*' ! -name '*-debugsource-*' -print | sort)
|
||||
[[ ${#main_rpms[@]} -eq 1 ]] || die "expected one main RPM, found ${#main_rpms[@]}"
|
||||
main_rpm=${main_rpms[0]}
|
||||
|
||||
RPM_PATH=$(find "$RPMROOT/RPMS" -name "$PKGNAME-${VERSION}-*.rpm" | head -1)
|
||||
if [ -n "$RPM_PATH" ]; then
|
||||
cp "$RPM_PATH" "/tmp/$PKGNAME-${VERSION}-${RELEASE}.${ARCH}.rpm"
|
||||
echo ""
|
||||
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
|
||||
mapfile -t source_rpms < <(find "$top/SRPMS" -type f -name "zupt-${version}-*.src.rpm" -print | sort)
|
||||
[[ ${#source_rpms[@]} -eq 1 ]] || die "expected one source RPM, found ${#source_rpms[@]}"
|
||||
source_rpm=${source_rpms[0]}
|
||||
|
||||
[[ $(rpm -qp --qf '%{NAME}' "$main_rpm") == zupt ]] || \
|
||||
die 'binary RPM name metadata is not zupt'
|
||||
[[ $(rpm -qp --qf '%{VERSION}' "$main_rpm") == "$version" ]] || \
|
||||
die 'binary RPM version metadata does not match the release'
|
||||
[[ $(rpm -qp --qf '%{RELEASE}' "$main_rpm") == 0 ]] || \
|
||||
die 'binary RPM release metadata is not 0'
|
||||
[[ $(rpm -qp --qf '%{SOURCEPACKAGE}' "$main_rpm") == '(none)' ]] || \
|
||||
die 'binary RPM is marked as a source package'
|
||||
[[ $(rpm -qp --qf '%{SOURCERPM}' "$main_rpm") == "$(basename -- "$source_rpm")" ]] || \
|
||||
die 'binary RPM does not reference the matching source RPM'
|
||||
[[ $(rpm -qp --qf '%{NAME}' "$source_rpm") == zupt ]] || \
|
||||
die 'source RPM name metadata is not zupt'
|
||||
[[ $(rpm -qp --qf '%{VERSION}' "$source_rpm") == "$version" ]] || \
|
||||
die 'source RPM version metadata does not match the release'
|
||||
[[ $(rpm -qp --qf '%{RELEASE}' "$source_rpm") == 0 ]] || \
|
||||
die 'source RPM release metadata is not 0'
|
||||
[[ $(rpm -qp --qf '%{SOURCEPACKAGE}' "$source_rpm") == 1 ]] || \
|
||||
die 'source RPM is not marked as a source package'
|
||||
[[ $(rpm -qp --qf '%{SOURCERPM}' "$source_rpm") == '(none)' ]] || \
|
||||
die 'source RPM unexpectedly references another source RPM'
|
||||
mapfile -t source_members < <(rpm -qpl "$source_rpm" | sort)
|
||||
expected_source_members=("zupt-${version}.tar.gz" zupt.spec)
|
||||
mapfile -t expected_source_members < <(printf '%s\n' "${expected_source_members[@]}" | sort)
|
||||
[[ ${#source_members[@]} -eq 2 && \
|
||||
${source_members[*]} == "${expected_source_members[*]}" ]] || \
|
||||
die 'source RPM payload is not the exact Source0/spec pair'
|
||||
|
||||
rpm -qpi "$main_rpm" >/dev/null
|
||||
rpm -qpl "$main_rpm" > "$work/contents.txt"
|
||||
if grep -Eq '(^/usr/bin/vaptvupt$|\.(o|obj|a|so|so\.[^/]+|dll|dylib)$)' "$work/contents.txt"; then
|
||||
cat "$work/contents.txt" >&2
|
||||
die 'forbidden alias or compiled library/object found in RPM contents'
|
||||
fi
|
||||
if grep -q '^/usr/local/' "$work/contents.txt"; then
|
||||
cat "$work/contents.txt" >&2
|
||||
die 'RPM contains files below /usr/local'
|
||||
fi
|
||||
|
||||
(cd -- "$extract" && rpm2cpio "$main_rpm" | cpio -idm --quiet)
|
||||
binary=$extract/usr/bin/zupt
|
||||
[[ -x $binary ]] || die 'RPM does not contain executable /usr/bin/zupt'
|
||||
if ! readelf -h "$binary" 2>/dev/null | grep -Eq 'Type:[[:space:]]+DYN'; then
|
||||
die 'RPM executable is not a position-independent executable (PIE)'
|
||||
fi
|
||||
if ! readelf -W -l "$binary" 2>/dev/null | grep -q 'GNU_RELRO'; then
|
||||
die 'RPM executable lacks a GNU_RELRO segment'
|
||||
fi
|
||||
stack_segment=$(readelf -W -l "$binary" 2>/dev/null | grep 'GNU_STACK' || true)
|
||||
[[ -n $stack_segment && $stack_segment != *RWE* ]] || \
|
||||
die 'RPM executable has a missing or executable GNU_STACK segment'
|
||||
if readelf -d "$binary" 2>/dev/null | grep -Eq '(RPATH|RUNPATH)'; then
|
||||
readelf -d "$binary" | grep -E '(RPATH|RUNPATH)' >&2
|
||||
die 'RPM executable contains RPATH/RUNPATH'
|
||||
fi
|
||||
if readelf -d "$binary" 2>/dev/null | grep -Eqi '(vendor/|libvuptsdk|libpqvaptvupt)'; then
|
||||
die 'RPM executable references a vendored optional library'
|
||||
fi
|
||||
bash scripts/test-installed-zupt.sh "$binary"
|
||||
|
||||
artifacts=("$main_rpm" "$source_rpm")
|
||||
for artifact in "${artifacts[@]}"; do
|
||||
destination=$dist_dir/$(basename -- "$artifact")
|
||||
[[ ! -e $destination ]] || die "refusing to overwrite existing output: $destination"
|
||||
done
|
||||
for artifact in "${artifacts[@]}"; do
|
||||
destination=$dist_dir/$(basename -- "$artifact")
|
||||
cp -- "$artifact" "$destination"
|
||||
sha256sum "$destination"
|
||||
done
|
||||
|
||||
printf 'PASS: built and extracted-package-tested %s\n' "$dist_dir/$(basename -- "$main_rpm")"
|
||||
printf 'PASS: built source RPM %s\n' "$dist_dir/$(basename -- "$source_rpm")"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,89 @@
|
|||
zupt (5.2.8-1) UNRELEASED; urgency=medium
|
||||
|
||||
* Close CodeQL High path-race findings in SDK key publication, disk-restore
|
||||
target handling, and benchmark workspace cleanup.
|
||||
* Treat a filesystem refusal to create the macOS raw-C1 scanner fixture as
|
||||
an explicit skip; reject redirected Windows prompts before _getch; and run
|
||||
sdk-test in the release and hosted Linux gates.
|
||||
* Preserve immutable, unpromoted v5.2.7 run 33445470664: 13 jobs succeeded,
|
||||
macOS failed the raw-C1 fixture, and Windows was cancelled after the hosted
|
||||
job stalled; a MinGW/Wine reproduction isolated redirected _getch entry.
|
||||
Require fresh 5.2.8 evidence.
|
||||
|
||||
-- Cristian Cezar Moisés <sac@securityops.co> Mon, 31 Aug 2026 23:30:00 +0000
|
||||
|
||||
zupt (5.2.7-1) UNRELEASED; urgency=medium
|
||||
|
||||
* Scope SHA-NI test helpers to supported x86 builds so macOS arm64 strict
|
||||
compilation does not fail on unused declarations.
|
||||
* Preserve safe UTF-8 fixture bytes across the Windows argv boundary.
|
||||
* Preserve the immutable, unpromoted 5.2.6 history and require fresh 5.2.7
|
||||
package, checksum, native-platform, OBS, and promotion gates.
|
||||
|
||||
-- Cristian Cezar Moisés <sac@securityops.co> Mon, 31 Aug 2026 23:00:00 +0000
|
||||
|
||||
zupt (5.2.6-1) UNRELEASED; urgency=medium
|
||||
|
||||
* Use the compiler-resistant volatile wipe fallback on macOS and NetBSD, and
|
||||
make the source scanner's empty-array handling compatible with Bash 3.2.
|
||||
* Preserve hostile archive-path fixture bytes exactly on Windows.
|
||||
* Preserve the immutable, unpromoted 5.2.5 history and require fresh 5.2.6
|
||||
package, checksum, native-platform, OBS, and promotion gates.
|
||||
|
||||
-- Cristian Cezar Moisés <sac@securityops.co> Mon, 31 Aug 2026 21:30:00 +0000
|
||||
|
||||
zupt (5.2.5-1) UNRELEASED; urgency=medium
|
||||
|
||||
* Run the standalone OBS source-service chain from its isolated working
|
||||
directory and add a packaging-policy regression for that contract.
|
||||
* Preserve the immutable, unpromoted 5.2.4 history and require fresh 5.2.5
|
||||
package, checksum, native-platform, and promotion gates.
|
||||
|
||||
-- Cristian Cezar Moisés <sac@securityops.co> Mon, 31 Aug 2026 19:55:00 +0000
|
||||
|
||||
zupt (5.2.4-1) UNRELEASED; urgency=medium
|
||||
|
||||
* Make the static Windows GUI package-version check robust to canonical
|
||||
CRLF checkouts.
|
||||
* Advance source-only package metadata and prepare final archive checksums.
|
||||
|
||||
-- Cristian Cezar Moisés <sac@securityops.co> Mon, 31 Aug 2026 18:55:00 +0000
|
||||
|
||||
zupt (5.2.3-1) UNRELEASED; urgency=medium
|
||||
|
||||
* Derive package checks from the upstream version header and stabilize the
|
||||
GUI version output consumed by package gates.
|
||||
* Replace busybox-gawk before installing the native openSUSE RPM toolchain.
|
||||
|
||||
-- Cristian Cezar Moisés <sac@securityops.co> Mon, 31 Aug 2026 18:15:00 +0000
|
||||
|
||||
zupt (5.2.2-1) UNRELEASED; urgency=medium
|
||||
|
||||
* Prepare a source-only upstream release and remove incomplete vendored SDK
|
||||
and PQBOX inputs together with every precompiled-library fallback.
|
||||
* Make optional integrations explicit system dependencies, disabled by
|
||||
default, and preserve distribution compiler/linker flags and DESTDIR.
|
||||
* Add the reusable source scanner and openSUSE/OBS source packaging.
|
||||
* Restore the ZUPT/zupt application, package, executable, documentation, and
|
||||
artifact names; build and test with WITH_SDK=0 WITH_PQBOX=0.
|
||||
* Add explicit password prompt, file, and inherited-descriptor inputs.
|
||||
* Correct the licensing record without revoking historical MIT grants present
|
||||
in earlier repository revisions; current files follow current SPDX notices.
|
||||
* Preserve Yann Collet's BSD-2-Clause notice for the two xxHash-derived
|
||||
XXH64 source units and include it in package license metadata.
|
||||
* Record the CC0-1.0 option for pq-crystals/kyber-derived ML-KEM portions
|
||||
and ship the complete license text in every binary bundle.
|
||||
* Preserve the BSD-3-Clause notice for curve25519-donna-derived X25519
|
||||
portions and document their provenance without inventing a revision.
|
||||
* Promote only license-complete release assets: Windows is ZIP-only and the
|
||||
AppImage remains downstream-only pending a complete runtime source/relink
|
||||
compliance handoff.
|
||||
* Qualify older changelog statements about formally verified or
|
||||
constant-time assembly: 5.2.2 retains source, generated output and runtime
|
||||
regressions, but no reproducible formal-proof certificate for those paths.
|
||||
|
||||
-- Cristian Cezar Moisés <sac@securityops.co> Mon, 31 Aug 2026 00:00:00 +0000
|
||||
|
||||
vaptvupt (5.0.0-1) UNRELEASED; urgency=high
|
||||
|
||||
* ML-KEM-768 is now genuinely FIPS 203-conformant. Earlier releases shipped
|
||||
|
|
@ -377,12 +463,10 @@ vaptvupt (3.0.2-1) UNRELEASED; urgency=medium
|
|||
|
||||
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 license metadata changed to AGPL-3.0-or-later for the then-current
|
||||
source. The original entry incorrectly called earlier MIT notices a
|
||||
templating mistake; the 5.2.2 erratum records that historical grants remain
|
||||
valid for the exact material distributed under them.
|
||||
* 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
|
||||
|
|
|
|||
|
|
@ -1,41 +1,49 @@
|
|||
Source: vaptvupt
|
||||
Source: zupt
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Maintainer: Cristian Cezar Moisés <sac@securityops.co>
|
||||
Build-Depends:
|
||||
bash,
|
||||
coreutils,
|
||||
debhelper-compat (= 13),
|
||||
diffutils,
|
||||
file,
|
||||
findutils,
|
||||
gcc,
|
||||
gawk,
|
||||
git,
|
||||
grep,
|
||||
gzip,
|
||||
libarchive-tools,
|
||||
make,
|
||||
libc6-dev,
|
||||
python3 (>= 3.8)
|
||||
python3 (>= 3.8),
|
||||
sed,
|
||||
tar
|
||||
Standards-Version: 4.6.2
|
||||
Homepage: https://git.securityops.co/cristiancmoises/vaptvupt
|
||||
Vcs-Browser: https://git.securityops.co/cristiancmoises/vaptvupt
|
||||
Vcs-Git: https://git.securityops.co/cristiancmoises/vaptvupt.git
|
||||
Homepage: https://github.com/cristiancmoises/zupt
|
||||
Vcs-Browser: https://github.com/cristiancmoises/zupt
|
||||
Vcs-Git: https://github.com/cristiancmoises/zupt.git
|
||||
Rules-Requires-Root: no
|
||||
|
||||
Package: vaptvupt
|
||||
Package: zupt
|
||||
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:
|
||||
Description: Post-quantum backup compression utility
|
||||
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)
|
||||
* PBKDF2-SHA256 password key derivation (Argon2id in WITH_SDK=1 builds)
|
||||
* Multi-threaded compression with the VaptVupt LZ + ANS codec 2.60.4
|
||||
* Multi-threaded compression with the VaptVupt LZ + ANS codec 2.65.3
|
||||
* 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
|
||||
* Authenticated encrypted-archive metadata and per-block integrity checks
|
||||
* Portable C implementations with optional source-built assembly paths
|
||||
* NIST/RFC test vectors for SHA-256, SHA-3, ML-KEM-768, AES-256-CTR,
|
||||
HMAC-SHA256, X25519, PBKDF2, Argon2id
|
||||
HMAC-SHA256, X25519 and PBKDF2
|
||||
.
|
||||
The archive extension stays .zupt for format continuity (header magic
|
||||
unchanged). The binary `zupt` is preserved as a symlink to `vaptvupt`.
|
||||
unchanged). The package installs only /usr/bin/zupt.
|
||||
.
|
||||
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.
|
||||
Encrypted archives include an integrity trailer that authenticates the
|
||||
header and footer, per-block HMAC with bound frame-preface AAD, and optional
|
||||
encrypted comments. Plain archives use non-cryptographic checksums.
|
||||
|
|
|
|||
|
|
@ -1,19 +1,35 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: zupt
|
||||
Upstream-Name: ZUPT
|
||||
Upstream-Contact: Cristian Cezar Moisés <sac@securityops.co>
|
||||
Source: https://git.securityops.co/cristiancmoises/vaptvupt
|
||||
Source: https://github.com/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
|
||||
Files: src/vaptvupt_api.c src/vv_*.c include/vaptvupt*.h include/vv_*.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: src/zupt_xxh.c
|
||||
Copyright: 2012-2021 Yann Collet
|
||||
2025-2026 Cristian Cezar Moisés
|
||||
License: AGPL-3.0-or-later and BSD-2-Clause
|
||||
|
||||
Files: src/zupt_mlkem.c
|
||||
Copyright: 2025-2026 Cristian Cezar Moisés
|
||||
pq-crystals/kyber contributors (adapted portions)
|
||||
License: AGPL-3.0-or-later and CC0-1.0
|
||||
|
||||
Files: src/zupt_x25519.c
|
||||
Copyright: 2008 Google Inc.
|
||||
2025-2026 Cristian Cezar Moisés
|
||||
License: AGPL-3.0-or-later and BSD-3-Clause
|
||||
|
||||
Files: src/vv_xxh64.c
|
||||
Copyright: 2012-2021 Yann Collet
|
||||
2025-2026 Cristian Cezar Moisés (VaptVupt codec adaptation)
|
||||
License: GPL-3.0-or-later and BSD-2-Clause
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2025-2026 Cristian Cezar Moisés <sac@securityops.co>
|
||||
|
|
@ -47,3 +63,41 @@ License: GPL-3.0-or-later
|
|||
.
|
||||
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'.
|
||||
|
||||
License: BSD-2-Clause
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
.
|
||||
* Redistributions of source code must retain the copyright notice, this list
|
||||
of conditions and the disclaimer.
|
||||
* Redistributions in binary form must reproduce the copyright notice, this
|
||||
list of conditions and the disclaimer in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
License: CC0-1.0
|
||||
To the extent possible under law, the upstream affirmer has waived all
|
||||
copyright and related or neighboring rights to the adapted portions.
|
||||
.
|
||||
The complete CC0 1.0 Universal legal text is shipped in
|
||||
`/usr/share/doc/zupt/LICENSE-CC0-1.0`.
|
||||
|
||||
License: BSD-3-Clause
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the copyright notice, conditions,
|
||||
and disclaimer are retained; neither the name of Google Inc. nor contributor
|
||||
names may be used to endorse derived products without prior permission.
|
||||
.
|
||||
The complete BSD-3-Clause notice and disclaimer are shipped in
|
||||
`/usr/share/doc/zupt/LICENSE-BSD-3-Clause`.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
# Honour Debian's reproducible-build epoch when set by dpkg-buildpackage.
|
||||
export SOURCE_DATE_EPOCH ?= 1747699200
|
||||
export SOURCE_DATE_EPOCH ?= 1788134400
|
||||
|
||||
# Hardening flags — Debian's defaults are already strong, this adds project-
|
||||
# specific ones.
|
||||
|
|
@ -15,21 +15,18 @@ export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
|
|||
|
||||
override_dh_auto_build:
|
||||
# Source-only build: no vendored libraries, native crypto only.
|
||||
$(MAKE) WITH_SDK=0 -j$$(nproc)
|
||||
$(MAKE) WITH_SDK=0 WITH_PQBOX=0 -j$$(nproc)
|
||||
|
||||
override_dh_auto_test:
|
||||
# Distro-safe regression subset (F-06..F-12, dedup-nonce, NIST/RFC vectors).
|
||||
$(MAKE) WITH_SDK=0 check
|
||||
# Distro-safe quick, traversal, integrity, codec and NIST/RFC checks.
|
||||
$(MAKE) WITH_SDK=0 WITH_PQBOX=0 check
|
||||
|
||||
override_dh_auto_install:
|
||||
# Binary package is `vaptvupt` -> stage into debian/vaptvupt (dh derives the
|
||||
# Binary package is `zupt` -> stage into debian/zupt (dh derives the
|
||||
# staging dir from the Package: name in debian/control). Source-only: nothing
|
||||
# to install beyond `make install` (no .so).
|
||||
$(MAKE) DESTDIR=$(CURDIR)/debian/vaptvupt PREFIX=/usr WITH_SDK=0 install
|
||||
$(MAKE) DESTDIR=$(CURDIR)/debian/zupt PREFIX=/usr \
|
||||
WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 install
|
||||
|
||||
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
|
||||
|
|
|
|||
12
packaging/debian/zupt.docs
Normal file
12
packaging/debian/zupt.docs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
README.md
|
||||
CHANGELOG.md
|
||||
SECURITY.md
|
||||
THREAT_MODEL.md
|
||||
NOTICE
|
||||
THIRD-PARTY-NOTICES.md
|
||||
LICENSE
|
||||
LICENSE-AGPL-3.0
|
||||
LICENSE-GPL-3.0
|
||||
LICENSE-BSD-2-Clause
|
||||
LICENSE-BSD-3-Clause
|
||||
LICENSE-CC0-1.0
|
||||
219
packaging/guix/zupt.scm
Normal file
219
packaging/guix/zupt.scm
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
;;; SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
;;; Copyright (c) 2026 Cristian Cezar Moisés
|
||||
;;;
|
||||
;;; GNU Guix package definitions for ZUPT (CLI + PySide6 GUI).
|
||||
;;; Source-only build (no vendored libraries): the CLI links only libc/libm/
|
||||
;;; pthread from the store.
|
||||
;;;
|
||||
;;; Install into your profile (additive; keeps everything else):
|
||||
;;; guix package -f packaging/guix/zupt.scm ; installs the GUI
|
||||
;;; guix package -e '(@ (guix) …)' — or, for the CLI on its own:
|
||||
;;; guix install -f packaging/guix/zupt.scm ; (last expr = GUI)
|
||||
;;; The last expression is the GUI, which carries the CLI as an input; to get
|
||||
;;; the `zupt` command in your profile too, also run:
|
||||
;;; guix package --install-from-expression='(begin (load "packaging/guix/zupt.scm") zupt)'
|
||||
;;;
|
||||
;;; GUI-on-Guix note: PySide6's Qt6 links several leaf libraries (libGL from
|
||||
;;; mesa, libxkbcommon, the X11/xcb family, libzstd, harfbuzz, icu, ...) that are
|
||||
;;; NOT in its RUNPATH. The launcher therefore sets LD_LIBRARY_PATH to those
|
||||
;;; libraries (see %gui-runtime-libs). Without this, `import PySide6.QtWidgets`
|
||||
;;; fails with "libGL.so.1: cannot open shared object file" and the GUI prints
|
||||
;;; "requires PySide6 or PyQt6". Qt's OWN libraries are intentionally excluded
|
||||
;;; from LD_LIBRARY_PATH — they resolve via PySide6's RUNPATH; forcing a second
|
||||
;;; copy causes Qt private-API symbol clashes.
|
||||
|
||||
(use-modules (guix packages)
|
||||
(guix download)
|
||||
(guix gexp)
|
||||
(guix utils)
|
||||
(guix build-system gnu)
|
||||
(guix build-system copy)
|
||||
((guix licenses) #:prefix license:)
|
||||
(gnu packages python) ; python
|
||||
(gnu packages qt) ; python-pyside-6, python-shiboken-6, qtbase, qtwayland
|
||||
(gnu packages bash) ; bash-minimal
|
||||
(gnu packages gl) ; mesa (libGL)
|
||||
(gnu packages xdisorg) ; libxkbcommon, pixman, mtdev
|
||||
(gnu packages fontutils) ; fontconfig, freetype, graphite2
|
||||
(gnu packages xorg) ; libX11 + xcb family, libxft, libevdev
|
||||
(gnu packages freedesktop); wayland, libinput-minimal
|
||||
(gnu packages glib) ; glib, dbus
|
||||
(gnu packages compression); zlib, zstd, brotli
|
||||
(gnu packages image) ; libpng, libjpeg-turbo
|
||||
(gnu packages xml) ; expat, libxml2
|
||||
(gnu packages gtk) ; harfbuzz
|
||||
(gnu packages icu4c) ; icu4c
|
||||
(gnu packages maths) ; double-conversion
|
||||
(gnu packages pcre) ; pcre2
|
||||
(gnu packages markup) ; md4c
|
||||
(gnu packages crypto) ; libb2
|
||||
(gnu packages linux)) ; eudev (libudev)
|
||||
|
||||
;; Leaf runtime libraries PySide6's Qt6 (Core/Gui/Widgets) needs but that are
|
||||
;; NOT in its RUNPATH. NEVER add qtbase/qtwayland here (see header note). These
|
||||
;; already live in PySide6's closure, so referencing them adds no store size.
|
||||
(define %gui-runtime-libs
|
||||
(list mesa libxkbcommon fontconfig freetype graphite2 harfbuzz
|
||||
icu4c double-conversion pcre2 md4c libb2 brotli
|
||||
libpng libjpeg-turbo zlib expat libxml2 pixman glib dbus wayland
|
||||
libx11 libxext libxrender libxcb libxrandr libxi libxcursor libxft
|
||||
libxfixes libxdamage libxcomposite libxtst libxinerama libsm libice
|
||||
libxau libxdmcp xcb-util xcb-util-image xcb-util-keysyms
|
||||
xcb-util-renderutil xcb-util-wm xcb-util-cursor
|
||||
libinput-minimal mtdev libevdev eudev))
|
||||
|
||||
(define %zupt-version "5.2.8")
|
||||
|
||||
(define %zupt-source
|
||||
(origin
|
||||
(method url-fetch)
|
||||
(uri (string-append
|
||||
"https://github.com/cristiancmoises/zupt"
|
||||
"/releases/download/v" %zupt-version
|
||||
"/zupt-" %zupt-version ".tar.gz"))
|
||||
(sha256
|
||||
(base32 "1xv5vd7bh9pcw2d3fszb6jn1r6sxjp48mlzh9icvji8m4439b2rp"))))
|
||||
|
||||
(define-public zupt
|
||||
(package
|
||||
(name "zupt")
|
||||
(version %zupt-version)
|
||||
(source %zupt-source)
|
||||
(build-system gnu-build-system)
|
||||
(arguments
|
||||
(list
|
||||
#:make-flags
|
||||
#~(list (string-append "PREFIX=" #$output)
|
||||
"WITH_SDK=0"
|
||||
"WITH_PQBOX=0"
|
||||
(string-append "CC=" #$(cc-for-target)))
|
||||
#:phases
|
||||
#~(modify-phases %standard-phases
|
||||
(delete 'configure) ; plain Makefile, no ./configure
|
||||
(replace 'check
|
||||
;; Self-contained NIST/RFC known-answer vectors (FIPS 180-4/202/203,
|
||||
;; SP 800-38A, RFC 4231/7748) are the crypto gate.
|
||||
(lambda* (#:key tests? #:allow-other-keys)
|
||||
(when tests?
|
||||
(invoke "make" "WITH_SDK=0" "WITH_PQBOX=0"
|
||||
(string-append "CC=" #$(cc-for-target))
|
||||
"test-vectors")
|
||||
(invoke "./test_vectors")))))))
|
||||
(home-page "https://github.com/cristiancmoises/zupt")
|
||||
(synopsis "Post-quantum backup compression utility")
|
||||
(description
|
||||
"ZUPT is a pure-C11 backup compressor with native
|
||||
post-quantum encryption. Two in-tree PQ modes: @code{--pq} hybridizes
|
||||
ML-KEM-768 with X25519 (recommended), and
|
||||
@code{--pq-only} uses ML-KEM-768 alone for @dfn{PQ-only} compliance postures.
|
||||
Payload protection is AES-256-CTR + HMAC-SHA256 Encrypt-then-MAC with a fresh
|
||||
random per-block nonce; AES-NI/SHA-NI dispatch at runtime; the bundled
|
||||
VaptVupt 2.65.3 LZ+ANS codec has portable fallbacks. Password mode uses
|
||||
PBKDF2-SHA256. The tool is
|
||||
AGPL-3.0-or-later; the embedded codec is GPL-3.0-or-later; the two
|
||||
xxHash-derived XXH64 units additionally carry BSD-2-Clause; and portions of
|
||||
native ML-KEM adapted from pq-crystals/kyber carry CC0-1.0. Native X25519
|
||||
portions adapted from curve25519-donna retain BSD-3-Clause.
|
||||
The x86 BCJ filter and SHA-NI path also record their public-domain LZMA SDK
|
||||
and SHA-Intrinsics origins; installed NOTICE and THIRD-PARTY-NOTICES.md carry
|
||||
the full provenance record.")
|
||||
(license (list license:agpl3+ license:gpl3+ license:bsd-2 license:bsd-3 license:cc0))))
|
||||
|
||||
(define-public zupt-gui
|
||||
(package
|
||||
(name "zupt-gui")
|
||||
(version %zupt-version)
|
||||
(source (package-source zupt)) ; same release tarball
|
||||
(build-system copy-build-system)
|
||||
(arguments
|
||||
(list
|
||||
#:install-plan
|
||||
#~'(("gui/src/zupt_gui.py" "lib/zupt-gui/")
|
||||
("gui/assets/zupt-icon.png"
|
||||
"share/icons/hicolor/256x256/apps/zupt-gui.png")
|
||||
("gui/README.md" "share/doc/zupt-gui/")
|
||||
("LICENSE-AGPL-3.0"
|
||||
"share/licenses/zupt-gui/LICENSE-AGPL-3.0")
|
||||
("gui/LICENSE-GUI"
|
||||
"share/licenses/zupt-gui/LICENSE-GUI")
|
||||
("gui/assets/README.md"
|
||||
"share/licenses/zupt-gui/ASSET-PROVENANCE.md"))
|
||||
#:phases
|
||||
#~(modify-phases %standard-phases
|
||||
(add-after 'install 'make-launcher
|
||||
(lambda* (#:key inputs outputs #:allow-other-keys)
|
||||
(let* ((out (assoc-ref outputs "out"))
|
||||
(bin (string-append out "/bin"))
|
||||
(gui (string-append
|
||||
out "/lib/zupt-gui/zupt_gui.py"))
|
||||
(sh (search-input-file inputs "/bin/sh"))
|
||||
(python3 (search-input-file inputs "/bin/python3"))
|
||||
(cli (search-input-file inputs "/bin/zupt"))
|
||||
(pyside (assoc-ref inputs "python-pyside-6"))
|
||||
(site (car (find-files pyside "^site-packages$"
|
||||
#:directories? #t)))
|
||||
;; Shiboken6 is a SEPARATE package PySide6 imports at
|
||||
;; runtime; its site-packages must be on GUIX_PYTHONPATH too.
|
||||
(shiboken (assoc-ref inputs "python-shiboken-6"))
|
||||
(shsite (car (find-files shiboken "^site-packages$"
|
||||
#:directories? #t)))
|
||||
(qtbase (assoc-ref inputs "qtbase"))
|
||||
(qtwl (assoc-ref inputs "qtwayland"))
|
||||
;; zstd ships libzstd.so.1 in its "lib" output (not "out").
|
||||
(zstdlib (assoc-ref inputs "zstd"))
|
||||
(ldpath (string-join
|
||||
(append
|
||||
(list #$@(map (lambda (p) (file-append p "/lib"))
|
||||
%gui-runtime-libs))
|
||||
(list (string-append zstdlib "/lib")))
|
||||
":")))
|
||||
(mkdir-p bin)
|
||||
(call-with-output-file (string-append bin "/zupt-gui")
|
||||
(lambda (port)
|
||||
(format port "#!~a
|
||||
export ZUPT_BIN=\"~a\"
|
||||
export GUIX_PYTHONPATH=\"~a:~a${GUIX_PYTHONPATH:+:}$GUIX_PYTHONPATH\"
|
||||
export QT_PLUGIN_PATH=\"~a/lib/qt6/plugins:~a/lib/qt6/plugins${QT_PLUGIN_PATH:+:}$QT_PLUGIN_PATH\"
|
||||
export LD_LIBRARY_PATH=\"~a${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH\"
|
||||
exec \"~a\" \"~a\" \"$@\"\n"
|
||||
sh cli site shsite qtbase qtwl ldpath python3 gui)))
|
||||
(chmod (string-append bin "/zupt-gui") #o755))))
|
||||
(add-after 'make-launcher 'install-desktop-file
|
||||
(lambda* (#:key outputs #:allow-other-keys)
|
||||
(let* ((out (assoc-ref outputs "out"))
|
||||
(apps (string-append out "/share/applications")))
|
||||
(mkdir-p apps)
|
||||
(call-with-output-file
|
||||
(string-append apps "/zupt-gui.desktop")
|
||||
(lambda (port)
|
||||
(format port "[Desktop Entry]
|
||||
Type=Application
|
||||
Name=ZUPT
|
||||
GenericName=Post-Quantum Backup
|
||||
Comment=Compress, encrypt and restore .zupt archives
|
||||
Exec=~a/bin/zupt-gui %F
|
||||
Icon=zupt-gui
|
||||
Terminal=false
|
||||
Categories=Utility;Archiving;Security;
|
||||
MimeType=application/x-zupt;
|
||||
Keywords=backup;encryption;post-quantum;compression;zupt;\n"
|
||||
out)))))))))
|
||||
(inputs
|
||||
(append (list bash-minimal python python-pyside-6 python-shiboken-6
|
||||
qtbase qtwayland zupt
|
||||
(list zstd "lib")) ; libzstd.so.1 is in zstd's "lib" output
|
||||
%gui-runtime-libs))
|
||||
(home-page "https://github.com/cristiancmoises/zupt")
|
||||
(synopsis "Desktop frontend for the ZUPT post-quantum backup tool")
|
||||
(description
|
||||
"PySide6 (Qt 6) graphical frontend for ZUPT: create, inspect and
|
||||
extract @code{.zupt} archives with password or post-quantum recipient
|
||||
encryption, including the @code{--pq} hybrid and @code{--pq-only} full
|
||||
post-quantum modes. The launcher pins the matching @code{zupt} CLI from the
|
||||
store via @env{ZUPT_BIN} and sets @env{LD_LIBRARY_PATH} to the Qt6 leaf
|
||||
libraries PySide6 needs but does not carry in its RUNPATH.")
|
||||
(license license:agpl3+)))
|
||||
|
||||
;; `guix package -f' evaluates the file's last expression — the GUI, which
|
||||
;; carries the CLI as an input.
|
||||
zupt-gui
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#
|
||||
# Homebrew formula for zupt.
|
||||
# Homebrew formula for ZUPT.
|
||||
#
|
||||
# To publish:
|
||||
# 1. Run `make dist` upstream to produce zupt-VERSION.tar.gz (reproducible).
|
||||
# 1. Run `make dist` upstream to produce zupt-VERSION.tar.gz.
|
||||
# 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
|
||||
|
|
@ -19,13 +19,13 @@
|
|||
# the C fallback for AES-256-CTR / HMAC compare paths is shipped.
|
||||
# * Source-only build: no vendored libraries; native crypto only.
|
||||
|
||||
class Vaptvupt < Formula
|
||||
class Zupt < Formula
|
||||
desc "Post-quantum backup compression utility (ML-KEM-768 + AES-256-CTR + HMAC-SHA256)"
|
||||
homepage "https://git.securityops.co/cristiancmoises/vaptvupt"
|
||||
url "https://git.securityops.co/cristiancmoises/vaptvupt/releases/download/v5.0.0/vaptvupt-5.0.0.tar.gz"
|
||||
version "5.0.0"
|
||||
sha256 "REPLACE_WITH_SHA256_OF_RELEASE_TARBALL"
|
||||
license "AGPL-3.0-or-later"
|
||||
homepage "https://github.com/cristiancmoises/zupt"
|
||||
url "https://github.com/cristiancmoises/zupt/releases/download/v5.2.8/zupt-5.2.8.tar.gz"
|
||||
version "5.2.8"
|
||||
sha256 "378b9506211545b9594cf0d38ac8955d9b1cac34eb6b379ae0ec26b84edb65f7"
|
||||
license all_of: ["AGPL-3.0-or-later", "GPL-3.0-or-later", "BSD-2-Clause", "BSD-3-Clause", "CC0-1.0"]
|
||||
|
||||
depends_on "python@3.12" => :test # only for test-suite tamper harness
|
||||
|
||||
|
|
@ -35,18 +35,24 @@ class Vaptvupt < Formula
|
|||
# this and falls back cleanly.
|
||||
ENV["CFLAGS"] = "#{ENV.cflags} -O2 -std=c11 -Wall -Wextra"
|
||||
|
||||
system "make", "WITH_SDK=0", "-j#{ENV.make_jobs}"
|
||||
system "make", "DESTDIR=#{prefix}", "PREFIX=", "WITH_SDK=0", "install"
|
||||
system "make", "WITH_SDK=0", "WITH_PQBOX=0", "-j#{ENV.make_jobs}"
|
||||
system "make", "PREFIX=#{prefix}", "WITH_SDK=0", "WITH_PQBOX=0",
|
||||
"INSTALL_LEGACY_ALIAS=0", "install"
|
||||
|
||||
# Docs (no vendored .so/.dylib in the source-only build).
|
||||
# Docs (no vendored .so/.dylib in the source-only build). `make install`
|
||||
# also installs the complete project license/notice set.
|
||||
doc.install "README.md", "SECURITY.md", "CHANGELOG.md"
|
||||
%w[LICENSE-BSD-3-Clause LICENSE-CC0-1.0].each do |notice|
|
||||
odie "missing installed license #{notice}" unless \
|
||||
(share/"licenses/zupt"/notice).exist?
|
||||
end
|
||||
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"
|
||||
system bin/"zupt", "t", "-p", "test", "out.zupt"
|
||||
mkdir "extracted"
|
||||
cd "extracted" do
|
||||
system bin/"zupt", "x", "-p", "test", "../out.zupt"
|
||||
|
|
@ -1,139 +1,8 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# Zupt + Zupt GUI all-in-one installer for Linux
|
||||
# Detects your distro, installs all dependencies, then installs
|
||||
# zupt and zupt-gui. Run as root or with sudo.
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ZUPT_CLI_DEB="$SCRIPT_DIR/zupt_2.2.3_amd64.deb"
|
||||
ZUPT_GUI_DEB="$SCRIPT_DIR/zupt-gui_1.1.1_all.deb"
|
||||
|
||||
print_step() { echo ""; echo "═══ $* ═══"; }
|
||||
print_err() { echo "ERROR: $*" >&2; exit 1; }
|
||||
|
||||
# Must be root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
print_err "Run with sudo: sudo bash $0"
|
||||
fi
|
||||
|
||||
# Detect distro
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
DISTRO="$ID"
|
||||
DISTRO_LIKE="${ID_LIKE:-}"
|
||||
else
|
||||
print_err "Cannot detect distribution (no /etc/os-release)"
|
||||
fi
|
||||
|
||||
print_step "Detected: $PRETTY_NAME"
|
||||
|
||||
# 1. Install Python 3 + Qt6 binding
|
||||
print_step "Step 1/3: Installing Python 3 and Qt6 binding"
|
||||
case "$DISTRO" in
|
||||
debian|ubuntu|linuxmint|pop)
|
||||
apt-get update
|
||||
apt-get install -y python3 python3-pyqt6 || \
|
||||
apt-get install -y python3 python3-pyside6
|
||||
;;
|
||||
fedora|rhel|centos|rocky|almalinux)
|
||||
if command -v dnf >/dev/null; then
|
||||
dnf install -y python3 python3-pyqt6 || dnf install -y python3 python3-pyside6
|
||||
else
|
||||
yum install -y python3 python3-pyqt6 || yum install -y python3 python3-pyside6
|
||||
fi
|
||||
;;
|
||||
opensuse*|suse)
|
||||
zypper install -y python3 python3-pyqt6 || zypper install -y python3 python3-PyQt6 \
|
||||
|| zypper install -y python3 python3-pyside6
|
||||
;;
|
||||
arch|manjaro|endeavouros)
|
||||
pacman -S --noconfirm python python-pyqt6 || pacman -S --noconfirm python python-pyside6
|
||||
;;
|
||||
alpine)
|
||||
apk add python3 py3-pyqt6 || apk add python3 py3-pyside6
|
||||
;;
|
||||
*)
|
||||
# Fallback: try pip
|
||||
echo "Unknown distribution '$DISTRO'. Trying pip fallback..."
|
||||
if command -v pip3 >/dev/null; then
|
||||
pip3 install --break-system-packages PySide6 || pip3 install PySide6
|
||||
else
|
||||
print_err "No pip3 available. Install python3-pyqt6 manually for your distro."
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Verify Qt6 binding works
|
||||
if ! python3 -c 'import PyQt6.QtWidgets' 2>/dev/null \
|
||||
&& ! python3 -c 'import PySide6.QtWidgets' 2>/dev/null; then
|
||||
print_err "Failed to install Qt6 Python binding. Install manually with your package manager."
|
||||
fi
|
||||
echo "✓ Python 3 + Qt6 binding installed"
|
||||
|
||||
# 2. Install zupt CLI
|
||||
print_step "Step 2/3: Installing zupt CLI 2.2.3"
|
||||
case "$DISTRO" in
|
||||
debian|ubuntu|linuxmint|pop)
|
||||
if [ ! -f "$ZUPT_CLI_DEB" ]; then
|
||||
print_err "Cannot find $ZUPT_CLI_DEB next to this script"
|
||||
fi
|
||||
# Force-replace any older zupt
|
||||
dpkg -i "$ZUPT_CLI_DEB" || apt-get -f install -y
|
||||
;;
|
||||
fedora|rhel|centos|rocky|almalinux|opensuse*|suse)
|
||||
ZUPT_CLI_RPM="$SCRIPT_DIR/zupt-2.2.3-1.x86_64.rpm"
|
||||
if [ -f "$ZUPT_CLI_RPM" ]; then
|
||||
rpm -Uvh --force "$ZUPT_CLI_RPM"
|
||||
else
|
||||
print_err "RPM build not provided. Build from source tarball or install via SRPM."
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# Fallback: tarball install
|
||||
ZUPT_CLI_TAR="$SCRIPT_DIR/zupt-2.2.3-linux-x86_64.tar.gz"
|
||||
if [ -f "$ZUPT_CLI_TAR" ]; then
|
||||
tar -xzf "$ZUPT_CLI_TAR" -C /opt/
|
||||
ln -sf /opt/zupt-2.2.3-linux-x86_64/zupt /usr/local/bin/zupt
|
||||
else
|
||||
print_err "No suitable installer for $DISTRO"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
echo "✓ zupt CLI installed"
|
||||
|
||||
# 3. Install zupt-gui
|
||||
print_step "Step 3/3: Installing zupt-gui"
|
||||
case "$DISTRO" in
|
||||
debian|ubuntu|linuxmint|pop)
|
||||
dpkg -i "$ZUPT_GUI_DEB" || apt-get -f install -y
|
||||
;;
|
||||
fedora|rhel|centos|rocky|almalinux|opensuse*|suse)
|
||||
ZUPT_GUI_RPM="$SCRIPT_DIR/zupt-gui-1.1.1-1.noarch.rpm"
|
||||
if [ -f "$ZUPT_GUI_RPM" ]; then
|
||||
rpm -Uvh --force "$ZUPT_GUI_RPM"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# Manual fallback
|
||||
mkdir -p /opt/zupt-gui /usr/local/bin
|
||||
cp "$SCRIPT_DIR/zupt_gui.py" /opt/zupt-gui/ 2>/dev/null || true
|
||||
cat > /usr/local/bin/zupt-gui <<'WRAP'
|
||||
#!/bin/sh
|
||||
exec python3 /opt/zupt-gui/zupt_gui.py "$@"
|
||||
WRAP
|
||||
chmod +x /usr/local/bin/zupt-gui
|
||||
;;
|
||||
esac
|
||||
echo "✓ zupt-gui installed"
|
||||
|
||||
print_step "Installation complete"
|
||||
echo ""
|
||||
echo "Run:"
|
||||
echo " zupt help # CLI help"
|
||||
echo " zupt-gui # Graphical interface"
|
||||
echo ""
|
||||
echo "If you encounter issues, check that your zupt version is correct:"
|
||||
echo " zupt version # should show 2.2.3"
|
||||
# Stable entry point for the source installer. Dependency installation belongs
|
||||
# to the operating-system package manager; this script performs no downloads.
|
||||
set -Eeuo pipefail
|
||||
repo_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
exec "$repo_root/gui/install.sh" "$@"
|
||||
|
|
|
|||
|
|
@ -1,25 +1,22 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#
|
||||
# Nix flake for zupt.
|
||||
# Nix flake for ZUPT.
|
||||
#
|
||||
# Usage (with flakes enabled):
|
||||
# nix build .#zupt # build the package
|
||||
# nix run .#zupt -- version # run zupt directly
|
||||
# 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";
|
||||
# inputs.zupt.url = "github:cristiancmoises/zupt/v5.2.8";
|
||||
# ...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.
|
||||
# `make dist` has its own reproducibility gate. This development flake has no
|
||||
# committed lock file and therefore makes no independent locked-output claim.
|
||||
|
||||
{
|
||||
description = "Zupt — post-quantum backup compression utility (C11)";
|
||||
description = "ZUPT — post-quantum backup compression utility (C11)";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
|
||||
|
|
@ -27,22 +24,25 @@
|
|||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system:
|
||||
flake-utils.lib.eachSystem [ "x86_64-linux" ] (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
|
||||
zupt = pkgs.stdenv.mkDerivation {
|
||||
pname = "vaptvupt";
|
||||
version = "5.0.0";
|
||||
pname = "zupt";
|
||||
version = "5.2.8";
|
||||
|
||||
# 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 = ./.;
|
||||
src = builtins.path { path = ../..; name = "zupt-source"; };
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
gcc
|
||||
git
|
||||
gnumake
|
||||
file
|
||||
gnutar
|
||||
];
|
||||
|
||||
# python3 is only used by the regression-test harness.
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
# Source-only build (WITH_SDK=0): native crypto, no vendored libraries.
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
make WITH_SDK=0 -j$NIX_BUILD_CORES
|
||||
make WITH_SDK=0 WITH_PQBOX=0 -j$NIX_BUILD_CORES
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
|
|
@ -63,27 +63,30 @@
|
|||
doCheck = true;
|
||||
checkPhase = ''
|
||||
runHook preCheck
|
||||
make WITH_SDK=0 check
|
||||
make WITH_SDK=0 WITH_PQBOX=0 check
|
||||
runHook postCheck
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
make DESTDIR=$out PREFIX= WITH_SDK=0 install
|
||||
make PREFIX=$out WITH_SDK=0 WITH_PQBOX=0 \
|
||||
INSTALL_LEGACY_ALIAS=0 install
|
||||
|
||||
# Docs
|
||||
mkdir -p $out/share/doc/vaptvupt
|
||||
cp README.md SECURITY.md CHANGELOG.md $out/share/doc/vaptvupt/
|
||||
mkdir -p $out/share/doc/zupt
|
||||
cp README.md SECURITY.md CHANGELOG.md $out/share/doc/zupt/
|
||||
test -f $out/share/licenses/zupt/LICENSE-BSD-3-Clause
|
||||
test -f $out/share/licenses/zupt/LICENSE-CC0-1.0
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
meta = with pkgs.lib; {
|
||||
description = "Post-quantum backup compression utility (ML-KEM-768 + X25519 + AES-256-CTR + HMAC-SHA256)";
|
||||
homepage = "https://git.securityops.co/cristiancmoises/vaptvupt";
|
||||
license = with licenses; [ agpl3Plus gpl3Plus ];
|
||||
homepage = "https://github.com/cristiancmoises/zupt";
|
||||
license = with licenses; [ agpl3Plus gpl3Plus bsd2 bsd3 cc0 ];
|
||||
maintainers = [ ];
|
||||
platforms = [ "x86_64-linux" "aarch64-linux" ];
|
||||
mainProgram = "vaptvupt";
|
||||
platforms = [ "x86_64-linux" ];
|
||||
mainProgram = "zupt";
|
||||
};
|
||||
};
|
||||
in {
|
||||
|
|
|
|||
|
|
@ -1,88 +1,324 @@
|
|||
# openSUSE Build Service update for `home:cabelo:innovators/vaptvupt`
|
||||
# ZUPT 5.2.8 for openSUSE Build Service
|
||||
|
||||
This directory contains the three files needed to build vaptvupt `5.0.0`
|
||||
in OBS:
|
||||
This directory is the upstream, source-only OBS recipe for ZUPT. It is a
|
||||
handoff for the downstream maintainer; its presence does not mean that the
|
||||
package has been submitted to or accepted by openSUSE Factory.
|
||||
|
||||
| File | Purpose |
|
||||
|---------------|-------------------------------------------------------------------------|
|
||||
| `_service` | `revision` pinned to `v5.0.0`. Format unchanged (still `tar_scm`). |
|
||||
| `vaptvupt.spec` | `Version: 5.0.0`. `License: AGPL-3.0-or-later`. `%check` calls `make check`. |
|
||||
| `vaptvupt.changes`| Changelog for the 4.x series. Older history preserved verbatim. |
|
||||
Cristian Cezar Moisés, ZUPT's creator and current upstream maintainer,
|
||||
prepared the current source, build, test, documentation, and upstream packaging
|
||||
changes in this handoff. Alessandro de Oliveira Faria (Cabelo) is credited only
|
||||
as the openSUSE collaborator and downstream OBS package maintainer: he reviews
|
||||
the handoff, commits it through the portal/project he maintains, and may make
|
||||
the openSUSE-side adjustments he considers necessary. This role does not
|
||||
attribute upstream code or the
|
||||
5.2.2/5.2.3/5.2.4/5.2.5/5.2.6/5.2.7/5.2.8 upstream changes to Cabelo.
|
||||
|
||||
## Spec notes
|
||||
## Files and source policy
|
||||
|
||||
1. **License** — `AGPL-3.0-or-later` (dual-licensed AGPL-3.0-or-later
|
||||
+ commercial).
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `_service` | Fetch the immutable `v5.2.8` tag and create `Source0` at build time. |
|
||||
| `zupt.spec` | Build and test the CLI with optional external system integrations disabled. |
|
||||
| `zupt.changes` | openSUSE-format package history. |
|
||||
| `source-audit.sh` | Handoff wrapper for the repository scanner; run it from the complete handoff tree. |
|
||||
|
||||
2. **No BuildRequires beyond the toolchain** — the default build needs
|
||||
only `gcc gzip make` (plus `libm`/`pthread` from glibc). There are
|
||||
**no system library BuildRequires**. The repository is source-only:
|
||||
the previously vendored `libzuptsdk.so` and `libpqvaptvupt.so` have
|
||||
been removed from the tree, `%build` and `%install` run with
|
||||
`WITH_SDK=0`, and `%files` no longer lists any `.so`. The package
|
||||
installs no shared library. Do not add system crypto BuildRequires.
|
||||
The source service uses `obs_scm`, with Git submodules and Git LFS explicitly
|
||||
disabled. Its primary URL is the canonical upstream:
|
||||
|
||||
The optional SDK modes (`--pq-sdk`, `--pq-box`) and the Argon2id KDF
|
||||
require an upstream `make WITH_SDK=1` build linked against the
|
||||
separately distributed `libzuptsdk`/`libpqvaptvupt` libraries. They
|
||||
are not part of this package.
|
||||
|
||||
3. **`%check` target** — the s390x branch falls back to `test-vectors`;
|
||||
other architectures run `make check`. This exercises the HMAC tamper
|
||||
detection, archive-integrity trailer, byte-level integrity preface
|
||||
AAD, default-KDF, auth-fail, and encrypted-comment suites, the
|
||||
NIST/RFC vectors (SHA-256, SHA-3, ML-KEM-768, AES-256-CTR, HMAC,
|
||||
X25519, PBKDF2), and the path-traversal, argument-order, and
|
||||
block-swap regressions.
|
||||
|
||||
The default password KDF is **PBKDF2-SHA256** (600k iterations).
|
||||
Argon2id test vectors run only in a `WITH_SDK=1` build and are not
|
||||
checked here.
|
||||
|
||||
4. **URLs** — the `URL:` field points at the canonical project URL
|
||||
`https://git.securityops.co/cristiancmoises/vaptvupt`. The `_service`
|
||||
file still fetches from GitHub
|
||||
(`https://github.com/cristiancmoises/vaptvupt`), which is what the
|
||||
existing `tar_scm` configuration uses in OBS.
|
||||
|
||||
## How to apply
|
||||
|
||||
```sh
|
||||
# 1. Check out the package
|
||||
osc checkout home:cabelo:innovators vaptvupt
|
||||
cd home:cabelo:innovators/vaptvupt
|
||||
|
||||
# 2. Drop the new files in (assuming this README is at
|
||||
# /path/to/vaptvupt-source/packaging/opensuse/README.md)
|
||||
cp /path/to/vaptvupt-source/packaging/opensuse/_service .
|
||||
cp /path/to/vaptvupt-source/packaging/opensuse/vaptvupt.spec .
|
||||
cp /path/to/vaptvupt-source/packaging/opensuse/vaptvupt.changes .
|
||||
|
||||
# 3. Trigger the service locally to fetch v5.0.0 from GitHub
|
||||
osc service runall
|
||||
# Produces vaptvupt-5.0.0.tar.gz in the current directory.
|
||||
|
||||
# 4. (Optional) Local build to verify before committing
|
||||
osc build openSUSE_Tumbleweed x86_64
|
||||
|
||||
# 5. Commit upstream
|
||||
osc status # confirm vaptvupt-5.0.0.tar.gz is staged alongside the
|
||||
# three text files
|
||||
osc commit -m "Update to 5.0.0"
|
||||
```text
|
||||
https://github.com/cristiancmoises/zupt.git
|
||||
```
|
||||
|
||||
## Notes for future updates
|
||||
`obs_scm` stores an `.obscpio` plus `.obsinfo`. The `tar` and `recompress`
|
||||
services reconstruct `zupt-5.2.8.tar.gz` inside the build environment, which
|
||||
matches `Source0` in the spec.
|
||||
|
||||
* The `_service` `revision` is pinned to `v5.0.0`. To track a new
|
||||
release, edit that one line and re-run `osc service runall`.
|
||||
* The spec's `Version:` field is hard-coded — when you bump `_service`
|
||||
`revision`, also bump `Version:` to match.
|
||||
* `BuildRequires` is intentionally minimal (`gcc gzip make`). vaptvupt
|
||||
has no external library dependencies in the default build; do not add
|
||||
system crypto BuildRequires.
|
||||
This source policy does not prohibit separately built release-page packages.
|
||||
The upstream 5.2.8 gates may publish the CLI source tarball, DEB, binary RPM,
|
||||
SRPM, notice-bearing Linux tar.xz, Windows ZIP, and macOS DMG, together with a
|
||||
GUI DEB, noarch RPM, GUI SRPM, and source-only portable GUI ZIP after each
|
||||
format-specific test succeeds. None of those files is an OBS `Source0` input
|
||||
or belongs in Git. AppImage and bare executables remain excluded: the former
|
||||
lacks an audited runtime source/relink handoff, while the latter does not carry
|
||||
the required license and notice payload beside the program.
|
||||
|
||||
## Reporting issues
|
||||
## License and bundled codec
|
||||
|
||||
* Upstream bugs: https://git.securityops.co/cristiancmoises/vaptvupt
|
||||
* openSUSE packaging bugs: https://bugs.opensuse.org/
|
||||
* Cabelo's OBS project: https://build.opensuse.org/project/show/home:cabelo:innovators
|
||||
The resulting executable combines the AGPL-3.0-or-later application with the
|
||||
GPL-3.0-or-later VaptVupt codec, adapted BSD-2-Clause XXH64 routines, and
|
||||
CC0-1.0 pq-crystals/kyber-derived ML-KEM portions, plus BSD-3-Clause
|
||||
curve25519-donna-derived X25519 portions, so the RPM uses:
|
||||
|
||||
```text
|
||||
AGPL-3.0-or-later AND GPL-3.0-or-later AND BSD-2-Clause AND BSD-3-Clause AND CC0-1.0
|
||||
```
|
||||
|
||||
The bundled codec is VaptVupt codec tag `v2.65.3`. It was integrated into this
|
||||
repository by commit `59f9ebc59ea13c6edf1d199ca795cdbf00e62226` and is declared
|
||||
as `bundled(vaptvupt-codec) = 2.65.3`. That integration commit records the local
|
||||
ANS safe-zone reserve patch applied on top of the upstream tag. The package
|
||||
retains all license and notice files, including Yann Collet's xxHash notice;
|
||||
it does not claim that the codec is unbundled.
|
||||
|
||||
## Optional SDK and PQBOX integrations
|
||||
|
||||
The OBS package always builds with:
|
||||
|
||||
```text
|
||||
WITH_SDK=0 WITH_PQBOX=0
|
||||
```
|
||||
|
||||
The resulting CLI retains the in-tree password, ML-KEM-768, X25519 and hybrid
|
||||
features. It does not enable the optional libvuptsdk-backed Argon2id/`--pq-sdk`
|
||||
integration or the separate libpqvaptvupt-backed `--pq-box` integration. Those
|
||||
options may only be enabled in a future package after their complete source or
|
||||
system development packages, licenses, ABI and dependencies have been audited.
|
||||
The build does not download dependencies and never loads a repository-local
|
||||
`.so`, `.a` or `.o` fallback.
|
||||
|
||||
## Archive integrity and compatibility in 5.2.2
|
||||
|
||||
New encrypted archives bind every DATA and DEDUP_REF frame to its logical
|
||||
position. An authenticated reference also carries the authenticated position of
|
||||
the source DATA frame, and new disk archives use flag-gated index/content-hash
|
||||
metadata. The on-disk version byte remains 1.6, but an older reader is not
|
||||
claimed to accept every new 5.2.2 encoding.
|
||||
|
||||
The packaged `extract`, `list`, `test`, and `disk restore` paths require an
|
||||
archive-integrity trailer by default, without trusting unauthenticated header
|
||||
flags. `--allow-legacy-no-ait` is accepted only by those commands for recovery
|
||||
of a known, trusted pre-AIT archive and emits a downgrade warning. `info` merely
|
||||
reports unauthenticated framing and apparent AIT presence; it does not validate
|
||||
the trailer or contents. Package documentation must not recommend the override
|
||||
for untrusted input or present `info` success as an integrity result.
|
||||
|
||||
The separate v5.2.1 compatibility claim is narrow: an actual
|
||||
password-encrypted, deduplicated DATA/DATA/REF/DATA disk archive created from the
|
||||
immutable v5.2.1 tag is stored as hexadecimal text with its source and SHA-256
|
||||
provenance. The 5.2.2 reader reconstructs the legacy linear block-AAD sequence,
|
||||
lists, tests, extracts, and restores its input byte-exact through the
|
||||
fixed-width legacy disk-index parser. This does not cover every historical mode
|
||||
and passed in the full local Linux gate for commit `ff99770`; the target RPM
|
||||
`%check` must still exercise it before that package is promoted.
|
||||
|
||||
Disk restore also snapshots the measured archive into a private scratch file
|
||||
before opening the destination, then validates and restores from that same
|
||||
stream. An invalid `ZUPT_TMPDIR` override (or the compatibility fallback
|
||||
`VAPTVUPT_TMPDIR`) and an unknown or insufficient raw-device capacity fail
|
||||
before the first target write. The package check covers
|
||||
the unprivileged unknown-capacity path; its loop-device size regression is
|
||||
reported `SKIP`, not `PASS`, when the builder cannot create a loop device.
|
||||
|
||||
## Migration from the former package name
|
||||
|
||||
The main package is named `zupt` and installs only `/usr/bin/zupt`, its man
|
||||
page, and its completions. The spec has a versioned `Provides: vaptvupt` and
|
||||
`Obsoletes: vaptvupt` so an installed package under the former public name can
|
||||
upgrade cleanly. It intentionally does not claim or install a second
|
||||
`/usr/bin/vaptvupt` executable. The bundled codec and optional library keep
|
||||
their established VaptVupt identifiers because those are compatibility-facing
|
||||
API names, not the application package name.
|
||||
|
||||
## Local validation workflow
|
||||
|
||||
Run these commands in an OBS package checkout, not in the upstream Git tree:
|
||||
|
||||
```sh
|
||||
xmllint --noout _service
|
||||
osc service manualrun
|
||||
rpmspec -P zupt.spec >/dev/null
|
||||
spec-cleaner --diff zupt.spec
|
||||
osc build --clean --keep-pkgs="$PWD/.osc-build-results" \
|
||||
openSUSE_Tumbleweed x86_64
|
||||
rpmlint .osc-build-results/*.rpm
|
||||
```
|
||||
|
||||
`osc service manualrun` materializes the service marked `manual` (the pinned
|
||||
SCM input). The tarball itself is
|
||||
reconstructed by the build-time services. Neither `%build` nor `%check` may
|
||||
access the network.
|
||||
|
||||
For a source RPM check outside OBS, place the service-produced
|
||||
`zupt-5.2.8.tar.gz` next to the spec and use a disposable RPM build tree:
|
||||
|
||||
```sh
|
||||
rpm_top=$(mktemp -d)
|
||||
trap 'rm -rf -- "$rpm_top"' EXIT
|
||||
mkdir -p "$rpm_top"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
||||
rpmbuild --define "_topdir $rpm_top" --define "_sourcedir $PWD" \
|
||||
-bs zupt.spec
|
||||
```
|
||||
|
||||
After building, inspect the RPM contents and dependencies, run `rpmlint`, then
|
||||
install it in a disposable openSUSE environment and execute
|
||||
`scripts/test-installed-zupt.sh`. Do not claim a repository or architecture
|
||||
as supported until its build and installed smoke test have actually passed.
|
||||
|
||||
## Prior 5.2.2 committed-candidate local Linux validation
|
||||
|
||||
The immutable 5.2.2 candidate at `ff99770` passed the full local
|
||||
`make release-check`. Packaging policy
|
||||
and syntax reported `PASS=49 FAIL=0 SKIP=0`; source-only scanner testing passed
|
||||
39/39, including GNU thin archives and safe diagnostic cases; strict GCC,
|
||||
strict Clang, GCC `-fanalyzer`, the 9/9 full tool-enabled static-analysis run,
|
||||
ASan/UBSan/LSan, and 1,000 mutation-fuzz iterations passed. A reduced
|
||||
environment completed six available static checks and reported `cppcheck`
|
||||
unavailable rather than passing it. Earlier off-screen GUI smoke evidence is
|
||||
supporting evidence, not an exact-commit package result.
|
||||
|
||||
Post-tag CI integration failures prevented 5.2.2 promotion. These historical
|
||||
local results do not establish 5.2.8, native Windows or macOS success, hosted
|
||||
GitHub CI/release promotion, authenticated OBS acceptance, or resolution of the
|
||||
automatic openSUSE `debugsource` rpmlint `no-binary` finding. The immutable
|
||||
5.2.3 candidate was not promoted because its source-policy test assumed LF for
|
||||
a Windows `.bat` file checked out as CRLF.
|
||||
|
||||
## Prior 5.2.4 exact-tag source-service evidence
|
||||
|
||||
The immutable v5.2.4 candidate was not promoted. Exact-tag GitHub Actions run
|
||||
`33431386002` recorded 12 successful jobs and one failed openSUSE job. That job's
|
||||
standalone `Serviceinfo` harness passed the service directory to the executor
|
||||
but did not make it the process working directory; dependent native Windows and
|
||||
macOS jobs were skipped.
|
||||
|
||||
A disposable local openSUSE Tumbleweed reproduction independently resolved
|
||||
`refs/tags/v5.2.4` to the tagged commit. With `osc` 1.27.3,
|
||||
`obs-service-obs_scm` 0.12.4, `obs-service-tar` 0.12.4, and
|
||||
`obs-service-recompress` 0.5.2 installed, the same executor completed
|
||||
`obs_scm`, `tar`, and `recompress` after `os.chdir(service_dir)`. It produced
|
||||
exactly one `zupt-5.2.4.tar.gz`; its SHA-256 was
|
||||
`aa68a58fc2e88ee92296542de1f189e2b8a803154d832fb04d5296b25acaef8f`, and the
|
||||
source scanner reported `PASS source-only: 204 files, 1 archives`.
|
||||
|
||||
This result establishes that the explicit tag revision works and isolates a
|
||||
release/test harness defect. It does not change the product, archive format,
|
||||
cryptography, codec, or SDK ABI; it does not make skipped native jobs pass or
|
||||
establish authenticated OBS/Factory acceptance. No v5.2.4 evidence transferred
|
||||
automatically to v5.2.8; the exact candidate later repeated every applicable
|
||||
upstream gate in run `33456209269`, as recorded below. The automatic openSUSE
|
||||
`debugsource` rpmlint `no-binary` finding remains unresolved and unsuppressed.
|
||||
|
||||
## Prior 5.2.5 exact-tag native-gate evidence
|
||||
|
||||
The immutable v5.2.5 candidate was not promoted. Exact-tag GitHub Actions run
|
||||
`33434986357` completed 13 jobs successfully and failed the native Windows and
|
||||
macOS jobs. Windows exposed a hostile-path fixture that did not preserve its
|
||||
requested bytes across the command-line boundary; macOS exposed the unsupported
|
||||
`explicit_bzero` assumption and Bash 3.2 empty-array handling. The 5.2.6
|
||||
corrections address those release/test integration defects without an archive,
|
||||
cryptographic, codec, or SDK ABI change.
|
||||
|
||||
## Prior 5.2.6 exact-tag native-gate evidence
|
||||
|
||||
The immutable v5.2.6 candidate was not promoted. Exact-tag GitHub Actions run
|
||||
`33442264243` completed 13 jobs successfully and failed two native jobs. The
|
||||
macOS arm64 SHA-NI test build treated unused x86-only helper declarations as
|
||||
errors under `-Werror`; Windows argv transcoding aborted the safe printable
|
||||
UTF-8 fixture before its intended path assertions. The 5.2.7 changes correct
|
||||
those test-harness boundaries without an archive-format, cryptographic, codec,
|
||||
or SDK ABI change. They do not establish 5.2.8 hosted, native, OBS, or promotion
|
||||
evidence.
|
||||
|
||||
## Prior 5.2.7 exact-tag native-gate evidence
|
||||
|
||||
The immutable v5.2.7 candidate was not promoted. Exact-tag GitHub Actions run
|
||||
`33445470664` concluded `cancelled` at `2026-08-31T23:11:19Z`, with 13
|
||||
successful jobs, one failed macOS job, and one cancelled Windows job. macOS
|
||||
rejected creation of the raw-C1 scanner fixture
|
||||
with `EILSEQ`; the hosted Windows job stalled in `make check`, and a MinGW/Wine
|
||||
reproduction isolated the cause to a redirected password prompt entering
|
||||
`_getch`. Version 5.2.8 makes those test
|
||||
boundaries fail or skip without hanging, addresses CodeQL High #5/#6/#7 in SDK
|
||||
key publication, disk restore, and benchmark cleanup, and adds `sdk-test` to
|
||||
release and hosted Linux gates. None of those changes establishes an exact
|
||||
5.2.8 OBS, native, hosted-CI, or promotion result.
|
||||
|
||||
## 5.2.8 exact-tag upstream package evidence
|
||||
|
||||
Manually dispatched exact-tag GitHub Actions run `33456209269` passed all 15
|
||||
jobs at `ebb9ab3aa1d42c50030ca02883f6162dc4771fe1`. Its openSUSE Tumbleweed
|
||||
x86_64 job parsed and normalized the spec, executed the pinned standalone OBS
|
||||
source-service chain, source-scanned the resulting archive, built the binary
|
||||
RPM and genuine SRPM, ran `rpmlint` without suppressions, and completed the
|
||||
install/round-trip/uninstall test. The canonical source archive is 798296 bytes
|
||||
with SHA-256
|
||||
`378b9506211545b9594cf0d38ac8955d9b1cac34eb6b379ae0ec26b84edb65f7`.
|
||||
|
||||
Promotion run `33457868306` published the exact tested binary RPM and SRPM with
|
||||
the other gated assets. The source package is identified by
|
||||
`%{SOURCEPACKAGE}=1` and an absent `%{SOURCERPM}`; its `%{ARCH}` legitimately
|
||||
reflects the spec's build architecture and is not the SRPM discriminator.
|
||||
Repository, Git archive, and upstream source tarball scans remain binary-free.
|
||||
|
||||
This is upstream local-service and package evidence, not a claim that the
|
||||
package was submitted to or accepted by openSUSE Factory, nor a result for the
|
||||
full set of automatically generated OBS debug packages or any untested
|
||||
architecture.
|
||||
|
||||
## Prior openSUSE packaging validation
|
||||
|
||||
The local results below were produced on 2026-08-24 from the 5.2.2 candidate
|
||||
snapshot captured for the packaging run, in a disposable openSUSE Tumbleweed
|
||||
20260822 x86_64 container. This matrix was documented afterward, so the results
|
||||
validate that captured snapshot, not the later documentation edit, a future
|
||||
commit or a tag. Commit- and tag-dependent checks must be repeated after the
|
||||
final commit; the validation tarball checksum below is not a release checksum.
|
||||
`SKIP` is not success.
|
||||
|
||||
| Gate | Result | Evidence |
|
||||
|---|---|---|
|
||||
| `_service` XML syntax | PASS | `xmllint --noout`; installed service definitions and parameters also exercised locally. |
|
||||
| ShellCheck for packaging, export, source-policy, and security regression scripts | PASS | ShellCheck 0.10.0 returned zero for the scripts listed in the CI source-policy job, including the scanner and new archive/disk regressions; repeat after the final commit/tag. |
|
||||
| Upstream source-only scanner and adversarial scanner tests | PASS | Clean snapshot: 191 files; OBS tar: 191 files/1 archive; SRPM tree: 193 files/1 archive; 29 positive/negative scanner regressions passed. |
|
||||
| Reproducible source archive (two builds, same SHA-256) | PASS | Two local `obs_scm`/`tar`/`recompress` runs were byte-identical (`39e59f5e...`, validation only; regenerate after the real tag). |
|
||||
| Upstream build, `make check`, and `make test-all` | SKIP | The real RPM `%check`/`make check` passed; an exact-candidate `make test-all` result was not produced by this packaging run. |
|
||||
| Positional DATA/DEDUP_REF AAD and mandatory-AIT regressions | PASS | `%check` passed AIT removal, F-09 preface, DATA/REF reorder/replay, little-endian, varint and atomic-output regressions. |
|
||||
| v5.2.1 encrypted+dedup disk compatibility | PASS | Working-tree candidate decoded the textual 718-byte v5.2.1 DATA/DATA/REF/DATA fixture, then `list`, `test`, generic extraction, and byte-exact disk restore passed; repeat after the final commit/tag. |
|
||||
| `rpmspec` parse | PASS | Both `rpmspec -P` and `rpmspec --parse` returned zero; Source0 resolved to `zupt-5.2.2.tar.gz`. |
|
||||
| `spec-cleaner` | PASS | Version 1.2.4+2 returned zero and proposed no diff. |
|
||||
| `rpmbuild` source and binary RPM | PASS | `rpmbuild -bs` and `-ba` passed from the service-generated Source0 with the openSUSE `.changes` conversion. |
|
||||
| `rpmlint` main RPM + SRPM | PASS | 0 errors and one `invalid-url Source0` warning for the service-generated local Source0; no `rpmlintrc` or suppression was added. |
|
||||
| `rpmlint` including automatic debug packages | FAIL | `debugsource: no-binary` error and expected `debuginfo: unstripped-binary-or-object` warning from the complete generated package set; debug packages were not disabled or suppressed. |
|
||||
| `osc service` | PASS | Installed `obs_scm` 0.12.4, `tar` 0.12.4 and `recompress` 0.5.2 produced the correctly named source tar locally; canonical tag fetch remains tag-dependent. |
|
||||
| Tumbleweed x86_64 local build/install/round trip/uninstall | PASS | Tumbleweed 20260822 container: RPM `%check`, root and `nobody` installed tests, content/hardening audit and clean uninstall passed. This is not an OBS/Factory result. |
|
||||
| Official OBS `osc build` invocation | FAIL | The command reached `https://api.opensuse.org` but returned HTTP 401 because no OBS credentials are configured. |
|
||||
| Factory/Tumbleweed x86_64 OBS validation | SKIP | The failed authenticated `osc build` invocation produced no Factory build result; local Tumbleweed evidence is not promoted to Factory evidence. |
|
||||
| aarch64, ppc64le, s390x, riscv64 | SKIP | No build evidence yet. |
|
||||
| Leap and SLE | SKIP | No build evidence yet. |
|
||||
|
||||
`SKIP` is not success. Factory/Tumbleweed x86_64 remains the primary downstream
|
||||
gate.
|
||||
|
||||
## Handoff procedure for Alessandro/Cabelo
|
||||
|
||||
1. Upstream completes every applicable pre-tag source and local audit gate,
|
||||
then creates and verifies the annotated `v5.2.8` tag. Exact-tag hosted,
|
||||
native-platform, package, and promotion gates must pass before release or
|
||||
downstream handoff; the tag itself is never moved to repair a failure.
|
||||
2. With Git, `file`, bsdtar, tar, zip, unzip and SHA-256 tools installed, run
|
||||
`scripts/export-opensuse-package.sh v5.2.8`. Verify the reported ZIP and
|
||||
SHA-256 outside the Git index. The handoff includes both
|
||||
`packaging/opensuse/source-audit.sh` and its required
|
||||
`scripts/check-source-only.sh`; keep that relative layout while auditing.
|
||||
3. Check out the OBS package:
|
||||
|
||||
```sh
|
||||
osc checkout home:cabelo:innovators zupt
|
||||
cd home:cabelo:innovators/zupt
|
||||
```
|
||||
|
||||
4. From the extracted handoff root, run
|
||||
`packaging/opensuse/source-audit.sh --archive /path/to/zupt-5.2.8.tar.gz`.
|
||||
Then copy `_service`, `zupt.spec`, `zupt.changes` and `README.md`
|
||||
into the flat OBS package checkout. The audit wrapper is not an OBS build
|
||||
source and must not be copied without its companion `scripts/` directory.
|
||||
5. Run the local validation workflow above, including the installed round-trip
|
||||
test. Build every repository and architecture enabled in the OBS project;
|
||||
record failures or unavailable gates as such.
|
||||
6. Review `osc diff`, confirm that no RPM or other binary was added as a source,
|
||||
and commit to OBS only after the required gates pass.
|
||||
|
||||
For future releases, increment the stable patch version, create a new immutable
|
||||
tag, update the matching revision/version in `_service`, spec and changes, run
|
||||
the source-only scanner, regenerate the handoff, and repeat every OBS gate.
|
||||
Never move an existing tag or consume forge release binaries as `Source0`.
|
||||
|
|
|
|||
|
|
@ -1,16 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
||||
<services>
|
||||
<service name="tar_scm" mode="manual">
|
||||
<param name="url">https://github.com/cristiancmoises/vaptvupt</param>
|
||||
<param name="scm">git</param>
|
||||
<param name="revision">v5.0.0</param>
|
||||
<param name="versionformat">@PARENT_TAG@</param>
|
||||
<param name="versionrewrite-pattern">v(.*)</param>
|
||||
<param name="submodules">enable</param>
|
||||
<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"/>
|
||||
<service name="obs_scm" mode="manual">
|
||||
<param name="url">https://github.com/cristiancmoises/zupt.git</param>
|
||||
<param name="scm">git</param>
|
||||
<param name="revision">refs/tags/v5.2.8</param>
|
||||
<param name="versionformat">@PARENT_TAG@</param>
|
||||
<param name="versionrewrite-pattern">^v(.*)$</param>
|
||||
<param name="versionrewrite-replacement">\1</param>
|
||||
<param name="filename">zupt</param>
|
||||
<param name="submodules">disable</param>
|
||||
<param name="lfs">disable</param>
|
||||
</service>
|
||||
<service name="tar" mode="buildtime"/>
|
||||
<service name="recompress" mode="buildtime">
|
||||
<param name="file">*.tar</param>
|
||||
<param name="compression">gz</param>
|
||||
</service>
|
||||
</services>
|
||||
|
|
|
|||
14
packaging/opensuse/source-audit.sh
Executable file
14
packaging/opensuse/source-audit.sh
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)
|
||||
SCANNER=$SCRIPT_DIR/../../scripts/check-source-only.sh
|
||||
|
||||
if [[ ! -x $SCANNER ]]; then
|
||||
printf 'ERROR: source-only scanner is missing or not executable: %s\n' "$SCANNER" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
exec "$SCANNER" "$@"
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
#
|
||||
# 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: 5.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/vaptvupt
|
||||
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.
|
||||
LZ + ANS compression (VaptVupt codec, ~2-3 GB/s decompression on x86_64
|
||||
with AVX2 / aarch64 with NEON), AES-256-CTR + HMAC-SHA256 per-block
|
||||
authenticated encryption, multi-threaded, with ML-KEM-768 + X25519
|
||||
post-quantum hybrid key encapsulation (FIPS 203 + RFC 7748) via --pq.
|
||||
|
||||
This package builds entirely from source with no external library
|
||||
dependency. The password KDF is PBKDF2-SHA256 (600k iterations). The
|
||||
optional libzuptsdk-backed modes (Argon2id KDF, --pq-sdk, --pq-box) are
|
||||
not built here; they require an upstream WITH_SDK=1 build against the
|
||||
separately distributed libzuptsdk/libpqvaptvupt.
|
||||
|
||||
Pure C11, ~5,000 lines of core code. Constant-time cryptographic
|
||||
primitives are formally verified with Jasmin on x86_64
|
||||
(zupt_mac_verify_ct, zupt_ct_select_32); a clean C fallback runs on
|
||||
aarch64 and other architectures.
|
||||
|
||||
%prep
|
||||
%autosetup -p1
|
||||
chmod +x tests/*.sh
|
||||
|
||||
%build
|
||||
%make_build V=1 WITH_SDK=0 \
|
||||
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 WITH_SDK=0 \
|
||||
CFLAGS="%{optflags} -fPIE -Wall -Wextra -std=c11 -Iinclude -Isrc" \
|
||||
LDFLAGS="%{?build_ldflags} -pie" \
|
||||
LDLIBS="-lm -lpthread" \
|
||||
test-vectors
|
||||
./test_vectors
|
||||
%else
|
||||
%make_build V=1 WITH_SDK=0 \
|
||||
CFLAGS="%{optflags} -fPIE -Wall -Wextra -std=c11 -Iinclude -Isrc" \
|
||||
LDFLAGS="%{?build_ldflags} -pie" \
|
||||
LDLIBS="-lm -lpthread" \
|
||||
check
|
||||
%endif
|
||||
|
||||
%install
|
||||
%make_install WITH_SDK=0 PREFIX=%{_prefix}
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc README.md SECURITY.md CHANGELOG.md
|
||||
%{_bindir}/vaptvupt
|
||||
%{_bindir}/zupt
|
||||
%{_datadir}/bash-completion/completions/vaptvupt
|
||||
%{_datadir}/bash-completion/completions/zupt
|
||||
%{_datadir}/zsh/site-functions/_vaptvupt
|
||||
%{_datadir}/zsh/site-functions/_zupt
|
||||
%{_datadir}/fish/vendor_completions.d/vaptvupt.fish
|
||||
%{_mandir}/man1/vaptvupt.1%{?ext_man}
|
||||
%{_mandir}/man1/zupt.1%{?ext_man}
|
||||
|
||||
%changelog
|
||||
|
|
@ -1,3 +1,131 @@
|
|||
-------------------------------------------------------------------
|
||||
Mon Aug 31 23:30:00 UTC 2026 - Cristian Cezar Moisés <sac@securityops.co>
|
||||
|
||||
- Update to 5.2.8:
|
||||
* Close CodeQL High path-race findings in SDK key publication,
|
||||
descriptor-first disk restore, and benchmark workspace cleanup.
|
||||
* Make the raw-C1 scanner fixture explicitly skip filesystems that reject
|
||||
the byte with EILSEQ, reject redirected Windows prompts before _getch, and
|
||||
add sdk-test to release/hosted Linux gates.
|
||||
* Preserve immutable, unpromoted v5.2.7 run 33445470664: 13 jobs succeeded,
|
||||
macOS failed the raw-C1 fixture, and Windows was cancelled after the hosted
|
||||
job stalled; a MinGW/Wine reproduction isolated redirected _getch entry.
|
||||
* Pin the OBS source service to the immutable v5.2.8 tag and require fresh
|
||||
exact-candidate evidence before promotion.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Aug 31 23:00:00 UTC 2026 - Cristian Cezar Moisés <sac@securityops.co>
|
||||
|
||||
- Update to 5.2.7:
|
||||
* Scope SHA-NI test helpers away from unsupported macOS arm64 builds.
|
||||
* Preserve safe UTF-8 fixture bytes across the Windows argv boundary.
|
||||
* Preserve immutable, unpromoted 5.2.6 history and require fresh 5.2.7 gates.
|
||||
* Pin the OBS source service to the immutable v5.2.7 tag.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Aug 31 21:30:00 UTC 2026 - Cristian Cezar Moisés <sac@securityops.co>
|
||||
|
||||
- Update to 5.2.6:
|
||||
* Use the compiler-resistant volatile wipe fallback on macOS and NetBSD.
|
||||
* Make source-scanner empty-array handling compatible with Bash 3.2.
|
||||
* Preserve hostile archive-path fixture bytes exactly on Windows.
|
||||
* Preserve immutable, unpromoted 5.2.5 history and require fresh 5.2.6 gates.
|
||||
* Pin the OBS source service to the immutable v5.2.6 tag.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Aug 31 19:55:00 UTC 2026 - Cristian Cezar Moisés <sac@securityops.co>
|
||||
|
||||
- Update to 5.2.5:
|
||||
* Run the standalone OBS source-service chain from its isolated working
|
||||
directory so downstream services can find .obsinfo.
|
||||
* Add a packaging-policy regression for the executor working directory.
|
||||
* Preserve immutable, unpromoted 5.2.4 history and require fresh 5.2.5 gates.
|
||||
* Pin the OBS source service to the immutable v5.2.5 tag.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Aug 31 18:55:00 UTC 2026 - Cristian Cezar Moisés <sac@securityops.co>
|
||||
|
||||
- Update to 5.2.4:
|
||||
* Make the static Windows GUI package-version check robust to canonical
|
||||
CRLF checkouts.
|
||||
* Advance source-only package metadata and prepare final archive hashes.
|
||||
* Pin the OBS source service to the immutable v5.2.4 tag.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Aug 31 18:15:00 UTC 2026 - Cristian Cezar Moisés <sac@securityops.co>
|
||||
|
||||
- Update to 5.2.3:
|
||||
* Derive package checks from the upstream version header and stabilize the
|
||||
GUI version output consumed by package gates.
|
||||
* Replace busybox-gawk before installing the native Tumbleweed RPM tooling.
|
||||
* Pin the OBS source service to the immutable v5.2.3 tag.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Aug 31 00:00:00 UTC 2026 - Cristian Cezar Moisés <sac@securityops.co>
|
||||
|
||||
- Update to 5.2.2:
|
||||
* Convert the upstream and OBS inputs to a source-only release: remove
|
||||
precompiled library and object inputs and reject their reintroduction with
|
||||
reusable source-archive auditing.
|
||||
* Build with WITH_SDK=0 and WITH_PQBOX=0. The optional integrations now
|
||||
require an explicit source or packaged system dependency and never use a
|
||||
private precompiled fallback.
|
||||
* Preserve portable compiler and linker flags, architecture-local optimized
|
||||
translation units, scalar fallbacks, parallel build, and staged DESTDIR
|
||||
installation.
|
||||
* Build the packaged executable as PIE with full RELRO/NOW and a
|
||||
non-executable stack while preserving automatic debuginfo generation and
|
||||
avoiding manual stripping or RPATH/RUNPATH.
|
||||
* Update OBS source services to obs_scm pinned to the immutable v5.2.2 tag;
|
||||
disable submodules and Git LFS and create the compressed tarball at build
|
||||
time.
|
||||
* Run the real upstream check target from the RPM check phase without
|
||||
architecture-specific test suppression.
|
||||
* Harden archive extraction against traversal, symlink/hardlink and Windows
|
||||
reparse-point races; publish only fully size/checksum-verified temporary
|
||||
output and add structurally valid hostile-archive regression fixtures.
|
||||
* Reject normal, solid, and disk-backup output aliases of an input file,
|
||||
including alternate spellings, hardlinks, and symlinks, before creating the
|
||||
output; --force cannot bypass this data-loss guard.
|
||||
* Snapshot disk-restore input privately before opening its destructive
|
||||
destination and restore from the same validated stream. Reject raw devices
|
||||
whose capacity is unknown or smaller than the image before the first write.
|
||||
* Enforce DATA frame types across serial, threaded, solid, test, and disk
|
||||
readers, and retain the exact encrypted+dedup AAD sequence used by v5.2.1.
|
||||
Test an actual v5.2.1 password-encrypted DATA/DATA/REF/DATA disk fixture
|
||||
through list, test, generic extraction, and disk restore.
|
||||
* Use random private benchmark scratch directories and remove them without
|
||||
following links instead of using a predictable process-ID path.
|
||||
* Package the AGPL-3.0-or-later application together with the bundled
|
||||
GPL-3.0-or-later VaptVupt codec 2.65.3 and the BSD-2-Clause XXH64-derived
|
||||
routines; preserve all applicable notices.
|
||||
* Rename the application and package back to ZUPT/zupt. Install only the
|
||||
zupt command and add versioned Provides/Obsoletes for migration from the
|
||||
former vaptvupt package without shipping a duplicate executable.
|
||||
* Add the source-only openSUSE handoff/export workflow and validation matrix.
|
||||
* Add explicit password prompt, file, and inherited-descriptor inputs.
|
||||
* Validate the source audit, rpmbuild -bs/-ba, the complete RPM check phase,
|
||||
package contents and dependencies, installed round trips, and clean
|
||||
uninstall in a disposable openSUSE Tumbleweed 20260822 x86_64 container.
|
||||
OBS/Factory, other architectures, Leap, and SLE remain separate unexecuted
|
||||
downstream gates and are not claimed by this validation.
|
||||
* Correct the licensing record without revoking historical MIT grants present
|
||||
in earlier repository revisions; current files follow current SPDX notices.
|
||||
* Correct the stale public-domain statement for XXH64-derived code and retain
|
||||
Yann Collet's BSD-2-Clause copyright, conditions, and disclaimer.
|
||||
* Record the CC0-1.0 option and provenance for pq-crystals/kyber-derived
|
||||
ML-KEM portions, the BSD-3-Clause curve25519-donna origin of native X25519
|
||||
portions, and the public-domain LZMA SDK origin of the x86 BCJ code.
|
||||
* Keep AppImage outside the 5.2.2 promoted set until its static runtime has a
|
||||
complete license/source-relink handoff; publish Windows only as a ZIP with
|
||||
the executable and notices.
|
||||
* Gate notice-bearing Linux tar.xz and Windows/macOS CLI bundles plus GUI
|
||||
DEB, noarch RPM, source RPM, and source-only portable ZIP artifacts; keep
|
||||
bare executables out of the promoted set.
|
||||
* Qualify historical formal-verification and constant-time wording: current
|
||||
source review and runtime regressions are not a proof for every compiler,
|
||||
CPU, or final package binary.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Fri Jul 10 18:00:00 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org>
|
||||
|
||||
|
|
@ -244,10 +372,10 @@ Tue May 26 02:27:34 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org
|
|||
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 license metadata changed to AGPL-3.0-or-later for the then-current
|
||||
source. The original entry incorrectly denied earlier MIT grants; the
|
||||
5.2.2 erratum records that they remain valid for the exact historical
|
||||
material distributed under them.
|
||||
* 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
|
||||
|
|
@ -296,7 +424,7 @@ Sun May 24 13:08:04 UTC 2026 - Alessandro de Oliveira Faria <cabelo@opensuse.org
|
|||
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.
|
||||
Commercial-terms inquiry information was documented separately.
|
||||
* Upstream URL updated to git.securityops.co.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
83
packaging/opensuse/zupt.spec
Normal file
83
packaging/opensuse/zupt.spec
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#
|
||||
# spec file for package zupt
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2026 SUSE LLC
|
||||
# Copyright (c) 2026 Alessandro de Oliveira Faria (A.K.A. Cabelo) <cabelo@opensuse.org>
|
||||
# Alessandro's attribution is for downstream openSUSE/OBS packaging only.
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés <sac@securityops.co> (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.
|
||||
#
|
||||
|
||||
Name: zupt
|
||||
Version: 5.2.8
|
||||
Release: 0
|
||||
Summary: Backup compression with authenticated and post-quantum encryption
|
||||
License: AGPL-3.0-or-later AND GPL-3.0-or-later AND BSD-2-Clause AND BSD-3-Clause AND CC0-1.0
|
||||
URL: https://github.com/cristiancmoises/zupt
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
BuildRequires: bash
|
||||
BuildRequires: coreutils
|
||||
BuildRequires: diffutils
|
||||
BuildRequires: file
|
||||
BuildRequires: findutils
|
||||
BuildRequires: gawk
|
||||
BuildRequires: gcc
|
||||
BuildRequires: git-core
|
||||
BuildRequires: grep
|
||||
BuildRequires: gzip
|
||||
BuildRequires: make
|
||||
BuildRequires: python3-base
|
||||
BuildRequires: sed
|
||||
BuildRequires: tar
|
||||
Provides: bundled(vaptvupt-codec) = 2.65.3
|
||||
Provides: vaptvupt = %{version}-%{release}
|
||||
Obsoletes: vaptvupt < %{version}
|
||||
|
||||
%description
|
||||
ZUPT creates compressed backup archives with optional authenticated
|
||||
password encryption or ML-KEM-768 and X25519 hybrid key encapsulation. The
|
||||
default package is built entirely from the source in the release archive.
|
||||
|
||||
Optional SDK and PQBOX features are disabled because audited development
|
||||
packages are unavailable. No private compiled library is installed.
|
||||
|
||||
%prep
|
||||
%autosetup -p1
|
||||
bash scripts/check-source-only.sh --tree .
|
||||
|
||||
%build
|
||||
%make_build WITH_SDK=0 WITH_PQBOX=0 \
|
||||
CFLAGS="%{optflags} -fPIE" \
|
||||
LDFLAGS="%{?build_ldflags} -Wl,-z,relro,-z,now -pie"
|
||||
|
||||
%check
|
||||
%make_build WITH_SDK=0 WITH_PQBOX=0 \
|
||||
CFLAGS="%{optflags} -fPIE" \
|
||||
LDFLAGS="%{?build_ldflags} -Wl,-z,relro,-z,now -pie" \
|
||||
check
|
||||
|
||||
%install
|
||||
%make_install WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 INSTALL_LICENSES=0 \
|
||||
PREFIX=%{_prefix} \
|
||||
BINDIR=%{_bindir} \
|
||||
MANDIR=%{_mandir}
|
||||
|
||||
%files
|
||||
%license LICENSE LICENSE-AGPL-3.0 LICENSE-GPL-3.0 LICENSE-BSD-2-Clause LICENSE-BSD-3-Clause LICENSE-CC0-1.0 NOTICE THIRD-PARTY-NOTICES.md
|
||||
%doc README.md CHANGELOG.md SECURITY.md THREAT_MODEL.md
|
||||
%{_bindir}/zupt
|
||||
%{_datadir}/bash-completion/completions/zupt
|
||||
%{_datadir}/zsh/site-functions/_zupt
|
||||
%{_datadir}/fish/vendor_completions.d/zupt.fish
|
||||
%{_mandir}/man1/zupt.1%{?ext_man}
|
||||
|
||||
%changelog
|
||||
|
|
@ -1,57 +1,59 @@
|
|||
VaptVupt GUI — portable cross-platform package
|
||||
==============================================
|
||||
ZUPT GUI — source-only portable launcher template
|
||||
=====================================================
|
||||
|
||||
The VaptVupt GUI is a single Python file (zupt_gui.py) built on Qt for Python
|
||||
(PySide6, or PyQt6 as a fallback). It runs on Windows, macOS, Linux and the
|
||||
BSDs — anywhere Python 3 and a Qt binding are installed. This portable package
|
||||
contains the GUI plus a launcher for each platform; it drives the `vaptvupt`
|
||||
command-line tool under the hood.
|
||||
This tracked directory contains three launcher templates and this assembly
|
||||
guide; it is not a complete bundle by itself. A downstream source-only bundle
|
||||
may add the integrated Python GUI source and artwork listed below, together
|
||||
with the required license/provenance files. It must not contain Python, Qt, a
|
||||
precompiled ZUPT command, or a vendored library. Its presence in a release
|
||||
would not be evidence that every target operating system was tested; consult
|
||||
that release's validation matrix.
|
||||
|
||||
Contents
|
||||
--------
|
||||
zupt_gui.py The GUI (PySide6 / PyQt6).
|
||||
vaptvupt-gui.bat Windows launcher.
|
||||
vaptvupt-gui.command macOS launcher (double-clickable in Finder).
|
||||
vaptvupt-gui.sh Linux / BSD launcher.
|
||||
assets/zupt-icon.png Application icon.
|
||||
zupt_gui.py GUI source module (the historical module filename is
|
||||
retained internally for source compatibility).
|
||||
zupt-gui.bat Windows launcher.
|
||||
zupt-gui.command macOS Finder launcher.
|
||||
zupt-gui.sh POSIX shell launcher.
|
||||
assets/zupt-icon.png PNG application artwork.
|
||||
assets/zupt.ico Windows application artwork.
|
||||
LICENSE-AGPL-3.0 Complete current GUI source license text.
|
||||
LICENSE-GUI GUI licensing and historical-license note.
|
||||
ASSET-PROVENANCE.md Artwork purpose, provenance, and license record.
|
||||
CHANGELOG.md Release history and current compatibility notes.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
1. Python 3.8 or newer.
|
||||
Windows: https://python.org (tick "Add python.exe to PATH")
|
||||
macOS: python.org, or `brew install python`
|
||||
Linux: your distro's python3 package
|
||||
FreeBSD: pkg install python311
|
||||
OpenBSD: pkg_add python%3
|
||||
2. A Qt binding:
|
||||
pip (any OS): python3 -m pip install PySide6
|
||||
Debian/Ubuntu: sudo apt install python3-pyqt6
|
||||
Fedora/RHEL: sudo dnf install python3-pyqt6
|
||||
FreeBSD: pkg install py311-pyside6
|
||||
OpenBSD: pkg_add py3-pyside6
|
||||
3. The vaptvupt CLI, either:
|
||||
* placed next to the launcher (vaptvupt.exe on Windows, vaptvupt
|
||||
elsewhere) — the launcher auto-detects it via VAPTVUPT_BIN, or
|
||||
* installed on PATH (deb/rpm/AppImage/Homebrew/pkg — see the project
|
||||
release page).
|
||||
1. Python 3.9 or newer.
|
||||
2. PySide6 6.5 or newer, or a compatible PyQt6 package.
|
||||
3. ZUPT 5.2.8, installed as `zupt` on PATH or placed beside the launcher
|
||||
(`zupt.exe` on Windows). A local command must have been built
|
||||
and tested independently; this bundle never downloads one.
|
||||
|
||||
Running
|
||||
-------
|
||||
Windows: double-click vaptvupt-gui.bat
|
||||
macOS: double-click vaptvupt-gui.command
|
||||
(first run: right-click > Open to bypass Gatekeeper for an
|
||||
unsigned script, or `xattr -dr com.apple.quarantine .`)
|
||||
Linux/BSD: ./vaptvupt-gui.sh
|
||||
Windows: zupt-gui.bat
|
||||
macOS: zupt-gui.command
|
||||
POSIX: ./zupt-gui.sh
|
||||
|
||||
The launchers set ZUPT_BIN when a local command is present. The GUI then
|
||||
checks `zupt version`, discovers native and optional capabilities, and
|
||||
exposes SDK or PQ-box modes only when the command reports the corresponding
|
||||
system-library integration enabled.
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
* "requires PySide6 or PyQt6" -> install a Qt binding (requirement 2).
|
||||
* "vaptvupt not found" -> put the CLI next to the launcher or on PATH.
|
||||
* Set VAPTVUPT_DEBUG=1 to print the binary-discovery log to stderr.
|
||||
* "requires PySide6 or PyQt6": install one Qt binding through your operating
|
||||
system package manager or another trusted, preconfigured Python source.
|
||||
* "zupt not found": install ZUPT 5.2.8 or place its command beside
|
||||
the launcher.
|
||||
* Set ZUPT_DEBUG=1 to print command-discovery diagnostics to stderr.
|
||||
|
||||
Fully self-contained native installers (Windows .exe/.msi, macOS .dmg) that
|
||||
bundle Python + Qt + the CLI are produced by the project's CI on real Windows
|
||||
and macOS runners — see the release page. This portable package is the
|
||||
dependency-light option that works identically on every platform.
|
||||
The old user-facing command name is not installed by this bundle. The `.zupt`
|
||||
archive extension remains unchanged for format compatibility.
|
||||
|
||||
License: AGPL-3.0-or-later. Project: https://git.securityops.co/cristiancmoises/vaptvupt
|
||||
Current GUI source license: AGPL-3.0-or-later. Published historical revisions
|
||||
include MIT grants for the exact material covered by their notices; see
|
||||
LICENSE-GUI and the 5.2.2 erratum in CHANGELOG.md.
|
||||
Project: https://github.com/cristiancmoises/zupt
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# VaptVupt GUI launcher for macOS (portable package).
|
||||
# Double-clickable in Finder (.command). Requirements on the target Mac:
|
||||
# * Python 3.8+ (python.org, Homebrew `brew install python`, or Xcode CLT)
|
||||
# * PySide6 or PyQt6: python3 -m pip install PySide6
|
||||
# * The vaptvupt CLI: `vaptvupt` next to this file, or on PATH
|
||||
# (Homebrew: `brew install cristiancmoises/tap/vaptvupt`).
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
[ -x "$HERE/vaptvupt" ] && export VAPTVUPT_BIN="$HERE/vaptvupt"
|
||||
|
||||
PY="$(command -v python3 || command -v python || true)"
|
||||
if [ -z "$PY" ]; then
|
||||
osascript -e 'display alert "VaptVupt GUI" message "Python 3 not found. Install it from python.org or `brew install python`, then run: python3 -m pip install PySide6"' 2>/dev/null
|
||||
echo "Python 3 not found." >&2; exit 1
|
||||
fi
|
||||
exec "$PY" "$HERE/zupt_gui.py" "$@"
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
@echo off
|
||||
rem SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
rem VaptVupt GUI launcher for Windows (portable package).
|
||||
rem ZUPT GUI launcher for Windows (portable package).
|
||||
rem
|
||||
rem Requirements on the target machine:
|
||||
rem * Python 3.8+ (https://python.org — tick "Add python.exe to PATH")
|
||||
rem * Python 3.9+
|
||||
rem * PySide6 or PyQt6: py -m pip install PySide6
|
||||
rem * The vaptvupt CLI: vaptvupt.exe next to this file, or on PATH.
|
||||
rem * The ZUPT CLI: zupt.exe next to this file, or on PATH.
|
||||
rem
|
||||
rem If vaptvupt.exe sits beside this launcher we pin it via VAPTVUPT_BIN so the
|
||||
rem If zupt.exe sits beside this launcher we pin it via ZUPT_BIN so the
|
||||
rem GUI drives the bundled CLI rather than any other copy on PATH.
|
||||
setlocal
|
||||
set "HERE=%~dp0"
|
||||
if exist "%HERE%vaptvupt.exe" set "VAPTVUPT_BIN=%HERE%vaptvupt.exe"
|
||||
if exist "%HERE%zupt.exe" set "ZUPT_BIN=%HERE%zupt.exe"
|
||||
|
||||
rem Prefer the py launcher, fall back to python on PATH.
|
||||
where py >nul 2>nul
|
||||
|
|
@ -23,9 +23,8 @@ if %ERRORLEVEL%==0 (
|
|||
set "RC=%ERRORLEVEL%"
|
||||
if not "%RC%"=="0" (
|
||||
echo.
|
||||
echo vaptvupt-gui exited with code %RC%.
|
||||
echo zupt-gui exited with code %RC%.
|
||||
echo If you saw an import error, install the Qt binding: py -m pip install PySide6
|
||||
echo If the CLI was not found, put vaptvupt.exe next to this launcher or on PATH.
|
||||
pause
|
||||
echo If the CLI was not found, put zupt.exe next to this launcher or on PATH.
|
||||
)
|
||||
endlocal
|
||||
endlocal & exit /b %RC%
|
||||
17
packaging/portable/zupt-gui.command
Executable file
17
packaging/portable/zupt-gui.command
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# ZUPT GUI launcher for macOS (portable package).
|
||||
# Double-clickable in Finder (.command). Requirements on the target Mac:
|
||||
# * Python 3.9+
|
||||
# * PySide6 or PyQt6: python3 -m pip install PySide6
|
||||
# * The ZUPT CLI: `zupt` next to this file, or on PATH
|
||||
# (Homebrew: `brew install cristiancmoises/tap/zupt`).
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
[ -x "$HERE/zupt" ] && export ZUPT_BIN="$HERE/zupt"
|
||||
|
||||
PY="$(command -v python3 || command -v python || true)"
|
||||
if [ -z "$PY" ]; then
|
||||
osascript -e 'display alert "ZUPT GUI" message "Python 3.9 or newer was not found. Install Python and a trusted PySide6 or PyQt6 package."' 2>/dev/null
|
||||
echo "Python 3 not found." >&2; exit 1
|
||||
fi
|
||||
exec "$PY" "$HERE/zupt_gui.py" "$@"
|
||||
10
packaging/portable/vaptvupt-gui.sh → packaging/portable/zupt-gui.sh
Normal file → Executable file
10
packaging/portable/vaptvupt-gui.sh → packaging/portable/zupt-gui.sh
Normal file → Executable file
|
|
@ -1,21 +1,21 @@
|
|||
#!/bin/sh
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# VaptVupt GUI launcher for Linux and the BSDs (portable package).
|
||||
# ZUPT GUI launcher for Linux and the BSDs (portable package).
|
||||
# Requirements on the target system:
|
||||
# * Python 3.8+
|
||||
# * Python 3.9+
|
||||
# * PySide6 or PyQt6
|
||||
# Debian/Ubuntu: sudo apt install python3-pyqt6
|
||||
# Fedora/RHEL: sudo dnf install python3-pyqt6
|
||||
# FreeBSD: pkg install py311-pyside6 (or py311-qt6-pyqt)
|
||||
# OpenBSD: pkg_add py3-pyside6
|
||||
# any OS via pip: python3 -m pip install PySide6
|
||||
# * The vaptvupt CLI: `vaptvupt` next to this file, or on PATH.
|
||||
# * The ZUPT CLI: `zupt` next to this file, or on PATH.
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
[ -x "$HERE/vaptvupt" ] && export VAPTVUPT_BIN="$HERE/vaptvupt"
|
||||
[ -x "$HERE/zupt" ] && export ZUPT_BIN="$HERE/zupt"
|
||||
|
||||
PY="$(command -v python3 || command -v python || true)"
|
||||
if [ -z "$PY" ]; then
|
||||
echo "vaptvupt-gui: Python 3 not found on PATH." >&2
|
||||
echo "zupt-gui: Python 3 not found on PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
exec "$PY" "$HERE/zupt_gui.py" "$@"
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#
|
||||
# Fedora / RHEL / CentOS RPM spec for vaptvupt.
|
||||
#
|
||||
# Build with:
|
||||
# spectool -g vaptvupt.spec # fetches the upstream tarball
|
||||
# rpmbuild -ba vaptvupt.spec # builds source + binary RPMs
|
||||
#
|
||||
# To bring a release into production:
|
||||
# 1. Run `make dist` upstream → /tmp/vaptvupt-VERSION.tar.gz (reproducible).
|
||||
# 2. Upload to a stable release URL (git.securityops.co releases).
|
||||
# 3. Update %{version} below.
|
||||
# 4. Run `sha256sum /tmp/vaptvupt-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: 5.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/vaptvupt
|
||||
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,
|
||||
validated byte-for-byte against OpenSSL's ML-KEM-768) and full
|
||||
pure ML-KEM-768 (--pq-only)
|
||||
* AES-256-CTR + HMAC-SHA256 authenticated encryption (Encrypt-then-MAC)
|
||||
* PBKDF2-SHA256 password key derivation (Argon2id in WITH_SDK=1 builds)
|
||||
* Multi-threaded compression with the VaptVupt LZ + ANS codec
|
||||
* Full-disk backup and restore with sparse-region detection
|
||||
* End-to-end byte-level tamper detection on encrypted archives
|
||||
(0 silent-accept positions in the v1.6 exhaustive byte sweep)
|
||||
* 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
|
||||
# Source-only build (WITH_SDK=0): no vendored libraries, no external crypto
|
||||
# dependency. Fedora's default optflags plus the project's warning set.
|
||||
%make_build WITH_SDK=0 \
|
||||
CFLAGS="%{optflags} -fPIE -Wall -Wextra -std=c11 -Iinclude -Isrc" \
|
||||
LDFLAGS="%{?build_ldflags} -pie" \
|
||||
LDLIBS="-lm -lpthread"
|
||||
|
||||
%check
|
||||
# Distro-safe regression subset: F-06 HMAC trials, F-08 top-MAC sweep,
|
||||
# F-09 byte sweep, F-10..F-12 regressions, the dedup-nonce regression,
|
||||
# and NIST/RFC vectors. Skips threaded/dist-reproducibility tests that
|
||||
# are sensitive to the build host.
|
||||
%make_build WITH_SDK=0 \
|
||||
CFLAGS="%{optflags} -fPIE -Wall -Wextra -std=c11 -Iinclude -Isrc" \
|
||||
LDFLAGS="%{?build_ldflags} -pie" \
|
||||
LDLIBS="-lm -lpthread" \
|
||||
check
|
||||
|
||||
%install
|
||||
%make_install WITH_SDK=0 DESTDIR=%{buildroot} PREFIX=/usr
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
%doc README.md SECURITY.md CHANGELOG.md
|
||||
%{_bindir}/%{name}
|
||||
%{_bindir}/zupt
|
||||
%{_datadir}/bash-completion/completions/%{name}
|
||||
%{_datadir}/bash-completion/completions/zupt
|
||||
%{_datadir}/zsh/site-functions/_%{name}
|
||||
%{_datadir}/zsh/site-functions/_zupt
|
||||
%{_datadir}/fish/vendor_completions.d/%{name}.fish
|
||||
%if 0%{?_mandir:1}
|
||||
%{_mandir}/man1/%{name}.1*
|
||||
%{_mandir}/man1/zupt.1*
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Fri Jul 10 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.0.0-1
|
||||
- ML-KEM-768 is now genuinely FIPS 203-conformant (was round-3 CRYSTALS-Kyber):
|
||||
fixed a transposed matrix-A sampling convention, the round-3 KDF, and the
|
||||
implicit-rejection domain. Validated byte-for-byte against OpenSSL 3.5's
|
||||
FIPS 203 ML-KEM-768 (tests/test_mlkem_fips203.sh, run in %%check).
|
||||
- BREAKING: --pq / --pq-only keys and archives from <= 4.2.1 no longer decrypt.
|
||||
Regenerate keys and re-encrypt. Password mode / plain compression unaffected.
|
||||
- Security: compress data-loss + silent-plaintext guards; AVX2 decoder heap
|
||||
OOB-read bound; overflow-safe solid-mode test path; secret-wipe on error.
|
||||
- GUI reworked for the source-only build; truthful banner/help.
|
||||
|
||||
* Fri Jul 10 2026 Cristian Cezar Moisés <sac@securityops.co> - 4.2.1-1
|
||||
- Fix: "vaptvupt info" mislabelled full post-quantum (--pq-only) archives as
|
||||
"PQ Hybrid (ML-KEM-768 + X25519)". info now reads the real enc_type from the
|
||||
encryption-header block and reports the actual mode ("ML-KEM-768 only, no
|
||||
classical layer" for --pq-only). Reader-side only; no wire-format change.
|
||||
|
||||
* Thu Jul 09 2026 Cristian Cezar Moisés <sac@securityops.co> - 4.2.0-1
|
||||
- New native full (pure) post-quantum mode --pq-only: ML-KEM-768 as the
|
||||
sole KEM, no classical X25519 (envelope 0x06). For "PQ-only" compliance
|
||||
postures; hybrid --pq remains the recommended default. In-tree crypto.
|
||||
- Security (critical): fixed AES-256-CTR keystream reuse under --dedup
|
||||
(every block now uses a fresh random 128-bit nonce). Re-encrypt any
|
||||
--dedup encrypted archives written by <= 4.1.0.
|
||||
- Clearer keygen --sdk/--box guidance on source-only builds.
|
||||
- Wire format v1.6 unchanged.
|
||||
|
||||
* 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).
|
||||
180
packaging/rpm/zupt.spec
Normal file
180
packaging/rpm/zupt.spec
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
# 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 the canonical GitHub release.
|
||||
# 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 /path/to/rpmbuild' -ba zupt.spec
|
||||
#
|
||||
# This is an upstream Fedora-family recipe. A target is supported only after
|
||||
# that exact distribution release and architecture have built and passed the
|
||||
# installed smoke test.
|
||||
|
||||
Name: zupt
|
||||
Version: 5.2.8
|
||||
Release: 1%{?dist}
|
||||
Summary: Backup compression with authenticated and post-quantum encryption
|
||||
|
||||
License: AGPL-3.0-or-later AND GPL-3.0-or-later AND BSD-2-Clause AND BSD-3-Clause AND CC0-1.0
|
||||
URL: https://github.com/cristiancmoises/zupt
|
||||
Source0: %{url}/releases/download/v%{version}/%{name}-%{version}.tar.gz
|
||||
|
||||
BuildRequires: gcc
|
||||
BuildRequires: git-core
|
||||
BuildRequires: make
|
||||
BuildRequires: glibc-devel
|
||||
BuildRequires: python3 >= 3.8
|
||||
BuildRequires: bash
|
||||
BuildRequires: coreutils
|
||||
BuildRequires: diffutils
|
||||
BuildRequires: file
|
||||
BuildRequires: findutils
|
||||
BuildRequires: gawk
|
||||
BuildRequires: grep
|
||||
BuildRequires: gzip
|
||||
BuildRequires: sed
|
||||
BuildRequires: tar
|
||||
# python3 is only needed for the regression-test harness (byte sweeps,
|
||||
# tamper injection). The shipped binary has no Python dependency.
|
||||
|
||||
Provides: bundled(vaptvupt-codec) = 2.65.3
|
||||
|
||||
%description
|
||||
ZUPT is a pure-C11 backup compression utility featuring:
|
||||
|
||||
* Post-quantum hybrid encryption (ML-KEM-768 + X25519) and full
|
||||
ML-KEM-768 mode (--pq-only)
|
||||
* AES-256-CTR + HMAC-SHA256 authenticated encryption (Encrypt-then-MAC)
|
||||
* PBKDF2-SHA256 password key derivation (Argon2id in WITH_SDK=1 builds)
|
||||
* Multi-threaded compression with the VaptVupt LZ + ANS codec
|
||||
* Full-disk backup and restore with sparse-region detection
|
||||
* Authenticated encrypted-archive metadata and per-block integrity checks
|
||||
* Portable C implementations with optional source-built assembly paths
|
||||
* NIST/RFC test vectors for SHA-256, SHA-3, ML-KEM-768, AES-256-CTR,
|
||||
HMAC-SHA256, X25519 and PBKDF2
|
||||
|
||||
Encrypted archives include an integrity trailer that authenticates the header
|
||||
and footer, per-block HMAC with bound frame-preface AAD, and optional encrypted
|
||||
comments. Plain archives use non-cryptographic checksums.
|
||||
|
||||
%prep
|
||||
%autosetup -n %{name}-%{version}
|
||||
|
||||
%build
|
||||
# Source-only build (WITH_SDK=0): no vendored libraries, no external crypto
|
||||
# dependency. Fedora's default optflags plus the project's warning set.
|
||||
%make_build V=1 WITH_SDK=0 WITH_PQBOX=0 \
|
||||
CFLAGS="%{optflags}" \
|
||||
LDFLAGS="%{?build_ldflags}"
|
||||
|
||||
%check
|
||||
# Distro-safe quick, path-traversal, integrity, codec, HMAC and NIST/RFC
|
||||
# checks. Full, optional-integration and dist-reproducibility suites remain
|
||||
# release gates outside the package build.
|
||||
%make_build V=1 WITH_SDK=0 WITH_PQBOX=0 \
|
||||
CFLAGS="%{optflags}" \
|
||||
LDFLAGS="%{?build_ldflags}" \
|
||||
check
|
||||
|
||||
%install
|
||||
%make_install WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 INSTALL_LICENSES=0 \
|
||||
PREFIX=%{_prefix} BINDIR=%{_bindir} MANDIR=%{_mandir}
|
||||
|
||||
%files
|
||||
%license LICENSE LICENSE-AGPL-3.0 LICENSE-GPL-3.0 LICENSE-BSD-2-Clause LICENSE-BSD-3-Clause LICENSE-CC0-1.0 NOTICE THIRD-PARTY-NOTICES.md
|
||||
%doc README.md SECURITY.md THREAT_MODEL.md CHANGELOG.md
|
||||
%{_bindir}/%{name}
|
||||
%{_datadir}/bash-completion/completions/%{name}
|
||||
%{_datadir}/zsh/site-functions/_%{name}
|
||||
%{_datadir}/fish/vendor_completions.d/%{name}.fish
|
||||
%if 0%{?_mandir:1}
|
||||
%{_mandir}/man1/%{name}.1*
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Mon Aug 31 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.2.8-1
|
||||
- Close CodeQL High path-race findings in SDK key save, disk restore, and
|
||||
benchmark cleanup; add the SDK gate, portable raw-C1 fixture handling, and
|
||||
redirected Windows password-prompt rejection.
|
||||
- Preserve immutable, unpromoted v5.2.7 run 33445470664: 13 jobs succeeded,
|
||||
macOS failed the raw-C1 fixture, and Windows was cancelled after the hosted
|
||||
job stalled; a MinGW/Wine reproduction isolated redirected _getch entry.
|
||||
- Require fresh 5.2.8 gates.
|
||||
|
||||
* Mon Aug 31 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.2.7-1
|
||||
- Correct native test integration: scope SHA-NI helpers away from macOS arm64
|
||||
and preserve safe UTF-8 fixture bytes across the Windows argv boundary.
|
||||
- Preserve immutable, unpromoted 5.2.6 history and require fresh 5.2.7 gates.
|
||||
|
||||
* Mon Aug 31 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.2.6-1
|
||||
- Correct native release gates: use the secure volatile wipe fallback on
|
||||
macOS and NetBSD, support Bash 3.2 empty arrays in the source scanner, and
|
||||
preserve hostile archive-path fixture bytes exactly on Windows.
|
||||
- Preserve immutable, unpromoted 5.2.5 history and require fresh 5.2.6 gates.
|
||||
|
||||
* Mon Aug 31 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.2.5-1
|
||||
- Run the standalone OBS source-service chain from its isolated working
|
||||
directory and add a packaging-policy regression for that contract.
|
||||
- Preserve immutable, unpromoted 5.2.4 history and require fresh 5.2.5 gates.
|
||||
|
||||
* Mon Aug 31 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.2.4-1
|
||||
- Make the static Windows GUI package-version check robust to canonical CRLF
|
||||
checkouts, advance source-only package metadata, and prepare final hashes.
|
||||
|
||||
* Mon Aug 31 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.2.3-1
|
||||
- Correct the release-package CI version checks and portable GUI version
|
||||
contract, and make the openSUSE container replace busybox-gawk before
|
||||
installing the native RPM toolchain.
|
||||
|
||||
* Mon Aug 31 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.2.2-1
|
||||
- Source-only release; optional SDK/PQBOX integrations use system development
|
||||
packages only and are disabled for this package.
|
||||
- Preserve distribution flags and debuginfo, remove RPATH/vendor-library
|
||||
fallbacks, run the real upstream check target, and restore the zupt command.
|
||||
|
||||
* Sat Jul 11 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.1.0-1
|
||||
- Codec 2.65.0; large compression-ratio gains (auto format_v2 + level-scaled
|
||||
block window); --dedup keeps a small block; GUI compress-hang and
|
||||
job-completion-crash fixes. Wire format unchanged (v1.6).
|
||||
|
||||
* Fri Jul 10 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.0.0-1
|
||||
- ML-KEM-768 is now genuinely FIPS 203-conformant (was round-3 CRYSTALS-Kyber):
|
||||
fixed a transposed matrix-A sampling convention, the round-3 KDF, and the
|
||||
implicit-rejection domain. Validated byte-for-byte against OpenSSL 3.5's
|
||||
FIPS 203 ML-KEM-768 (tests/test_mlkem_fips203.sh, run in %%check).
|
||||
- BREAKING: --pq / --pq-only keys and archives from <= 4.2.1 no longer decrypt.
|
||||
Regenerate keys and re-encrypt. Password mode / plain compression unaffected.
|
||||
- Security: compress data-loss + silent-plaintext guards; AVX2 decoder heap
|
||||
OOB-read bound; overflow-safe solid-mode test path; secret-wipe on error.
|
||||
- GUI reworked for the source-only build; truthful banner/help.
|
||||
|
||||
* Fri Jul 10 2026 Cristian Cezar Moisés <sac@securityops.co> - 4.2.1-1
|
||||
- Fix: "vaptvupt info" mislabelled full post-quantum (--pq-only) archives as
|
||||
"PQ Hybrid (ML-KEM-768 + X25519)". info now reads the real enc_type from the
|
||||
encryption-header block and reports the actual mode ("ML-KEM-768 only, no
|
||||
classical layer" for --pq-only). Reader-side only; no wire-format change.
|
||||
|
||||
* Thu Jul 09 2026 Cristian Cezar Moisés <sac@securityops.co> - 4.2.0-1
|
||||
- New native full (pure) post-quantum mode --pq-only: ML-KEM-768 as the
|
||||
sole KEM, no classical X25519 (envelope 0x06). For "PQ-only" compliance
|
||||
postures; hybrid --pq remains the recommended default. In-tree crypto.
|
||||
- Security (critical): fixed AES-256-CTR keystream reuse under --dedup
|
||||
(every block now uses a fresh random 128-bit nonce). Re-encrypt any
|
||||
--dedup encrypted archives written by <= 4.1.0.
|
||||
- Clearer keygen --sdk/--box guidance on source-only builds.
|
||||
- Wire format v1.6 unchanged.
|
||||
|
||||
* 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).
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
; SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
; Inno Setup script for the VaptVupt GUI Windows installer.
|
||||
;
|
||||
; Compiled by the cross-platform CI (.github/workflows/cross-platform.yml) with:
|
||||
; ISCC.exe /DAppVersion=<version> packaging/windows/vaptvupt-gui.iss
|
||||
; after PyInstaller has produced dist\vaptvupt-gui.exe (a onefile bundle that
|
||||
; already contains Python, PySide6 and vaptvupt.exe). Requires Inno Setup 6+.
|
||||
;
|
||||
; To build locally on Windows: install Inno Setup, then run the same ISCC line
|
||||
; from the repo root (with dist\vaptvupt-gui.exe present).
|
||||
|
||||
#ifndef AppVersion
|
||||
#define AppVersion "0.0.0"
|
||||
#endif
|
||||
|
||||
[Setup]
|
||||
AppName=VaptVupt
|
||||
AppVersion={#AppVersion}
|
||||
AppPublisher=Cristian Cezar Moises
|
||||
AppPublisherURL=https://git.securityops.co/cristiancmoises/vaptvupt
|
||||
DefaultDirName={autopf}\VaptVupt
|
||||
DefaultGroupName=VaptVupt
|
||||
UninstallDisplayIcon={app}\vaptvupt-gui.exe
|
||||
OutputDir=packaging\windows\Output
|
||||
OutputBaseFilename=VaptVupt-Setup-{#AppVersion}
|
||||
Compression=lzma2
|
||||
SolidCompression=yes
|
||||
ArchitecturesAllowed=x64compatible
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
WizardStyle=modern
|
||||
LicenseFile=LICENSE
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Files]
|
||||
; PyInstaller onefile bundle (Python + PySide6 + the GUI + vaptvupt.exe).
|
||||
Source: "dist\vaptvupt-gui.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
; Ship the raw CLI too so it can be added to PATH and used from a terminal.
|
||||
Source: "vaptvupt.exe"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist
|
||||
Source: "README.md"; DestDir: "{app}"; Flags: ignoreversion isreadme
|
||||
Source: "CHANGELOG.md"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\VaptVupt"; Filename: "{app}\vaptvupt-gui.exe"
|
||||
Name: "{group}\Uninstall VaptVupt"; Filename: "{uninstallexe}"
|
||||
Name: "{autodesktop}\VaptVupt"; Filename: "{app}\vaptvupt-gui.exe"; Tasks: desktopicon
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "Create a desktop shortcut"; GroupDescription: "Additional icons:"
|
||||
Name: "addtopath"; Description: "Add the vaptvupt CLI to PATH (current user)"; GroupDescription: "Command line:"
|
||||
|
||||
[Registry]
|
||||
; Optionally add the install dir to the user PATH (for the vaptvupt.exe CLI).
|
||||
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; \
|
||||
ValueData: "{olddata};{app}"; Tasks: addtopath; Check: NeedsAddPath('{app}')
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\vaptvupt-gui.exe"; Description: "Launch VaptVupt"; \
|
||||
Flags: nowait postinstall skipifsilent
|
||||
|
||||
[Code]
|
||||
function NeedsAddPath(Param: string): Boolean;
|
||||
var
|
||||
OrigPath: string;
|
||||
begin
|
||||
if not RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', OrigPath) then
|
||||
begin
|
||||
Result := True;
|
||||
exit;
|
||||
end;
|
||||
Result := Pos(';' + ExpandConstant(Param) + ';', ';' + OrigPath + ';') = 0;
|
||||
end;
|
||||
98
packaging/windows/zupt-gui.iss
Normal file
98
packaging/windows/zupt-gui.iss
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
; SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
; Inno Setup 6 recipe for target-built ZUPT Windows artifacts.
|
||||
;
|
||||
; All paths are mandatory command-line definitions. This prevents the recipe
|
||||
; from silently picking up a stale or placeholder executable from the tree.
|
||||
|
||||
#ifndef AppVersion
|
||||
#error AppVersion must be defined
|
||||
#endif
|
||||
#ifndef GuiExecutable
|
||||
#error GuiExecutable must name a tested PyInstaller GUI executable
|
||||
#endif
|
||||
#ifndef CliExecutable
|
||||
#error CliExecutable must name a tested source-built zupt.exe
|
||||
#endif
|
||||
#ifndef BuildOutputDir
|
||||
#error BuildOutputDir must be an external output directory
|
||||
#endif
|
||||
#ifndef RuntimeNoticesDir
|
||||
#error RuntimeNoticesDir must contain notices for the exact bundled GUI runtime
|
||||
#endif
|
||||
|
||||
[Setup]
|
||||
AppId={{59AD35E4-1860-445D-8E89-4563DB9ED4E2}
|
||||
AppName=ZUPT
|
||||
AppVersion={#AppVersion}
|
||||
AppPublisher=Cristian Cezar Moises
|
||||
AppPublisherURL=https://github.com/cristiancmoises/zupt
|
||||
AppSupportURL=https://github.com/cristiancmoises/zupt/issues
|
||||
DefaultDirName={autopf}\ZUPT
|
||||
DefaultGroupName=ZUPT
|
||||
UninstallDisplayIcon={app}\zupt-gui.exe
|
||||
OutputDir={#BuildOutputDir}
|
||||
OutputBaseFilename=ZUPT-Setup-{#AppVersion}
|
||||
Compression=lzma2
|
||||
SolidCompression=yes
|
||||
ArchitecturesAllowed=x64compatible
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
WizardStyle=modern
|
||||
LicenseFile=..\..\LICENSE
|
||||
ChangesAssociations=yes
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Files]
|
||||
Source: "{#GuiExecutable}"; DestDir: "{app}"; DestName: "zupt-gui.exe"; Flags: ignoreversion
|
||||
Source: "{#CliExecutable}"; DestDir: "{app}"; DestName: "zupt.exe"; Flags: ignoreversion
|
||||
Source: "..\..\LICENSE"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\..\LICENSE-AGPL-3.0"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\..\LICENSE-GPL-3.0"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\..\LICENSE-BSD-2-Clause"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\..\LICENSE-BSD-3-Clause"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\..\LICENSE-CC0-1.0"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\..\NOTICE"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\..\THIRD-PARTY-NOTICES.md"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\..\gui\LICENSE-GUI"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\..\gui\assets\README.md"; DestDir: "{app}"; DestName: "GUI-ASSET-PROVENANCE.md"; Flags: ignoreversion
|
||||
Source: "{#RuntimeNoticesDir}\*"; DestDir: "{app}\third-party-runtime-notices"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "..\..\README.md"; DestDir: "{app}"; Flags: ignoreversion isreadme
|
||||
Source: "..\..\CHANGELOG.md"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\ZUPT GUI"; Filename: "{app}\zupt-gui.exe"
|
||||
Name: "{group}\ZUPT command prompt"; Filename: "{cmd}"; Parameters: "/K cd /d ""{app}"""
|
||||
Name: "{group}\Uninstall ZUPT"; Filename: "{uninstallexe}"
|
||||
Name: "{autodesktop}\ZUPT GUI"; Filename: "{app}\zupt-gui.exe"; Tasks: desktopicon
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "Create a desktop shortcut"; GroupDescription: "Additional icons:"
|
||||
Name: "addtopath"; Description: "Add the ZUPT command to PATH for this user"; GroupDescription: "Command line:"
|
||||
|
||||
[Registry]
|
||||
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; \
|
||||
ValueData: "{olddata};{app}"; Tasks: addtopath; Check: NeedsAddPath('{app}')
|
||||
Root: HKCU; Subkey: "Software\Classes\.zupt"; ValueType: string; ValueName: ""; \
|
||||
ValueData: "ZUPT.Archive"; Flags: uninsdeletevalue
|
||||
Root: HKCU; Subkey: "Software\Classes\ZUPT.Archive"; ValueType: string; \
|
||||
ValueName: ""; ValueData: "ZUPT archive"; Flags: uninsdeletekey
|
||||
Root: HKCU; Subkey: "Software\Classes\ZUPT.Archive\shell\open\command"; \
|
||||
ValueType: string; ValueName: ""; ValueData: """{app}\zupt-gui.exe"" --extract ""%1"""
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\zupt-gui.exe"; Description: "Launch ZUPT GUI"; \
|
||||
Flags: nowait postinstall skipifsilent
|
||||
|
||||
[Code]
|
||||
function NeedsAddPath(Param: string): Boolean;
|
||||
var
|
||||
OrigPath: string;
|
||||
begin
|
||||
if not RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', OrigPath) then
|
||||
begin
|
||||
Result := True;
|
||||
exit;
|
||||
end;
|
||||
Result := Pos(';' + ExpandConstant(Param) + ';', ';' + OrigPath + ';') = 0;
|
||||
end;
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue