# 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](https://github.com/cslashm/pqc) workspace. ## Design rules The crate inherits the krypteia workspace design rules: 1. **Pure Rust, zero external crates** — only `core` (and `alloc`); `std` is optional behind a feature flag. The only workspace dependency is `silentops` for shared CT primitives and timing-leak verification. 2. **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_std` is on the roadmap. 3. **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. 4. **Validated** — every algorithm is tested against pinned RFC / NIST / FIPS reference vectors, plus three external corpora (Wycheproof, NIST CAVP, NIST ACVP). 5. **C FFI-exposable** — the companion crate `arcana_ffi` exports ~20 `extern "C"` functions with a C header at `arcana_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 ```toml [dependencies] arcana = { path = "../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) ```rust use arcana::hash::sha256::Sha256; use arcana::Hasher; let digest = Sha256::hash(b"hello, arcana"); assert_eq!(digest.len(), 32); ``` ### AEAD (AES-128-GCM) ```rust use arcana::cipher::aes::Aes128; use arcana::cipher::modes::Gcm; use arcana::BlockCipher; let cipher = Aes128::new(&[0x42u8; 16]); let nonce = [0u8; 12]; let (ct, tag) = Gcm::encrypt(&cipher, &nonce, b"aad", b"plaintext"); let pt = Gcm::decrypt(&cipher, &nonce, b"aad", &ct, &tag).unwrap(); assert_eq!(pt, b"plaintext"); ``` ### X25519 ECDH ```rust use arcana::ecc::x25519::{x25519_derive_public, x25519_ecdh}; let alice_sk = [0x77u8; 32]; let bob_sk = [0x88u8; 32]; let alice_pk = x25519_derive_public(&alice_sk); let bob_pk = x25519_derive_public(&bob_sk); let shared_a = x25519_ecdh(&alice_sk, &bob_pk); let shared_b = x25519_ecdh(&bob_sk, &alice_pk); assert_eq!(shared_a, shared_b); ``` ### HMAC-SHA-256 (streaming) ```rust use arcana::mac::ctx::{Mac, Algorithm}; let mut m = Mac::new(Algorithm::HmacSha256); m.init(b"secret key").unwrap(); m.update(b"hello, ").unwrap(); m.update(b"world!").unwrap(); let tag = m.sign_to_vec().unwrap(); assert_eq!(tag.len(), 32); ``` ### AES-256-CBC (Cipher ctx) ```rust use arcana::cipher::ctx::{Cipher, Algorithm, Mode, Padding, Direction}; let mut c = Cipher::new(Algorithm::Aes256, Mode::Cbc, Padding::Pkcs7).unwrap(); c.init(Direction::Encrypt, &[0x42u8; 32], &[0x77u8; 16]).unwrap(); let mut out = vec![0u8; 64]; let mut n = c.update(b"hello, streaming!", &mut out).unwrap(); n += c.finalize(&mut out[n..]).unwrap(); out.truncate(n); // 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 1. **Function-oriented API + streaming objects** — one-shot functions (`Sha256::hash`, `Gcm::encrypt`, `P256::sign_rfc6979`) for the common case; `Cipher` and `Mac` streaming objects for callers that feed data in chunks or need caller-provided buffers without heap. 2. **Hash function is always explicit** — ECDSA, RSA-PSS, RSA-PKCS1 all take the hash as a type or enum parameter. No hidden default. 3. **AEAD stays function-oriented** — GCM, CCM, ChaCha20-Poly1305, XChaCha20-Poly1305 are *not* routed through the streaming `Cipher` to avoid releasing unverified plaintext during streaming decryption. 4. **`Curve` trait 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. 5. **Three MAC init variants** — `init(key)` for HMAC/CMAC/KMAC, `init_kmac(key, S)` for KMAC with customization, `init_with_nonce` for GMAC. Wrong variant → compile-time or runtime error. 6. **Poly1305 excluded from Mac ctx** — it is a one-time MAC; reusing the key breaks it. Keeping it function-oriented prevents misuse. 7. **CT scalar mul split** — `scalar_mul_point` calls the ladder-only `point_add_ct` (no branches on point coords); the variable `point_add` (with `H==0` short-circuits) is reserved for `double_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. ```{toctree} :maxdepth: 2 ../arcana/sca/index ``` ## 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) ```bash # Build everything (opt-level=2, CT-safe) cargo build --release -p arcana # Build with the RustCrypto trait bridge feature cargo build --release -p arcana --features rust-crypto-traits # Run all tests cargo test --release -p arcana # Generate the rustdoc API reference (strict mode: -D warnings -D missing-docs) RUSTDOCFLAGS="-D warnings -D missing-docs" cargo doc -p arcana --no-deps ``` 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` / `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: ```bash # Install the targets we care about rustup target add thumbv6m-none-eabi # Cortex-M0/M0+ rustup target add thumbv7em-none-eabihf # Cortex-M4/M7 rustup target add thumbv8m.main-none-eabihf # Cortex-M33 (TrustZone) rustup target add riscv32imc-unknown-none-elf # ESP32-C3, SiFive ``` ### 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=3` can defeat constant-time guarantees: LLVM may > convert bitwise mask patterns into conditional memory accesses. > Always use `opt-level=2` or lower for security-critical builds, or > rely on the assembly CT backends from `silentops` (`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](https://github.com/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 ```bash cargo test --release -p arcana cargo test --release -p arcana --features rust-crypto-traits ``` ### 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 ```bash cargo run -p arcana --release --example hash_demo # SHA-256, SHA3, SHAKE cargo run -p arcana --release --example aes_demo # AES-128-GCM encrypt/decrypt cargo run -p arcana --release --example rsa_demo # RSA-2048 sign + OAEP encrypt cargo run -p arcana --release --example ecdsa_demo # ECDSA P-256 keygen/sign/verify cargo run -p arcana --release --example eddsa_demo # Ed25519 sign/verify cargo run -p arcana --release --example x25519_demo # X25519 ECDH key exchange ``` ### 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: ```bash cargo build --release -p arcana_ffi gcc -O2 -o test_arcana arcana_ffi/examples/test_arcana.c \ -Iarcana_ffi/include -Ltarget/release -larcana_ffi -lpthread -ldl -lm LD_LIBRARY_PATH=target/release ./test_arcana ``` The generated C header (`arcana.h`) is kept under the FFI crate's `include/` directory. ## Module map ```text 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 via `silentops::ct_zeroize`. * Heap allocations on the secret path — secret-key buffers come from `alloc` rather 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; a `BigInt` review + dudect run is scheduled. * Bit-mask CT primitives are defended against LLVM branch-recovery via `core::hint::black_box`; on targets without the `silentops` asm 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` everywhere); a true `no_std` mode 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/urandom` source on their target. ### Testing * No fuzz harness yet (`cargo-fuzz` is 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) 1. **Sprint 1**: T1-A + T1-C + T1-E — closes the active attack surface. 2. **Sprint 2**: T2-A + T2-B + T2-E + T1-B Minerva audit — hardens ECC to SOTA. 3. **Sprint 3**: T3-A ctgrind + T3-B dudect — provides CT evidence for the eval. 4. **Sprint 4**: T1-D hedged + T2-D HMAC masking — final hardening. 5. 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](https://csrc.nist.gov/publications/detail/fips/180/4/final) — SHA-1, SHA-2 family - [FIPS 197](https://csrc.nist.gov/publications/detail/fips/197/final) — AES - [FIPS 202](https://csrc.nist.gov/publications/detail/fips/202/final) — SHA-3, SHAKE - [FIPS 186-5](https://csrc.nist.gov/publications/detail/fips/186/5/final) — ECDSA - [NIST SP 800-185](https://csrc.nist.gov/publications/detail/sp/800-185/final) — KMAC, cSHAKE - [RFC 7693](https://www.rfc-editor.org/rfc/rfc7693) — BLAKE2 - [RFC 8017](https://www.rfc-editor.org/rfc/rfc8017) — PKCS#1 (RSA) - [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032) — EdDSA (Ed25519, Ed448) - [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748) — X25519, X448 - [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439) — ChaCha20-Poly1305 - [RFC 6979](https://www.rfc-editor.org/rfc/rfc6979) — Deterministic ECDSA - [RFC 4493](https://www.rfc-editor.org/rfc/rfc4493) — AES-CMAC - [RFC 5639](https://www.rfc-editor.org/rfc/rfc5639) — Brainpool curves - [SEC 2 v2.0](https://www.secg.org/sec2-v2.pdf) — secp256k1 / NIST P-256 / NIST P-384 - [C2SP / Wycheproof](https://github.com/C2SP/wycheproof) — edge-case and negative test vectors - [NIST CAVP](https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program) — official conformance test vectors - [NIST ACVP-Server](https://github.com/usnistgov/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.