diff --git a/src/libextra/crypto/cryptoutil.rs b/src/libextra/crypto/cryptoutil.rs new file mode 100644 index 0000000000000..43e3b5c89af48 --- /dev/null +++ b/src/libextra/crypto/cryptoutil.rs @@ -0,0 +1,327 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::num::One; +use std::vec::bytes::{MutableByteVector, copy_memory}; + + +/// Write a u64 into a vector, which must be 8 bytes long. The value is written in big-endian +/// format. +pub fn write_u64_be(dst: &mut[u8], input: u64) { + use std::cast::transmute; + use std::unstable::intrinsics::to_be64; + assert!(dst.len() == 8); + unsafe { + let x: *mut i64 = transmute(dst.unsafe_mut_ref(0)); + *x = to_be64(input as i64); + } +} + +/// Write a u32 into a vector, which must be 4 bytes long. The value is written in big-endian +/// format. +pub fn write_u32_be(dst: &mut[u8], input: u32) { + use std::cast::transmute; + use std::unstable::intrinsics::to_be32; + assert!(dst.len() == 4); + unsafe { + let x: *mut i32 = transmute(dst.unsafe_mut_ref(0)); + *x = to_be32(input as i32); + } +} + +/// Read a vector of bytes into a vector of u64s. The values are read in big-endian format. +pub fn read_u64v_be(dst: &mut[u64], input: &[u8]) { + use std::cast::transmute; + use std::unstable::intrinsics::to_be64; + assert!(dst.len() * 8 == input.len()); + unsafe { + let mut x: *mut i64 = transmute(dst.unsafe_mut_ref(0)); + let mut y: *i64 = transmute(input.unsafe_ref(0)); + do dst.len().times() { + *x = to_be64(*y); + x = x.offset(1); + y = y.offset(1); + } + } +} + +/// Read a vector of bytes into a vector of u32s. The values are read in big-endian format. +pub fn read_u32v_be(dst: &mut[u32], input: &[u8]) { + use std::cast::transmute; + use std::unstable::intrinsics::to_be32; + assert!(dst.len() * 4 == input.len()); + unsafe { + let mut x: *mut i32 = transmute(dst.unsafe_mut_ref(0)); + let mut y: *i32 = transmute(input.unsafe_ref(0)); + do dst.len().times() { + *x = to_be32(*y); + x = x.offset(1); + y = y.offset(1); + } + } +} + + +/// Returns true if adding the two parameters will result in integer overflow +pub fn will_add_overflow(x: T, y: T) -> bool { + // This doesn't handle negative values! Don't copy this code elsewhere without considering if + // negative values are important to you! + let max: T = Bounded::max_value(); + return x > max - y; +} + +/// Shifts the second parameter and then adds it to the first. fails!() if there would be unsigned +/// integer overflow. +pub fn shift_add_check_overflow(x: T, mut y: T, shift: T) -> T { + if y.leading_zeros() < shift { + fail!("Could not add values - integer overflow."); + } + y = y << shift; + + if will_add_overflow(x.clone(), y.clone()) { + fail!("Could not add values - integer overflow."); + } + + return x + y; +} + +/// Shifts the second parameter and then adds it to the first, which is a tuple where the first +/// element is the high order value. fails!() if there would be unsigned integer overflow. +pub fn shift_add_check_overflow_tuple + + (x: (T, T), mut y: T, shift: T) -> (T, T) { + if y.leading_zeros() < shift { + fail!("Could not add values - integer overflow."); + } + y = y << shift; + + match x { + (hi, low) => { + let one: T = One::one(); + if will_add_overflow(low.clone(), y.clone()) { + if will_add_overflow(hi.clone(), one.clone()) { + fail!("Could not add values - integer overflow."); + } else { + return (hi + one, low + y); + } + } else { + return (hi, low + y); + } + } + } +} + + +/// A FixedBuffer, likes its name implies, is a fixed size buffer. When the buffer becomes full, it +/// must be processed. The input() method takes care of processing and then clearing the buffer +/// automatically. However, other methods do not and require the caller to process the buffer. Any +/// method that modifies the buffer directory or provides the caller with bytes that can be modifies +/// results in those bytes being marked as used by the buffer. +pub trait FixedBuffer { + /// Input a vector of bytes. If the buffer becomes full, proccess it with the provided + /// function and then clear the buffer. + fn input(&mut self, input: &[u8], func: &fn(&[u8])); + + /// Reset the buffer. + fn reset(&mut self); + + /// Zero the buffer up until the specified index. The buffer position currently must not be + /// greater than that index. + fn zero_until(&mut self, idx: uint); + + /// Get a slice of the buffer of the specified size. There must be at least that many bytes + /// remaining in the buffer. + fn next<'s>(&'s mut self, len: uint) -> &'s mut [u8]; + + /// Get the current buffer. The buffer must already be full. This clears the buffer as well. + fn full_buffer<'s>(&'s mut self) -> &'s [u8]; + + /// Get the current position of the buffer. + fn position(&self) -> uint; + + /// Get the number of bytes remaining in the buffer until it is full. + fn remaining(&self) -> uint; + + /// Get the size of the buffer + fn size(&self) -> uint; +} + +macro_rules! impl_fixed_buffer( ($name:ident, $size:expr) => ( + impl FixedBuffer for $name { + fn input(&mut self, input: &[u8], func: &fn(&[u8])) { + let mut i = 0; + + // FIXME: #6304 - This local variable shouldn't be necessary. + let size = $size; + + // If there is already data in the buffer, copy as much as we can into it and process + // the data if the buffer becomes full. + if self.buffer_idx != 0 { + let buffer_remaining = size - self.buffer_idx; + if input.len() >= buffer_remaining { + copy_memory( + self.buffer.mut_slice(self.buffer_idx, size), + input.slice_to(buffer_remaining), + buffer_remaining); + self.buffer_idx = 0; + func(self.buffer); + i += buffer_remaining; + } else { + copy_memory( + self.buffer.mut_slice(self.buffer_idx, self.buffer_idx + input.len()), + input, + input.len()); + self.buffer_idx += input.len(); + return; + } + } + + // While we have at least a full buffer size chunks's worth of data, process that data + // without copying it into the buffer + while input.len() - i >= size { + func(input.slice(i, i + size)); + i += size; + } + + // Copy any input data into the buffer. At this point in the method, the ammount of + // data left in the input vector will be less than the buffer size and the buffer will + // be empty. + let input_remaining = input.len() - i; + copy_memory( + self.buffer.mut_slice(0, input_remaining), + input.slice_from(i), + input.len() - i); + self.buffer_idx += input_remaining; + } + + fn reset(&mut self) { + self.buffer_idx = 0; + } + + fn zero_until(&mut self, idx: uint) { + assert!(idx >= self.buffer_idx); + self.buffer.mut_slice(self.buffer_idx, idx).set_memory(0); + self.buffer_idx = idx; + } + + fn next<'s>(&'s mut self, len: uint) -> &'s mut [u8] { + self.buffer_idx += len; + return self.buffer.mut_slice(self.buffer_idx - len, self.buffer_idx); + } + + fn full_buffer<'s>(&'s mut self) -> &'s [u8] { + assert!(self.buffer_idx == $size); + self.buffer_idx = 0; + return self.buffer.slice_to($size); + } + + fn position(&self) -> uint { self.buffer_idx } + + fn remaining(&self) -> uint { $size - self.buffer_idx } + + fn size(&self) -> uint { $size } + } +)) + + +/// A fixed size buffer of 64 bytes useful for cryptographic operations. +pub struct FixedBuffer64 { + priv buffer: [u8, ..64], + priv buffer_idx: uint, +} + +impl FixedBuffer64 { + /// Create a new buffer + pub fn new() -> FixedBuffer64 { + return FixedBuffer64 { + buffer: [0u8, ..64], + buffer_idx: 0 + }; + } +} + +impl_fixed_buffer!(FixedBuffer64, 64) + +/// A fixed size buffer of 128 bytes useful for cryptographic operations. +pub struct FixedBuffer128 { + priv buffer: [u8, ..128], + priv buffer_idx: uint, +} + +impl FixedBuffer128 { + /// Create a new buffer + pub fn new() -> FixedBuffer128 { + return FixedBuffer128 { + buffer: [0u8, ..128], + buffer_idx: 0 + }; + } +} + +impl_fixed_buffer!(FixedBuffer128, 128) + + +/// The StandardPadding trait adds a method useful for various hash algorithms to a FixedBuffer +/// struct. +pub trait StandardPadding { + /// Add standard padding to the buffer. The buffer must not be full when this method is called + /// and is guaranteed to have exactly rem remaining bytes when it returns. If there are not at + /// least rem bytes available, the buffer will be zero padded, processed, cleared, and then + /// filled with zeros again until only rem bytes are remaining. + fn standard_padding(&mut self, rem: uint, func: &fn(&[u8])); +} + +impl StandardPadding for T { + fn standard_padding(&mut self, rem: uint, func: &fn(&[u8])) { + let size = self.size(); + + self.next(1)[0] = 128; + + if self.remaining() < rem { + self.zero_until(size); + func(self.full_buffer()); + } + + self.zero_until(size - rem); + } +} + + +#[cfg(test)] +mod test { + use std::rand::IsaacRng; + use std::rand::RngUtil; + use std::vec; + + use digest::Digest; + + /// Feed 1,000,000 'a's into the digest with varying input sizes and check that the result is + /// correct. + pub fn test_digest_1million_random(digest: &mut D, blocksize: uint, expected: &str) { + let total_size = 1000000; + let buffer = vec::from_elem(blocksize * 2, 'a' as u8); + let mut rng = IsaacRng::new_unseeded(); + let mut count = 0; + + digest.reset(); + + while count < total_size { + let next: uint = rng.gen_uint_range(0, 2 * blocksize + 1); + let remaining = total_size - count; + let size = if next > remaining { remaining } else { next }; + digest.input(buffer.slice_to(size)); + count += size; + } + + let result_str = digest.result_str(); + + assert!(expected == result_str); + } +} diff --git a/src/libextra/crypto/digest.rs b/src/libextra/crypto/digest.rs index 0efd88fdf5043..217573a4135b0 100644 --- a/src/libextra/crypto/digest.rs +++ b/src/libextra/crypto/digest.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - use std::uint; use std::vec; + /** * The Digest trait specifies an interface common to digest functions, such as SHA-1 and the SHA-2 * family of digest functions. @@ -28,6 +28,10 @@ pub trait Digest { /** * Retrieve the digest result. This method may be called multiple times. + * + * # Arguments + * + * * out - the vector to hold the result. Must be large enough to contain output_bits(). */ fn result(&mut self, out: &mut [u8]); @@ -41,23 +45,7 @@ pub trait Digest { * Get the output size in bits. */ fn output_bits(&self) -> uint; -} - -fn to_hex(rr: &[u8]) -> ~str { - let mut s = ~""; - foreach b in rr.iter() { - let hex = uint::to_str_radix(*b as uint, 16u); - if hex.len() == 1 { - s.push_char('0'); - } - s.push_str(hex); - } - return s; -} -/// Contains utility methods for Digests. -/// FIXME: #7339: Convert to default methods when issues with them are resolved. -pub trait DigestUtil { /** * Convenience functon that feeds a string into a digest * @@ -65,23 +53,29 @@ pub trait DigestUtil { * * * in The string to feed into the digest */ - fn input_str(&mut self, input: &str); + fn input_str(&mut self, input: &str) { + self.input(input.as_bytes()); + } /** * Convenience functon that retrieves the result of a digest as a * ~str in hexadecimal format. */ - fn result_str(&mut self) -> ~str; -} - -impl DigestUtil for D { - fn input_str(&mut self, input: &str) { - self.input(input.as_bytes()); - } - fn result_str(&mut self) -> ~str { let mut buf = vec::from_elem((self.output_bits()+7)/8, 0u8); self.result(buf); return to_hex(buf); } } + +fn to_hex(rr: &[u8]) -> ~str { + let mut s = ~""; + foreach b in rr.iter() { + let hex = uint::to_str_radix(*b as uint, 16u); + if hex.len() == 1 { + s.push_char('0'); + } + s.push_str(hex); + } + return s; +} diff --git a/src/libextra/crypto/sha1.rs b/src/libextra/crypto/sha1.rs index 7ede1978495c6..86a89d79f1add 100644 --- a/src/libextra/crypto/sha1.rs +++ b/src/libextra/crypto/sha1.rs @@ -23,6 +23,8 @@ */ +use cryptoutil::{write_u32_be, read_u32v_be, shift_add_check_overflow, FixedBuffer, FixedBuffer64, + StandardPadding}; use digest::Digest; /* @@ -33,7 +35,6 @@ use digest::Digest; // Some unexported constants static DIGEST_BUF_LEN: uint = 5u; -static MSG_BLOCK_LEN: uint = 64u; static WORK_BUF_LEN: uint = 80u; static K0: u32 = 0x5A827999u32; static K1: u32 = 0x6ED9EBA1u32; @@ -43,58 +44,38 @@ static K3: u32 = 0xCA62C1D6u32; /// Structure representing the state of a Sha1 computation pub struct Sha1 { priv h: [u32, ..DIGEST_BUF_LEN], - priv len_low: u32, - priv len_high: u32, - priv msg_block: [u8, ..MSG_BLOCK_LEN], - priv msg_block_idx: uint, + priv length_bits: u64, + priv buffer: FixedBuffer64, priv computed: bool, - priv work_buf: [u32, ..WORK_BUF_LEN] } fn add_input(st: &mut Sha1, msg: &[u8]) { assert!((!st.computed)); - foreach element in msg.iter() { - st.msg_block[st.msg_block_idx] = *element; - st.msg_block_idx += 1; - st.len_low += 8; - if st.len_low == 0 { - st.len_high += 1; - if st.len_high == 0 { - // FIXME: Need better failure mode (#2346) - fail!(); - } - } - if st.msg_block_idx == MSG_BLOCK_LEN { process_msg_block(st); } - } + // Assumes that msg.len() can be converted to u64 without overflow + st.length_bits = shift_add_check_overflow(st.length_bits, msg.len() as u64, 3); + st.buffer.input(msg, |d: &[u8]| { process_msg_block(d, &mut st.h); }); } -fn process_msg_block(st: &mut Sha1) { +fn process_msg_block(data: &[u8], h: &mut [u32, ..DIGEST_BUF_LEN]) { let mut t: int; // Loop counter - let mut w = st.work_buf; + + let mut w = [0u32, ..WORK_BUF_LEN]; // Initialize the first 16 words of the vector w - t = 0; - while t < 16 { - let mut tmp; - tmp = (st.msg_block[t * 4] as u32) << 24u32; - tmp = tmp | (st.msg_block[t * 4 + 1] as u32) << 16u32; - tmp = tmp | (st.msg_block[t * 4 + 2] as u32) << 8u32; - tmp = tmp | (st.msg_block[t * 4 + 3] as u32); - w[t] = tmp; - t += 1; - } + read_u32v_be(w.mut_slice(0, 16), data); // Initialize the rest of vector w + t = 16; while t < 80 { let val = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]; w[t] = circular_shift(1, val); t += 1; } - let mut a = st.h[0]; - let mut b = st.h[1]; - let mut c = st.h[2]; - let mut d = st.h[3]; - let mut e = st.h[4]; + let mut a = h[0]; + let mut b = h[1]; + let mut c = h[2]; + let mut d = h[3]; + let mut e = h[4]; let mut temp: u32; t = 0; while t < 20 { @@ -135,12 +116,11 @@ fn process_msg_block(st: &mut Sha1) { a = temp; t += 1; } - st.h[0] = st.h[0] + a; - st.h[1] = st.h[1] + b; - st.h[2] = st.h[2] + c; - st.h[3] = st.h[3] + d; - st.h[4] = st.h[4] + e; - st.msg_block_idx = 0; + h[0] += a; + h[1] += b; + h[2] += c; + h[3] += d; + h[4] += e; } fn circular_shift(bits: u32, word: u32) -> u32 { @@ -148,60 +128,20 @@ fn circular_shift(bits: u32, word: u32) -> u32 { } fn mk_result(st: &mut Sha1, rs: &mut [u8]) { - if !st.computed { pad_msg(st); st.computed = true; } - let mut i = 0; - foreach ptr_hpart in st.h.mut_iter() { - let hpart = *ptr_hpart; - rs[i] = (hpart >> 24u32 & 0xFFu32) as u8; - rs[i+1] = (hpart >> 16u32 & 0xFFu32) as u8; - rs[i+2] = (hpart >> 8u32 & 0xFFu32) as u8; - rs[i+3] = (hpart & 0xFFu32) as u8; - i += 4; - } -} + if !st.computed { + st.buffer.standard_padding(8, |d: &[u8]| { process_msg_block(d, &mut st.h) }); + write_u32_be(st.buffer.next(4), (st.length_bits >> 32) as u32 ); + write_u32_be(st.buffer.next(4), st.length_bits as u32); + process_msg_block(st.buffer.full_buffer(), &mut st.h); -/* - * According to the standard, the message must be padded to an even - * 512 bits. The first padding bit must be a '1'. The last 64 bits - * represent the length of the original message. All bits in between - * should be 0. This function will pad the message according to those - * rules by filling the msg_block vector accordingly. It will also - * call process_msg_block() appropriately. When it returns, it - * can be assumed that the message digest has been computed. - */ -fn pad_msg(st: &mut Sha1) { - /* - * Check to see if the current message block is too small to hold - * the initial padding bits and length. If so, we will pad the - * block, process it, and then continue padding into a second block. - */ - if st.msg_block_idx > 55 { - st.msg_block[st.msg_block_idx] = 0x80; - st.msg_block_idx += 1; - while st.msg_block_idx < MSG_BLOCK_LEN { - st.msg_block[st.msg_block_idx] = 0; - st.msg_block_idx += 1; - } - process_msg_block(st); - } else { - st.msg_block[st.msg_block_idx] = 0x80; - st.msg_block_idx += 1; - } - while st.msg_block_idx < 56 { - st.msg_block[st.msg_block_idx] = 0u8; - st.msg_block_idx += 1; + st.computed = true; } - // Store the message length as the last 8 octets - st.msg_block[56] = (st.len_high >> 24u32 & 0xFFu32) as u8; - st.msg_block[57] = (st.len_high >> 16u32 & 0xFFu32) as u8; - st.msg_block[58] = (st.len_high >> 8u32 & 0xFFu32) as u8; - st.msg_block[59] = (st.len_high & 0xFFu32) as u8; - st.msg_block[60] = (st.len_low >> 24u32 & 0xFFu32) as u8; - st.msg_block[61] = (st.len_low >> 16u32 & 0xFFu32) as u8; - st.msg_block[62] = (st.len_low >> 8u32 & 0xFFu32) as u8; - st.msg_block[63] = (st.len_low & 0xFFu32) as u8; - process_msg_block(st); + write_u32_be(rs.mut_slice(0, 4), st.h[0]); + write_u32_be(rs.mut_slice(4, 8), st.h[1]); + write_u32_be(rs.mut_slice(8, 12), st.h[2]); + write_u32_be(rs.mut_slice(12, 16), st.h[3]); + write_u32_be(rs.mut_slice(16, 20), st.h[4]); } impl Sha1 { @@ -209,12 +149,9 @@ impl Sha1 { pub fn new() -> Sha1 { let mut st = Sha1 { h: [0u32, ..DIGEST_BUF_LEN], - len_low: 0u32, - len_high: 0u32, - msg_block: [0u8, ..MSG_BLOCK_LEN], - msg_block_idx: 0, + length_bits: 0u64, + buffer: FixedBuffer64::new(), computed: false, - work_buf: [0u32, ..WORK_BUF_LEN] }; st.reset(); return st; @@ -223,14 +160,13 @@ impl Sha1 { impl Digest for Sha1 { pub fn reset(&mut self) { - self.len_low = 0; - self.len_high = 0; - self.msg_block_idx = 0; + self.length_bits = 0; self.h[0] = 0x67452301u32; self.h[1] = 0xEFCDAB89u32; self.h[2] = 0x98BADCFEu32; self.h[3] = 0x10325476u32; self.h[4] = 0xC3D2E1F0u32; + self.buffer.reset(); self.computed = false; } pub fn input(&mut self, msg: &[u8]) { add_input(self, msg); } @@ -240,8 +176,8 @@ impl Digest for Sha1 { #[cfg(test)] mod tests { - - use digest::{Digest, DigestUtil}; + use cryptoutil::test::test_digest_1million_random; + use digest::Digest; use sha1::Sha1; #[deriving(Clone)] @@ -253,15 +189,6 @@ mod tests { #[test] fn test() { - fn a_million_letter_a() -> ~str { - let mut i = 0; - let mut rs = ~""; - while i < 100000 { - rs.push_str("aaaaaaaaaa"); - i += 1; - } - return rs; - } // Test messages from FIPS 180-1 let fips_180_1_tests = ~[ @@ -289,17 +216,6 @@ mod tests { ], output_str: ~"84983e441c3bd26ebaae4aa1f95129e5e54670f1" }, - Test { - input: a_million_letter_a(), - output: ~[ - 0x34u8, 0xAAu8, 0x97u8, 0x3Cu8, - 0xD4u8, 0xC4u8, 0xDAu8, 0xA4u8, - 0xF6u8, 0x1Eu8, 0xEBu8, 0x2Bu8, - 0xDBu8, 0xADu8, 0x27u8, 0x31u8, - 0x65u8, 0x34u8, 0x01u8, 0x6Fu8, - ], - output_str: ~"34aa973cd4c4daa4f61eeb2bdbad27316534016f" - }, ]; // Examples from wikipedia @@ -366,6 +282,15 @@ mod tests { sh.reset(); } } + + #[test] + fn test_1million_random_sha1() { + let mut sh = Sha1::new(); + test_digest_1million_random( + &mut sh, + 64, + "34aa973cd4c4daa4f61eeb2bdbad27316534016f"); + } } #[cfg(test)] diff --git a/src/libextra/crypto/sha2.rs b/src/libextra/crypto/sha2.rs index d713e19297295..d92a4be43c388 100644 --- a/src/libextra/crypto/sha2.rs +++ b/src/libextra/crypto/sha2.rs @@ -8,47 +8,35 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::uint; + +use cryptoutil::{write_u64_be, write_u32_be, read_u64v_be, read_u32v_be, shift_add_check_overflow, + shift_add_check_overflow_tuple, FixedBuffer, FixedBuffer128, FixedBuffer64, StandardPadding}; use digest::Digest; -// BitCounter is a specialized structure intended simply for counting the -// number of bits that have been processed by the SHA-2 512 family of functions. -// It does very little overflow checking since such checking is not necessary -// for how it is used. A more generic structure would have to do this checking. -// So, don't copy this structure and use it elsewhere! -struct BitCounter { - high_bit_count: u64, - low_byte_count: u64 -} -impl BitCounter { - fn add_bytes(&mut self, bytes: uint) { - self.low_byte_count += bytes as u64; - if(self.low_byte_count > 0x1fffffffffffffffu64) { - self.high_bit_count += (self.low_byte_count >> 61); - self.low_byte_count &= 0x1fffffffffffffffu64; - } - } +// Sha-512 and Sha-256 use basically the same calculations which are implemented by these macros. +// Inlining the calculations seems to result in better generated code. +macro_rules! schedule_round( ($t:expr) => ( + W[$t] = sigma1(W[$t - 2]) + W[$t - 7] + sigma0(W[$t - 15]) + W[$t - 16]; + ) +) - fn reset(&mut self) { - self.low_byte_count = 0; - self.high_bit_count = 0; - } - - fn get_low_bit_count(&self) -> u64 { - self.low_byte_count << 3 - } +macro_rules! sha2_round( + ($A:ident, $B:ident, $C:ident, $D:ident, + $E:ident, $F:ident, $G:ident, $H:ident, $K:ident, $t:expr) => ( + { + $H += sum1($E) + ch($E, $F, $G) + $K[$t] + W[$t]; + $D += $H; + $H += sum0($A) + maj($A, $B, $C); + } + ) +) - fn get_high_bit_count(&self) -> u64 { - self.high_bit_count - } -} -// A structure that represents that state of a digest computation -// for the SHA-2 512 family of digest functions -struct Engine512 { - input_buffer: [u8, ..8], - input_buffer_idx: uint, - bit_counter: BitCounter, +// A structure that represents that state of a digest computation for the SHA-2 512 family of digest +// functions +struct Engine512State { H0: u64, H1: u64, H2: u64, @@ -57,91 +45,34 @@ struct Engine512 { H5: u64, H6: u64, H7: u64, - W: [u64, ..80], - W_idx: uint, - finished: bool, -} - -// Convert a [u8] to a u64 in big-endian format -fn to_u64(input: &[u8]) -> u64 { - (input[0] as u64) << 56 | - (input[1] as u64) << 48 | - (input[2] as u64) << 40 | - (input[3] as u64) << 32 | - (input[4] as u64) << 24 | - (input[5] as u64) << 16 | - (input[6] as u64) << 8 | - (input[7] as u64) -} - -// Convert a u64 to a [u8] in big endian format -fn from_u64(input: u64, out: &mut [u8]) { - out[0] = (input >> 56) as u8; - out[1] = (input >> 48) as u8; - out[2] = (input >> 40) as u8; - out[3] = (input >> 32) as u8; - out[4] = (input >> 24) as u8; - out[5] = (input >> 16) as u8; - out[6] = (input >> 8) as u8; - out[7] = input as u8; } -impl Engine512 { - fn input_byte(&mut self, input: u8) { - assert!(!self.finished) - - self.input_buffer[self.input_buffer_idx] = input; - self.input_buffer_idx += 1; - - if (self.input_buffer_idx == 8) { - self.input_buffer_idx = 0; - let w = to_u64(self.input_buffer); - self.process_word(w); - } - - self.bit_counter.add_bytes(1); - } - - fn input_vec(&mut self, input: &[u8]) { - assert!(!self.finished) - - let mut i = 0; - - while i < input.len() && self.input_buffer_idx != 0 { - self.input_byte(input[i]); - i += 1; - } - - while input.len() - i >= 8 { - let w = to_u64(input.slice(i, i + 8)); - self.process_word(w); - self.bit_counter.add_bytes(8); - i += 8; - } - - while i < input.len() { - self.input_byte(input[i]); - i += 1; - } - } - - fn reset(&mut self) { - self.bit_counter.reset(); - self.finished = false; - self.input_buffer_idx = 0; - self.W_idx = 0; - } - - fn process_word(&mut self, input: u64) { - self.W[self.W_idx] = input; - self.W_idx += 1; - if (self.W_idx == 16) { - self.W_idx = 0; - self.process_block(); - } - } - - fn process_block(&mut self) { +impl Engine512State { + fn new(h: &[u64, ..8]) -> Engine512State { + return Engine512State { + H0: h[0], + H1: h[1], + H2: h[2], + H3: h[3], + H4: h[4], + H5: h[5], + H6: h[6], + H7: h[7] + }; + } + + fn reset(&mut self, h: &[u64, ..8]) { + self.H0 = h[0]; + self.H1 = h[1]; + self.H2 = h[2]; + self.H3 = h[3]; + self.H4 = h[4]; + self.H5 = h[5]; + self.H6 = h[6]; + self.H7 = h[7]; + } + + fn process_block(&mut self, data: &[u8]) { fn ch(x: u64, y: u64, z: u64) -> u64 { ((x & y) ^ ((!x) & z)) } @@ -166,11 +97,6 @@ impl Engine512 { ((x << 45) | (x >> 19)) ^ ((x << 3) | (x >> 61)) ^ (x >> 6) } - foreach t in range(16u, 80) { - self.W[t] = sigma1(self.W[t - 2]) + self.W[t - 7] + sigma0(self.W[t - 15]) + - self.W[t - 16]; - } - let mut a = self.H0; let mut b = self.H1; let mut c = self.H2; @@ -180,47 +106,41 @@ impl Engine512 { let mut g = self.H6; let mut h = self.H7; - let mut t = 0; - foreach _ in range(0u, 10) { - h += sum1(e) + ch(e, f, g) + K64[t] + self.W[t]; - d += h; - h += sum0(a) + maj(a, b, c); - t += 1; - - g += sum1(d) + ch(d, e, f) + K64[t] + self.W[t]; - c += g; - g += sum0(h) + maj(h, a, b); - t += 1; - - f += sum1(c) + ch(c, d, e) + K64[t] + self.W[t]; - b += f; - f += sum0(g) + maj(g, h, a); - t += 1; - - e += sum1(b) + ch(b, c, d) + K64[t] + self.W[t]; - a += e; - e += sum0(f) + maj(f, g, h); - t += 1; - - d += sum1(a) + ch(a, b, c) + K64[t] + self.W[t]; - h += d; - d += sum0(e) + maj(e, f, g); - t += 1; - - c += sum1(h) + ch(h, a, b) + K64[t] + self.W[t]; - g += c; - c += sum0(d) + maj(d, e, f); - t += 1; - - b += sum1(g) + ch(g, h, a) + K64[t] + self.W[t]; - f += b; - b += sum0(c) + maj(c, d, e); - t += 1; - - a += sum1(f) + ch(f, g, h) + K64[t] + self.W[t]; - e += a; - a += sum0(b) + maj(b, c, d); - t += 1; + let mut W = [0u64, ..80]; + + read_u64v_be(W.mut_slice(0, 16), data); + + // Putting the message schedule inside the same loop as the round calculations allows for + // the compiler to generate better code. + for uint::range_step(0, 64, 8) |t| { + schedule_round!(t + 16); + schedule_round!(t + 17); + schedule_round!(t + 18); + schedule_round!(t + 19); + schedule_round!(t + 20); + schedule_round!(t + 21); + schedule_round!(t + 22); + schedule_round!(t + 23); + + sha2_round!(a, b, c, d, e, f, g, h, K64, t); + sha2_round!(h, a, b, c, d, e, f, g, K64, t + 1); + sha2_round!(g, h, a, b, c, d, e, f, K64, t + 2); + sha2_round!(f, g, h, a, b, c, d, e, K64, t + 3); + sha2_round!(e, f, g, h, a, b, c, d, K64, t + 4); + sha2_round!(d, e, f, g, h, a, b, c, K64, t + 5); + sha2_round!(c, d, e, f, g, h, a, b, K64, t + 6); + sha2_round!(b, c, d, e, f, g, h, a, K64, t + 7); + } + + for uint::range_step(64, 80, 8) |t| { + sha2_round!(a, b, c, d, e, f, g, h, K64, t); + sha2_round!(h, a, b, c, d, e, f, g, K64, t + 1); + sha2_round!(g, h, a, b, c, d, e, f, K64, t + 2); + sha2_round!(f, g, h, a, b, c, d, e, K64, t + 3); + sha2_round!(e, f, g, h, a, b, c, d, K64, t + 4); + sha2_round!(d, e, f, g, h, a, b, c, K64, t + 5); + sha2_round!(c, d, e, f, g, h, a, b, K64, t + 6); + sha2_round!(b, c, d, e, f, g, h, a, K64, t + 7); } self.H0 += a; @@ -232,199 +152,322 @@ impl Engine512 { self.H6 += g; self.H7 += h; } +} - fn finish(&mut self) { - if (self.finished) { - return; +// Constants necessary for SHA-2 512 family of digests. +static K64: [u64, ..80] = [ + 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, + 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, + 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, + 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, + 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, + 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, + 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, + 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, + 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, + 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, + 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, + 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, + 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 +]; + + +// A structure that keeps track of the state of the Sha-512 operation and contains the logic +// necessary to perform the final calculations. +struct Engine512 { + length_bits: (u64, u64), + buffer: FixedBuffer128, + state: Engine512State, + finished: bool, +} + +impl Engine512 { + fn new(h: &[u64, ..8]) -> Engine512 { + return Engine512 { + length_bits: (0, 0), + buffer: FixedBuffer128::new(), + state: Engine512State::new(h), + finished: false } + } - // must get message length before padding is added - let high_bit_count = self.bit_counter.get_high_bit_count(); - let low_bit_count = self.bit_counter.get_low_bit_count(); + fn reset(&mut self, h: &[u64, ..8]) { + self.length_bits = (0, 0); + self.buffer.reset(); + self.state.reset(h); + self.finished = false; + } - // add padding - self.input_byte(128u8); + fn input(&mut self, input: &[u8]) { + assert!(!self.finished) + // Assumes that input.len() can be converted to u64 without overflow + self.length_bits = shift_add_check_overflow_tuple(self.length_bits, input.len() as u64, 3); + self.buffer.input(input, |input: &[u8]| { self.state.process_block(input) }); + } - while self.input_buffer_idx != 0 { - self.input_byte(0u8); + fn finish(&mut self) { + if self.finished { + return; } - // add length - if (self.W_idx > 14) { - foreach _ in range(self.W_idx, 16) { - self.process_word(0); + self.buffer.standard_padding(16, |input: &[u8]| { self.state.process_block(input) }); + match self.length_bits { + (hi, low) => { + write_u64_be(self.buffer.next(8), hi); + write_u64_be(self.buffer.next(8), low); } } + self.state.process_block(self.buffer.full_buffer()); - while self.W_idx < 14 { - self.process_word(0); - } + self.finished = true; + } +} - self.process_word(high_bit_count); - self.process_word(low_bit_count); - self.finished = true; +struct Sha512 { + priv engine: Engine512 +} + +impl Sha512 { + /** + * Construct an new instance of a SHA-512 digest. + */ + pub fn new() -> Sha512 { + return Sha512 { + engine: Engine512::new(&H512) + }; } +} - fn result_512(&mut self, out: &mut [u8]) { - self.finish(); - - from_u64(self.H0, out.mut_slice(0, 8)); - from_u64(self.H1, out.mut_slice(8, 16)); - from_u64(self.H2, out.mut_slice(16, 24)); - from_u64(self.H3, out.mut_slice(24, 32)); - from_u64(self.H4, out.mut_slice(32, 40)); - from_u64(self.H5, out.mut_slice(40, 48)); - from_u64(self.H6, out.mut_slice(48, 56)); - from_u64(self.H7, out.mut_slice(56, 64)); +impl Digest for Sha512 { + fn input(&mut self, d: &[u8]) { + self.engine.input(d); } - fn result_384(&mut self, out: &mut [u8]) { - self.finish(); + fn result(&mut self, out: &mut [u8]) { + self.engine.finish(); - from_u64(self.H0, out.mut_slice(0, 8)); - from_u64(self.H1, out.mut_slice(8, 16)); - from_u64(self.H2, out.mut_slice(16, 24)); - from_u64(self.H3, out.mut_slice(24, 32)); - from_u64(self.H4, out.mut_slice(32, 40)); - from_u64(self.H5, out.mut_slice(40, 48)); + write_u64_be(out.mut_slice(0, 8), self.engine.state.H0); + write_u64_be(out.mut_slice(8, 16), self.engine.state.H1); + write_u64_be(out.mut_slice(16, 24), self.engine.state.H2); + write_u64_be(out.mut_slice(24, 32), self.engine.state.H3); + write_u64_be(out.mut_slice(32, 40), self.engine.state.H4); + write_u64_be(out.mut_slice(40, 48), self.engine.state.H5); + write_u64_be(out.mut_slice(48, 56), self.engine.state.H6); + write_u64_be(out.mut_slice(56, 64), self.engine.state.H7); } - fn result_256(&mut self, out: &mut [u8]) { - self.finish(); + fn reset(&mut self) { + self.engine.reset(&H512); + } + + fn output_bits(&self) -> uint { 512 } +} + +static H512: [u64, ..8] = [ + 0x6a09e667f3bcc908, + 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, + 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, + 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, + 0x5be0cd19137e2179 +]; + + +struct Sha384 { + priv engine: Engine512 +} + +impl Sha384 { + /** + * Construct an new instance of a SHA-384 digest. + */ + pub fn new() -> Sha384 { + Sha384 { + engine: Engine512::new(&H384) + } + } +} - from_u64(self.H0, out.mut_slice(0, 8)); - from_u64(self.H1, out.mut_slice(8, 16)); - from_u64(self.H2, out.mut_slice(16, 24)); - from_u64(self.H3, out.mut_slice(24, 32)); +impl Digest for Sha384 { + fn input(&mut self, d: &[u8]) { + self.engine.input(d); } - fn result_224(&mut self, out: &mut [u8]) { - self.finish(); + fn result(&mut self, out: &mut [u8]) { + self.engine.finish(); - from_u64(self.H0, out.mut_slice(0, 8)); - from_u64(self.H1, out.mut_slice(8, 16)); - from_u64(self.H2, out.mut_slice(16, 24)); - from_u32((self.H3 >> 32) as u32, out.mut_slice(24, 28)); + write_u64_be(out.mut_slice(0, 8), self.engine.state.H0); + write_u64_be(out.mut_slice(8, 16), self.engine.state.H1); + write_u64_be(out.mut_slice(16, 24), self.engine.state.H2); + write_u64_be(out.mut_slice(24, 32), self.engine.state.H3); + write_u64_be(out.mut_slice(32, 40), self.engine.state.H4); + write_u64_be(out.mut_slice(40, 48), self.engine.state.H5); + } + + fn reset(&mut self) { + self.engine.reset(&H384); } + + fn output_bits(&self) -> uint { 384 } } -// Constants necessary for SHA-2 512 family of digests. -static K64: [u64, ..80] = [ - 0x428a2f98d728ae22u64, 0x7137449123ef65cdu64, 0xb5c0fbcfec4d3b2fu64, 0xe9b5dba58189dbbcu64, - 0x3956c25bf348b538u64, 0x59f111f1b605d019u64, 0x923f82a4af194f9bu64, 0xab1c5ed5da6d8118u64, - 0xd807aa98a3030242u64, 0x12835b0145706fbeu64, 0x243185be4ee4b28cu64, 0x550c7dc3d5ffb4e2u64, - 0x72be5d74f27b896fu64, 0x80deb1fe3b1696b1u64, 0x9bdc06a725c71235u64, 0xc19bf174cf692694u64, - 0xe49b69c19ef14ad2u64, 0xefbe4786384f25e3u64, 0x0fc19dc68b8cd5b5u64, 0x240ca1cc77ac9c65u64, - 0x2de92c6f592b0275u64, 0x4a7484aa6ea6e483u64, 0x5cb0a9dcbd41fbd4u64, 0x76f988da831153b5u64, - 0x983e5152ee66dfabu64, 0xa831c66d2db43210u64, 0xb00327c898fb213fu64, 0xbf597fc7beef0ee4u64, - 0xc6e00bf33da88fc2u64, 0xd5a79147930aa725u64, 0x06ca6351e003826fu64, 0x142929670a0e6e70u64, - 0x27b70a8546d22ffcu64, 0x2e1b21385c26c926u64, 0x4d2c6dfc5ac42aedu64, 0x53380d139d95b3dfu64, - 0x650a73548baf63deu64, 0x766a0abb3c77b2a8u64, 0x81c2c92e47edaee6u64, 0x92722c851482353bu64, - 0xa2bfe8a14cf10364u64, 0xa81a664bbc423001u64, 0xc24b8b70d0f89791u64, 0xc76c51a30654be30u64, - 0xd192e819d6ef5218u64, 0xd69906245565a910u64, 0xf40e35855771202au64, 0x106aa07032bbd1b8u64, - 0x19a4c116b8d2d0c8u64, 0x1e376c085141ab53u64, 0x2748774cdf8eeb99u64, 0x34b0bcb5e19b48a8u64, - 0x391c0cb3c5c95a63u64, 0x4ed8aa4ae3418acbu64, 0x5b9cca4f7763e373u64, 0x682e6ff3d6b2b8a3u64, - 0x748f82ee5defb2fcu64, 0x78a5636f43172f60u64, 0x84c87814a1f0ab72u64, 0x8cc702081a6439ecu64, - 0x90befffa23631e28u64, 0xa4506cebde82bde9u64, 0xbef9a3f7b2c67915u64, 0xc67178f2e372532bu64, - 0xca273eceea26619cu64, 0xd186b8c721c0c207u64, 0xeada7dd6cde0eb1eu64, 0xf57d4f7fee6ed178u64, - 0x06f067aa72176fbau64, 0x0a637dc5a2c898a6u64, 0x113f9804bef90daeu64, 0x1b710b35131c471bu64, - 0x28db77f523047d84u64, 0x32caab7b40c72493u64, 0x3c9ebe0a15c9bebcu64, 0x431d67c49c100d4cu64, - 0x4cc5d4becb3e42b6u64, 0x597f299cfc657e2au64, 0x5fcb6fab3ad6faecu64, 0x6c44198c4a475817u64 +static H384: [u64, ..8] = [ + 0xcbbb9d5dc1059ed8, + 0x629a292a367cd507, + 0x9159015a3070dd17, + 0x152fecd8f70e5939, + 0x67332667ffc00b31, + 0x8eb44a8768581511, + 0xdb0c2e0d64f98fa7, + 0x47b5481dbefa4fa4 ]; -// A structure that represents that state of a digest computation -// for the SHA-2 256 family of digest functions -struct Engine256 { - input_buffer: [u8, ..4], - input_buffer_idx: uint, - length_bytes: u64, - H0: u32, - H1: u32, - H2: u32, - H3: u32, - H4: u32, - H5: u32, - H6: u32, - H7: u32, - W: [u32, ..64], - W_idx: uint, - finished: bool -} -// Convert a [u8] to a u32 in big endian format -fn to_u32(input: &[u8]) -> u32 { - (input[0] as u32) << 24 | - (input[1] as u32) << 16 | - (input[2] as u32) << 8 | - (input[3] as u32) +struct Sha512Trunc256 { + priv engine: Engine512 } -// Convert a u32 to a [u8] in big endian format -fn from_u32(input: u32, out: &mut [u8]) { - out[0] = (input >> 24) as u8; - out[1] = (input >> 16) as u8; - out[2] = (input >> 8) as u8; - out[3] = input as u8; +impl Sha512Trunc256 { + /** + * Construct an new instance of a SHA-512/256 digest. + */ + pub fn new() -> Sha512Trunc256 { + Sha512Trunc256 { + engine: Engine512::new(&H512_TRUNC_256) + } + } } -impl Engine256 { - fn input_byte(&mut self, input: u8) { - assert!(!self.finished) +impl Digest for Sha512Trunc256 { + fn input(&mut self, d: &[u8]) { + self.engine.input(d); + } - self.input_buffer[self.input_buffer_idx] = input; - self.input_buffer_idx += 1; + fn result(&mut self, out: &mut [u8]) { + self.engine.finish(); - if (self.input_buffer_idx == 4) { - self.input_buffer_idx = 0; - let w = to_u32(self.input_buffer); - self.process_word(w); - } + write_u64_be(out.mut_slice(0, 8), self.engine.state.H0); + write_u64_be(out.mut_slice(8, 16), self.engine.state.H1); + write_u64_be(out.mut_slice(16, 24), self.engine.state.H2); + write_u64_be(out.mut_slice(24, 32), self.engine.state.H3); + } - self.length_bytes += 1; + fn reset(&mut self) { + self.engine.reset(&H512_TRUNC_256); } - fn input_vec(&mut self, input: &[u8]) { - assert!(!self.finished) + fn output_bits(&self) -> uint { 256 } +} - let mut i = 0; +static H512_TRUNC_256: [u64, ..8] = [ + 0x22312194fc2bf72c, + 0x9f555fa3c84c64c2, + 0x2393b86b6f53b151, + 0x963877195940eabd, + 0x96283ee2a88effe3, + 0xbe5e1e2553863992, + 0x2b0199fc2c85b8aa, + 0x0eb72ddc81c52ca2 +]; - while i < input.len() && self.input_buffer_idx != 0 { - self.input_byte(input[i]); - i += 1; - } - while input.len() - i >= 4 { - let w = to_u32(input.slice(i, i + 4)); - self.process_word(w); - self.length_bytes += 4; - i += 4; - } +struct Sha512Trunc224 { + priv engine: Engine512 +} - while i < input.len() { - self.input_byte(input[i]); - i += 1; +impl Sha512Trunc224 { + /** + * Construct an new instance of a SHA-512/224 digest. + */ + pub fn new() -> Sha512Trunc224 { + Sha512Trunc224 { + engine: Engine512::new(&H512_TRUNC_224) } + } +} +impl Digest for Sha512Trunc224 { + fn input(&mut self, d: &[u8]) { + self.engine.input(d); } - fn reset(&mut self) { - self.length_bytes = 0; - self.finished = false; - self.input_buffer_idx = 0; - self.W_idx = 0; + fn result(&mut self, out: &mut [u8]) { + self.engine.finish(); + + write_u64_be(out.mut_slice(0, 8), self.engine.state.H0); + write_u64_be(out.mut_slice(8, 16), self.engine.state.H1); + write_u64_be(out.mut_slice(16, 24), self.engine.state.H2); + write_u32_be(out.mut_slice(24, 28), (self.engine.state.H3 >> 32) as u32); } - fn process_word(&mut self, input: u32) { - self.W[self.W_idx] = input; - self.W_idx += 1; - if (self.W_idx == 16) { - self.W_idx = 0; - self.process_block(); - } + fn reset(&mut self) { + self.engine.reset(&H512_TRUNC_224); } - fn process_block(&mut self) { + fn output_bits(&self) -> uint { 224 } +} + +static H512_TRUNC_224: [u64, ..8] = [ + 0x8c3d37c819544da2, + 0x73e1996689dcd4d6, + 0x1dfab7ae32ff9c82, + 0x679dd514582f9fcf, + 0x0f6d2b697bd44da8, + 0x77e36f7304c48942, + 0x3f9d85a86a1d36c8, + 0x1112e6ad91d692a1, +]; + + +// A structure that represents that state of a digest computation for the SHA-2 512 family of digest +// functions +struct Engine256State { + H0: u32, + H1: u32, + H2: u32, + H3: u32, + H4: u32, + H5: u32, + H6: u32, + H7: u32, +} + +impl Engine256State { + fn new(h: &[u32, ..8]) -> Engine256State { + return Engine256State { + H0: h[0], + H1: h[1], + H2: h[2], + H3: h[3], + H4: h[4], + H5: h[5], + H6: h[6], + H7: h[7] + }; + } + + fn reset(&mut self, h: &[u32, ..8]) { + self.H0 = h[0]; + self.H1 = h[1]; + self.H2 = h[2]; + self.H3 = h[3]; + self.H4 = h[4]; + self.H5 = h[5]; + self.H6 = h[6]; + self.H7 = h[7]; + } + + fn process_block(&mut self, data: &[u8]) { fn ch(x: u32, y: u32, z: u32) -> u32 { ((x & y) ^ ((!x) & z)) } @@ -449,11 +492,6 @@ impl Engine256 { ((x >> 17) | (x << 15)) ^ ((x >> 19) | (x << 13)) ^ (x >> 10) } - foreach t in range(16u, 64) { - self.W[t] = sigma1(self.W[t - 2]) + self.W[t - 7] + sigma0(self.W[t - 15]) + - self.W[t - 16]; - } - let mut a = self.H0; let mut b = self.H1; let mut c = self.H2; @@ -463,47 +501,41 @@ impl Engine256 { let mut g = self.H6; let mut h = self.H7; - let mut t = 0; - foreach _ in range(0u, 8) { - h += sum1(e) + ch(e, f, g) + K32[t] + self.W[t]; - d += h; - h += sum0(a) + maj(a, b, c); - t += 1; - - g += sum1(d) + ch(d, e, f) + K32[t] + self.W[t]; - c += g; - g += sum0(h) + maj(h, a, b); - t += 1; - - f += sum1(c) + ch(c, d, e) + K32[t] + self.W[t]; - b += f; - f += sum0(g) + maj(g, h, a); - t += 1; - - e += sum1(b) + ch(b, c, d) + K32[t] + self.W[t]; - a += e; - e += sum0(f) + maj(f, g, h); - t += 1; - - d += sum1(a) + ch(a, b, c) + K32[t] + self.W[t]; - h += d; - d += sum0(e) + maj(e, f, g); - t += 1; - - c += sum1(h) + ch(h, a, b) + K32[t] + self.W[t]; - g += c; - c += sum0(d) + maj(d, e, f); - t += 1; - - b += sum1(g) + ch(g, h, a) + K32[t] + self.W[t]; - f += b; - b += sum0(c) + maj(c, d, e); - t += 1; - - a += sum1(f) + ch(f, g, h) + K32[t] + self.W[t]; - e += a; - a += sum0(b) + maj(b, c, d); - t += 1; + let mut W = [0u32, ..64]; + + read_u32v_be(W.mut_slice(0, 16), data); + + // Putting the message schedule inside the same loop as the round calculations allows for + // the compiler to generate better code. + for uint::range_step(0, 48, 8) |t| { + schedule_round!(t + 16); + schedule_round!(t + 17); + schedule_round!(t + 18); + schedule_round!(t + 19); + schedule_round!(t + 20); + schedule_round!(t + 21); + schedule_round!(t + 22); + schedule_round!(t + 23); + + sha2_round!(a, b, c, d, e, f, g, h, K32, t); + sha2_round!(h, a, b, c, d, e, f, g, K32, t + 1); + sha2_round!(g, h, a, b, c, d, e, f, K32, t + 2); + sha2_round!(f, g, h, a, b, c, d, e, K32, t + 3); + sha2_round!(e, f, g, h, a, b, c, d, K32, t + 4); + sha2_round!(d, e, f, g, h, a, b, c, K32, t + 5); + sha2_round!(c, d, e, f, g, h, a, b, K32, t + 6); + sha2_round!(b, c, d, e, f, g, h, a, K32, t + 7); + } + + for uint::range_step(48, 64, 8) |t| { + sha2_round!(a, b, c, d, e, f, g, h, K32, t); + sha2_round!(h, a, b, c, d, e, f, g, K32, t + 1); + sha2_round!(g, h, a, b, c, d, e, f, K32, t + 2); + sha2_round!(f, g, h, a, b, c, d, e, K32, t + 3); + sha2_round!(e, f, g, h, a, b, c, d, K32, t + 4); + sha2_round!(d, e, f, g, h, a, b, c, K32, t + 5); + sha2_round!(c, d, e, f, g, h, a, b, K32, t + 6); + sha2_round!(b, c, d, e, f, g, h, a, K32, t + 7); } self.H0 += a; @@ -515,418 +547,182 @@ impl Engine256 { self.H6 += g; self.H7 += h; } +} - fn finish(&mut self) { - if (self.finished) { - return; - } - - // must get length before adding padding - let bit_length = self.length_bytes << 3; - - // add padding - self.input_byte(128u8); +static K32: [u32, ..64] = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +]; - while self.input_buffer_idx != 0 { - self.input_byte(0u8); - } - // add length - if (self.W_idx > 14) { - foreach _ in range(self.W_idx, 16) { - self.process_word(0); - } - } +// A structure that keeps track of the state of the Sha-256 operation and contains the logic +// necessary to perform the final calculations. +struct Engine256 { + length_bits: u64, + buffer: FixedBuffer64, + state: Engine256State, + finished: bool, +} - while self.W_idx < 14 { - self.process_word(0); +impl Engine256 { + fn new(h: &[u32, ..8]) -> Engine256 { + return Engine256 { + length_bits: 0, + buffer: FixedBuffer64::new(), + state: Engine256State::new(h), + finished: false } - - self.process_word((bit_length >> 32) as u32); - self.process_word(bit_length as u32); - - self.finished = true; } - fn result_256(&mut self, out: &mut [u8]) { - self.finish(); - - from_u32(self.H0, out.mut_slice(0, 4)); - from_u32(self.H1, out.mut_slice(4, 8)); - from_u32(self.H2, out.mut_slice(8, 12)); - from_u32(self.H3, out.mut_slice(12, 16)); - from_u32(self.H4, out.mut_slice(16, 20)); - from_u32(self.H5, out.mut_slice(20, 24)); - from_u32(self.H6, out.mut_slice(24, 28)); - from_u32(self.H7, out.mut_slice(28, 32)); + fn reset(&mut self, h: &[u32, ..8]) { + self.length_bits = 0; + self.buffer.reset(); + self.state.reset(h); + self.finished = false; } - fn result_224(&mut self, out: &mut [u8]) { - self.finish(); - - from_u32(self.H0, out.mut_slice(0, 4)); - from_u32(self.H1, out.mut_slice(4, 8)); - from_u32(self.H2, out.mut_slice(8, 12)); - from_u32(self.H3, out.mut_slice(12, 16)); - from_u32(self.H4, out.mut_slice(16, 20)); - from_u32(self.H5, out.mut_slice(20, 24)); - from_u32(self.H6, out.mut_slice(24, 28)); + fn input(&mut self, input: &[u8]) { + assert!(!self.finished) + // Assumes that input.len() can be converted to u64 without overflow + self.length_bits = shift_add_check_overflow(self.length_bits, input.len() as u64, 3); + self.buffer.input(input, |input: &[u8]| { self.state.process_block(input) }); } -} - -static K32: [u32, ..64] = [ - 0x428a2f98u32, 0x71374491u32, 0xb5c0fbcfu32, 0xe9b5dba5u32, - 0x3956c25bu32, 0x59f111f1u32, 0x923f82a4u32, 0xab1c5ed5u32, - 0xd807aa98u32, 0x12835b01u32, 0x243185beu32, 0x550c7dc3u32, - 0x72be5d74u32, 0x80deb1feu32, 0x9bdc06a7u32, 0xc19bf174u32, - 0xe49b69c1u32, 0xefbe4786u32, 0x0fc19dc6u32, 0x240ca1ccu32, - 0x2de92c6fu32, 0x4a7484aau32, 0x5cb0a9dcu32, 0x76f988dau32, - 0x983e5152u32, 0xa831c66du32, 0xb00327c8u32, 0xbf597fc7u32, - 0xc6e00bf3u32, 0xd5a79147u32, 0x06ca6351u32, 0x14292967u32, - 0x27b70a85u32, 0x2e1b2138u32, 0x4d2c6dfcu32, 0x53380d13u32, - 0x650a7354u32, 0x766a0abbu32, 0x81c2c92eu32, 0x92722c85u32, - 0xa2bfe8a1u32, 0xa81a664bu32, 0xc24b8b70u32, 0xc76c51a3u32, - 0xd192e819u32, 0xd6990624u32, 0xf40e3585u32, 0x106aa070u32, - 0x19a4c116u32, 0x1e376c08u32, 0x2748774cu32, 0x34b0bcb5u32, - 0x391c0cb3u32, 0x4ed8aa4au32, 0x5b9cca4fu32, 0x682e6ff3u32, - 0x748f82eeu32, 0x78a5636fu32, 0x84c87814u32, 0x8cc70208u32, - 0x90befffau32, 0xa4506cebu32, 0xbef9a3f7u32, 0xc67178f2u32 -]; -struct Sha512 { - priv engine: Engine512 -} + fn finish(&mut self) { + if self.finished { + return; + } -struct Sha384 { - priv engine: Engine512 -} + self.buffer.standard_padding(8, |input: &[u8]| { self.state.process_block(input) }); + write_u32_be(self.buffer.next(4), (self.length_bits >> 32) as u32 ); + write_u32_be(self.buffer.next(4), self.length_bits as u32); + self.state.process_block(self.buffer.full_buffer()); -struct Sha512Trunc256 { - priv engine: Engine512 + self.finished = true; + } } -struct Sha512Trunc224 { - priv engine: Engine512 -} struct Sha256 { priv engine: Engine256 } -struct Sha224 { - priv engine: Engine256 -} - -impl Sha512 { - /** - * Construct an new instance of a SHA-512 digest. - */ - pub fn new() -> Sha512 { - Sha512 { - engine: Engine512 { - input_buffer: [0u8, ..8], - input_buffer_idx: 0, - bit_counter: BitCounter { high_bit_count: 0, low_byte_count: 0 }, - H0: 0x6a09e667f3bcc908u64, - H1: 0xbb67ae8584caa73bu64, - H2: 0x3c6ef372fe94f82bu64, - H3: 0xa54ff53a5f1d36f1u64, - H4: 0x510e527fade682d1u64, - H5: 0x9b05688c2b3e6c1fu64, - H6: 0x1f83d9abfb41bd6bu64, - H7: 0x5be0cd19137e2179u64, - W: [0u64, ..80], - W_idx: 0, - finished: false, - } - } - } -} - -impl Sha384 { - /** - * Construct an new instance of a SHA-384 digest. - */ - pub fn new() -> Sha384 { - Sha384 { - engine: Engine512 { - input_buffer: [0u8, ..8], - input_buffer_idx: 0, - bit_counter: BitCounter { high_bit_count: 0, low_byte_count: 0 }, - H0: 0xcbbb9d5dc1059ed8u64, - H1: 0x629a292a367cd507u64, - H2: 0x9159015a3070dd17u64, - H3: 0x152fecd8f70e5939u64, - H4: 0x67332667ffc00b31u64, - H5: 0x8eb44a8768581511u64, - H6: 0xdb0c2e0d64f98fa7u64, - H7: 0x47b5481dbefa4fa4u64, - W: [0u64, ..80], - W_idx: 0, - finished: false, - } - } - } -} - -impl Sha512Trunc256 { - /** - * Construct an new instance of a SHA-512/256 digest. - */ - pub fn new() -> Sha512Trunc256 { - Sha512Trunc256 { - engine: Engine512 { - input_buffer: [0u8, ..8], - input_buffer_idx: 0, - bit_counter: BitCounter { high_bit_count: 0, low_byte_count: 0 }, - H0: 0x22312194fc2bf72cu64, - H1: 0x9f555fa3c84c64c2u64, - H2: 0x2393b86b6f53b151u64, - H3: 0x963877195940eabdu64, - H4: 0x96283ee2a88effe3u64, - H5: 0xbe5e1e2553863992u64, - H6: 0x2b0199fc2c85b8aau64, - H7: 0x0eb72ddc81c52ca2u64, - W: [0u64, ..80], - W_idx: 0, - finished: false, - } - } - } -} - -impl Sha512Trunc224 { - /** - * Construct an new instance of a SHA-512/224 digest. - */ - pub fn new() -> Sha512Trunc224 { - Sha512Trunc224 { - engine: Engine512 { - input_buffer: [0u8, ..8], - input_buffer_idx: 0, - bit_counter: BitCounter { high_bit_count: 0, low_byte_count: 0 }, - H0: 0x8c3d37c819544da2u64, - H1: 0x73e1996689dcd4d6u64, - H2: 0x1dfab7ae32ff9c82u64, - H3: 0x679dd514582f9fcfu64, - H4: 0x0f6d2b697bd44da8u64, - H5: 0x77e36f7304c48942u64, - H6: 0x3f9d85a86a1d36c8u64, - H7: 0x1112e6ad91d692a1u64, - W: [0u64, ..80], - W_idx: 0, - finished: false, - } - } - } -} - impl Sha256 { /** * Construct an new instance of a SHA-256 digest. */ pub fn new() -> Sha256 { Sha256 { - engine: Engine256 { - input_buffer: [0u8, ..4], - input_buffer_idx: 0, - length_bytes: 0, - H0: 0x6a09e667u32, - H1: 0xbb67ae85u32, - H2: 0x3c6ef372u32, - H3: 0xa54ff53au32, - H4: 0x510e527fu32, - H5: 0x9b05688cu32, - H6: 0x1f83d9abu32, - H7: 0x5be0cd19u32, - W: [0u32, ..64], - W_idx: 0, - finished: false, - } - } - } -} - -impl Sha224 { - /** - * Construct an new instance of a SHA-224 digest. - */ - pub fn new() -> Sha224 { - Sha224 { - engine: Engine256 { - input_buffer: [0u8, ..4], - input_buffer_idx: 0, - length_bytes: 0, - H0: 0xc1059ed8u32, - H1: 0x367cd507u32, - H2: 0x3070dd17u32, - H3: 0xf70e5939u32, - H4: 0xffc00b31u32, - H5: 0x68581511u32, - H6: 0x64f98fa7u32, - H7: 0xbefa4fa4u32, - W: [0u32, ..64], - W_idx: 0, - finished: false, - } + engine: Engine256::new(&H256) } } } -impl Digest for Sha512 { - fn input(&mut self, d: &[u8]) { - self.engine.input_vec(d); - } - - fn result(&mut self, out: &mut [u8]) { - self.engine.result_512(out) - } - - fn reset(&mut self) { - self.engine.reset(); - - self.engine.H0 = 0x6a09e667f3bcc908u64; - self.engine.H1 = 0xbb67ae8584caa73bu64; - self.engine.H2 = 0x3c6ef372fe94f82bu64; - self.engine.H3 = 0xa54ff53a5f1d36f1u64; - self.engine.H4 = 0x510e527fade682d1u64; - self.engine.H5 = 0x9b05688c2b3e6c1fu64; - self.engine.H6 = 0x1f83d9abfb41bd6bu64; - self.engine.H7 = 0x5be0cd19137e2179u64; - } - - fn output_bits(&self) -> uint { 512 } -} - -impl Digest for Sha384 { +impl Digest for Sha256 { fn input(&mut self, d: &[u8]) { - self.engine.input_vec(d); + self.engine.input(d); } fn result(&mut self, out: &mut [u8]) { - self.engine.result_384(out) - } - - fn reset(&mut self) { - self.engine.reset(); - - self.engine.H0 = 0xcbbb9d5dc1059ed8u64; - self.engine.H1 = 0x629a292a367cd507u64; - self.engine.H2 = 0x9159015a3070dd17u64; - self.engine.H3 = 0x152fecd8f70e5939u64; - self.engine.H4 = 0x67332667ffc00b31u64; - self.engine.H5 = 0x8eb44a8768581511u64; - self.engine.H6 = 0xdb0c2e0d64f98fa7u64; - self.engine.H7 = 0x47b5481dbefa4fa4u64; - } - - fn output_bits(&self) -> uint { 384 } -} + self.engine.finish(); -impl Digest for Sha512Trunc256 { - fn input(&mut self, d: &[u8]) { - self.engine.input_vec(d); - } - - fn result(&mut self, out: &mut [u8]) { - self.engine.result_256(out) + write_u32_be(out.mut_slice(0, 4), self.engine.state.H0); + write_u32_be(out.mut_slice(4, 8), self.engine.state.H1); + write_u32_be(out.mut_slice(8, 12), self.engine.state.H2); + write_u32_be(out.mut_slice(12, 16), self.engine.state.H3); + write_u32_be(out.mut_slice(16, 20), self.engine.state.H4); + write_u32_be(out.mut_slice(20, 24), self.engine.state.H5); + write_u32_be(out.mut_slice(24, 28), self.engine.state.H6); + write_u32_be(out.mut_slice(28, 32), self.engine.state.H7); } fn reset(&mut self) { - self.engine.reset(); - - self.engine.H0 = 0x22312194fc2bf72cu64; - self.engine.H1 = 0x9f555fa3c84c64c2u64; - self.engine.H2 = 0x2393b86b6f53b151u64; - self.engine.H3 = 0x963877195940eabdu64; - self.engine.H4 = 0x96283ee2a88effe3u64; - self.engine.H5 = 0xbe5e1e2553863992u64; - self.engine.H6 = 0x2b0199fc2c85b8aau64; - self.engine.H7 = 0x0eb72ddc81c52ca2u64; + self.engine.reset(&H256); } fn output_bits(&self) -> uint { 256 } } -impl Digest for Sha512Trunc224 { - fn input(&mut self, d: &[u8]) { - self.engine.input_vec(d); - } - - fn result(&mut self, out: &mut [u8]) { - self.engine.result_224(out) - } +static H256: [u32, ..8] = [ + 0x6a09e667, + 0xbb67ae85, + 0x3c6ef372, + 0xa54ff53a, + 0x510e527f, + 0x9b05688c, + 0x1f83d9ab, + 0x5be0cd19 +]; - fn reset(&mut self) { - self.engine.reset(); - - self.engine.H0 = 0x8c3d37c819544da2u64; - self.engine.H1 = 0x73e1996689dcd4d6u64; - self.engine.H2 = 0x1dfab7ae32ff9c82u64; - self.engine.H3 = 0x679dd514582f9fcfu64; - self.engine.H4 = 0x0f6d2b697bd44da8u64; - self.engine.H5 = 0x77e36f7304c48942u64; - self.engine.H6 = 0x3f9d85a86a1d36c8u64; - self.engine.H7 = 0x1112e6ad91d692a1u64; - } - fn output_bits(&self) -> uint { 224 } +struct Sha224 { + priv engine: Engine256 } -impl Digest for Sha256 { - fn input(&mut self, d: &[u8]) { - self.engine.input_vec(d); - } - - fn result(&mut self, out: &mut [u8]) { - self.engine.result_256(out) - } - - fn reset(&mut self) { - self.engine.reset(); - - self.engine.H0 = 0x6a09e667u32; - self.engine.H1 = 0xbb67ae85u32; - self.engine.H2 = 0x3c6ef372u32; - self.engine.H3 = 0xa54ff53au32; - self.engine.H4 = 0x510e527fu32; - self.engine.H5 = 0x9b05688cu32; - self.engine.H6 = 0x1f83d9abu32; - self.engine.H7 = 0x5be0cd19u32; +impl Sha224 { + /** + * Construct an new instance of a SHA-224 digest. + */ + pub fn new() -> Sha224 { + Sha224 { + engine: Engine256::new(&H224) + } } - - fn output_bits(&self) -> uint { 256 } } impl Digest for Sha224 { fn input(&mut self, d: &[u8]) { - self.engine.input_vec(d); + self.engine.input(d); } fn result(&mut self, out: &mut [u8]) { - self.engine.result_224(out) + self.engine.finish(); + write_u32_be(out.mut_slice(0, 4), self.engine.state.H0); + write_u32_be(out.mut_slice(4, 8), self.engine.state.H1); + write_u32_be(out.mut_slice(8, 12), self.engine.state.H2); + write_u32_be(out.mut_slice(12, 16), self.engine.state.H3); + write_u32_be(out.mut_slice(16, 20), self.engine.state.H4); + write_u32_be(out.mut_slice(20, 24), self.engine.state.H5); + write_u32_be(out.mut_slice(24, 28), self.engine.state.H6); } fn reset(&mut self) { - self.engine.reset(); - - self.engine.H0 = 0xc1059ed8u32; - self.engine.H1 = 0x367cd507u32; - self.engine.H2 = 0x3070dd17u32; - self.engine.H3 = 0xf70e5939u32; - self.engine.H4 = 0xffc00b31u32; - self.engine.H5 = 0x68581511u32; - self.engine.H6 = 0x64f98fa7u32; - self.engine.H7 = 0xbefa4fa4u32; + self.engine.reset(&H224); } fn output_bits(&self) -> uint { 224 } } +static H224: [u32, ..8] = [ + 0xc1059ed8, + 0x367cd507, + 0x3070dd17, + 0xf70e5939, + 0xffc00b31, + 0x68581511, + 0x64f98fa7, + 0xbefa4fa4 +]; + #[cfg(test)] mod tests { - use digest::{Digest, DigestUtil}; + use cryptoutil::test::test_digest_1million_random; + use digest::Digest; use sha2::{Sha512, Sha384, Sha512Trunc256, Sha512Trunc224, Sha256, Sha224}; struct Test { @@ -1117,6 +913,25 @@ mod tests { test_hash(sh, tests); } + + #[test] + fn test_1million_random_sha512() { + let mut sh = Sha512::new(); + test_digest_1million_random( + &mut sh, + 128, + "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb" + + "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"); + } + + #[test] + fn test_1million_random_sha256() { + let mut sh = Sha256::new(); + test_digest_1million_random( + &mut sh, + 64, + "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); + } } diff --git a/src/libextra/extra.rs b/src/libextra/extra.rs index 80dee18f89b86..e6134bb340cd8 100644 --- a/src/libextra/extra.rs +++ b/src/libextra/extra.rs @@ -67,6 +67,8 @@ pub mod dlist; pub mod treemap; // Crypto +#[path="crypto/cryptoutil.rs"] +mod cryptoutil; #[path="crypto/digest.rs"] pub mod digest; #[path="crypto/sha1.rs"] diff --git a/src/libextra/workcache.rs b/src/libextra/workcache.rs index 4cfe77273541a..d28407a984a01 100644 --- a/src/libextra/workcache.rs +++ b/src/libextra/workcache.rs @@ -10,8 +10,7 @@ #[allow(missing_doc)]; - -use digest::DigestUtil; +use digest::Digest; use json; use sha1::Sha1; use serialize::{Encoder, Encodable, Decoder, Decodable};