Skip to main content

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().is_multiple_of(Self::BLOCK_LEN) {
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}