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}