/rust/registry/src/index.crates.io-1949cf8c6b5b557f/sha1_smol-1.0.1/src/lib.rs
Line | Count | Source |
1 | | //! A minimal implementation of SHA1 for rust. |
2 | | //! |
3 | | //! This implementation supports no_std which is the default mode. The |
4 | | //! following features are available and can be optionally enabled: |
5 | | //! |
6 | | //! * ``serde``: when enabled the `Digest` type can be serialized. |
7 | | //! * ``std``: when enabled errors from this library implement `std::error::Error` |
8 | | //! and the `hexdigest` shortcut becomes available. |
9 | | //! |
10 | | //! ## Example |
11 | | //! |
12 | | //! ```rust |
13 | | //! let mut m = sha1_smol::Sha1::new(); |
14 | | //! m.update(b"Hello World!"); |
15 | | //! assert_eq!(m.digest().to_string(), |
16 | | //! "2ef7bde608ce5404e97d5f042f95f89f1c232871"); |
17 | | //! ``` |
18 | | //! |
19 | | //! The sha1 object can be updated multiple times. If you only need to use |
20 | | //! it once you can also use shortcuts (requires std): |
21 | | //! |
22 | | //! ``` |
23 | | //! # trait X { fn hexdigest(&self) -> &'static str { "2ef7bde608ce5404e97d5f042f95f89f1c232871" }} |
24 | | //! # impl X for sha1_smol::Sha1 {} |
25 | | //! # fn main() { |
26 | | //! assert_eq!(sha1_smol::Sha1::from("Hello World!").hexdigest(), |
27 | | //! "2ef7bde608ce5404e97d5f042f95f89f1c232871"); |
28 | | //! # } |
29 | | //! ``` |
30 | | |
31 | | #![no_std] |
32 | | #![deny(missing_docs)] |
33 | | #![allow(deprecated)] |
34 | | #![allow(clippy::double_parens)] |
35 | | #![allow(clippy::identity_op)] |
36 | | |
37 | | use core::cmp; |
38 | | use core::fmt; |
39 | | use core::hash; |
40 | | use core::str; |
41 | | |
42 | | mod simd; |
43 | | use crate::simd::*; |
44 | | |
45 | | #[cfg(feature = "alloc")] |
46 | | extern crate alloc; |
47 | | |
48 | | #[cfg(feature = "std")] |
49 | | extern crate std; |
50 | | |
51 | | /// The length of a SHA1 digest in bytes |
52 | | pub const DIGEST_LENGTH: usize = 20; |
53 | | |
54 | | /// Represents a Sha1 hash object in memory. |
55 | | #[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] |
56 | | pub struct Sha1 { |
57 | | state: Sha1State, |
58 | | blocks: Blocks, |
59 | | len: u64, |
60 | | } |
61 | | |
62 | | struct Blocks { |
63 | | len: u32, |
64 | | block: [u8; 64], |
65 | | } |
66 | | |
67 | | #[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Default)] |
68 | | struct Sha1State { |
69 | | state: [u32; 5], |
70 | | } |
71 | | |
72 | | /// Digest generated from a `Sha1` instance. |
73 | | /// |
74 | | /// A digest can be formatted to view the digest as a hex string, or the bytes |
75 | | /// can be extracted for later processing. |
76 | | /// |
77 | | /// To retrieve a hex string result call `to_string` on it (requires that std |
78 | | /// is available). |
79 | | /// |
80 | | /// If the `serde` feature is enabled a digest can also be serialized and |
81 | | /// deserialized. Likewise a digest can be parsed from a hex string. |
82 | | #[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy, Default)] |
83 | | pub struct Digest { |
84 | | data: Sha1State, |
85 | | } |
86 | | |
87 | | const DEFAULT_STATE: Sha1State = Sha1State { |
88 | | state: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0], |
89 | | }; |
90 | | |
91 | | #[inline(always)] |
92 | 0 | fn as_block(input: &[u8]) -> &[u8; 64] { |
93 | | unsafe { |
94 | 0 | assert!(input.len() == 64); |
95 | 0 | let arr: &[u8; 64] = &*(input.as_ptr() as *const [u8; 64]); |
96 | 0 | arr |
97 | | } |
98 | 0 | } |
99 | | |
100 | | impl Default for Sha1 { |
101 | 0 | fn default() -> Sha1 { |
102 | 0 | Sha1::new() |
103 | 0 | } |
104 | | } |
105 | | |
106 | | impl Sha1 { |
107 | | /// Creates an fresh sha1 hash object. |
108 | | /// |
109 | | /// This is equivalent to creating a hash with `Default::default`. |
110 | 0 | pub fn new() -> Sha1 { |
111 | 0 | Sha1 { |
112 | 0 | state: DEFAULT_STATE, |
113 | 0 | len: 0, |
114 | 0 | blocks: Blocks { |
115 | 0 | len: 0, |
116 | 0 | block: [0; 64], |
117 | 0 | }, |
118 | 0 | } |
119 | 0 | } |
120 | | |
121 | | /// Shortcut to create a sha1 from some bytes. |
122 | | /// |
123 | | /// This also lets you create a hash from a utf-8 string. This is equivalent |
124 | | /// to making a new Sha1 object and calling `update` on it once. |
125 | 0 | pub fn from<D: AsRef<[u8]>>(data: D) -> Sha1 { |
126 | 0 | let mut rv = Sha1::new(); |
127 | 0 | rv.update(data.as_ref()); |
128 | 0 | rv |
129 | 0 | } |
130 | | |
131 | | /// Resets the hash object to it's initial state. |
132 | 0 | pub fn reset(&mut self) { |
133 | 0 | self.state = DEFAULT_STATE; |
134 | 0 | self.len = 0; |
135 | 0 | self.blocks.len = 0; |
136 | 0 | } |
137 | | |
138 | | /// Update hash with input data. |
139 | 0 | pub fn update(&mut self, data: &[u8]) { |
140 | 0 | let len = &mut self.len; |
141 | 0 | let state = &mut self.state; |
142 | 0 | self.blocks.input(data, |block| { |
143 | 0 | *len += block.len() as u64; |
144 | 0 | state.process(block); |
145 | 0 | }) |
146 | 0 | } |
147 | | |
148 | | /// Retrieve digest result. |
149 | 0 | pub fn digest(&self) -> Digest { |
150 | 0 | let mut state = self.state; |
151 | 0 | let bits = (self.len + (self.blocks.len as u64)) * 8; |
152 | 0 | let extra = [ |
153 | 0 | (bits >> 56) as u8, |
154 | 0 | (bits >> 48) as u8, |
155 | 0 | (bits >> 40) as u8, |
156 | 0 | (bits >> 32) as u8, |
157 | 0 | (bits >> 24) as u8, |
158 | 0 | (bits >> 16) as u8, |
159 | 0 | (bits >> 8) as u8, |
160 | 0 | (bits >> 0) as u8, |
161 | 0 | ]; |
162 | 0 | let mut last = [0; 128]; |
163 | 0 | let blocklen = self.blocks.len as usize; |
164 | 0 | last[..blocklen].clone_from_slice(&self.blocks.block[..blocklen]); |
165 | 0 | last[blocklen] = 0x80; |
166 | | |
167 | 0 | if blocklen < 56 { |
168 | 0 | last[56..64].clone_from_slice(&extra); |
169 | 0 | state.process(as_block(&last[0..64])); |
170 | 0 | } else { |
171 | 0 | last[120..128].clone_from_slice(&extra); |
172 | 0 | state.process(as_block(&last[0..64])); |
173 | 0 | state.process(as_block(&last[64..128])); |
174 | 0 | } |
175 | | |
176 | 0 | Digest { data: state } |
177 | 0 | } |
178 | | |
179 | | /// Retrieve the digest result as hex string directly. |
180 | | /// |
181 | | /// (The function is only available if the `alloc` feature is enabled) |
182 | | #[cfg(feature = "alloc")] |
183 | | pub fn hexdigest(&self) -> std::string::String { |
184 | | use std::string::ToString; |
185 | | self.digest().to_string() |
186 | | } |
187 | | } |
188 | | |
189 | | impl Digest { |
190 | | /// Returns the 160 bit (20 byte) digest as a byte array. |
191 | 0 | pub fn bytes(&self) -> [u8; DIGEST_LENGTH] { |
192 | 0 | [ |
193 | 0 | (self.data.state[0] >> 24) as u8, |
194 | 0 | (self.data.state[0] >> 16) as u8, |
195 | 0 | (self.data.state[0] >> 8) as u8, |
196 | 0 | (self.data.state[0] >> 0) as u8, |
197 | 0 | (self.data.state[1] >> 24) as u8, |
198 | 0 | (self.data.state[1] >> 16) as u8, |
199 | 0 | (self.data.state[1] >> 8) as u8, |
200 | 0 | (self.data.state[1] >> 0) as u8, |
201 | 0 | (self.data.state[2] >> 24) as u8, |
202 | 0 | (self.data.state[2] >> 16) as u8, |
203 | 0 | (self.data.state[2] >> 8) as u8, |
204 | 0 | (self.data.state[2] >> 0) as u8, |
205 | 0 | (self.data.state[3] >> 24) as u8, |
206 | 0 | (self.data.state[3] >> 16) as u8, |
207 | 0 | (self.data.state[3] >> 8) as u8, |
208 | 0 | (self.data.state[3] >> 0) as u8, |
209 | 0 | (self.data.state[4] >> 24) as u8, |
210 | 0 | (self.data.state[4] >> 16) as u8, |
211 | 0 | (self.data.state[4] >> 8) as u8, |
212 | 0 | (self.data.state[4] >> 0) as u8, |
213 | 0 | ] |
214 | 0 | } |
215 | | } |
216 | | |
217 | | impl Blocks { |
218 | 0 | fn input<F>(&mut self, mut input: &[u8], mut f: F) |
219 | 0 | where |
220 | 0 | F: FnMut(&[u8; 64]), |
221 | | { |
222 | 0 | if self.len > 0 { |
223 | 0 | let len = self.len as usize; |
224 | 0 | let amt = cmp::min(input.len(), self.block.len() - len); |
225 | 0 | self.block[len..len + amt].clone_from_slice(&input[..amt]); |
226 | 0 | if len + amt == self.block.len() { |
227 | 0 | f(&self.block); |
228 | 0 | self.len = 0; |
229 | 0 | input = &input[amt..]; |
230 | 0 | } else { |
231 | 0 | self.len += amt as u32; |
232 | 0 | return; |
233 | | } |
234 | 0 | } |
235 | 0 | assert_eq!(self.len, 0); |
236 | 0 | for chunk in input.chunks(64) { |
237 | 0 | if chunk.len() == 64 { |
238 | 0 | f(as_block(chunk)) |
239 | 0 | } else { |
240 | 0 | self.block[..chunk.len()].clone_from_slice(chunk); |
241 | 0 | self.len = chunk.len() as u32; |
242 | 0 | } |
243 | | } |
244 | 0 | } |
245 | | } |
246 | | |
247 | | // Round key constants |
248 | | const K0: u32 = 0x5A827999u32; |
249 | | const K1: u32 = 0x6ED9EBA1u32; |
250 | | const K2: u32 = 0x8F1BBCDCu32; |
251 | | const K3: u32 = 0xCA62C1D6u32; |
252 | | |
253 | | /// Not an intrinsic, but gets the first element of a vector. |
254 | | #[inline] |
255 | 0 | fn sha1_first(w0: u32x4) -> u32 { |
256 | 0 | w0.0 |
257 | 0 | } |
258 | | |
259 | | /// Not an intrinsic, but adds a word to the first element of a vector. |
260 | | #[inline] |
261 | 0 | fn sha1_first_add(e: u32, w0: u32x4) -> u32x4 { |
262 | 0 | let u32x4(a, b, c, d) = w0; |
263 | 0 | u32x4(e.wrapping_add(a), b, c, d) |
264 | 0 | } |
265 | | |
266 | | /// Emulates `llvm.x86.sha1msg1` intrinsic. |
267 | 0 | fn sha1msg1(a: u32x4, b: u32x4) -> u32x4 { |
268 | 0 | let u32x4(_, _, w2, w3) = a; |
269 | 0 | let u32x4(w4, w5, _, _) = b; |
270 | 0 | a ^ u32x4(w2, w3, w4, w5) |
271 | 0 | } |
272 | | |
273 | | /// Emulates `llvm.x86.sha1msg2` intrinsic. |
274 | 0 | fn sha1msg2(a: u32x4, b: u32x4) -> u32x4 { |
275 | 0 | let u32x4(x0, x1, x2, x3) = a; |
276 | 0 | let u32x4(_, w13, w14, w15) = b; |
277 | | |
278 | 0 | let w16 = (x0 ^ w13).rotate_left(1); |
279 | 0 | let w17 = (x1 ^ w14).rotate_left(1); |
280 | 0 | let w18 = (x2 ^ w15).rotate_left(1); |
281 | 0 | let w19 = (x3 ^ w16).rotate_left(1); |
282 | | |
283 | 0 | u32x4(w16, w17, w18, w19) |
284 | 0 | } |
285 | | |
286 | | /// Emulates `llvm.x86.sha1nexte` intrinsic. |
287 | | #[inline] |
288 | 0 | fn sha1_first_half(abcd: u32x4, msg: u32x4) -> u32x4 { |
289 | 0 | sha1_first_add(sha1_first(abcd).rotate_left(30), msg) |
290 | 0 | } |
291 | | |
292 | | /// Emulates `llvm.x86.sha1rnds4` intrinsic. |
293 | | /// Performs 4 rounds of the message block digest. |
294 | 0 | fn sha1_digest_round_x4(abcd: u32x4, work: u32x4, i: i8) -> u32x4 { |
295 | | const K0V: u32x4 = u32x4(K0, K0, K0, K0); |
296 | | const K1V: u32x4 = u32x4(K1, K1, K1, K1); |
297 | | const K2V: u32x4 = u32x4(K2, K2, K2, K2); |
298 | | const K3V: u32x4 = u32x4(K3, K3, K3, K3); |
299 | | |
300 | 0 | match i { |
301 | 0 | 0 => sha1rnds4c(abcd, work + K0V), |
302 | 0 | 1 => sha1rnds4p(abcd, work + K1V), |
303 | 0 | 2 => sha1rnds4m(abcd, work + K2V), |
304 | 0 | 3 => sha1rnds4p(abcd, work + K3V), |
305 | 0 | _ => panic!("unknown icosaround index"), |
306 | | } |
307 | 0 | } |
308 | | |
309 | | /// Not an intrinsic, but helps emulate `llvm.x86.sha1rnds4` intrinsic. |
310 | 0 | fn sha1rnds4c(abcd: u32x4, msg: u32x4) -> u32x4 { |
311 | 0 | let u32x4(mut a, mut b, mut c, mut d) = abcd; |
312 | 0 | let u32x4(t, u, v, w) = msg; |
313 | 0 | let mut e = 0u32; |
314 | | |
315 | | macro_rules! bool3ary_202 { |
316 | | ($a:expr, $b:expr, $c:expr) => { |
317 | | ($c ^ ($a & ($b ^ $c))) |
318 | | }; |
319 | | } // Choose, MD5F, SHA1C |
320 | | |
321 | 0 | e = e |
322 | 0 | .wrapping_add(a.rotate_left(5)) |
323 | 0 | .wrapping_add(bool3ary_202!(b, c, d)) |
324 | 0 | .wrapping_add(t); |
325 | 0 | b = b.rotate_left(30); |
326 | | |
327 | 0 | d = d |
328 | 0 | .wrapping_add(e.rotate_left(5)) |
329 | 0 | .wrapping_add(bool3ary_202!(a, b, c)) |
330 | 0 | .wrapping_add(u); |
331 | 0 | a = a.rotate_left(30); |
332 | | |
333 | 0 | c = c |
334 | 0 | .wrapping_add(d.rotate_left(5)) |
335 | 0 | .wrapping_add(bool3ary_202!(e, a, b)) |
336 | 0 | .wrapping_add(v); |
337 | 0 | e = e.rotate_left(30); |
338 | | |
339 | 0 | b = b |
340 | 0 | .wrapping_add(c.rotate_left(5)) |
341 | 0 | .wrapping_add(bool3ary_202!(d, e, a)) |
342 | 0 | .wrapping_add(w); |
343 | 0 | d = d.rotate_left(30); |
344 | | |
345 | 0 | u32x4(b, c, d, e) |
346 | 0 | } |
347 | | |
348 | | /// Not an intrinsic, but helps emulate `llvm.x86.sha1rnds4` intrinsic. |
349 | 0 | fn sha1rnds4p(abcd: u32x4, msg: u32x4) -> u32x4 { |
350 | 0 | let u32x4(mut a, mut b, mut c, mut d) = abcd; |
351 | 0 | let u32x4(t, u, v, w) = msg; |
352 | 0 | let mut e = 0u32; |
353 | | |
354 | | macro_rules! bool3ary_150 { |
355 | | ($a:expr, $b:expr, $c:expr) => { |
356 | | ($a ^ $b ^ $c) |
357 | | }; |
358 | | } // Parity, XOR, MD5H, SHA1P |
359 | | |
360 | 0 | e = e |
361 | 0 | .wrapping_add(a.rotate_left(5)) |
362 | 0 | .wrapping_add(bool3ary_150!(b, c, d)) |
363 | 0 | .wrapping_add(t); |
364 | 0 | b = b.rotate_left(30); |
365 | | |
366 | 0 | d = d |
367 | 0 | .wrapping_add(e.rotate_left(5)) |
368 | 0 | .wrapping_add(bool3ary_150!(a, b, c)) |
369 | 0 | .wrapping_add(u); |
370 | 0 | a = a.rotate_left(30); |
371 | | |
372 | 0 | c = c |
373 | 0 | .wrapping_add(d.rotate_left(5)) |
374 | 0 | .wrapping_add(bool3ary_150!(e, a, b)) |
375 | 0 | .wrapping_add(v); |
376 | 0 | e = e.rotate_left(30); |
377 | | |
378 | 0 | b = b |
379 | 0 | .wrapping_add(c.rotate_left(5)) |
380 | 0 | .wrapping_add(bool3ary_150!(d, e, a)) |
381 | 0 | .wrapping_add(w); |
382 | 0 | d = d.rotate_left(30); |
383 | | |
384 | 0 | u32x4(b, c, d, e) |
385 | 0 | } |
386 | | |
387 | | /// Not an intrinsic, but helps emulate `llvm.x86.sha1rnds4` intrinsic. |
388 | 0 | fn sha1rnds4m(abcd: u32x4, msg: u32x4) -> u32x4 { |
389 | 0 | let u32x4(mut a, mut b, mut c, mut d) = abcd; |
390 | 0 | let u32x4(t, u, v, w) = msg; |
391 | 0 | let mut e = 0u32; |
392 | | |
393 | | macro_rules! bool3ary_232 { |
394 | | ($a:expr, $b:expr, $c:expr) => { |
395 | | ($a & $b) ^ ($a & $c) ^ ($b & $c) |
396 | | }; |
397 | | } // Majority, SHA1M |
398 | | |
399 | 0 | e = e |
400 | 0 | .wrapping_add(a.rotate_left(5)) |
401 | 0 | .wrapping_add(bool3ary_232!(b, c, d)) |
402 | 0 | .wrapping_add(t); |
403 | 0 | b = b.rotate_left(30); |
404 | | |
405 | 0 | d = d |
406 | 0 | .wrapping_add(e.rotate_left(5)) |
407 | 0 | .wrapping_add(bool3ary_232!(a, b, c)) |
408 | 0 | .wrapping_add(u); |
409 | 0 | a = a.rotate_left(30); |
410 | | |
411 | 0 | c = c |
412 | 0 | .wrapping_add(d.rotate_left(5)) |
413 | 0 | .wrapping_add(bool3ary_232!(e, a, b)) |
414 | 0 | .wrapping_add(v); |
415 | 0 | e = e.rotate_left(30); |
416 | | |
417 | 0 | b = b |
418 | 0 | .wrapping_add(c.rotate_left(5)) |
419 | 0 | .wrapping_add(bool3ary_232!(d, e, a)) |
420 | 0 | .wrapping_add(w); |
421 | 0 | d = d.rotate_left(30); |
422 | | |
423 | 0 | u32x4(b, c, d, e) |
424 | 0 | } |
425 | | |
426 | | impl Sha1State { |
427 | 0 | fn process(&mut self, block: &[u8; 64]) { |
428 | 0 | let mut words = [0u32; 16]; |
429 | 0 | for (i, word) in words.iter_mut().enumerate() { |
430 | 0 | let off = i * 4; |
431 | 0 | *word = (block[off + 3] as u32) |
432 | 0 | | ((block[off + 2] as u32) << 8) |
433 | 0 | | ((block[off + 1] as u32) << 16) |
434 | 0 | | ((block[off] as u32) << 24); |
435 | 0 | } |
436 | | macro_rules! schedule { |
437 | | ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => { |
438 | | sha1msg2(sha1msg1($v0, $v1) ^ $v2, $v3) |
439 | | }; |
440 | | } |
441 | | |
442 | | macro_rules! rounds4 { |
443 | | ($h0:ident, $h1:ident, $wk:expr, $i:expr) => { |
444 | | sha1_digest_round_x4($h0, sha1_first_half($h1, $wk), $i) |
445 | | }; |
446 | | } |
447 | | |
448 | | // Rounds 0..20 |
449 | 0 | let mut h0 = u32x4(self.state[0], self.state[1], self.state[2], self.state[3]); |
450 | 0 | let mut w0 = u32x4(words[0], words[1], words[2], words[3]); |
451 | 0 | let mut h1 = sha1_digest_round_x4(h0, sha1_first_add(self.state[4], w0), 0); |
452 | 0 | let mut w1 = u32x4(words[4], words[5], words[6], words[7]); |
453 | 0 | h0 = rounds4!(h1, h0, w1, 0); |
454 | 0 | let mut w2 = u32x4(words[8], words[9], words[10], words[11]); |
455 | 0 | h1 = rounds4!(h0, h1, w2, 0); |
456 | 0 | let mut w3 = u32x4(words[12], words[13], words[14], words[15]); |
457 | 0 | h0 = rounds4!(h1, h0, w3, 0); |
458 | 0 | let mut w4 = schedule!(w0, w1, w2, w3); |
459 | 0 | h1 = rounds4!(h0, h1, w4, 0); |
460 | | |
461 | | // Rounds 20..40 |
462 | 0 | w0 = schedule!(w1, w2, w3, w4); |
463 | 0 | h0 = rounds4!(h1, h0, w0, 1); |
464 | 0 | w1 = schedule!(w2, w3, w4, w0); |
465 | 0 | h1 = rounds4!(h0, h1, w1, 1); |
466 | 0 | w2 = schedule!(w3, w4, w0, w1); |
467 | 0 | h0 = rounds4!(h1, h0, w2, 1); |
468 | 0 | w3 = schedule!(w4, w0, w1, w2); |
469 | 0 | h1 = rounds4!(h0, h1, w3, 1); |
470 | 0 | w4 = schedule!(w0, w1, w2, w3); |
471 | 0 | h0 = rounds4!(h1, h0, w4, 1); |
472 | | |
473 | | // Rounds 40..60 |
474 | 0 | w0 = schedule!(w1, w2, w3, w4); |
475 | 0 | h1 = rounds4!(h0, h1, w0, 2); |
476 | 0 | w1 = schedule!(w2, w3, w4, w0); |
477 | 0 | h0 = rounds4!(h1, h0, w1, 2); |
478 | 0 | w2 = schedule!(w3, w4, w0, w1); |
479 | 0 | h1 = rounds4!(h0, h1, w2, 2); |
480 | 0 | w3 = schedule!(w4, w0, w1, w2); |
481 | 0 | h0 = rounds4!(h1, h0, w3, 2); |
482 | 0 | w4 = schedule!(w0, w1, w2, w3); |
483 | 0 | h1 = rounds4!(h0, h1, w4, 2); |
484 | | |
485 | | // Rounds 60..80 |
486 | 0 | w0 = schedule!(w1, w2, w3, w4); |
487 | 0 | h0 = rounds4!(h1, h0, w0, 3); |
488 | 0 | w1 = schedule!(w2, w3, w4, w0); |
489 | 0 | h1 = rounds4!(h0, h1, w1, 3); |
490 | 0 | w2 = schedule!(w3, w4, w0, w1); |
491 | 0 | h0 = rounds4!(h1, h0, w2, 3); |
492 | 0 | w3 = schedule!(w4, w0, w1, w2); |
493 | 0 | h1 = rounds4!(h0, h1, w3, 3); |
494 | 0 | w4 = schedule!(w0, w1, w2, w3); |
495 | 0 | h0 = rounds4!(h1, h0, w4, 3); |
496 | | |
497 | 0 | let e = sha1_first(h1).rotate_left(30); |
498 | 0 | let u32x4(a, b, c, d) = h0; |
499 | | |
500 | 0 | self.state[0] = self.state[0].wrapping_add(a); |
501 | 0 | self.state[1] = self.state[1].wrapping_add(b); |
502 | 0 | self.state[2] = self.state[2].wrapping_add(c); |
503 | 0 | self.state[3] = self.state[3].wrapping_add(d); |
504 | 0 | self.state[4] = self.state[4].wrapping_add(e); |
505 | 0 | } |
506 | | } |
507 | | |
508 | | impl PartialEq for Blocks { |
509 | 0 | fn eq(&self, other: &Blocks) -> bool { |
510 | 0 | (self.len, &self.block[..]).eq(&(other.len, &other.block[..])) |
511 | 0 | } |
512 | | } |
513 | | |
514 | | impl Ord for Blocks { |
515 | 0 | fn cmp(&self, other: &Blocks) -> cmp::Ordering { |
516 | 0 | (self.len, &self.block[..]).cmp(&(other.len, &other.block[..])) |
517 | 0 | } |
518 | | } |
519 | | |
520 | | impl PartialOrd for Blocks { |
521 | 0 | fn partial_cmp(&self, other: &Blocks) -> Option<cmp::Ordering> { |
522 | 0 | Some(self.cmp(other)) |
523 | 0 | } |
524 | | } |
525 | | |
526 | | impl Eq for Blocks {} |
527 | | |
528 | | impl hash::Hash for Blocks { |
529 | 0 | fn hash<H: hash::Hasher>(&self, state: &mut H) { |
530 | 0 | self.len.hash(state); |
531 | 0 | self.block.hash(state); |
532 | 0 | } |
533 | | } |
534 | | |
535 | | impl Clone for Blocks { |
536 | 0 | fn clone(&self) -> Blocks { |
537 | 0 | Blocks { ..*self } |
538 | 0 | } |
539 | | } |
540 | | |
541 | | /// Indicates that a digest couldn't be parsed. |
542 | | #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)] |
543 | | pub struct DigestParseError(()); |
544 | | |
545 | | impl fmt::Display for DigestParseError { |
546 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
547 | 0 | write!(f, "not a valid sha1 hash") |
548 | 0 | } |
549 | | } |
550 | | |
551 | | #[cfg(feature = "std")] |
552 | | impl std::error::Error for DigestParseError { |
553 | | fn description(&self) -> &str { |
554 | | "not a valid sha1 hash" |
555 | | } |
556 | | } |
557 | | |
558 | | impl str::FromStr for Digest { |
559 | | type Err = DigestParseError; |
560 | | |
561 | 0 | fn from_str(s: &str) -> Result<Digest, DigestParseError> { |
562 | 0 | if s.len() != 40 { |
563 | 0 | return Err(DigestParseError(())); |
564 | 0 | } |
565 | 0 | let mut rv: Digest = Default::default(); |
566 | 0 | for idx in 0..5 { |
567 | 0 | rv.data.state[idx] = |
568 | 0 | r#try!(u32::from_str_radix(&s[idx * 8..idx * 8 + 8], 16) |
569 | 0 | .map_err(|_| DigestParseError(()))); |
570 | | } |
571 | 0 | Ok(rv) |
572 | 0 | } |
573 | | } |
574 | | |
575 | | impl fmt::Display for Digest { |
576 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
577 | 0 | for i in self.data.state.iter() { |
578 | 0 | r#try!(write!(f, "{:08x}", i)); |
579 | | } |
580 | 0 | Ok(()) |
581 | 0 | } |
582 | | } |
583 | | |
584 | | impl fmt::Debug for Digest { |
585 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
586 | 0 | write!(f, "Digest {{ \"{}\" }}", self) |
587 | 0 | } |
588 | | } |
589 | | |
590 | | #[cfg(feature = "serde")] |
591 | | impl serde::ser::Serialize for Digest { |
592 | | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
593 | | where |
594 | | S: serde::ser::Serializer, |
595 | | { |
596 | | fn to_hex(num: u8) -> u8 { |
597 | | b"0123456789abcdef"[num as usize] |
598 | | } |
599 | | |
600 | | let mut hex_str = [0u8; 40]; |
601 | | let mut c = 0; |
602 | | for state in self.data.state.iter() { |
603 | | for off in 0..4 { |
604 | | let byte = (state >> (8 * (3 - off))) as u8; |
605 | | hex_str[c] = to_hex(byte >> 4); |
606 | | hex_str[c + 1] = to_hex(byte & 0xf); |
607 | | c += 2; |
608 | | } |
609 | | } |
610 | | serializer.serialize_str(unsafe { str::from_utf8_unchecked(&hex_str[..]) }) |
611 | | } |
612 | | } |
613 | | |
614 | | #[cfg(feature = "serde")] |
615 | | impl<'de> serde::de::Deserialize<'de> for Digest { |
616 | | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
617 | | where |
618 | | D: serde::de::Deserializer<'de>, |
619 | | { |
620 | | struct V; |
621 | | |
622 | | impl<'de> serde::de::Visitor<'de> for V { |
623 | | type Value = Digest; |
624 | | |
625 | | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
626 | | formatter.write_str("SHA-1 hash") |
627 | | } |
628 | | |
629 | | fn visit_str<E>(self, value: &str) -> Result<Digest, E> |
630 | | where |
631 | | E: serde::de::Error, |
632 | | { |
633 | | value.parse().map_err(|_| { |
634 | | serde::de::Error::invalid_value(serde::de::Unexpected::Str(value), &self) |
635 | | }) |
636 | | } |
637 | | } |
638 | | |
639 | | deserializer.deserialize_str(V) |
640 | | } |
641 | | } |
642 | | |
643 | | #[rustfmt::skip] |
644 | | #[cfg(test)] |
645 | | mod tests { |
646 | | extern crate std; |
647 | | extern crate alloc; |
648 | | extern crate rand; |
649 | | extern crate openssl; |
650 | | |
651 | | use self::std::prelude::v1::*; |
652 | | |
653 | | use crate::Sha1; |
654 | | |
655 | | #[test] |
656 | | fn test_simple() { |
657 | | let mut m = Sha1::new(); |
658 | | |
659 | | let tests = [ |
660 | | ("The quick brown fox jumps over the lazy dog", |
661 | | "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"), |
662 | | ("The quick brown fox jumps over the lazy cog", |
663 | | "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3"), |
664 | | ("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"), |
665 | | ("testing\n", "9801739daae44ec5293d4e1f53d3f4d2d426d91c"), |
666 | | ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", |
667 | | "025ecbd5d70f8fb3c5457cd96bab13fda305dc59"), |
668 | | ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", |
669 | | "4300320394f7ee239bcdce7d3b8bcee173a0cd5c"), |
670 | | ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", |
671 | | "cef734ba81a024479e09eb5a75b6ddae62e6abf1"), |
672 | | ]; |
673 | | |
674 | | for &(s, ref h) in tests.iter() { |
675 | | let data = s.as_bytes(); |
676 | | |
677 | | m.reset(); |
678 | | m.update(data); |
679 | | let hh = m.digest().to_string(); |
680 | | |
681 | | assert_eq!(hh.len(), h.len()); |
682 | | assert_eq!(hh, *h); |
683 | | } |
684 | | } |
685 | | |
686 | | #[test] |
687 | | fn test_shortcuts() { |
688 | | let s = Sha1::from("The quick brown fox jumps over the lazy dog"); |
689 | | assert_eq!(s.digest().to_string(), "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"); |
690 | | |
691 | | let s = Sha1::from(&b"The quick brown fox jumps over the lazy dog"[..]); |
692 | | assert_eq!(s.digest().to_string(), "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"); |
693 | | |
694 | | #[cfg(feature="alloc")] { |
695 | | let s = Sha1::from("The quick brown fox jumps over the lazy dog"); |
696 | | assert_eq!(s.hexdigest(), "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"); |
697 | | } |
698 | | } |
699 | | |
700 | | #[test] |
701 | | fn test_multiple_updates() { |
702 | | let mut m = Sha1::new(); |
703 | | |
704 | | m.reset(); |
705 | | m.update("The quick brown ".as_bytes()); |
706 | | m.update("fox jumps over ".as_bytes()); |
707 | | m.update("the lazy dog".as_bytes()); |
708 | | let hh = m.digest().to_string(); |
709 | | |
710 | | |
711 | | let h = "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"; |
712 | | assert_eq!(hh.len(), h.len()); |
713 | | assert_eq!(hh, &*h); |
714 | | } |
715 | | |
716 | | #[test] |
717 | | fn test_sha1_loop() { |
718 | | let mut m = Sha1::new(); |
719 | | let s = "The quick brown fox jumps over the lazy dog."; |
720 | | let n = 1000u64; |
721 | | |
722 | | for _ in 0..3 { |
723 | | m.reset(); |
724 | | for _ in 0..n { |
725 | | m.update(s.as_bytes()); |
726 | | } |
727 | | assert_eq!(m.digest().to_string(), |
728 | | "7ca27655f67fceaa78ed2e645a81c7f1d6e249d2"); |
729 | | } |
730 | | } |
731 | | |
732 | | #[test] |
733 | | fn spray_and_pray() { |
734 | | use self::rand::Rng; |
735 | | |
736 | | let mut rng = rand::thread_rng(); |
737 | | let mut m = Sha1::new(); |
738 | | let mut bytes = [0; 512]; |
739 | | |
740 | | for _ in 0..20 { |
741 | | let ty = openssl::hash::MessageDigest::sha1(); |
742 | | let mut r = openssl::hash::Hasher::new(ty).unwrap(); |
743 | | m.reset(); |
744 | | for _ in 0..50 { |
745 | | let len = rng.gen::<usize>() % bytes.len(); |
746 | | rng.fill_bytes(&mut bytes[..len]); |
747 | | m.update(&bytes[..len]); |
748 | | r.update(&bytes[..len]).unwrap(); |
749 | | } |
750 | | assert_eq!(r.finish().unwrap().as_ref(), &m.digest().bytes()); |
751 | | } |
752 | | } |
753 | | |
754 | | #[test] |
755 | | #[cfg(feature="std")] |
756 | | fn test_parse() { |
757 | | use crate::Digest; |
758 | | use std::error::Error; |
759 | | let y: Digest = "2ef7bde608ce5404e97d5f042f95f89f1c232871".parse().unwrap(); |
760 | | assert_eq!(y.to_string(), "2ef7bde608ce5404e97d5f042f95f89f1c232871"); |
761 | | assert!("asdfasdf".parse::<Digest>().is_err()); |
762 | | assert_eq!("asdfasdf".parse::<Digest>() |
763 | | .map_err(|x| x.description().to_string()).unwrap_err(), "not a valid sha1 hash"); |
764 | | } |
765 | | } |
766 | | |
767 | | #[rustfmt::skip] |
768 | | #[cfg(all(test, feature="serde"))] |
769 | | mod serde_tests { |
770 | | extern crate std; |
771 | | extern crate serde_json; |
772 | | |
773 | | use self::std::prelude::v1::*; |
774 | | |
775 | | use crate::{Sha1, Digest}; |
776 | | |
777 | | #[test] |
778 | | fn test_to_json() { |
779 | | let mut s = Sha1::new(); |
780 | | s.update(b"Hello World!"); |
781 | | let x = s.digest(); |
782 | | let y = serde_json::to_vec(&x).unwrap(); |
783 | | assert_eq!(y, &b"\"2ef7bde608ce5404e97d5f042f95f89f1c232871\""[..]); |
784 | | } |
785 | | |
786 | | #[test] |
787 | | fn test_from_json() { |
788 | | let y: Digest = serde_json::from_str("\"2ef7bde608ce5404e97d5f042f95f89f1c232871\"").unwrap(); |
789 | | assert_eq!(y.to_string(), "2ef7bde608ce5404e97d5f042f95f89f1c232871"); |
790 | | } |
791 | | } |