Skip to main content

encryption/hash/
sha3.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2
3//! SHA-3(FIPS 202):SHA3-256 输出 32 字节、SHA3-512 输出 64 字节。
4//!
5//! 是 FIPS 202 标准化的 SHA-3(域分隔符 0x06),不是 Keccak 提交版
6//! (0x01 填充,SHAKE 的前身)——两者对同一输入给出不同摘要。
7
8use sha3::{Digest, Sha3_256, Sha3_512};
9
10use crate::contract::{Hasher, Identified};
11use crate::error::Result;
12
13/// SHA3-256 哈希器(无状态,可复用)。
14#[derive(Debug, Default, Clone, Copy)]
15pub struct Sha3_256Hasher;
16
17impl Sha3_256Hasher {
18    pub const IDENTIFIER: &'static str = "sha3-256";
19
20    pub fn new() -> Self {
21        Self
22    }
23}
24
25impl Identified for Sha3_256Hasher {
26    fn identifier(&self) -> &str {
27        Self::IDENTIFIER
28    }
29}
30
31impl Hasher for Sha3_256Hasher {
32    fn digest(&self, data: &[u8]) -> Result<Vec<u8>> {
33        Ok(Sha3_256::digest(data).to_vec())
34    }
35}
36
37/// SHA3-512 哈希器(无状态,可复用)。
38#[derive(Debug, Default, Clone, Copy)]
39pub struct Sha3_512Hasher;
40
41impl Sha3_512Hasher {
42    pub const IDENTIFIER: &'static str = "sha3-512";
43
44    pub fn new() -> Self {
45        Self
46    }
47}
48
49impl Identified for Sha3_512Hasher {
50    fn identifier(&self) -> &str {
51        Self::IDENTIFIER
52    }
53}
54
55impl Hasher for Sha3_512Hasher {
56    fn digest(&self, data: &[u8]) -> Result<Vec<u8>> {
57        Ok(Sha3_512::digest(data).to_vec())
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    /// FIPS 202 发布时给出的 "abc" 示例摘要(NIST SHA-3 标准示例值)。
66    #[test]
67    fn matches_nist_example_vectors() {
68        assert_eq!(
69            Sha3_256Hasher::new().digest_hex(b"abc").unwrap(),
70            "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"
71        );
72        assert_eq!(
73            Sha3_512Hasher::new().digest_hex(b"abc").unwrap(),
74            "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e\
75             10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0"
76        );
77        assert_eq!(Sha3_256Hasher::new().digest(b"abc").unwrap().len(), 32);
78        assert_eq!(Sha3_512Hasher::new().digest(b"abc").unwrap().len(), 64);
79    }
80
81    /// digest_hex 输出为 2×摘要长度的小写十六进制。
82    #[test]
83    fn digest_hex_is_lowercase_hex() {
84        let hex = Sha3_256Hasher::new().digest_hex(b"abc").unwrap();
85        assert_eq!(hex.len(), 64);
86        assert!(
87            hex.bytes()
88                .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
89        );
90
91        let hex = Sha3_512Hasher::new().digest_hex(b"abc").unwrap();
92        assert_eq!(hex.len(), 128);
93        assert!(
94            hex.bytes()
95                .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
96        );
97    }
98
99    /// 弱检查:不同输入给出不同摘要。
100    #[test]
101    fn distinct_inputs_distinct_digests() {
102        let hasher = Sha3_256Hasher::new();
103        assert_ne!(
104            hasher.digest(b"abc").unwrap(),
105            hasher.digest(b"abd").unwrap()
106        );
107
108        let hasher = Sha3_512Hasher::new();
109        assert_ne!(
110            hasher.digest(b"abc").unwrap(),
111            hasher.digest(b"abd").unwrap()
112        );
113    }
114}