ic_core/traits.rs
1//! The algorithm contracts every IronCrypto primitive implements.
2//!
3//! Two properties make these traits agent-friendly:
4//!
5//! 1. **Self-describing.** Every implementation carries an [`Algorithm::ID`]
6//! that resolves to an entry in the ontology, so a value at runtime can be
7//! traced back to its full machine-readable specification.
8//! 2. **Total.** Nothing panics on bad input; sizes are surfaced as associated
9//! constants *and* as ontology metadata, so an agent can validate a call
10//! before making it.
11
12use crate::Result;
13
14/// Links a concrete implementation to its ontology entry.
15///
16/// `ID` is the stable ontology identifier (for example `"sha2-256"`), and is
17/// the join key between runtime types and the machine-readable catalog exposed
18/// by `ic-ontology`.
19pub trait Algorithm {
20 /// Stable ontology identifier for this algorithm.
21 const ID: &'static str;
22 /// Human-facing display name (for example `"SHA-256"`).
23 const NAME: &'static str;
24}
25
26/// A cryptographic hash function with a fixed-length output.
27pub trait Digest: Algorithm + Clone + Default {
28 /// The fixed-size output buffer type, e.g. `[u8; 32]`.
29 type Output: AsRef<[u8]> + AsMut<[u8]> + Copy;
30
31 /// Output length in bytes.
32 const OUTPUT_LEN: usize;
33 /// Internal block (rate) length in bytes — required by HMAC and KMAC.
34 const BLOCK_LEN: usize;
35
36 /// Create a fresh, empty hasher.
37 fn new() -> Self {
38 Self::default()
39 }
40
41 /// Absorb more input. May be called any number of times.
42 fn update(&mut self, data: &[u8]);
43
44 /// Consume the hasher and produce the digest.
45 fn finalize(self) -> Self::Output;
46
47 /// One-shot convenience: hash `data` in a single call.
48 fn digest(data: &[u8]) -> Self::Output {
49 let mut h = Self::new();
50 h.update(data);
51 h.finalize()
52 }
53}
54
55/// An extendable-output function (XOF) such as SHAKE128 / SHAKE256.
56pub trait Xof: Algorithm + Clone + Default {
57 /// Rate in bytes.
58 const BLOCK_LEN: usize;
59
60 /// Absorb more input.
61 fn update(&mut self, data: &[u8]);
62
63 /// Squeeze `out.len()` bytes of output, consuming the state.
64 fn finalize_xof(self, out: &mut [u8]);
65}
66
67/// A keyed message authentication code.
68pub trait Mac: Algorithm + Clone {
69 /// The fixed-size tag type.
70 type Tag: AsRef<[u8]> + AsMut<[u8]> + Copy;
71
72 /// Tag length in bytes.
73 const TAG_LEN: usize;
74
75 /// Create a MAC instance from a key of any length the algorithm accepts.
76 fn new(key: &[u8]) -> Result<Self>
77 where
78 Self: Sized;
79
80 /// Absorb more input.
81 fn update(&mut self, data: &[u8]);
82
83 /// Consume the instance and produce the tag.
84 fn finalize(self) -> Self::Tag;
85
86 /// One-shot authentication.
87 fn mac(key: &[u8], data: &[u8]) -> Result<Self::Tag>
88 where
89 Self: Sized,
90 {
91 let mut m = Self::new(key)?;
92 m.update(data);
93 Ok(m.finalize())
94 }
95
96 /// Constant-time tag verification.
97 ///
98 /// Returns [`crate::ErrorKind::AuthenticationFailed`] on mismatch and
99 /// never reveals *where* the tags diverged.
100 fn verify(key: &[u8], data: &[u8], tag: &[u8]) -> Result<()>
101 where
102 Self: Sized,
103 {
104 let expected = Self::mac(key, data)?;
105 if crate::ct::verify(expected.as_ref(), tag) {
106 Ok(())
107 } else {
108 Err(crate::err!(AuthenticationFailed, "mac tag"))
109 }
110 }
111}
112
113/// A fixed-width block cipher primitive (raw ECB core, for use inside modes).
114pub trait BlockCipher: Algorithm {
115 /// Block size in bytes.
116 const BLOCK_LEN: usize;
117 /// Accepted key length in bytes.
118 const KEY_LEN: usize;
119
120 /// Expand a key into a round-key schedule.
121 fn new(key: &[u8]) -> Result<Self>
122 where
123 Self: Sized;
124
125 /// Encrypt a single block in place.
126 fn encrypt_block(&self, block: &mut [u8]) -> Result<()>;
127
128 /// Decrypt a single block in place.
129 fn decrypt_block(&self, block: &mut [u8]) -> Result<()>;
130
131 /// Encrypt a whole number of blocks in place.
132 ///
133 /// The default implementation loops over [`encrypt_block`][Self::encrypt_block].
134 /// Backends with instruction-level parallelism override it: AES-NI has a
135 /// pipelined round instruction, so encrypting eight independent blocks at
136 /// once is several times faster than eight sequential calls. Counter-based
137 /// modes route through here for exactly that reason.
138 ///
139 /// Returns [`crate::ErrorKind::InvalidLength`] if `data` is not a whole
140 /// number of blocks.
141 fn encrypt_blocks(&self, data: &mut [u8]) -> Result<()> {
142 if data.len() % Self::BLOCK_LEN != 0 {
143 return Err(crate::err!(InvalidLength, "batch must be block-aligned"));
144 }
145 for block in data.chunks_mut(Self::BLOCK_LEN) {
146 self.encrypt_block(block)?;
147 }
148 Ok(())
149 }
150}
151
152/// An authenticated cipher with associated data.
153pub trait Aead: Algorithm {
154 /// Key length in bytes.
155 const KEY_LEN: usize;
156 /// Nonce length in bytes.
157 const NONCE_LEN: usize;
158 /// Authentication tag length in bytes.
159 const TAG_LEN: usize;
160
161 /// Bind a key to a cipher instance.
162 fn new(key: &[u8]) -> Result<Self>
163 where
164 Self: Sized;
165
166 /// Encrypt `in_out` in place and write the tag to `tag`.
167 fn seal_detached(
168 &self,
169 nonce: &[u8],
170 aad: &[u8],
171 in_out: &mut [u8],
172 tag: &mut [u8],
173 ) -> Result<()>;
174
175 /// Verify `tag` and decrypt `in_out` in place.
176 ///
177 /// On failure `in_out` is zeroized before returning, so a caller that
178 /// ignores the error cannot expose unauthenticated plaintext.
179 fn open_detached(&self, nonce: &[u8], aad: &[u8], in_out: &mut [u8], tag: &[u8]) -> Result<()>;
180}
181
182/// A key derivation function that expands keying material to a requested length.
183pub trait Kdf: Algorithm {
184 /// Derive `out.len()` bytes from the supplied inputs.
185 fn derive(secret: &[u8], salt: &[u8], info: &[u8], out: &mut [u8]) -> Result<()>;
186}
187
188/// A deterministic random bit generator (SP 800-90A).
189pub trait Drbg: Algorithm {
190 /// Instantiate from entropy input, a nonce, and an optional personalization
191 /// string.
192 fn instantiate(entropy: &[u8], nonce: &[u8], personalization: &[u8]) -> Result<Self>
193 where
194 Self: Sized;
195
196 /// Reseed with fresh entropy and optional additional input.
197 fn reseed(&mut self, entropy: &[u8], additional: &[u8]) -> Result<()>;
198
199 /// Fill `out` with generated bits, honouring the reseed interval.
200 fn generate(&mut self, additional: &[u8], out: &mut [u8]) -> Result<()>;
201}
202
203/// A source of random bytes suitable for key generation.
204pub trait RandomSource {
205 /// Fill `out` with random bytes.
206 fn fill(&mut self, out: &mut [u8]) -> Result<()>;
207}
208
209/// A Diffie-Hellman style key agreement scheme.
210pub trait KeyAgreement: Algorithm {
211 /// Private key (scalar) length in bytes.
212 const PRIVATE_KEY_LEN: usize;
213 /// Public key (encoded point) length in bytes.
214 const PUBLIC_KEY_LEN: usize;
215 /// Shared secret length in bytes.
216 const SHARED_SECRET_LEN: usize;
217
218 /// Compute the public key for a private key.
219 fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()>;
220
221 /// Compute the shared secret from our private key and their public key.
222 fn agree(private_key: &[u8], peer_public_key: &[u8], out: &mut [u8]) -> Result<()>;
223}
224
225/// A digital signature scheme.
226pub trait SignatureScheme: Algorithm {
227 /// Seed / private key length in bytes.
228 const PRIVATE_KEY_LEN: usize;
229 /// Public key length in bytes.
230 const PUBLIC_KEY_LEN: usize;
231 /// Signature length in bytes.
232 const SIGNATURE_LEN: usize;
233
234 /// Derive the public key from a private key.
235 fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()>;
236
237 /// Sign `message`, writing exactly [`Self::SIGNATURE_LEN`] bytes.
238 fn sign(private_key: &[u8], message: &[u8], signature: &mut [u8]) -> Result<()>;
239
240 /// Verify a signature, returning [`crate::ErrorKind::AuthenticationFailed`]
241 /// when it does not check out.
242 fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<()>;
243}
244
245/// A known-answer test an implementation runs to satisfy FIPS 140-3 CAST
246/// requirements.
247///
248/// Implemented by every approved algorithm so `ic-fips` can enumerate and drive
249/// the full self-test suite without hard-coding a list.
250pub trait SelfTest {
251 /// Run the algorithm's known-answer test.
252 ///
253 /// Returns [`crate::ErrorKind::SelfTestFailed`] if the computed value does
254 /// not match the embedded vector.
255 fn self_test() -> Result<()>;
256}