crypto/blake3/mod.rs
1//! BLAKE3: a fast cryptographic hash function with support for keyed hashing
2//! (MAC), key derivation and extendable output (XOF).
3//!
4//! Pure-Rust implementation without heap allocation: the incremental [`Blake3`] state
5//! lives entirely on the stack (about 1.9 KiB).
6//!
7//! # Backends
8//!
9//! The tree structure of BLAKE3 lets several chunks be hashed in parallel:
10//!
11//! | Target | Backend | Chunks in parallel |
12//! |---------------|----------------------------------|--------------------|
13//! | `x86_64` | AVX2 (runtime-detected with `std`) | 8 |
14//! | `aarch64` | NEON (always available) | 4 |
15//! | `wasm32` | `simd128` (compile-time) | 4 |
16//! | anything else | portable scalar | 1 |
17//!
18//! On `x86_64`, runtime detection requires the `std` feature; without it the
19//! backend is chosen at compile time from `target_feature`. On `wasm32` the
20//! `simd128` kernel is compiled only when `-C target-feature=+simd128` is set.
21//! The portable fallback is written with 32-bit words so that it maps well onto
22//! 32-bit platforms.
23//!
24//! # One-shot API
25//!
26//! ```ignore
27//! use crypto::{Hasher, blake3::Blake3};
28//!
29//! let hash = Blake3::hash(b"hello world");
30//! ```
31//!
32//! # Incremental API
33//!
34//! ```ignore
35//! use crypto::{Hasher, blake3::Blake3};
36//!
37//! let mut hasher = Blake3::new();
38//! hasher.update(b"hello ");
39//! hasher.update(b"world");
40//! let hash = hasher.sum();
41//! ```
42//!
43//! # XOF (Extendable-Output Function) API
44//!
45//! ```ignore
46//! use crypto::{Hasher, blake3::Blake3};
47//!
48//! let mut out = [0u8; 64];
49//! let mut hasher = Blake3::new();
50//! hasher.update(b"hello world");
51//! hasher.finalize_xof().squeeze(&mut out);
52//! ```
53//!
54//! # Keyed hashing
55//!
56//! ```ignore
57//! use crypto::{Hasher, blake3::Blake3};
58//!
59//! let key = b"whats the Elvish word for friend";
60//! let mut hasher = Blake3::new_keyed(key);
61//! hasher.update(b"hello world");
62//! let hash = hasher.sum();
63//! ```
64//!
65//! The [`keyed_hash`](Blake3::keyed_hash) one-shot is also available:
66//!
67//! ```ignore
68//! use crypto::blake3::Blake3;
69//!
70//! let key = b"whats the Elvish word for friend";
71//! let hash = Blake3::keyed_hash(key, b"hello world");
72//! ```
73//!
74//! # Derive-key mode
75//!
76//! ```ignore
77//! use crypto::blake3::Blake3;
78//!
79//! let key = Blake3::derive_key("my app context", b"key material");
80//! let mut hasher = Blake3::new_keyed(&key);
81//! hasher.update(b"hello world");
82//! let hash = hasher.sum();
83//! ```
84//!
85//! # Security notes
86//!
87//! * Outputs are truncated to [`Hasher::OUTPUT_SIZE`] bytes; shorter outputs
88//! provide proportionally less security. Outputs longer than 32 bytes (via
89//! the XOF API) do not add any security margin.
90//! * The XOF seek position is not secret. An attacker who knows the message
91//! (and key, if any) can recover the offset of an extended output block, so
92//! do not treat the position as a secret.
93//! * Inputs are limited to less than 2^64 bytes. All counter arithmetic wraps
94//! rather than panicking, so a longer input (only reachable with more than
95//! 2^64 bytes of input) yields an unspecified digest instead of a crash.
96//! * [`Blake3::derive_key`] is **not** a password hash; use Argon2 or similar
97//! for passwords.
98
99mod platform;
100
101mod scalar;
102
103// aarch64 assumes that NEON instructions are always present
104#[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
105mod neon;
106
107// import if the target runtime supports the feature (wasm has no runtime detection)
108#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
109mod wasm32_simd128;
110
111// import if runtime CPU detection is enabled or if the target CPU supports the feature
112#[cfg(all(target_arch = "x86_64", any(feature = "std", target_feature = "avx2")))]
113mod avx2;
114
115mod hasher;
116
117#[cfg(test)]
118mod tests;
119
120use crate::{Bytes, Hash, Hasher, Xof};
121
122/// BLAKE3 hash function.
123///
124/// Implements the [`Hasher`] trait. See the [module documentation](self) for
125/// examples and backend details.
126///
127/// When the `zeroize` feature is enabled, the internal state (key words, chunk
128/// buffer and chaining-value stack) is overwritten with zeros when the hasher is
129/// dropped, so key material does not linger in memory.
130#[derive(Clone)]
131#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
132pub struct Blake3 {
133 hasher: hasher::Hasher,
134}
135
136impl Blake3 {
137 /// Create a keyed hasher (MAC) from a 32-byte key.
138 #[inline]
139 pub fn new_keyed(key: &[u8; 32]) -> Self {
140 Self {
141 hasher: hasher::Hasher::new_keyed(key),
142 }
143 }
144
145 /// Create a key-derivation hasher from a context string.
146 ///
147 /// The context string should be hardcoded, globally unique and
148 /// application-specific; see [`derive_key`](Blake3::derive_key).
149 #[inline]
150 pub fn new_derive_key(context: &str) -> Self {
151 Self {
152 hasher: hasher::Hasher::new_derive_key(context),
153 }
154 }
155
156 /// Derive a 32-byte subkey from `key_material` and a context string.
157 ///
158 /// The context string should be hardcoded, globally unique and
159 /// application-specific, e.g. `"example.com 2019-12-25 16:18:03 session
160 /// tokens v1"`. This function is not a password hash.
161 #[inline]
162 pub fn derive_key(context: &str, key_material: &[u8]) -> [u8; 32] {
163 hasher::derive_key(context, key_material)
164 }
165
166 /// One-shot keyed hash (MAC).
167 #[inline]
168 pub fn keyed_hash(key: &[u8; 32], input: &[u8]) -> Hash {
169 Hash(Bytes::<64>::from(hasher::keyed_hash(key, input)))
170 }
171
172 /// Finalize the hasher and return an extendable-output reader.
173 #[inline]
174 pub fn finalize_xof(self) -> Blake3XofReader {
175 Blake3XofReader {
176 reader: self.hasher.finalize_xof(),
177 }
178 }
179}
180
181impl Hasher for Blake3 {
182 const BLOCK_SIZE: usize = 64;
183 const OUTPUT_SIZE: usize = 32;
184
185 #[inline]
186 fn new() -> Self {
187 Self {
188 hasher: hasher::Hasher::new(),
189 }
190 }
191
192 #[inline]
193 fn update(&mut self, data: &[u8]) {
194 self.hasher.update(data);
195 }
196
197 #[inline]
198 fn sum(self) -> Hash {
199 Hash(Bytes::<64>::from(self.hasher.finalize()))
200 }
201
202 #[inline]
203 fn hash(data: &[u8]) -> Hash {
204 Hash(Bytes::<64>::from(hasher::hash(data)))
205 }
206}
207
208/// BLAKE3 extensible-output function (XOF) reader.
209///
210/// Produced by [`Blake3::finalize_xof`]. Implements the [`Xof`] trait.
211///
212/// **Note:** a BLAKE3 XOF is an *output* stream, so [`Xof::absorb`] is a no-op
213/// and any bytes passed to it are silently ignored. Add input through
214/// [`Blake3::update`](Hasher::update) before finalizing instead.
215///
216/// ```ignore
217/// use crypto::{blake3::Blake3, Xof};
218///
219/// let mut hasher = Blake3::new();
220/// hasher.update(b"hello world");
221/// let mut xof = hasher.finalize_xof();
222/// let mut out = [0u8; 64];
223/// xof.squeeze(&mut out);
224/// ```
225///
226/// When the `zeroize` feature is enabled, the retained root/node state — which
227/// for a keyed hash includes key material — is overwritten with zeros when the
228/// reader is dropped.
229#[derive(Clone)]
230#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
231pub struct Blake3XofReader {
232 reader: hasher::OutputReader,
233}
234
235impl Xof for Blake3XofReader {
236 /// No-op: a XOF reader cannot absorb data. Any bytes passed here are
237 /// ignored; pass them to [`Blake3::update`](Hasher::update) before
238 /// finalizing instead.
239 fn absorb(&mut self, _data: &[u8]) {}
240
241 /// Fill `out` with bytes.
242 fn squeeze(&mut self, out: &mut [u8]) {
243 self.reader.fill(out);
244 }
245}