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
use ophelia_hasher::{HashValue, Hasher};

pub struct Blake2b {
    params: blake2b_simd::Params,
}

impl Blake2b {
    pub fn new(key: &[u8]) -> Self {
        let mut params = blake2b_simd::Params::new();
        params.hash_length(HashValue::LENGTH);
        params.key(key);

        Blake2b { params }
    }
}

impl Hasher for Blake2b {
    fn digest(&self, data: &[u8]) -> HashValue {
        let hash = self.params.hash(data);
        let bytes_slice = hash.as_bytes();

        assert_eq!(bytes_slice.len(), HashValue::LENGTH);

        let mut bytes = [0u8; 32];
        bytes.copy_from_slice(&bytes_slice[..HashValue::LENGTH]);

        HashValue::from_bytes_unchecked(bytes)
    }
}

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

    use ophelia_hasher::{HashValue, Hasher};

    use quickcheck::TestResult;
    use quickcheck_macros::quickcheck;

    #[quickcheck]
    fn prop_blake2b_bytes(key: String, msg: String) -> TestResult {
        if key.len() > blake2b_simd::KEYBYTES {
            return TestResult::discard();
        }

        let expect_hash = blake2b_simd::Params::new()
            .hash_length(HashValue::LENGTH)
            .key(&key.as_bytes())
            .hash(msg.as_bytes());

        let bytes = Blake2b::new(key.as_bytes())
            .digest(msg.as_bytes())
            .to_bytes();

        TestResult::from_bool(bytes == expect_hash.as_bytes())
    }
}