Skip to main content

dryoc/classic/
crypto_generichash.rs

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