]> git.proxmox.com Git - rustc.git/blob - vendor/orion/src/hazardous/mac/blake2b.rs
New upstream version 1.70.0+dfsg2
[rustc.git] / vendor / orion / src / hazardous / mac / blake2b.rs
1 // MIT License
2
3 // Copyright (c) 2018-2022 The orion Developers
4
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22
23 //! # Parameters:
24 //! - `secret_key`: The authentication key.
25 //! - `size`: The desired output length for the authentication tag.
26 //! - `data`: Data to be authenticated.
27 //! - `expected`: The expected authentication tag.
28 //!
29 //! # Errors:
30 //! An error will be returned if:
31 //! - `size` is 0 or greater than 64.
32 //! - [`finalize()`] is called twice without a [`reset()`] in between.
33 //! - [`update()`] is called after [`finalize()`] without a [`reset()`] in
34 //! between.
35 //!
36 //! # Panics:
37 //! A panic will occur if:
38 //! - More than 2*(2^64-1) bytes of data are hashed.
39 //!
40 //! # Security:
41 //! - The secret key should always be generated using a CSPRNG.
42 //! [`SecretKey::generate()`] can be used for this. It generates
43 //! a secret key of 32 bytes.
44 //! - The minimum recommended size for a secret key is 32 bytes.
45 //! - The recommended minimum output size is 32.
46 //! - This interface only allows creating authentication tag using BLAKE2b. If hash digests are needed,
47 //! please refer to the [`hash::blake2::blake2b`] module.
48 //!
49 //! # Example:
50 //! ```rust
51 //! # #[cfg(feature = "safe_api")] {
52 //! use orion::hazardous::mac::blake2b::{Blake2b, SecretKey};
53 //!
54 //! let key = SecretKey::generate();
55 //!
56 //! let mut state = Blake2b::new(&key, 64)?;
57 //! state.update(b"Some data")?;
58 //! let tag = state.finalize()?;
59 //!
60 //! assert!(Blake2b::verify(&tag, &key, 64, b"Some data").is_ok());
61 //! # }
62 //! # Ok::<(), orion::errors::UnknownCryptoError>(())
63 //! ```
64 //! [`update()`]: blake2b::Blake2b::update
65 //! [`reset()`]: blake2b::Blake2b::reset
66 //! [`finalize()`]: blake2b::Blake2b::finalize
67 //! [`SecretKey::generate()`]: blake2b::SecretKey::generate
68 //! [`hash::blake2::blake2b`]: crate::hazardous::hash::blake2::blake2b
69
70 use crate::errors::UnknownCryptoError;
71 use crate::hazardous::hash::blake2::blake2b_core::{self, BLAKE2B_KEYSIZE, BLAKE2B_OUTSIZE};
72 use core::ops::DerefMut;
73 use zeroize::Zeroizing;
74
75 construct_secret_key! {
76 /// A type to represent the secret key that BLAKE2b uses for keyed mode.
77 ///
78 /// # Errors:
79 /// An error will be returned if:
80 /// - `slice` is empty.
81 /// - `slice` is greater than 64 bytes.
82 ///
83 /// # Panics:
84 /// A panic will occur if:
85 /// - Failure to generate random bytes securely.
86 (SecretKey, test_secret_key, 1, BLAKE2B_KEYSIZE, 32)
87 }
88
89 construct_tag! {
90 /// A type to represent the `Tag` that BLAKE2b returns.
91 ///
92 /// # Errors:
93 /// An error will be returned if:
94 /// - `slice` is empty.
95 /// - `slice` is greater than 64 bytes.
96 (Tag, test_tag, 1, BLAKE2B_OUTSIZE)
97 }
98
99 #[derive(Debug, Clone)]
100 /// BLAKE2b streaming state.
101 pub struct Blake2b {
102 _state: blake2b_core::State,
103 }
104
105 impl Blake2b {
106 #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
107 /// Initialize a `Blake2b` struct with a given size (in bytes) and key.
108 pub fn new(secret_key: &SecretKey, size: usize) -> Result<Self, UnknownCryptoError> {
109 Ok(Self {
110 _state: blake2b_core::State::_new(secret_key.unprotected_as_bytes(), size)?,
111 })
112 }
113
114 #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
115 /// Reset to `new()` state.
116 pub fn reset(&mut self, secret_key: &SecretKey) -> Result<(), UnknownCryptoError> {
117 self._state._reset(secret_key.unprotected_as_bytes())
118 }
119
120 #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
121 /// Update state with `data`. This can be called multiple times.
122 pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> {
123 self._state._update(data)
124 }
125
126 #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
127 /// Return a BLAKE2b tag.
128 pub fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> {
129 let mut tmp: Zeroizing<[u8; BLAKE2B_OUTSIZE]> = Zeroizing::new([0u8; BLAKE2B_OUTSIZE]);
130 self._state._finalize(tmp.deref_mut())?;
131
132 Tag::from_slice(&tmp[..self._state.size])
133 }
134
135 #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
136 /// Verify a BLAKE2b tag in constant time.
137 pub fn verify(
138 expected: &Tag,
139 secret_key: &SecretKey,
140 size: usize,
141 data: &[u8],
142 ) -> Result<(), UnknownCryptoError> {
143 let mut ctx = Self::new(secret_key, size)?;
144 ctx.update(data)?;
145
146 if &ctx.finalize()? == expected {
147 Ok(())
148 } else {
149 Err(UnknownCryptoError)
150 }
151 }
152 }
153
154 #[cfg(test)]
155 mod public {
156 mod test_streaming_interface_no_key {
157 use crate::errors::UnknownCryptoError;
158 use crate::hazardous::hash::blake2::blake2b_core::{
159 compare_blake2b_states, BLAKE2B_BLOCKSIZE, BLAKE2B_OUTSIZE,
160 };
161 use crate::hazardous::mac::blake2b::{Blake2b, SecretKey, Tag};
162 use crate::test_framework::incremental_interface::{
163 StreamingContextConsistencyTester, TestableStreamingContext,
164 };
165
166 const KEY: [u8; 32] = [255u8; 32];
167
168 impl TestableStreamingContext<Tag> for Blake2b {
169 fn reset(&mut self) -> Result<(), UnknownCryptoError> {
170 let key = SecretKey::from_slice(&KEY).unwrap();
171 self.reset(&key)
172 }
173
174 fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> {
175 self.update(input)
176 }
177
178 fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> {
179 self.finalize()
180 }
181
182 fn one_shot(input: &[u8]) -> Result<Tag, UnknownCryptoError> {
183 let key = SecretKey::from_slice(&KEY).unwrap();
184 let mut ctx = Blake2b::new(&key, BLAKE2B_OUTSIZE)?;
185 ctx.update(input)?;
186 ctx.finalize()
187 }
188
189 fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> {
190 let actual = Self::one_shot(input)?;
191
192 if &actual == expected {
193 Ok(())
194 } else {
195 Err(UnknownCryptoError)
196 }
197 }
198
199 fn compare_states(state_1: &Blake2b, state_2: &Blake2b) {
200 compare_blake2b_states(&state_1._state, &state_2._state)
201 }
202 }
203
204 #[test]
205 fn default_consistency_tests() {
206 let key = SecretKey::from_slice(&KEY).unwrap();
207 let initial_state: Blake2b = Blake2b::new(&key, BLAKE2B_OUTSIZE).unwrap();
208
209 let test_runner = StreamingContextConsistencyTester::<Tag, Blake2b>::new(
210 initial_state,
211 BLAKE2B_BLOCKSIZE,
212 );
213 test_runner.run_all_tests();
214 }
215
216 #[quickcheck]
217 #[cfg(feature = "safe_api")]
218 /// Related bug: https://github.com/orion-rs/orion/issues/46
219 /// Test different streaming state usage patterns.
220 fn prop_input_to_consistency(data: Vec<u8>) -> bool {
221 let key = SecretKey::from_slice(&KEY).unwrap();
222 let initial_state: Blake2b = Blake2b::new(&key, BLAKE2B_OUTSIZE).unwrap();
223
224 let test_runner = StreamingContextConsistencyTester::<Tag, Blake2b>::new(
225 initial_state,
226 BLAKE2B_BLOCKSIZE,
227 );
228 test_runner.run_all_tests_property(&data);
229 true
230 }
231 }
232
233 mod test_new {
234 use crate::hazardous::mac::blake2b::{Blake2b, SecretKey};
235
236 #[test]
237 fn test_init_size() {
238 let sk = SecretKey::from_slice(&[0u8; 32]).unwrap();
239 assert!(Blake2b::new(&sk, 0).is_err());
240 assert!(Blake2b::new(&sk, 65).is_err());
241 assert!(Blake2b::new(&sk, 1).is_ok());
242 assert!(Blake2b::new(&sk, 64).is_ok());
243 }
244 }
245
246 #[cfg(feature = "safe_api")]
247 mod test_verify {
248 use crate::hazardous::mac::blake2b::{Blake2b, SecretKey};
249
250 #[quickcheck]
251 #[cfg(feature = "safe_api")]
252 /// When using a different key, verify() should always yield an error.
253 /// NOTE: Using different and same input data is tested with TestableStreamingContext.
254 fn prop_verify_diff_key_false(data: Vec<u8>) -> bool {
255 let sk = SecretKey::generate();
256 let mut state = Blake2b::new(&sk, 64).unwrap();
257 state.update(&data[..]).unwrap();
258 let tag = state.finalize().unwrap();
259 let bad_sk = SecretKey::generate();
260
261 Blake2b::verify(&tag, &bad_sk, 64, &data[..]).is_err()
262 }
263
264 #[quickcheck]
265 #[cfg(feature = "safe_api")]
266 /// When using a different size, verify() should always yield an error.
267 /// NOTE: Using different and same input data is tested with TestableStreamingContext.
268 fn prop_verify_diff_size_false(data: Vec<u8>, size_one: usize, size_two: usize) -> bool {
269 let (size_one, size_two) = match (size_one, size_two) {
270 (1..=64, 1..=64) => (size_one, size_two),
271 (_, _) => (32, 64),
272 };
273
274 let sk = SecretKey::generate();
275 let mut state = Blake2b::new(&sk, size_one).unwrap();
276 state.update(&data[..]).unwrap();
277 let tag = state.finalize().unwrap();
278
279 if size_one != size_two {
280 Blake2b::verify(&tag, &sk, size_two, &data[..]).is_err()
281 } else {
282 Blake2b::verify(&tag, &sk, size_two, &data[..]).is_ok()
283 }
284 }
285 }
286
287 mod test_streaming_interface {
288 use crate::hazardous::hash::blake2::blake2b_core::compare_blake2b_states;
289 use crate::hazardous::mac::blake2b::{Blake2b, SecretKey};
290
291 /// Related bug: https://github.com/orion-rs/orion/issues/46
292 /// Testing different usage combinations of new(), update(),
293 /// finalize() and reset() produce the same Digest/Tag.
294 fn produces_same_hash(sk: &SecretKey, size: usize, data: &[u8]) {
295 // new(), update(), finalize()
296 let mut state_1 = Blake2b::new(sk, size).unwrap();
297 state_1.update(data).unwrap();
298 let res_1 = state_1.finalize().unwrap();
299
300 // new(), reset(), update(), finalize()
301 let mut state_2 = Blake2b::new(sk, size).unwrap();
302 state_2.reset(sk).unwrap();
303 state_2.update(data).unwrap();
304 let res_2 = state_2.finalize().unwrap();
305
306 // new(), update(), reset(), update(), finalize()
307 let mut state_3 = Blake2b::new(sk, size).unwrap();
308 state_3.update(data).unwrap();
309 state_3.reset(sk).unwrap();
310 state_3.update(data).unwrap();
311 let res_3 = state_3.finalize().unwrap();
312
313 // new(), update(), finalize(), reset(), update(), finalize()
314 let mut state_4 = Blake2b::new(sk, size).unwrap();
315 state_4.update(data).unwrap();
316 let _ = state_4.finalize().unwrap();
317 state_4.reset(sk).unwrap();
318 state_4.update(data).unwrap();
319 let res_4 = state_4.finalize().unwrap();
320
321 assert_eq!(res_1, res_2);
322 assert_eq!(res_2, res_3);
323 assert_eq!(res_3, res_4);
324
325 // Tests for the assumption that returning Ok() on empty update() calls
326 // with streaming APIs, gives the correct result. This is done by testing
327 // the reasoning that if update() is empty, returns Ok(), it is the same as
328 // calling new() -> finalize(). i.e not calling update() at all.
329 if data.is_empty() {
330 // new(), finalize()
331 let mut state_5 = Blake2b::new(sk, size).unwrap();
332 let res_5 = state_5.finalize().unwrap();
333
334 // new(), reset(), finalize()
335 let mut state_6 = Blake2b::new(sk, size).unwrap();
336 state_6.reset(sk).unwrap();
337 let res_6 = state_6.finalize().unwrap();
338
339 // new(), update(), reset(), finalize()
340 let mut state_7 = Blake2b::new(sk, size).unwrap();
341 state_7.update(b"Wrong data").unwrap();
342 state_7.reset(sk).unwrap();
343 let res_7 = state_7.finalize().unwrap();
344
345 assert_eq!(res_4, res_5);
346 assert_eq!(res_5, res_6);
347 assert_eq!(res_6, res_7);
348 }
349 }
350
351 /// Related bug: https://github.com/orion-rs/orion/issues/46
352 /// Testing different usage combinations of new(), update(),
353 /// finalize() and reset() produce the same Digest/Tag.
354 fn produces_same_state(sk: &SecretKey, size: usize, data: &[u8]) {
355 // new()
356 let state_1 = Blake2b::new(sk, size).unwrap();
357
358 // new(), reset()
359 let mut state_2 = Blake2b::new(sk, size).unwrap();
360 state_2.reset(sk).unwrap();
361
362 // new(), update(), reset()
363 let mut state_3 = Blake2b::new(sk, size).unwrap();
364 state_3.update(data).unwrap();
365 state_3.reset(sk).unwrap();
366
367 // new(), update(), finalize(), reset()
368 let mut state_4 = Blake2b::new(sk, size).unwrap();
369 state_4.update(data).unwrap();
370 let _ = state_4.finalize().unwrap();
371 state_4.reset(sk).unwrap();
372
373 compare_blake2b_states(&state_1._state, &state_2._state);
374 compare_blake2b_states(&state_2._state, &state_3._state);
375 compare_blake2b_states(&state_3._state, &state_4._state);
376 }
377
378 #[test]
379 /// Related bug: https://github.com/orion-rs/orion/issues/46
380 fn test_produce_same_state() {
381 let sk = SecretKey::from_slice(b"Testing").unwrap();
382 produces_same_state(&sk, 1, b"Tests");
383 produces_same_state(&sk, 32, b"Tests");
384 produces_same_state(&sk, 64, b"Tests");
385 produces_same_state(&sk, 28, b"Tests");
386 }
387
388 #[test]
389 /// Related bug: https://github.com/orion-rs/orion/issues/46
390 fn test_produce_same_hash() {
391 let sk = SecretKey::from_slice(b"Testing").unwrap();
392 produces_same_hash(&sk, 1, b"Tests");
393 produces_same_hash(&sk, 32, b"Tests");
394 produces_same_hash(&sk, 64, b"Tests");
395 produces_same_hash(&sk, 28, b"Tests");
396
397 produces_same_hash(&sk, 1, b"");
398 produces_same_hash(&sk, 32, b"");
399 produces_same_hash(&sk, 64, b"");
400 produces_same_hash(&sk, 28, b"");
401 }
402
403 #[quickcheck]
404 #[cfg(feature = "safe_api")]
405 /// Related bug: https://github.com/orion-rs/orion/issues/46
406 /// Test different streaming state usage patterns.
407 fn prop_same_hash_different_usage(data: Vec<u8>, size: usize) -> bool {
408 use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_OUTSIZE;
409
410 if (1..=BLAKE2B_OUTSIZE).contains(&size) {
411 // Will panic on incorrect results.
412 let sk = SecretKey::generate();
413 produces_same_hash(&sk, size, &data[..]);
414 }
415
416 true
417 }
418
419 #[quickcheck]
420 #[cfg(feature = "safe_api")]
421 /// Related bug: https://github.com/orion-rs/orion/issues/46
422 /// Test different streaming state usage patterns.
423 fn prop_same_state_different_usage(data: Vec<u8>, size: usize) -> bool {
424 use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_OUTSIZE;
425
426 if (1..=BLAKE2B_OUTSIZE).contains(&size) {
427 // Will panic on incorrect results.
428 let sk = SecretKey::generate();
429 produces_same_state(&sk, size, &data[..]);
430 }
431
432 true
433 }
434 }
435 }