wolf_crypto/hash/
blake_api.rs

1macro_rules! blake_api {
2    (
3        name: $name:ident,
4        wc: $wc:ty,
5        // applies to both key and the digest length
6        max: $max:literal,
7        init: $init:ident,
8        init_with_key: $init_key:ident,
9        update: $update:ident,
10        final: $final:ident,
11        ll_final: $ll_final:ident,
12        w_loc: $warning:literal $(,)?
13    ) => {
14        #[doc = concat!("The `", stringify!($name), "` hasher.")]
15        #[doc = ""]
16        #[doc = "# Soundness Note"]
17        #[doc = ""]
18        #[doc = concat!(
19            "In the underlying `wolfcrypt` source, the `", stringify!($ll_final),
20            "` function includes a comment "
21        )]
22        #[doc = "[`/* Is this correct? */`][1], which may raise concern about its implementation."]
23        #[doc = "However, we have subjected this Rust API to extensive testing, including property tests"]
24        #[doc = concat!(
25            "against other trusted ", stringify!($name),
26            " implementations, and no failures have been observed."
27        )]
28        #[doc = ""]
29        #[doc = "Furthermore, this comment is not present in the public WolfSSL API, suggesting "]
30        #[doc = "that they may have confidence in their own implementation despite the internal comment."]
31        #[doc = ""]
32        #[doc = "# Const Generic"]
33        #[doc = ""]
34        #[doc = concat!(
35            "* `C` - The length of the ", stringify!($name),
36            " digest to implement, with a maximum length of `", stringify!($max), "`."
37        )]
38        #[doc = ""]
39        #[doc = "# Example"]
40        #[doc = ""]
41        #[doc = "```"]
42        #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
43        #[doc = ""]
44        #[doc = concat!(
45            "let mut hasher = ", stringify!($name), "::<", stringify!($max), ">::new().unwrap();"
46        )]
47        #[doc = ""]
48        #[doc = "let input = b\"hello world\";"]
49        #[doc = "assert!(hasher.try_update(input.as_slice()).is_ok());"]
50        #[doc = ""]
51        #[doc = "let finalized = hasher.try_finalize().unwrap();"]
52        #[doc = concat!("assert_eq!(finalized.len(), ", stringify!($max), ");")]
53        #[doc = "```"]
54        #[doc = ""]
55        #[doc = concat!("[1]: ", $warning)]
56        pub struct $name<const C: usize> {
57            inner: $wc
58        }
59
60        impl<const C: usize> $name<C> {
61            #[doc = concat!("Create a new `", stringify!($name), "` instance.")]
62            #[doc = ""]
63            #[doc = "# Errors"]
64            #[doc = ""]
65            #[doc = concat!(
66                "- If the digest length is greater than `", stringify!($max), "` (const generic `C`)"
67            )]
68            #[doc = concat!(
69                "- If the underling initialization function fails (`", stringify!($init), "`)"
70            )]
71            #[doc = ""]
72            #[doc = "# Example"]
73            #[doc = ""]
74            #[doc = "```"]
75            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
76            #[doc = ""]
77            #[doc = concat!(
78                "let mut hasher = ", stringify!($name), "::<", stringify!($max), ">::new().unwrap();"
79            )]
80            #[doc = ""]
81            #[doc = "let input = b\"hello world\";"]
82            #[doc = "assert!(hasher.try_update(input.as_slice()).is_ok());"]
83            #[doc = ""]
84            #[doc = "let finalized = hasher.try_finalize().unwrap();"]
85            #[doc = "assert_ne!(finalized.as_slice(), input.as_slice());"]
86            #[doc = concat!("assert_eq!(finalized.len(), ", stringify!($max), ");")]
87            #[doc = ""]
88            #[doc = concat!("// Maximum `C` is ", stringify!($max))]
89            #[doc = concat!(
90                "assert!(", stringify!($name), "::<{", stringify!($max * 2), "}>::new().is_err());"
91            )]
92            #[doc = "```"]
93            pub fn new() -> Result<Self, $crate::error::Unspecified> {
94                if !$crate::const_lte::<C, { $max }>() { return Err($crate::error::Unspecified); }
95                let mut res = $crate::opaque_res::Res::new();
96
97                unsafe {
98                    let mut inner = ::core::mem::MaybeUninit::<$wc>::uninit();
99                    res.ensure_0($init(inner.as_mut_ptr(), C as u32));
100                    res.unit_err_with(|| Self { inner: inner.assume_init() })
101                }
102            }
103
104            #[inline]
105            unsafe fn new_with_key_unchecked(
106                key: &[u8]
107            ) -> (::core::mem::MaybeUninit<$wc>, $crate::opaque_res::Res) {
108                let mut res = $crate::opaque_res::Res::new();
109                let mut inner = ::core::mem::MaybeUninit::<$wc>::uninit();
110
111                res.ensure_0($init_key(
112                    inner.as_mut_ptr(),
113                    C as u32,
114                    key.as_ptr(),
115                    key.len() as u32
116                ));
117
118                (inner, res)
119            }
120
121            #[doc = concat!("Create a new `", stringify!($name), "` instance using a key.")]
122            #[doc = ""]
123            #[doc = concat!(
124                "The key is used to create a keyed ", stringify!($name), " instance, ",
125                "which is suitable for"
126            )]
127            #[doc = "message authentication (MAC) purposes. The output digest length is determined"]
128            #[doc = "by the constant generic parameter `C`."]
129            #[doc = ""]
130            #[doc = "# Errors"]
131            #[doc = ""]
132            #[doc = concat!("- If the digest length `C` is greater than `", stringify!($max), "`.")]
133            #[doc = concat!("- If the key length exceeds `", stringify!($max), "` bytes.")]
134            #[doc = "- If the key is of zero length."]
135            #[doc = concat!(
136                "- If the underlying initialization function (`", stringify!($init_key), "`) fails.\
137            ")]
138            #[doc = ""]
139            #[doc = "# Arguments"]
140            #[doc = ""]
141            #[doc = concat!(
142                "* `key` - A secret key used to initialize the ", stringify!($name),
143                " instance. The length of the key must be less than or equal to ", stringify!($max),
144                " bytes."
145            )]
146            #[doc = ""]
147            #[doc = "# Example"]
148            #[doc = ""]
149            #[doc = "```"]
150            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
151            #[doc = ""]
152            #[doc = "let key = b\"my-secret-key\";"]
153            #[doc = concat!(
154                "let mut hasher = ", stringify!($name), "::<", stringify!($max),
155                ">::new_with_key(key).unwrap();"
156            )]
157            #[doc = ""]
158            #[doc = "let input = b\"hello world\";"]
159            #[doc = "assert!(hasher.try_update(input.as_slice()).is_ok());"]
160            #[doc = ""]
161            #[doc = "let finalized = hasher.try_finalize().unwrap();"]
162            #[doc = "assert_ne!(finalized.as_slice(), input.as_slice());"]
163            #[doc = concat!("assert_eq!(finalized.len(), ", stringify!($max), ");")]
164            #[doc = ""]
165            #[doc = concat!(
166                "// Key length must be less than or equal to ", stringify!($max), " bytes."
167            )]
168            #[doc = concat!("let long_key = [0u8; ", stringify!($max * 2), "];")]
169            #[doc = concat!(
170                "assert!(", stringify!($name), "::<", stringify!($max),
171                ">::new_with_key(&long_key).is_err());"
172            )]
173            #[doc = "```"]
174            pub fn new_with_key(key: &[u8]) -> Result<Self, $crate::error::Unspecified> {
175                if !($crate::const_lte::<C, { $max }>() && $crate::lte::<{ $max }>(key.len())) {
176                    return Err($crate::error::Unspecified);
177                }
178
179                unsafe {
180                    let (inner, res) = Self::new_with_key_unchecked(key);
181                    res.unit_err_with(|| Self { inner: inner.assume_init() })
182                }
183            }
184
185            #[doc = concat!("Create a new `", stringify!($name), "` instance using a fixed-size key.")]
186            #[doc = ""]
187            #[doc = "This function allows you to specify the key as a fixed-size array. It is"]
188            #[doc = "similar to [`new_with_key`] but function preconditions are checked at compile time."]
189            #[doc = ""]
190            #[doc = "# Errors"]
191            #[doc = ""]
192            #[doc = concat!("- If the digest length `C` is greater than `", stringify!($max), "`.")]
193            #[doc = concat!(
194                "- If the key length exceeds `", stringify!($max), "` bytes (compile-time check)."
195            )]
196            #[doc = "- If the key is of zero length."]
197            #[doc = concat!(
198                "- If the underlying initialization function (`", stringify!($init_key), "`) fails."
199            )]
200            #[doc = ""]
201            #[doc = "# Arguments"]
202            #[doc = ""]
203            #[doc = concat!(
204                "- `key`: A fixed-size secret key (length `K`) used to initialize the `",
205                stringify!($name), "` instance. The length of the key must be less than or equal to ",
206                stringify!($max), " bytes."
207            )]
208            #[doc = ""]
209            #[doc = "# Example"]
210            #[doc = ""]
211            #[doc = "```"]
212            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
213            #[doc = ""]
214            #[doc = "let key: [u8; 32] = [0x00; 32];"]
215            #[doc = concat!(
216                "let mut hasher = ", stringify!($name), "::<", stringify!($max),
217                ">::new_with_sized_key(&key).unwrap();"
218            )]
219            #[doc = ""]
220            #[doc = "let input = b\"some data\";"]
221            #[doc = "assert!(hasher.try_update(input.as_slice()).is_ok());"]
222            #[doc = ""]
223            #[doc = "let finalized = hasher.try_finalize().unwrap();"]
224            #[doc = concat!("assert_eq!(finalized.len(), ", stringify!($max), ");")]
225            #[doc = ""]
226            #[doc = concat!(
227                "// Key length must be less than or equal to ", stringify!($max), " bytes."
228            )]
229            #[doc = concat!("let oversized_key = [0u8; ", stringify!($max * 2), "];")]
230            #[doc = concat!(
231                "assert!(", stringify!($name), "::<", stringify!($max),
232                ">::new_with_sized_key(&oversized_key).is_err());"
233            )]
234            #[doc = "```"]
235            #[doc = "[`new_with_key`]: Self::new_with_key"]
236            pub fn new_with_sized_key<const K: usize>(key: &[u8; K]) -> Result<Self, $crate::error::Unspecified> {
237                if !($crate::const_lte::<C, { $max }>() && $crate::const_lte::<K, { $max }>()) {
238                    return Err($crate::error::Unspecified);
239                }
240
241                unsafe {
242                    let (inner, res) = Self::new_with_key_unchecked(key);
243                    res.unit_err_with(|| Self { inner: inner.assume_init() })
244                }
245            }
246
247            #[doc = concat!(
248                "Update the `", stringify!($name),
249                "` instance with the provided data, without performing any safety checks."
250            )]
251            #[doc = ""]
252            #[doc = "# Safety"]
253            #[doc = ""]
254            #[doc = "The length of the data is cast to a 32-bit unsigned integer without checking for"]
255            #[doc = "overflow. While this is unlikely to occur in most practical scenarios, it is not impossible,"]
256            #[doc = "especially with very large slices. Therefore, this function is marked `unsafe`."]
257            #[doc = ""]
258            #[doc = "# Arguments"]
259            #[doc = ""]
260            #[doc = "* `data` - The slice to update the underlying hasher state with."]
261            #[doc = ""]
262            #[doc = "# Returns"]
263            #[doc = ""]
264            #[doc = "This function returns the result of the operation."]
265            #[doc = ""]
266            #[doc = "# Example"]
267            #[doc = ""]
268            #[doc = "```"]
269            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
270            #[doc = ""]
271            #[doc = concat!("let mut hasher = ", stringify!($name), "::<", stringify!($max), ">::new().unwrap();")]
272            #[doc = ""]
273            #[doc = "let input = b\"hello world\";"]
274            #[doc = "// SAFETY: The length of `hello world` is 11, which cannot overflow even an 8-bit integer."]
275            #[doc = "let res = unsafe { hasher.update_unchecked(input.as_slice()) };"]
276            #[doc = "assert!(res.is_ok());"]
277            #[doc = ""]
278            #[doc = "let finalized = hasher.try_finalize().unwrap();"]
279            #[doc = "assert_ne!(finalized.as_slice(), input.as_slice());"]
280            #[doc = concat!("assert_eq!(finalized.len(), ", stringify!($max), ");")]
281            #[doc = "```"]
282            #[inline]
283            pub unsafe fn update_unchecked(&mut self, data: &[u8]) -> $crate::opaque_res::Res {
284                let mut res = $crate::opaque_res::Res::new();
285
286                res.ensure_0($update(
287                    ::core::ptr::addr_of_mut!(self.inner),
288                    data.as_ptr(),
289                    data.len() as u32
290                ));
291
292                res
293            }
294
295            #[doc = concat!("Update the `", stringify!($name), "` instance with the provided data.")]
296            #[doc = ""]
297            #[doc = "# Arguments"]
298            #[doc = ""]
299            #[doc = "* `data` - The slice to update the underlying hasher state with."]
300            #[doc = ""]
301            #[doc = "# Returns"]
302            #[doc = ""]
303            #[doc = "This function returns the result of the operation."]
304            #[doc = ""]
305            #[doc = "# Errors"]
306            #[doc = ""]
307            #[doc = "- If the length of `data` cannot be safely cast to a `u32`."]
308            #[doc = concat!("- If the underlying `", stringify!($update), "` function fails.")]
309            #[doc = ""]
310            #[doc = "# Example"]
311            #[doc = ""]
312            #[doc = "```"]
313            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
314            #[doc = ""]
315            #[doc = concat!("let mut hasher = ", stringify!($name), "::<", stringify!($max), ">::new().unwrap();")]
316            #[doc = ""]
317            #[doc = "let input = b\"hello world\";"]
318            #[doc = "assert!(hasher.try_update(input.as_slice()).is_ok());"]
319            #[doc = ""]
320            #[doc = "let finalized = hasher.try_finalize().unwrap();"]
321            #[doc = "assert_ne!(finalized.as_slice(), input.as_slice());"]
322            #[doc = concat!("assert_eq!(finalized.len(), ", stringify!($max), ");")]
323            #[doc = "```"]
324            #[inline]
325            pub fn try_update(&mut self, data: &[u8]) -> $crate::opaque_res::Res {
326                if !$crate::can_cast_u32(data.len()) { return $crate::opaque_res::Res::ERR }
327                unsafe { self.update_unchecked(data) }
328            }
329
330            #[doc = concat!(
331                "Update the `", stringify!($name),
332                "` instance with the provided data, using compile-time safety checks."
333            )]
334            #[doc = ""]
335            #[doc = "# Arguments"]
336            #[doc = ""]
337            #[doc = "* `data` - The slice to update the underlying hasher state with, where the size of the slice"]
338            #[doc = "   is known at compile time."]
339            #[doc = ""]
340            #[doc = "# Returns"]
341            #[doc = ""]
342            #[doc = "This function returns the result of the operation."]
343            #[doc = ""]
344            #[doc = "# Errors"]
345            #[doc = ""]
346            #[doc = "- If the length of `data` cannot be safely cast to a `u32`."]
347            #[doc = concat!("- If the underlying `", stringify!($update), "` function fails.")]
348            #[doc = ""]
349            #[doc = "# Example"]
350            #[doc = ""]
351            #[doc = "```"]
352            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
353            #[doc = ""]
354            #[doc = concat!("let mut hasher = ", stringify!($name), "::<", stringify!($max), ">::new().unwrap();")]
355            #[doc = ""]
356            #[doc = "let input = b\"hello world\";"]
357            #[doc = "assert!(hasher.update_sized(&input).is_ok());"]
358            #[doc = ""]
359            #[doc = "let finalized = hasher.try_finalize().unwrap();"]
360            #[doc = "assert_ne!(finalized.as_slice(), input.as_slice());"]
361            #[doc = concat!("assert_eq!(finalized.len(), ", stringify!($max), ");")]
362            #[doc = "```"]
363            #[inline]
364            pub fn update_sized<const OC: usize>(&mut self, data: &[u8; OC]) -> $crate::opaque_res::Res {
365                if !$crate::const_can_cast_u32::<{ OC }>() { return $crate::opaque_res::Res::ERR }
366                unsafe { self.update_unchecked(data) }
367            }
368
369            panic_api! {
370            #[doc = concat!("Update the `", stringify!($name), "` instance with the provided data, panicking on failure.")]
371            #[doc = ""]
372            #[doc = "# Arguments"]
373            #[doc = ""]
374            #[doc = "* `data` - The slice to update the underlying hasher state with."]
375            #[doc = ""]
376            #[doc = "# Panics"]
377            #[doc = ""]
378            #[doc = "- If the length of `data` cannot be safely cast to a `u32`."]
379            #[doc = concat!("- If the underlying `", stringify!($update), "` function fails.")]
380            #[doc = ""]
381            #[doc = "If a panic is not acceptable for your use case, consider using [`try_update`] instead."]
382            #[doc = ""]
383            #[doc = "# Example"]
384            #[doc = ""]
385            #[doc = "```"]
386            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
387            #[doc = ""]
388            #[doc = concat!("let mut hasher = ", stringify!($name), "::<", stringify!($max), ">::new().unwrap();")]
389            #[doc = ""]
390            #[doc = "let input = b\"hello world\";"]
391            #[doc = "hasher.update(input.as_slice());"]
392            #[doc = ""]
393            #[doc = "let finalized = hasher.try_finalize().unwrap();"]
394            #[doc = "assert_ne!(finalized.as_slice(), input.as_slice());"]
395            #[doc = concat!("assert_eq!(finalized.len(), ", stringify!($max), ");")]
396            #[doc = "```"]
397            #[doc = ""]
398            #[doc = "[`try_update`]: Self::try_update"]
399            #[track_caller]
400            pub fn update(&mut self, data: &[u8]) {
401                self.try_update(data).unit_err(())
402                    .expect(concat!("Failed to update hash in `", stringify!($name), "`"))
403            }
404            }
405
406            #[doc = concat!(
407                "Finalize the `", stringify!($name),
408                "` hashing process, writing the output to the provided buffer, without"
409            )]
410            #[doc = "performing safety checks."]
411            #[doc = ""]
412            #[doc = "# Safety"]
413            #[doc = ""]
414            #[doc = "The size of the `output` argument must be at least `C` (the size of the digest)."]
415            #[doc = ""]
416            #[doc = "# Arguments"]
417            #[doc = ""]
418            #[doc = "* `output` - The buffer to store the output digest in."]
419            #[doc = ""]
420            #[doc = "# Returns"]
421            #[doc = ""]
422            #[doc = "This function returns the result of the operation."]
423            #[doc = ""]
424            #[doc = "# Example"]
425            #[doc = ""]
426            #[doc = "```"]
427            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
428            #[doc = concat!("let mut hasher = ", stringify!($name), "::<", stringify!($max), ">::new().unwrap();")]
429            #[doc = ""]
430            #[doc = concat!("let mut output = [0u8; ", stringify!($max), "];")]
431            #[doc = concat!(
432                "// SAFETY: The size of the output buffer is exactly ", stringify!($max),
433                " bytes (the size of the digest)."
434            )]
435            #[doc = "let res = unsafe { hasher.finalize_unchecked(&mut output) };"]
436            #[doc = "assert!(res.is_ok());"]
437            #[doc = "```"]
438            #[inline]
439            pub unsafe fn finalize_unchecked(mut self, output: &mut [u8]) -> $crate::opaque_res::Res {
440                let mut res = $crate::opaque_res::Res::new();
441
442                res.ensure_0($final(
443                    ::core::ptr::addr_of_mut!(self.inner),
444                    output.as_mut_ptr(),
445                    C as u32
446                ));
447
448                res
449            }
450
451            #[doc = concat!(
452                "Finalize the `", stringify!($name),
453                "` hashing process, writing the output to the provided buffer."
454            )]
455            #[doc = ""]
456            #[doc = "# Arguments"]
457            #[doc = ""]
458            #[doc = "* `output` - The buffer to store the output digest in."]
459            #[doc = ""]
460            #[doc = "# Errors"]
461            #[doc = ""]
462            #[doc = "- If the size of `output` is less than `C` (the size of the digest)."]
463            #[doc = "- If the underlying finalize function fails."]
464            #[doc = ""]
465            #[doc = "# Example"]
466            #[doc = ""]
467            #[doc = "```"]
468            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
469            #[doc = concat!("let mut hasher = ", stringify!($name), "::<", stringify!($max), ">::new().unwrap();")]
470            #[doc = ""]
471            #[doc = "// use the hasher ..."]
472            #[doc = ""]
473            #[doc = concat!("let mut output = [0u8; ", stringify!($max), "];")]
474            #[doc = "assert!(hasher.finalize_into(&mut output).is_ok());"]
475            #[doc = "```"]
476            #[inline]
477            pub fn finalize_into(self, output: &mut [u8]) -> $crate::opaque_res::Res {
478                if !$crate::gte::<{ C }>(output.len()) { return $crate::opaque_res::Res::ERR }
479                unsafe { self.finalize_unchecked(output) }
480            }
481
482            #[doc = concat!(
483                "Finalize the `", stringify!($name),
484                "` hashing process, writing the output to a fixed-size buffer, and"
485            )]
486            #[doc = "performing safety checks at compilation time."]
487            #[doc = ""]
488            #[doc = "# Arguments"]
489            #[doc = ""]
490            #[doc = "* `output` - The fixed-size buffer to store the output digest in."]
491            #[doc = ""]
492            #[doc = "# Errors"]
493            #[doc = ""]
494            #[doc = "- If the length of `output` is less than `C` (the size of the digest)."]
495            #[doc = "- If the underlying finalize function fails."]
496            #[doc = ""]
497            #[doc = "# Example"]
498            #[doc = ""]
499            #[doc = "```"]
500            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
501            #[doc = concat!("let mut hasher = ", stringify!($name), "::<", stringify!($max), ">::new().unwrap();")]
502            #[doc = ""]
503            #[doc = "// use the hasher ..."]
504            #[doc = ""]
505            #[doc = concat!("let mut output = [0u8; ", stringify!($max), "];")]
506            #[doc = "assert!(hasher.finalize_into_sized(&mut output).is_ok());"]
507            #[doc = "```"]
508            #[inline]
509            pub fn finalize_into_sized<const OC: usize>(self, output: &mut [u8; OC]) -> $crate::opaque_res::Res {
510                if !$crate::const_gte::<{ OC }, { C }>() { return $crate::opaque_res::Res::ERR }
511                unsafe { self.finalize_unchecked(output) }
512            }
513
514            #[doc = concat!(
515                "Finalize the `", stringify!($name),
516                "` hashing process, writing the output to a buffer with an exact size."
517            )]
518            #[doc = ""]
519            #[doc = "This method is for cases where the size of the output buffer is exactly the same as the"]
520            #[doc = "digest size (`C`). The buffer size is checked at compile time, so no runtime size checks"]
521            #[doc = "are necessary, making this a highly optimized version of finalization."]
522            #[doc = ""]
523            #[doc = "# Arguments"]
524            #[doc = ""]
525            #[doc = "* `output` - The buffer to store the output digest in, with a size exactly equal to the"]
526            #[doc = "             digest size (`C`)."]
527            #[doc = ""]
528            #[doc = "# Returns"]
529            #[doc = ""]
530            #[doc = "This function returns the result of the operation."]
531            #[doc = ""]
532            #[doc = "# Example"]
533            #[doc = ""]
534            #[doc = "```"]
535            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
536            #[doc = concat!("let mut hasher = ", stringify!($name), "::<", stringify!($max), ">::new().unwrap();")]
537            #[doc = ""]
538            #[doc = "// use the hasher ..."]
539            #[doc = ""]
540            #[doc = concat!("let mut output = [0u8; ", stringify!($max), "];")]
541            #[doc = "assert!(hasher.finalize_into_exact(&mut output).is_ok());"]
542            #[doc = "```"]
543            #[doc = ""]
544            #[doc = "**Note**: If the size of the output buffer is not exactly `C`, see [`finalize_into`] for"]
545            #[doc = "greater flexibility, or [`finalize_into_sized`] if the size is known at compile time but is"]
546            #[doc = "not exactly `C`."]
547            #[doc = ""]
548            #[doc = "[`finalize_into`]: Self::finalize_into"]
549            #[doc = "[`finalize_into_sized`]: Self::finalize_into_sized"]
550            #[inline]
551            pub fn finalize_into_exact(self, output: &mut [u8; C]) -> $crate::opaque_res::Res {
552                unsafe { self.finalize_unchecked(output) }
553            }
554
555            #[doc = concat!(
556                "Finalize the `", stringify!($name), "` hashing process, returning the result as an array."
557            )]
558            #[doc = ""]
559            #[doc = "# Returns"]
560            #[doc = ""]
561            #[doc = "On success, this returns the output digest as an array."]
562            #[doc = ""]
563            #[doc = "# Errors"]
564            #[doc = ""]
565            #[doc = "If the underlying finalize function fails."]
566            #[doc = ""]
567            #[doc = "# Example"]
568            #[doc = ""]
569            #[doc = "```"]
570            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
571            #[doc = concat!(
572                "let mut hasher = ", stringify!($name), "::<", stringify!($max), ">::new().unwrap();"
573            )]
574            #[doc = ""]
575            #[doc = "// use the hasher ..."]
576            #[doc = ""]
577            #[doc = "let res = hasher.try_finalize().unwrap();"]
578            #[doc = concat!("assert_eq!(res.len(), ", stringify!($max), ");")]
579            #[doc = "```"]
580            #[inline]
581            pub fn try_finalize(self) -> Result<[u8; C], $crate::error::Unspecified> {
582                let mut buf = [0u8; C];
583                self.finalize_into_exact(&mut buf).unit_err(buf)
584            }
585
586            panic_api! {
587            #[doc = concat!(
588                "Finalize the `", stringify!($name),
589                "` hashing process, returning the result as an array, panicking on"
590            )]
591            #[doc = "failure."]
592            #[doc = ""]
593            #[doc = "# Returns"]
594            #[doc = ""]
595            #[doc = "On success, this returns the output digest as an array."]
596            #[doc = ""]
597            #[doc = "# Panics"]
598            #[doc = ""]
599            #[doc = "If the underlying finalize function fails."]
600            #[doc = ""]
601            #[doc = "If panicking is not acceptable for your use case, consider using [`try_finalize`] instead."]
602            #[doc = ""]
603            #[doc = "# Example"]
604            #[doc = ""]
605            #[doc = "```"]
606            #[doc = concat!("use wolf_crypto::hash::", stringify!($name), ";")]
607            #[doc = concat!("let mut hasher = ", stringify!($name), "::<", stringify!($max), ">::new().unwrap();")]
608            #[doc = ""]
609            #[doc = "// use the hasher ..."]
610            #[doc = ""]
611            #[doc = "let res = hasher.finalize();"]
612            #[doc = concat!("assert_eq!(res.len(), ", stringify!($max), ");")]
613            #[doc = "```"]
614            #[doc = ""]
615            #[doc = "[`try_finalize`]: Self::try_finalize"]
616            #[track_caller]
617            pub fn finalize(self) -> [u8; C] {
618                self.try_finalize()
619                    .expect(concat!("Failed to finalize in `", stringify!($name), "`"))
620            }
621            }
622        }
623
624        // SAFETY:
625        // All methods which mutate the underlying state require a mutable reference,
626        // the only way to obtain a mutable reference across thread boundaries is via
627        // synchronization or unsafe in Rust (which then would be the user's responsibility).
628        unsafe impl<const C: usize> Send for $name<C> {}
629
630        // SAFETY:
631        // There is no providing of interior mutability, all methods which mutate the underlying
632        // state require a mutable reference, thus making this safe to mark `Sync`.
633        unsafe impl<const C: usize> Sync for $name<C> {}
634
635        #[cfg(test)]
636        mod unit_tests {
637            use super::*;
638            use digest::Digest;
639
640            #[test]
641            fn rust_crypto_equivalence() {
642                many_blake!($name => |mut wolf, mut rc| {
643                    let input = b"hello world";
644
645                    assert!(wolf.try_update(input.as_slice()).is_ok());
646                    rc.update(input.as_slice());
647
648                    let w_out  = wolf.try_finalize().unwrap();
649                    let rc_out = rc.finalize();
650
651                    assert_eq!(w_out.as_slice(), rc_out.as_slice());
652                });
653            }
654
655
656            #[test]
657            fn rust_crypto_partial_update_equivalence() {
658                many_blake!($name => |mut wolf, mut rc| {
659                    let input = b"hello w";
660
661                    assert!(wolf.try_update(input.as_slice()).is_ok());
662                    rc.update(input.as_slice());
663
664                    let f = b"orld";
665
666                    wolf.update(f.as_slice());
667                    rc.update(f.as_slice());
668
669                    let w_out  = wolf.try_finalize().unwrap();
670                    let rc_out = rc.finalize();
671
672                    assert_eq!(w_out.as_slice(), rc_out.as_slice());
673                });
674            }
675
676            #[test]
677            fn rust_crypto_empty_equivalence() {
678                many_blake!($name => |mut wolf, mut rc| {
679                    let input = b"";
680
681                    assert!(wolf.try_update(input.as_slice()).is_ok());
682                    rc.update(input.as_slice());
683
684                    let w_out  = wolf.try_finalize().unwrap();
685                    let rc_out = rc.finalize();
686
687                    assert_eq!(w_out.as_slice(), rc_out.as_slice());
688                })
689            }
690
691            #[test]
692            fn rust_crypto_1mb_equivalence() {
693                let input = vec![7u8; 1_000_000];
694
695                many_blake!($name => |mut wolf, mut rc| {
696                    assert!(wolf.try_update(input.as_slice()).is_ok());
697                    rc.update(input.as_slice());
698
699                    let w_out  = wolf.try_finalize().unwrap();
700                    let rc_out = rc.finalize();
701
702                    assert_eq!(w_out.as_slice(), rc_out.as_slice());
703                })
704            }
705        }
706
707        #[cfg(test)]
708        mod property_tests {
709            use super::*;
710            use digest::Digest;
711
712            use $crate::aes::test_utils::{BoundList, AnyList};
713            use proptest::prelude::*;
714
715            proptest! {
716                #![proptest_config(ProptestConfig::with_cases(15_000))]
717
718                #[test]
719                fn rust_crypto_eq_wolf_single_update(
720                    input in any::<BoundList<1024>>()
721                ) {
722                    many_blake!($name => |mut wolf, mut rc| {
723                        prop_assert!(wolf.try_update(input.as_slice()).is_ok());
724                        rc.update(input.as_slice());
725
726                        let finalized = wolf.try_finalize().unwrap();
727                        let rc_finalized = rc.finalize();
728
729                        prop_assert_eq!(finalized.as_slice(), rc_finalized.as_slice());
730                    });
731                }
732            }
733
734            proptest! {
735                #![proptest_config(ProptestConfig::with_cases(5_000))]
736
737                // this takes a while, as testing multiple different digest sizes, iterating over
738                // all inputs updating each.
739                #[test]
740                fn rust_crypto_eq_wolf_arb_updates(
741                    inputs in any::<AnyList<32, BoundList<512>>>()
742                ) {
743                    many_blake!($name => |mut wolf, mut rc| {
744                        for input in inputs.as_slice().iter() {
745                            prop_assert!(wolf.try_update(input.as_slice()).is_ok());
746                            rc.update(input.as_slice());
747                        }
748
749                        let finalized = wolf.try_finalize().unwrap();
750                        let rc_finalized = rc.finalize();
751
752                        prop_assert_eq!(finalized.as_slice(), rc_finalized.as_slice());
753                    });
754                }
755            }
756        }
757    };
758}
759
760#[cfg(test)]
761macro_rules! many_blake {
762    (
763        $blake:ident => |mut $wolf:ident, mut $rc:ident $(, $sz:ident)?| $do:expr
764    ) => {{
765        many_blake! { $blake => |mut $wolf is 32, mut $rc is U32 $(, $sz)?| $do }
766        many_blake! { $blake => |mut $wolf is 28, mut $rc is U28 $(, $sz)?| $do }
767        many_blake! { $blake => |mut $wolf is 24, mut $rc is U24 $(, $sz)?| $do }
768        many_blake! { $blake => |mut $wolf is 22, mut $rc is U22 $(, $sz)?| $do }
769        many_blake! { $blake => |mut $wolf is 20, mut $rc is U20 $(, $sz)?| $do }
770        many_blake! { $blake => |mut $wolf is 14, mut $rc is U14 $(, $sz)?| $do }
771        many_blake! { $blake => |mut $wolf is 12, mut $rc is U12 $(, $sz)?| $do }
772        many_blake! { $blake => |mut $wolf is 9, mut $rc is U9 $(, $sz)?| $do }
773        many_blake! { $blake => |mut $wolf is 8, mut $rc is U8 $(, $sz)?| $do }
774        many_blake! { $blake => |mut $wolf is 6, mut $rc is U6 $(, $sz)?| $do }
775        many_blake! { $blake => |mut $wolf is 4, mut $rc is U4 $(, $sz)?| $do }
776        many_blake! { $blake => |mut $wolf is 2, mut $rc is U2 $(, $sz)?| $do }
777        many_blake! { $blake => |mut $wolf is 1, mut $rc is U1 $(, $sz)?| $do }
778    }};
779    (
780        $blake:ident => |mut $wolf:ident is $sz:literal, mut $rc:ident is $rSz:ident $(, $gS:ident)?|
781        $do:expr
782    ) => {{
783        $(let $gS = $sz;)?
784        let mut $wolf = $blake::<{ $sz }>::new().unwrap();
785        let mut $rc   = blake2::$blake::<::digest::consts::$rSz>::new();
786        $do
787    }}
788}
789
790#[cfg(test)]
791pub struct KeyedVector {
792    pub input: &'static [u8],
793    pub key: &'static [u8],
794    pub hash: &'static [u8]
795}
796
797#[cfg(test)]
798macro_rules! ingest_blake_vectors {
799    (
800        $(
801            in: $input:literal
802            key: $key:literal
803            hash: $output:literal
804        )*
805    ) => {
806        &[
807            $(
808                $crate::hash::blake_api::KeyedVector {
809                    input: &hex_literal::hex!($input),
810                    key: &hex_literal::hex!($key),
811                    hash: &hex_literal::hex!($output)
812                }
813            ),*
814        ]
815    };
816    ($ident:ident => $($tt:tt)*) => {
817        const $ident: &'static [$crate::hash::blake_api::KeyedVector] = ingest_blake_vectors!(
818            $($tt)*
819        );
820    }
821}