ChaCha20 SIMD kernels in Eä — encrypt, fused stats, and searchable cipher
Project description
eachacha
ChaCha20 (RFC 7539) SIMD kernels in the Eä language. Encrypt at 1.78 GB/s. Search encrypted data without decrypting to disk.
pip install eachacha
Quick Start
from eachacha import encrypt, search
key = bytes(range(32)) # 32-byte key
nonce = bytes(12) # 12-byte nonce
# Encrypt
ct = encrypt(b"INFO ok\nERROR disk full\nINFO done\n", key, nonce)
# Search encrypted data — plaintext never touches disk
result = search(ct, b"ERROR", key, nonce)
print(result.offsets) # [8]
# Multi-needle search with context lines (auto-selects v2 kernel)
result = search(ct, [b"ERROR", b"INFO"], key, nonce)
for i, line in enumerate(result.lines):
print(f"[{result.needle_ids[i]}] {line}")
The Searchable Cipher
Standard process for searching encrypted logs:
Read file → Decrypt to /tmp (vulnerability!) → Read /tmp → Search → Delete /tmp
The Eä process:
Read encrypted file → Decrypt in buffer → Search in buffer → Report match → Zero buffer
Plaintext never exists as a full-file allocation. Only a 4 KB window lives in memory at a time, zeroed after each iteration.
CLI
# Single needle
eachacha-grep "ERROR" encrypted.bin --key <hex> --nonce <hex>
# Multi-needle with context lines
eachacha-grep "ERROR" "FATAL" "PANIC" encrypted.bin --key <hex> --nonce <hex>
Benchmarks
AMD EPYC 9354P (2 vCPUs), 64 MB:
Single-needle search (v1):
| Implementation | GB/s |
|---|---|
| Ea fused decrypt+search | 1.28 |
| Ea decrypt → C memmem (two-pass) | 0.96 |
| C memmem on plaintext | 2.22 |
Multi-needle search (v2, 3 needles + context lines):
| Implementation | GB/s |
|---|---|
| Ea v2 multi-needle (1 pass) | 0.52 |
| Ea v1 single-needle x3 (3 passes) | 0.41 |
| C memmem x3 on plaintext | 0.78 |
- v1 fused vs two-pass: 1.34x faster
- v2 multi-needle vs v1 x3: 1.28x faster (one decrypt instead of three)
Security Model
| Property | Guarantee |
|---|---|
| Full-file plaintext buffer | Never created — 4 KB window at a time (v2) |
| Working buffer | Zeroed after each iteration |
| Plaintext on disk | Never written |
| Kernel output | Match offsets + extracted lines only |
| Keystream buffer | Zeroed on exit |
Not provided: This is ChaCha20 only — no authentication (no Poly1305/AEAD). An attacker can flip ciphertext bits to deterministically flip plaintext bits. For integrity guarantees, use ChaCha20-Poly1305 for encryption and verify the tag before searching. This tool assumes the ciphertext is authentic.
Not constant-time: The search comparison uses early-exit on mismatch. This leaks timing information correlated with partial matches. Since the tool's purpose is to reveal match positions, this is acceptable — but do not use the search kernel as a general-purpose constant-time comparison.
How It Works
v1 kernel (chacha20_search.ea, 583 lines): Decrypts 256 bytes at a time, searches with .== + movemask SIMD first-byte filter (same algorithm as glibc memmem: vpcmpeqb + vpmovmskb), handles cross-block boundaries via overlap buffer.
v2 kernel (chacha20_search_v2.ea, 750 lines): Decrypts into a 4 KB window, searches for multiple needles by OR:ing .== + movemask bitmasks per unique first-byte, extracts matched log lines by finding \n boundaries with the same SIMD primitives.
Encrypt + Statistics
The fused kernel encrypts data and computes sum/count/min/max in a single pass:
| Implementation | GB/s |
|---|---|
| Generic C (-O3, no SIMD) | 0.54 |
| OpenSSL ChaCha20 (Python wrapper) | 0.59 |
| Ea ChaCha20 (single core) | 1.78 |
| Ea fused (encrypt + stats) | 1.43 |
| Ea encrypt + NumPy stats (two passes) | 1.08 |
Fusion adds ~20% overhead vs encrypt-only. The separate approach pays for a second memory traversal — fusion eliminates it.
Complexity
| Kernel | Lines | Throughput |
|---|---|---|
chacha20.ea (encrypt) |
272 | 1.78 GB/s |
chacha20_fused.ea (encrypt+stats) |
376 | 1.43 GB/s |
chacha20_search.ea (v1 search) |
583 | 1.28 GB/s |
chacha20_search_v2.ea (v2 multi-needle) |
750 | 0.52 GB/s |
| Total | 1,981 |
~2,000 lines of Eä produce four kernels. For comparison, OpenSSL's ChaCha20 alone is ~100,000+ lines of C/ASM.
Build from Source
Requires ea-compiler (pip install ea-compiler) and a C compiler.
./build.sh
python3 test_vectors.py && python3 test_fused.py && python3 test_search.py && python3 test_search_v2.py
Files
| File | Purpose |
|---|---|
chacha20.ea |
ChaCha20 encrypt kernel (4-block ILP) |
chacha20_fused.ea |
Fused encrypt + statistics kernel |
chacha20_search.ea |
v1: single-needle fused decrypt+search |
chacha20_search_v2.ea |
v2: multi-needle + context-line extraction |
eachacha_grep.py |
CLI for searching encrypted files |
test_vectors.py |
RFC 7539 test vectors + OpenSSL cross-check (4 tests, 9 assertions) |
test_fused.py |
Fused encrypt+stats tests (3 tests, 8 assertions) |
test_search.py |
v1 search tests (17 tests, 21 assertions) |
test_search_v2.py |
v2 search tests (27 tests, 43 assertions) |
bench.py |
Encrypt benchmark suite |
bench_search.py |
v1 search benchmark suite |
bench_search_v2.py |
v2 multi-needle benchmark suite |
autoresearch/ |
Automated kernel optimization loop |
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file eachacha-1.0.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: eachacha-1.0.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 26.2 kB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
219691700b7f98aac0629e91b7e9fbc977c335f6076801c2db75eb05ebed8dc7
|
|
| MD5 |
964b94b5528c9b3d8c1c2d758952ccb3
|
|
| BLAKE2b-256 |
f2a715ebf8b4205db375767e4199eb638a8d0c505f40ba997a9be1498fcbe302
|
Provenance
The following attestation bundles were made for eachacha-1.0.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
publish.yml on petlukk/eachacha
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
eachacha-1.0.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
219691700b7f98aac0629e91b7e9fbc977c335f6076801c2db75eb05ebed8dc7 - Sigstore transparency entry: 1147237093
- Sigstore integration time:
-
Permalink:
petlukk/eachacha@ce50a2e6a75d3292dd0a4c37603a265ac169aa00 -
Branch / Tag:
refs/tags/v1.0.4 - Owner: https://github.com/petlukk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ce50a2e6a75d3292dd0a4c37603a265ac169aa00 -
Trigger Event:
push
-
Statement type: