blake2b_rs/
blake2b.rs

1use crate::binding::{
2    blake2b_constant_BLAKE2B_KEYBYTES, blake2b_constant_BLAKE2B_OUTBYTES,
3    blake2b_constant_BLAKE2B_PERSONALBYTES, blake2b_constant_BLAKE2B_SALTBYTES, blake2b_final,
4    blake2b_init_key_with_param, blake2b_init_param, blake2b_param, blake2b_state, blake2b_update,
5};
6use core::ffi::c_void;
7use core::mem::MaybeUninit;
8
9pub struct Blake2b {
10    pub(crate) state: MaybeUninit<blake2b_state>,
11}
12
13pub struct Blake2bBuilder {
14    pub(crate) state: MaybeUninit<blake2b_state>,
15    pub(crate) param: blake2b_param,
16    pub(crate) key_len: usize,
17    pub(crate) key: [u8; blake2b_constant_BLAKE2B_KEYBYTES as usize],
18}
19
20impl Blake2bBuilder {
21    pub fn new(out_len: usize) -> Self {
22        assert!(out_len >= 1 && out_len <= blake2b_constant_BLAKE2B_OUTBYTES as usize);
23        let param = blake2b_param {
24            digest_length: out_len as u8,
25            key_length: 0,
26            fanout: 1,
27            depth: 1,
28            leaf_length: 0,
29            node_offset: 0,
30            xof_length: 0,
31            node_depth: 0,
32            inner_length: 0,
33            reserved: [0u8; 14usize],
34            salt: [0u8; blake2b_constant_BLAKE2B_SALTBYTES as usize],
35            personal: [0u8; blake2b_constant_BLAKE2B_PERSONALBYTES as usize],
36        };
37
38        let state = MaybeUninit::<blake2b_state>::uninit();
39        let key_len = 0;
40        let key = [0u8; blake2b_constant_BLAKE2B_KEYBYTES as usize];
41
42        Blake2bBuilder {
43            param,
44            state,
45            key_len,
46            key,
47        }
48    }
49
50    pub fn salt(mut self, salt: &[u8]) -> Blake2bBuilder {
51        let len = salt.len();
52        assert!(len <= blake2b_constant_BLAKE2B_SALTBYTES as usize);
53
54        unsafe {
55            ::core::ptr::copy_nonoverlapping(salt.as_ptr(), self.param.salt.as_mut_ptr(), len);
56        }
57        self
58    }
59
60    pub fn personal(mut self, personal: &[u8]) -> Blake2bBuilder {
61        let len = personal.len();
62        assert!(len <= blake2b_constant_BLAKE2B_PERSONALBYTES as usize);
63
64        unsafe {
65            ::core::ptr::copy_nonoverlapping(
66                personal.as_ptr(),
67                self.param.personal.as_mut_ptr(),
68                len,
69            );
70        }
71        self
72    }
73
74    pub fn key(mut self, key: &[u8]) -> Blake2bBuilder {
75        let key_len = key.len();
76        assert!(key_len <= blake2b_constant_BLAKE2B_KEYBYTES as usize);
77        self.param.key_length = key_len as u8;
78        self.key_len = key_len;
79
80        unsafe {
81            ::core::ptr::copy_nonoverlapping(key.as_ptr(), self.key.as_mut_ptr(), key_len);
82        }
83        self
84    }
85
86    pub fn build(self) -> Blake2b {
87        let Blake2bBuilder {
88            mut state,
89            param,
90            key,
91            key_len,
92        } = self;
93        if self.key_len == 0 {
94            unsafe {
95                blake2b_init_param(state.as_mut_ptr(), &param as *const blake2b_param);
96            }
97        } else {
98            unsafe {
99                blake2b_init_key_with_param(
100                    state.as_mut_ptr(),
101                    &param as *const blake2b_param,
102                    key.as_ptr() as *const c_void,
103                    key_len,
104                );
105            }
106        }
107        Blake2b { state }
108    }
109}
110
111impl Blake2b {
112    pub fn update(&mut self, data: &[u8]) {
113        unsafe {
114            blake2b_update(
115                self.state.as_mut_ptr(),
116                data.as_ptr() as *const c_void,
117                data.len(),
118            );
119        }
120    }
121
122    pub fn finalize(mut self, dst: &mut [u8]) {
123        unsafe {
124            blake2b_final(
125                self.state.as_mut_ptr(),
126                dst.as_mut_ptr() as *mut c_void,
127                dst.len(),
128            );
129        }
130    }
131}
132
133pub fn blake2b(key: &[u8], data: &[u8], dst: &mut [u8]) {
134    let mut blake2b = Blake2bBuilder::new(dst.len()).key(key).build();
135    blake2b.update(data);
136    blake2b.finalize(dst)
137}
138
139#[cfg(test)]
140mod tests {
141    use super::Blake2bBuilder;
142    use faster_hex::{hex_decode, hex_string};
143    use serde_derive::Deserialize;
144    use std::fs::File;
145    use std::io::BufReader;
146    use std::path::Path;
147    use std::string::String;
148    use std::vec;
149    use std::vec::Vec;
150
151    #[derive(Deserialize, Debug)]
152    struct TestItem {
153        outlen: usize,
154        out: String,
155        input: String,
156        key: String,
157        salt: String,
158        personal: String,
159    }
160
161    #[test]
162    fn test_full() {
163        let test_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("fixtures/test.json");
164
165        let f = File::open(test_path).unwrap();
166        let reader = BufReader::new(f);
167        let tests: Vec<TestItem> = serde_json::from_reader(reader).unwrap();
168
169        for test in tests {
170            let mut hash = vec![0u8; test.outlen];
171            let mut blake2b = Blake2bBuilder::new(test.outlen)
172                .key(&unhex(test.key.as_bytes()))
173                .personal(&unhex(test.personal.as_bytes()))
174                .salt(&unhex(test.salt.as_bytes()))
175                .build();
176            blake2b.update(&unhex(test.input.as_bytes()));
177            blake2b.finalize(&mut hash);
178            assert_eq!(hex_string(&hash).unwrap(), test.out);
179        }
180    }
181
182    fn unhex(src: &[u8]) -> Vec<u8> {
183        let len = src.len() / 2;
184        let mut ret = vec![0u8; len];
185        if !src.is_empty() {
186            hex_decode(src, &mut ret).unwrap();
187        }
188        ret
189    }
190}