krypteia-arcana 0.1.0

Pure-Rust classical cryptographic primitives: RSA (PKCS#1 v1.5, OAEP), ECC (NIST P-256/384/521, secp256k1), ECDSA, EdDSA (Ed25519), X25519, AES (128/192/256, GCM/CBC), DES/3DES, SHA-1/2/3, HMAC. Side-channel-aware (Montgomery ladder, branchless point_add_ct). Targets embedded (no_std), STM32 M0/M4/M33, ESP32-C3 RISC-V. Zero runtime dependencies.
Documentation

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:

  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

[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)

use arcana::hash::sha256::Sha256;
use arcana::Hasher;

let digest = Sha256::hash(b"hello, arcana");
assert_eq!(digest.len(), 32);

AEAD (AES-128-GCM)

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

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)

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)

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 variantsinit(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 splitscalar_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.

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)
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<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
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 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

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

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:

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

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<u8> 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

License

Apache-2.0.