ring/
digest.rs

1// Copyright 2015-2019 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15//! SHA-2 and the legacy SHA-1 digest algorithm.
16//!
17//! If all the data is available in a single contiguous slice then the `digest`
18//! function should be used. Otherwise, the digest can be calculated in
19//! multiple steps using `Context`.
20
21use self::{
22    dynstate::DynState,
23    sha2::{SHA256_BLOCK_LEN, SHA512_BLOCK_LEN},
24};
25use crate::{
26    bits::{BitLength, FromByteLen as _},
27    cpu, debug, error,
28    polyfill::{self, slice, sliceutil},
29};
30use core::num::Wrapping;
31
32pub(crate) use self::finish_error::FinishError;
33
34mod dynstate;
35mod sha1;
36mod sha2;
37
38#[derive(Clone)]
39pub(crate) struct BlockContext {
40    state: DynState,
41
42    // Note that SHA-512 has a 128-bit input bit counter, but this
43    // implementation only supports up to 2^64-1 input bits for all algorithms,
44    // so a 64-bit counter is more than sufficient.
45    completed_bytes: u64,
46
47    /// The context's algorithm.
48    pub algorithm: &'static Algorithm,
49}
50
51impl BlockContext {
52    pub(crate) fn new(algorithm: &'static Algorithm) -> Self {
53        Self {
54            state: algorithm.initial_state.clone(),
55            completed_bytes: 0,
56            algorithm,
57        }
58    }
59
60    /// Processes all the full blocks in `input`, returning the partial block
61    /// at the end, which may be empty.
62    pub(crate) fn update<'i>(&mut self, input: &'i [u8], cpu_features: cpu::Features) -> &'i [u8] {
63        let (completed_bytes, leftover) = self.block_data_order(input, cpu_features);
64        // Using saturated addition here allows `update` to be infallible and
65        // panic-free. If we were to reach the maximum value here then `finish`
66        // will detect that we processed too much data when it converts this to
67        // a bit length.
68        self.completed_bytes = self
69            .completed_bytes
70            .saturating_add(polyfill::u64_from_usize(completed_bytes));
71        leftover
72    }
73
74    // On input, `block[..num_pending]` is the (possibly-empty) last *partial*
75    // chunk of input. It *must* be partial; that is, it is required that
76    // `num_pending < self.algorithm.block_len`.
77    //
78    // `block` may be arbitrarily overwritten.
79    pub(crate) fn try_finish(
80        mut self,
81        block: &mut [u8; MAX_BLOCK_LEN],
82        num_pending: usize,
83        cpu_features: cpu::Features,
84    ) -> Result<Digest, FinishError> {
85        let completed_bits = self
86            .completed_bytes
87            .checked_add(polyfill::u64_from_usize(num_pending))
88            .ok_or_else(|| {
89                // Choosing self.completed_bytes here is lossy & somewhat arbitrary.
90                InputTooLongError::new(self.completed_bytes)
91            })
92            .and_then(BitLength::from_byte_len)
93            .map_err(FinishError::input_too_long)?;
94
95        let block_len = self.algorithm.block_len();
96        let block = &mut block[..block_len];
97
98        let padding = match block.get_mut(num_pending..) {
99            Some([separator, padding @ ..]) => {
100                *separator = 0x80;
101                padding
102            }
103            // Precondition violated.
104            unreachable => {
105                return Err(FinishError::pending_not_a_partial_block(
106                    unreachable.as_deref(),
107                ));
108            }
109        };
110
111        let padding = match padding
112            .len()
113            .checked_sub(self.algorithm.block_len.len_len())
114        {
115            Some(_) => padding,
116            None => {
117                padding.fill(0);
118                let (completed_bytes, leftover) = self.block_data_order(block, cpu_features);
119                debug_assert_eq!((completed_bytes, leftover.len()), (block_len, 0));
120                // We don't increase |self.completed_bytes| because the padding
121                // isn't data, and so it isn't included in the data length.
122                &mut block[..]
123            }
124        };
125
126        let (to_zero, len) = padding.split_at_mut(padding.len() - 8);
127        to_zero.fill(0);
128        len.copy_from_slice(&completed_bits.to_be_bytes());
129
130        let (completed_bytes, leftover) = self.block_data_order(block, cpu_features);
131        debug_assert_eq!((completed_bytes, leftover.len()), (block_len, 0));
132
133        Ok(Digest {
134            algorithm: self.algorithm,
135            value: self.state.format_output(),
136        })
137    }
138
139    #[must_use]
140    fn block_data_order<'d>(
141        &mut self,
142        data: &'d [u8],
143        cpu_features: cpu::Features,
144    ) -> (usize, &'d [u8]) {
145        (self.algorithm.block_data_order)(&mut self.state, data, cpu_features)
146    }
147}
148
149pub(crate) type InputTooLongError = error::InputTooLongError<u64>;
150
151cold_exhaustive_error! {
152    enum finish_error::FinishError {
153        input_too_long => InputTooLong(InputTooLongError),
154        pending_not_a_partial_block_inner => PendingNotAPartialBlock(usize),
155    }
156}
157
158impl FinishError {
159    #[cold]
160    #[inline(never)]
161    fn pending_not_a_partial_block(padding: Option<&[u8]>) -> Self {
162        match padding {
163            None => Self::pending_not_a_partial_block_inner(0),
164            Some(padding) => Self::pending_not_a_partial_block_inner(padding.len()),
165        }
166    }
167}
168
169/// A context for multi-step (Init-Update-Finish) digest calculations.
170///
171/// # Examples
172///
173/// ```
174/// use ring::digest;
175///
176/// let one_shot = digest::digest(&digest::SHA384, b"hello, world");
177///
178/// let mut ctx = digest::Context::new(&digest::SHA384);
179/// ctx.update(b"hello");
180/// ctx.update(b", ");
181/// ctx.update(b"world");
182/// let multi_part = ctx.finish();
183///
184/// assert_eq!(&one_shot.as_ref(), &multi_part.as_ref());
185/// ```
186#[derive(Clone)]
187pub struct Context {
188    block: BlockContext,
189    // TODO: More explicitly force 64-bit alignment for |pending|.
190    pending: [u8; MAX_BLOCK_LEN],
191
192    // Invariant: `self.num_pending < self.block.algorithm.block_len`.
193    num_pending: usize,
194}
195
196impl Context {
197    /// Constructs a new context.
198    pub fn new(algorithm: &'static Algorithm) -> Self {
199        Self {
200            block: BlockContext::new(algorithm),
201            pending: [0u8; MAX_BLOCK_LEN],
202            num_pending: 0,
203        }
204    }
205
206    pub(crate) fn clone_from(block: &BlockContext) -> Self {
207        Self {
208            block: block.clone(),
209            pending: [0u8; MAX_BLOCK_LEN],
210            num_pending: 0,
211        }
212    }
213
214    /// Updates the digest with all the data in `data`.
215    pub fn update(&mut self, data: &[u8]) {
216        let cpu_features = cpu::features();
217
218        let block_len = self.block.algorithm.block_len();
219        let buffer = &mut self.pending[..block_len];
220
221        let to_digest = if self.num_pending == 0 {
222            data
223        } else {
224            let buffer_to_fill = match buffer.get_mut(self.num_pending..) {
225                Some(buffer_to_fill) => buffer_to_fill,
226                None => {
227                    // Impossible because of the invariant.
228                    unreachable!();
229                }
230            };
231            sliceutil::overwrite_at_start(buffer_to_fill, data);
232            match slice::split_at_checked(data, buffer_to_fill.len()) {
233                Some((just_copied, to_digest)) => {
234                    debug_assert_eq!(buffer_to_fill.len(), just_copied.len());
235                    debug_assert_eq!(self.num_pending + just_copied.len(), block_len);
236                    let leftover = self.block.update(buffer, cpu_features);
237                    debug_assert_eq!(leftover.len(), 0);
238                    self.num_pending = 0;
239                    to_digest
240                }
241                None => {
242                    self.num_pending += data.len();
243                    // If `data` isn't enough to complete a block, buffer it and stop.
244                    debug_assert!(self.num_pending < block_len);
245                    return;
246                }
247            }
248        };
249
250        let leftover = self.block.update(to_digest, cpu_features);
251        sliceutil::overwrite_at_start(buffer, leftover);
252        self.num_pending = leftover.len();
253        debug_assert!(self.num_pending < block_len);
254    }
255
256    /// Finalizes the digest calculation and returns the digest value.
257    ///
258    /// `finish` consumes the context so it cannot be (mis-)used after `finish`
259    /// has been called.
260    pub fn finish(self) -> Digest {
261        let cpu = cpu::features();
262        self.try_finish(cpu)
263            .map_err(error::erase::<InputTooLongError>)
264            .unwrap()
265    }
266
267    pub(crate) fn try_finish(
268        mut self,
269        cpu_features: cpu::Features,
270    ) -> Result<Digest, InputTooLongError> {
271        self.block
272            .try_finish(&mut self.pending, self.num_pending, cpu_features)
273            .map_err(|err| match err {
274                FinishError::InputTooLong(i) => i,
275                FinishError::PendingNotAPartialBlock(_) => {
276                    // Due to invariant.
277                    unreachable!()
278                }
279            })
280    }
281
282    /// The algorithm that this context is using.
283    #[inline(always)]
284    pub fn algorithm(&self) -> &'static Algorithm {
285        self.block.algorithm
286    }
287}
288
289/// Returns the digest of `data` using the given digest algorithm.
290///
291/// # Examples:
292///
293/// ```
294/// # #[cfg(feature = "alloc")]
295/// # {
296/// use ring::{digest, test};
297/// let expected_hex = "09ca7e4eaa6e8ae9c7d261167129184883644d07dfba7cbfbc4c8a2e08360d5b";
298/// let expected: Vec<u8> = test::from_hex(expected_hex).unwrap();
299/// let actual = digest::digest(&digest::SHA256, b"hello, world");
300///
301/// assert_eq!(&expected, &actual.as_ref());
302/// # }
303/// ```
304pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest {
305    let cpu = cpu::features();
306    Digest::compute_from(algorithm, data, cpu)
307        .map_err(error::erase::<InputTooLongError>)
308        .unwrap()
309}
310
311/// A calculated digest value.
312///
313/// Use [`Self::as_ref`] to get the value as a `&[u8]`.
314#[derive(Clone, Copy)]
315pub struct Digest {
316    value: Output,
317    algorithm: &'static Algorithm,
318}
319
320impl Digest {
321    pub(crate) fn compute_from(
322        algorithm: &'static Algorithm,
323        data: &[u8],
324        cpu: cpu::Features,
325    ) -> Result<Self, InputTooLongError> {
326        let mut ctx = Context::new(algorithm);
327        ctx.update(data);
328        ctx.try_finish(cpu)
329    }
330
331    /// The algorithm that was used to calculate the digest value.
332    #[inline(always)]
333    pub fn algorithm(&self) -> &'static Algorithm {
334        self.algorithm
335    }
336}
337
338impl AsRef<[u8]> for Digest {
339    #[inline(always)]
340    fn as_ref(&self) -> &[u8] {
341        &self.value.0[..self.algorithm.output_len()]
342    }
343}
344
345impl core::fmt::Debug for Digest {
346    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
347        write!(fmt, "{:?}:", self.algorithm)?;
348        debug::write_hex_bytes(fmt, self.as_ref())
349    }
350}
351
352/// A digest algorithm.
353pub struct Algorithm {
354    output_len: OutputLen,
355    chaining_len: usize,
356    block_len: BlockLen,
357
358    /// `block_data_order` processes all the full blocks of data in `data`. It
359    /// returns the number of bytes processed and the unprocessed data, which
360    /// is guaranteed to be less than `block_len` bytes long.
361    block_data_order: for<'d> fn(
362        state: &mut DynState,
363        data: &'d [u8],
364        cpu_features: cpu::Features,
365    ) -> (usize, &'d [u8]),
366
367    initial_state: DynState,
368
369    id: AlgorithmID,
370}
371
372#[derive(Debug, Eq, PartialEq)]
373enum AlgorithmID {
374    SHA1,
375    SHA256,
376    SHA384,
377    SHA512,
378    SHA512_256,
379}
380
381impl PartialEq for Algorithm {
382    fn eq(&self, other: &Self) -> bool {
383        self.id == other.id
384    }
385}
386
387impl Eq for Algorithm {}
388
389derive_debug_via_id!(Algorithm);
390
391impl Algorithm {
392    /// The internal block length.
393    pub fn block_len(&self) -> usize {
394        self.block_len.into()
395    }
396
397    /// The size of the chaining value of the digest function, in bytes.
398    ///
399    /// For non-truncated algorithms (SHA-1, SHA-256, SHA-512), this is equal
400    /// to [`Self::output_len()`]. For truncated algorithms (e.g. SHA-384,
401    /// SHA-512/256), this is equal to the length before truncation. This is
402    /// mostly helpful for determining the size of an HMAC key that is
403    /// appropriate for the digest algorithm.
404    pub fn chaining_len(&self) -> usize {
405        self.chaining_len
406    }
407
408    /// The length of a finalized digest.
409    pub fn output_len(&self) -> usize {
410        self.output_len.into()
411    }
412}
413
414/// SHA-1 as specified in [FIPS 180-4]. Deprecated.
415///
416/// [FIPS 180-4]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
417pub static SHA1_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm {
418    output_len: sha1::OUTPUT_LEN,
419    chaining_len: sha1::CHAINING_LEN,
420    block_len: sha1::BLOCK_LEN,
421    block_data_order: dynstate::sha1_block_data_order,
422    initial_state: DynState::new32([
423        Wrapping(0x67452301u32),
424        Wrapping(0xefcdab89u32),
425        Wrapping(0x98badcfeu32),
426        Wrapping(0x10325476u32),
427        Wrapping(0xc3d2e1f0u32),
428        Wrapping(0),
429        Wrapping(0),
430        Wrapping(0),
431    ]),
432    id: AlgorithmID::SHA1,
433};
434
435/// SHA-256 as specified in [FIPS 180-4].
436///
437/// [FIPS 180-4]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
438pub static SHA256: Algorithm = Algorithm {
439    output_len: OutputLen::_256,
440    chaining_len: SHA256_OUTPUT_LEN,
441    block_len: SHA256_BLOCK_LEN,
442    block_data_order: dynstate::sha256_block_data_order,
443    initial_state: DynState::new32([
444        Wrapping(0x6a09e667u32),
445        Wrapping(0xbb67ae85u32),
446        Wrapping(0x3c6ef372u32),
447        Wrapping(0xa54ff53au32),
448        Wrapping(0x510e527fu32),
449        Wrapping(0x9b05688cu32),
450        Wrapping(0x1f83d9abu32),
451        Wrapping(0x5be0cd19u32),
452    ]),
453    id: AlgorithmID::SHA256,
454};
455
456/// SHA-384 as specified in [FIPS 180-4].
457///
458/// [FIPS 180-4]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
459pub static SHA384: Algorithm = Algorithm {
460    output_len: OutputLen::_384,
461    chaining_len: SHA512_OUTPUT_LEN,
462    block_len: SHA512_BLOCK_LEN,
463    block_data_order: dynstate::sha512_block_data_order,
464    initial_state: DynState::new64([
465        Wrapping(0xcbbb9d5dc1059ed8),
466        Wrapping(0x629a292a367cd507),
467        Wrapping(0x9159015a3070dd17),
468        Wrapping(0x152fecd8f70e5939),
469        Wrapping(0x67332667ffc00b31),
470        Wrapping(0x8eb44a8768581511),
471        Wrapping(0xdb0c2e0d64f98fa7),
472        Wrapping(0x47b5481dbefa4fa4),
473    ]),
474    id: AlgorithmID::SHA384,
475};
476
477/// SHA-512 as specified in [FIPS 180-4].
478///
479/// [FIPS 180-4]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
480pub static SHA512: Algorithm = Algorithm {
481    output_len: OutputLen::_512,
482    chaining_len: SHA512_OUTPUT_LEN,
483    block_len: SHA512_BLOCK_LEN,
484    block_data_order: dynstate::sha512_block_data_order,
485    initial_state: DynState::new64([
486        Wrapping(0x6a09e667f3bcc908),
487        Wrapping(0xbb67ae8584caa73b),
488        Wrapping(0x3c6ef372fe94f82b),
489        Wrapping(0xa54ff53a5f1d36f1),
490        Wrapping(0x510e527fade682d1),
491        Wrapping(0x9b05688c2b3e6c1f),
492        Wrapping(0x1f83d9abfb41bd6b),
493        Wrapping(0x5be0cd19137e2179),
494    ]),
495    id: AlgorithmID::SHA512,
496};
497
498/// SHA-512/256 as specified in [FIPS 180-4].
499///
500/// This is *not* the same as just truncating the output of SHA-512, as
501/// SHA-512/256 has its own initial state distinct from SHA-512's initial
502/// state.
503///
504/// [FIPS 180-4]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
505pub static SHA512_256: Algorithm = Algorithm {
506    output_len: OutputLen::_256,
507    chaining_len: SHA512_OUTPUT_LEN,
508    block_len: SHA512_BLOCK_LEN,
509    block_data_order: dynstate::sha512_block_data_order,
510    initial_state: DynState::new64([
511        Wrapping(0x22312194fc2bf72c),
512        Wrapping(0x9f555fa3c84c64c2),
513        Wrapping(0x2393b86b6f53b151),
514        Wrapping(0x963877195940eabd),
515        Wrapping(0x96283ee2a88effe3),
516        Wrapping(0xbe5e1e2553863992),
517        Wrapping(0x2b0199fc2c85b8aa),
518        Wrapping(0x0eb72ddc81c52ca2),
519    ]),
520    id: AlgorithmID::SHA512_256,
521};
522
523#[derive(Clone, Copy)]
524struct Output([u8; MAX_OUTPUT_LEN]);
525
526/// The maximum block length ([`Algorithm::block_len()`]) of all the algorithms
527/// in this module.
528pub const MAX_BLOCK_LEN: usize = BlockLen::MAX.into();
529
530/// The maximum output length ([`Algorithm::output_len()`]) of all the
531/// algorithms in this module.
532pub const MAX_OUTPUT_LEN: usize = OutputLen::MAX.into();
533
534/// The maximum chaining length ([`Algorithm::chaining_len()`]) of all the
535/// algorithms in this module.
536pub const MAX_CHAINING_LEN: usize = MAX_OUTPUT_LEN;
537
538#[inline]
539fn format_output<T, F, const N: usize>(input: [Wrapping<T>; sha2::CHAINING_WORDS], f: F) -> Output
540where
541    F: Fn(T) -> [u8; N],
542    T: Copy,
543{
544    let mut output = Output([0; MAX_OUTPUT_LEN]);
545    output
546        .0
547        .chunks_mut(N)
548        .zip(input.iter().copied().map(|Wrapping(w)| f(w)))
549        .for_each(|(o, i)| {
550            o.copy_from_slice(&i);
551        });
552    output
553}
554
555/// The length of the output of SHA-1, in bytes.
556pub const SHA1_OUTPUT_LEN: usize = sha1::OUTPUT_LEN.into();
557
558/// The length of the output of SHA-256, in bytes.
559pub const SHA256_OUTPUT_LEN: usize = OutputLen::_256.into();
560
561/// The length of the output of SHA-384, in bytes.
562pub const SHA384_OUTPUT_LEN: usize = OutputLen::_384.into();
563
564/// The length of the output of SHA-512, in bytes.
565pub const SHA512_OUTPUT_LEN: usize = OutputLen::_512.into();
566
567/// The length of the output of SHA-512/256, in bytes.
568pub const SHA512_256_OUTPUT_LEN: usize = OutputLen::_256.into();
569
570#[derive(Clone, Copy)]
571enum BlockLen {
572    _512 = 512 / 8,
573    _1024 = 1024 / 8, // MAX
574}
575
576impl BlockLen {
577    const MAX: Self = Self::_1024;
578    #[inline(always)]
579    const fn into(self) -> usize {
580        self as usize
581    }
582
583    #[inline(always)]
584    const fn len_len(self) -> usize {
585        let len_len = match self {
586            BlockLen::_512 => LenLen::_64,
587            BlockLen::_1024 => LenLen::_128,
588        };
589        len_len as usize
590    }
591}
592
593#[derive(Clone, Copy)]
594enum LenLen {
595    _64 = 64 / 8,
596    _128 = 128 / 8,
597}
598
599#[derive(Clone, Copy)]
600enum OutputLen {
601    _160 = 160 / 8,
602    _256 = 256 / 8,
603    _384 = 384 / 8,
604    _512 = 512 / 8, // MAX
605}
606
607impl OutputLen {
608    const MAX: Self = Self::_512;
609
610    #[inline(always)]
611    const fn into(self) -> usize {
612        self as usize
613    }
614}
615
616#[cfg(test)]
617mod tests {
618    mod max_input {
619        extern crate alloc;
620        use super::super::super::digest;
621        use crate::polyfill::u64_from_usize;
622        use alloc::vec;
623
624        macro_rules! max_input_tests {
625            ( $algorithm_name:ident ) => {
626                mod $algorithm_name {
627                    use super::super::super::super::digest;
628
629                    #[test]
630                    fn max_input_test() {
631                        super::max_input_test(&digest::$algorithm_name);
632                    }
633
634                    #[test]
635                    #[should_panic]
636                    fn too_long_input_test_block() {
637                        super::too_long_input_test_block(&digest::$algorithm_name);
638                    }
639
640                    #[test]
641                    #[should_panic]
642                    fn too_long_input_test_byte() {
643                        super::too_long_input_test_byte(&digest::$algorithm_name);
644                    }
645                }
646            };
647        }
648
649        fn max_input_test(alg: &'static digest::Algorithm) {
650            let mut context = nearly_full_context(alg);
651            let next_input = vec![0u8; alg.block_len() - 1];
652            context.update(&next_input);
653            let _ = context.finish(); // no panic
654        }
655
656        fn too_long_input_test_block(alg: &'static digest::Algorithm) {
657            let mut context = nearly_full_context(alg);
658            let next_input = vec![0u8; alg.block_len()];
659            context.update(&next_input);
660            let _ = context.finish(); // should panic
661        }
662
663        fn too_long_input_test_byte(alg: &'static digest::Algorithm) {
664            let mut context = nearly_full_context(alg);
665            let next_input = vec![0u8; alg.block_len() - 1];
666            context.update(&next_input);
667            context.update(&[0]);
668            let _ = context.finish(); // should panic
669        }
670
671        fn nearly_full_context(alg: &'static digest::Algorithm) -> digest::Context {
672            // All implementations currently support up to 2^64-1 bits
673            // of input; according to the spec, SHA-384 and SHA-512
674            // support up to 2^128-1, but that's not implemented yet.
675            let max_bytes = 1u64 << (64 - 3);
676            let max_blocks = max_bytes / u64_from_usize(alg.block_len());
677            let completed_bytes = (max_blocks - 1) * u64_from_usize(alg.block_len());
678            digest::Context {
679                block: digest::BlockContext {
680                    state: alg.initial_state.clone(),
681                    completed_bytes,
682                    algorithm: alg,
683                },
684                pending: [0u8; digest::MAX_BLOCK_LEN],
685                num_pending: 0,
686            }
687        }
688
689        max_input_tests!(SHA1_FOR_LEGACY_USE_ONLY);
690        max_input_tests!(SHA256);
691        max_input_tests!(SHA384);
692        max_input_tests!(SHA512);
693    }
694}