Cagoule v2.4.0
CAGOULE v2.4.0 — Pipeline & Bulk Release
Released: May 20, 2026
CAGOULE v2.4.0 focuses on pipeline parallelism in the C algebraic layer and batch encryption in the Python API. After fully vectorising all algebraic components (v2.2.0–v2.3.0), the remaining bottlenecks were instruction-level parallelism in the CBC cipher and Argon2id overhead in multi-message workloads.
What's New
P1a — Encrypt Unroll4 + Prefetch
Loop unrolling ×4 with L2/L3 cache prefetch on the encrypt path. CBC's sequential dependency prevents true pipelining, but unrolling reduces loop overhead and prefetch hides memory latency. ~+25% C-layer encrypt throughput.
P1b — Decrypt Pipeline4
True 4-way parallel decryption exploiting CBC's natural independence: RK-sub, SBox⁻¹, and MatInv for block N depend only on cipher[N], enabling simultaneous execution across 4 blocks via CPU out-of-order execution. ~×2 C-layer decrypt throughput.
P2 — encrypt_bulk / decrypt_bulk
Encrypt N messages with a single Argon2id key derivation. Parameters are derived once and reused across all messages, dropping the GIL-holding serial fraction from ~46% to ~10%. ProcessPool scaling delivers near-linear speedup through 8 workers.
P4 — Thread-Local Buffer Pool
Reusable ctypes buffers (padded_buf, out_buf, rk_arr, input_buf) allocated once per thread and grown on demand. Eliminates per-call allocation overhead. +71% single-core throughput in parallel workloads.
GIL Release on Heavy C Calls
cagoule_cbc_encrypt, cagoule_cbc_decrypt, cagoule_matrix_mul, cagoule_sbox_block_forward, and their inverses now release the Python GIL during execution via ctypes.release_gil = True.
Bug Fixes
- Decrypt residual loop (
n_blocks % 4 != 0): fixedsaved_r[]CBC ciphertext tracking — all 9 residual edge cases now pass _binding.pystruct size: removed oversized_pad/AVX2 fields fromCagouleMatrixC— was 8 bytes too large, causing memory corruptionmake installstale.so: addedrm -fbeforecpto prevent silent overwrite failures
📊 Performance
C Layer (65,536 blocks ≡ 1 MB)
| Metric | v2.3.0 | v2.4.0 |
|---|---|---|
| C encrypt | 9.7 MB/s | 8.0 MB/s |
| C decrypt | — | 8.1 MB/s |
| Ratio dec/enc | — | 0.99× |
| S-box AVX2 | 120 MB/s | 70.1 MB/s |
Python API
| Test | v2.4.0 |
|---|---|
| encrypt-1KB | 5.8 MB/s |
| encrypt-1MB | 5.2 MB/s |
| encrypt-10MB | 5.1 MB/s |
| decrypt-1MB | 4.6 MB/s |
| decrypt-10MB | 8.5 MB/s |
Parallel Scaling (encrypt_bulk + ProcessPoolExecutor)
| Workers | Throughput | Speedup | Efficiency |
|---|---|---|---|
| 1 | 4.8 MB/s | 1.00× | — |
| 2 | 10.0 MB/s | 2.08× | 104% |
| 4 | 18.5 MB/s | 3.88× | 97% |
| 8 | 29.6 MB/s | 6.19× | 77% |
| 20 | 39.9 MB/s | 8.34× | 42% |
Streaming (64 KB chunks)
| Size | CAGOULE | AES-256-GCM | ChaCha20 |
|---|---|---|---|
| 50 MB | 5.6 MB/s | 456 MB/s | 402 MB/s |
| 100 MB | 5.7 MB/s | 459 MB/s | 401 MB/s |
| 500 MB | 5.7 MB/s | 458 MB/s | 402 MB/s |
Test Suite
| Suite | Result |
|---|---|
| C tests (9 binaries) | 43,686 assertions, 0 failures |
| Python tests | 578 passed, 0 failed, 18 skipped |
| Valgrind (7 binaries) | 0 memory leaks |
Installation
git clone https://github.com/slimissa/cagoule.git
cd cagoule/cagoule/c
make clean && make && make install
pip install -e ".[dev]"from cagoule import encrypt, decrypt, encrypt_bulk, decrypt_bulk
# Single message
ct = encrypt(b"secret", b"password")
pt = decrypt(ct, b"password")
# Bulk encryption (v2.4.0)
messages = [b"alpha", b"beta", b"gamma"]
cts = encrypt_bulk(messages, b"password")
pts = decrypt_bulk(cts, b"password")🔮 Preview: v2.5.0 — Mersenne Acceleration
The performance diagnostic from v2.4.0 identified the Barrett modular multiplication (mulmod64x4) as the dominant bottleneck, consuming 80% of algebraic layer time with a depth-16 dependency chain that limits CPU out-of-order execution.
v2.5.0 will introduce four complementary optimizations:
| Feature | Description |
|---|---|
| Mersenne-64 Prime Pool | 8 primes of form p = 2⁶⁴ – k (k < 2¹⁰), selected via HKDF. Replaces pseudo-random prime with a structure-exploitable one without security reduction. |
mulmod_mersenne64x4 |
AVX2 modular reduction with no DIV instruction, no branching (constant-time). Reduces mulmod cost from ~22 to ~13 instructions. |
| Dual Accumulator | Two independent accumulators per row group in cagoule_matrix_avx2.c. Now feasible without register spill thanks to the register budget freed by Mersenne reduction. |
| Z-Domain Shifting | Key whitening via modular addmod/submod before the matrix and after the S-box. Defense-in-depth against pre-computation attacks on a fixed field. |
Changelog
v2.4.0 — 2026-05-17
C Backend
- New: Pipeline4 decrypt (
_cbc_decrypt_pipeline4_avx2) - New: Encrypt unroll4 + prefetch (
_cbc_encrypt_pipeline4_avx2) - Fixed: Decrypt residual loop —
saved_r[]tracking - New:
test_cipher_pipeline4.c(88 assertions)
Python Layer
- New:
encrypt_bulk()/decrypt_bulk() - New: Thread-local buffer pool (
_buffer_pool.py) - New: GIL release on heavy C calls
- Fixed:
_binding.pystruct size - Fixed:
make installstale.sooverwrite - Fixed: 3 compiler warnings
v2.3.0 — 2026-05-08
S-box AVX2 vectorisation; Mersenne-like reduction; cycle-walking AVX2.
v2.2.0 — 2026-05-06
AVX2 Vandermonde matrix multiply (+67% algebraic layer).
v2.1.0 — 2026-05-01
C port of omega.c; security fix for wrong-password detection.
Author: Slim Issa — Kairouan, Tunisia
Repository: github.com/slimissa/cagoule