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(), ¶m as *const blake2b_param);
96 }
97 } else {
98 unsafe {
99 blake2b_init_key_with_param(
100 state.as_mut_ptr(),
101 ¶m 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}