1#![allow(non_snake_case)]
31use crate::fips::indicator_check;
32use crate::{debug, derive_debug_via_id};
33
34pub(crate) mod digest_ctx;
35pub(crate) mod sha;
38#[cfg(all(not(feature = "fips"), feature = "unstable"))]
39use crate::aws_lc::EVP_keccak256;
40use crate::aws_lc::{
41 EVP_DigestFinal, EVP_DigestUpdate, EVP_sha1, EVP_sha224, EVP_sha256, EVP_sha384, EVP_sha3_256,
42 EVP_sha3_384, EVP_sha3_512, EVP_sha512, EVP_sha512_256, EVP_MD,
43};
44use crate::error::Unspecified;
45use crate::ptr::ConstPointer;
46use core::ffi::c_uint;
47use core::mem::MaybeUninit;
48use digest_ctx::DigestContext;
49#[cfg(all(test, not(feature = "fips"), feature = "unstable"))]
52pub(crate) use sha::KECCAK256;
53pub use sha::{
54 SHA1_FOR_LEGACY_USE_ONLY, SHA1_OUTPUT_LEN, SHA224, SHA224_OUTPUT_LEN, SHA256,
55 SHA256_OUTPUT_LEN, SHA384, SHA384_OUTPUT_LEN, SHA3_256, SHA3_384, SHA3_512, SHA512, SHA512_256,
56 SHA512_256_OUTPUT_LEN, SHA512_OUTPUT_LEN,
57};
58
59#[derive(Clone)]
70pub struct Context {
71 pub(crate) algorithm: &'static Algorithm,
73 digest_ctx: DigestContext,
74 msg_len: u64,
81 max_input_reached: bool,
82}
83
84impl Context {
85 #[must_use]
92 pub fn new(algorithm: &'static Algorithm) -> Self {
93 Self {
94 algorithm,
95 digest_ctx: DigestContext::new(algorithm).unwrap(),
96 msg_len: 0u64,
97 max_input_reached: false,
98 }
99 }
100
101 #[inline]
106 pub fn update(&mut self, data: &[u8]) {
107 Self::try_update(self, data).expect("digest update failed");
108 }
109
110 #[inline]
111 fn try_update(&mut self, data: &[u8]) -> Result<(), Unspecified> {
112 unsafe {
113 let (msg_len, overflowed) = self.msg_len.overflowing_add(data.len() as u64);
116 if overflowed || msg_len > self.algorithm.max_input_len {
117 return Err(Unspecified);
118 }
119
120 self.msg_len = msg_len;
121 self.max_input_reached = self.msg_len == self.algorithm.max_input_len;
122
123 if 1 != EVP_DigestUpdate(
125 self.digest_ctx.as_mut_ptr(),
126 data.as_ptr().cast(),
127 data.len(),
128 ) {
129 return Err(Unspecified);
130 }
131 Ok(())
132 }
133 }
134
135 #[inline]
143 #[must_use]
144 pub fn finish(self) -> Digest {
145 Self::try_finish(self).expect("EVP_DigestFinal failed")
146 }
147
148 #[inline]
149 fn try_finish(mut self) -> Result<Digest, Unspecified> {
150 let mut output = [0u8; MAX_OUTPUT_LEN];
151 let mut out_len = MaybeUninit::<c_uint>::uninit();
152 if 1 != indicator_check!(unsafe {
153 EVP_DigestFinal(
154 self.digest_ctx.as_mut_ptr(),
155 output.as_mut_ptr(),
156 out_len.as_mut_ptr(),
157 )
158 }) {
159 return Err(Unspecified);
160 }
161
162 Ok(Digest {
163 algorithm: self.algorithm,
164 message: output,
165 len: self.algorithm.output_len,
166 })
167 }
168
169 #[inline]
171 #[must_use]
172 pub fn algorithm(&self) -> &'static Algorithm {
173 self.algorithm
174 }
175}
176
177#[inline]
201#[must_use]
202pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest {
203 let mut output = [0u8; MAX_OUTPUT_LEN];
204 (algorithm.one_shot_hash)(data, &mut output);
205
206 Digest {
207 algorithm,
208 message: output,
209 len: algorithm.output_len,
210 }
211}
212
213#[derive(Clone, Copy)]
217pub struct Digest {
218 message: [u8; MAX_OUTPUT_LEN],
221 len: usize,
222
223 algorithm: &'static Algorithm,
224}
225
226impl Digest {
227 pub fn import_less_safe(
236 digest: &[u8],
237 algorithm: &'static Algorithm,
238 ) -> Result<Self, Unspecified> {
239 if digest.len() != algorithm.output_len {
240 return Err(Unspecified);
241 }
242 let mut my_digest = [0u8; MAX_OUTPUT_LEN];
243 my_digest[0..digest.len()].copy_from_slice(&digest[0..digest.len()]);
244 Ok(Digest {
245 message: my_digest,
246 len: digest.len(),
247 algorithm,
248 })
249 }
250
251 #[inline]
253 #[must_use]
254 pub fn algorithm(&self) -> &'static Algorithm {
255 self.algorithm
256 }
257}
258
259impl AsRef<[u8]> for Digest {
260 #[inline]
261 fn as_ref(&self) -> &[u8] {
262 &self.message[..self.len]
263 }
264}
265
266impl core::fmt::Debug for Digest {
267 fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
268 write!(fmt, "{:?}:", self.algorithm)?;
269 debug::write_hex_bytes(fmt, self.as_ref())
270 }
271}
272
273pub struct Algorithm {
275 pub output_len: usize,
277
278 #[deprecated(
288 since = "1.1.0",
289 note = "Unused by aws-lc-rs; retained for ring compatibility. Use `output_len()` to size an HMAC key."
290 )]
291 pub chaining_len: usize,
292
293 pub block_len: usize,
295
296 max_input_len: u64,
298
299 one_shot_hash: fn(msg: &[u8], output: &mut [u8]),
300
301 pub(crate) id: AlgorithmID,
302}
303
304unsafe impl Send for Algorithm {}
305
306impl Algorithm {
307 #[inline]
309 #[must_use]
310 pub fn output_len(&self) -> usize {
311 self.output_len
312 }
313
314 #[deprecated(
324 since = "1.5.0",
325 note = "Unused by aws-lc-rs; retained for ring compatibility. Use `output_len()` to size an HMAC key."
326 )]
327 #[inline]
328 #[must_use]
329 pub fn chaining_len(&self) -> usize {
330 #![allow(deprecated)]
332 self.chaining_len
333 }
334
335 #[inline]
337 #[must_use]
338 pub fn block_len(&self) -> usize {
339 self.block_len
340 }
341}
342
343#[derive(Clone, Copy, Debug, Eq, PartialEq)]
344pub(crate) enum AlgorithmID {
345 SHA1,
346 SHA224,
347 SHA256,
348 SHA384,
349 SHA512,
350 SHA512_256,
351 SHA3_256,
352 SHA3_384,
353 SHA3_512,
354 #[cfg(all(not(feature = "fips"), feature = "unstable"))]
355 KECCAK256,
356}
357
358impl PartialEq for Algorithm {
359 fn eq(&self, other: &Self) -> bool {
360 self.id == other.id
361 }
362}
363
364impl Eq for Algorithm {}
365
366derive_debug_via_id!(Algorithm);
367
368pub const MAX_BLOCK_LEN: usize = 1024 / 8;
371
372pub const MAX_OUTPUT_LEN: usize = 512 / 8;
375
376pub const MAX_CHAINING_LEN: usize = MAX_OUTPUT_LEN;
379
380pub(crate) fn match_digest_type(algorithm_id: &AlgorithmID) -> ConstPointer<'_, EVP_MD> {
382 unsafe {
383 ConstPointer::new_static(match algorithm_id {
384 AlgorithmID::SHA1 => EVP_sha1(),
385 AlgorithmID::SHA224 => EVP_sha224(),
386 AlgorithmID::SHA256 => EVP_sha256(),
387 AlgorithmID::SHA384 => EVP_sha384(),
388 AlgorithmID::SHA512 => EVP_sha512(),
389 AlgorithmID::SHA512_256 => EVP_sha512_256(),
390 AlgorithmID::SHA3_256 => EVP_sha3_256(),
391 AlgorithmID::SHA3_384 => EVP_sha3_384(),
392 AlgorithmID::SHA3_512 => EVP_sha3_512(),
393 #[cfg(all(not(feature = "fips"), feature = "unstable"))]
394 AlgorithmID::KECCAK256 => EVP_keccak256(),
395 })
396 .unwrap_or_else(|()| panic!("Digest algorithm not found: {algorithm_id:?}"))
397 }
398}
399
400#[cfg(test)]
401mod tests {
402 use crate::digest;
403 #[cfg(feature = "fips")]
404 mod fips;
405
406 mod max_input {
407 extern crate alloc;
408
409 use super::super::super::digest;
410 use crate::digest::digest_ctx::DigestContext;
411 use crate::digest::Digest;
412 use alloc::vec;
413
414 macro_rules! max_input_tests {
415 ( $algorithm_name:ident ) => {
416 mod $algorithm_name {
417 use super::super::super::super::digest;
418
419 #[test]
420 fn max_input_test() {
421 super::max_input_test(&digest::$algorithm_name);
422 }
423 #[test]
424 #[should_panic(expected = "digest update failed")]
425 fn too_long_input_test_block() {
426 super::too_long_input_test_block(&digest::$algorithm_name);
427 }
428
429 #[test]
430 #[should_panic(expected = "digest update failed")]
431 fn too_long_input_test_byte() {
432 super::too_long_input_test_byte(&digest::$algorithm_name);
433 }
434 }
435 };
436 }
437
438 fn max_input_test(alg: &'static digest::Algorithm) {
439 let mut context = nearly_full_context(alg);
440 let next_input = vec![0u8; alg.block_len - 1];
441 context.update(&next_input);
442 let _: Digest = context.finish(); }
444
445 fn too_long_input_test_block(alg: &'static digest::Algorithm) {
446 let mut context = nearly_full_context(alg);
447 let next_input = vec![0u8; alg.block_len];
448 context.update(&next_input);
449 let _: Digest = context.finish(); }
451
452 fn too_long_input_test_byte(alg: &'static digest::Algorithm) {
453 let mut context = nearly_full_context(alg);
454 let next_input = vec![0u8; alg.block_len - 1];
455 context.update(&next_input); context.update(&[0]);
457 let _: Digest = context.finish(); }
459
460 fn nearly_full_context(alg: &'static digest::Algorithm) -> digest::Context {
461 let block_len = alg.block_len as u64;
464 digest::Context {
465 algorithm: alg,
466 digest_ctx: DigestContext::new(alg).unwrap(),
467 msg_len: alg.max_input_len - block_len + 1,
468 max_input_reached: false,
469 }
470 }
471
472 max_input_tests!(SHA1_FOR_LEGACY_USE_ONLY);
473 max_input_tests!(SHA224);
474 max_input_tests!(SHA256);
475 max_input_tests!(SHA384);
476 max_input_tests!(SHA512);
477 max_input_tests!(SHA3_384);
478 max_input_tests!(SHA3_512);
479 #[cfg(all(not(feature = "fips"), feature = "unstable"))]
480 max_input_tests!(KECCAK256);
481 }
482
483 #[test]
484 fn digest_coverage() {
485 for alg in [
486 &digest::SHA1_FOR_LEGACY_USE_ONLY,
487 &digest::SHA224,
488 &digest::SHA256,
489 &digest::SHA384,
490 &digest::SHA512,
491 &digest::SHA3_384,
492 &digest::SHA3_512,
493 #[cfg(all(not(feature = "fips"), feature = "unstable"))]
494 &digest::KECCAK256,
495 ] {
496 let mut ctx = digest::Context::new(alg);
498 ctx.update(b"hello, world");
499 let ctx_clone = ctx.clone();
500 assert_eq!(ctx_clone.algorithm(), ctx.algorithm());
501
502 let orig_digest = ctx.finish();
503 let clone_digest = ctx_clone.finish();
504 assert_eq!(orig_digest.algorithm(), clone_digest.algorithm());
505 assert_eq!(orig_digest.as_ref(), clone_digest.as_ref());
506 assert_eq!(orig_digest.clone().as_ref(), clone_digest.as_ref());
507 }
508 }
509
510 #[test]
511 fn test_import_less_safe() {
512 let digest = digest::digest(&digest::SHA256, b"hello, world");
513 let digest_copy =
514 digest::Digest::import_less_safe(digest.as_ref(), &digest::SHA256).unwrap();
515
516 assert_eq!(digest.as_ref(), digest_copy.as_ref());
517 assert_eq!(digest.algorithm, digest_copy.algorithm);
518 }
519}