# 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.

## 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<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:

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

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