Skip to main content

crypto/
crypto.rs

1//! Pure Rust cryptography with `no_std` support and hardware / SIMD acceleration
2//! for `x86_64`, `aarch64` and WebAssembly.
3//!
4//! # ⚠️ Warning
5//!
6//! This crate has **not** undergone a third-party security audit yet. Use at your own risk.
7
8#![cfg_attr(not(feature = "std"), no_std)]
9
10#[cfg(feature = "alloc")]
11extern crate alloc;
12
13#[cfg(feature = "alloc")]
14use alloc::vec::Vec;
15
16mod bytes;
17
18pub mod aes;
19#[cfg(feature = "alloc")]
20pub mod argon2;
21pub mod ascon;
22pub mod blake2;
23pub mod blake3;
24pub mod chacha;
25pub mod curve25519;
26pub mod hkdf;
27pub mod hmac;
28pub mod mldsa;
29pub mod mlkem;
30pub mod poly1305;
31#[cfg(feature = "random")]
32pub mod random;
33pub mod sha2;
34pub mod sha3;
35pub mod xwing;
36
37#[cfg(feature = "alloc")]
38pub mod encoding;
39mod p_curves;
40pub mod pbkdf2;
41pub mod rsa;
42pub(crate) use bytes::Bytes;
43pub use bytes::Hash;
44pub use p_curves::{p224, p256, p384, p521};
45
46const MAX_HASH_BLOCK_SIZE: usize = 136;
47
48/// Maximum output size supported by [`Hash`].
49pub const MAX_HASH_OUTPUT_SIZE: usize = 64;
50
51////////////////////////////////////////////////////////////////////////////////////////////////////
52/// Errors
53////////////////////////////////////////////////////////////////////////////////////////////////////
54
55// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
56// pub enum Error {
57//     Hkdf(HkdfError),
58//     Aead(AeadError),
59//     EllipticCurve(EllipticCurveError),
60// }
61
62// impl core::fmt::Display for Error {
63//     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
64//         match self {
65//             Error::Hkdf(err) => write!(f, "{err}"),
66//             Error::Aead(err) => write!(f, "{err}"),
67//             Error::EllipticCurve(err) => write!(f, "{err}"),
68//         }
69//     }
70// }
71
72// #[cfg(feature = "std")]
73// impl std::error::Error for Error {}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum HashError {
77    TooLong,
78}
79
80impl core::fmt::Display for HashError {
81    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
82        match self {
83            HashError::TooLong => write!(f, "hash is too long"),
84        }
85    }
86}
87
88impl core::error::Error for HashError {}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum AeadError {
92    InvalidKey,
93    InvalidNonce,
94    InvalidCiphertext,
95    Unsupported,
96}
97
98impl core::fmt::Display for AeadError {
99    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
100        match self {
101            AeadError::InvalidKey => write!(f, "key is not valid"),
102            AeadError::InvalidNonce => write!(f, "nonce is not valid"),
103            AeadError::InvalidCiphertext => write!(f, "ciphertext is not valid"),
104            AeadError::Unsupported => write!(f, "operation is not supported"),
105        }
106    }
107}
108
109impl core::error::Error for AeadError {}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub enum EllipticCurveError {
113    InvalidKey,
114    Unspecified,
115    InvalidSignature,
116    InvalidSharedSecret,
117    Random(RandomError),
118}
119
120impl core::fmt::Display for EllipticCurveError {
121    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
122        match self {
123            EllipticCurveError::InvalidKey => write!(f, "key is not valid"),
124            EllipticCurveError::Unspecified => write!(f, "unknown error"),
125            EllipticCurveError::InvalidSignature => write!(f, "signature is not valid"),
126            EllipticCurveError::InvalidSharedSecret => write!(f, "shared secret is not valid"),
127            EllipticCurveError::Random(err) => write!(f, "{err}"),
128        }
129    }
130}
131
132impl core::error::Error for EllipticCurveError {}
133
134impl From<RandomError> for EllipticCurveError {
135    fn from(err: RandomError) -> Self {
136        EllipticCurveError::Random(err)
137    }
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141pub enum RsaError {
142    InvalidKey,
143    InvalidSignature,
144    NotSupported,
145    Unspecified,
146}
147
148impl core::fmt::Display for RsaError {
149    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
150        match self {
151            RsaError::InvalidKey => write!(f, "key is not valid"),
152            RsaError::InvalidSignature => write!(f, "signature is not valid"),
153            RsaError::NotSupported => write!(f, "key size not supported"),
154            RsaError::Unspecified => write!(f, "unknown error"),
155        }
156    }
157}
158
159impl core::error::Error for RsaError {}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
162pub enum HkdfError {
163    PrkIsTooShort(usize),
164    OutputIsTooLong,
165}
166
167impl core::fmt::Display for HkdfError {
168    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
169        match self {
170            HkdfError::PrkIsTooShort(_) => write!(f, "PRK is too short"),
171            HkdfError::OutputIsTooLong => {
172                write!(f, "HKDF output length exceeds RFC 5869 limit (255 * Hash's output size)")
173            }
174        }
175    }
176}
177
178impl core::error::Error for HkdfError {}
179
180/// Error returned when the operating system's random number generator fails.
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182pub enum RandomError {
183    /// No random number generator is available on this platform.
184    Unsupported,
185    /// The operating system's random number generator failed with this OS
186    /// error code.
187    OsError(i64),
188    /// An unexpected failure occurred while generating random data.
189    Unspecified,
190}
191
192impl core::fmt::Display for RandomError {
193    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
194        match self {
195            RandomError::Unsupported => write!(f, "random number generation is not supported on this platform"),
196            RandomError::OsError(code) => write!(f, "random number generation failed with OS error {code}"),
197            RandomError::Unspecified => write!(f, "random number generation failed"),
198        }
199    }
200}
201
202impl core::error::Error for RandomError {}
203
204#[cfg(feature = "random")]
205impl From<getrandom::Error> for RandomError {
206    fn from(err: getrandom::Error) -> Self {
207        if err == getrandom::Error::UNSUPPORTED {
208            RandomError::Unsupported
209        } else if let Some(code) = err.raw_os_error() {
210            RandomError::OsError(code as i64)
211        } else {
212            RandomError::Unspecified
213        }
214    }
215}
216
217////////////////////////////////////////////////////////////////////////////////////////////////////
218// Traits
219////////////////////////////////////////////////////////////////////////////////////////////////////
220
221pub trait StreamCipher {
222    fn xor_keystream(&mut self, in_out: &mut [u8]);
223}
224
225pub trait Aead {
226    const KEY_SIZE: usize;
227    const TAG_SIZE: usize;
228    const NONCE_SIZE: usize;
229
230    fn encrypt_in_place(&self, in_out: &mut [u8], nonce: &[u8], aad: &[u8]) -> Hash;
231
232    fn decrypt_in_place(&self, in_out: &mut [u8], nonce: &[u8], aad: &[u8], tag: &[u8]) -> Result<(), AeadError>;
233
234    #[cfg(feature = "alloc")]
235    fn encrypt(&self, plaintext: &[u8], nonce: &[u8], aad: &[u8]) -> Vec<u8> {
236        let mut ciphertext = Vec::with_capacity(plaintext.len() + Self::TAG_SIZE);
237        ciphertext.extend_from_slice(plaintext);
238
239        let tag = self.encrypt_in_place(&mut ciphertext, nonce, aad);
240        ciphertext.extend_from_slice(tag.as_ref());
241
242        return ciphertext;
243    }
244
245    #[cfg(feature = "alloc")]
246    fn decrypt(&self, ciphertext: &[u8], nonce: &[u8], aad: &[u8]) -> Result<Vec<u8>, AeadError> {
247        if ciphertext.len() < Self::TAG_SIZE {
248            return Err(AeadError::InvalidCiphertext);
249        }
250
251        let plaintext_length = ciphertext.len() - Self::TAG_SIZE;
252        let mut plaintext = Vec::with_capacity(plaintext_length);
253        plaintext.extend_from_slice(&ciphertext[..plaintext_length]);
254
255        self.decrypt_in_place(&mut plaintext, nonce, aad, &ciphertext[plaintext_length..])?;
256
257        return Ok(plaintext);
258    }
259}
260
261#[cfg(feature = "zeroize")]
262pub trait Zeroize: zeroize::Zeroize {}
263#[cfg(feature = "zeroize")]
264impl<T: zeroize::Zeroize> Zeroize for T {}
265
266#[cfg(not(feature = "zeroize"))]
267pub trait Zeroize {}
268#[cfg(not(feature = "zeroize"))]
269impl<T> Zeroize for T {}
270
271/// `Zeroize` is a no-op when the `zeroize` feature is not enabled.
272pub trait Hasher: Clone + Zeroize {
273    /// The internal block size of the hash function
274    const BLOCK_SIZE: usize;
275    /// The output size of the hash function.
276    ///
277    /// Must not exceed [`MAX_HASH_OUTPUT_SIZE`] (64), the fixed capacity of
278    /// [`Hash`]; implementors with a larger `OUTPUT_SIZE` will not compile when
279    /// used with functions that bound-check against this limit.
280    const OUTPUT_SIZE: usize;
281
282    fn new() -> Self;
283    fn update(&mut self, data: &[u8]);
284    fn sum(self) -> Hash;
285
286    #[inline]
287    fn hash(data: &[u8]) -> Hash {
288        let mut hasher = Self::new();
289        hasher.update(data);
290        return hasher.sum();
291    }
292}
293
294pub trait Xof: Send + Sync {
295    fn absorb(&mut self, data: &[u8]);
296    fn squeeze(&mut self, out: &mut [u8]);
297}