1use crate::{
28 c, cpu, debug,
29 endian::{self, BigEndian},
30 polyfill,
31};
32use core::num::Wrapping;
33
34mod sha1;
35mod sha2;
36
37#[derive(Clone)]
38pub(crate) struct BlockContext {
39 state: State,
40
41 completed_data_blocks: u64,
45
46 pub algorithm: &'static Algorithm,
48
49 cpu_features: cpu::Features,
50}
51
52impl BlockContext {
53 pub(crate) fn new(algorithm: &'static Algorithm) -> Self {
54 Self {
55 state: algorithm.initial_state,
56 completed_data_blocks: 0,
57 algorithm,
58 cpu_features: cpu::features(),
59 }
60 }
61
62 #[inline]
63 pub(crate) fn update(&mut self, input: &[u8]) {
64 let num_blocks = input.len() / self.algorithm.block_len;
65 assert_eq!(num_blocks * self.algorithm.block_len, input.len());
66 if num_blocks > 0 {
67 unsafe {
68 (self.algorithm.block_data_order)(&mut self.state, input.as_ptr(), num_blocks);
69 }
70 self.completed_data_blocks = self
71 .completed_data_blocks
72 .checked_add(polyfill::u64_from_usize(num_blocks))
73 .unwrap();
74 }
75 }
76
77 pub(crate) fn finish(mut self, pending: &mut [u8], num_pending: usize) -> Digest {
78 let block_len = self.algorithm.block_len;
79 assert_eq!(pending.len(), block_len);
80 assert!(num_pending <= pending.len());
81
82 let mut padding_pos = num_pending;
83 pending[padding_pos] = 0x80;
84 padding_pos += 1;
85
86 if padding_pos > block_len - self.algorithm.len_len {
87 polyfill::slice::fill(&mut pending[padding_pos..block_len], 0);
88 unsafe {
89 (self.algorithm.block_data_order)(&mut self.state, pending.as_ptr(), 1);
90 }
91 padding_pos = 0;
94 }
95
96 polyfill::slice::fill(&mut pending[padding_pos..(block_len - 8)], 0);
97
98 let completed_data_bits = self
100 .completed_data_blocks
101 .checked_mul(polyfill::u64_from_usize(block_len))
102 .unwrap()
103 .checked_add(polyfill::u64_from_usize(num_pending))
104 .unwrap()
105 .checked_mul(8)
106 .unwrap();
107 pending[(block_len - 8)..block_len].copy_from_slice(&u64::to_be_bytes(completed_data_bits));
108
109 unsafe {
110 (self.algorithm.block_data_order)(&mut self.state, pending.as_ptr(), 1);
111 }
112
113 Digest {
114 algorithm: self.algorithm,
115 value: (self.algorithm.format_output)(self.state),
116 }
117 }
118}
119
120#[derive(Clone)]
138pub struct Context {
139 block: BlockContext,
140 pending: [u8; MAX_BLOCK_LEN],
142 num_pending: usize,
143}
144
145impl Context {
146 pub fn new(algorithm: &'static Algorithm) -> Self {
148 Self {
149 block: BlockContext::new(algorithm),
150 pending: [0u8; MAX_BLOCK_LEN],
151 num_pending: 0,
152 }
153 }
154
155 pub(crate) fn clone_from(block: &BlockContext) -> Self {
156 Self {
157 block: block.clone(),
158 pending: [0u8; MAX_BLOCK_LEN],
159 num_pending: 0,
160 }
161 }
162
163 pub fn update(&mut self, data: &[u8]) {
167 let block_len = self.block.algorithm.block_len;
168 if data.len() < block_len - self.num_pending {
169 self.pending[self.num_pending..(self.num_pending + data.len())].copy_from_slice(data);
170 self.num_pending += data.len();
171 return;
172 }
173
174 let mut remaining = data;
175 if self.num_pending > 0 {
176 let to_copy = block_len - self.num_pending;
177 self.pending[self.num_pending..block_len].copy_from_slice(&data[..to_copy]);
178 self.block.update(&self.pending[..block_len]);
179 remaining = &remaining[to_copy..];
180 self.num_pending = 0;
181 }
182
183 let num_blocks = remaining.len() / block_len;
184 let num_to_save_for_later = remaining.len() % block_len;
185 self.block.update(&remaining[..(num_blocks * block_len)]);
186 if num_to_save_for_later > 0 {
187 self.pending[..num_to_save_for_later]
188 .copy_from_slice(&remaining[(remaining.len() - num_to_save_for_later)..]);
189 self.num_pending = num_to_save_for_later;
190 }
191 }
192
193 pub fn finish(mut self) -> Digest {
197 let block_len = self.block.algorithm.block_len;
198 self.block
199 .finish(&mut self.pending[..block_len], self.num_pending)
200 }
201
202 #[inline(always)]
204 pub fn algorithm(&self) -> &'static Algorithm {
205 self.block.algorithm
206 }
207}
208
209pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest {
225 let mut ctx = Context::new(algorithm);
226 ctx.update(data);
227 ctx.finish()
228}
229
230#[derive(Clone, Copy)]
234pub struct Digest {
235 value: Output,
236 algorithm: &'static Algorithm,
237}
238
239impl Digest {
240 #[inline(always)]
242 pub fn algorithm(&self) -> &'static Algorithm {
243 self.algorithm
244 }
245}
246
247impl AsRef<[u8]> for Digest {
248 #[inline(always)]
249 fn as_ref(&self) -> &[u8] {
250 let as64 = unsafe { &self.value.as64 };
251 &endian::as_byte_slice(as64)[..self.algorithm.output_len]
252 }
253}
254
255impl core::fmt::Debug for Digest {
256 fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
257 write!(fmt, "{:?}:", self.algorithm)?;
258 debug::write_hex_bytes(fmt, self.as_ref())
259 }
260}
261
262pub struct Algorithm {
264 pub output_len: usize,
266
267 pub chaining_len: usize,
274
275 pub block_len: usize,
277
278 len_len: usize,
280
281 block_data_order: unsafe extern "C" fn(state: &mut State, data: *const u8, num: c::size_t),
282 format_output: fn(input: State) -> Output,
283
284 initial_state: State,
285
286 id: AlgorithmID,
287}
288
289#[derive(Debug, Eq, PartialEq)]
290enum AlgorithmID {
291 SHA1,
292 SHA256,
293 SHA384,
294 SHA512,
295 SHA512_256,
296}
297
298impl PartialEq for Algorithm {
299 fn eq(&self, other: &Self) -> bool {
300 self.id == other.id
301 }
302}
303
304impl Eq for Algorithm {}
305
306derive_debug_via_id!(Algorithm);
307
308pub static SHA1_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm {
312 output_len: sha1::OUTPUT_LEN,
313 chaining_len: sha1::CHAINING_LEN,
314 block_len: sha1::BLOCK_LEN,
315 len_len: 64 / 8,
316 block_data_order: sha1::block_data_order,
317 format_output: sha256_format_output,
318 initial_state: State {
319 as32: [
320 Wrapping(0x67452301u32),
321 Wrapping(0xefcdab89u32),
322 Wrapping(0x98badcfeu32),
323 Wrapping(0x10325476u32),
324 Wrapping(0xc3d2e1f0u32),
325 Wrapping(0),
326 Wrapping(0),
327 Wrapping(0),
328 ],
329 },
330 id: AlgorithmID::SHA1,
331};
332
333pub static SHA256: Algorithm = Algorithm {
337 output_len: SHA256_OUTPUT_LEN,
338 chaining_len: SHA256_OUTPUT_LEN,
339 block_len: 512 / 8,
340 len_len: 64 / 8,
341 block_data_order: sha2::GFp_sha256_block_data_order,
342 format_output: sha256_format_output,
343 initial_state: State {
344 as32: [
345 Wrapping(0x6a09e667u32),
346 Wrapping(0xbb67ae85u32),
347 Wrapping(0x3c6ef372u32),
348 Wrapping(0xa54ff53au32),
349 Wrapping(0x510e527fu32),
350 Wrapping(0x9b05688cu32),
351 Wrapping(0x1f83d9abu32),
352 Wrapping(0x5be0cd19u32),
353 ],
354 },
355 id: AlgorithmID::SHA256,
356};
357
358pub static SHA384: Algorithm = Algorithm {
362 output_len: SHA384_OUTPUT_LEN,
363 chaining_len: SHA512_OUTPUT_LEN,
364 block_len: SHA512_BLOCK_LEN,
365 len_len: SHA512_LEN_LEN,
366 block_data_order: sha2::GFp_sha512_block_data_order,
367 format_output: sha512_format_output,
368 initial_state: State {
369 as64: [
370 Wrapping(0xcbbb9d5dc1059ed8),
371 Wrapping(0x629a292a367cd507),
372 Wrapping(0x9159015a3070dd17),
373 Wrapping(0x152fecd8f70e5939),
374 Wrapping(0x67332667ffc00b31),
375 Wrapping(0x8eb44a8768581511),
376 Wrapping(0xdb0c2e0d64f98fa7),
377 Wrapping(0x47b5481dbefa4fa4),
378 ],
379 },
380 id: AlgorithmID::SHA384,
381};
382
383pub static SHA512: Algorithm = Algorithm {
387 output_len: SHA512_OUTPUT_LEN,
388 chaining_len: SHA512_OUTPUT_LEN,
389 block_len: SHA512_BLOCK_LEN,
390 len_len: SHA512_LEN_LEN,
391 block_data_order: sha2::GFp_sha512_block_data_order,
392 format_output: sha512_format_output,
393 initial_state: State {
394 as64: [
395 Wrapping(0x6a09e667f3bcc908),
396 Wrapping(0xbb67ae8584caa73b),
397 Wrapping(0x3c6ef372fe94f82b),
398 Wrapping(0xa54ff53a5f1d36f1),
399 Wrapping(0x510e527fade682d1),
400 Wrapping(0x9b05688c2b3e6c1f),
401 Wrapping(0x1f83d9abfb41bd6b),
402 Wrapping(0x5be0cd19137e2179),
403 ],
404 },
405 id: AlgorithmID::SHA512,
406};
407
408pub static SHA512_256: Algorithm = Algorithm {
416 output_len: SHA512_256_OUTPUT_LEN,
417 chaining_len: SHA512_OUTPUT_LEN,
418 block_len: SHA512_BLOCK_LEN,
419 len_len: SHA512_LEN_LEN,
420 block_data_order: sha2::GFp_sha512_block_data_order,
421 format_output: sha512_format_output,
422 initial_state: State {
423 as64: [
424 Wrapping(0x22312194fc2bf72c),
425 Wrapping(0x9f555fa3c84c64c2),
426 Wrapping(0x2393b86b6f53b151),
427 Wrapping(0x963877195940eabd),
428 Wrapping(0x96283ee2a88effe3),
429 Wrapping(0xbe5e1e2553863992),
430 Wrapping(0x2b0199fc2c85b8aa),
431 Wrapping(0x0eb72ddc81c52ca2),
432 ],
433 },
434 id: AlgorithmID::SHA512_256,
435};
436
437#[derive(Clone, Copy)] #[repr(C)]
439union State {
440 as64: [Wrapping<u64>; sha2::CHAINING_WORDS],
441 as32: [Wrapping<u32>; sha2::CHAINING_WORDS],
442}
443
444#[derive(Clone, Copy)]
445#[repr(C)]
446union Output {
447 as64: [BigEndian<u64>; 512 / 8 / core::mem::size_of::<BigEndian<u64>>()],
448 as32: [BigEndian<u32>; 256 / 8 / core::mem::size_of::<BigEndian<u32>>()],
449}
450
451pub const MAX_BLOCK_LEN: usize = 1024 / 8;
454
455pub const MAX_OUTPUT_LEN: usize = 512 / 8;
458
459pub const MAX_CHAINING_LEN: usize = MAX_OUTPUT_LEN;
462
463fn sha256_format_output(input: State) -> Output {
464 let input = unsafe { &input.as32 };
465 Output {
466 as32: [
467 BigEndian::from(input[0]),
468 BigEndian::from(input[1]),
469 BigEndian::from(input[2]),
470 BigEndian::from(input[3]),
471 BigEndian::from(input[4]),
472 BigEndian::from(input[5]),
473 BigEndian::from(input[6]),
474 BigEndian::from(input[7]),
475 ],
476 }
477}
478
479fn sha512_format_output(input: State) -> Output {
480 let input = unsafe { &input.as64 };
481 Output {
482 as64: [
483 BigEndian::from(input[0]),
484 BigEndian::from(input[1]),
485 BigEndian::from(input[2]),
486 BigEndian::from(input[3]),
487 BigEndian::from(input[4]),
488 BigEndian::from(input[5]),
489 BigEndian::from(input[6]),
490 BigEndian::from(input[7]),
491 ],
492 }
493}
494
495pub const SHA1_OUTPUT_LEN: usize = sha1::OUTPUT_LEN;
497
498pub const SHA256_OUTPUT_LEN: usize = 256 / 8;
500
501pub const SHA384_OUTPUT_LEN: usize = 384 / 8;
503
504pub const SHA512_OUTPUT_LEN: usize = 512 / 8;
506
507pub const SHA512_256_OUTPUT_LEN: usize = 256 / 8;
509
510const SHA512_BLOCK_LEN: usize = 1024 / 8;
512
513const SHA512_LEN_LEN: usize = 128 / 8;
515
516#[cfg(test)]
517mod tests {
518
519 mod max_input {
520 use super::super::super::digest;
521 use crate::polyfill;
522 use alloc::vec;
523
524 macro_rules! max_input_tests {
525 ( $algorithm_name:ident ) => {
526 mod $algorithm_name {
527 use super::super::super::super::digest;
528
529 #[test]
530 fn max_input_test() {
531 super::max_input_test(&digest::$algorithm_name);
532 }
533
534 #[test]
535 #[should_panic]
536 fn too_long_input_test_block() {
537 super::too_long_input_test_block(&digest::$algorithm_name);
538 }
539
540 #[test]
541 #[should_panic]
542 fn too_long_input_test_byte() {
543 super::too_long_input_test_byte(&digest::$algorithm_name);
544 }
545 }
546 };
547 }
548
549 fn max_input_test(alg: &'static digest::Algorithm) {
550 let mut context = nearly_full_context(alg);
551 let next_input = vec![0u8; alg.block_len - 1];
552 context.update(&next_input);
553 let _ = context.finish(); }
555
556 fn too_long_input_test_block(alg: &'static digest::Algorithm) {
557 let mut context = nearly_full_context(alg);
558 let next_input = vec![0u8; alg.block_len];
559 context.update(&next_input);
560 let _ = context.finish(); }
562
563 fn too_long_input_test_byte(alg: &'static digest::Algorithm) {
564 let mut context = nearly_full_context(alg);
565 let next_input = vec![0u8; alg.block_len - 1];
566 context.update(&next_input); context.update(&[0]);
568 let _ = context.finish(); }
570
571 fn nearly_full_context(alg: &'static digest::Algorithm) -> digest::Context {
572 let max_bytes = 1u64 << (64 - 3);
576 let max_blocks = max_bytes / polyfill::u64_from_usize(alg.block_len);
577 digest::Context {
578 block: digest::BlockContext {
579 state: alg.initial_state,
580 completed_data_blocks: max_blocks - 1,
581 algorithm: alg,
582 cpu_features: crate::cpu::features(),
583 },
584 pending: [0u8; digest::MAX_BLOCK_LEN],
585 num_pending: 0,
586 }
587 }
588
589 max_input_tests!(SHA1_FOR_LEGACY_USE_ONLY);
590 max_input_tests!(SHA256);
591 max_input_tests!(SHA384);
592 max_input_tests!(SHA512);
593 }
594}