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