3 // Copyright (c) 2018-2022 The orion Developers
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:
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
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
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.
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
37 //! A panic will occur if:
38 //! - More than 2*(2^64-1) bytes of data are hashed.
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.
51 //! # #[cfg(feature = "safe_api")] {
52 //! use orion::hazardous::mac::blake2b::{Blake2b, SecretKey};
54 //! let key = SecretKey::generate();
56 //! let mut state = Blake2b::new(&key, 64)?;
57 //! state.update(b"Some data")?;
58 //! let tag = state.finalize()?;
60 //! assert!(Blake2b::verify(&tag, &key, 64, b"Some data").is_ok());
62 //! # Ok::<(), orion::errors::UnknownCryptoError>(())
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
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
;
75 construct_secret_key
! {
76 /// A type to represent the secret key that BLAKE2b uses for keyed mode.
79 /// An error will be returned if:
80 /// - `slice` is empty.
81 /// - `slice` is greater than 64 bytes.
84 /// A panic will occur if:
85 /// - Failure to generate random bytes securely.
86 (SecretKey
, test_secret_key
, 1, BLAKE2B_KEYSIZE
, 32)
90 /// A type to represent the `Tag` that BLAKE2b returns.
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
)
99 #[derive(Debug, Clone)]
100 /// BLAKE2b streaming state.
102 _state
: blake2b_core
::State
,
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
> {
110 _state
: blake2b_core
::State
::_new(secret_key
.unprotected_as_bytes(), size
)?
,
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())
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
)
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())?
;
132 Tag
::from_slice(&tmp
[..self._state
.size
])
135 #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
136 /// Verify a BLAKE2b tag in constant time.
139 secret_key
: &SecretKey
,
142 ) -> Result
<(), UnknownCryptoError
> {
143 let mut ctx
= Self::new(secret_key
, size
)?
;
146 if &ctx
.finalize()?
== expected
{
149 Err(UnknownCryptoError
)
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
,
161 use crate::hazardous
::mac
::blake2b
::{Blake2b, SecretKey, Tag}
;
162 use crate::test_framework
::incremental_interface
::{
163 StreamingContextConsistencyTester
, TestableStreamingContext
,
166 const KEY
: [u8; 32] = [255u8; 32];
168 impl TestableStreamingContext
<Tag
> for Blake2b
{
169 fn reset(&mut self) -> Result
<(), UnknownCryptoError
> {
170 let key
= SecretKey
::from_slice(&KEY
).unwrap();
174 fn update(&mut self, input
: &[u8]) -> Result
<(), UnknownCryptoError
> {
178 fn finalize(&mut self) -> Result
<Tag
, UnknownCryptoError
> {
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
)?
;
189 fn verify_result(expected
: &Tag
, input
: &[u8]) -> Result
<(), UnknownCryptoError
> {
190 let actual
= Self::one_shot(input
)?
;
192 if &actual
== expected
{
195 Err(UnknownCryptoError
)
199 fn compare_states(state_1
: &Blake2b
, state_2
: &Blake2b
) {
200 compare_blake2b_states(&state_1
._state
, &state_2
._state
)
205 fn default_consistency_tests() {
206 let key
= SecretKey
::from_slice(&KEY
).unwrap();
207 let initial_state
: Blake2b
= Blake2b
::new(&key
, BLAKE2B_OUTSIZE
).unwrap();
209 let test_runner
= StreamingContextConsistencyTester
::<Tag
, Blake2b
>::new(
213 test_runner
.run_all_tests();
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();
224 let test_runner
= StreamingContextConsistencyTester
::<Tag
, Blake2b
>::new(
228 test_runner
.run_all_tests_property(&data
);
234 use crate::hazardous
::mac
::blake2b
::{Blake2b, SecretKey}
;
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());
246 #[cfg(feature = "safe_api")]
248 use crate::hazardous
::mac
::blake2b
::{Blake2b, SecretKey}
;
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();
261 Blake2b
::verify(&tag
, &bad_sk
, 64, &data
[..]).is_err()
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
),
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();
279 if size_one
!= size_two
{
280 Blake2b
::verify(&tag
, &sk
, size_two
, &data
[..]).is_err()
282 Blake2b
::verify(&tag
, &sk
, size_two
, &data
[..]).is_ok()
287 mod test_streaming_interface
{
288 use crate::hazardous
::hash
::blake2
::blake2b_core
::compare_blake2b_states
;
289 use crate::hazardous
::mac
::blake2b
::{Blake2b, SecretKey}
;
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();
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();
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();
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();
321 assert_eq
!(res_1
, res_2
);
322 assert_eq
!(res_2
, res_3
);
323 assert_eq
!(res_3
, res_4
);
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.
331 let mut state_5
= Blake2b
::new(sk
, size
).unwrap();
332 let res_5
= state_5
.finalize().unwrap();
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();
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();
345 assert_eq
!(res_4
, res_5
);
346 assert_eq
!(res_5
, res_6
);
347 assert_eq
!(res_6
, res_7
);
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]) {
356 let state_1
= Blake2b
::new(sk
, size
).unwrap();
359 let mut state_2
= Blake2b
::new(sk
, size
).unwrap();
360 state_2
.reset(sk
).unwrap();
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();
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();
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
);
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");
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");
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
"");
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
;
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
[..]);
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
;
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
[..]);