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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
# 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.