Skip to main content

ofd_core/
crypto.rs

1//! 杂凑算法与编码工具(供签名完整性校验使用)。
2//!
3//! OFD 数字签名(见 GB/T 33190—2016 第 18 章)在 `Signature.xml` 中以
4//! `References/Reference/CheckValue` 给出每个被保护文件的摘要值,摘要算法由
5//! `References@CheckMethod` 指定。国内电子发票/版式文件普遍采用国密 **SM3**
6//! (OID `1.2.156.10197.1.401`),`CheckValue` 为 SM3 摘要的 Base64 文本。
7//!
8//! 为避免引入额外的密码学依赖,这里给出自包含的 SM3(GB/T 32905—2016)实现
9//! 与标准 Base64 编码,足以完成“重新计算摘要并与 `CheckValue` 比对”的完整性
10//! 校验。注意:本模块**不**实现对签名值(`SignedValue.dat`,SM2 签名)的密码学
11//! 验签,那需要证书解析与椭圆曲线运算,超出基础库范围。
12
13/// SM3 初始向量 IV(见 GB/T 32905—2016 4.1)。
14const IV: [u32; 8] = [
15    0x7380_166f,
16    0x4914_b2b9,
17    0x1724_42d7,
18    0xda8a_0600,
19    0xa96f_30bc,
20    0x1631_38aa,
21    0xe38d_ee4d,
22    0xb0fb_0e4e,
23];
24
25/// 布尔置换 `P0`(见 4.4)。
26#[inline]
27fn p0(x: u32) -> u32 {
28    x ^ x.rotate_left(9) ^ x.rotate_left(17)
29}
30
31/// 布尔置换 `P1`(见 4.4),用于消息扩展。
32#[inline]
33fn p1(x: u32) -> u32 {
34    x ^ x.rotate_left(15) ^ x.rotate_left(23)
35}
36
37/// 计算输入数据的 SM3 摘要,返回 32 字节结果。
38pub fn sm3(data: &[u8]) -> [u8; 32] {
39    let mut v = IV;
40
41    // 消息填充(见 4.2):追加 `0x80`,补 `0`,末尾附加 64 位比特长度。
42    let bit_len = (data.len() as u64).wrapping_mul(8);
43    let mut msg = data.to_vec();
44    msg.push(0x80);
45    while msg.len() % 64 != 56 {
46        msg.push(0x00);
47    }
48    msg.extend_from_slice(&bit_len.to_be_bytes());
49
50    // 逐 512 比特分组迭代压缩(见 4.3)。
51    for block in msg.chunks_exact(64) {
52        compress(&mut v, block);
53    }
54
55    let mut out = [0u8; 32];
56    for (i, word) in v.iter().enumerate() {
57        out[i * 4..i * 4 + 4].copy_from_slice(&word.to_be_bytes());
58    }
59    out
60}
61
62/// 对单个 512 比特分组执行压缩函数 `CF`(见 4.3)。
63fn compress(v: &mut [u32; 8], block: &[u8]) {
64    // 消息扩展:生成 W[0..68] 与 W'[0..64](见 5.3.2)。
65    let mut w = [0u32; 68];
66    for i in 0..16 {
67        w[i] = u32::from_be_bytes([
68            block[i * 4],
69            block[i * 4 + 1],
70            block[i * 4 + 2],
71            block[i * 4 + 3],
72        ]);
73    }
74    for i in 16..68 {
75        w[i] = p1(w[i - 16] ^ w[i - 9] ^ w[i - 3].rotate_left(15))
76            ^ w[i - 13].rotate_left(7)
77            ^ w[i - 6];
78    }
79    let mut w1 = [0u32; 64];
80    for i in 0..64 {
81        w1[i] = w[i] ^ w[i + 4];
82    }
83
84    let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h] = *v;
85
86    for j in 0..64 {
87        // 常量 Tj:前 16 轮与后 48 轮不同(见 4.3)。
88        let t = if j < 16 {
89            0x79cc_4519u32
90        } else {
91            0x7a87_9d8au32
92        };
93        let ss1 = a
94            .rotate_left(12)
95            .wrapping_add(e)
96            .wrapping_add(t.rotate_left(j as u32))
97            .rotate_left(7);
98        let ss2 = ss1 ^ a.rotate_left(12);
99        // 布尔函数 FFj / GGj:前 16 轮为异或,后 48 轮为择多/选择(见 4.4)。
100        let (ff, gg) = if j < 16 {
101            (a ^ b ^ c, e ^ f ^ g)
102        } else {
103            ((a & b) | (a & c) | (b & c), (e & f) | (!e & g))
104        };
105        let tt1 = ff.wrapping_add(d).wrapping_add(ss2).wrapping_add(w1[j]);
106        let tt2 = gg.wrapping_add(h).wrapping_add(ss1).wrapping_add(w[j]);
107        d = c;
108        c = b.rotate_left(9);
109        b = a;
110        a = tt1;
111        h = g;
112        g = f.rotate_left(19);
113        f = e;
114        e = p0(tt2);
115    }
116
117    v[0] ^= a;
118    v[1] ^= b;
119    v[2] ^= c;
120    v[3] ^= d;
121    v[4] ^= e;
122    v[5] ^= f;
123    v[6] ^= g;
124    v[7] ^= h;
125}
126
127/// 标准 Base64 编码表(RFC 4648)。
128const B64: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
129
130/// 将字节序列编码为标准 Base64 文本(含 `=` 填充)。
131pub fn base64_encode(data: &[u8]) -> String {
132    let mut out = String::with_capacity(data.len().div_ceil(3) * 4);
133    for chunk in data.chunks(3) {
134        let b0 = chunk[0] as usize;
135        let b1 = chunk.get(1).copied().unwrap_or(0) as usize;
136        let b2 = chunk.get(2).copied().unwrap_or(0) as usize;
137        out.push(B64[b0 >> 2] as char);
138        out.push(B64[((b0 & 0x03) << 4) | (b1 >> 4)] as char);
139        out.push(if chunk.len() > 1 {
140            B64[((b1 & 0x0f) << 2) | (b2 >> 6)] as char
141        } else {
142            '='
143        });
144        out.push(if chunk.len() > 2 {
145            B64[b2 & 0x3f] as char
146        } else {
147            '='
148        });
149    }
150    out
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    /// GB/T 32905—2016 附录 A 标准示例:`abc` 的 SM3 摘要。
158    #[test]
159    fn sm3_abc() {
160        let digest = sm3(b"abc");
161        let hex: String = digest.iter().map(|b| format!("{b:02x}")).collect();
162        assert_eq!(
163            hex,
164            "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0"
165        );
166    }
167
168    /// 国标第二个示例:64 字节 `abcd...abcd` 的 SM3 摘要。
169    #[test]
170    fn sm3_long() {
171        let data = b"abcd".repeat(16);
172        let digest = sm3(&data);
173        let hex: String = digest.iter().map(|b| format!("{b:02x}")).collect();
174        assert_eq!(
175            hex,
176            "debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732"
177        );
178    }
179
180    #[test]
181    fn base64_basic() {
182        assert_eq!(base64_encode(b""), "");
183        assert_eq!(base64_encode(b"f"), "Zg==");
184        assert_eq!(base64_encode(b"fo"), "Zm8=");
185        assert_eq!(base64_encode(b"foo"), "Zm9v");
186        assert_eq!(base64_encode(b"foobar"), "Zm9vYmFy");
187    }
188
189    /// SM3 摘要的 Base64 文本即为 OFD `CheckValue` 的取值形式。
190    #[test]
191    fn checkvalue_form() {
192        // 与真实 OFD 样张一致:base64(sm3(bytes)) 长度为 44 且以 `=` 结尾。
193        let cv = base64_encode(&sm3(b"hello"));
194        assert_eq!(cv.len(), 44);
195        assert!(cv.ends_with('='));
196    }
197}