krypteia-arcana — Classical Cryptography for the krypteia workspace
Pure-Rust implementations of the classical cryptographic primitives
(hashes, symmetric ciphers, MACs, RSA, ECC, EdDSA, Montgomery DH),
sharing the side-channel countermeasure toolkit silentops with the
post-quantum crate quantica. The crate is the "classical" half of
the krypteia workspace.
Design rules
The crate inherits the krypteia workspace design rules:
- Pure Rust, zero external crates — only
core(andalloc);stdis optional behind a feature flag. The only workspace dependency issilentopsfor shared CT primitives and timing-leak verification. - Embedded-friendly — caller-provided buffers, no hidden heap
allocation in the hot path. Target devices: secure elements,
STM32 (Cortex-M0/M4/M33), RISC-V (ESP32-C3, …).
no_stdis on the roadmap. - Side-channel hardened — timing-constant comparisons, deterministic ECDSA nonces (RFC 6979), no secret-dependent branches on the CT-critical paths. AES S-box is table-based (known cache surface, documented below). ECC scalar multiplication uses a CT Montgomery ladder hardened against branch reintroduction by the optimizer.
- Validated — every algorithm is tested against pinned RFC / NIST / FIPS reference vectors, plus three external corpora (Wycheproof, NIST CAVP, NIST ACVP).
- C FFI-exposable — the companion crate
arcana_ffiexports ~20extern "C"functions with a C header atarcana_ffi/include/arcana.h.
Algorithms
Arcana exposes six primitive families, all in src/:
Hash functions
| Algorithm | Output | Module | Standard |
|---|---|---|---|
| SHA-1 | 160 b | hash::sha1::Sha1 (legacy) |
FIPS 180-4 |
| SHA-224 | 224 b | hash::sha224::Sha224 |
FIPS 180-4 |
| SHA-256 | 256 b | hash::sha256::Sha256 |
FIPS 180-4 |
| SHA-384 | 384 b | hash::sha384::Sha384 |
FIPS 180-4 |
| SHA-512 | 512 b | hash::sha512::Sha512 |
FIPS 180-4 |
| SHA-512/224 | 224 b | hash::sha512_trunc::Sha512_224 |
FIPS 180-4 |
| SHA-512/256 | 256 b | hash::sha512_trunc::Sha512_256 |
FIPS 180-4 |
| SHA3-224 | 224 b | hash::sha3::Sha3_224 |
FIPS 202 |
| SHA3-256 | 256 b | hash::sha3::Sha3_256 |
FIPS 202 |
| SHA3-384 | 384 b | hash::sha3::Sha3_384 |
FIPS 202 |
| SHA3-512 | 512 b | hash::sha3::Sha3_512 |
FIPS 202 |
| SHAKE128 | XOF | hash::sha3::Shake128 |
FIPS 202 |
| SHAKE256 | XOF | hash::sha3::Shake256 |
FIPS 202 |
| cSHAKE128 | XOF | hash::sha3::CShake128 |
NIST SP 800-185 |
| cSHAKE256 | XOF | hash::sha3::CShake256 |
NIST SP 800-185 |
| BLAKE2b | 1-512 b | hash::blake2::Blake2b |
RFC 7693 |
| BLAKE2s | 1-256 b | hash::blake2::Blake2s |
RFC 7693 |
| RIPEMD-160 | 160 b | hash::ripemd160::Ripemd160 (legacy) |
ISO/IEC 10118-3 |
Symmetric ciphers and modes
| Algorithm | Module | Standard |
|---|---|---|
| AES-128 / 192 / 256 | cipher::aes |
FIPS 197 |
| DES, Triple-DES (EDE) | cipher::des |
FIPS 46-3 |
| ECB / CBC / CTR / GCM | cipher::modes |
NIST SP 800-38A/D |
| AES-CCM AEAD | cipher::ccm |
NIST SP 800-38C, RFC 3610 |
| AES-XTS disk encryption | cipher::xts |
IEEE 1619 |
| ChaCha20 stream cipher | cipher::chacha20 |
RFC 8439 |
| Poly1305 one-time MAC | cipher::poly1305 |
RFC 8439 |
| ChaCha20-Poly1305 AEAD | cipher::chacha20poly1305 |
RFC 8439 |
| XChaCha20-Poly1305 AEAD (24B nonce) | cipher::xchacha20poly1305 |
draft-irtf-cfrg-xchacha |
| Streaming Cipher ctx | cipher::ctx |
— |
The streaming Cipher ctx wraps AES / DES / 3DES with ECB / CBC / CTR
behind init / update / finalize, with 5 padding schemes: None,
Pkcs7, Iso9797M1 (zero), Iso9797M2 (ISO 7816-4), AnsiX923.
AEAD modes stay function-oriented (no unverified plaintext release).
Message authentication codes (MACs)
| Family | Algorithms | Standard |
|---|---|---|
| HMAC | SHA-1, SHA-256/384/512, SHA3-256/384/512, RIPEMD-160 | RFC 2104, FIPS 198-1 |
| CMAC | AES-128/192/256, Triple-DES | NIST SP 800-38B, RFC 4493 |
| KMAC | KMAC128, KMAC256 | NIST SP 800-185 |
| GMAC | AES-128/192/256 | NIST SP 800-38D |
Three init variants: init(key), init_kmac(key, custom),
init_with_nonce(key, nonce). verify accepts truncated tags,
constant-time comparison. Poly1305 excluded (one-time MAC, unsafe to
reuse via an "init then update again" object).
RSA
| Scheme | Module | Standard |
|---|---|---|
| PKCS#1 v1.5 enc+sig | rsa::pkcs1 |
RFC 8017 §7-8 |
| OAEP encryption | rsa::oaep |
RFC 8017 §7.1 |
| RSASSA-PSS signature | rsa::pss |
RFC 8017 §8.1 |
Signatures support 8 hash functions: SHA-1, SHA-256/384/512, SHA3-256/384/512, RIPEMD-160.
Elliptic curve cryptography
Seven short-Weierstrass curves behind a unified Curve trait
(keygen, ECDSA RFC 6979, ECDSA random-nonce, ECDH, SEC1
compression / decompression, DER signatures):
| Wrapper | Curve | Standard |
|---|---|---|
P256 |
NIST P-256 | FIPS 186-5 |
P384 |
NIST P-384 | FIPS 186-5 |
P521 |
NIST P-521 | FIPS 186-5 |
Secp256k1 |
secp256k1 | SEC 2 §2.4.1 |
BrainpoolP256r1 |
Brainpool 256-bit | RFC 5639 |
BrainpoolP384r1 |
Brainpool 384-bit | RFC 5639 |
BrainpoolP512r1 |
Brainpool 512-bit | RFC 5639 |
The Curve trait + per-curve unit structs live in
ecc::curves; the LIMBS-generic ECDSA / ECDH internals live in
ecc::ecdsa (signing helpers, DER, RFC 6979) and ecc::curve
(Jacobian point ops + curve params).
Edwards / Montgomery curves
| Algorithm | Module | Standard |
|---|---|---|
| Ed25519 (pure) | ecc::eddsa |
RFC 8032 §5.1 |
| Ed25519ctx | ecc::eddsa |
RFC 8032 §5.1.6 |
| Ed25519ph | ecc::eddsa |
RFC 8032 §5.1.7 |
| X25519 ECDH | ecc::x25519 |
RFC 7748 |
| X448 ECDH | ecc::x448 |
RFC 7748 |
Ed448 is planned but not yet implemented (RFC 8032 Appendix A port pending).
Cargo features
[]
= { = "../arcana" } # default = no features
| Feature | Default | Effect |
|---|---|---|
std |
☐ | Reserved for future no_std work (currently a no-op; the crate already only uses core + alloc). |
rust-crypto-traits |
☐ | Pulls in digest 0.10 / cipher 0.4 / signature 2.0. Activates the bridge module which wraps every hash in a digest::Digest impl for ecosystem interop (HMAC, HKDF, PBKDF2, Argon2, …). |
Default builds are zero-dependency (only the workspace-local
silentops crate). The rust-crypto-traits feature is opt-in for
callers who need to plug arcana hashes into the RustCrypto ecosystem.
Quick start
Hashing (SHA-256)
use Sha256;
use Hasher;
let digest = hash;
assert_eq!;
AEAD (AES-128-GCM)
use Aes128;
use Gcm;
use BlockCipher;
let cipher = new;
let nonce = ;
let = encrypt;
let pt = decrypt.unwrap;
assert_eq!;
X25519 ECDH
use ;
let alice_sk = ;
let bob_sk = ;
let alice_pk = x25519_derive_public;
let bob_pk = x25519_derive_public;
let shared_a = x25519_ecdh;
let shared_b = x25519_ecdh;
assert_eq!;
HMAC-SHA-256 (streaming)
use ;
let mut m = new;
m.init.unwrap;
m.update.unwrap;
m.update.unwrap;
let tag = m.sign_to_vec.unwrap;
assert_eq!;
AES-256-CBC (Cipher ctx)
use ;
let mut c = new.unwrap;
c.init.unwrap;
let mut out = vec!;
let mut n = c.update.unwrap;
n += c.finalize.unwrap;
out.truncate;
// out now contains the padded AES-256-CBC ciphertext.
Typed key wrappers (Zeroize-on-Drop)
Arcana exposes typed key types for RSA, EdDSA, and the seven
short-Weierstrass curves, but none of them currently implement
Drop with silentops::ct_zeroize. Callers must zeroize sensitive
buffers explicitly until the wrappers grow Zeroize-on-Drop — this
gap is tracked under Known limitations → Side-channel.
| Module | Public | Secret (currently NOT zeroized on drop) |
|---|---|---|
ecc::curves |
PublicKey |
SecretKey |
ecc::eddsa |
Ed25519PublicKey |
Ed25519SecretKey |
rsa::rsa |
RsaPublicKey |
RsaSecretKey (n, d, p, q, dp, dq, qinv as BigInt) |
The X25519 / X448 APIs operate on raw [u8; 32] / [u8; 56] byte
arrays today; a typed wrapper layer is a candidate refresh once the
ECC SecretKey wrapper grows Drop.
silentops::ct_zeroize is available to callers as the canonical
volatile-write zeroizer (it relies on core::ptr::write_volatile +
a compiler fence so the compiler cannot elide the writes); apply it
to bytes fields of secret-key types before they go out of scope on
the caller side.
Parameter sets / curve families
NIST P-curves
| Curve | Field prime | Order bit length | felem (B) | sec. level |
|---|---|---|---|---|
| P-256 | 2^256 − 2^224 + 2^192 + 2^96 − 1 |
256 | 32 | 128 |
| P-384 | 2^384 − 2^128 − 2^96 + 2^32 − 1 |
384 | 48 | 192 |
| P-521 | 2^521 − 1 (Mersenne) |
521 | 66 | 256 |
P-521 is the only curve where the SEC1 octet width (66 B) does not
match the internal storage width (LIMBS * 8 = 72 B). The
serialization layer strips / left-pads the 6 leading zero bytes at
the boundary.
Brainpool
| Curve | Field prime size | Order bit length | felem (B) | Source |
|---|---|---|---|---|
| brainpoolP256r1 | 256 bits | 256 | 32 | RFC 5639 |
| brainpoolP384r1 | 384 bits | 384 | 48 | RFC 5639 |
| brainpoolP512r1 | 512 bits | 512 | 64 | RFC 5639 |
secp256k1
| Curve | Field prime | Order bit length | felem (B) | Source |
|---|---|---|---|---|
| secp256k1 | 2^256 − 2^32 − 977 |
256 | 32 | SEC 2 §2.4.1 |
y^2 = x^3 + 7 (a = 0, b = 7). The Bitcoin / Ethereum signing curve.
Edwards / Montgomery
| Family | Curve | Wire size (B) | Source |
|---|---|---|---|
| Edwards | Ed25519 | 32 (pk), 32 (sk), 64 (sig) | RFC 8032 §5.1 |
| Edwards | Ed448 | (planned) | RFC 8032 §5.2 |
| Montgomery | X25519 | 32 (pk = sk = ss) | RFC 7748 |
| Montgomery | X448 | 56 (pk = sk = ss) | RFC 7748 |
RSA key sizes
The rsa::rsa::rsa_keygen constructor accepts arbitrary bit lengths
(within the bounds of the BigInt arithmetic). Tested values:
1024, 2048, 3072, 4096. Keys above 4096 bits work but key
generation gets slow (BigInt multi-precision is unoptimized). For
typical TLS/CMS usage 2048 or 3072 are the right defaults; 4096 is
documented but expensive.
Design decisions
-
Function-oriented API + streaming objects — one-shot functions (
Sha256::hash,Gcm::encrypt,P256::sign_rfc6979) for the common case;CipherandMacstreaming objects for callers that feed data in chunks or need caller-provided buffers without heap. -
Hash function is always explicit — ECDSA, RSA-PSS, RSA-PKCS1 all take the hash as a type or enum parameter. No hidden default.
-
AEAD stays function-oriented — GCM, CCM, ChaCha20-Poly1305, XChaCha20-Poly1305 are not routed through the streaming
Cipherto avoid releasing unverified plaintext during streaming decryption. -
Curvetrait unifies ECDSA + ECDH + SEC1 — all 7 short- Weierstrass curves are unit structs implementing one trait, so the same code works for P-256 and BrainpoolP512r1 with just a type change. -
Three MAC init variants —
init(key)for HMAC/CMAC/KMAC,init_kmac(key, S)for KMAC with customization,init_with_noncefor GMAC. Wrong variant → compile-time or runtime error. -
Poly1305 excluded from Mac ctx — it is a one-time MAC; reusing the key breaks it. Keeping it function-oriented prevents misuse.
-
CT scalar mul split —
scalar_mul_pointcalls the ladder-onlypoint_add_ct(no branches on point coords); the variablepoint_add(withH==0short-circuits) is reserved fordouble_scalar_mul, used by ECDSA verify on public values.
Side-channel countermeasures (summary)
Always-on
These defences are active in every build, regardless of feature flags:
| Defence | Scope |
|---|---|
| Constant-time tag comparison | All AEAD decrypt, MAC verify, ECDSA verify |
| Constant-time field arithmetic | ECC (P-256, P-384, P-521, secp256k1, Brainpool), Ed25519, X25519, X448 |
| CT Montgomery ladder | ecc::curve::scalar_mul_point — branch-free across all 7 short-Weierstrass curves |
core::hint::black_box shielding |
field_add / field_sub / reduce_wide masks, to keep LLVM from recovering branches |
| No secret-dependent branches | Hash, symmetric, HMAC, CMAC, KMAC, GMAC |
| RFC 6979 deterministic nonce | ECDSA — eliminates nonce-reuse attacks |
silentops::ct_zeroize available |
Caller can zeroize buffers holding secrets explicitly |
Feature-gated
Arcana does not ship a feature-gated SCA layer today. Algorithm choice is the only knob: prefer the curve / cipher with the strongest intrinsic CT properties for your threat model (e.g. Curve25519 over NIST P-curves on cache-shared targets, ChaCha20-Poly1305 over AES-GCM on cores without AES-NI).
A sca-protected feature mirroring the quantica side (masking +
shuffled NTT) is on the roadmap for symmetric primitives whose state
is amenable; classical curves are protected algorithmically (CT
ladder) rather than by masking.
Timing leakage verification (dudect)
The shared silentops::verify module implements the dudect
methodology of Reparaz, Balasch & Verbauwhede (2017). Arcana does
not yet ship a pre-built dudect harness of its own; instructions for
hooking new test scenarios into the workspace harness live in
silentops/examples/ct_verify_pqc.rs (the post-quantum harness) —
mirror that file with arcana primitives when running a CT campaign.
The eventual third-party evaluation pass will require dudect runs on:
scalar_mul_point(the Montgomery ladder) — already audited at release-asm level: 0 secret-dependent branches in the loop body.field_inv/scalar_inv(Fermat's little theorem ladder).- RSA CRT decrypt path (
rsa::rsa::rsa_decrypt_raw). - AEAD decrypt tag compare (constant-time via
silentops::ct_eq).
A t-statistic with |t| < 4.5 after ~10⁶ samples is considered
passing (p < 10⁻⁵).
Known residual surface
The following attack surfaces are not defended against and are documented here so the reader knows what they are deploying:
| Primitive | Issue | Mitigation path |
|---|---|---|
| AES S-box | Table-based lookup — cache-line leak | Bitsliced / AES-NI backend (not yet shipped) |
| DES / 3DES S-boxes | Table-based | Legacy only; avoid on SCA-sensitive targets |
| RSA CRT decrypt | BigInt ops not formally CT-audited |
Audit + dudect run scheduled |
| Heap allocations | Secret-key buffers come from alloc |
Caller-provided fixed buffers (planned refactor) |
| Zeroize-on-Drop | Typed key wrappers do not yet Drop |
Wrap secret types with silentops::ct_zeroize |
| DPA / EM / fault | Out of scope for software library | See workspace-level SCA design docs |
| Compiler CMOV | Bit-mask CT depends on black_box |
silentops asm backends on x86_64 / aarch64 |
Per-algorithm deep dives
The summary above lists which countermeasures are active; the full
per-algorithm SCA analyses — threat matrices, attack references, code
pointers, residual risks — live under
arcana/doc/sca/countermeasures/ in the repository. The Sphinx
documentation pack (./gendoc.sh arcana) inlines them as a
navigable cross-linked tree below.
Performance
Arcana does not yet ship a dedicated benchmarking crate (no
arcana_bench companion exists at workspace level). Representative
single-threaded numbers obtained from cargo test --release timings
on x86_64 desktop hardware:
| Algorithm | KeyGen / setup | Sign / Encrypt | Verify / Decrypt |
|---|---|---|---|
| AES-128-GCM (1 KiB) | — | < 0.01 ms | < 0.01 ms |
| ChaCha20-Poly1305 | — | < 0.01 ms | < 0.01 ms |
| SHA-256 (1 KiB) | — | < 0.01 ms | — |
| RSA-2048 (PKCS#1) | ~150 ms | ~6 ms | < 0.5 ms |
| ECDSA P-256 | ~1 ms | ~3 ms | ~3 ms |
| Ed25519 | < 0.1 ms | < 0.1 ms | < 0.5 ms |
| X25519 | — | < 0.1 ms | < 0.1 ms |
Numbers are coarse and should not be cited; they vary widely with
hardware and feature flags. A proper Criterion-based bench harness
mirroring quantica_bench is on the roadmap.
Building
Desktop / server (default)
# Build everything (opt-level=2, CT-safe)
# Build with the RustCrypto trait bridge feature
# Run all tests
# Generate the rustdoc API reference (strict mode: -D warnings -D missing-docs)
RUSTDOCFLAGS="-D warnings -D missing-docs"
Both the default and the rust-crypto-traits configurations pass
strict-doc mode with zero warnings.
no_std / bare-metal cross-compile
The crate is still in transition: it currently uses Vec<u8> /
alloc end-to-end and the std feature is reserved as a no-op for
future use. Bare-metal cross-compilation will work once the
caller-provided-buffer refactor lands; today it is only verified at
the workspace level for quantica. Targeted rustc targets:
# Install the targets we care about
Cargo profiles
The workspace Cargo.toml declares three profiles:
| Profile | opt-level | CT guarantee | Use case |
|---|---|---|---|
release |
2 | Yes (Rust source-level + black_box) |
Desktop / server production |
release-embedded |
z + abort | Yes (asm CT backends from silentops) |
Embedded, minimum size |
release-bench |
3 | No (LLVM may break CT patterns) | Benchmarks only |
⚠️
opt-level=3can defeat constant-time guarantees: LLVM may convert bitwise mask patterns into conditional memory accesses. Always useopt-level=2or lower for security-critical builds, or rely on the assembly CT backends fromsilentops(asm-aarch64,asm-thumbv7,asm-thumbv6m,asm-riscv32) which bypass the compiler entirely.
Test validation
All implementations are validated against four independent vector
suites; total ≈ 351 tests in the default build (358 with
rust-crypto-traits).
NIST CAVP / FIPS / RFC happy-path conformance
The bulk of the conformance evidence comes from RFC and FIPS pinned
vectors (in-source tests, arcana/src/**/tests) plus the official
NIST CAVP .rsp corpus mirrored under arcana/tests/cavp/:
| Family | # tests | Vector sources |
|---|---|---|
| SHA-1/2 family | ~25 | FIPS 180-4 examples, NIST CAVP, RFC 6234 |
| SHA3 / SHAKE / cSHAKE | ~25 | FIPS 202 examples, NIST SP 800-185 sample 1/2/4 |
| BLAKE2 | ~10 | RFC 7693 §B test vectors |
| AES (all modes) | ~40 | FIPS 197, NIST SP 800-38A/B/C/D, IEEE 1619, RFC 4493 |
| ChaCha20-Poly1305 | ~15 | RFC 8439 §2.8 + draft-irtf-cfrg-xchacha §3 |
| RSA | ~20 | PKCS#1 / OAEP / PSS round-trips, RFC 8017 §A.1 |
| ECDSA | ~50 | RFC 6979 §A.2, NIST P-256/384/521 generators, 7 curves |
| EdDSA | ~25 | RFC 8032 §7.1 (Ed25519 pure + ctx + ph) |
| X25519 / X448 | ~20 | RFC 7748 §6 |
| HMAC | ~15 | RFC 4231, RFC 2202 |
| CMAC / KMAC / GMAC | ~20 | RFC 4493, NIST SP 800-185, NIST SP 800-38D |
| Streaming Cipher / Mac ctx | ~40 | Round-trips × (mode × padding), error paths |
| DER / PEM key serialization | ~10 | PKCS#1, SEC1, SPKI, PKCS#8 round-trips |
| CAVP corpus | ~2 200 | NIST CAVP .rsp (SHA, AES, HMAC, ECDSA SigVer) |
| ACVP corpus | ~1 250 | NIST ACVP JSON (SHA3, AES-CTR/CCM/XTS, HMAC-SHA3, ECDSA SigVer) |
Wycheproof
Vectors from the C2SP/wycheproof
project, covering malformed inputs, corrupted keys, oversized DER
INTEGERs, edge-case ECDSA signatures, and other negative tests the
NIST happy-path vectors do not exercise. Each vector carries a
result field — valid, invalid, or acceptable — against which
the implementation's accept / reject decision is compared.
| Family | Vectors | Notes |
|---|---|---|
| AES-GCM | ~250 | invalid IVs, tag truncation, corrupted ciphertext |
| AES-CBC-PKCS5 | ~180 | padding-oracle robustness |
| ChaCha20-Poly1305 | ~120 | RFC 8439 negatives |
| ECDSA P-256 | ~380 | edge cases on r/s, oversized DER (also closed a real bug) |
| ECDSA P-384 | ~270 | same edge-case set on the larger curve |
| EdDSA Ed25519 | ~160 | RFC 8032 §5.1 corner cases |
| RSA OAEP / PSS | ~240 | wrong hash, modulus length, label |
| HMAC-SHA-2 | ~70 | wrong key length, truncated tags |
| Total | ~1 670 |
The Wycheproof import surfaced (and we fixed) one ECDSA
oversized-s bug — see commit 191d40e.
Custom negative / robustness tests
Hand-curated tests targeting the specific error paths of each typed key wrapper — wrong-length inputs, off-curve public keys (defence against the invalid-curve attack), tampered signatures, infinity encodings, malformed DER, etc. Around 30 tests across the families.
Running everything
Policy on test suites
A necessary condition for adding a new cryptographic primitive to this crate is the availability of a public reference test suite for it. When a new peer-reviewed test corpus appears — a refreshed Wycheproof release, a new CAVP tranche, an IETF CFRG vector set — we import it and extend the test matrix accordingly, and call the refresh out in the changelog.
Examples
Rust
C FFI
For C consumers, the arcana_ffi companion crate exposes ~20
extern "C" functions and ships a standalone test_arcana.c
example program. Build the shared library and run the C test:
LD_LIBRARY_PATH=target/release
The generated C header (arcana.h) is kept under the FFI crate's
include/ directory.
Module map
arcana/src/
lib.rs Crate root: Hasher, Xof, BlockCipher traits
hash/
mod.rs Re-exports
sha1.rs SHA-1 (FIPS 180-4) — legacy
sha224.rs SHA-224
sha256.rs SHA-256
sha384.rs SHA-384
sha512.rs SHA-512
sha512_trunc.rs SHA-512/224, SHA-512/256
sha3.rs SHA3-224/256/384/512, SHAKE128/256, cSHAKE128/256
blake2.rs BLAKE2b, BLAKE2s (RFC 7693)
ripemd160.rs RIPEMD-160 (ISO 10118-3) — legacy
cipher/
mod.rs Re-exports
aes.rs AES-128/192/256 (FIPS 197, table-based S-box)
des.rs DES + Triple-DES (FIPS 46-3)
modes.rs ECB, CBC, CTR, GCM (SP 800-38A/D)
ccm.rs AES-CCM AEAD (SP 800-38C, RFC 3610)
xts.rs AES-XTS disk encryption (IEEE 1619)
chacha20.rs ChaCha20 stream cipher (RFC 8439)
poly1305.rs Poly1305 one-time MAC (RFC 8439)
chacha20poly1305.rs ChaCha20-Poly1305 AEAD (RFC 8439)
xchacha20poly1305.rs XChaCha20-Poly1305 AEAD (24-byte nonce)
ctx.rs Streaming Cipher: init/update/finalize + padding
mac/
mod.rs Re-exports
ctx.rs Streaming Mac: HMAC×8, CMAC×4, KMAC×2, GMAC×3
rsa/
mod.rs Re-exports
bigint.rs Arbitrary-precision integer arithmetic
rsa.rs Key generation, raw encrypt/decrypt (CRT)
pkcs1.rs PKCS#1 v1.5 enc + sig (8 hash functions)
oaep.rs RSAES-OAEP (RFC 8017 §7.1)
pss.rs RSASSA-PSS (RFC 8017 §8.1)
ecc/
mod.rs Re-exports
field.rs Multi-precision field arithmetic (P-256..P-521, Curve25519, Curve448)
curve.rs Short-Weierstrass curve params + Jacobian point ops + CT scalar_mul_point
curves.rs Curve trait + 7 unit structs + dispatch macro (shared ECDSA/ECDH)
ecdsa.rs ECDSA Signature + DER + RFC 6979 + LIMBS-generic internals
ecdh.rs ECDH integration tests (impl lives in ecdsa.rs)
eddsa.rs Ed25519 pure + ctx + ph (RFC 8032)
x25519.rs X25519 ECDH (RFC 7748)
x448.rs X448 ECDH (RFC 7748)
encoding/
mod.rs Re-exports
der.rs DER encoder / parser (canonical, strict)
pem.rs PEM armor / dearmor
keys.rs PKCS#1 / SEC1 / SPKI / PKCS#8 key serialization
bridge/
mod.rs RustCrypto digest::Digest adapters (feature-gated)
Known limitations
Side-channel protection
- AES uses a table-based S-box — vulnerable to cache-timing on shared L1 targets. Bitsliced / AES-NI backend is not yet shipped.
- DES / 3DES use table-based S-boxes; legacy use only.
RsaSecretKey/Ed25519SecretKey/SecretKey(ECC) lack Zeroize-on-Drop. Callers must zeroize sensitive buffers explicitly viasilentops::ct_zeroize.- Heap allocations on the secret path — secret-key buffers come from
allocrather than caller-provided fixed buffers. A future refactor will thread&mut [u8]end-to-end for bare-metal stack-only operation. - RSA CRT decrypt path (
rsa::rsa::rsa_decrypt_raw) has not been formally CT-audited; aBigIntreview + dudect run is scheduled. - Bit-mask CT primitives are defended against LLVM
branch-recovery via
core::hint::black_box; on targets without thesilentopsasm backend (e.g. WebAssembly) the CT guarantee is best-effort source-level only.
Standards conformance
- Ed448 is not yet implemented (RFC 8032 Appendix A port pending — prior WebFetch attempts to fetch the RFC reference Python were inconsistent).
- AES-OCB (RFC 7253) is not implemented; will be added on demand.
- DER/PEM key serialization covers PKCS#1, SEC1, SPKI, PKCS#8; ASN.1 BER (non-canonical) input is rejected by design.
Portability
- The crate depends on
alloc(Vec<u8>everywhere); a trueno_stdmode requires the caller-provided-buffer refactor noted above. - No platform-specific OsRng adapter; ECDSA and RSA keygen take a
user-supplied RNG callback. The caller is responsible for wiring a
hardware-RNG /
BCryptGenRandom/SecRandomCopyBytes//dev/urandomsource on their target.
Testing
- No fuzz harness yet (
cargo-fuzzis on the roadmap). - No CI/CD pipeline yet — the workspace
.gitea/workflows/will cover the test matrix once the post-quantum side closes its CT3 (QEMU-user) milestone. - No formal third-party evaluation yet; arcana feeds the same documentation pack as quantica and is aimed at the same evaluation target.
Roadmap
The full hardening roadmap lives under arcana/doc/sca/ (HTML
rendered by ./gendoc.sh arcana). The summary below is the project's
living plan towards a third-party evaluation, indexed by Tier
item identifier so each row maps to a stable cross-reference in
the source code, the SCA annex and the workspace SECURITY.md
lifecycle.
Status legend: ✅ done · 🔧 in progress · 📋 planned · 💤 deferred.
Tier 1 — Active vulnerabilities (critical path)
| Id | Item | Status |
|---|---|---|
| T1-A | Fixsliced AES (Adomnicai-Peyrin TCHES 2021/1) — replace table-based S-box | 📋 |
| T1-B | Minerva audit on bits2int / reduce_mod_n / scalar_inv / sample_random_scalar |
📋 |
| T1-C | Aumüller RSA-CRT against Bellcore (formally verified by Rauzy-Guilley) | 📋 |
| T1-D | Hedged ECDSA / EdDSA mode (CFRG det-sigs-with-noise) |
📋 |
| T1-E | RSA bigint CT audit (Montgomery_mul, cmp, pow_mod, mod_inv) | 📋 |
| T1-F | Ed25519 scalar-mul audit (mirror of 76191c1) |
📋 |
| T1-G | X25519 / X448 ladder audit + black_box shielding |
📋 |
Tier 2 — Hardening for evaluation
| Id | Item | Status |
|---|---|---|
| T2-A | Z-coordinate randomization (Brier-Joye 2002) on ECC | 📋 |
| T2-B | Scalar blinding (Coron 1999) on ECC | 📋 |
| T2-D | First-order Boolean masking of SHA-2 (Belenky TCHES 2023/3 — CDPA) | 📋 |
| T2-E | Zeroize-on-Drop on RsaSecretKey, Ed25519SecretKey, ECC SecretKey |
📋 |
| T2-G | First-order Boolean masking on fixsliced AES (post T1-A) | 📋 |
| T2-H | CT carry-less GHASH multiplier (PCLMULQDQ / PMULL on host; bitsliced on embedded) | 📋 |
| T2-I | RSA message blinding + exponent blinding (Coron 1999) | 📋 |
| T2-J | PKCS#1 v1.5 CT padding-oracle handling (RFC 8017 §7.2.2) | 📋 |
| T2-K | X25519 / X448 small-subgroup contributory check audit | 📋 |
Tier 3 — Verification tooling
The cross-arch test infrastructure tracked under quantica's T3-A
/ T3-B (qemu-user matrix on the three non-x86_64 Linux triplets,
qemu-system matrix on the bare-metal targets, and the
semihosting host↔guest vector-streaming protocol — see
.forgejo/workflows/qemu-cross-tests.yml, Cross.toml, and the
tools/qemu-*.sh drivers) is workspace-wide and already exercises
arcana — cross test --target aarch64-unknown-linux-gnu -p arcana
runs the full 315-vector arcana KAT set under qemu-user with the
asm-aarch64 silentops backend on every PR. arcana's own Tier 3
rows below cover the additional CT-evidence harnesses (ctgrind /
dudect) that are not subsumed by the cross-arch matrix.
| Id | Item | Status |
|---|---|---|
| T3-A | ctgrind harness (mirror of quantica's, via silentops::ct_grind) |
📋 |
| T3-B | dudect harness on scalar_mul_point, rsa_decrypt, AEAD decrypt, fixsliced AES |
📋 |
Tier 4 — Deferred / beyond the current evaluation scope
| Id | Item | Status |
|---|---|---|
| T4-RSA-A | Joye-Tunstall infective computation (multi-fault resistance) | 💤 |
| T4-AES-A | AES last-round redundancy + infective DFA defence | 💤 |
| T4-CC | Higher-order DPA / template (2-share masking) for CC EAL4+ | 💤 |
Tier 5 — Documentation pass
Cross-cutting documentation work, orthogonal to the cryptographic
tiers above. Planned (not deferred); timing to be sequenced
against the external evaluation calendar. Items are workspace-wide
and shared with the quantica Tier 5.
| Id | Item | Status |
|---|---|---|
| T5-A | Workspace-wide doc pass (quantica + arcana): neutralise evaluation-target references — replace any CSPN-/ANSSI-specific language with generic evaluation / certification / audit terminology so the doc set reads cleanly against any third-party reviewer |
✅ |
| T5-B | TOC review across the workspace doc set (doc/TOC.md contract + per-crate doc/ trees) — reorder chapters into 4 thematic clusters; rename ch.8 "Side-channel countermeasures" → "(summary)" + add Per-algorithm deep dives H3 bridging to the Sphinx pack |
✅ |
ECC follow-ups (already shipped)
| Id | Item | Status |
|---|---|---|
| — | ecc::curves split (Curve trait + unit structs out of ecdsa.rs) |
✅ commit 0feb5b5 |
| — | CT hardening of scalar_mul_point (Montgomery ladder, branchless point_add_ct, core::hint::black_box shielding on field_* masks) |
✅ commit 76191c1 |
| Ed448 | RFC 8032 Appendix A port | 💤 (RFC fetch fragile, deferred until reference Python is available locally) |
| AES-OCB | RFC 7253 | 💤 (skip unless explicit ask) |
Suggested execution order (critical path)
- Sprint 1: T1-A + T1-C + T1-E — closes the active attack surface.
- Sprint 2: T2-A + T2-B + T2-E + T1-B Minerva audit — hardens ECC to SOTA.
- Sprint 3: T3-A ctgrind + T3-B dudect — provides CT evidence for the eval.
- Sprint 4: T1-D hedged + T2-D HMAC masking — final hardening.
- Evaluation doc pack ships, referencing each countermeasure to its paper.
Effort estimate: 4 – 6 weeks full-time for T1 + T2 + T3, plus ~2 weeks
documentation. Updates to this table are tracked in the change log
of arcana/doc/sca/index.rst.
References
- FIPS 180-4 — SHA-1, SHA-2 family
- FIPS 197 — AES
- FIPS 202 — SHA-3, SHAKE
- FIPS 186-5 — ECDSA
- NIST SP 800-185 — KMAC, cSHAKE
- RFC 7693 — BLAKE2
- RFC 8017 — PKCS#1 (RSA)
- RFC 8032 — EdDSA (Ed25519, Ed448)
- RFC 7748 — X25519, X448
- RFC 8439 — ChaCha20-Poly1305
- RFC 6979 — Deterministic ECDSA
- RFC 4493 — AES-CMAC
- RFC 5639 — Brainpool curves
- SEC 2 v2.0 — secp256k1 / NIST P-256 / NIST P-384
- C2SP / Wycheproof — edge-case and negative test vectors
- NIST CAVP — official conformance test vectors
- NIST ACVP-Server — modern conformance test vectors
- Reparaz, Balasch & Verbauwhede (2017) — "dude, is my code constant time?"
(the dudect methodology used in
silentops::verify)
License
Apache-2.0.