feat: add Jasmin assembly integration for crypto acceleration

- Integrated `zupt_mac_verify_ct` in `zupt_decrypt_buffer()` to replace C XOR loop for HMAC-SHA256
- Integrated `zupt_ct_select_32` in `zupt_mlkem768_decaps()` to replace C `cmov()` for FO transformation
- Added `include/zupt_jasmin.h` with extern declarations and ABI docs
- Added `#ifdef ZUPT_USE_JASMIN` guards with clean C fallbacks in `zupt_crypto.c` and `zupt_mlkem.c`
- Makefile now auto-detects `jasmin/*.s`, assembles and links with `-DZUPT_USE_JASMIN`

Closes #3
This commit is contained in:
Cristian Cezar Moisés 2026-03-28 23:00:28 -03:00
commit 06c877ec86
43 changed files with 1913 additions and 546 deletions

View file

@ -1,71 +1,145 @@
# Changelog
All notable changes to Zupt are documented in this file.
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`.
- **`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.0v1.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.0v1.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 (bit 4) set in all v1.0+ archives. Future format changes require v2.0 with new magic bytes.
- **FORMAT.md:** Complete field-level specification of every byte in the archive format.
- **AUDIT.md:** Security audit checklist with findings and mitigations.
- **FUZZING.md:** AFL++ setup, corpus generation, expected coverage targets.
- **License changed:** GPL-3.0 → MIT. All source file headers updated.
- **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.**
## [0.7.0] — 2026-03-21
### 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
- **ML-KEM-768 (FIPS 203)** pure C11 implementation in `src/zupt_mlkem.c` (~600 lines). Constant-time NTT, Barrett/Montgomery reduction, CBD sampling, Fujisaki-Okamoto CCA transform with implicit rejection.
- **X25519 (RFC 7748)** pure C11 implementation in `src/zupt_x25519.c` (~270 lines). Montgomery ladder, constant-time `fe_cswap`, 5×51-bit field arithmetic.
- **Keccak-f[1600]** with SHA3-256, SHA3-512, SHAKE-128, SHAKE-256 in `src/zupt_keccak.c` (~215 lines). Required by ML-KEM for hashing and sampling.
- **Hybrid KEM:** ML-KEM-768 + X25519 combined key encapsulation. Shared secret derived via `SHA3-512(ml_kem_ss XOR x25519_ss ‖ ct ‖ ephemeral_pk ‖ "ZUPT-HYBRID-v1")`. Secure if EITHER ML-KEM or X25519 is secure.
- **`zupt keygen`** subcommand generates ML-KEM-768 + X25519 keypair (`.zupt-key` format). `--pub` exports public key only.
- **`--pq <keyfile>`** flag for compress/extract/list/test. Encrypts with recipient's public key (no password needed).
- **Key file format:** `ZKEY` magic, version byte, flags, ML-KEM pk (1184B) + X25519 pk (32B) + optional sk (2400B + 32B), XXH64 checksum.
- **10-test PQ test suite** (`tests/test_pq.sh`): keygen, pubkey export, key sizes, PQ compress, round-trip, integrity, wrong-key rejection, password backward compat, PQ+MT, large file.
### 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.
### Fixed — ML-KEM Bugs (Critical)
- **basemul array out-of-bounds:** Loop accessed `zetas[64+i]` past the 128-entry array. Fixed to 64 iterations with ±zeta per FIPS 203.
- **Missing `poly_tomont` in keygen:** Public key was computed without Montgomery domain normalization. K-PKE encrypt/decrypt produced different results.
- **Inverted cmov in FO decaps:** Constant-time conditional always selected the rejection key, even on valid ciphertext. Root cause: C integer promotion in `(diff-1) >> 8` expression.
- **`inv_ntt` used wrong zetas table:** Separate `zetas_inv[]` with incorrect values. Fixed to reuse `zetas[]` with k counting 127→0.
- **PQ nonce mismatch:** Encrypt and decrypt independently generated random `base_nonce`. Fixed: nonce stored in encryption header, decrypt reads it back.
### 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).
### Changed
- Archive format: v1.3 → v1.4.
- Encryption header extended: `enc_type` prefix byte (0x01=PBKDF2, 0x02=PQ-Hybrid).
- Legacy v0.5 archives (no enc_type) still read correctly via fallback detection.
- `ZUPT_FLAG_PQ_HYBRID` (bit 3) added to global_flags.
### 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.
### Security — No Regressions
- HMAC verified before decryption in every worker (unchanged).
- Constant-time MAC comparison (unchanged).
- Password mode (-p) fully backward compatible.
- All intermediate ML-KEM/X25519 key material wiped with `zupt_secure_wipe()`.
### Core Features (v0.1.0v0.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.
## [0.6.0] — 2026-03-21
---
### Added
- Multi-threaded compression (`-t <N>`). Batch-parallel pipeline. 14-test MT suite.
## Summary
## [0.5.1] — 2026-03-21
| Version | Key Change | Tests |
|---------|-----------|-------|
| **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 |
### Fixed
- 16 bugs: Huffman over-subscription, heap-buffer-overflows, CSPRNG fallback removed, constant-time MAC, LE serialization, write error tracking.
---
## [0.4.0] — 2026-03-01
### Added
- Byte prediction preprocessor (Zupt-LZHP). Solid mode.
## [0.3.0] — 2026-02-15
### Added
- Zupt-LZH codec: LZ77 + Huffman, 1MB window, near-optimal parsing.
## [0.2.0] — 2026-01-20
### Added
- AES-256-CTR + HMAC-SHA256 encryption. PBKDF2. Directory recursion.
## [0.1.0] — 2026-01-01
### Added
- Initial release. Zupt-LZ codec, `.zupt` format, XXH64 checksums, CLI.
© 2026 Cristian Cezar Moisés — MIT License