Coverage Report

Created: 2025-07-23 07:04

/rust/registry/src/index.crates.io-6f17d22bba15001f/aws-lc-rs-1.13.0/src/digest.rs
Line
Count
Source (jump to first uncovered line)
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)]
31
use crate::fips::indicator_check;
32
use crate::{debug, derive_debug_via_id};
33
34
pub(crate) mod digest_ctx;
35
mod sha;
36
use 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
};
40
use crate::error::Unspecified;
41
use crate::ptr::ConstPointer;
42
use core::mem::MaybeUninit;
43
use digest_ctx::DigestContext;
44
pub 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;
51
use 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)]
64
pub 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
78
impl 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
0
    pub fn new(algorithm: &'static Algorithm) -> Self {
87
0
        Self {
88
0
            algorithm,
89
0
            digest_ctx: DigestContext::new(algorithm).unwrap(),
90
0
            msg_len: 0u64,
91
0
            max_input_reached: false,
92
0
        }
93
0
    }
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
0
    pub fn update(&mut self, data: &[u8]) {
101
0
        Self::try_update(self, data).expect("digest update failed");
102
0
    }
Unexecuted instantiation: <aws_lc_rs::digest::Context>::update
Unexecuted instantiation: <aws_lc_rs::digest::Context>::update
103
104
    #[inline]
105
0
    fn try_update(&mut self, data: &[u8]) -> Result<(), Unspecified> {
106
0
        unsafe {
107
0
            // Check if the message has reached the algorithm's maximum allowed input, or overflowed
108
0
            // the msg_len counter.
109
0
            let (msg_len, overflowed) = self.msg_len.overflowing_add(data.len() as u64);
110
0
            if overflowed || msg_len > self.algorithm.max_input_len {
111
0
                return Err(Unspecified);
112
0
            }
113
0
114
0
            self.msg_len = msg_len;
115
0
            self.max_input_reached = self.msg_len == self.algorithm.max_input_len;
116
0
117
0
            // Doesn't require boundary_check! guard
118
0
            if 1 != EVP_DigestUpdate(
119
0
                self.digest_ctx.as_mut_ptr(),
120
0
                data.as_ptr().cast(),
121
0
                data.len(),
122
0
            ) {
123
0
                return Err(Unspecified);
124
0
            }
125
0
            Ok(())
126
        }
127
0
    }
Unexecuted instantiation: <aws_lc_rs::digest::Context>::try_update
Unexecuted instantiation: <aws_lc_rs::digest::Context>::try_update
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
0
    pub fn finish(self) -> Digest {
139
0
        Self::try_finish(self).expect("EVP_DigestFinal failed")
140
0
    }
Unexecuted instantiation: <aws_lc_rs::digest::Context>::finish
Unexecuted instantiation: <aws_lc_rs::digest::Context>::finish
141
142
    #[inline]
143
0
    fn try_finish(mut self) -> Result<Digest, Unspecified> {
144
0
        let mut output = [0u8; MAX_OUTPUT_LEN];
145
0
        let mut out_len = MaybeUninit::<c_uint>::uninit();
146
0
        if 1 != indicator_check!(unsafe {
147
0
            EVP_DigestFinal(
148
0
                self.digest_ctx.as_mut_ptr(),
149
0
                output.as_mut_ptr(),
150
0
                out_len.as_mut_ptr(),
151
0
            )
152
0
        }) {
153
0
            return Err(Unspecified);
154
0
        }
155
0
156
0
        Ok(Digest {
157
0
            algorithm: self.algorithm,
158
0
            message: output,
159
0
            len: self.algorithm.output_len,
160
0
        })
161
0
    }
Unexecuted instantiation: <aws_lc_rs::digest::Context>::try_finish
Unexecuted instantiation: <aws_lc_rs::digest::Context>::try_finish
162
163
    /// The algorithm that this context is using.
164
    #[inline]
165
    #[must_use]
166
0
    pub fn algorithm(&self) -> &'static Algorithm {
167
0
        self.algorithm
168
0
    }
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]
196
0
pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest {
197
0
    let mut output = [0u8; MAX_OUTPUT_LEN];
198
0
    (algorithm.one_shot_hash)(data, &mut output);
199
0
200
0
    Digest {
201
0
        algorithm,
202
0
        message: output,
203
0
        len: algorithm.output_len,
204
0
    }
205
0
}
Unexecuted instantiation: aws_lc_rs::digest::digest
Unexecuted instantiation: aws_lc_rs::digest::digest
206
207
/// A calculated digest value.
208
///
209
/// Use [`Self::as_ref`] to get the value as a `&[u8]`.
210
#[derive(Clone, Copy)]
211
pub 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
    message: [u8; MAX_OUTPUT_LEN],
215
    len: usize,
216
217
    algorithm: &'static Algorithm,
218
}
219
220
impl Digest {
221
    /// The algorithm that was used to calculate the digest value.
222
    #[inline]
223
    #[must_use]
224
0
    pub fn algorithm(&self) -> &'static Algorithm {
225
0
        self.algorithm
226
0
    }
227
}
228
229
impl AsRef<[u8]> for Digest {
230
    #[inline]
231
0
    fn as_ref(&self) -> &[u8] {
232
0
        &self.message[..self.len]
233
0
    }
Unexecuted instantiation: <aws_lc_rs::digest::Digest as core::convert::AsRef<[u8]>>::as_ref
Unexecuted instantiation: <aws_lc_rs::digest::Digest as core::convert::AsRef<[u8]>>::as_ref
234
}
235
236
impl core::fmt::Debug for Digest {
237
0
    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
238
0
        write!(fmt, "{:?}:", self.algorithm)?;
239
0
        debug::write_hex_bytes(fmt, self.as_ref())
240
0
    }
241
}
242
243
/// A digest algorithm.
244
pub 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
271
unsafe impl Send for Algorithm {}
272
273
impl Algorithm {
274
    /// The length of a finalized digest.
275
    #[inline]
276
    #[must_use]
277
0
    pub fn output_len(&self) -> usize {
278
0
        self.output_len
279
0
    }
Unexecuted instantiation: <aws_lc_rs::digest::Algorithm>::output_len
Unexecuted instantiation: <aws_lc_rs::digest::Algorithm>::output_len
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
0
    pub fn chaining_len(&self) -> usize {
294
0
        // clippy warns on deprecated functions accessing deprecated fields
295
0
        #![allow(deprecated)]
296
0
        self.chaining_len
297
0
    }
298
299
    /// The internal block length.
300
    #[inline]
301
    #[must_use]
302
0
    pub fn block_len(&self) -> usize {
303
0
        self.block_len
304
0
    }
305
}
306
307
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
308
pub(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
320
impl PartialEq for Algorithm {
321
0
    fn eq(&self, other: &Self) -> bool {
322
0
        self.id == other.id
323
0
    }
324
}
325
326
impl Eq for Algorithm {}
327
328
derive_debug_via_id!(Algorithm);
329
330
/// The maximum block length ([`Algorithm::block_len`]) of all the algorithms
331
/// in this module.
332
pub const MAX_BLOCK_LEN: usize = 1024 / 8;
333
334
/// The maximum output length ([`Algorithm::output_len`]) of all the
335
/// algorithms in this module.
336
pub const MAX_OUTPUT_LEN: usize = 512 / 8;
337
338
/// The maximum chaining length ([`Algorithm::chaining_len`]) of all the
339
/// algorithms in this module.
340
pub const MAX_CHAINING_LEN: usize = MAX_OUTPUT_LEN;
341
342
/// Match digest types for `EVP_MD` functions.
343
0
pub(crate) fn match_digest_type(algorithm_id: &AlgorithmID) -> ConstPointer<EVP_MD> {
344
0
    unsafe {
345
0
        ConstPointer::new(match algorithm_id {
346
0
            AlgorithmID::SHA1 => EVP_sha1(),
347
0
            AlgorithmID::SHA224 => EVP_sha224(),
348
0
            AlgorithmID::SHA256 => EVP_sha256(),
349
0
            AlgorithmID::SHA384 => EVP_sha384(),
350
0
            AlgorithmID::SHA512 => EVP_sha512(),
351
0
            AlgorithmID::SHA512_256 => EVP_sha512_256(),
352
0
            AlgorithmID::SHA3_256 => EVP_sha3_256(),
353
0
            AlgorithmID::SHA3_384 => EVP_sha3_384(),
354
0
            AlgorithmID::SHA3_512 => EVP_sha3_512(),
355
        })
356
0
        .unwrap_or_else(|()| panic!("Digest algorithm not found: {algorithm_id:?}"))
357
0
    }
358
0
}
359
360
#[cfg(test)]
361
mod 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
}