1#![no_std]
35#![deny(missing_docs)]
36#![allow(deprecated)]
37#![allow(clippy::double_parens)]
38#![allow(clippy::identity_op)]
39
40use core::cmp;
41use core::fmt;
42use core::hash;
43use core::str;
44
45mod simd;
46use crate::simd::*;
47
48#[cfg(feature = "std")]
49extern crate std;
50
51pub const DIGEST_LENGTH: usize = 20;
53
54#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
56pub struct Sha1 {
57 state: Sha1State,
58 blocks: Blocks,
59 len: u64,
60}
61
62struct Blocks {
63 len: u32,
64 block: [u8; 64],
65}
66
67#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Default)]
68struct Sha1State {
69 state: [u32; 5],
70}
71
72#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy, Default)]
83pub struct Digest {
84 data: Sha1State,
85}
86
87const DEFAULT_STATE: Sha1State = Sha1State {
88 state: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0],
89};
90
91#[inline(always)]
92fn as_block(input: &[u8]) -> &[u8; 64] {
93 unsafe {
94 assert!(input.len() == 64);
95 let arr: &[u8; 64] = &*(input.as_ptr() as *const [u8; 64]);
96 arr
97 }
98}
99
100impl Default for Sha1 {
101 fn default() -> Sha1 {
102 Sha1::new()
103 }
104}
105
106impl Sha1 {
107 pub fn new() -> Sha1 {
111 Sha1 {
112 state: DEFAULT_STATE,
113 len: 0,
114 blocks: Blocks {
115 len: 0,
116 block: [0; 64],
117 },
118 }
119 }
120
121 pub fn from<D: AsRef<[u8]>>(data: D) -> Sha1 {
126 let mut rv = Sha1::new();
127 rv.update(data.as_ref());
128 rv
129 }
130
131 pub fn reset(&mut self) {
133 self.state = DEFAULT_STATE;
134 self.len = 0;
135 self.blocks.len = 0;
136 }
137
138 pub fn update(&mut self, data: &[u8]) {
140 let len = &mut self.len;
141 let state = &mut self.state;
142 self.blocks.input(data, |block| {
143 *len += block.len() as u64;
144 state.process(block);
145 })
146 }
147
148 pub fn digest(&self) -> Digest {
150 let mut state = self.state;
151 let bits = (self.len + (self.blocks.len as u64)) * 8;
152 let extra = [
153 (bits >> 56) as u8,
154 (bits >> 48) as u8,
155 (bits >> 40) as u8,
156 (bits >> 32) as u8,
157 (bits >> 24) as u8,
158 (bits >> 16) as u8,
159 (bits >> 8) as u8,
160 (bits >> 0) as u8,
161 ];
162 let mut last = [0; 128];
163 let blocklen = self.blocks.len as usize;
164 last[..blocklen].clone_from_slice(&self.blocks.block[..blocklen]);
165 last[blocklen] = 0x80;
166
167 if blocklen < 56 {
168 last[56..64].clone_from_slice(&extra);
169 state.process(as_block(&last[0..64]));
170 } else {
171 last[120..128].clone_from_slice(&extra);
172 state.process(as_block(&last[0..64]));
173 state.process(as_block(&last[64..128]));
174 }
175
176 Digest { data: state }
177 }
178
179 #[cfg(feature = "std")]
183 pub fn hexdigest(&self) -> std::string::String {
184 use std::string::ToString;
185 self.digest().to_string()
186 }
187}
188
189impl Digest {
190 pub fn bytes(&self) -> [u8; DIGEST_LENGTH] {
192 [
193 (self.data.state[0] >> 24) as u8,
194 (self.data.state[0] >> 16) as u8,
195 (self.data.state[0] >> 8) as u8,
196 (self.data.state[0] >> 0) as u8,
197 (self.data.state[1] >> 24) as u8,
198 (self.data.state[1] >> 16) as u8,
199 (self.data.state[1] >> 8) as u8,
200 (self.data.state[1] >> 0) as u8,
201 (self.data.state[2] >> 24) as u8,
202 (self.data.state[2] >> 16) as u8,
203 (self.data.state[2] >> 8) as u8,
204 (self.data.state[2] >> 0) as u8,
205 (self.data.state[3] >> 24) as u8,
206 (self.data.state[3] >> 16) as u8,
207 (self.data.state[3] >> 8) as u8,
208 (self.data.state[3] >> 0) as u8,
209 (self.data.state[4] >> 24) as u8,
210 (self.data.state[4] >> 16) as u8,
211 (self.data.state[4] >> 8) as u8,
212 (self.data.state[4] >> 0) as u8,
213 ]
214 }
215}
216
217impl Blocks {
218 fn input<F>(&mut self, mut input: &[u8], mut f: F)
219 where
220 F: FnMut(&[u8; 64]),
221 {
222 if self.len > 0 {
223 let len = self.len as usize;
224 let amt = cmp::min(input.len(), self.block.len() - len);
225 self.block[len..len + amt].clone_from_slice(&input[..amt]);
226 if len + amt == self.block.len() {
227 f(&self.block);
228 self.len = 0;
229 input = &input[amt..];
230 } else {
231 self.len += amt as u32;
232 return;
233 }
234 }
235 assert_eq!(self.len, 0);
236 for chunk in input.chunks(64) {
237 if chunk.len() == 64 {
238 f(as_block(chunk))
239 } else {
240 self.block[..chunk.len()].clone_from_slice(chunk);
241 self.len = chunk.len() as u32;
242 }
243 }
244 }
245}
246
247const K0: u32 = 0x5A827999u32;
249const K1: u32 = 0x6ED9EBA1u32;
250const K2: u32 = 0x8F1BBCDCu32;
251const K3: u32 = 0xCA62C1D6u32;
252
253#[inline]
255fn sha1_first(w0: u32x4) -> u32 {
256 w0.0
257}
258
259#[inline]
261fn sha1_first_add(e: u32, w0: u32x4) -> u32x4 {
262 let u32x4(a, b, c, d) = w0;
263 u32x4(e.wrapping_add(a), b, c, d)
264}
265
266fn sha1msg1(a: u32x4, b: u32x4) -> u32x4 {
268 let u32x4(_, _, w2, w3) = a;
269 let u32x4(w4, w5, _, _) = b;
270 a ^ u32x4(w2, w3, w4, w5)
271}
272
273fn sha1msg2(a: u32x4, b: u32x4) -> u32x4 {
275 let u32x4(x0, x1, x2, x3) = a;
276 let u32x4(_, w13, w14, w15) = b;
277
278 let w16 = (x0 ^ w13).rotate_left(1);
279 let w17 = (x1 ^ w14).rotate_left(1);
280 let w18 = (x2 ^ w15).rotate_left(1);
281 let w19 = (x3 ^ w16).rotate_left(1);
282
283 u32x4(w16, w17, w18, w19)
284}
285
286#[inline]
288fn sha1_first_half(abcd: u32x4, msg: u32x4) -> u32x4 {
289 sha1_first_add(sha1_first(abcd).rotate_left(30), msg)
290}
291
292fn 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 match i {
301 0 => sha1rnds4c(abcd, work + K0V),
302 1 => sha1rnds4p(abcd, work + K1V),
303 2 => sha1rnds4m(abcd, work + K2V),
304 3 => sha1rnds4p(abcd, work + K3V),
305 _ => panic!("unknown icosaround index"),
306 }
307}
308
309fn sha1rnds4c(abcd: u32x4, msg: u32x4) -> u32x4 {
311 let u32x4(mut a, mut b, mut c, mut d) = abcd;
312 let u32x4(t, u, v, w) = msg;
313 let mut e = 0u32;
314
315 macro_rules! bool3ary_202 {
316 ($a:expr, $b:expr, $c:expr) => {
317 ($c ^ ($a & ($b ^ $c)))
318 };
319 } e = e
322 .wrapping_add(a.rotate_left(5))
323 .wrapping_add(bool3ary_202!(b, c, d))
324 .wrapping_add(t);
325 b = b.rotate_left(30);
326
327 d = d
328 .wrapping_add(e.rotate_left(5))
329 .wrapping_add(bool3ary_202!(a, b, c))
330 .wrapping_add(u);
331 a = a.rotate_left(30);
332
333 c = c
334 .wrapping_add(d.rotate_left(5))
335 .wrapping_add(bool3ary_202!(e, a, b))
336 .wrapping_add(v);
337 e = e.rotate_left(30);
338
339 b = b
340 .wrapping_add(c.rotate_left(5))
341 .wrapping_add(bool3ary_202!(d, e, a))
342 .wrapping_add(w);
343 d = d.rotate_left(30);
344
345 u32x4(b, c, d, e)
346}
347
348fn sha1rnds4p(abcd: u32x4, msg: u32x4) -> u32x4 {
350 let u32x4(mut a, mut b, mut c, mut d) = abcd;
351 let u32x4(t, u, v, w) = msg;
352 let mut e = 0u32;
353
354 macro_rules! bool3ary_150 {
355 ($a:expr, $b:expr, $c:expr) => {
356 ($a ^ $b ^ $c)
357 };
358 } e = e
361 .wrapping_add(a.rotate_left(5))
362 .wrapping_add(bool3ary_150!(b, c, d))
363 .wrapping_add(t);
364 b = b.rotate_left(30);
365
366 d = d
367 .wrapping_add(e.rotate_left(5))
368 .wrapping_add(bool3ary_150!(a, b, c))
369 .wrapping_add(u);
370 a = a.rotate_left(30);
371
372 c = c
373 .wrapping_add(d.rotate_left(5))
374 .wrapping_add(bool3ary_150!(e, a, b))
375 .wrapping_add(v);
376 e = e.rotate_left(30);
377
378 b = b
379 .wrapping_add(c.rotate_left(5))
380 .wrapping_add(bool3ary_150!(d, e, a))
381 .wrapping_add(w);
382 d = d.rotate_left(30);
383
384 u32x4(b, c, d, e)
385}
386
387fn sha1rnds4m(abcd: u32x4, msg: u32x4) -> u32x4 {
389 let u32x4(mut a, mut b, mut c, mut d) = abcd;
390 let u32x4(t, u, v, w) = msg;
391 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 } e = e
400 .wrapping_add(a.rotate_left(5))
401 .wrapping_add(bool3ary_232!(b, c, d))
402 .wrapping_add(t);
403 b = b.rotate_left(30);
404
405 d = d
406 .wrapping_add(e.rotate_left(5))
407 .wrapping_add(bool3ary_232!(a, b, c))
408 .wrapping_add(u);
409 a = a.rotate_left(30);
410
411 c = c
412 .wrapping_add(d.rotate_left(5))
413 .wrapping_add(bool3ary_232!(e, a, b))
414 .wrapping_add(v);
415 e = e.rotate_left(30);
416
417 b = b
418 .wrapping_add(c.rotate_left(5))
419 .wrapping_add(bool3ary_232!(d, e, a))
420 .wrapping_add(w);
421 d = d.rotate_left(30);
422
423 u32x4(b, c, d, e)
424}
425
426impl Sha1State {
427 fn process(&mut self, block: &[u8; 64]) {
428 let mut words = [0u32; 16];
429 for (i, word) in words.iter_mut().enumerate() {
430 let off = i * 4;
431 *word = (block[off + 3] as u32)
432 | ((block[off + 2] as u32) << 8)
433 | ((block[off + 1] as u32) << 16)
434 | ((block[off] as u32) << 24);
435 }
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 let mut h0 = u32x4(self.state[0], self.state[1], self.state[2], self.state[3]);
450 let mut w0 = u32x4(words[0], words[1], words[2], words[3]);
451 let mut h1 = sha1_digest_round_x4(h0, sha1_first_add(self.state[4], w0), 0);
452 let mut w1 = u32x4(words[4], words[5], words[6], words[7]);
453 h0 = rounds4!(h1, h0, w1, 0);
454 let mut w2 = u32x4(words[8], words[9], words[10], words[11]);
455 h1 = rounds4!(h0, h1, w2, 0);
456 let mut w3 = u32x4(words[12], words[13], words[14], words[15]);
457 h0 = rounds4!(h1, h0, w3, 0);
458 let mut w4 = schedule!(w0, w1, w2, w3);
459 h1 = rounds4!(h0, h1, w4, 0);
460
461 w0 = schedule!(w1, w2, w3, w4);
463 h0 = rounds4!(h1, h0, w0, 1);
464 w1 = schedule!(w2, w3, w4, w0);
465 h1 = rounds4!(h0, h1, w1, 1);
466 w2 = schedule!(w3, w4, w0, w1);
467 h0 = rounds4!(h1, h0, w2, 1);
468 w3 = schedule!(w4, w0, w1, w2);
469 h1 = rounds4!(h0, h1, w3, 1);
470 w4 = schedule!(w0, w1, w2, w3);
471 h0 = rounds4!(h1, h0, w4, 1);
472
473 w0 = schedule!(w1, w2, w3, w4);
475 h1 = rounds4!(h0, h1, w0, 2);
476 w1 = schedule!(w2, w3, w4, w0);
477 h0 = rounds4!(h1, h0, w1, 2);
478 w2 = schedule!(w3, w4, w0, w1);
479 h1 = rounds4!(h0, h1, w2, 2);
480 w3 = schedule!(w4, w0, w1, w2);
481 h0 = rounds4!(h1, h0, w3, 2);
482 w4 = schedule!(w0, w1, w2, w3);
483 h1 = rounds4!(h0, h1, w4, 2);
484
485 w0 = schedule!(w1, w2, w3, w4);
487 h0 = rounds4!(h1, h0, w0, 3);
488 w1 = schedule!(w2, w3, w4, w0);
489 h1 = rounds4!(h0, h1, w1, 3);
490 w2 = schedule!(w3, w4, w0, w1);
491 h0 = rounds4!(h1, h0, w2, 3);
492 w3 = schedule!(w4, w0, w1, w2);
493 h1 = rounds4!(h0, h1, w3, 3);
494 w4 = schedule!(w0, w1, w2, w3);
495 h0 = rounds4!(h1, h0, w4, 3);
496
497 let e = sha1_first(h1).rotate_left(30);
498 let u32x4(a, b, c, d) = h0;
499
500 self.state[0] = self.state[0].wrapping_add(a);
501 self.state[1] = self.state[1].wrapping_add(b);
502 self.state[2] = self.state[2].wrapping_add(c);
503 self.state[3] = self.state[3].wrapping_add(d);
504 self.state[4] = self.state[4].wrapping_add(e);
505 }
506}
507
508impl PartialEq for Blocks {
509 fn eq(&self, other: &Blocks) -> bool {
510 (self.len, &self.block[..]).eq(&(other.len, &other.block[..]))
511 }
512}
513
514impl Ord for Blocks {
515 fn cmp(&self, other: &Blocks) -> cmp::Ordering {
516 (self.len, &self.block[..]).cmp(&(other.len, &other.block[..]))
517 }
518}
519
520impl PartialOrd for Blocks {
521 fn partial_cmp(&self, other: &Blocks) -> Option<cmp::Ordering> {
522 Some(self.cmp(other))
523 }
524}
525
526impl Eq for Blocks {}
527
528impl hash::Hash for Blocks {
529 fn hash<H: hash::Hasher>(&self, state: &mut H) {
530 self.len.hash(state);
531 self.block.hash(state);
532 }
533}
534
535impl Clone for Blocks {
536 fn clone(&self) -> Blocks {
537 Blocks { ..*self }
538 }
539}
540
541#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)]
543pub struct DigestParseError(());
544
545impl fmt::Display for DigestParseError {
546 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
547 write!(f, "not a valid sha1 hash")
548 }
549}
550
551#[cfg(feature = "std")]
552impl std::error::Error for DigestParseError {
553 fn description(&self) -> &str {
554 "not a valid sha1 hash"
555 }
556}
557
558impl str::FromStr for Digest {
559 type Err = DigestParseError;
560
561 fn from_str(s: &str) -> Result<Digest, DigestParseError> {
562 if s.len() != 40 {
563 return Err(DigestParseError(()));
564 }
565 let mut rv: Digest = Default::default();
566 for idx in 0..5 {
567 rv.data.state[idx] =
568 r#try!(u32::from_str_radix(&s[idx * 8..idx * 8 + 8], 16)
569 .map_err(|_| DigestParseError(())));
570 }
571 Ok(rv)
572 }
573}
574
575impl fmt::Display for Digest {
576 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
577 for i in self.data.state.iter() {
578 r#try!(write!(f, "{:08x}", i));
579 }
580 Ok(())
581 }
582}
583
584impl fmt::Debug for Digest {
585 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
586 write!(f, "Digest {{ \"{}\" }}", self)
587 }
588}
589
590#[cfg(feature = "serde")]
591impl 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")]
615impl<'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)]
645mod tests {
646 extern crate std;
647 extern crate rand;
648 extern crate openssl;
649
650 use self::std::prelude::v1::*;
651
652 use crate::Sha1;
653
654 #[test]
655 fn test_simple() {
656 let mut m = Sha1::new();
657
658 let tests = [
659 ("The quick brown fox jumps over the lazy dog",
660 "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"),
661 ("The quick brown fox jumps over the lazy cog",
662 "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3"),
663 ("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"),
664 ("testing\n", "9801739daae44ec5293d4e1f53d3f4d2d426d91c"),
665 ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
666 "025ecbd5d70f8fb3c5457cd96bab13fda305dc59"),
667 ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
668 "4300320394f7ee239bcdce7d3b8bcee173a0cd5c"),
669 ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
670 "cef734ba81a024479e09eb5a75b6ddae62e6abf1"),
671 ];
672
673 for &(s, ref h) in tests.iter() {
674 let data = s.as_bytes();
675
676 m.reset();
677 m.update(data);
678 let hh = m.digest().to_string();
679
680 assert_eq!(hh.len(), h.len());
681 assert_eq!(hh, *h);
682 }
683 }
684
685 #[test]
686 fn test_shortcuts() {
687 let s = Sha1::from("The quick brown fox jumps over the lazy dog");
688 assert_eq!(s.digest().to_string(), "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12");
689
690 let s = Sha1::from(&b"The quick brown fox jumps over the lazy dog"[..]);
691 assert_eq!(s.digest().to_string(), "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12");
692
693 #[cfg(feature="std")] {
694 let s = Sha1::from("The quick brown fox jumps over the lazy dog");
695 assert_eq!(s.hexdigest(), "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12");
696 }
697 }
698
699 #[test]
700 fn test_multiple_updates() {
701 let mut m = Sha1::new();
702
703 m.reset();
704 m.update("The quick brown ".as_bytes());
705 m.update("fox jumps over ".as_bytes());
706 m.update("the lazy dog".as_bytes());
707 let hh = m.digest().to_string();
708
709
710 let h = "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12";
711 assert_eq!(hh.len(), h.len());
712 assert_eq!(hh, &*h);
713 }
714
715 #[test]
716 fn test_sha1_loop() {
717 let mut m = Sha1::new();
718 let s = "The quick brown fox jumps over the lazy dog.";
719 let n = 1000u64;
720
721 for _ in 0..3 {
722 m.reset();
723 for _ in 0..n {
724 m.update(s.as_bytes());
725 }
726 assert_eq!(m.digest().to_string(),
727 "7ca27655f67fceaa78ed2e645a81c7f1d6e249d2");
728 }
729 }
730
731 #[test]
732 fn spray_and_pray() {
733 use self::rand::Rng;
734
735 let mut rng = rand::thread_rng();
736 let mut m = Sha1::new();
737 let mut bytes = [0; 512];
738
739 for _ in 0..20 {
740 let ty = openssl::hash::MessageDigest::sha1();
741 let mut r = openssl::hash::Hasher::new(ty).unwrap();
742 m.reset();
743 for _ in 0..50 {
744 let len = rng.gen::<usize>() % bytes.len();
745 rng.fill_bytes(&mut bytes[..len]);
746 m.update(&bytes[..len]);
747 r.update(&bytes[..len]).unwrap();
748 }
749 assert_eq!(r.finish().unwrap().as_ref(), &m.digest().bytes());
750 }
751 }
752
753 #[test]
754 #[cfg(feature="std")]
755 fn test_parse() {
756 use crate::Digest;
757 use std::error::Error;
758 let y: Digest = "2ef7bde608ce5404e97d5f042f95f89f1c232871".parse().unwrap();
759 assert_eq!(y.to_string(), "2ef7bde608ce5404e97d5f042f95f89f1c232871");
760 assert!("asdfasdf".parse::<Digest>().is_err());
761 assert_eq!("asdfasdf".parse::<Digest>()
762 .map_err(|x| x.description().to_string()).unwrap_err(), "not a valid sha1 hash");
763 }
764}
765
766#[rustfmt::skip]
767#[cfg(all(test, feature="serde"))]
768mod serde_tests {
769 extern crate std;
770 extern crate serde_json;
771
772 use self::std::prelude::v1::*;
773
774 use crate::{Sha1, Digest};
775
776 #[test]
777 fn test_to_json() {
778 let mut s = Sha1::new();
779 s.update(b"Hello World!");
780 let x = s.digest();
781 let y = serde_json::to_vec(&x).unwrap();
782 assert_eq!(y, &b"\"2ef7bde608ce5404e97d5f042f95f89f1c232871\""[..]);
783 }
784
785 #[test]
786 fn test_from_json() {
787 let y: Digest = serde_json::from_str("\"2ef7bde608ce5404e97d5f042f95f89f1c232871\"").unwrap();
788 assert_eq!(y.to_string(), "2ef7bde608ce5404e97d5f042f95f89f1c232871");
789 }
790}