1#![cfg_attr(not(test), no_std)]
2mod consts;
3mod util;
4
5use consts::*;
6pub use consts::{DIGEST_LEN, INPUT_BUFFER_LEN};
7
8#[derive(Debug)]
9pub struct Context {
10 pub size: u64,
12 pub input: [u8; INPUT_BUFFER_LEN],
16 digest: [u8; DIGEST_LEN],
18 buffer: [u32; 4],
20}
21
22impl Context {
23 pub fn new() -> Self {
24 Self {
25 size: 0,
26 input: [0; INPUT_BUFFER_LEN],
27 digest: [0; DIGEST_LEN],
28 buffer: [A, B, C, D],
29 }
30 }
31
32 pub fn read(&mut self, buf: &[u8]) {
40 let mut offset = (self.size % BLOCK_SIZE as u64) as usize;
41 self.size += buf.len() as u64;
42 for i in 0..buf.len() {
43 self.input[offset] = buf[i];
44 offset += 1;
45 offset %= BLOCK_SIZE;
46 if offset == 0 {
47 self.step();
48 }
49 }
50 }
51
52 pub fn step(&mut self) {
54 let [mut a, mut b, mut c, mut d] = self.buffer;
55 let mut e: u32;
56 let mut g: usize;
57 for i in 0..BLOCK_SIZE {
58 if i < 16 {
59 e = util::f(b, c, d);
60 g = i;
61 } else if i < 32 {
62 e = util::g(b, c, d);
63 g = ((i * 5) + 1) % 16;
64 } else if i < 48 {
65 e = util::h(b, c, d);
66 g = ((i * 3) + 5) % 16;
67 } else {
68 e = util::i(b, c, d);
69 g = (i * 7) % 16;
70 }
71 g *= 4;
72
73 let mut u32_input: u32 = 0;
75 u32_input |= (self.input[g + 3] as u32) << 24;
76 u32_input |= (self.input[g + 2] as u32) << 16;
77 u32_input |= (self.input[g + 1] as u32) << 8;
78 u32_input |= self.input[g] as u32;
79
80 let f = a.wrapping_add(e).wrapping_add(K[i]).wrapping_add(u32_input);
81 a = d;
82 d = c;
83 c = b;
84 b = b.wrapping_add(util::rotate_u32_left(f, S[i]));
85 }
86
87 self.buffer[0] = self.buffer[0].wrapping_add(a);
89 self.buffer[1] = self.buffer[1].wrapping_add(b);
90 self.buffer[2] = self.buffer[2].wrapping_add(c);
91 self.buffer[3] = self.buffer[3].wrapping_add(d);
92 }
93
94 pub fn finish(mut self) -> [u8; DIGEST_LEN] {
105 let offset = (self.size % (BLOCK_SIZE as u64)) as usize;
107 let padding_len: usize = if offset < 56 {
108 56 - offset
109 } else {
110 (56 + BLOCK_SIZE) - offset
111 };
112 self.read(&PADDING[..padding_len]);
113 self.size -= padding_len as u64;
114
115 self.input[(INPUT_BUFFER_LEN - 8)..]
117 .copy_from_slice((self.size * 8).to_ne_bytes().as_slice());
118 self.step();
119
120 for i in 0..4 {
122 self.digest[i * 4] = (self.buffer[i] & 0x000000FF) as u8;
123 self.digest[(i * 4) + 1] = ((self.buffer[i] & 0x0000FF00) >> 8) as u8;
124 self.digest[(i * 4) + 2] = ((self.buffer[i] & 0x00FF0000) >> 16) as u8;
125 self.digest[(i * 4) + 3] = ((self.buffer[i] & 0xFF000000) >> 24) as u8;
126 }
127 self.digest
128 }
129}
130
131#[cfg(test)]
132mod test {
133 use super::Context;
134
135 fn compute_string(bytes: &[u8]) -> String {
136 let mut ctx = Context::new();
137 ctx.read(bytes);
138 let digest = ctx.finish();
139 digest
140 .iter()
141 .map(|x| format!("{:02x}", x))
142 .collect::<String>()
143 }
144
145 macro_rules! hash_eq {
146 ($input:expr, $hash:expr) => {
147 assert_eq!(compute_string($input).as_str(), $hash)
148 };
149 }
150
151 #[test]
152 fn empty() {
153 hash_eq!(b"", "d41d8cd98f00b204e9800998ecf8427e")
154 }
155
156 #[test]
157 fn a() {
158 hash_eq!(b"a", "0cc175b9c0f1b6a831c399e269772661")
159 }
160
161 #[test]
162 fn abc() {
163 hash_eq!(b"abc", "900150983cd24fb0d6963f7d28e17f72")
164 }
165
166 #[test]
167 fn abcdefghijklmnopqrstuvwxyz() {
168 hash_eq!(
169 b"abcdefghijklmnopqrstuvwxyz",
170 "c3fcd3d76192e4007dfb496cca67e13b"
171 )
172 }
173
174 #[test]
175 fn foo() {
176 hash_eq!(b"foo", "acbd18db4cc2f85cedef654fccc4a4d8")
177 }
178
179 #[test]
180 fn bar() {
181 hash_eq!(b"bar", "37b51d194a7513e45b56f6524f2d51f2")
182 }
183
184 #[test]
185 fn baz() {
186 hash_eq!(b"baz", "73feffa4b7f6bb68e44cf984c85f6e88")
187 }
188
189 #[test]
190 fn foobar() {
191 hash_eq!(b"foobar", "3858f62230ac3c915f300c664312c63f")
192 }
193
194 #[test]
195 fn foobarbaz() {
196 hash_eq!(b"foobarbaz", "6df23dc03f9b54cc38a0fc1483df6e21")
197 }
198
199 #[test]
200 fn quick_brown_fox() {
201 hash_eq!(
202 b"The quick brown fox jumps over the lazy dog",
203 "9e107d9d372bb6826bd81d3542a419d6"
204 )
205 }
206
207 #[test]
208 fn hello_world() {
209 hash_eq!(b"Hello, world", "bc6e6f16b8a077ef5fbc8d59d0b931b9")
210 }
211}