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