1#![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#[derive(Clone)]
62pub struct Context {
63 pub(crate) algorithm: &'static Algorithm,
65 digest_ctx: DigestContext,
66 msg_len: u64,
73 max_input_reached: bool,
74}
75
76impl Context {
77 #[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 #[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 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 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 #[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 #[inline]
163 #[must_use]
164 pub fn algorithm(&self) -> &'static Algorithm {
165 self.algorithm
166 }
167}
168
169#[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#[derive(Clone, Copy)]
209pub struct Digest {
210 message: [u8; MAX_OUTPUT_LEN],
213 len: usize,
214
215 algorithm: &'static Algorithm,
216}
217
218impl Digest {
219 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 #[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
265pub struct Algorithm {
267 pub output_len: usize,
269
270 #[deprecated]
280 pub chaining_len: usize,
281
282 pub block_len: usize,
284
285 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 #[inline]
298 #[must_use]
299 pub fn output_len(&self) -> usize {
300 self.output_len
301 }
302
303 #[deprecated]
313 #[inline]
314 #[must_use]
315 pub fn chaining_len(&self) -> usize {
316 #![allow(deprecated)]
318 self.chaining_len
319 }
320
321 #[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
352pub const MAX_BLOCK_LEN: usize = 1024 / 8;
355
356pub const MAX_OUTPUT_LEN: usize = 512 / 8;
359
360pub const MAX_CHAINING_LEN: usize = MAX_OUTPUT_LEN;
363
364pub(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(); }
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(); }
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); context.update(&[0]);
439 let _: Digest = context.finish(); }
441
442 fn nearly_full_context(alg: &'static digest::Algorithm) -> digest::Context {
443 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 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}