/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 | | } |