1use core::convert::TryInto;
6
7const IV: [u32; 8] = [
8 0x7380_166f,
9 0x4914_b2b9,
10 0x1724_42d7,
11 0xda8a_0600,
12 0xa96f_30bc,
13 0x1631_38aa,
14 0xe38d_ee4d,
15 0xb0fb_0e4e,
16];
17
18const T_J_LOW: u32 = 0x79cc_4519; const T_J_HIGH: u32 = 0x7a87_9d8a; pub const DIGEST_SIZE: usize = 32;
23
24pub const BLOCK_SIZE: usize = 64;
26
27#[must_use]
29pub fn hash(message: &[u8]) -> [u8; DIGEST_SIZE] {
30 let mut hasher = Sm3::new();
31 hasher.update(message);
32 hasher.finalize()
33}
34
35#[derive(Clone, Debug)]
37pub struct Sm3 {
38 state: [u32; 8],
39 buffer: [u8; BLOCK_SIZE],
40 buffer_len: usize,
41 total_len: u64, }
43
44impl Default for Sm3 {
45 fn default() -> Self {
46 Self::new()
47 }
48}
49
50impl Sm3 {
51 #[must_use]
53 pub const fn new() -> Self {
54 Self {
55 state: IV,
56 buffer: [0u8; BLOCK_SIZE],
57 buffer_len: 0,
58 total_len: 0,
59 }
60 }
61
62 #[allow(clippy::missing_panics_doc)]
64 pub fn update(&mut self, mut data: &[u8]) {
65 self.total_len = self.total_len.wrapping_add(data.len() as u64);
66
67 if self.buffer_len > 0 {
69 let need = BLOCK_SIZE - self.buffer_len;
70 let take = need.min(data.len());
71 self.buffer[self.buffer_len..self.buffer_len + take].copy_from_slice(&data[..take]);
72 self.buffer_len += take;
73 data = &data[take..];
74 if self.buffer_len == BLOCK_SIZE {
75 let block = self.buffer;
76 compress(&mut self.state, &block);
77 self.buffer_len = 0;
78 }
79 }
80
81 while data.len() >= BLOCK_SIZE {
83 let (block, rest) = data.split_at(BLOCK_SIZE);
84 compress(
85 &mut self.state,
86 block.try_into().expect("BLOCK_SIZE-len slice"),
87 );
88 data = rest;
89 }
90
91 if !data.is_empty() {
93 self.buffer[..data.len()].copy_from_slice(data);
94 self.buffer_len = data.len();
95 }
96 }
97
98 #[must_use]
100 pub fn finalize(mut self) -> [u8; DIGEST_SIZE] {
101 let bit_len = self.total_len.wrapping_mul(8);
103 self.buffer[self.buffer_len] = 0x80;
104 self.buffer_len += 1;
105
106 if self.buffer_len > BLOCK_SIZE - 8 {
107 for byte in &mut self.buffer[self.buffer_len..] {
110 *byte = 0;
111 }
112 let block = self.buffer;
113 compress(&mut self.state, &block);
114 self.buffer = [0u8; BLOCK_SIZE];
115 self.buffer_len = 0;
116 }
117
118 for byte in &mut self.buffer[self.buffer_len..BLOCK_SIZE - 8] {
119 *byte = 0;
120 }
121 self.buffer[BLOCK_SIZE - 8..].copy_from_slice(&bit_len.to_be_bytes());
122 let block = self.buffer;
123 compress(&mut self.state, &block);
124
125 let mut out = [0u8; DIGEST_SIZE];
126 for (i, w) in self.state.iter().enumerate() {
127 out[i * 4..(i + 1) * 4].copy_from_slice(&w.to_be_bytes());
128 }
129 out
130 }
131}
132
133#[inline]
134const fn p0(x: u32) -> u32 {
135 x ^ x.rotate_left(9) ^ x.rotate_left(17)
136}
137#[inline]
138const fn p1(x: u32) -> u32 {
139 x ^ x.rotate_left(15) ^ x.rotate_left(23)
140}
141#[inline]
142const fn ff_low(x: u32, y: u32, z: u32) -> u32 {
143 x ^ y ^ z
144}
145#[inline]
146const fn ff_high(x: u32, y: u32, z: u32) -> u32 {
147 (x & y) | (x & z) | (y & z)
148}
149#[inline]
150const fn gg_low(x: u32, y: u32, z: u32) -> u32 {
151 x ^ y ^ z
152}
153#[inline]
154const fn gg_high(x: u32, y: u32, z: u32) -> u32 {
155 (x & y) | (!x & z)
156}
157
158#[allow(clippy::many_single_char_names)]
159fn compress(state: &mut [u32; 8], block: &[u8; BLOCK_SIZE]) {
160 let mut w = [0u32; 68];
162 for j in 0..16 {
163 w[j] = u32::from_be_bytes(block[j * 4..(j + 1) * 4].try_into().expect("4-byte slice"));
164 }
165 for j in 16..68 {
166 w[j] = p1(w[j - 16] ^ w[j - 9] ^ w[j - 3].rotate_left(15))
167 ^ w[j - 13].rotate_left(7)
168 ^ w[j - 6];
169 }
170 let mut wp = [0u32; 64];
171 for j in 0..64 {
172 wp[j] = w[j] ^ w[j + 4];
173 }
174
175 let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h] = *state;
176
177 for j in 0..64 {
178 let t_j = if j < 16 { T_J_LOW } else { T_J_HIGH };
179 #[allow(clippy::cast_possible_truncation)]
180 let ss1 = a
181 .rotate_left(12)
182 .wrapping_add(e)
183 .wrapping_add(t_j.rotate_left((j % 32) as u32))
184 .rotate_left(7);
185 let ss2 = ss1 ^ a.rotate_left(12);
186 let (ff, gg) = if j < 16 {
187 (ff_low(a, b, c), gg_low(e, f, g))
188 } else {
189 (ff_high(a, b, c), gg_high(e, f, g))
190 };
191 let tt1 = ff.wrapping_add(d).wrapping_add(ss2).wrapping_add(wp[j]);
192 let tt2 = gg.wrapping_add(h).wrapping_add(ss1).wrapping_add(w[j]);
193 d = c;
194 c = b.rotate_left(9);
195 b = a;
196 a = tt1;
197 h = g;
198 g = f.rotate_left(19);
199 f = e;
200 e = p0(tt2);
201 }
202
203 state[0] ^= a;
204 state[1] ^= b;
205 state[2] ^= c;
206 state[3] ^= d;
207 state[4] ^= e;
208 state[5] ^= f;
209 state[6] ^= g;
210 state[7] ^= h;
211}
212
213#[cfg(feature = "digest-traits")]
214mod digest_impl {
215 use super::{DIGEST_SIZE, Sm3};
221 use digest::{
222 FixedOutput, FixedOutputReset, HashMarker, Output, OutputSizeUser, Reset, Update,
223 consts::U32,
224 };
225
226 impl HashMarker for Sm3 {}
227
228 impl OutputSizeUser for Sm3 {
229 type OutputSize = U32;
230 }
231
232 impl Update for Sm3 {
233 fn update(&mut self, data: &[u8]) {
234 Self::update(self, data);
235 }
236 }
237
238 impl FixedOutput for Sm3 {
239 fn finalize_into(self, out: &mut Output<Self>) {
240 let digest: [u8; DIGEST_SIZE] = Self::finalize(self);
241 out.copy_from_slice(&digest);
242 }
243 }
244
245 impl Reset for Sm3 {
246 fn reset(&mut self) {
247 *self = Self::new();
248 }
249 }
250
251 impl FixedOutputReset for Sm3 {
252 fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
253 let mut taken = Self::new();
254 core::mem::swap(self, &mut taken);
255 let digest: [u8; DIGEST_SIZE] = Self::finalize(taken);
256 out.copy_from_slice(&digest);
257 }
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264 use hex_literal::hex;
265
266 #[test]
268 fn hash_empty() {
269 assert_eq!(
270 hash(&[]),
271 hex!("1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b"),
272 );
273 }
274
275 #[test]
277 fn hash_abc() {
278 assert_eq!(
279 hash(b"abc"),
280 hex!("66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0"),
281 );
282 }
283
284 #[test]
286 fn hash_sixteen_abcd() {
287 let input = b"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
288 assert_eq!(
289 hash(input),
290 hex!("debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732"),
291 );
292 }
293
294 #[test]
296 fn hash_sixty_three_zeroes() {
297 let zeroes = [0u8; 63];
298 assert_eq!(
299 hash(&zeroes),
300 hex!("5241dc10cb3c700e46446943d27b971fefa7e88115f866d6f83d502ff1bc06c2"),
301 );
302 }
303
304 #[test]
306 fn streaming_matches_one_shot() {
307 let mut h = Sm3::new();
308 h.update(b"ab");
309 h.update(b"c");
310 assert_eq!(h.finalize(), hash(b"abc"));
311 }
312}