1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use byteorder::{ByteOrder, BE};
use lazy_static::lazy_static;

use crate::{consts::H256, platform::Implementation};

lazy_static! {
    static ref IMPL: Implementation = Implementation::detect();
}

#[derive(Clone)]
pub struct Sha256 {
    len: u64,
    state: [u32; 8],
}

impl Default for Sha256 {
    fn default() -> Self {
        Sha256 {
            len: 0,
            state: H256,
        }
    }
}

impl Sha256 {
    pub fn new() -> Self {
        Sha256::default()
    }

    pub fn digest(blocks: &[&[u8]]) -> [u8; 32] {
        let mut sha = Sha256::new();
        sha.input(blocks);
        sha.finish()
    }

    pub fn input(&mut self, blocks: &[&[u8]]) {
        debug_assert_eq!(blocks.len() % 2, 0, "invalid block length");

        self.len += (blocks.len() as u64) << 8;

        IMPL.compress256(&mut self.state, blocks);
    }

    pub fn finish(mut self) -> [u8; 32] {
        let mut block0 = [0u8; 32];
        let mut block1 = [0u8; 32];

        // Append single 1 bit
        block0[0] = 0b1000_0000;

        // Write L as 64 big endian integer
        let l = self.len;
        block1[32 - 8..].copy_from_slice(&l.to_be_bytes()[..]);

        IMPL.compress256(&mut self.state, &[&block0[..], &block1[..]][..]);

        let mut out = [0u8; 32];
        BE::write_u32_into(&self.state, &mut out);
        out
    }

    pub fn finish_with(mut self, block0: &[u8]) -> [u8; 32] {
        debug_assert_eq!(block0.len(), 32);

        let mut block1 = [0u8; 32];

        // Append single 1 bit
        block1[0] = 0b1000_0000;

        // Write L as 64 big endian integer
        let l = self.len + 256;
        block1[32 - 8..].copy_from_slice(&l.to_be_bytes()[..]);

        IMPL.compress256(&mut self.state, &[block0, &block1[..]][..]);

        let mut out = [0u8; 32];
        BE::write_u32_into(&self.state, &mut out);
        out
    }
}

opaque_debug::implement!(Sha256);

#[cfg(test)]
mod tests {
    use super::*;

    use rand::{RngCore, SeedableRng};
    use rand_xorshift::XorShiftRng;
    use sha2::{Digest, Sha256 as Original};

    #[test]
    fn test_fuzz_simple() {
        fuzz(10);
    }

    #[test]
    #[ignore]
    fn test_fuzz_long() {
        fuzz(1_000);
    }

    fn fuzz(n: usize) {
        let rng = &mut XorShiftRng::from_seed([
            0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
            0xbc, 0xe5,
        ]);
        for k in 1..n {
            for _ in 0..100 {
                let mut input = vec![0u8; 64 * k];
                rng.fill_bytes(&mut input);
                let chunked = input.chunks(32).collect::<Vec<_>>();
                assert_eq!(&Sha256::digest(&chunked)[..], &Original::digest(&input)[..])
            }
        }

        for k in (1..n).step_by(2) {
            for _ in 0..100 {
                let mut input = vec![0u8; 32 * k];
                rng.fill_bytes(&mut input);
                let mut hasher = Sha256::new();
                for chunk in input.chunks(64) {
                    if chunk.len() == 64 {
                        hasher.input(&[&chunk[..32], &chunk[32..]]);
                    }
                }
                assert_eq!(input.len() % 64, 32);
                let hash = hasher.finish_with(&input[input.len() - 32..]);

                assert_eq!(
                    &hash[..],
                    &Original::digest(&input)[..],
                    "input: {:?}",
                    &input
                );
            }
        }
    }
}