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