libcrux/
digest.rs

1//! # Hashing
2//!
3//! Depending on the platform and available features the most efficient implementation
4//! is chosen.
5//!
6//! ## SHA 2
7//!
8//! The HACL streaming implementations are used for all SHA2 variants on all platforms.
9//!
10//! ## SHA 3
11//!
12//! The portable HACL implementations are used unless running on an x64 CPU.
13//! On x64 CPUs the libjade implementation is used and if AVX2 is available, the
14//! optimised libjade implementation is used.
15
16use crate::hacl::{
17    blake2,
18    sha2::{
19        self,
20        streaming::{Sha224, Sha256, Sha384, Sha512},
21    },
22    sha3,
23};
24
25use libcrux_platform::{simd128_support, simd256_support};
26
27#[derive(Debug)]
28pub enum Error {
29    InvalidStateFinished,
30    ModeUnsupportedForStreaming,
31}
32
33/// The Digest Algorithm.
34#[derive(Copy, Clone, Debug, PartialEq)]
35#[repr(u32)]
36pub enum Algorithm {
37    Sha1 = 1,
38    Sha224 = 2,
39    Sha256 = 3,
40    Sha384 = 4,
41    Sha512 = 5,
42    Blake2s = 6,
43    Blake2b = 7,
44    Sha3_224 = 8,
45    Sha3_256 = 9,
46    Sha3_384 = 10,
47    Sha3_512 = 11,
48}
49
50impl From<u32> for Algorithm {
51    fn from(v: u32) -> Algorithm {
52        match v {
53            1 => Algorithm::Sha1,
54            2 => Algorithm::Sha224,
55            3 => Algorithm::Sha256,
56            4 => Algorithm::Sha384,
57            5 => Algorithm::Sha512,
58            6 => Algorithm::Blake2s,
59            7 => Algorithm::Blake2b,
60            8 => Algorithm::Sha3_224,
61            9 => Algorithm::Sha3_256,
62            10 => Algorithm::Sha3_384,
63            11 => Algorithm::Sha3_512,
64            _ => panic!("Unknown Digest mode {}", v),
65        }
66    }
67}
68
69impl From<Algorithm> for u32 {
70    fn from(v: Algorithm) -> u32 {
71        match v {
72            Algorithm::Sha1 => 1,
73            Algorithm::Sha224 => 2,
74            Algorithm::Sha256 => 3,
75            Algorithm::Sha384 => 4,
76            Algorithm::Sha512 => 5,
77            Algorithm::Blake2s => 6,
78            Algorithm::Blake2b => 7,
79            Algorithm::Sha3_224 => 8,
80            Algorithm::Sha3_256 => 9,
81            Algorithm::Sha3_384 => 10,
82            Algorithm::Sha3_512 => 11,
83        }
84    }
85}
86
87/// Returns the output size of a digest.
88pub const fn digest_size(mode: Algorithm) -> usize {
89    match mode {
90        Algorithm::Sha1 => 20,
91        Algorithm::Sha224 => 28,
92        Algorithm::Sha256 => 32,
93        Algorithm::Sha384 => 48,
94        Algorithm::Sha512 => 64,
95        Algorithm::Blake2s => 32,
96        Algorithm::Blake2b => 64,
97        Algorithm::Sha3_224 => 28,
98        Algorithm::Sha3_256 => 32,
99        Algorithm::Sha3_384 => 48,
100        Algorithm::Sha3_512 => 64,
101    }
102}
103
104impl Algorithm {
105    pub fn size(self) -> usize {
106        digest_size(self)
107    }
108}
109
110pub type Sha2_224Digest = [u8; digest_size(Algorithm::Sha224)];
111pub type Sha2_256Digest = [u8; digest_size(Algorithm::Sha256)];
112pub type Sha2_384Digest = [u8; digest_size(Algorithm::Sha384)];
113pub type Sha2_512Digest = [u8; digest_size(Algorithm::Sha512)];
114
115pub type Sha3_224Digest = [u8; digest_size(Algorithm::Sha3_224)];
116pub type Sha3_256Digest = [u8; digest_size(Algorithm::Sha3_256)];
117pub type Sha3_384Digest = [u8; digest_size(Algorithm::Sha3_384)];
118pub type Sha3_512Digest = [u8; digest_size(Algorithm::Sha3_512)];
119
120// Single-shot API
121
122macro_rules! sha3_impl {
123    ($fun_name:ident, $output:ty, $jasmin_fun:expr, $hacl_fun:expr) => {
124        #[cfg(all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))]
125        pub fn $fun_name(payload: &[u8]) -> $output {
126            // On x64 we use Jasmin for AVX2 and fallback.
127            $jasmin_fun(payload)
128        }
129
130        #[cfg(not(all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos"))))]
131        pub fn $fun_name(payload: &[u8]) -> $output {
132            // On all other platforms we use HACL
133            $hacl_fun(payload)
134        }
135    };
136}
137
138sha3_impl!(
139    sha3_224,
140    Sha3_224Digest,
141    crate::jasmin::sha3::sha3_224,
142    sha3::sha224
143);
144sha3_impl!(
145    sha3_256,
146    Sha3_256Digest,
147    crate::jasmin::sha3::sha3_256,
148    sha3::sha256
149);
150sha3_impl!(
151    sha3_384,
152    Sha3_384Digest,
153    crate::jasmin::sha3::sha3_384,
154    sha3::sha384
155);
156sha3_impl!(
157    sha3_512,
158    Sha3_512Digest,
159    crate::jasmin::sha3::sha3_512,
160    sha3::sha512
161);
162
163/// Create the digest for the given `data` and mode `alg`.
164/// The output has length [`digest_size(alg)`], i.e. blake2 returns a full-sized
165/// digest.
166///
167/// Note that this will return a vector on the heap. Use functions like [`sha2_256`]
168/// if you want an array.
169pub fn hash(alg: Algorithm, payload: &[u8]) -> Vec<u8> {
170    // Note that one-shot hacl functions are slower than streaming.
171    // So we only use streaming.
172    match alg {
173        Algorithm::Sha1 => todo!(),
174        Algorithm::Sha224 => sha2::sha224(payload).into(),
175        Algorithm::Sha256 => sha2::sha256(payload).into(),
176        Algorithm::Sha384 => sha2::sha384(payload).into(),
177        Algorithm::Sha512 => sha2::sha512(payload).into(),
178        Algorithm::Blake2s => blake2s(payload, &[]),
179        Algorithm::Blake2b => blake2b(payload, &[]),
180        Algorithm::Sha3_224 => sha3_224(payload).into(),
181        Algorithm::Sha3_256 => sha3_256(payload).into(),
182        Algorithm::Sha3_384 => sha3_384(payload).into(),
183        Algorithm::Sha3_512 => sha3_512(payload).into(),
184    }
185}
186
187#[cfg(simd128)]
188fn blake2s_128<const LEN: usize>(payload: &[u8], key: &[u8]) -> [u8; LEN] {
189    blake2::simd128::blake2s(payload, key)
190}
191
192#[cfg(not(simd128))]
193fn blake2s_128<const LEN: usize>(payload: &[u8], key: &[u8]) -> [u8; LEN] {
194    blake2::blake2s::<LEN>(payload, key)
195}
196
197/// A simple wrapper to multiplex
198fn blake2s(payload: &[u8], key: &[u8]) -> Vec<u8> {
199    const DIGEST_LEN: usize = digest_size(Algorithm::Blake2s);
200    if simd128_support() {
201        blake2s_128::<DIGEST_LEN>(payload, key)
202    } else {
203        blake2::blake2s::<DIGEST_LEN>(payload, key)
204    }
205    .into()
206}
207
208#[cfg(simd256)]
209fn blake2b_256<const LEN: usize>(payload: &[u8], key: &[u8]) -> [u8; LEN] {
210    blake2::simd256::blake2b(payload, key)
211}
212
213#[cfg(not(simd256))]
214fn blake2b_256<const LEN: usize>(payload: &[u8], key: &[u8]) -> [u8; LEN] {
215    blake2::blake2b::<LEN>(payload, key)
216}
217
218/// Blake2b
219fn blake2b(payload: &[u8], key: &[u8]) -> Vec<u8> {
220    const DIGEST_LEN: usize = digest_size(Algorithm::Blake2b);
221    if simd256_support() {
222        blake2b_256::<DIGEST_LEN>(payload, key)
223    } else {
224        blake2::blake2b::<DIGEST_LEN>(payload, key)
225    }
226    .into()
227}
228
229/// SHA2 224
230pub fn sha2_224(payload: &[u8]) -> Sha2_224Digest {
231    sha2::sha224(payload)
232}
233
234/// SHA2 256
235pub fn sha2_256(payload: &[u8]) -> Sha2_256Digest {
236    sha2::sha256(payload)
237}
238
239/// SHA2 384
240pub fn sha2_384(payload: &[u8]) -> Sha2_384Digest {
241    sha2::sha384(payload)
242}
243
244/// SHA2 512
245pub fn sha2_512(payload: &[u8]) -> Sha2_512Digest {
246    sha2::sha512(payload)
247}
248
249// Streaming API - This is the recommended one.
250macro_rules! impl_streaming {
251    ($name:ident, $state:ty, $result:ty) => {
252        pub struct $name {
253            state: $state,
254        }
255        impl $name {
256            /// Initialize a new digest state.
257            pub fn new() -> Self {
258                Self {
259                    state: <$state>::new(),
260                }
261            }
262
263            /// Add the `payload` to the digest.
264            pub fn update(&mut self, payload: &[u8]) {
265                self.state.update(payload);
266            }
267
268            /// Get the digest.
269            ///
270            /// Note that the digest state can be continued to be used, to extend the
271            /// digest.
272            pub fn finish(&mut self) -> $result {
273                self.state.finish()
274            }
275        }
276
277        impl Default for $name {
278            fn default() -> Self {
279                Self::new()
280            }
281        }
282    };
283}
284impl_streaming!(Sha2_224, Sha224, Sha2_224Digest);
285impl_streaming!(Sha2_256, Sha256, Sha2_256Digest);
286impl_streaming!(Sha2_384, Sha384, Sha2_384Digest);
287impl_streaming!(Sha2_512, Sha512, Sha2_512Digest);
288
289// SHAKE messages from SHA 3
290
291/// SHAKE 128
292///
293/// The caller must define the size of the output in the return type.
294pub fn shake128<const LEN: usize>(data: &[u8]) -> [u8; LEN] {
295    sha3::shake128(data)
296}
297
298/// SHAKE 256
299///
300/// The caller must define the size of the output in the return type.
301pub fn shake256<const LEN: usize>(data: &[u8]) -> [u8; LEN] {
302    sha3::shake256(data)
303}