cess_sha2raw/
sha256.rs

1use byteorder::{ByteOrder, BE};
2use lazy_static::lazy_static;
3
4use crate::{consts::H256, platform::Implementation};
5
6lazy_static! {
7    static ref IMPL: Implementation = Implementation::detect();
8}
9
10#[derive(Clone)]
11pub struct Sha256 {
12    len: u64,
13    state: [u32; 8],
14}
15
16impl Default for Sha256 {
17    fn default() -> Self {
18        Sha256 {
19            len: 0,
20            state: H256,
21        }
22    }
23}
24
25impl Sha256 {
26    pub fn new() -> Self {
27        Sha256::default()
28    }
29
30    pub fn digest(blocks: &[&[u8]]) -> [u8; 32] {
31        let mut sha = Sha256::new();
32        sha.input(blocks);
33        sha.finish()
34    }
35
36    pub fn input(&mut self, blocks: &[&[u8]]) {
37        debug_assert_eq!(blocks.len() % 2, 0, "invalid block length");
38
39        self.len += (blocks.len() as u64) << 8;
40
41        IMPL.compress256(&mut self.state, blocks);
42    }
43
44    pub fn finish(mut self) -> [u8; 32] {
45        let mut block0 = [0u8; 32];
46        let mut block1 = [0u8; 32];
47
48        // Append single 1 bit
49        block0[0] = 0b1000_0000;
50
51        // Write L as 64 big endian integer
52        let l = self.len;
53        block1[32 - 8..].copy_from_slice(&l.to_be_bytes()[..]);
54
55        IMPL.compress256(&mut self.state, &[&block0[..], &block1[..]][..]);
56
57        let mut out = [0u8; 32];
58        BE::write_u32_into(&self.state, &mut out);
59        out
60    }
61
62    pub fn finish_with(mut self, block0: &[u8]) -> [u8; 32] {
63        debug_assert_eq!(block0.len(), 32);
64
65        let mut block1 = [0u8; 32];
66
67        // Append single 1 bit
68        block1[0] = 0b1000_0000;
69
70        // Write L as 64 big endian integer
71        let l = self.len + 256;
72        block1[32 - 8..].copy_from_slice(&l.to_be_bytes()[..]);
73
74        IMPL.compress256(&mut self.state, &[block0, &block1[..]][..]);
75
76        let mut out = [0u8; 32];
77        BE::write_u32_into(&self.state, &mut out);
78        out
79    }
80}
81
82opaque_debug::implement!(Sha256);
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    use rand::{RngCore, SeedableRng};
89    use rand_xorshift::XorShiftRng;
90    use sha2::{Digest, Sha256 as Original};
91
92    #[test]
93    fn test_fuzz_simple() {
94        fuzz(10);
95    }
96
97    #[test]
98    #[ignore]
99    fn test_fuzz_long() {
100        fuzz(1_000);
101    }
102
103    fn fuzz(n: usize) {
104        let rng = &mut XorShiftRng::from_seed([
105            0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
106            0xbc, 0xe5,
107        ]);
108        for k in 1..n {
109            for _ in 0..100 {
110                let mut input = vec![0u8; 64 * k];
111                rng.fill_bytes(&mut input);
112                let chunked = input.chunks(32).collect::<Vec<_>>();
113                assert_eq!(&Sha256::digest(&chunked)[..], &Original::digest(&input)[..])
114            }
115        }
116
117        for k in (1..n).step_by(2) {
118            for _ in 0..100 {
119                let mut input = vec![0u8; 32 * k];
120                rng.fill_bytes(&mut input);
121                let mut hasher = Sha256::new();
122                for chunk in input.chunks(64) {
123                    if chunk.len() == 64 {
124                        hasher.input(&[&chunk[..32], &chunk[32..]]);
125                    }
126                }
127                assert_eq!(input.len() % 64, 32);
128                let hash = hasher.finish_with(&input[input.len() - 32..]);
129
130                assert_eq!(
131                    &hash[..],
132                    &Original::digest(&input)[..],
133                    "input: {:?}",
134                    &input
135                );
136            }
137        }
138    }
139}