ic_cipher/lib.rs
1//! # ic-cipher — block ciphers, stream ciphers, and AEADs
2//!
3//! Pure-Rust, `no_std`, dependency-free implementations of AES (FIPS 197),
4//! the SP 800-38A confidentiality modes, AES-GCM (SP 800-38D), and the
5//! RFC 8439 ChaCha20-Poly1305 suite.
6//!
7//! ```
8//! use ic_cipher::{Aes256Gcm, Opener, Sealer};
9//!
10//! // A session key, from a key exchange and a KDF in real use.
11//! let key = [0x2a; 32];
12//! let mut tx = Sealer::<Aes256Gcm>::new(&key, *b"c->s")?;
13//! let mut rx = Opener::<Aes256Gcm>::new(&key, *b"c->s")?;
14//! let mut buf = *b"ship it";
15//! let mut tag = [0u8; 16];
16//! let nonce = tx.seal(b"context", &mut buf, &mut tag)?;
17//! rx.open(&nonce, b"context", &mut buf, &tag)?;
18//! assert_eq!(&buf, b"ship it");
19//! # Ok::<(), ic_core::Error>(())
20//! ```
21//!
22//! [`Sealer`] chooses every nonce itself and [`Opener`] refuses replays; see
23//! [`sealer`] for when that is enough. The AEAD types underneath take a nonce
24//! from the caller, which is what protocols with their own nonce rules (TLS,
25//! QUIC, HPKE) need, and which is where nonce reuse comes from.
26//!
27//! ## Backend status
28//!
29//! AES computes its S-box algebraically and GHASH multiplies without tables, so
30//! neither touches a key-dependent memory address — the cache-timing channel
31//! that table-driven AES leaves open is closed by construction.
32//!
33//! Three backends sit behind the same traits, chosen by the CPU and never by
34//! key material: AES-NI with PCLMULQDQ on x86-64, the ARMv8 crypto extensions
35//! behind a feature, and a portable one everywhere else. The portable AES path
36//! is bitsliced for encryption — four blocks at a time in transposed form, at
37//! roughly the rate of RustCrypto's fixsliced implementation. Decryption is
38//! not bitsliced and runs a byte at a time, which is correct and slow; the
39//! modes that move volume (CTR, GCM, GCM-SIV) only encrypt.
40//!
41//! `ic_ontology::runtime::backend()` reports which one is active, so an agent
42//! can decide whether a workload belongs here.
43#![cfg_attr(not(feature = "std"), no_std)]
44#![deny(missing_docs)]
45// Every unsafe operation inside an unsafe fn must be marked explicitly, so the
46// SIMD backends cannot smuggle one in under the function signature.
47#![forbid(unsafe_op_in_unsafe_fn)]
48// And unsafe may only appear where a CPU intrinsic is being called, which is
49// what the allowances below mark. Everywhere else in this crate -- the modes,
50// the key wrapping, the field arithmetic -- it is a compile error.
51#![deny(unsafe_code)]
52#![warn(clippy::all)]
53
54// AES-NI and the ARMv8 crypto extensions, behind runtime detection.
55#[allow(unsafe_code)]
56pub mod aes;
57
58// AVX2 for the ChaCha20 keystream, behind runtime detection.
59#[allow(unsafe_code)]
60pub mod chacha;
61#[cfg(all(target_arch = "x86_64", feature = "std"))]
62// The carry-less multiply instruction.
63#[allow(unsafe_code)]
64mod clmul;
65// GHASH via CLMUL when the CPU has it.
66#[allow(unsafe_code)]
67pub mod gcm;
68pub mod gcm_siv;
69pub mod gf;
70pub mod keywrap;
71pub mod modes;
72pub mod polyval;
73pub mod sealer;
74pub mod shamir;
75
76pub use aes::{Aes128, Aes192, Aes256};
77pub use chacha::{chacha20_xor, ChaCha20Poly1305, Poly1305};
78pub use gcm::{Aes128Gcm, Aes192Gcm, Aes256Gcm, GcmLimits};
79pub use gcm_siv::{Aes128GcmSiv, Aes256GcmSiv};
80pub use keywrap::{Aes128Kw, Aes128Kwp, Aes192Kw, Aes192Kwp, Aes256Kw, Aes256Kwp};
81pub use modes::{cbc_decrypt, cbc_encrypt, ctr_xor, pkcs7_pad, pkcs7_unpad};
82pub use sealer::{Opener, Sealer};
83
84/// Ontology identifiers for the AEADs this crate provides.
85pub const AEAD_IDS: &[&str] = &[
86 "aes-128-gcm",
87 "aes-192-gcm",
88 "aes-256-gcm",
89 "chacha20-poly1305",
90];
91
92/// Ontology identifiers for the raw block ciphers this crate provides.
93pub const BLOCK_CIPHER_IDS: &[&str] = &["aes-128", "aes-192", "aes-256"];