Skip to main content

encryption/hash/
blake3.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2
3//! BLAKE3 哈希;默认输出 32 字节。
4//!
5//! BLAKE3 支持任意长度扩展输出(XOF),但 [`Hasher`] 契约返回固定长度摘要,
6//! 本库统一走官方默认的 32 字节(即扩展输出流的前 32 字节),不提供可变长度
7//! 接口。blake3 crate 为独立实现,不依赖 RustCrypto digest trait 代际。
8
9use crate::contract::{Hasher, Identified};
10use crate::error::Result;
11
12/// BLAKE3 哈希器(无状态,可复用)。
13#[derive(Debug, Default, Clone, Copy)]
14pub struct Blake3Hasher;
15
16impl Blake3Hasher {
17    pub const IDENTIFIER: &'static str = "blake3";
18
19    pub fn new() -> Self {
20        Self
21    }
22}
23
24impl Identified for Blake3Hasher {
25    fn identifier(&self) -> &str {
26        Self::IDENTIFIER
27    }
28}
29
30impl Hasher for Blake3Hasher {
31    fn digest(&self, data: &[u8]) -> Result<Vec<u8>> {
32        Ok(blake3::hash(data).as_bytes().to_vec())
33    }
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    /// 官方仓库 BLAKE3-team/BLAKE3 的 test_vectors/test_vectors.json 条目:
41    /// input_len 0(空输入)与 input_len 1(单字节 0x00)。官方文件给出 131 字节
42    /// 扩展输出,本库取默认 32 字节 = 其前 32 字节(两个文件字段的前 64 个十六
43    /// 进制字符)。
44    #[test]
45    fn matches_official_test_vectors() {
46        let hasher = Blake3Hasher::new();
47        assert_eq!(
48            hasher.digest_hex(b"").unwrap(),
49            "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"
50        );
51        assert_eq!(
52            hasher.digest_hex(&[0x00]).unwrap(),
53            "2d3adedff11b61f14c886e35afa036736dcd87a74d27b5c1510225d0f592e213"
54        );
55        assert_eq!(hasher.digest(b"").unwrap().len(), 32);
56    }
57
58    /// digest_hex 输出为 2×摘要长度的小写十六进制。
59    #[test]
60    fn digest_hex_is_lowercase_hex() {
61        let hex = Blake3Hasher::new().digest_hex(b"abc").unwrap();
62        assert_eq!(hex.len(), 64);
63        assert!(
64            hex.bytes()
65                .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
66        );
67    }
68
69    /// 弱检查:不同输入给出不同摘要。
70    #[test]
71    fn distinct_inputs_distinct_digests() {
72        let hasher = Blake3Hasher::new();
73        assert_ne!(
74            hasher.digest(b"abc").unwrap(),
75            hasher.digest(b"abd").unwrap()
76        );
77    }
78}