crypt_io/mac/
blake3_impl.rs1use super::{BLAKE3_MAC_KEY_LEN, BLAKE3_MAC_OUTPUT_LEN};
14
15#[must_use]
28pub fn blake3_keyed(key: &[u8; BLAKE3_MAC_KEY_LEN], data: &[u8]) -> [u8; BLAKE3_MAC_OUTPUT_LEN] {
29 *::blake3::keyed_hash(key, data).as_bytes()
30}
31
32#[must_use]
57pub fn blake3_keyed_verify(
58 key: &[u8; BLAKE3_MAC_KEY_LEN],
59 data: &[u8],
60 expected_tag: &[u8],
61) -> bool {
62 if expected_tag.len() != BLAKE3_MAC_OUTPUT_LEN {
63 return false;
64 }
65 let computed = ::blake3::keyed_hash(key, data);
66 let mut expected = [0u8; BLAKE3_MAC_OUTPUT_LEN];
67 expected.copy_from_slice(expected_tag);
68 let expected_hash = ::blake3::Hash::from_bytes(expected);
69 computed == expected_hash
70}
71
72#[derive(Debug, Clone)]
94pub struct Blake3Mac {
95 inner: ::blake3::Hasher,
96}
97
98impl Blake3Mac {
99 #[must_use]
105 pub fn new(key: &[u8; BLAKE3_MAC_KEY_LEN]) -> Self {
106 Self {
107 inner: ::blake3::Hasher::new_keyed(key),
108 }
109 }
110
111 pub fn update(&mut self, data: &[u8]) -> &mut Self {
114 let _ = self.inner.update(data);
115 self
116 }
117
118 #[must_use]
120 pub fn finalize(self) -> [u8; BLAKE3_MAC_OUTPUT_LEN] {
121 *self.inner.finalize().as_bytes()
122 }
123
124 #[must_use]
129 pub fn verify(self, expected_tag: &[u8]) -> bool {
130 if expected_tag.len() != BLAKE3_MAC_OUTPUT_LEN {
131 return false;
132 }
133 let mut expected = [0u8; BLAKE3_MAC_OUTPUT_LEN];
134 expected.copy_from_slice(expected_tag);
135 let expected_hash = ::blake3::Hash::from_bytes(expected);
136 self.inner.finalize() == expected_hash
137 }
138}
139
140#[cfg(test)]
141#[allow(clippy::unwrap_used, clippy::expect_used, unused_results)]
142mod tests {
143 use super::*;
144
145 const ELVISH_KEY: &[u8; 32] = b"whats the Elvish word for friend";
152
153 const KAT_EMPTY: [u8; 32] = [
157 0x92, 0xb2, 0xb7, 0x56, 0x04, 0xed, 0x3c, 0x76, 0x1f, 0x9d, 0x6f, 0x62, 0x39, 0x2c, 0x8a,
158 0x92, 0x27, 0xad, 0x0e, 0xa3, 0xf0, 0x95, 0x73, 0xe7, 0x83, 0xf1, 0x49, 0x8a, 0x4e, 0xd6,
159 0x0d, 0x26,
160 ];
161
162 #[test]
163 fn kat_empty_input() {
164 assert_eq!(blake3_keyed(ELVISH_KEY, b""), KAT_EMPTY);
165 assert!(blake3_keyed_verify(ELVISH_KEY, b"", &KAT_EMPTY));
166 }
167
168 #[test]
169 fn round_trip_short_input() {
170 let key = [0x01u8; 32];
171 let tag = blake3_keyed(&key, b"the quick brown fox");
172 assert!(blake3_keyed_verify(&key, b"the quick brown fox", &tag));
173 }
174
175 #[test]
176 fn different_keys_produce_different_tags() {
177 let key1 = [0x01u8; 32];
178 let key2 = [0x02u8; 32];
179 let tag1 = blake3_keyed(&key1, b"same data");
180 let tag2 = blake3_keyed(&key2, b"same data");
181 assert_ne!(tag1, tag2);
182 }
183
184 #[test]
185 fn different_data_produces_different_tags() {
186 let key = [0x01u8; 32];
187 let tag1 = blake3_keyed(&key, b"data one");
188 let tag2 = blake3_keyed(&key, b"data two");
189 assert_ne!(tag1, tag2);
190 }
191
192 #[test]
193 fn verify_rejects_wrong_tag() {
194 let key = [0x01u8; 32];
195 let tag = blake3_keyed(&key, b"message");
196 let mut tampered = tag;
197 tampered[0] ^= 0x01;
198 assert!(!blake3_keyed_verify(&key, b"message", &tampered));
199 }
200
201 #[test]
202 fn verify_rejects_wrong_key() {
203 let correct = [0x01u8; 32];
204 let wrong = [0x02u8; 32];
205 let tag = blake3_keyed(&correct, b"message");
206 assert!(!blake3_keyed_verify(&wrong, b"message", &tag));
207 }
208
209 #[test]
210 fn verify_rejects_wrong_data() {
211 let key = [0x01u8; 32];
212 let tag = blake3_keyed(&key, b"original");
213 assert!(!blake3_keyed_verify(&key, b"tampered", &tag));
214 }
215
216 #[test]
217 fn verify_rejects_truncated_tag() {
218 let key = [0x01u8; 32];
219 let tag = blake3_keyed(&key, b"message");
220 assert!(!blake3_keyed_verify(&key, b"message", &tag[..16]));
221 }
222
223 #[test]
224 fn verify_rejects_oversized_tag() {
225 let key = [0x01u8; 32];
226 let tag = blake3_keyed(&key, b"message");
227 let mut oversized = alloc::vec::Vec::from(&tag[..]);
228 oversized.push(0u8);
229 assert!(!blake3_keyed_verify(&key, b"message", &oversized));
230 }
231
232 #[test]
235 fn streaming_equals_one_shot() {
236 let key = [0x42u8; 32];
237 let data = b"the quick brown fox jumps over the lazy dog";
238 let one_shot = blake3_keyed(&key, data);
239 let mut m = Blake3Mac::new(&key);
240 m.update(&data[..10]);
241 m.update(&data[10..25]);
242 m.update(&data[25..]);
243 assert_eq!(m.finalize(), one_shot);
244 }
245
246 #[test]
247 fn streaming_chain_returns_self() {
248 let key = [0x01u8; 32];
249 let mut m = Blake3Mac::new(&key);
250 m.update(b"chain").update(b"-friendly");
251 let one_shot = blake3_keyed(&key, b"chain-friendly");
252 assert_eq!(m.finalize(), one_shot);
253 }
254
255 #[test]
256 fn streaming_verify_accepts_correct_tag() {
257 let key = [0x01u8; 32];
258 let tag = blake3_keyed(&key, b"msg");
259 let mut m = Blake3Mac::new(&key);
260 m.update(b"msg");
261 assert!(m.verify(&tag));
262 }
263
264 #[test]
265 fn streaming_verify_rejects_wrong_tag() {
266 let key = [0x01u8; 32];
267 let tag = blake3_keyed(&key, b"msg");
268 let mut tampered = tag;
269 tampered[0] ^= 0xff;
270 let mut m = Blake3Mac::new(&key);
271 m.update(b"msg");
272 assert!(!m.verify(&tampered));
273 }
274}