aws_lc_rs/
digest.rs

1// Copyright 2015-2019 Brian Smith.
2// SPDX-License-Identifier: ISC
3// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4// SPDX-License-Identifier: Apache-2.0 OR ISC
5
6//! SHA-2 and the legacy SHA-1 digest algorithm.
7//!
8//! If all the data is available in a single contiguous slice then the `digest`
9//! function should be used. Otherwise, the digest can be calculated in
10//! multiple steps using `Context`.
11
12//! # Example
13//!
14//! ```
15//! use aws_lc_rs::digest;
16//!
17//! // Using `digest::digest`
18//! let one_shot = digest::digest(&digest::SHA384, b"hello, world");
19//!
20//! // Using `digest::Context`
21//! let mut ctx = digest::Context::new(&digest::SHA384);
22//! ctx.update(b"hello");
23//! ctx.update(b", ");
24//! ctx.update(b"world");
25//! let multi_part = ctx.finish();
26//!
27//! assert_eq!(&one_shot.as_ref(), &multi_part.as_ref());
28//! ```
29
30#![allow(non_snake_case)]
31use crate::fips::indicator_check;
32use crate::{debug, derive_debug_via_id};
33
34pub(crate) mod digest_ctx;
35mod sha;
36use crate::aws_lc::{
37    EVP_DigestFinal, EVP_DigestUpdate, EVP_sha1, EVP_sha224, EVP_sha256, EVP_sha384, EVP_sha3_256,
38    EVP_sha3_384, EVP_sha3_512, EVP_sha512, EVP_sha512_256, EVP_MD,
39};
40use crate::error::Unspecified;
41use crate::ptr::ConstPointer;
42use core::mem::MaybeUninit;
43use digest_ctx::DigestContext;
44pub use sha::{
45    SHA1_FOR_LEGACY_USE_ONLY, SHA1_OUTPUT_LEN, SHA224, SHA224_OUTPUT_LEN, SHA256,
46    SHA256_OUTPUT_LEN, SHA384, SHA384_OUTPUT_LEN, SHA3_256, SHA3_384, SHA3_512, SHA512, SHA512_256,
47    SHA512_256_OUTPUT_LEN, SHA512_OUTPUT_LEN,
48};
49// TODO: Uncomment when MSRV >= 1.64
50//use core::ffi::c_uint;
51use std::os::raw::c_uint;
52
53/// A context for multi-step (Init-Update-Finish) digest calculations.
54//
55// # FIPS
56// Context must be used with one of the following algorithms:
57// * `SHA1_FOR_LEGACY_USE_ONLY`
58// * `SHA224`
59// * `SHA256`
60// * `SHA384`
61// * `SHA512`
62// * `SHA512_256`
63#[derive(Clone)]
64pub struct Context {
65    /// The context's algorithm.
66    pub(crate) algorithm: &'static Algorithm,
67    digest_ctx: DigestContext,
68    // The spec specifies that SHA-1 and SHA-256 support up to
69    // 2^64-1 bits of input. SHA-384 and SHA-512 support up to
70    // 2^128-1 bits.
71    // Implementations of `digest` only support up
72    // to 2^64-1 bits of input, which should be sufficient enough for
73    // practical use cases.
74    msg_len: u64,
75    max_input_reached: bool,
76}
77
78impl Context {
79    /// Constructs a new context.
80    ///
81    /// # Panics
82    ///
83    /// `new` panics if it fails to initialize an aws-lc digest context for the given
84    /// algorithm.
85    #[must_use]
86    pub fn new(algorithm: &'static Algorithm) -> Self {
87        Self {
88            algorithm,
89            digest_ctx: DigestContext::new(algorithm).unwrap(),
90            msg_len: 0u64,
91            max_input_reached: false,
92        }
93    }
94
95    /// Updates the message to digest with all the data in `data`.
96    ///
97    /// # Panics
98    /// Panics if update causes total input length to exceed maximum allowed (`u64::MAX`).
99    #[inline]
100    pub fn update(&mut self, data: &[u8]) {
101        Self::try_update(self, data).expect("digest update failed");
102    }
103
104    #[inline]
105    fn try_update(&mut self, data: &[u8]) -> Result<(), Unspecified> {
106        unsafe {
107            // Check if the message has reached the algorithm's maximum allowed input, or overflowed
108            // the msg_len counter.
109            let (msg_len, overflowed) = self.msg_len.overflowing_add(data.len() as u64);
110            if overflowed || msg_len > self.algorithm.max_input_len {
111                return Err(Unspecified);
112            }
113
114            self.msg_len = msg_len;
115            self.max_input_reached = self.msg_len == self.algorithm.max_input_len;
116
117            // Doesn't require boundary_check! guard
118            if 1 != EVP_DigestUpdate(
119                self.digest_ctx.as_mut_ptr(),
120                data.as_ptr().cast(),
121                data.len(),
122            ) {
123                return Err(Unspecified);
124            }
125            Ok(())
126        }
127    }
128
129    /// Finalizes the digest calculation and returns the digest value.
130    ///
131    /// `finish` consumes the context so it cannot be (mis-)used after `finish`
132    /// has been called.
133    ///
134    /// # Panics
135    /// Panics if the digest is unable to be finalized
136    #[inline]
137    #[must_use]
138    pub fn finish(self) -> Digest {
139        Self::try_finish(self).expect("EVP_DigestFinal failed")
140    }
141
142    #[inline]
143    fn try_finish(mut self) -> Result<Digest, Unspecified> {
144        let mut output = [0u8; MAX_OUTPUT_LEN];
145        let mut out_len = MaybeUninit::<c_uint>::uninit();
146        if 1 != indicator_check!(unsafe {
147            EVP_DigestFinal(
148                self.digest_ctx.as_mut_ptr(),
149                output.as_mut_ptr(),
150                out_len.as_mut_ptr(),
151            )
152        }) {
153            return Err(Unspecified);
154        }
155
156        Ok(Digest {
157            algorithm: self.algorithm,
158            digest_msg: output,
159            digest_len: self.algorithm.output_len,
160        })
161    }
162
163    /// The algorithm that this context is using.
164    #[inline]
165    #[must_use]
166    pub fn algorithm(&self) -> &'static Algorithm {
167        self.algorithm
168    }
169}
170
171/// Returns the digest of `data` using the given digest algorithm.
172///
173// # FIPS
174// This function must only be used with one of the following algorithms:
175// * `SHA1_FOR_LEGACY_USE_ONLY`
176// * `SHA224`
177// * `SHA256`
178// * `SHA384`
179// * `SHA512`
180// * `SHA512_256`
181//
182/// # Examples:
183///
184/// ```
185/// # {
186/// use aws_lc_rs::{digest, test};
187/// let expected_hex = "09ca7e4eaa6e8ae9c7d261167129184883644d07dfba7cbfbc4c8a2e08360d5b";
188/// let expected: Vec<u8> = test::from_hex(expected_hex).unwrap();
189/// let actual = digest::digest(&digest::SHA256, b"hello, world");
190///
191/// assert_eq!(&expected, &actual.as_ref());
192/// # }
193/// ```
194#[inline]
195#[must_use]
196pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest {
197    let mut output = [0u8; MAX_OUTPUT_LEN];
198    (algorithm.one_shot_hash)(data, &mut output);
199
200    Digest {
201        algorithm,
202        digest_msg: output,
203        digest_len: algorithm.output_len,
204    }
205}
206
207/// A calculated digest value.
208///
209/// Use [`Self::as_ref`] to get the value as a `&[u8]`.
210#[derive(Clone, Copy)]
211pub struct Digest {
212    /// The trait `Copy` can't be implemented for dynamic arrays, so we set a
213    /// fixed array and the appropriate length.
214    digest_msg: [u8; MAX_OUTPUT_LEN],
215    digest_len: usize,
216
217    algorithm: &'static Algorithm,
218}
219
220impl Digest {
221    /// The algorithm that was used to calculate the digest value.
222    #[inline]
223    #[must_use]
224    pub fn algorithm(&self) -> &'static Algorithm {
225        self.algorithm
226    }
227}
228
229impl AsRef<[u8]> for Digest {
230    #[inline]
231    fn as_ref(&self) -> &[u8] {
232        &self.digest_msg[..self.digest_len]
233    }
234}
235
236impl core::fmt::Debug for Digest {
237    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
238        write!(fmt, "{:?}:", self.algorithm)?;
239        debug::write_hex_bytes(fmt, self.as_ref())
240    }
241}
242
243/// A digest algorithm.
244pub struct Algorithm {
245    /// The length of a finalized digest.
246    pub output_len: usize,
247
248    /// The size of the chaining value of the digest function, in bytes. For
249    /// non-truncated algorithms (SHA-1, SHA-256, SHA-512), this is equal to
250    /// `output_len`. For truncated algorithms (e.g. SHA-224, SHA-384, SHA-512/256),
251    /// this is equal to the length before truncation. This is mostly helpful
252    /// for determining the size of an HMAC key that is appropriate for the
253    /// digest algorithm.
254    ///
255    /// This function isn't actually used in *aws-lc-rs*, and is only
256    /// kept for compatibility with the original *ring* implementation.
257    #[deprecated]
258    pub chaining_len: usize,
259
260    /// The internal block length.
261    pub block_len: usize,
262
263    // max_input_len is computed as u64 instead of usize to prevent overflowing on 32-bit machines.
264    max_input_len: u64,
265
266    one_shot_hash: fn(msg: &[u8], output: &mut [u8]),
267
268    pub(crate) id: AlgorithmID,
269}
270
271unsafe impl Send for Algorithm {}
272
273impl Algorithm {
274    /// The length of a finalized digest.
275    #[inline]
276    #[must_use]
277    pub fn output_len(&self) -> usize {
278        self.output_len
279    }
280
281    /// The size of the chaining value of the digest function, in bytes. For
282    /// non-truncated algorithms (SHA-1, SHA-256, SHA-512), this is equal to
283    /// `output_len`. For truncated algorithms (e.g. SHA-224, SHA-384, SHA-512/256),
284    /// this is equal to the length before truncation. This is mostly helpful
285    /// for determining the size of an HMAC key that is appropriate for the
286    /// digest algorithm.
287    ///
288    /// This function isn't actually used in *aws-lc-rs*, and is only
289    /// kept for compatibility with the original *ring* implementation.
290    #[deprecated]
291    #[inline]
292    #[must_use]
293    pub fn chaining_len(&self) -> usize {
294        // clippy warns on deprecated functions accessing deprecated fields
295        #![allow(deprecated)]
296        self.chaining_len
297    }
298
299    /// The internal block length.
300    #[inline]
301    #[must_use]
302    pub fn block_len(&self) -> usize {
303        self.block_len
304    }
305}
306
307#[derive(Clone, Copy, Debug, Eq, PartialEq)]
308pub(crate) enum AlgorithmID {
309    SHA1,
310    SHA224,
311    SHA256,
312    SHA384,
313    SHA512,
314    SHA512_256,
315    SHA3_256,
316    SHA3_384,
317    SHA3_512,
318}
319
320impl PartialEq for Algorithm {
321    fn eq(&self, other: &Self) -> bool {
322        self.id == other.id
323    }
324}
325
326impl Eq for Algorithm {}
327
328derive_debug_via_id!(Algorithm);
329
330/// The maximum block length ([`Algorithm::block_len`]) of all the algorithms
331/// in this module.
332pub const MAX_BLOCK_LEN: usize = 1024 / 8;
333
334/// The maximum output length ([`Algorithm::output_len`]) of all the
335/// algorithms in this module.
336pub const MAX_OUTPUT_LEN: usize = 512 / 8;
337
338/// The maximum chaining length ([`Algorithm::chaining_len`]) of all the
339/// algorithms in this module.
340pub const MAX_CHAINING_LEN: usize = MAX_OUTPUT_LEN;
341
342/// Match digest types for `EVP_MD` functions.
343pub(crate) fn match_digest_type(algorithm_id: &AlgorithmID) -> ConstPointer<EVP_MD> {
344    unsafe {
345        ConstPointer::new(match algorithm_id {
346            AlgorithmID::SHA1 => EVP_sha1(),
347            AlgorithmID::SHA224 => EVP_sha224(),
348            AlgorithmID::SHA256 => EVP_sha256(),
349            AlgorithmID::SHA384 => EVP_sha384(),
350            AlgorithmID::SHA512 => EVP_sha512(),
351            AlgorithmID::SHA512_256 => EVP_sha512_256(),
352            AlgorithmID::SHA3_256 => EVP_sha3_256(),
353            AlgorithmID::SHA3_384 => EVP_sha3_384(),
354            AlgorithmID::SHA3_512 => EVP_sha3_512(),
355        })
356        .unwrap_or_else(|()| panic!("Digest algorithm not found: {algorithm_id:?}"))
357    }
358}
359
360#[cfg(test)]
361mod tests {
362    #[cfg(feature = "fips")]
363    mod fips;
364
365    mod max_input {
366        extern crate alloc;
367
368        use super::super::super::digest;
369        use crate::digest::digest_ctx::DigestContext;
370        use crate::digest::Digest;
371        use alloc::vec;
372
373        macro_rules! max_input_tests {
374            ( $algorithm_name:ident ) => {
375                mod $algorithm_name {
376                    use super::super::super::super::digest;
377
378                    #[test]
379                    fn max_input_test() {
380                        super::max_input_test(&digest::$algorithm_name);
381                    }
382                    #[test]
383                    #[should_panic(expected = "digest update failed")]
384                    fn too_long_input_test_block() {
385                        super::too_long_input_test_block(&digest::$algorithm_name);
386                    }
387
388                    #[test]
389                    #[should_panic(expected = "digest update failed")]
390                    fn too_long_input_test_byte() {
391                        super::too_long_input_test_byte(&digest::$algorithm_name);
392                    }
393                }
394            };
395        }
396
397        fn max_input_test(alg: &'static digest::Algorithm) {
398            let mut context = nearly_full_context(alg);
399            let next_input = vec![0u8; alg.block_len - 1];
400            context.update(&next_input);
401            let _: Digest = context.finish(); // no panic
402        }
403
404        fn too_long_input_test_block(alg: &'static digest::Algorithm) {
405            let mut context = nearly_full_context(alg);
406            let next_input = vec![0u8; alg.block_len];
407            context.update(&next_input);
408            let _: Digest = context.finish(); // should panic
409        }
410
411        fn too_long_input_test_byte(alg: &'static digest::Algorithm) {
412            let mut context = nearly_full_context(alg);
413            let next_input = vec![0u8; alg.block_len - 1];
414            context.update(&next_input); // no panic
415            context.update(&[0]);
416            let _: Digest = context.finish(); // should panic
417        }
418
419        fn nearly_full_context(alg: &'static digest::Algorithm) -> digest::Context {
420            // Implementations of `digest` only support up
421            // to 2^64-1 bits of input.
422            let block_len = alg.block_len as u64;
423            digest::Context {
424                algorithm: alg,
425                digest_ctx: DigestContext::new(alg).unwrap(),
426                msg_len: alg.max_input_len - block_len + 1,
427                max_input_reached: false,
428            }
429        }
430
431        max_input_tests!(SHA1_FOR_LEGACY_USE_ONLY);
432        max_input_tests!(SHA224);
433        max_input_tests!(SHA256);
434        max_input_tests!(SHA384);
435        max_input_tests!(SHA512);
436        max_input_tests!(SHA3_384);
437        max_input_tests!(SHA3_512);
438    }
439
440    #[test]
441    fn digest_coverage() {
442        use crate::digest;
443
444        for alg in [
445            &digest::SHA1_FOR_LEGACY_USE_ONLY,
446            &digest::SHA224,
447            &digest::SHA256,
448            &digest::SHA384,
449            &digest::SHA512,
450            &digest::SHA3_384,
451            &digest::SHA3_512,
452        ] {
453            // Clone after updating context with message, then check if the final Digest is the same.
454            let mut ctx = digest::Context::new(alg);
455            ctx.update(b"hello, world");
456            let ctx_clone = ctx.clone();
457            assert_eq!(ctx_clone.algorithm(), ctx.algorithm());
458
459            let orig_digest = ctx.finish();
460            let clone_digest = ctx_clone.finish();
461            assert_eq!(orig_digest.algorithm(), clone_digest.algorithm());
462            assert_eq!(orig_digest.as_ref(), clone_digest.as_ref());
463            assert_eq!(orig_digest.clone().as_ref(), clone_digest.as_ref());
464        }
465    }
466}