Skip to main content

encryption/hash/
belt.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2
3//! Belt-Hash(STB 34.101.31,白俄罗斯国家标准);输出 32 字节。
4
5use belt_hash::{BeltHash, Digest};
6
7use crate::contract::{Hasher, Identified};
8use crate::error::Result;
9
10/// Belt-Hash 哈希器(无状态,可复用)。
11#[derive(Debug, Default, Clone, Copy)]
12pub struct BeltHashHasher;
13
14impl BeltHashHasher {
15    pub const IDENTIFIER: &'static str = "belt-hash";
16
17    pub fn new() -> Self {
18        Self
19    }
20}
21
22impl Identified for BeltHashHasher {
23    fn identifier(&self) -> &str {
24        Self::IDENTIFIER
25    }
26}
27
28impl Hasher for BeltHashHasher {
29    fn digest(&self, data: &[u8]) -> Result<Vec<u8>> {
30        Ok(BeltHash::digest(data).to_vec())
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    /// STB 34.101.31-2020 附录 A.11 表 A.23 测试 A.23-1:标准测试数据 H 的
39    /// 前 13 字节(X = B194BAC80A08F53B366D008E58)。
40    const X: [u8; 13] = [
41        0xb1, 0x94, 0xba, 0xc8, 0x0a, 0x08, 0xf5, 0x3b, 0x36, 0x6d, 0x00, 0x8e, 0x58,
42    ];
43
44    #[test]
45    fn matches_stb_appendix_vector() {
46        let hasher = BeltHashHasher::new();
47        assert_eq!(
48            hasher.digest_hex(&X).unwrap(),
49            "abef9725d4c5a83597a367d14494cc2542f20f659ddfecc961a3ec550cba8c75"
50        );
51        assert_eq!(hasher.digest(&X).unwrap().len(), 32);
52    }
53
54    /// digest_hex 输出为 2×摘要长度的小写十六进制。
55    #[test]
56    fn digest_hex_is_lowercase_hex() {
57        let hex = BeltHashHasher::new().digest_hex(&X).unwrap();
58        assert_eq!(hex.len(), 64);
59        assert!(
60            hex.bytes()
61                .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
62        );
63    }
64
65    /// 弱检查:不同输入给出不同摘要。
66    #[test]
67    fn distinct_inputs_distinct_digests() {
68        let hasher = BeltHashHasher::new();
69        assert_ne!(hasher.digest(&X).unwrap(), hasher.digest(b"abc").unwrap());
70    }
71}