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// Each `not(ic_approved_only)` below is an algorithm FIPS approved mode does
59// not permit, left out of that build; see `ic_core::APPROVED_ONLY`.
60//
61// AVX2 for the ChaCha20 keystream, behind runtime detection.
62#[allow(unsafe_code)]
63#[cfg(not(ic_approved_only))]
64pub mod chacha;
65#[cfg(all(target_arch = "x86_64", feature = "std"))]
66// The carry-less multiply instruction.
67#[allow(unsafe_code)]
68mod clmul;
69// The x86-64 ChaCha20 and Poly1305 kernels, for scripts/ct-audit.
70#[cfg(all(feature = "ct-probes", not(ic_approved_only)))]
71#[allow(unsafe_code, missing_docs)]
72#[doc(hidden)]
73pub mod ct_probes;
74// GHASH via CLMUL when the CPU has it.
75#[allow(unsafe_code)]
76pub mod gcm;
77#[cfg(not(ic_approved_only))]
78pub mod gcm_siv;
79#[cfg(all(target_arch = "x86_64", feature = "std"))]
80// AES-GCM as one fused pass: AES-NI or VAES rounds interleaved with the
81// carry-less multiply, behind runtime detection. The wide instructions are
82// inline assembly because their intrinsics postdate the MSRV.
83#[allow(unsafe_code)]
84mod gcm_x86;
85pub mod gf;
86pub mod keywrap;
87pub mod modes;
88#[cfg(not(ic_approved_only))]
89pub mod polyval;
90pub mod sealer;
91#[cfg(not(ic_approved_only))]
92pub mod shamir;
93
94pub use aes::{Aes128, Aes192, Aes256};
95#[cfg(not(ic_approved_only))]
96pub use chacha::{chacha20_xor, ChaCha20Poly1305, Poly1305, XChaCha20Poly1305};
97pub use gcm::{Aes128Gcm, Aes192Gcm, Aes256Gcm, GcmLimits};
98#[cfg(not(ic_approved_only))]
99pub use gcm_siv::{Aes128GcmSiv, Aes256GcmSiv};
100pub use keywrap::{Aes128Kw, Aes128Kwp, Aes192Kw, Aes192Kwp, Aes256Kw, Aes256Kwp};
101pub use modes::{cbc_decrypt, cbc_encrypt, ctr_xor, pkcs7_pad, pkcs7_unpad};
102pub use sealer::{Opener, Sealer};
103
104/// Ontology identifiers for the AEADs this crate provides.
105pub const AEAD_IDS: &[&str] = &[
106 "aes-128-gcm",
107 "aes-192-gcm",
108 "aes-256-gcm",
109 #[cfg(not(ic_approved_only))]
110 "chacha20-poly1305",
111 #[cfg(not(ic_approved_only))]
112 "xchacha20-poly1305",
113];
114
115/// Ontology identifiers for the raw block ciphers this crate provides.
116pub const BLOCK_CIPHER_IDS: &[&str] = &["aes-128", "aes-192", "aes-256"];