Skip to main content

encryption/hash/
streebog.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2
3//! Streebog(ГОСТ Р 34.11-2012 / RFC 6986):Streebog-256 输出 32 字节、
4//! Streebog-512 输出 64 字节。
5
6use streebog::{Digest, Streebog256, Streebog512};
7
8use crate::contract::{Hasher, Identified};
9use crate::error::Result;
10
11/// Streebog-256 哈希器(无状态,可复用)。
12#[derive(Debug, Default, Clone, Copy)]
13pub struct Streebog256Hasher;
14
15impl Streebog256Hasher {
16    pub const IDENTIFIER: &'static str = "streebog-256";
17
18    pub fn new() -> Self {
19        Self
20    }
21}
22
23impl Identified for Streebog256Hasher {
24    fn identifier(&self) -> &str {
25        Self::IDENTIFIER
26    }
27}
28
29impl Hasher for Streebog256Hasher {
30    fn digest(&self, data: &[u8]) -> Result<Vec<u8>> {
31        Ok(Streebog256::digest(data).to_vec())
32    }
33}
34
35/// Streebog-512 哈希器(无状态,可复用)。
36#[derive(Debug, Default, Clone, Copy)]
37pub struct Streebog512Hasher;
38
39impl Streebog512Hasher {
40    pub const IDENTIFIER: &'static str = "streebog-512";
41
42    pub fn new() -> Self {
43        Self
44    }
45}
46
47impl Identified for Streebog512Hasher {
48    fn identifier(&self) -> &str {
49        Self::IDENTIFIER
50    }
51}
52
53impl Hasher for Streebog512Hasher {
54    fn digest(&self, data: &[u8]) -> Result<Vec<u8>> {
55        Ok(Streebog512::digest(data).to_vec())
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    /// ГОСТ Р 34.11-2012 附录 A.1(RFC 6986 A.1)的 M1 向量:63 字节 ASCII
64    /// 数字串 "0123…012"(标准中记作 0x3231…,为小端字节序显示)。
65    /// 断言值为**本库输出序**(RFC 打印值的逐字节反转)——GOST 传统的字节序
66    /// 惯例,请勿按打印序「纠正」。
67    const M1: &[u8; 63] = b"012345678901234567890123456789012345678901234567890123456789012";
68
69    #[test]
70    fn matches_standard_m1_vector() {
71        assert_eq!(
72            Streebog256Hasher::new().digest_hex(M1).unwrap(),
73            "9d151eefd8590b89daa6ba6cb74af9275dd051026bb149a452fd84e5e57b5500"
74        );
75        assert_eq!(
76            Streebog512Hasher::new().digest_hex(M1).unwrap(),
77            "1b54d01a4af5b9d5cc3d86d68d285462b19abc2475222f35c085122be4ba1ffa\
78             00ad30f8767b3a82384c6574f024c311e2a481332b08ef7f41797891c1646f48"
79        );
80        assert_eq!(Streebog256Hasher::new().digest(M1).unwrap().len(), 32);
81        assert_eq!(Streebog512Hasher::new().digest(M1).unwrap().len(), 64);
82    }
83
84    /// digest_hex 输出为 2×摘要长度的小写十六进制。
85    #[test]
86    fn digest_hex_is_lowercase_hex() {
87        let hex = Streebog256Hasher::new().digest_hex(b"abc").unwrap();
88        assert_eq!(hex.len(), 64);
89        assert!(
90            hex.bytes()
91                .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
92        );
93
94        let hex = Streebog512Hasher::new().digest_hex(b"abc").unwrap();
95        assert_eq!(hex.len(), 128);
96        assert!(
97            hex.bytes()
98                .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
99        );
100    }
101
102    /// 弱检查:不同输入给出不同摘要。
103    #[test]
104    fn distinct_inputs_distinct_digests() {
105        let hasher = Streebog256Hasher::new();
106        assert_ne!(hasher.digest(M1).unwrap(), hasher.digest(b"abc").unwrap());
107
108        let hasher = Streebog512Hasher::new();
109        assert_ne!(hasher.digest(M1).unwrap(), hasher.digest(b"abc").unwrap());
110    }
111}