Skip to main content

dryoc/
generichash.rs

1//! # Generic hashing
2//!
3//! [`GenericHash`] implements libsodium's generic hashing with BLAKE2b. Without
4//! a key, it produces a general-purpose cryptographic hash. With a secret key,
5//! it acts as a message authentication code (MAC) or pseudorandom function
6//! (PRF). Keyed BLAKE2b is not HMAC.
7//!
8//! # Rustaceous API example, single-part interface
9//!
10//! ```
11//! use base64::Engine as _;
12//! use base64::engine::general_purpose;
13//! use dryoc::generichash::{GenericHash, Key};
14//!
15//! // The key type must be specified because `None` does not identify it.
16//! let hash =
17//!     GenericHash::hash_with_defaults_to_vec::<_, Key>(b"hello", None).expect("hash failed");
18//!
19//! assert_eq!(
20//!     general_purpose::STANDARD.encode(&hash),
21//!     "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8="
22//! );
23//! ```
24//!
25//! # Rustaceous API example, incremental interface
26//!
27//! ```
28//! use base64::Engine as _;
29//! use base64::engine::general_purpose;
30//! use dryoc::generichash::{GenericHash, Key};
31//!
32//! // The key type must be specified because `None` does not identify it.
33//! let mut hasher = GenericHash::new_with_defaults::<Key>(None).expect("new failed");
34//! hasher.update(b"hello");
35//! let hash = hasher.finalize_to_vec().expect("finalize failed");
36//!
37//! assert_eq!(
38//!     general_purpose::STANDARD.encode(&hash),
39//!     "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8="
40//! );
41//! ```
42
43use crate::classic::crypto_generichash::{
44    GenericHashState, crypto_generichash, crypto_generichash_final, crypto_generichash_init,
45    crypto_generichash_update,
46};
47use crate::constants::{CRYPTO_GENERICHASH_BYTES, CRYPTO_GENERICHASH_KEYBYTES};
48use crate::error::Error;
49pub use crate::types::*;
50
51/// Stack-allocated hash output of the recommended output length.
52pub type Hash = StackByteArray<CRYPTO_GENERICHASH_BYTES>;
53/// Stack-allocated secret key for use with the generic hash algorithm.
54pub type Key = StackByteArray<CRYPTO_GENERICHASH_KEYBYTES>;
55
56#[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
57#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
58pub mod protected {
59    //! # Protected memory type aliases for [`GenericHash`]
60    //!
61    //! Protected-memory aliases for generic-hash keys and outputs.
62    //!
63    //! ## Example
64    //!
65    //! ```
66    //! use dryoc::generichash::GenericHash;
67    //! use dryoc::generichash::protected::*;
68    //!
69    //! // Create a randomly generated key, lock it, protect it as read-only
70    //! let key = Key::generate_readonly_locked().expect("generate failed");
71    //! let input =
72    //!     HeapBytes::from_slice_into_readonly_locked(b"super secret input").expect("input failed");
73    //! let hash: Locked<Hash> = GenericHash::hash(&input, Some(&key)).expect("hash failed");
74    //! ```
75    use super::*;
76    pub use crate::protected::*;
77
78    /// Heap-allocated, page-aligned secret key for the generic hash algorithm,
79    /// for use with protected memory.
80    pub type Key = HeapByteArray<CRYPTO_GENERICHASH_KEYBYTES>;
81    /// Heap-allocated, page-aligned hash output for the generic hash algorithm,
82    /// for use with protected memory.
83    pub type Hash = HeapByteArray<CRYPTO_GENERICHASH_BYTES>;
84}
85
86/// Provides a generic hash function implementation based on Blake2b. Compatible
87/// with libsodium's generic hash.
88pub struct GenericHash<const KEY_LENGTH: usize, const OUTPUT_LENGTH: usize> {
89    state: GenericHashState,
90}
91
92impl<const KEY_LENGTH: usize, const OUTPUT_LENGTH: usize> GenericHash<KEY_LENGTH, OUTPUT_LENGTH> {
93    /// Returns a new incremental hasher with an optional secret `key`.
94    ///
95    /// # Errors
96    ///
97    /// Returns an error if `OUTPUT_LENGTH` or the length of `key` is outside
98    /// the range supported by libsodium's generic hash function.
99    pub fn new<Key: ByteArray<KEY_LENGTH>>(key: Option<&Key>) -> Result<Self, Error> {
100        Ok(Self {
101            state: crypto_generichash_init(key.map(|k| k.as_slice()), OUTPUT_LENGTH)?,
102        })
103    }
104
105    /// Updates the hasher state from `input`.
106    pub fn update<Input: Bytes + ?Sized>(&mut self, input: &Input) {
107        crypto_generichash_update(&mut self.state, input.as_slice())
108    }
109
110    /// Computes and returns the final hash value.
111    ///
112    /// # Errors
113    ///
114    /// Returns an error if the underlying BLAKE2b finalization rejects the
115    /// output. Initialization normally guarantees a valid output length.
116    pub fn finalize<Output: NewByteArray<OUTPUT_LENGTH>>(self) -> Result<Output, Error> {
117        let mut output = Output::new_byte_array();
118
119        crypto_generichash_final(self.state, output.as_mut_slice())?;
120
121        Ok(output)
122    }
123
124    /// Computes and returns the final hash value as a [`Vec`]. Provided for
125    /// convenience.
126    ///
127    /// # Errors
128    ///
129    /// Returns an error if the underlying BLAKE2b finalization rejects the
130    /// output. Initialization normally guarantees a valid output length.
131    pub fn finalize_to_vec(self) -> Result<Vec<u8>, Error> {
132        self.finalize()
133    }
134
135    /// Computes the hash of `input` with an optional secret `key`.
136    ///
137    /// The output length is determined by `Output`. Providing a key selects
138    /// keyed BLAKE2b, which can be used as a MAC or PRF.
139    ///
140    /// # Errors
141    ///
142    /// Returns an error if `OUTPUT_LENGTH` or the length of `key` is outside
143    /// the range supported by libsodium's generic hash function.
144    ///
145    /// # Example
146    ///
147    /// ```
148    /// use base64::Engine as _;
149    /// use base64::engine::general_purpose;
150    /// use dryoc::generichash::{GenericHash, Hash};
151    ///
152    /// let output: Hash =
153    ///     GenericHash::hash(b"hello", Some(b"a very secret key")).expect("hash failed");
154    ///
155    /// assert_eq!(
156    ///     general_purpose::STANDARD.encode(&output),
157    ///     "AECDe+XJsB6nOkbCsbS/OPXdzpcRm3AolW/Bg1LFY9A="
158    /// );
159    /// ```
160    pub fn hash<
161        Input: Bytes + ?Sized,
162        Key: ByteArray<KEY_LENGTH>,
163        Output: NewByteArray<OUTPUT_LENGTH>,
164    >(
165        input: &Input,
166        key: Option<&Key>,
167    ) -> Result<Output, Error> {
168        let mut output = Output::new_byte_array();
169        crypto_generichash(
170            output.as_mut_slice(),
171            input.as_slice(),
172            key.map(|k| k.as_slice()),
173        )?;
174        Ok(output)
175    }
176
177    /// Convenience wrapper for [`GenericHash::hash`].
178    ///
179    /// # Errors
180    ///
181    /// Returns an error under the same conditions as [`GenericHash::hash`].
182    pub fn hash_to_vec<Input: Bytes, Key: ByteArray<KEY_LENGTH>>(
183        input: &Input,
184        key: Option<&Key>,
185    ) -> Result<Vec<u8>, Error> {
186        Self::hash(input, key)
187    }
188}
189
190impl GenericHash<CRYPTO_GENERICHASH_KEYBYTES, CRYPTO_GENERICHASH_BYTES> {
191    /// Returns an instance of [`GenericHash`] with the default output and key
192    /// length parameters.
193    ///
194    /// # Errors
195    ///
196    /// The default lengths are valid, so this method does not return an error
197    /// for valid [`ByteArray`] implementations. Its return type matches the
198    /// generic initialization interface.
199    pub fn new_with_defaults<Key: ByteArray<CRYPTO_GENERICHASH_KEYBYTES>>(
200        key: Option<&Key>,
201    ) -> Result<Self, Error> {
202        Ok(Self {
203            state: crypto_generichash_init(key.map(|k| k.as_slice()), CRYPTO_GENERICHASH_BYTES)?,
204        })
205    }
206
207    /// Hashes `input` using `key`, with the default length parameters. Provided
208    /// for convenience.
209    ///
210    /// # Errors
211    ///
212    /// The default lengths are valid, so this method does not return an error
213    /// for valid [`ByteArray`] implementations. Its return type matches the
214    /// generic hashing interface.
215    pub fn hash_with_defaults<
216        Input: Bytes + ?Sized,
217        Key: ByteArray<CRYPTO_GENERICHASH_KEYBYTES>,
218        Output: NewByteArray<CRYPTO_GENERICHASH_BYTES>,
219    >(
220        input: &Input,
221        key: Option<&Key>,
222    ) -> Result<Output, Error> {
223        Self::hash(input, key)
224    }
225
226    /// Hashes `input` using `key`, with the default length parameters,
227    /// returning a [`Vec`]. Provided for convenience.
228    ///
229    /// # Errors
230    ///
231    /// The default lengths are valid, so this method does not return an error
232    /// for valid [`ByteArray`] implementations. Its return type matches the
233    /// generic hashing interface.
234    pub fn hash_with_defaults_to_vec<
235        Input: Bytes + ?Sized,
236        Key: ByteArray<CRYPTO_GENERICHASH_KEYBYTES>,
237    >(
238        input: &Input,
239        key: Option<&Key>,
240    ) -> Result<Vec<u8>, Error> {
241        Self::hash(input, key)
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    use super::*;
248
249    #[test]
250    fn test_generichash() {
251        use base64::Engine as _;
252        use base64::engine::general_purpose;
253
254        let mut hasher = GenericHash::new_with_defaults::<Key>(None).expect("new hash failed");
255        hasher.update(b"hello");
256
257        let output: Vec<u8> = hasher.finalize().expect("finalize failed");
258
259        assert_eq!(
260            general_purpose::STANDARD.encode(output),
261            "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8="
262        );
263
264        let mut hasher = GenericHash::new_with_defaults::<Key>(None).expect("new hash failed");
265        hasher.update(b"hello");
266
267        let output = hasher.finalize_to_vec().expect("finalize failed");
268
269        assert_eq!(
270            general_purpose::STANDARD.encode(output),
271            "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8="
272        );
273    }
274
275    #[test]
276    fn test_generichash_onetime() {
277        use base64::Engine as _;
278        use base64::engine::general_purpose;
279
280        let output: Hash =
281            GenericHash::hash(b"hello", Some(b"a very secret key")).expect("hash failed");
282
283        assert_eq!(
284            general_purpose::STANDARD.encode(&output),
285            "AECDe+XJsB6nOkbCsbS/OPXdzpcRm3AolW/Bg1LFY9A="
286        );
287
288        let output: Vec<u8> =
289            GenericHash::hash_with_defaults::<_, Key, _>(b"hello", None).expect("hash failed");
290
291        assert_eq!(
292            general_purpose::STANDARD.encode(output),
293            "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8="
294        );
295
296        let output =
297            GenericHash::hash_with_defaults_to_vec::<_, Key>(b"hello", None).expect("hash failed");
298
299        assert_eq!(
300            general_purpose::STANDARD.encode(output),
301            "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8="
302        );
303    }
304    #[test]
305    fn test_generichash_onetime_empty() {
306        use base64::Engine as _;
307        use base64::engine::general_purpose;
308
309        let output =
310            GenericHash::hash_with_defaults_to_vec::<_, Key>(&[], None).expect("hash failed");
311
312        assert_eq!(
313            general_purpose::STANDARD.encode(output),
314            "DldRwCblQ7Loqy6wYJnaodHl30d3j3eH+qtFzfEv46g="
315        );
316    }
317
318    #[test]
319    fn test_vectors() {
320        let test_vec = |input, key, hash| {
321            let input = hex::decode(input).expect("decode input");
322            let key = hex::decode(key).expect("decode key");
323            let expected_hash = hex::decode(hash).expect("decode hash");
324
325            let hash: Vec<u8> =
326                GenericHash::<64, 64>::hash(&input, Some(&key)).expect("hash failed");
327
328            assert_eq!(expected_hash, hash);
329        };
330
331        test_vec("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568");
332        test_vec("00", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd");
333        test_vec("0001", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965");
334        test_vec("000102", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "33d0825dddf7ada99b0e7e307104ad07ca9cfd9692214f1561356315e784f3e5a17e364ae9dbb14cb2036df932b77f4b292761365fb328de7afdc6d8998f5fc1");
335        test_vec("00010203", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "beaa5a3d08f3807143cf621d95cd690514d0b49efff9c91d24b59241ec0eefa5f60196d407048bba8d2146828ebcb0488d8842fd56bb4f6df8e19c4b4daab8ac");
336        test_vec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfc", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "a6213743568e3b3158b9184301f3690847554c68457cb40fc9a4b8cfd8d4a118c301a07737aeda0f929c68913c5f51c80394f53bff1c3e83b2e40ca97eba9e15");
337        test_vec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfd", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "d444bfa2362a96df213d070e33fa841f51334e4e76866b8139e8af3bb3398be2dfaddcbc56b9146de9f68118dc5829e74b0c28d7711907b121f9161cb92b69a9");
338        test_vec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461");
339    }
340}