dryoc/classic/
crypto_generichash.rs

1//! # Generic hashing
2//!
3//! Implements libsodium's generic hashing functions, based on blake2b. Can also
4//! be used as an HMAC function, if a key is provided.
5//!
6//! For details, refer to [libsodium docs](https://libsodium.gitbook.io/doc/hashing/generic_hashing).
7//!
8//! # Classic API example, one-time interface
9//!
10//! ```
11//! use base64::Engine as _;
12//! use base64::engine::general_purpose;
13//! use dryoc::classic::crypto_generichash::*;
14//! use dryoc::constants::CRYPTO_GENERICHASH_BYTES;
15//!
16//! // Use the default hash length
17//! let mut output = [0u8; CRYPTO_GENERICHASH_BYTES];
18//! // Compute the hash using the one-time interface
19//! crypto_generichash(&mut output, b"a string of bytes", None).ok();
20//!
21//! assert_eq!(
22//!     general_purpose::STANDARD.encode(output),
23//!     "GdztjR9nU/rLh8VJt8e74+/seKTUnHgBexhGSpxLau0="
24//! );
25//! ```
26//!
27//! # Classic API example, incremental interface
28//!
29//! ```
30//! use base64::Engine as _;
31//! use base64::engine::general_purpose;
32//! use dryoc::classic::crypto_generichash::*;
33//! use dryoc::constants::CRYPTO_GENERICHASH_BYTES;
34//!
35//! // Use the default hash length
36//! let mut output = [0u8; CRYPTO_GENERICHASH_BYTES];
37//! // Initialize the state for the incremental interface
38//! let mut state = crypto_generichash_init(None, CRYPTO_GENERICHASH_BYTES).expect("state");
39//! // Update the hash
40//! crypto_generichash_update(&mut state, b"a string of bytes");
41//! // Finalize, compute the hash and copy it into `output`
42//! crypto_generichash_final(state, &mut output).expect("final failed");
43//!
44//! assert_eq!(
45//!     general_purpose::STANDARD.encode(output),
46//!     "GdztjR9nU/rLh8VJt8e74+/seKTUnHgBexhGSpxLau0="
47//! );
48//! ```
49use super::generichash_blake2b::*;
50use crate::blake2b;
51use crate::constants::CRYPTO_GENERICHASH_KEYBYTES;
52use crate::error::Error;
53
54/**
55Computes a hash from `input` and `key`, copying the result into `output`.
56
57| Parameter | Typical length | Minimum length | Maximum length |
58|-|-|-|-|
59| `output` | [`CRYPTO_GENERICHASH_BYTES`](crate::constants::CRYPTO_GENERICHASH_BYTES) | [`CRYPTO_GENERICHASH_BYTES_MIN`](crate::constants::CRYPTO_GENERICHASH_BYTES_MIN) | [ `CRYPTO_GENERICHASH_BYTES_MAX`](crate::constants::CRYPTO_GENERICHASH_BYTES_MAX) |
60| `key` | [`CRYPTO_GENERICHASH_KEYBYTES`] | [`CRYPTO_GENERICHASH_KEYBYTES_MIN`](crate::constants::CRYPTO_GENERICHASH_KEYBYTES_MIN) | [ `CRYPTO_GENERICHASH_KEYBYTES_MAX`](crate::constants::CRYPTO_GENERICHASH_KEYBYTES_MAX) |
61
62Compatible with libsodium's `crypto_generichash_final`
63*/
64#[inline]
65pub fn crypto_generichash(
66    output: &mut [u8],
67    input: &[u8],
68    key: Option<&[u8]>,
69) -> Result<(), Error> {
70    crypto_generichash_blake2b(output, input, key)
71}
72
73/// State struct for the generic hash algorithm, based on BLAKE2B.
74pub struct GenericHashState {
75    state: blake2b::State,
76}
77
78/**
79Initializes the state for the generic hash function using `outlen` for the expected hash output length, and optional `key`, returning it upon success.
80
81| Parameter | Typical length | Minimum length | Maximum length |
82|-|-|-|-|
83| `outlen` | [`CRYPTO_GENERICHASH_BYTES`](crate::constants::CRYPTO_GENERICHASH_BYTES) | [`CRYPTO_GENERICHASH_BYTES_MIN`](crate::constants::CRYPTO_GENERICHASH_BYTES_MIN) | [`CRYPTO_GENERICHASH_BYTES_MAX`](crate::constants::CRYPTO_GENERICHASH_BYTES_MAX) |
84| `key` | [`CRYPTO_GENERICHASH_KEYBYTES`] | [`CRYPTO_GENERICHASH_KEYBYTES_MIN`](crate::constants::CRYPTO_GENERICHASH_KEYBYTES_MIN) | [ `CRYPTO_GENERICHASH_KEYBYTES_MAX`](crate::constants::CRYPTO_GENERICHASH_KEYBYTES_MAX) |
85
86Equivalent to libsodium's `crypto_generichash_final`
87*/
88#[inline]
89pub fn crypto_generichash_init(
90    key: Option<&[u8]>,
91    outlen: usize,
92) -> Result<GenericHashState, Error> {
93    let state = crypto_generichash_blake2b_init(key, outlen, None, None)?;
94    Ok(GenericHashState { state })
95}
96
97/// Updates the internal hash state with `input`.
98///
99/// Equivalent to libsodium's `crypto_generichash_final`
100#[inline]
101pub fn crypto_generichash_update(state: &mut GenericHashState, input: &[u8]) {
102    crypto_generichash_blake2b_update(&mut state.state, input)
103}
104
105/// Finalizes the hash computation, copying the result into `output`. The length
106/// of `output` should match `outlen` from the call to
107/// [`crypto_generichash_init`].
108///
109/// Equivalent to libsodium's `crypto_generichash_final`
110#[inline]
111pub fn crypto_generichash_final(state: GenericHashState, output: &mut [u8]) -> Result<(), Error> {
112    crypto_generichash_blake2b_final(state.state, output)
113}
114
115/// Generates a random hash key using the OS's random number source.
116///
117/// Equivalent to libsodium's `crypto_generichash_keygen`
118pub fn crypto_generichash_keygen() -> [u8; CRYPTO_GENERICHASH_KEYBYTES] {
119    let mut key = [0u8; CRYPTO_GENERICHASH_KEYBYTES];
120    crate::rng::copy_randombytes(&mut key);
121    key
122}
123
124#[cfg(test)]
125mod tests {
126    use rand::TryRngCore;
127
128    use super::*;
129
130    #[test]
131    fn test_generichash() {
132        use libsodium_sys::crypto_generichash as so_crypto_generichash;
133        use rand_core::OsRng;
134
135        use crate::constants::{CRYPTO_GENERICHASH_BYTES_MAX, CRYPTO_GENERICHASH_BYTES_MIN};
136        use crate::rng::copy_randombytes;
137
138        for _ in 0..20 {
139            let outlen = CRYPTO_GENERICHASH_BYTES_MIN
140                + (OsRng.try_next_u32().unwrap() as usize
141                    % (CRYPTO_GENERICHASH_BYTES_MAX - CRYPTO_GENERICHASH_BYTES_MIN));
142            let mut output = vec![0u8; outlen];
143
144            let mut input = vec![0u8; (OsRng.try_next_u32().unwrap() % 5000) as usize];
145
146            copy_randombytes(&mut input);
147
148            let mut so_output = output.clone();
149
150            crypto_generichash(&mut output, &input, None).ok();
151
152            unsafe {
153                so_crypto_generichash(
154                    so_output.as_mut_ptr(),
155                    so_output.len(),
156                    input.as_ptr(),
157                    input.len() as u64,
158                    std::ptr::null(),
159                    0,
160                );
161            }
162
163            assert_eq!(output, so_output);
164        }
165    }
166
167    #[test]
168    fn test_generichash_key() {
169        use libsodium_sys::crypto_generichash as so_crypto_generichash;
170        use rand_core::OsRng;
171
172        use crate::constants::{
173            CRYPTO_GENERICHASH_BYTES_MAX, CRYPTO_GENERICHASH_BYTES_MIN,
174            CRYPTO_GENERICHASH_KEYBYTES_MAX, CRYPTO_GENERICHASH_KEYBYTES_MIN,
175        };
176        use crate::rng::copy_randombytes;
177
178        for _ in 0..20 {
179            let outlen = CRYPTO_GENERICHASH_BYTES_MIN
180                + (OsRng.try_next_u32().unwrap() as usize
181                    % (CRYPTO_GENERICHASH_BYTES_MAX - CRYPTO_GENERICHASH_BYTES_MIN));
182            let mut output = vec![0u8; outlen];
183
184            let mut input = vec![0u8; (OsRng.try_next_u32().unwrap() % 5000) as usize];
185
186            let keylen = CRYPTO_GENERICHASH_KEYBYTES_MIN
187                + (OsRng.try_next_u32().unwrap() as usize
188                    % (CRYPTO_GENERICHASH_KEYBYTES_MAX - CRYPTO_GENERICHASH_KEYBYTES_MIN));
189            let mut key = vec![0u8; keylen];
190
191            copy_randombytes(&mut input);
192            copy_randombytes(&mut key);
193
194            let mut so_output = output.clone();
195
196            crypto_generichash(&mut output, &input, Some(&key)).ok();
197
198            unsafe {
199                so_crypto_generichash(
200                    so_output.as_mut_ptr(),
201                    so_output.len(),
202                    input.as_ptr(),
203                    input.len() as u64,
204                    key.as_ptr(),
205                    key.len(),
206                );
207            }
208
209            assert_eq!(output, so_output);
210        }
211    }
212}