use {c, init, polyfill};
use core;
#[cfg(target_endian = "little")]
macro_rules! u32x2 {
( $first:expr, $second:expr ) =>
( ((($second as u64) << 32) | ($first as u64)) )
}
mod sha1;
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: 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 }
}
impl Clone for Context {
fn clone(&self) -> Context {
Context {
state: self.state,
pending: self.pending,
completed_data_blocks: self.completed_data_blocks,
num_pending: self.num_pending,
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 {
try!(write!(fmt, "{:?}:", self.algorithm));
for byte in self.as_ref() {
try!(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 fn(state: &mut State, data: *const u8,
num: c::size_t),
format_output: fn(input: &State) -> Output,
initial_state: State,
}
impl core::fmt::Debug for Algorithm {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
let n = if self.output_len == 20 {
1
} else {
self.output_len * 8
};
write!(fmt, "SHA-{:?}", n)
}
}
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,
],
};
pub static SHA256: Algorithm = Algorithm {
output_len: 256 / 8,
chaining_len: 256 / 8,
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,
],
};
pub static SHA384: Algorithm = Algorithm {
output_len: 384 / 8,
chaining_len: 512 / 8,
block_len: 1024 / 8,
len_len: 128 / 8,
block_data_order: GFp_sha512_block_data_order,
format_output: sha512_format_output,
initial_state: [
0xcbbb9d5dc1059ed8,
0x629a292a367cd507,
0x9159015a3070dd17,
0x152fecd8f70e5939,
0x67332667ffc00b31,
0x8eb44a8768581511,
0xdb0c2e0d64f98fa7,
0x47b5481dbefa4fa4,
],
};
pub static SHA512: Algorithm = Algorithm {
output_len: 512 / 8,
chaining_len: 512 / 8,
block_len: 1024 / 8,
len_len: 128 / 8,
block_data_order: GFp_sha512_block_data_order,
format_output: sha512_format_output,
initial_state: [
0x6a09e667f3bcc908,
0xbb67ae8584caa73b,
0x3c6ef372fe94f82b,
0xa54ff53a5f1d36f1,
0x510e527fade682d1,
0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b,
0x5be0cd19137e2179,
],
};
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;
extern {
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: [&'static digest::Algorithm; 4] = [
&digest::SHA1,
&digest::SHA256,
&digest::SHA384,
&digest::SHA512,
];
}
#[cfg(test)]
mod tests {
use std::vec::Vec;
use super::super::{digest, test};
#[test]
fn test_bssl() {
test::from_file("src/digest/digest_tests.txt", |section, test_case| {
assert_eq!(section, "");
let digest_alg = test_case.consume_digest_alg("Hash").unwrap();
let input = test_case.consume_bytes("Input");
let repeat = test_case.consume_usize("Repeat");
let expected = test_case.consume_bytes("Output");
let mut ctx = digest::Context::new(digest_alg);
let mut data = Vec::new();
for _ in 0..repeat {
ctx.update(&input);
data.extend(&input);
}
let actual_from_chunks = ctx.finish();
assert_eq!(&expected, &actual_from_chunks.as_ref());
let actual_from_one_shot = digest::digest(digest_alg, &data);
assert_eq!(&expected, &actual_from_one_shot.as_ref());
Ok(())
});
}
mod shavs {
use std::vec::Vec;
use super::super::super::{digest, test};
macro_rules! shavs_tests {
( $algorithm_name:ident ) => {
#[allow(non_snake_case)]
mod $algorithm_name {
use super::{run_known_answer_test, run_monte_carlo_test};
use super::super::super::super::digest;
#[test]
fn short_msg_known_answer_test() {
run_known_answer_test(
&digest::$algorithm_name,
&format!("third-party/NIST/SHAVS/{}ShortMsg.rsp",
stringify!($algorithm_name)));
}
#[test]
fn long_msg_known_answer_test() {
run_known_answer_test(
&digest::$algorithm_name,
&format!("third-party/NIST/SHAVS/{}LongMsg.rsp",
stringify!($algorithm_name)));
}
#[test]
fn monte_carlo_test() {
run_monte_carlo_test(
&digest::$algorithm_name,
&format!("third-party/NIST/SHAVS/{}Monte.rsp",
stringify!($algorithm_name)));
}
}
}
}
fn run_known_answer_test(digest_alg: &'static digest::Algorithm,
file_name: &str) {
let section_name = &format!("L = {}", digest_alg.output_len);
test::from_file(file_name, |section, test_case| {
assert_eq!(section_name, section);
let len_bits = test_case.consume_usize("Len");
let mut msg = test_case.consume_bytes("Msg");
if len_bits == 0 {
assert_eq!(msg, &[0u8]);
msg.truncate(0);
}
assert_eq!(msg.len() * 8, len_bits);
let expected = test_case.consume_bytes("MD");
let actual = digest::digest(digest_alg, &msg);
assert_eq!(&expected, &actual.as_ref());
Ok(())
});
}
fn run_monte_carlo_test(digest_alg: &'static digest::Algorithm,
file_name: &str) {
let section_name = &format!("L = {}", digest_alg.output_len);
let mut expected_count: isize = -1;
let mut seed = Vec::with_capacity(digest_alg.output_len);
test::from_file(file_name, |section, test_case| {
assert_eq!(section_name, section);
if expected_count == -1 {
seed.extend(test_case.consume_bytes("Seed"));
expected_count = 0;
return Ok(());
}
assert!(expected_count >= 0);
let actual_count = test_case.consume_usize("COUNT");
assert_eq!(expected_count as usize, actual_count);
expected_count += 1;
let expected_md = test_case.consume_bytes("MD");
let mut mds = Vec::with_capacity(4);
mds.push(seed.clone());
mds.push(seed.clone());
mds.push(seed.clone());
for _ in 0..1000 {
let mut ctx = digest::Context::new(digest_alg);
ctx.update(&mds[0]);
ctx.update(&mds[1]);
ctx.update(&mds[2]);
let md_i = ctx.finish();
let _ = mds.remove(0);
mds.push(Vec::from(md_i.as_ref()));
}
let md_j = mds.last().unwrap();
assert_eq!(&expected_md, md_j);
seed = md_j.clone();
Ok(())
});
assert_eq!(expected_count, 100);
}
shavs_tests!(SHA1);
shavs_tests!(SHA256);
shavs_tests!(SHA384);
shavs_tests!(SHA512);
}
macro_rules! test_i_u_f {
( $test_name:ident, $alg:expr) => {
#[cfg(not(debug_assertions))]
#[test]
fn $test_name() {
let mut input = [0; (super::MAX_BLOCK_LEN + 1) * 3];
let max = $alg.block_len + 1;
for i in 0..(max * 3) {
input[i] = (i & 0xff) as u8;
}
for i in 0..max {
for j in 0..max {
for k in 0..max {
let part1 = &input[..i];
let part2 = &input[i..(i+j)];
let part3 = &input[(i+j)..(i+j+k)];
let mut ctx = digest::Context::new(&$alg);
ctx.update(part1);
ctx.update(part2);
ctx.update(part3);
let i_u_f = ctx.finish();
let one_shot =
digest::digest(&$alg, &input[..(i + j + k)]);
assert_eq!(i_u_f.as_ref(), one_shot.as_ref());
}
}
}
}
}
}
test_i_u_f!(test_i_u_f_sha1, digest::SHA1);
test_i_u_f!(test_i_u_f_sha256, digest::SHA256);
test_i_u_f!(test_i_u_f_sha384, digest::SHA384);
test_i_u_f!(test_i_u_f_sha512, digest::SHA512);
macro_rules! test_large_digest {
( $test_name:ident, $alg:expr, $len:expr, $expected:expr) => {
#[cfg(not(debug_assertions))]
#[test]
fn $test_name() {
let chunk = vec![123u8; 16 * 1024];
let chunk_len = chunk.len() as u64;
let mut ctx = digest::Context::new(&$alg);
let mut hashed = 0u64;
loop {
ctx.update(&chunk);
hashed += chunk_len;
if hashed >= 8u64 * 1024 * 1024 * 1024 {
break;
}
}
let calculated = ctx.finish();
let expected: [u8; $len] = $expected;
assert_eq!(&expected[..], calculated.as_ref());
}
}
}
#[cfg(any(not(target_os = "android"), not(target_arch = "arm")))]
test_large_digest!(test_large_digest_sha1, digest::SHA1, 160 / 8, [
0xCA, 0xC3, 0x4C, 0x31, 0x90, 0x5B, 0xDE, 0x3B,
0xE4, 0x0D, 0x46, 0x6D, 0x70, 0x76, 0xAD, 0x65,
0x3C, 0x20, 0xE4, 0xBD
]);
test_large_digest!(test_large_digest_sha256, digest::SHA256, 256 / 8, [
0x8D, 0xD1, 0x6D, 0xD8, 0xB2, 0x5A, 0x29, 0xCB,
0x7F, 0xB9, 0xAE, 0x86, 0x72, 0xE9, 0xCE, 0xD6,
0x65, 0x4C, 0xB6, 0xC3, 0x5C, 0x58, 0x21, 0xA7,
0x07, 0x97, 0xC5, 0xDD, 0xAE, 0x5C, 0x68, 0xBD
]);
test_large_digest!(test_large_digest_sha384, digest::SHA384, 384 / 8, [
0x3D, 0xFE, 0xC1, 0xA9, 0xD0, 0x9F, 0x08, 0xD5,
0xBB, 0xE8, 0x7C, 0x9E, 0xE0, 0x0A, 0x87, 0x0E,
0xB0, 0xEA, 0x8E, 0xEA, 0xDB, 0x82, 0x36, 0xAE,
0x74, 0xCF, 0x9F, 0xDC, 0x86, 0x1C, 0xE3, 0xE9,
0xB0, 0x68, 0xCD, 0x19, 0x3E, 0x39, 0x90, 0x02,
0xE1, 0x58, 0x5D, 0x66, 0xC4, 0x55, 0x11, 0x9B
]);
test_large_digest!(test_large_digest_sha512, digest::SHA512, 512 / 8, [
0xFC, 0x8A, 0x98, 0x20, 0xFC, 0x82, 0xD8, 0x55,
0xF8, 0xFF, 0x2F, 0x6E, 0xAE, 0x41, 0x60, 0x04,
0x08, 0xE9, 0x49, 0xD7, 0xCD, 0x1A, 0xED, 0x22,
0xEB, 0x55, 0xE1, 0xFD, 0x80, 0x50, 0x3B, 0x01,
0x2F, 0xC6, 0xF4, 0x33, 0x86, 0xFB, 0x60, 0x75,
0x2D, 0xA5, 0xA9, 0x93, 0xE7, 0x00, 0x45, 0xA8,
0x49, 0x1A, 0x6B, 0xEC, 0x9C, 0x98, 0xC8, 0x19,
0xA6, 0xA9, 0x88, 0x3E, 0x2F, 0x09, 0xB9, 0x9A
]);
#[test]
fn test_fmt_algorithm() {
assert_eq!("SHA-1", &format!("{:?}", digest::SHA1));
assert_eq!("SHA-256", &format!("{:?}", digest::SHA256));
assert_eq!("SHA-384", &format!("{:?}", digest::SHA384));
assert_eq!("SHA-512", &format!("{:?}", digest::SHA512));
}
#[test]
fn test_fmt_digest() {
assert_eq!("SHA-1:b7e23ec29af22b0b4e41da31e868d57226121c84",
&format!("{:?}",
digest::digest(&digest::SHA1, b"hello, world")));
assert_eq!("SHA-256:09ca7e4eaa6e8ae9c7d261167129184883644d\
07dfba7cbfbc4c8a2e08360d5b",
&format!("{:?}",
digest::digest(&digest::SHA256, b"hello, world")));
assert_eq!("SHA-384:1fcdb6059ce05172a26bbe2a3ccc88ed5a8cd5\
fc53edfd9053304d429296a6da23b1cd9e5c9ed3bb34f0\
0418a70cdb7e",
&format!("{:?}",
digest::digest(&digest::SHA384, b"hello, world")));
assert_eq!("SHA-512:8710339dcb6814d0d9d2290ef422285c9322b7\
163951f9a0ca8f883d3305286f44139aa374848e4174f5\
aada663027e4548637b6d19894aec4fb6c46a139fbf9",
&format!("{:?}",
digest::digest(&digest::SHA512, b"hello, world")));
}
mod max_input {
use super::super::super::digest;
macro_rules! max_input_tests {
( $algorithm_name:ident ) => {
#[allow(non_snake_case)]
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);
}
}