arcana/lib.rs
1//! # arcana — classical cryptography for the `krypteia` workspace
2//!
3//! Pure-Rust implementations of widely-used classical primitives —
4//! hash functions, symmetric ciphers, AEAD modes, RSA, ECDSA / ECDH,
5//! EdDSA, X25519, and X448 — sharing the same side-channel
6//! countermeasure toolkit ([`silentops`](https://docs.rs/silentops))
7//! used by the post-quantum side of the workspace
8//! ([`quantica`](https://docs.rs/quantica)).
9//!
10//! Zero external runtime dependencies (only `std` / `alloc` and the
11//! workspace-local `silentops`); constant-time on the data path.
12//!
13//! # Algorithms
14//!
15//! ## Hash functions ([`hash`])
16//!
17//! | Algorithm | Output | Module |
18//! |--------------|---------|-------------------------------------------------|
19//! | SHA-1 | 160 b | [`hash::sha1::Sha1`] (legacy) |
20//! | SHA-224 | 224 b | [`hash::sha224::Sha224`] |
21//! | SHA-256 | 256 b | [`hash::sha256::Sha256`] |
22//! | SHA-384 | 384 b | [`hash::sha384::Sha384`] |
23//! | SHA-512 | 512 b | [`hash::sha512::Sha512`] |
24//! | SHA-512/224 | 224 b | [`hash::sha512_trunc::Sha512_224`] |
25//! | SHA-512/256 | 256 b | [`hash::sha512_trunc::Sha512_256`] |
26//! | SHA3-224 | 224 b | [`hash::sha3::Sha3_224`] |
27//! | SHA3-256 | 256 b | [`hash::sha3::Sha3_256`] |
28//! | SHA3-384 | 384 b | [`hash::sha3::Sha3_384`] |
29//! | SHA3-512 | 512 b | [`hash::sha3::Sha3_512`] |
30//! | SHAKE128 | XOF | [`hash::sha3::Shake128`] |
31//! | SHAKE256 | XOF | [`hash::sha3::Shake256`] |
32//! | cSHAKE128 | XOF | [`hash::sha3::CShake128`] |
33//! | cSHAKE256 | XOF | [`hash::sha3::CShake256`] |
34//! | BLAKE2b | 1-512 b | [`hash::blake2::Blake2b`] |
35//! | BLAKE2s | 1-256 b | [`hash::blake2::Blake2s`] |
36//! | RIPEMD-160 | 160 b | [`hash::ripemd160::Ripemd160`] (legacy) |
37//!
38//! ## Symmetric ciphers and modes ([`cipher`])
39//!
40//! | Algorithm | Module |
41//! |-----------------------------------|----------------------------------------------|
42//! | AES-128 / 192 / 256 | [`cipher::aes`] |
43//! | DES, Triple-DES (EDE) | [`cipher::des`] |
44//! | ECB, CBC, CTR, GCM modes | [`cipher::modes`] |
45//! | **AES-CCM** (RFC 3610) AEAD | [`cipher::ccm`] |
46//! | **AES-XTS** (IEEE 1619) disk crypto| [`cipher::xts`] |
47//! | **ChaCha20** (RFC 8439) | [`cipher::chacha20`] |
48//! | **Poly1305** one-time MAC | [`cipher::poly1305`] |
49//! | **ChaCha20-Poly1305** AEAD | [`cipher::chacha20poly1305`] |
50//! | **XChaCha20-Poly1305** AEAD | [`cipher::xchacha20poly1305`] (24-byte nonce) |
51//!
52//! In addition to these one-shot, function-oriented APIs, a
53//! stateful **streaming `Cipher` object** lives in
54//! [`cipher::ctx`]. It wraps the AES / DES / 3DES block ciphers
55//! with the ECB / CBC / CTR modes behind a uniform
56//! `init / update / finalize` cycle, with caller-provided output
57//! buffers (no allocation required) and configurable padding
58//! (`None`, `Pkcs7`, `Iso9797M1`, `Iso9797M2`, `AnsiX923`). AEAD
59//! modes intentionally stay function-oriented to avoid releasing
60//! unverified plaintext during streaming decryption.
61//!
62//! ## Message authentication codes ([`mac`])
63//!
64//! A streaming MAC object lives in [`mac::ctx`], exposing a
65//! uniform `init / update / sign | verify` cycle across four
66//! families:
67//!
68//! | Family | Algorithms | Standard |
69//! |--------------|------------------------------------------------------------------|-------------------|
70//! | HMAC | SHA-1, SHA-256, SHA-384, SHA-512, SHA3-256/384/512, RIPEMD-160 | RFC 2104, FIPS 198-1 |
71//! | CMAC | AES-128 / 192 / 256, Triple-DES | NIST SP 800-38B, RFC 4493 |
72//! | KMAC | KMAC128, KMAC256 | FIPS SP 800-185 |
73//! | GMAC | AES-128 / 192 / 256 | NIST SP 800-38D |
74//!
75//! Three init variants distinguish families that need different
76//! inputs: `init(key)` (HMAC, CMAC, KMAC default),
77//! `init_kmac(key, custom)` (KMAC with explicit customization
78//! string), and `init_with_nonce(key, nonce)` (GMAC, 12-byte
79//! unique nonce). `verify` accepts truncated tags and runs in
80//! constant time. Poly1305 is intentionally **not** routed
81//! through `Mac` because it is a one-time MAC and would be unsafe
82//! behind an "init then reuse" object.
83//!
84//! ## RSA ([`rsa`])
85//!
86//! | Padding | Module |
87//! |-----------------------------------|----------------------------------------------|
88//! | PKCS#1 v1.5 encryption + signature| [`rsa::pkcs1`] (8 hash functions supported) |
89//! | OAEP encryption (PKCS#1 v2.2) | [`rsa::oaep`] |
90//! | RSASSA-PSS signature (PKCS#1 v2.2)| [`rsa::pss`] |
91//!
92//! ## Elliptic curve cryptography ([`ecc`])
93//!
94//! ECDSA / ECDH / SEC1-compressed / signature DER are all unified
95//! behind a single [`ecc::curves::Curve`] trait, implemented by:
96//!
97//! | Wrapper | Curve |
98//! |-------------------------------------------|--------------------|
99//! | [`ecc::curves::P256`] | NIST P-256 |
100//! | [`ecc::curves::P384`] | NIST P-384 |
101//! | [`ecc::curves::P521`] | NIST P-521 |
102//! | [`ecc::curves::Secp256k1`] | secp256k1 (SECG) |
103//! | [`ecc::curves::BrainpoolP256r1`] | BSI / RFC 5639 |
104//! | [`ecc::curves::BrainpoolP384r1`] | BSI / RFC 5639 |
105//! | [`ecc::curves::BrainpoolP512r1`] | BSI / RFC 5639 |
106//!
107//! Edwards / Montgomery curves live in standalone modules because
108//! their algebraic shape doesn't fit the short-Weierstrass `Curve`
109//! trait:
110//!
111//! | Algorithm | Module |
112//! |-----------------------------------|-----------------------|
113//! | Ed25519, Ed25519ctx, Ed25519ph | [`ecc::eddsa`] |
114//! | X25519 ECDH (Curve25519) | [`ecc::x25519`] |
115//! | X448 ECDH (Curve448) | [`ecc::x448`] |
116//!
117//! Ed448 is planned but not yet implemented.
118//!
119//! # Cargo features
120//!
121//! | Feature | Default | Effect |
122//! |----------------------|---------|---------------------------------------------------------|
123//! | `std` | no | Reserved for future `no_std` work (currently a no-op). |
124//! | `rust-crypto-traits` | no | Pulls in `digest 0.10` / `cipher 0.4` / `signature 2.0` and activates the `bridge` module, which wraps every hash in a `digest::Digest` impl for ecosystem interop (HMAC, HKDF, PBKDF2, Argon2, ...). |
125//!
126//! Default builds are **zero-dependency** (only the workspace-local
127//! `silentops` crate). The `rust-crypto-traits` feature is opt-in
128//! and pulls in definitions only (no crypto code).
129//!
130//! # Quick start
131//!
132//! ```rust
133//! // SHA-256 one-shot hash
134//! use arcana::hash::sha256::Sha256;
135//! use arcana::Hasher;
136//! let digest = Sha256::hash(b"hello, arcana");
137//! assert_eq!(digest.len(), 32);
138//! ```
139//!
140//! ```rust
141//! // AES-128-GCM AEAD
142//! use arcana::cipher::aes::Aes128;
143//! use arcana::cipher::modes::Gcm;
144//! use arcana::BlockCipher;
145//!
146//! let key = [0x42u8; 16];
147//! let nonce = [0u8; 12];
148//! let cipher = Aes128::new(&key);
149//! let (ct, tag) = Gcm::encrypt(&cipher, &nonce, b"aad", b"plaintext");
150//! let pt = Gcm::decrypt(&cipher, &nonce, b"aad", &ct, &tag).unwrap();
151//! assert_eq!(pt, b"plaintext");
152//! ```
153//!
154//! ```rust
155//! // X25519 ECDH key exchange
156//! use arcana::ecc::x25519::{x25519_derive_public, x25519_ecdh};
157//! let alice_sk = [0x77u8; 32];
158//! let bob_sk = [0x88u8; 32];
159//! let alice_pk = x25519_derive_public(&alice_sk);
160//! let bob_pk = x25519_derive_public(&bob_sk);
161//! let shared_a = x25519_ecdh(&alice_sk, &bob_pk);
162//! let shared_b = x25519_ecdh(&bob_sk, &alice_pk);
163//! assert_eq!(shared_a, shared_b);
164//! ```
165//!
166//! # Examples
167//!
168//! ```sh
169//! cargo run -p arcana --release --example hash_demo
170//! cargo run -p arcana --release --example aes_demo
171//! cargo run -p arcana --release --example rsa_demo
172//! cargo run -p arcana --release --example ecdsa_demo
173//! cargo run -p arcana --release --example eddsa_demo
174//! cargo run -p arcana --release --example x25519_demo
175//! ```
176//!
177//! # Side-channel guarantees
178//!
179//! The full threat model and per-algorithm countermeasure roadmap
180//! lives in `arcana/doc/sca/` (HTML rendered by `./gendoc.sh
181//! arcana`). The roadmap items referenced here (`T1-A`, `T1-C`,
182//! `T2-D` …) are defined there. This section is a quick-reference
183//! summary; for evaluation evidence, refer to the annex.
184//!
185//! ## Always-on
186//!
187//! | Defence | Scope |
188//! |------------------------------------|----------------------------------------------------------------------------------------|
189//! | Constant-time tag comparison | All AEAD decrypt, MAC verify, ECDSA verify (via `silentops::ct_eq`) |
190//! | Constant-time field arithmetic | ECC (P-256, P-384, P-521, secp256k1, Brainpool), Ed25519, X25519, X448 |
191//! | **CT Montgomery ladder** | [`ecc::curve::scalar_mul_point`] — branch-free across all 7 short-Weierstrass curves |
192//! | `core::hint::black_box` shielding | [`ecc::field`] mask selects, to keep LLVM from recovering branches at `opt-level >= 2` |
193//! | No secret-dependent branches | Hash, symmetric, HMAC, CMAC, KMAC, GMAC |
194//! | RFC 6979 deterministic nonce | ECDSA — eliminates nonce-reuse attacks (but see fault-attack note below) |
195//! | `silentops::ct_zeroize` available | Caller can zeroize sensitive buffers explicitly |
196//!
197//! ## Open vulnerabilities (evaluation gaps — track in roadmap)
198//!
199//! | Primitive | Issue | Roadmap item |
200//! |---------------------|----------------------------------------------------------------------|--------------|
201//! | AES S-box | Table-based lookup — cache-line leaks on CPUs with shared L1 | `T1-A` (fixsliced AES port from Adomnicai-Peyrin TCHES 2021/1) |
202//! | RSA-CRT decrypt | Bellcore single-fault → factorisation of `N` | `T1-C` (Aumüller 2002, formally verified) |
203//! | RSA bigint | `Montgomery_mul`, `cmp`, `pow_mod` not formally CT-audited | `T1-E` |
204//! | ECDSA / ECDH | Minerva-class bit-length leak audit not yet completed | `T1-B` |
205//! | Ed25519 / ECDSA-RFC 6979 | Single-fault on deterministic signing → key recovery | `T1-D` (hedged signatures, CFRG draft) |
206//! | HMAC-SHA-2 | Carry-based DPA breaks unmasked HMAC-SHA-2 in 30 K – 275 K traces | `T2-D` (first-order Boolean masking) |
207//! | DES / 3DES S-boxes | Table-based; legacy only | unscoped — avoid on SCA-sensitive targets |
208//!
209//! ## Not yet implemented
210//!
211//! - **Zeroize-on-Drop**: typed key wrappers ([`ecc::curves::SecretKey`],
212//! [`ecc::eddsa::Ed25519SecretKey`], [`rsa::rsa::RsaSecretKey`])
213//! currently **do not** implement `Drop` with
214//! `silentops::ct_zeroize`. Callers should zeroize sensitive
215//! buffers explicitly. Roadmap item `T2-E`.
216//! - **DPA / EM / multi-fault**: out of scope for the current
217//! evaluation profile; tier-4 items in the SCA annex.
218//!
219//! # Native API vs RustCrypto bridge
220//!
221//! By default, this crate exposes its own trait hierarchy
222//! ([`Hasher`], [`Xof`], [`BlockCipher`], ...). Enable
223//! `rust-crypto-traits` to additionally activate the `bridge`
224//! module which implements `digest::Digest` for the hash
225//! functions. The two universes are intentionally separate so the
226//! native modules never depend on a specific external crate
227//! version.
228
229// ============================================================
230// Our own trait definitions (zero-dependency)
231// ============================================================
232
233/// Trait for hash functions (fixed-output).
234pub trait Hasher {
235 /// Output size in bytes.
236 const OUTPUT_LEN: usize;
237 /// Block size in bytes (for HMAC computation).
238 const BLOCK_LEN: usize;
239
240 /// Create a new hasher instance.
241 fn new() -> Self;
242 /// Feed data into the hasher (can be called multiple times).
243 fn update(&mut self, data: &[u8]);
244 /// Finalize and return the digest. Consumes the hasher.
245 fn finalize(self) -> Vec<u8>;
246 /// Finalize into a caller-provided buffer (no allocation).
247 fn finalize_into(self, out: &mut [u8]);
248 /// One-shot: hash data and return the digest.
249 fn hash(data: &[u8]) -> Vec<u8>
250 where
251 Self: Sized,
252 {
253 let mut h = Self::new();
254 h.update(data);
255 h.finalize()
256 }
257}
258
259/// Trait for extendable-output functions (XOF) such as SHAKE128 / SHAKE256.
260///
261/// Unlike a fixed-output [`Hasher`], an XOF can be `squeeze`-d for any
262/// number of bytes after absorption is complete. The internal sponge
263/// state is mutated by every `squeeze` call, so successive squeezes
264/// extend the output stream rather than restart it.
265pub trait Xof {
266 /// Sponge rate (number of bytes absorbed per Keccak-f permutation).
267 const BLOCK_LEN: usize;
268
269 /// Create a fresh XOF instance with empty absorbed state.
270 fn new() -> Self;
271 /// Absorb additional input. May be called any number of times before
272 /// the first `squeeze`.
273 fn update(&mut self, data: &[u8]);
274 /// Squeeze `out.len()` bytes of output. Can be called multiple times;
275 /// successive calls extend the same output stream (they do not reset).
276 fn squeeze(&mut self, out: &mut [u8]);
277}
278
279/// Trait for symmetric block ciphers operating on a fixed block size.
280///
281/// Implementors include the AES family ([`cipher::Aes`], [`cipher::Aes128`],
282/// [`cipher::Aes192`], [`cipher::Aes256`]) and the DES family
283/// ([`cipher::Des`], [`cipher::TripleDes`]).
284pub trait BlockCipher {
285 /// Block size in bytes (16 for AES, 8 for DES / 3DES).
286 const BLOCK_LEN: usize;
287 /// Key sizes supported by `new`, in bytes.
288 const KEY_LENS: &'static [usize];
289
290 /// Initialise the cipher with a key. The key length must be one of
291 /// the values listed in [`Self::KEY_LENS`].
292 fn new(key: &[u8]) -> Self;
293 /// Encrypt `block` in place. The slice must be at least
294 /// [`Self::BLOCK_LEN`] bytes long.
295 fn encrypt_block(&self, block: &mut [u8]);
296 /// Decrypt `block` in place. The slice must be at least
297 /// [`Self::BLOCK_LEN`] bytes long.
298 fn decrypt_block(&self, block: &mut [u8]);
299}
300
301/// Trait for digital signature schemes that produce fixed-shape keys
302/// and signatures (no per-call hash parameter).
303///
304/// **Note**: this trait is currently informational. Most signature
305/// schemes in this crate (ECDSA, EdDSA, RSA-PSS, RSA-PKCS1) take a
306/// hash function as a generic or runtime parameter and therefore do
307/// not implement this trait directly -- they expose their own
308/// curve/key types and sign/verify functions. The trait is kept as
309/// the canonical "shape" of the simplest signature scheme for
310/// future extensions and for documentation purposes.
311pub trait SignatureScheme {
312 /// Public key type for this scheme.
313 type PublicKey;
314 /// Secret key type for this scheme.
315 type SecretKey;
316 /// Signature type.
317 type Signature;
318
319 /// Generate a fresh key pair.
320 fn keygen() -> (Self::PublicKey, Self::SecretKey);
321 /// Sign a message.
322 fn sign(sk: &Self::SecretKey, message: &[u8]) -> Self::Signature;
323 /// Verify a signature against a message.
324 fn verify(pk: &Self::PublicKey, message: &[u8], sig: &Self::Signature) -> bool;
325}
326
327/// Trait for public-key encryption schemes.
328///
329/// Like [`SignatureScheme`], this trait is currently informational --
330/// the RSA encryption helpers in [`rsa::pkcs1`] and [`rsa::oaep`] take
331/// a parameter of type [`rsa::rsa::RsaPublicKey`] / `RsaSecretKey`
332/// directly rather than going through this trait, because OAEP and
333/// PKCS#1 v1.5 each take additional protocol parameters (label, RNG)
334/// that don't fit the simple shape below.
335pub trait PublicKeyEncryption {
336 /// Public key type.
337 type PublicKey;
338 /// Secret key type.
339 type SecretKey;
340
341 /// Generate a key pair of the given bit size.
342 fn keygen(bits: usize) -> (Self::PublicKey, Self::SecretKey);
343 /// Encrypt a plaintext under the public key.
344 fn encrypt(pk: &Self::PublicKey, plaintext: &[u8]) -> Vec<u8>;
345 /// Decrypt a ciphertext with the secret key. Returns `None` if the
346 /// padding does not validate.
347 fn decrypt(sk: &Self::SecretKey, ciphertext: &[u8]) -> Option<Vec<u8>>;
348}
349
350// ============================================================
351// Modules
352// ============================================================
353
354/// Hash functions: SHA-1, SHA-2, SHA-3, RIPEMD-160.
355pub mod hash;
356
357/// Symmetric ciphers: AES, DES, 3DES.
358pub mod cipher;
359
360/// Elliptic curve cryptography: ECDSA, EdDSA.
361pub mod ecc;
362
363/// RSA encryption and signatures.
364pub mod rsa;
365
366/// Message authentication codes (HMAC, CMAC, KMAC, GMAC).
367pub mod mac;
368
369/// Key serialization: DER, PEM, PKCS#1, PKCS#8, SEC1, SPKI.
370pub mod encoding;
371
372/// RustCrypto trait bridges (feature-gated).
373#[cfg(feature = "rust-crypto-traits")]
374pub mod bridge;