ab_blake3/
single_block.rs

1//! BLAKE3 functions that process at most a single block.
2//!
3//! This module and submodules are copied with modifications from the official [`blake3`] crate, but
4//! is unlikely to be upstreamed.
5
6#[cfg(test)]
7mod tests;
8
9use crate::platform::{le_bytes_from_words_32, words_from_le_bytes_32};
10use crate::{
11    portable, BlockWords, CVWords, BLOCK_LEN, CHUNK_END, CHUNK_START, DERIVE_KEY_CONTEXT,
12    DERIVE_KEY_MATERIAL, IV, KEYED_HASH, KEY_LEN, OUT_LEN, ROOT,
13};
14use blake3::platform::Platform;
15
16// Hash a single block worth of values
17#[inline(always)]
18fn hash_block(input: &[u8], key: CVWords, flags: u8) -> Option<[u8; OUT_LEN]> {
19    // If the whole subtree is one block, hash it directly with a ChunkState.
20    if input.len() > BLOCK_LEN {
21        return None;
22    }
23
24    let mut cv = key;
25
26    let mut block = [0; BLOCK_LEN];
27    block[..input.len()].copy_from_slice(input);
28    Platform::detect().compress_in_place(
29        &mut cv,
30        &block,
31        input.len() as u8,
32        0,
33        flags | CHUNK_START | CHUNK_END | ROOT,
34    );
35
36    Some(*le_bytes_from_words_32(&cv))
37}
38
39/// Hashing function for at most a single block worth of bytes.
40///
41/// Returns `None` if input length exceeds one block.
42#[inline]
43#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
44pub fn single_block_hash(input: &[u8]) -> Option<[u8; OUT_LEN]> {
45    hash_block(input, *IV, 0)
46}
47
48/// The keyed hash function for at most a single block worth of bytes.
49///
50/// Returns `None` if input length exceeds one block.
51#[inline]
52#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
53pub fn single_block_keyed_hash(key: &[u8; KEY_LEN], input: &[u8]) -> Option<[u8; OUT_LEN]> {
54    let key_words = words_from_le_bytes_32(key);
55    hash_block(input, key_words, KEYED_HASH)
56}
57
58// The key derivation function for at most a single block worth of bytes.
59//
60// Returns `None` if either context or key material length exceed one block.
61#[inline]
62#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
63pub fn single_block_derive_key(context: &str, key_material: &[u8]) -> Option<[u8; OUT_LEN]> {
64    let context_key = hash_block(context.as_bytes(), *IV, DERIVE_KEY_CONTEXT)?;
65    let context_key_words = words_from_le_bytes_32(&context_key);
66    hash_block(key_material, context_key_words, DERIVE_KEY_MATERIAL)
67}
68
69/// Hashing function for at most a single block worth of words using portable implementation.
70///
71/// This API operates on words and is GPU-friendly.
72///
73/// `num_bytes` specifies how many actual bytes are occupied by useful value in `input`. Bytes
74/// outside that must be set to `0`.
75///
76/// NOTE: If unused bytes are not set to `0` or invalid number of bytes is specified, it'll simply
77/// result in invalid hash.
78///
79/// [`words_from_le_bytes_32()`], [`words_from_le_bytes_64()`] and [`le_bytes_from_words_32()`] can
80/// be used to convert bytes to words and back if necessary.
81///
82/// [`words_from_le_bytes_64()`]: crate::words_from_le_bytes_64
83#[inline]
84#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
85pub fn single_block_hash_portable_words(input: &BlockWords, num_bytes: u32) -> CVWords {
86    let mut cv = *IV;
87
88    portable::compress_in_place(
89        &mut cv,
90        input,
91        num_bytes,
92        0,
93        (CHUNK_START | CHUNK_END | ROOT) as u32,
94    );
95
96    cv
97}