dryoc/
generichash.rs

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