Skip to main content

encryption/hash/
blake2.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2
3//! BLAKE2(RFC 7693):BLAKE2b-512 输出 64 字节、BLAKE2s-256 输出 32 字节。
4//!
5//! 均为无密钥(unkeyed)模式;带密钥 / 加盐的 BLAKE2 不在本库范围。
6
7use blake2::{Blake2b512, Blake2s256, Digest};
8
9use crate::contract::{Hasher, Identified};
10use crate::error::Result;
11
12/// BLAKE2b-512 哈希器(无状态,可复用)。
13#[derive(Debug, Default, Clone, Copy)]
14pub struct Blake2b512Hasher;
15
16impl Blake2b512Hasher {
17    pub const IDENTIFIER: &'static str = "blake2b-512";
18
19    pub fn new() -> Self {
20        Self
21    }
22}
23
24impl Identified for Blake2b512Hasher {
25    fn identifier(&self) -> &str {
26        Self::IDENTIFIER
27    }
28}
29
30impl Hasher for Blake2b512Hasher {
31    fn digest(&self, data: &[u8]) -> Result<Vec<u8>> {
32        Ok(Blake2b512::digest(data).to_vec())
33    }
34}
35
36/// BLAKE2s-256 哈希器(无状态,可复用)。
37#[derive(Debug, Default, Clone, Copy)]
38pub struct Blake2s256Hasher;
39
40impl Blake2s256Hasher {
41    pub const IDENTIFIER: &'static str = "blake2s-256";
42
43    pub fn new() -> Self {
44        Self
45    }
46}
47
48impl Identified for Blake2s256Hasher {
49    fn identifier(&self) -> &str {
50        Self::IDENTIFIER
51    }
52}
53
54impl Hasher for Blake2s256Hasher {
55    fn digest(&self, data: &[u8]) -> Result<Vec<u8>> {
56        Ok(Blake2s256::digest(data).to_vec())
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    /// RFC 7693 附录 A 的 "abc" 向量:A.1 BLAKE2b-512、A.2 BLAKE2s-256。
65    #[test]
66    fn matches_rfc7693_appendix_vectors() {
67        assert_eq!(
68            Blake2b512Hasher::new().digest_hex(b"abc").unwrap(),
69            "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1\
70             7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"
71        );
72        assert_eq!(
73            Blake2s256Hasher::new().digest_hex(b"abc").unwrap(),
74            "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"
75        );
76        assert_eq!(Blake2b512Hasher::new().digest(b"abc").unwrap().len(), 64);
77        assert_eq!(Blake2s256Hasher::new().digest(b"abc").unwrap().len(), 32);
78    }
79
80    /// digest_hex 输出为 2×摘要长度的小写十六进制。
81    #[test]
82    fn digest_hex_is_lowercase_hex() {
83        let hex = Blake2b512Hasher::new().digest_hex(b"abc").unwrap();
84        assert_eq!(hex.len(), 128);
85        assert!(
86            hex.bytes()
87                .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
88        );
89
90        let hex = Blake2s256Hasher::new().digest_hex(b"abc").unwrap();
91        assert_eq!(hex.len(), 64);
92        assert!(
93            hex.bytes()
94                .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
95        );
96    }
97
98    /// 弱检查:不同输入给出不同摘要。
99    #[test]
100    fn distinct_inputs_distinct_digests() {
101        let hasher = Blake2b512Hasher::new();
102        assert_ne!(
103            hasher.digest(b"abc").unwrap(),
104            hasher.digest(b"abd").unwrap()
105        );
106
107        let hasher = Blake2s256Hasher::new();
108        assert_ne!(
109            hasher.digest(b"abc").unwrap(),
110            hasher.digest(b"abd").unwrap()
111        );
112    }
113}