441 lines
34 KiB
Markdown
441 lines
34 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to Zupt are documented in this file.
|
||
Format follows [Keep a Changelog](https://keepachangelog.com/).
|
||
|
||
---
|
||
|
||
## [2.1.7] — 2026-04-26
|
||
|
||
### Changed — License (MIT → AGPL-3.0-or-later)
|
||
|
||
- **Zupt is now licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later).** All previous versions are also retroactively re-licensed under AGPL-3.0-or-later by the sole copyright holder. The MIT license under which v0.1.0 through v2.1.6 were originally released is hereby withdrawn for any future use; cached MIT-licensed copies of those versions remain valid under their original terms but are not authorized for redistribution under MIT going forward.
|
||
- **Rationale.** Years of work on post-quantum hybrid encryption (ML-KEM-768 + X25519), formally-verified Jasmin constant-time assembly, authenticated encryption (AES-256-CTR + HMAC-SHA256), and the custom VaptVupt compression codec were given to the world under MIT — and silent absorption into proprietary products is the foreseeable end-state of permissive licensing on infrastructure software. The AGPL closes the SaaS loophole specifically: anyone running a modified Zupt as a network service (cloud backup product, archival backend, etc.) must release the source code of their modifications. Personal use, internal corporate use, sysadmin use, research use, and academic use are unaffected and remain unrestricted.
|
||
- **Commercial licensing channel established.** Organizations whose use is incompatible with the AGPL (proprietary embedding, hosted SaaS without source disclosure, redistribution under closed terms, requirement of warranty/indemnification) can obtain a commercial license at: **sac@securityops.co**.
|
||
|
||
### Changed — VaptVupt License clarification (GPL-3.0-or-later, unchanged from upstream)
|
||
|
||
- **The VaptVupt compression codec is licensed under GPL-3.0-or-later** (`src/vv_*.c`, `src/vaptvupt_api.c`, `include/vaptvupt.h`, `include/vaptvupt_api.h`, `include/vv_huffman.h`, `include/vv_ans.h`, `include/vv_platform.h`). This matches the canonical upstream at [github.com/cristiancmoises/vaptvupt](https://github.com/cristiancmoises/vaptvupt) and was not changed in this sprint — VaptVupt has been GPL-3.0-or-later since its initial release.
|
||
- **Why a separate license from Zupt's AGPL-3.0.** VaptVupt has independent reuse value as a standalone compression codec. Anyone wishing to embed VaptVupt in another GPL-3.0 project should be able to do so without inheriting the network-clause obligations of Zupt's AGPL.
|
||
- **Compatibility with Zupt.** GPL-3.0-or-later is two-way compatible with AGPL-3.0-or-later via the explicit GPL/AGPL combination provision in section 13 of both licenses. The combined Zupt binary remains valid AGPL-3.0.
|
||
- Earlier sprint drafts briefly considered GPL-2.0-or-later for kernel-mergeability; that path was incorrect (the Linux kernel is GPL-2-only and rejects GPL-3+ code) and was withdrawn before release. VaptVupt is and remains GPL-3.0-or-later.
|
||
|
||
### Upgraded — VaptVupt 2.40.0 → 2.46.1
|
||
|
||
- **Pulled in upstream VaptVupt v2.46.1** (April 22, 2026), six minor versions ahead of the previously bundled v2.40.0. All 12 VaptVupt files (`src/vv_*.c`, `src/vaptvupt_api.c`, `include/vaptvupt*.h`, `include/vv_*.h`) replaced from `github.com/cristiancmoises/vaptvupt` `master`.
|
||
- **Public API and on-disk frame format are byte-identical** to v2.40.0 — confirmed by diffing the public `vv_compress`/`vv_decompress`/`vv_default_options`/`vv_compress_bound` declarations and the `vv_options_t` struct. **No caller changes required** in `zupt_lzh.c`, `zupt_format.c`, `zupt_main.c`, etc.
|
||
- **Improvements pulled in:**
|
||
- **v2.41–v2.43** internal tightening (covered by upstream's "correctness release" v2.44.0 rollup).
|
||
- **v2.44.0** — correctness release ships over v2.43.0 (encoder ANS state recovery, decoder bounds tightening).
|
||
- **v2.45.0** — size-based window-log heuristic (better ratio on small inputs without manual tuning).
|
||
- **v2.46.0** — Huffman-in-SEQ literal coding (additional ratio gain on text-heavy streams).
|
||
- **v2.46.1** — **memory-safety patch:** fixes a 16,384-byte leak on three decoder error paths in `vva_decode_sequences_impl` (`src/vv_ans.c`). The `dec_ll` buffer was not freed on three malformed-input return paths. No behavior change on the success path; output byte-identical to v2.46.0 for valid inputs. **This is a correctness/safety fix that closes a denial-of-service vector** where an attacker controlling many malformed compressed frames could exhaust memory.
|
||
|
||
### Notes on the v2.46.1 upgrade
|
||
- VaptVupt 2.46.1 retains the same SPDX-License-Identifier headers (`GPL-3.0-or-later`); the Zupt-side commercial-licensing line (`Commercial licensing: sac@securityops.co`) was re-applied to each file's header block.
|
||
|
||
### Added — `LICENSE-VAPTVUPT`
|
||
- New top-level file containing the formal GPL-3.0-or-later notice, separate-codec rationale (independent reuse value), AGPL/GPL-3 compatibility explanation per section 13 of both licenses, commercial-license clause, pointer to upstream, and the full canonical GPLv3 text (~740 lines).
|
||
|
||
### Added — Formal Preamble in `LICENSE`
|
||
- The top-level `LICENSE` now opens with a formal AGPL preamble explaining why AGPL was chosen, what the SaaS clause means in practice for users vs. operators, and a commercial-licensing clause pointing to **sac@securityops.co**. The full canonical AGPL-3.0 text follows below the preamble.
|
||
|
||
### Added — SPDX-License-Identifier headers (full coverage)
|
||
- Every `.c` and `.h` source file now carries a machine-readable SPDX tag: `AGPL-3.0-or-later` for Zupt-core, `GPL-3.0-or-later` for VaptVupt. Each header also includes the commercial-license contact. This brings Zupt into REUSE 3.0 / SPDX 2.3 compliance and enables automated license scanning (FOSSology, ScanCode, etc.) to correctly classify the project.
|
||
|
||
### Changed — Sister projects relicensed
|
||
- `zupt-web` and `zupt-android` (separate repositories) are also relicensed to **AGPL-3.0-or-later** for the same reason. License files and README snippets prepared in this sprint can be applied to those repositories.
|
||
|
||
### Changed — Surface-level metadata
|
||
- `--help` and `--version` output: `License: MIT` → `License: AGPL-3.0-or-later (commercial: sac@securityops.co)`.
|
||
- Manpage `doc/zupt.1` `.SH LICENSE` section rewritten.
|
||
- README badge: `license-MIT-blue` → `license-AGPL--3.0-blue`. New commercial badge added.
|
||
- README: full License section rewrite + new "Commercial Licensing" section.
|
||
- README comparison table: license row updated.
|
||
- GUI About box: zupt credit "MIT" → "AGPL-3.0".
|
||
- `gui/setup.py`: `license="AGPL-3.0-or-later"` and OSI classifier updated.
|
||
- `gui/packaging/rpm/zupt-gui.spec`: `License: AGPL-3.0-or-later`, `Requires: zupt >= 2.1.7`.
|
||
- `gui/LICENSE-GUI`: rewritten as AGPL-3.0-or-later notice.
|
||
- `Makefile`: header banner updated to v2.1.7 with SPDX tag; comment "Apache-2.0, integrated under MIT" replaced with "GPL-3.0-or-later, separate copyleft".
|
||
|
||
### Audit — Security re-validation on the relicensed tree
|
||
- See `AUDIT.md` for the v2.1.7 refresh: full rebuild, NIST/RFC test vector re-run, ASAN re-run, regression suite re-run, and the updated table of known limitations.
|
||
|
||
### Notes for downstream packagers
|
||
- The SPDX tags are authoritative. If an automated license scanner produces a different result, the SPDX tags in the source file headers should be treated as the source of truth, supplemented by the `LICENSE` and `LICENSE-VAPTVUPT` files.
|
||
- Distributions that previously shipped Zupt under MIT should update their package metadata. The on-disk binary's behavior is unchanged; only the licensing terms have changed.
|
||
|
||
---
|
||
|
||
## [2.1.6] — 2026-04-22
|
||
|
||
### Added — Archive Info Command (`zupt info`)
|
||
- **New `zupt info <archive>` subcommand**: Shows archive metadata without needing a password. Displays format version, UUID, creation timestamp, file size, block count, and all flags (encrypted, PQ hybrid, solid, multithreaded, dedup, disk image). Useful for inspecting archives before decryption, triaging backups, and scripting.
|
||
- Works on all archive types: plain, encrypted, PQ, dedup, disk images.
|
||
- Rejects non-archive files with a clear error message.
|
||
|
||
### Added — Password Strength Warnings
|
||
- **Weak password detection** during `zupt compress -p`: warns if password is shorter than 8 characters ("very short") or shorter than 12 characters with fewer than 3 character classes ("weak"). Uses character class analysis: uppercase, lowercase, digits, special characters.
|
||
- Non-blocking: warning is informational only, compression proceeds. PBKDF2 with 600K iterations provides baseline protection even for weaker passwords.
|
||
|
||
### Enhanced — `zupt list` Shows Dedup & PQ Flags
|
||
- Archive listing header now displays `| Dedup`, `| PQ`, `| Disk` flags when present, making it immediately visible what features an archive uses.
|
||
|
||
### Upgraded — VaptVupt 2.40.0 Codec
|
||
- **VaptVupt 2.40.0** integrated (production hardening release). 55-case adversarial test suite, 10,200-case differential fuzzer, new `vv_xxh64.c` and `vv_platform.h`.
|
||
- **format_v2 enabled**: 4-7% better compression ratio on binary data (ELF, shared libraries). All v2.33.0+ decoders read v2 frames transparently.
|
||
- **VV_DECOMPRESS_SKIP_CHECKSUM**: Decode skips redundant XXH64 verification since zupt's HMAC-SHA256 already authenticates compressed data. Up to 3.7x faster decode on post-encryption random data.
|
||
|
||
### Added — Zupt GUI (PySide6 Desktop Application)
|
||
- Cross-platform graphical interface in `gui/` subdirectory. Covers all zupt operations: key generation (generate + export public key), compress (all codecs, levels, dedup, solid, password, PQ), extract, verify, info, disk backup/restore.
|
||
- System integration: Nemo right-click actions (Compress/Extract with Zupt), .zupt MIME type, desktop file.
|
||
- Packaging: .deb, .rpm, .whl, source tarball, NSIS Windows installer, AppImage, Flatpak configs.
|
||
- Dynamic version display from zupt binary (no hardcoded version strings). Window icon on all platforms.
|
||
|
||
### Tests
|
||
- **97 total**: 84 existing (70 core + 8 disk + 6 dedup) + 13 v2.1.6 (7 info, 4 password, 2 list flags). VV unit tests 11/11. ASAN clean.
|
||
|
||
---
|
||
|
||
## [2.1.5] — 2026-04-12
|
||
|
||
### 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 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.
|
||
- **Backward compatible**: Archives without `--dedup` are byte-identical to v2.1.4. Dedup reference blocks are handled transparently on extract/restore — no `--dedup` flag needed for reading.
|
||
- **New source file**: `src/zupt_dedup.c` (165 lines) — dedup context, hash table, ref block writer.
|
||
- **New global flag**: `ZUPT_FLAG_DEDUP (1u << 7)` — informational, set in archive header.
|
||
- **Extract paths updated**: Both `zupt_extract_archive()` (single-threaded) and `zupt_disk_restore()` handle `DEDUP_REF` blocks by seeking to the referenced offset, reading+decompressing the original block, then seeking back.
|
||
|
||
### Tests
|
||
- **84 total**: 70 core + 8 disk + 6 dedup (plain, password, PQ, PQ+password, disk, no-duplicates). ASAN clean.
|
||
|
||
---
|
||
|
||
## [2.1.4] — 2026-04-11
|
||
|
||
### Fixed — CodeQL Security Alerts (4/4 resolved)
|
||
|
||
- **Alert #1 & #2: TOCTOU filesystem race in `get_device_size()`** (High). The function called `stat(path)` to classify the file type, then `open(path)` to read it — between those two calls an attacker could swap the path to a different file. Fix: open the fd first with `open()`, then classify via `fstat(fd)`. The fd is stable and cannot be swapped.
|
||
- **Alert #3: Dead-store `memset` in `zupt_x25519()`** (High). The `memset(e, 0, 32)` call to wipe the clamped scalar was the last use of `e` before the function returned, so the compiler could legally optimize it away (and some do at `-O2`). Fix: volatile pointer loop (`volatile uint8_t *ve = e; for(...) ve[i] = 0;`) which the compiler must emit.
|
||
- **Alert #4: TOCTOU filesystem race in `zupt_disk_restore()`** (High). Restore called `stat(target_path)` to check for block devices, then `open(target_path)` — same race window as alerts #1/#2. Fix: open fd first, then `fstat(fd)` to classify, then `fcntl(fd, F_SETFL, O_SYNC)` for block devices.
|
||
|
||
### Tests
|
||
- **78 total:** 70 core + 8 disk. ASAN + UBSan clean.
|
||
|
||
---
|
||
|
||
## [2.1.3] — 2026-04-11
|
||
|
||
### Fixed — LZHP Prediction Encoding Missing in Disk Backup (data corruption)
|
||
- **Root cause:** `zupt_disk_backup()` LZHP compression path skipped the `zupt_predict_encode()` step. When byte prediction was active (`pred_active=1`), it stored the prediction table and wrote `cbuf[0] = 0x01`, but then compressed the **raw block data** instead of the prediction-encoded data. On restore, `decompress_block()` correctly applied `zupt_predict_decode()` to the decompressed output, producing corrupted data. Checksum mismatch on block 0 for any block with structured content (ext4 metadata, NTFS headers, partition tables).
|
||
- **Impact:** ALL disk backups using LZHP codec (default on CPUs without AVX2) on non-random data were silently corrupted. VaptVupt codec was unaffected (no prediction path). Random/incompressible data was unaffected (prediction benefit < threshold → `pred_active=0`).
|
||
- **Fix:** Added `zupt_predict_encode(rbuf, transformed, nread, pred)` before `zupt_lzh_compress()`, matching the correct path in `zupt_format.c` (lines 557–563). Allocated temporary buffer for prediction-encoded data, freed after compression.
|
||
|
||
### Fixed — Spurious SOLID Flag on Disk Archives
|
||
- Disk backup no longer sets `ZUPT_FLAG_SOLID` in the archive header. Disk images are independent per-block archives, not solid streams. The SOLID flag caused `zupt_extract_archive()` to take the wrong code path if a disk archive was ever parsed by the extract function.
|
||
|
||
### Fixed — Shared Encryption Header (eliminates all format mismatches)
|
||
- **Extracted `write_enc_header()`** from `zupt_format.c` as a shared non-static function. ALL three encryption write paths — `zupt_compress_files()`, `zupt_compress_solid()`, and `zupt_disk_backup()` — now call the same function.
|
||
- **Solid compress now supports PQ encryption.**
|
||
- **`zupt_w8()`, `zupt_w16le()`, `zupt_w64le()`** made non-static and declared in `zupt.h`.
|
||
|
||
### Fixed — Block Device Restore I/O
|
||
- Restore uses POSIX raw I/O (`open()` + `write()` loop) with `O_SYNC` for block devices, `fsync()` + `sync()` before close.
|
||
|
||
### Fixed — Termux/Android Build
|
||
- Arch-safety guard uses `$(CC) -dumpmachine` for host detection. Falls back to `uname -m`.
|
||
|
||
### Tests
|
||
- **78 total:** 70 core + 8 disk (including LZHP+PQ+password on ext4 — the exact failing case). ASAN + UBSan clean.
|
||
|
||
---
|
||
|
||
## [2.1.2] — 2026-04-06
|
||
|
||
### Added — Full-Disk Backup/Restore
|
||
- **`zupt disk backup`** — streams a raw block device or file in 4MB chunks, compresses each block with the selected codec (VaptVupt default), detects all-zero (sparse) blocks and stores them with near-zero overhead. Supports password encryption (`-p`), post-quantum encryption (`--pq`), compression level override (`-l 1-9`), and codec selection (`--vv`, `--lzhp`). Real-time progress bar with throughput on stderr.
|
||
- **`zupt disk restore`** — reads a disk image archive block-by-block, decrypts + decompresses each block, validates per-block XXH64 checksums, and writes sequentially to the target device or file. Rejects wrong passwords/keys immediately on first block failure.
|
||
- **`ZUPT_FLAG_DISK_IMAGE (1u << 6)`** — new global flag in the archive header. `zupt disk restore` validates this flag and rejects non-disk archives. Standard `zupt extract` rejects disk archives with a clear error message.
|
||
- **`src/zupt_disk.c`** — 530 lines. Portable device size detection: `BLKGETSIZE64` on Linux, `DKIOCGETBLOCKCOUNT` on macOS, `lseek(SEEK_END)` fallback on FreeBSD/generic. 8-byte-wide sparse block detection.
|
||
- CLI: `zupt disk backup [OPTIONS] <output.zupt> <device_or_file>` / `zupt disk restore [OPTIONS] <archive.zupt> <target>`
|
||
|
||
### Tests
|
||
- **77 tests total:** 11 VV unit + 13 NIST/RFC vectors + 22 regression + 14 multi-threaded + 10 post-quantum + 7 disk backup (normal, encrypted, PQ, sparse, LZHP, extreme, wrong-password rejection). ASAN + UBSan clean across all paths.
|
||
|
||
---
|
||
|
||
## [2.1.1] — 2026-04-06
|
||
|
||
### Fixed — Multi-Architecture Build
|
||
- **Stale object files removed from distribution.** Previous tarballs shipped pre-compiled x86_64 `.o` files. On aarch64 (Termux, Raspberry Pi, etc.) the linker failed with `ld.lld: error: src/zupt_xxh.o is incompatible with aarch64linux`. All `.o` files now excluded from release tarballs.
|
||
- **Arch-safety guard in Makefile.** Detects pre-compiled `.o` files from a different architecture via `file(1)` and auto-removes them before linking. Prevents silent link failures if stale objects are accidentally present.
|
||
- **Termux/Android compatibility.** Default compiler changed from `gcc` to `cc` (Termux ships clang). `-lpthread` skipped on Android/Termux (bionic libc has pthreads built-in, detected via `uname -o`).
|
||
- **`sys/syscall.h` include moved to file top** in `zupt_crypto.c`. Was inside function body (non-standard C, rejected by some compilers).
|
||
|
||
### Fixed — Undefined Behavior
|
||
- **Keccak ROL64 shift-by-64 UB.** `ROL64(x, 0)` expanded to `(x >> 64)` which is undefined behavior in C. The Keccak rotation table has `KECCAK_ROT[0] = 0`, triggering this on every Keccak-f[1600] call (SHA3-256, SHA3-512, SHAKE-128, SHAKE-256, ML-KEM-768). Fix: `ROL64` now returns `x` unchanged when `n == 0`. Confirmed zero UBSan violations across all PQ paths.
|
||
|
||
### Tests
|
||
- 70/70: 11 VV + 13 NIST + 22 regression + 14 MT + 10 PQ. ASAN + UBSan clean (zero violations).
|
||
|
||
---
|
||
|
||
## [2.1.0] — 2026-04-05
|
||
|
||
### Upgraded — VaptVupt 1.4.0 Codec
|
||
- **Cross-block dictionary carry** — hash chain now spans block boundaries. The encoder passes absolute positions to `compress_block()` so matches can reference data from previous blocks. Large structured files (7MB logs) compress **5.73:1** instead of per-block independent ratios. The decoder accepts cross-block offsets via a `dst_base` parameter threaded through all decode functions.
|
||
- **Context model decode prefetch** — `__builtin_prefetch` in the order-1 context ANS decode loop hides L2/L3 latency for the 4MB context tables. Extreme-mode decode throughput improved significantly on cache-constrained systems.
|
||
- **Faster adaptive window trial** — greedy depth=4 on 256KB sample instead of full lazy parse on entire first block. Encode speed improved **2.6×** with same ratio decisions.
|
||
- **Zupt integration API** — new `vvz_compress`/`vvz_decompress`/`vvz_compress_bound` wrappers (`vaptvupt_api.h`/`vaptvupt_api.c`) simplify codec dispatch with backup-optimized defaults.
|
||
|
||
### Changed
|
||
- `zupt_format.c` compress paths (normal, solid) now use `vvz_compress()` API instead of raw `vv_compress()` with manual option setup.
|
||
- `zupt_format.c` decompress path now uses `vvz_decompress()` API.
|
||
- Version bumped to 2.1.0.
|
||
|
||
### Performance (balanced mode, vs gzip-9)
|
||
| File Type | v2.1.0 | gzip-9 | vs gzip |
|
||
|-----------|--------|--------|---------|
|
||
| Source code (531K) | 59.5:1 | 51.7:1 | +15% better |
|
||
| JSON (232K) | 10.7:1 | 8.8:1 | +21% better |
|
||
| XML markup (641K) | 18.1:1 | 14.6:1 | +24% better |
|
||
| Long-range (800K) | 5.7:1 | 1.4:1 | +307% better |
|
||
| Logs 7MB (7.5MB) | 5.7:1 | 7.5:1 | gap 24% |
|
||
|
||
### Tests
|
||
- 70/70: 11 VV + 13 NIST + 22 regression + 14 MT + 10 PQ. ASAN clean.
|
||
|
||
---
|
||
|
||
## [2.0.0] — 2026-04-05
|
||
|
||
### Added — VaptVupt 1.1.0 Codec Integration
|
||
- **VaptVupt codec** integrated as `0x0010` — LZ77 + tANS entropy + AVX2 SIMD decode.
|
||
- Three compression modes: Ultra-Fast (greedy), Balanced (lazy + 4-way ANS), Extreme (lazy-2 + order-1 context).
|
||
- **Rep-match offset coding** — 3 recent offsets tracked (like zstd), saves 10–15 bits per repeated match.
|
||
- **Adaptive window selection** — trial-compresses at wlog=16 vs wlog=20, picks larger window only if ≥3% improvement.
|
||
- CLI flags `--vv` / `--vaptvupt` to select VaptVupt codec.
|
||
- CLI flag `--lzhp` to explicitly select Zupt-LZHP codec.
|
||
- VaptVupt source files with dual MIT + Apache-2.0 headers.
|
||
- `vv_xxh64` aliased to `zupt_xxh64` via macro (no duplicate symbol).
|
||
- Wired into compress (single-thread, multi-thread, solid) and decompress paths.
|
||
- 11 VaptVupt unit tests + 6 regression tests (T13–T18).
|
||
|
||
### Added — Auto Codec Detection
|
||
- **`ZUPT_CODEC_AUTO`** — hardware-aware default codec selection:
|
||
- x86_64 with AVX2: VaptVupt (inline AVX2 SIMD decode, ~2–3 GB/s).
|
||
- aarch64 with NEON: VaptVupt (NEON SIMD decode path).
|
||
- All other architectures: Zupt-LZHP (scalar decoder, no SIMD dependency).
|
||
- `zupt_resolve_auto_codec()` checks compile-time flags (`__AVX2__`, `__ARM_NEON`) and runtime CPUID.
|
||
- Decompression is universal — any archive extracts on any architecture regardless of codec.
|
||
- Users can override with `--vv` (force VaptVupt) or `--lzhp` (force LZHP).
|
||
|
||
### Fixed — Jasmin Assembly
|
||
- **AES-NI stack offset bug** fixed: replaced `stack u128[15]` with 15 individual `stack u128` variables to avoid jasminc byte-offset indexing. Round keys now at correct 16-byte aligned offsets.
|
||
- **X25519 fe_cswap** wired: Jasmin swaps first 4 limbs (32 bytes), C handles 5th limb.
|
||
- **All 5 Jasmin functions now active**: `zupt_mac_verify_ct`, `zupt_ct_select_32`, `zupt_fe_cswap`, `zupt_aes256_blk`, `zupt_aes256_ctr4`.
|
||
- **SIGILL fix: AVX detection with OSXSAVE/XCR0 check.** The Jasmin AES assembly uses VEX-encoded instructions (`vaesenc`, `vmovdqu`, `vpxor`) which require AVX — not just AES-NI. Previous dispatch only checked `has_aesni`, causing SIGILL on CPUs with AES-NI but without AVX or without OS XSAVE support. Now checks `has_aesni && has_avx` with proper XGETBV XCR0 validation.
|
||
- Added `has_avx` field to `zupt_cpu_features_t` with correct detection: CPUID ECX[28] (AVX) + ECX[27] (OSXSAVE) + XCR0 bits 1+2.
|
||
|
||
### Fixed — VaptVupt Codec Bugs
|
||
- **`copy_match_scalar` overlap corruption** (`vv_simd.c`): 8-byte bulk copy was used for offsets 4–7, where source overlaps destination by more than the copy stride. The `memcpy` read-then-write semantics don't correctly replicate the overlapping pattern. Fix: byte-by-byte for offsets < 8 (was < 4). This caused silent data corruption on inputs with short-offset matches near the output buffer tail.
|
||
- **`vva_encode_sequences` heap overflow** (`vv_ans.c`): litlen varint buffer allocated as `nseq * 5 + 1` bytes, but individual literal lengths in solid mode can reach 1 MB, requiring up to `ceil(litlen/255) + 1` bytes per varint. Fix: compute exact bound from actual litlen values. This caused heap corruption and abort (`malloc(): invalid size`) on large solid-mode archives.
|
||
|
||
### Added — ACSL Formal Annotations
|
||
- 19 security-critical functions annotated with complete `requires/ensures/assigns` ACSL contracts.
|
||
- Covers: SHA-256, HMAC, PBKDF2, AES-256-CTR, key derivation, encrypt/decrypt, hybrid KEM, SHA3, SHAKE, ML-KEM-768, X25519, secure_wipe.
|
||
- Target: `frama-c -wp -wp-rte -wp-model Typed+Cast`.
|
||
|
||
### Added — Security Hardening
|
||
- **mlock()** for key material — prevents swap to disk (Linux/BSD/Windows).
|
||
- **Buffer canaries** on `zupt_keyring_t` — `canary_head`/`canary_tail` detect overflow, abort on corruption.
|
||
- **Always-decrypt timing mitigation** — `zupt_decrypt_buffer()` always decrypts even on MAC failure (then wipes), preventing timing oracle.
|
||
- **AFL++ fuzzing harnesses** — `fuzz_decompress.c` (archive format) and `fuzz_vv_decompress.c` (VaptVupt codec). `make fuzz-build`.
|
||
|
||
### Added — Performance
|
||
- **AES-NI 4-block pipeline** — `zupt_aes256_ctr4` interleaves 4 counter blocks per AES round for pipeline saturation.
|
||
- **Multi-threaded decompression** — non-solid extract dispatches blocks to N worker threads via `zpar_ctx_t` infrastructure.
|
||
- **Adaptive compression** — `zupt_detect_filetype()` identifies 16+ file formats by magic bytes; already-compressed files get STORE.
|
||
- **Benchmark harness** — `zupt bench --compare` tests all codecs + auto-detects gzip/lz4/zstd.
|
||
|
||
### Changed — Multi-Architecture Support
|
||
- **Makefile rewritten** for full multi-arch builds: x86_64, aarch64, armhf, ppc64le, s390x, riscv64.
|
||
- Jasmin CT assembly: x86_64 only (C fallback on all others).
|
||
- AVX2 SIMD decode: x86_64 only. NEON decode: aarch64. Scalar fallback: everywhere.
|
||
- `LDFLAGS` honored on link line for PIE linking (`-pie -Wl,-z,relro,-z,now`).
|
||
- `LDLIBS` placed after objects (correct rpmlint/OBS link order).
|
||
- `DESTDIR` support for staged packaging installs.
|
||
- Man page `doc/zupt.1` compressed and installed to `$(MANDIR)/man1/zupt.1.gz`.
|
||
- Verbose build with `make V=1`.
|
||
- `make help` shows available targets and detected architecture capabilities.
|
||
- AVX2 detection gates `has_avx2` on `has_avx` (OS XSAVE must be enabled).
|
||
|
||
### Tests
|
||
- **70 tests total:** 11 VV unit + 13 NIST/RFC vectors + 22 regression + 14 multi-threaded + 10 post-quantum.
|
||
- ASAN clean across all modes (normal, encrypted, solid, threaded, PQ).
|
||
- All 5 Jasmin symbols linked (confirmed via `nm`).
|
||
|
||
---
|
||
|
||
## [1.5.5] — 2026-04-01
|
||
|
||
### Fixed — Makefile & Packaging
|
||
- Added man page installation (`doc/zupt.1` → `zupt.1.gz` in `$(MAN1DIR)`).
|
||
- Enabled verbose build output with `V=1` support.
|
||
- Fixed Makefile to honor `LDFLAGS` and support PIE linking.
|
||
- Improved rpmlint compliance for OBS/openSUSE packaging.
|
||
- Jasmin assembly gated to x86_64 only (`ifeq ($(ARCH),x86_64)`) — clean build on aarch64/armhf/ppc64le.
|
||
- Object files excluded from distribution tarballs.
|
||
- `install.sh` convenience installer restored.
|
||
|
||
---
|
||
|
||
## [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`.
|
||
- **`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`.
|
||
|
||
### Not Wired (documented, requires upstream fixes)
|
||
- `zupt_fe_cswap` (X25519): Jasmin uses 4×u64 limbs, C uses 5×u51-bit — incompatible layout. C fallback active.
|
||
- `zupt_aes256_blk` (AES-NI): Assembly has stack offset bug (`[rsp+1]` instead of `[rsp+16]`). C table-based AES active.
|
||
|
||
### Changed
|
||
- Version: 1.4.0 → 1.5.0.
|
||
- `cmov()` in `zupt_mlkem.c` guarded with `#ifndef ZUPT_USE_JASMIN`.
|
||
- MAC comparison return type widened from `uint8_t` to `uint64_t` to match Jasmin signature.
|
||
|
||
### Security
|
||
- 53/53 tests pass with Jasmin linked. 13/13 NIST vectors. ASAN clean. Zero warnings.
|
||
|
||
---
|
||
|
||
## [1.4.0] — 2026-03-28
|
||
|
||
### Fixed — Jasmin Parse Errors (jasminc 2026.03.0)
|
||
All 4 `.jazz` files rewritten to fix compilation errors:
|
||
|
||
- **`zupt_mac_verify.jazz`**: `diff |= a ^ b` — compound XOR+OR not a single x86-64 op. Split into `tmp = a; tmp ^= b; diff |= tmp`.
|
||
- **`zupt_mlkem_select.jazz`**: `out.[i] = (8u)sel` — `reg ptr` is read-only. Changed to `reg u64 out_ptr` with raw pointer writes.
|
||
- **`zupt_x25519_fe.jazz`**: `a.[i] = ta ^ diff` — same const-ptr write. Changed to `reg u64 a_ptr`.
|
||
- **`zupt_aes_ctr.jazz`**: Memory syntax `(u128)[ptr]` → `u128[ptr]` → `[ptr]` — all wrong. Correct: `key.[0]` via `reg ptr u128[N]` for reads; `stack u128[15]` for writes; bare `[ptr + 0]` for u64-width.
|
||
- Uninitialized variable warning: `#VPXOR(zero, zero)` → `wipe = rk.[z]; wipe ^= wipe; rk.[z] = wipe`.
|
||
|
||
### 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`.
|
||
- 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.3.0] — 2026-03-28
|
||
|
||
### Added
|
||
- `include/zupt_acsl.h` — ACSL predicates: `ValidBuffer`, `ValidWriteBuffer`, `Separated2`, `KeyWiped`, `ValidKey`.
|
||
- `SECURITY_REVIEW.md` — 8-section security review with per-function CT analysis table.
|
||
- `jasmin/README.jazz.md` — build instructions, CT verification explanation, error history.
|
||
|
||
### Fixed
|
||
- First round of Jasmin syntax fixes (partial — completed in v1.4.0).
|
||
|
||
---
|
||
|
||
## [1.2.0] — 2026-03-28
|
||
|
||
### Added — CPUID Runtime Detection
|
||
- **`src/zupt_cpuid.c`** + **`include/zupt_cpuid.h`** — runtime detection of AES-NI, PCLMUL, AVX2, SSE4.1 via CPUID. Supports GCC/Clang, MSVC, and inline assembly fallback.
|
||
- `zupt_detect_cpu()` called at program start. Global `zupt_cpu` struct for dispatch.
|
||
|
||
### Added — Jasmin Source Files (initial)
|
||
- 4 `.jazz` files created for AES-CTR, MAC verify, X25519, ML-KEM select.
|
||
- **Note:** All had parse errors — fixed in v1.3.0–v1.4.0.
|
||
|
||
---
|
||
|
||
## [1.1.0] — 2026-03-28
|
||
|
||
### Fixed — Critical Cryptographic Bugs
|
||
|
||
- **X25519 Montgomery formula** (`zupt_x25519.c`): `AA + 121666*E` → `BB + 121666*E`. The doubling formula was algebraically wrong. DH exchanges produced consistently wrong but matching values, so PQ archives worked. RFC 7748 test vectors exposed the bug. **All X25519 in v0.7.0–v1.0.0 was not interoperable with any other implementation.**
|
||
- **Dead `match_cost()`** (`zupt_lzh.c`): Defined but never called. Removed (Clang `-Wunused-function`).
|
||
- **ML-KEM `const polyvec`** warnings: C11 doesn't support multi-level const for arrays-of-arrays. Removed `const` (matches pqcrystals reference).
|
||
- **`__int128` pedantic** warning: Wrapped with `#pragma GCC diagnostic push/pop`.
|
||
|
||
### Added
|
||
- **`tests/test_vectors.c`** — 13 NIST/RFC test vectors: SHA-256 (3), HMAC-SHA256 (2), SHA3-256 (2), SHAKE-128 (1), X25519 (2), ML-KEM-768 (2), XXH64 (1).
|
||
|
||
### Changed
|
||
- Zero warnings on GCC + Clang with `-Wall -Wextra -Wpedantic`.
|
||
|
||
---
|
||
|
||
## [1.0.0] — 2026-03-21
|
||
|
||
### Stable Release
|
||
- **Archive format frozen at v1.4.** `FORMAT_STABLE` flag set. Future changes require v2.0.
|
||
- Documentation: FORMAT.md, AUDIT.md, FUZZING.md, SECURITY.md.
|
||
- **License: GPL-3.0 → MIT.**
|
||
|
||
### Fixed — ML-KEM-768 Bugs (5 critical)
|
||
1. **`poly_basemul` OOB**: `zetas[64+i]` accessed past 128-entry array. Fixed to 64 iterations.
|
||
2. **Missing `poly_tomont()` in keygen**: Public key in wrong Montgomery domain.
|
||
3. **Inverted `cmov` in FO decaps**: C integer promotion caused rejection key selected on valid ciphertext. Fixed: `(-(int64_t)diff) >> 63`.
|
||
4. **`inv_ntt` wrong zetas table**: Separate wrong table. Fixed: reuse `zetas[]`, k counts 127→0.
|
||
5. **PQ nonce mismatch**: Encrypt/decrypt independently generated nonces. Fixed: store in header.
|
||
|
||
### Added — Post-Quantum Hybrid Encryption (v0.7.0)
|
||
- **ML-KEM-768** (FIPS 203): ~658 lines pure C11. NTT, Barrett/Montgomery, CBD, FO transform.
|
||
- **X25519** (RFC 7748): ~270 lines. Montgomery ladder, constant-time fe_cswap.
|
||
- **Keccak-f[1600]**: SHA3-256/512, SHAKE-128/256. ~215 lines.
|
||
- **Hybrid KEM**: `SHA3-512(ml_ss XOR x25519_ss ‖ transcript)`. Secure if EITHER holds.
|
||
- `zupt keygen` subcommand, `--pq <keyfile>` flag.
|
||
- Key file format: ZKEY magic, ML-KEM pk(1184B) + X25519 pk(32B) + optional sk + XXH64.
|
||
- 10-test PQ suite.
|
||
- Format v1.3 → v1.4 with `enc_type` dispatch byte.
|
||
|
||
### Added — Multi-Threaded Compression (v0.6.0)
|
||
- `-t <N>` flag. Batch-parallel pipeline. 14-test MT suite.
|
||
- Solid mode falls back to N=1 (shared LZ context).
|
||
|
||
### Added — Security Hardening (v0.5.1)
|
||
- 16 bug fixes: Huffman Kraft violation (data corruption), heap-buffer-overflows, removed `rand()` fallback, constant-time MAC, secure key wipe, LE serialization, realloc checks, empty file checksum.
|
||
|
||
### Core Features (v0.1.0–v0.4.0)
|
||
- LZ77+Huffman compression (1MB window, near-optimal parsing).
|
||
- AES-256-CTR + HMAC-SHA256 authenticated encryption.
|
||
- PBKDF2-SHA256 (600,000 iterations).
|
||
- Per-block XXH64 integrity. Recursive directory backup. Solid mode.
|
||
|
||
---
|
||
|
||
## Summary
|
||
|
||
| Version | Key Change | Tests |
|
||
|---------|-----------|-------|
|
||
| **2.1.4** | Shared `write_enc_header()` eliminates all format mismatches, solid PQ support, block device O_SYNC | 78 PASS |
|
||
| **2.1.3** | Disk restore rewritten — uses shared block I/O, fixes checksum mismatch with all encryption formats | 77 PASS |
|
||
| **2.1.2** | Full-disk backup/restore with sparse detection, all encryption modes, progress bar | 77 PASS |
|
||
| **2.1.1** | Termux/Android build fix, arch-safety guard, Keccak UB fix, no stale .o in tarballs | 70 PASS |
|
||
| **2.1.0** | VaptVupt 1.4.0: cross-block dictionary, context prefetch, faster adaptive window, integration API | 70 PASS |
|
||
| **2.0.0** | VaptVupt 1.1.0 codec, auto codec detection, all 5 Jasmin wired, AVX SIGILL fix, multi-arch, copy_match fix, litlen overflow fix | 70 PASS |
|
||
| **1.5.5** | Man page install, V=1 verbose, LDFLAGS/PIE, rpmlint, multi-arch Makefile | 53+13 PASS |
|
||
| **1.5.0** | Jasmin assembly linked: MAC verify + ML-KEM select active in binary | 53+13 PASS |
|
||
| **1.4.0** | All 4 `.jazz` files compile on jasminc 2026.03.0 | 53+13 PASS |
|
||
| **1.3.0** | ACSL predicates, security review, partial Jasmin fixes | 53+13 PASS |
|
||
| **1.2.0** | CPUID detection, Jasmin source files (with errors) | 53+13 PASS |
|
||
| **1.1.0** | X25519 BB formula fix, 13 NIST/RFC test vectors | 53+13 PASS |
|
||
| **1.0.0** | Format frozen v1.4, ML-KEM bugs fixed, MIT license | 40 PASS |
|
||
|
||
---
|
||
|
||
© 2026 Cristian Cezar Moisés — MIT License
|