zupt/AUDIT.md
Cristian Cezar Moisés 5bb280eb25 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
2026-03-28 23:03:04 -03:00

178 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Security Audit — Zupt v1.5.0
**Date:** March 28, 2026
**Author:** Cristian Cezar Moisés
**Audit type:** Self-audit with formal verification (Jasmin) and NIST/RFC test vectors
**Status:** No independent third-party audit performed
---
## 1. Cryptographic Test Vector Verification
All primitives tested against published reference vectors:
| Primitive | Standard | Vectors | Status |
|-----------|----------|---------|--------|
| SHA-256 | FIPS 180-4 | 3 (empty, "abc", 448-bit) | **PASS** |
| HMAC-SHA256 | RFC 4231 | 2 (TC2: "Jefe", TC3: 20×0xAA) | **PASS** |
| SHA3-256 | FIPS 202 | 2 (empty, "abc") | **PASS** |
| SHAKE-128 | FIPS 202 | 1 (empty, 128-bit output) | **PASS** |
| X25519 | RFC 7748 §5.2 | 2 (both test vectors) | **PASS** |
| ML-KEM-768 | FIPS 203 | 2 (5-trial roundtrip + implicit rejection) | **PASS** |
| XXH64 | xxHash spec | 1 (empty string, seed=0) | **PASS** |
| **Total** | | **13** | **13/13 PASS** |
Reproduction: `make test-vectors && ./test_vectors`
---
## 2. Functional Test Results
| Suite | Tests | Result | What It Covers |
|-------|-------|--------|----------------|
| Regression | 16 | **16/16 PASS** | All codecs, modes, encryption, edge cases, corruption detection |
| Multi-threaded | 14 | **14/14 PASS** | N=1/2/4/8 threads, large files, 1000 files, MT+encryption |
| Post-quantum | 10 | **10/10 PASS** | Keygen, PQ encrypt/decrypt, wrong key, password compat, PQ+MT, 2MB |
| Quick smoke | 9 | **9/9 PASS** | Normal, solid, encrypted, wrong pw, MT, fast, store, PQ, integrity |
| NIST vectors | 13 | **13/13 PASS** | See table above |
| **Total** | **62** | **62/62 PASS** | |
Reproduction: `make test-all`
---
## 3. Memory Safety
| Tool | Command | Result |
|------|---------|--------|
| AddressSanitizer | `make test-asan` | **Zero errors** |
| UndefinedBehaviorSanitizer | Built with `-fsanitize=address,undefined` | **Zero errors** |
| All code paths tested | Normal + solid + encrypted + PQ + MT | **Clean** |
Reproduction:
```bash
make test-asan
./zupt_asan compress /tmp/t.zupt /path/to/data/
./zupt_asan extract -o /tmp/out/ /tmp/t.zupt
./zupt_asan keygen -o /tmp/k.key
./zupt_asan compress --pq /tmp/pub.key /tmp/pq.zupt /path/to/data/
./zupt_asan extract --pq /tmp/k.key -o /tmp/pqout/ /tmp/pq.zupt
```
---
## 4. Compiler Warning Audit
| Compiler | Flags | Warnings |
|----------|-------|----------|
| GCC 13.x | `-Wall -Wextra -Wpedantic -O2 -std=c11` | **Zero** |
| Clang 18.x | `-Wall -Wextra -Wpedantic -O2 -std=c11` | **Zero** |
---
## 5. Constant-Time Analysis
| Function | Location | CT Method | Jasmin Verified? | Risk Level |
|----------|----------|-----------|-----------------|------------|
| HMAC comparison | `zupt_crypto.c:252` | 4×u64 XOR accumulation | **Yes**`zupt_mac_verify_ct` linked | **None** (Jasmin proven) |
| ML-KEM FO select | `zupt_mlkem.c:593` | 4×u64 masked select | **Yes**`zupt_ct_select_32` linked | **None** (Jasmin proven) |
| ML-KEM NTT butterfly | `zupt_mlkem.c` | Montgomery reduction (branchless) | No | Low |
| ML-KEM CBD sampling | `zupt_mlkem.c` | Bitwise operations only | No | Low |
| X25519 fe_cswap | `zupt_x25519.c:95` | Masked XOR swap | No (limb mismatch) | Low (C is branchless) |
| X25519 Montgomery ladder | `zupt_x25519.c:243` | Fixed 255 iterations | No | Low |
| AES-256 encrypt | `zupt_aes256.c:59` | **Table-based S-box** | **No** | **HIGH on shared HW** |
| SHA-256 | `zupt_sha256.c` | Table-based constants | No | Low (not secret-indexed) |
| Keccak-f[1600] | `zupt_keccak.c` | Bitwise XOR/ROT only | No | None |
| Key wipe | `zupt_crypto.c` | `explicit_bzero` / volatile | No | Low |
### Jasmin Assembly Verification
Two functions confirmed active in binary via `nm`:
```
0000000000014ae0 T zupt_mac_verify_ct ← Jasmin assembly, CT proven
0000000000014b20 T zupt_ct_select_32 ← Jasmin assembly, CT proven
```
Assembly generated by `jasminc 2026.03.0`. Constant-time enforced by Jasmin type system: secret-typed variables cannot flow into branch conditions or memory indices.
### Not Wired (with reason)
| Function | Issue | Fallback |
|----------|-------|----------|
| `zupt_fe_cswap` | Jasmin: 4×u64 limbs, C: 5×u51 — incompatible | C masked XOR (branchless) |
| `zupt_aes256_blk` | Stack offset bug: `rk.[1]``[rsp+1]` not `[rsp+16]` | C table-based AES |
---
## 6. Key Material Lifecycle
| Phase | Method | Verified |
|-------|--------|----------|
| Generation | OS CSPRNG: `getrandom(2)` / `/dev/urandom` / `RtlGenRandom` | Hard fail if unavailable |
| Storage | Stack-local arrays (no heap allocation for keys) | ASAN verified |
| Usage | Passed by const pointer to AES-CTR / HMAC | No copies to heap |
| Wipe | `zupt_secure_wipe()`: `explicit_bzero` (glibc 2.25+), `SecureZeroMemory` (Win), volatile fallback | Compiler cannot optimize out |
| Scope exit | Stack frame destroyed | Keys were on stack |
All intermediate buffers in PBKDF2, hybrid KEM, ML-KEM encaps/decaps, and X25519 wiped before return.
---
## 7. Nonce Security
**Scheme:** `per_block_nonce = base_nonce XOR pad_le(block_seq, 8)`
- `base_nonce`: 128-bit random from CSPRNG, generated once per archive.
- `block_seq`: monotonically increasing 0, 1, 2, ... per archive.
- **Uniqueness within archive:** Guaranteed (distinct seq → distinct nonce).
- **Uniqueness across archives:** 2^-128 collision probability per pair (birthday bound on random base).
---
## 8. Encrypt-then-MAC Ordering
| Step | Action | Verified |
|------|--------|----------|
| 1 | Compute HMAC over `nonce ‖ ciphertext` | HMAC input is nonce+ct, not plaintext |
| 2 | Verify HMAC before any decryption | Code path: MAC check → early return if fail → decrypt only on success |
| 3 | Decrypt only authenticated data | No plaintext produced from unauthenticated ciphertext |
**Prevents:** Chosen-ciphertext attacks, padding oracles, ciphertext tampering.
---
## 9. Bugs Found and Fixed (v0.5.1 → v1.5.0)
| Bug | Severity | Version Fixed | Impact |
|-----|----------|---------------|--------|
| Huffman Kraft-inequality violation | Critical | v0.5.1 | Data corruption on specific inputs |
| Heap-buffer-overflow in LZ match finder | Critical | v0.5.1 | Potential code execution |
| `rand()` CSPRNG fallback | Critical | v0.5.1 | Predictable encryption keys |
| ML-KEM `poly_basemul` OOB | Critical | v1.0.0 | Buffer overread in NTT |
| ML-KEM missing `poly_tomont` | Critical | v1.0.0 | Public key in wrong domain |
| ML-KEM inverted FO `cmov` | Critical | v1.0.0 | Always selected rejection key |
| ML-KEM `inv_ntt` wrong table | High | v1.0.0 | NTT roundtrip failure |
| PQ nonce mismatch | High | v1.0.0 | Encrypt/decrypt used different nonces |
| X25519 `AA + a24*E` formula | High | v1.1.0 | Wrong curve, not interoperable |
| Dead `match_cost()` | Low | v1.1.0 | Clang warning |
| `const polyvec` qualifier | Low | v1.1.0 | Pedantic warnings |
| `__int128` pedantic | Low | v1.1.0 | Pedantic warning |
---
## 10. Known Limitations
| Limitation | Impact | Mitigation | Status |
|------------|--------|------------|--------|
| Table-based AES (C fallback) | Cache-timing on shared hardware | Jasmin AES-NI path exists but has offset bug | **Open** — fix `.jazz` source |
| Table-based SHA-256 | Theoretical cache-timing | Not used on secret-indexed data | **Accepted** |
| PBKDF2 not quantum-safe | Quantum password brute-force | Use `--pq` mode | **Documented** |
| No `mlock()` | Keys swappable to disk | Short key lifetime + `zupt_secure_wipe` | **Planned** |
| No fuzzing performed | Undiscovered bugs | AFL++ setup in FUZZING.md | **Planned** |
| No independent audit | Self-assessed only | Open source + Jasmin proofs | **Planned** |
| X25519 Jasmin not linked | C fallback for fe_cswap | C is branchless but compiler-dependent | **Open** — limb mismatch |
---
© 2026 Cristian Cezar Moisés — MIT License