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}