//! # Generic hashing
//!
//! [`GenericHash`] implements libsodium's generic hashing, based on the Blake2b
//! algorithm. Can also be used as an HMAC function, if a key is provided.
//!
//! # Rustaceous API example, one-time interface
//!
//! ```
//! use base64::Engine as _;
//! use base64::engine::general_purpose;
//! use dryoc::generichash::{GenericHash, Key};
//!
//! // NOTE: The type for `key` param must be specified, the compiler cannot infer it when
//! // we pass `None`.
//! let hash =
//! GenericHash::hash_with_defaults_to_vec::<_, Key>(b"hello", None).expect("hash failed");
//!
//! assert_eq!(
//! general_purpose::STANDARD.encode(&hash),
//! "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8="
//! );
//! ```
//!
//! # Rustaceous API example, incremental interface
//!
//! ```
//! use base64::Engine as _;
//! use base64::engine::general_purpose;
//! use dryoc::generichash::{GenericHash, Key};
//!
//! // The compiler cannot infer the `Key` type, so we pass it below.
//! let mut hasher = GenericHash::new_with_defaults::<Key>(None).expect("new failed");
//! hasher.update(b"hello");
//! let hash = hasher.finalize_to_vec().expect("finalize failed");
//!
//! assert_eq!(
//! general_purpose::STANDARD.encode(&hash),
//! "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8="
//! );
//! ```
use crate::classic::crypto_generichash::{
GenericHashState, crypto_generichash, crypto_generichash_final, crypto_generichash_init,
crypto_generichash_update,
};
use crate::constants::{CRYPTO_GENERICHASH_BYTES, CRYPTO_GENERICHASH_KEYBYTES};
use crate::error::Error;
pub use crate::types::*;
/// Stack-allocated hash output of the recommended output length.
pub type Hash = StackByteArray<CRYPTO_GENERICHASH_BYTES>;
/// Stack-allocated secret key for use with the generic hash algorithm.
pub type Key = StackByteArray<CRYPTO_GENERICHASH_KEYBYTES>;
#[cfg(any(feature = "nightly", all(doc, not(doctest))))]
#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))]
pub mod protected {
//! # Protected memory type aliases for [`GenericHash`]
//!
//! This mod provides re-exports of type aliases for protected memory usage
//! with [`GenericHash`]. These type aliases are provided for
//! convenience.
//!
//! ## Example
//!
//! ```
//! use dryoc::generichash::GenericHash;
//! use dryoc::generichash::protected::*;
//!
//! // Create a randomly generated key, lock it, protect it as read-only
//! let key = Key::gen_readonly_locked().expect("gen failed");
//! let input =
//! HeapBytes::from_slice_into_readonly_locked(b"super secret input").expect("input failed");
//! let hash: Locked<Hash> = GenericHash::hash(&input, Some(&key)).expect("hash failed");
//! ```
use super::*;
pub use crate::protected::*;
/// Heap-allocated, page-aligned secret key for the generic hash algorithm,
/// for use with protected memory.
pub type Key = HeapByteArray<CRYPTO_GENERICHASH_KEYBYTES>;
/// Heap-allocated, page-aligned hash output for the generic hash algorithm,
/// for use with protected memory.
pub type Hash = HeapByteArray<CRYPTO_GENERICHASH_BYTES>;
}
/// Provides a generic hash function implementation based on Blake2b. Compatible
/// with libsodium's generic hash.
pub struct GenericHash<const KEY_LENGTH: usize, const OUTPUT_LENGTH: usize> {
state: GenericHashState,
}
impl<const KEY_LENGTH: usize, const OUTPUT_LENGTH: usize> GenericHash<KEY_LENGTH, OUTPUT_LENGTH> {
/// Returns a new hasher instance, with `key`.
pub fn new<Key: ByteArray<KEY_LENGTH>>(key: Option<&Key>) -> Result<Self, Error> {
Ok(Self {
state: crypto_generichash_init(key.map(|k| k.as_slice()), OUTPUT_LENGTH)?,
})
}
/// Updates the hasher state from `input`.
pub fn update<Input: Bytes + ?Sized>(&mut self, input: &Input) {
crypto_generichash_update(&mut self.state, input.as_slice())
}
/// Computes and returns the final hash value.
pub fn finalize<Output: NewByteArray<OUTPUT_LENGTH>>(self) -> Result<Output, Error> {
let mut output = Output::new_byte_array();
crypto_generichash_final(self.state, output.as_mut_slice())?;
Ok(output)
}
/// Computes and returns the final hash value as a [`Vec`]. Provided for
/// convenience.
pub fn finalize_to_vec(self) -> Result<Vec<u8>, Error> {
self.finalize()
}
/// Onet-time interface for the generic hash function. Computes the hash for
/// `input` with optional `key`. The output length is determined by the type
/// signature of `Output`.
///
/// # Example
///
/// ```
/// use base64::Engine as _;
/// use base64::engine::general_purpose;
/// use dryoc::generichash::{GenericHash, Hash};
///
/// let output: Hash =
/// GenericHash::hash(b"hello", Some(b"a very secret key")).expect("hash failed");
///
/// assert_eq!(
/// general_purpose::STANDARD.encode(&output),
/// "AECDe+XJsB6nOkbCsbS/OPXdzpcRm3AolW/Bg1LFY9A="
/// );
/// ```
pub fn hash<
Input: Bytes + ?Sized,
Key: ByteArray<KEY_LENGTH>,
Output: NewByteArray<OUTPUT_LENGTH>,
>(
input: &Input,
key: Option<&Key>,
) -> Result<Output, Error> {
let mut output = Output::new_byte_array();
crypto_generichash(
output.as_mut_slice(),
input.as_slice(),
key.map(|k| k.as_slice()),
)?;
Ok(output)
}
/// Convenience wrapper for [`GenericHash::hash`].
pub fn hash_to_vec<Input: Bytes, Key: ByteArray<KEY_LENGTH>>(
input: &Input,
key: Option<&Key>,
) -> Result<Vec<u8>, Error> {
Self::hash(input, key)
}
}
impl GenericHash<CRYPTO_GENERICHASH_KEYBYTES, CRYPTO_GENERICHASH_BYTES> {
/// Returns an instance of [`GenericHash`] with the default output and key
/// length parameters.
pub fn new_with_defaults<Key: ByteArray<CRYPTO_GENERICHASH_KEYBYTES>>(
key: Option<&Key>,
) -> Result<Self, Error> {
Ok(Self {
state: crypto_generichash_init(key.map(|k| k.as_slice()), CRYPTO_GENERICHASH_BYTES)?,
})
}
/// Hashes `input` using `key`, with the default length parameters. Provided
/// for convenience.
pub fn hash_with_defaults<
Input: Bytes + ?Sized,
Key: ByteArray<CRYPTO_GENERICHASH_KEYBYTES>,
Output: NewByteArray<CRYPTO_GENERICHASH_BYTES>,
>(
input: &Input,
key: Option<&Key>,
) -> Result<Output, Error> {
Self::hash(input, key)
}
/// Hashes `input` using `key`, with the default length parameters,
/// returning a [`Vec`]. Provided for convenience.
pub fn hash_with_defaults_to_vec<
Input: Bytes + ?Sized,
Key: ByteArray<CRYPTO_GENERICHASH_KEYBYTES>,
>(
input: &Input,
key: Option<&Key>,
) -> Result<Vec<u8>, Error> {
Self::hash(input, key)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generichash() {
use base64::Engine as _;
use base64::engine::general_purpose;
let mut hasher = GenericHash::new_with_defaults::<Key>(None).expect("new hash failed");
hasher.update(b"hello");
let output: Vec<u8> = hasher.finalize().expect("finalize failed");
assert_eq!(
general_purpose::STANDARD.encode(output),
"Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8="
);
let mut hasher = GenericHash::new_with_defaults::<Key>(None).expect("new hash failed");
hasher.update(b"hello");
let output = hasher.finalize_to_vec().expect("finalize failed");
assert_eq!(
general_purpose::STANDARD.encode(output),
"Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8="
);
}
#[test]
fn test_generichash_onetime() {
use base64::Engine as _;
use base64::engine::general_purpose;
let output: Hash =
GenericHash::hash(b"hello", Some(b"a very secret key")).expect("hash failed");
assert_eq!(
general_purpose::STANDARD.encode(&output),
"AECDe+XJsB6nOkbCsbS/OPXdzpcRm3AolW/Bg1LFY9A="
);
let output: Vec<u8> =
GenericHash::hash_with_defaults::<_, Key, _>(b"hello", None).expect("hash failed");
assert_eq!(
general_purpose::STANDARD.encode(output),
"Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8="
);
let output =
GenericHash::hash_with_defaults_to_vec::<_, Key>(b"hello", None).expect("hash failed");
assert_eq!(
general_purpose::STANDARD.encode(output),
"Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8="
);
}
#[test]
fn test_generichash_onetime_empty() {
use base64::Engine as _;
use base64::engine::general_purpose;
let output =
GenericHash::hash_with_defaults_to_vec::<_, Key>(&[], None).expect("hash failed");
assert_eq!(
general_purpose::STANDARD.encode(output),
"DldRwCblQ7Loqy6wYJnaodHl30d3j3eH+qtFzfEv46g="
);
}
#[test]
fn test_vectors() {
let test_vec = |input, key, hash| {
let input = hex::decode(input).expect("decode input");
let key = hex::decode(key).expect("decode key");
let expected_hash = hex::decode(hash).expect("decode hash");
let hash: Vec<u8> =
GenericHash::<64, 64>::hash(&input, Some(&key)).expect("hash failed");
assert_eq!(expected_hash, hash);
};
test_vec("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568");
test_vec("00", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd");
test_vec("0001", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965");
test_vec("000102", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "33d0825dddf7ada99b0e7e307104ad07ca9cfd9692214f1561356315e784f3e5a17e364ae9dbb14cb2036df932b77f4b292761365fb328de7afdc6d8998f5fc1");
test_vec("00010203", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "beaa5a3d08f3807143cf621d95cd690514d0b49efff9c91d24b59241ec0eefa5f60196d407048bba8d2146828ebcb0488d8842fd56bb4f6df8e19c4b4daab8ac");
test_vec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfc", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "a6213743568e3b3158b9184301f3690847554c68457cb40fc9a4b8cfd8d4a118c301a07737aeda0f929c68913c5f51c80394f53bff1c3e83b2e40ca97eba9e15");
test_vec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfd", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "d444bfa2362a96df213d070e33fa841f51334e4e76866b8139e8af3bb3398be2dfaddcbc56b9146de9f68118dc5829e74b0c28d7711907b121f9161cb92b69a9");
test_vec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461");
}
}