use core;
use crate::{c, init, polyfill};
#[cfg(target_endian = "little")]
macro_rules! u32x2 {
( $first:expr, $second:expr ) => {
((($second as u64) << 32) | ($first as u64))
};
}
mod sha1;
#[derive(Clone)]
pub struct Context {
state: State,
completed_data_blocks: u64,
pending: [u8; MAX_BLOCK_LEN],
num_pending: usize,
pub algorithm: &'static Algorithm,
}
impl Context {
pub fn new(algorithm: &'static Algorithm) -> Context {
init::init_once();
Context {
algorithm,
state: algorithm.initial_state,
completed_data_blocks: 0,
pending: [0u8; MAX_BLOCK_LEN],
num_pending: 0,
}
}
pub fn update(&mut self, data: &[u8]) {
if data.len() < self.algorithm.block_len - self.num_pending {
self.pending[self.num_pending..(self.num_pending + data.len())].copy_from_slice(data);
self.num_pending += data.len();
return;
}
let mut remaining = data;
if self.num_pending > 0 {
let to_copy = self.algorithm.block_len - self.num_pending;
self.pending[self.num_pending..self.algorithm.block_len]
.copy_from_slice(&data[..to_copy]);
unsafe {
(self.algorithm.block_data_order)(&mut self.state, self.pending.as_ptr(), 1);
}
self.completed_data_blocks = self.completed_data_blocks.checked_add(1).unwrap();
remaining = &remaining[to_copy..];
self.num_pending = 0;
}
let num_blocks = remaining.len() / self.algorithm.block_len;
let num_to_save_for_later = remaining.len() % self.algorithm.block_len;
if num_blocks > 0 {
unsafe {
(self.algorithm.block_data_order)(&mut self.state, remaining.as_ptr(), num_blocks);
}
self.completed_data_blocks = self
.completed_data_blocks
.checked_add(polyfill::u64_from_usize(num_blocks))
.unwrap();
}
if num_to_save_for_later > 0 {
self.pending[..num_to_save_for_later]
.copy_from_slice(&remaining[(remaining.len() - num_to_save_for_later)..]);
self.num_pending = num_to_save_for_later;
}
}
pub fn finish(mut self) -> Digest {
let mut padding_pos = self.num_pending;
self.pending[padding_pos] = 0x80;
padding_pos += 1;
if padding_pos > self.algorithm.block_len - self.algorithm.len_len {
polyfill::slice::fill(&mut self.pending[padding_pos..self.algorithm.block_len], 0);
unsafe {
(self.algorithm.block_data_order)(&mut self.state, self.pending.as_ptr(), 1);
}
padding_pos = 0;
}
polyfill::slice::fill(
&mut self.pending[padding_pos..(self.algorithm.block_len - 8)],
0,
);
let mut completed_data_bits: u64 = self
.completed_data_blocks
.checked_mul(polyfill::u64_from_usize(self.algorithm.block_len))
.unwrap()
.checked_add(polyfill::u64_from_usize(self.num_pending))
.unwrap()
.checked_mul(8)
.unwrap();
for b in (&mut self.pending[(self.algorithm.block_len - 8)..self.algorithm.block_len])
.into_iter()
.rev()
{
*b = completed_data_bits as u8;
completed_data_bits /= 0x100;
}
unsafe {
(self.algorithm.block_data_order)(&mut self.state, self.pending.as_ptr(), 1);
}
Digest {
algorithm: self.algorithm,
value: (self.algorithm.format_output)(&self.state),
}
}
#[inline(always)]
pub fn algorithm(&self) -> &'static Algorithm { self.algorithm }
}
pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest {
let mut ctx = Context::new(algorithm);
ctx.update(data);
ctx.finish()
}
#[derive(Clone, Copy)]
pub struct Digest {
value: Output,
algorithm: &'static Algorithm,
}
impl Digest {
#[inline(always)]
pub fn algorithm(&self) -> &'static Algorithm { self.algorithm }
}
impl AsRef<[u8]> for Digest {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
&(polyfill::slice::u64_as_u8(&self.value))[..self.algorithm.output_len]
}
}
impl core::fmt::Debug for Digest {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(fmt, "{:?}:", self.algorithm)?;
for byte in self.as_ref() {
write!(fmt, "{:02x}", byte)?;
}
Ok(())
}
}
pub struct Algorithm {
pub output_len: usize,
pub chaining_len: usize,
pub block_len: usize,
len_len: usize,
block_data_order: unsafe extern "C" fn(state: &mut State, data: *const u8, num: c::size_t),
format_output: fn(input: &State) -> Output,
initial_state: State,
id: AlgorithmID,
}
#[derive(Debug, Eq, PartialEq)]
enum AlgorithmID {
SHA1,
SHA256,
SHA384,
SHA512,
SHA512_256,
}
impl PartialEq for Algorithm {
fn eq(&self, other: &Self) -> bool { self.id == other.id }
}
impl Eq for Algorithm {}
derive_debug_via_self!(Algorithm, self.id);
pub static SHA1: Algorithm = Algorithm {
output_len: sha1::OUTPUT_LEN,
chaining_len: sha1::CHAINING_LEN,
block_len: sha1::BLOCK_LEN,
len_len: 64 / 8,
block_data_order: sha1::block_data_order,
format_output: sha256_format_output,
initial_state: [
u32x2!(0x67452301u32, 0xefcdab89u32),
u32x2!(0x98badcfeu32, 0x10325476u32),
u32x2!(0xc3d2e1f0u32, 0u32),
0,
0,
0,
0,
0,
],
id: AlgorithmID::SHA1,
};
pub static SHA256: Algorithm = Algorithm {
output_len: SHA256_OUTPUT_LEN,
chaining_len: SHA256_OUTPUT_LEN,
block_len: 512 / 8,
len_len: 64 / 8,
block_data_order: GFp_sha256_block_data_order,
format_output: sha256_format_output,
initial_state: [
u32x2!(0x6a09e667u32, 0xbb67ae85u32),
u32x2!(0x3c6ef372u32, 0xa54ff53au32),
u32x2!(0x510e527fu32, 0x9b05688cu32),
u32x2!(0x1f83d9abu32, 0x5be0cd19u32),
0,
0,
0,
0,
],
id: AlgorithmID::SHA256,
};
pub static SHA384: Algorithm = Algorithm {
output_len: SHA384_OUTPUT_LEN,
chaining_len: SHA512_OUTPUT_LEN,
block_len: SHA512_BLOCK_LEN,
len_len: SHA512_LEN_LEN,
block_data_order: GFp_sha512_block_data_order,
format_output: sha512_format_output,
initial_state: [
0xcbbb9d5dc1059ed8,
0x629a292a367cd507,
0x9159015a3070dd17,
0x152fecd8f70e5939,
0x67332667ffc00b31,
0x8eb44a8768581511,
0xdb0c2e0d64f98fa7,
0x47b5481dbefa4fa4,
],
id: AlgorithmID::SHA384,
};
pub static SHA512: Algorithm = Algorithm {
output_len: SHA512_OUTPUT_LEN,
chaining_len: SHA512_OUTPUT_LEN,
block_len: SHA512_BLOCK_LEN,
len_len: SHA512_LEN_LEN,
block_data_order: GFp_sha512_block_data_order,
format_output: sha512_format_output,
initial_state: [
0x6a09e667f3bcc908,
0xbb67ae8584caa73b,
0x3c6ef372fe94f82b,
0xa54ff53a5f1d36f1,
0x510e527fade682d1,
0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b,
0x5be0cd19137e2179,
],
id: AlgorithmID::SHA512,
};
pub static SHA512_256: Algorithm = Algorithm {
output_len: SHA512_256_OUTPUT_LEN,
chaining_len: SHA512_OUTPUT_LEN,
block_len: SHA512_BLOCK_LEN,
len_len: SHA512_LEN_LEN,
block_data_order: GFp_sha512_block_data_order,
format_output: sha512_format_output,
initial_state: [
0x22312194fc2bf72c,
0x9f555fa3c84c64c2,
0x2393b86b6f53b151,
0x963877195940eabd,
0x96283ee2a88effe3,
0xbe5e1e2553863992,
0x2b0199fc2c85b8aa,
0x0eb72ddc81c52ca2,
],
id: AlgorithmID::SHA512_256,
};
type State = [u64; MAX_CHAINING_LEN / 8];
type Output = [u64; MAX_OUTPUT_LEN / 8];
pub const MAX_BLOCK_LEN: usize = 1024 / 8;
pub const MAX_OUTPUT_LEN: usize = 512 / 8;
pub const MAX_CHAINING_LEN: usize = MAX_OUTPUT_LEN;
fn sha256_format_output(input: &State) -> Output {
let input = &polyfill::slice::u64_as_u32(input)[..8];
[
u32x2!(input[0].to_be(), input[1].to_be()),
u32x2!(input[2].to_be(), input[3].to_be()),
u32x2!(input[4].to_be(), input[5].to_be()),
u32x2!(input[6].to_be(), input[7].to_be()),
0,
0,
0,
0,
]
}
fn sha512_format_output(input: &State) -> Output {
[
input[0].to_be(),
input[1].to_be(),
input[2].to_be(),
input[3].to_be(),
input[4].to_be(),
input[5].to_be(),
input[6].to_be(),
input[7].to_be(),
]
}
pub const SHA1_OUTPUT_LEN: usize = sha1::OUTPUT_LEN;
pub const SHA256_OUTPUT_LEN: usize = 256 / 8;
pub const SHA384_OUTPUT_LEN: usize = 384 / 8;
pub const SHA512_OUTPUT_LEN: usize = 512 / 8;
pub const SHA512_256_OUTPUT_LEN: usize = 256 / 8;
const SHA512_BLOCK_LEN: usize = 1024 / 8;
const SHA512_LEN_LEN: usize = 128 / 8;
extern "C" {
fn GFp_sha256_block_data_order(state: &mut State, data: *const u8, num: c::size_t);
fn GFp_sha512_block_data_order(state: &mut State, data: *const u8, num: c::size_t);
}
#[cfg(test)]
pub mod test_util {
use super::super::digest;
pub static ALL_ALGORITHMS: [&digest::Algorithm; 5] = [
&digest::SHA1,
&digest::SHA256,
&digest::SHA384,
&digest::SHA512,
&digest::SHA512_256,
];
}
#[cfg(test)]
mod tests {
mod max_input {
use super::super::super::digest;
macro_rules! max_input_tests {
( $algorithm_name:ident ) => {
mod $algorithm_name {
use super::super::super::super::digest;
#[test]
fn max_input_test() { super::max_input_test(&digest::$algorithm_name); }
#[test]
#[should_panic]
fn too_long_input_test_block() {
super::too_long_input_test_block(&digest::$algorithm_name);
}
#[test]
#[should_panic]
fn too_long_input_test_byte() {
super::too_long_input_test_byte(&digest::$algorithm_name);
}
}
};
}
fn max_input_test(alg: &'static digest::Algorithm) {
let mut context = nearly_full_context(alg);
let next_input = vec![0u8; alg.block_len - 1];
context.update(&next_input);
let _ = context.finish();
}
fn too_long_input_test_block(alg: &'static digest::Algorithm) {
let mut context = nearly_full_context(alg);
let next_input = vec![0u8; alg.block_len];
context.update(&next_input);
let _ = context.finish();
}
fn too_long_input_test_byte(alg: &'static digest::Algorithm) {
let mut context = nearly_full_context(alg);
let next_input = vec![0u8; alg.block_len - 1];
context.update(&next_input);
context.update(&[0]);
let _ = context.finish();
}
fn nearly_full_context(alg: &'static digest::Algorithm) -> digest::Context {
let max_bytes = 1u64 << (64 - 3);
let max_blocks = max_bytes / (alg.block_len as u64);
digest::Context {
algorithm: alg,
state: alg.initial_state,
completed_data_blocks: max_blocks - 1,
pending: [0u8; digest::MAX_BLOCK_LEN],
num_pending: 0,
}
}
max_input_tests!(SHA1);
max_input_tests!(SHA256);
max_input_tests!(SHA384);
max_input_tests!(SHA512);
}
}