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:
parent
cea317653e
commit
06c877ec86
43 changed files with 1913 additions and 546 deletions
214
AUDIT.md
214
AUDIT.md
|
|
@ -1,60 +1,178 @@
|
|||
# Security Audit — Zupt v1.0.0
|
||||
# Security Audit — Zupt v1.5.0
|
||||
|
||||
## Cryptographic Correctness
|
||||
**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
|
||||
|
||||
| Check | Status | Evidence |
|
||||
---
|
||||
|
||||
## 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 |
|
||||
|-------|--------|----------|
|
||||
| ML-KEM-768 keygen+encaps+decaps roundtrip | ✅ | 10/10 trials pass (`test_pq.sh`) |
|
||||
| ML-KEM-768 constant-time basemul | ✅ | No secret-dependent branches; Montgomery reduction is branchless |
|
||||
| ML-KEM-768 FO implicit rejection | ✅ | cmov selects rejection key on invalid ct; both paths always execute |
|
||||
| X25519 Montgomery ladder | ✅ | Constant-time by construction (cswap on every iteration) |
|
||||
| AES-256-CTR | ✅ | Verified against NIST SP 800-38A via regression tests |
|
||||
| HMAC-SHA256 | ✅ | Verified via password-mode archive integrity tests |
|
||||
| PBKDF2-SHA256 | ✅ | 600,000 iterations, 32-byte random salt per archive |
|
||||
| SHA-256 | ✅ | Used by HMAC/PBKDF2, verified transitively |
|
||||
| SHA3-256/512 | ✅ | Used by ML-KEM; Keccak-f[1600] per FIPS 202 |
|
||||
| SHAKE-128/256 | ✅ | Used by ML-KEM sampling; verified via KEM roundtrip |
|
||||
| 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 |
|
||||
|
||||
## Constant-Time Verification
|
||||
All intermediate buffers in PBKDF2, hybrid KEM, ML-KEM encaps/decaps, and X25519 wiped before return.
|
||||
|
||||
| Operation | Constant-Time | Method |
|
||||
|-----------|---------------|--------|
|
||||
| HMAC comparison | Yes | XOR accumulation (`diff \|= a[i] ^ b[i]`) |
|
||||
| ML-KEM decaps implicit rejection | Yes | cmov with branchless fail detection |
|
||||
| ML-KEM NTT/basemul | Yes | No secret-dependent branches; Barrett/Montgomery reduction branchless |
|
||||
| ML-KEM CBD sampling | Yes | Bitwise operations only |
|
||||
| X25519 ladder | Yes | fe_cswap with masked XOR on every bit |
|
||||
| AES-256 | **No** | Table-based (T-tables). Vulnerable to cache-timing on shared hardware. |
|
||||
| SHA-256 | **No** | Standard implementation. Not constant-time w.r.t. message length. |
|
||||
---
|
||||
|
||||
**Documented limitation:** AES-256 and SHA-256 use lookup tables susceptible to cache-timing side channels. Do not use on shared multi-tenant hardware where an attacker can measure cache access patterns.
|
||||
## 7. Nonce Security
|
||||
|
||||
## Memory Safety
|
||||
**Scheme:** `per_block_nonce = base_nonce XOR pad_le(block_seq, 8)`
|
||||
|
||||
| Check | Status |
|
||||
|-------|--------|
|
||||
| `make test-asan`: zero errors | ✅ All modes: normal, solid, encrypted, PQ, MT |
|
||||
| All `malloc()` return values checked | ✅ Propagated via `ZUPT_ERR_NOMEM` |
|
||||
| All ML-KEM polynomial buffers wiped | ✅ `zupt_secure_wipe()` in keygen/encaps/decaps |
|
||||
| All X25519 scalars wiped | ✅ `memset(e, 0, 32)` after ladder |
|
||||
| All intermediate key material wiped | ✅ In `zupt_crypto.c` hybrid encrypt/decrypt init |
|
||||
| Keyring copy wiped in parallel pool destructor | ✅ `zupt_secure_wipe(&ctx->keyring, ...)` |
|
||||
- `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).
|
||||
|
||||
## Format Stability
|
||||
---
|
||||
|
||||
| Check | Status |
|
||||
|-------|--------|
|
||||
| v1.0 reads v0.3+ archives | ✅ Regression test covers password-encrypted v0.5 format |
|
||||
| v0.6 rejects v1.4 PQ archives cleanly | ✅ Version check returns `ZUPT_ERR_BAD_VERSION` |
|
||||
| FORMAT.md documents all fields | ✅ See FORMAT.md |
|
||||
| FORMAT_STABLE flag set in v1.0 archives | ✅ Bit 4 of global_flags |
|
||||
## 8. Encrypt-then-MAC Ordering
|
||||
|
||||
## Known Bugs Fixed (v0.7.0)
|
||||
| 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 |
|
||||
|
||||
| Bug | Impact | Fix |
|
||||
|-----|--------|-----|
|
||||
| ML-KEM basemul OOB (`zetas[64+i]`, i up to 127) | Buffer overread → undefined behavior | Fixed to 64 iterations, 4-coeff groups |
|
||||
| ML-KEM missing `poly_tomont` in keygen | Public key in wrong domain → K-PKE roundtrip fails | Added `poly_tomont()` after basemul in keygen |
|
||||
| ML-KEM inverted cmov in FO decaps | Always selected rejection key → KEM roundtrip fails | Fixed fail detection: `(-(int64_t)diff) >> 63` |
|
||||
| ML-KEM `inv_ntt` used wrong zetas table | NTT/invNTT roundtrip failed | Uses same `zetas[]` table, k counts 127→0 |
|
||||
| PQ hybrid nonce mismatch | Encrypt/decrypt used different random nonces | Store base_nonce in enc_hdr; decrypt reads it back |
|
||||
**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
|
||||
|
|
|
|||
Loading…
Reference in a new issue