about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-08-02 19:29:00 -0700
committerbors <bors@rust-lang.org>2013-08-02 19:29:00 -0700
commitefd6eafeb4f734ef7883afadb4e50099430c76f2 (patch)
treee62b6513fa316f611de2bf6f7d6efdb85825b7be /src
parent3ddc72f69be4d0a2027ff598ad262ea2b2ca3812 (diff)
parent1252472bb76da5aa082db00086cc27c0bbe9d723 (diff)
downloadrust-efd6eafeb4f734ef7883afadb4e50099430c76f2.tar.gz
rust-efd6eafeb4f734ef7883afadb4e50099430c76f2.zip
auto merge of #8174 : DaGenix/rust/digest-improvements, r=brson
Same content as #8097, but bors had an issue with that pull request. Opening a new one.
Diffstat (limited to 'src')
-rw-r--r--src/libextra/crypto/cryptoutil.rs327
-rw-r--r--src/libextra/crypto/digest.rs46
-rw-r--r--src/libextra/crypto/sha1.rs171
-rw-r--r--src/libextra/crypto/sha2.rs1213
-rw-r--r--src/libextra/extra.rs2
-rw-r--r--src/libextra/workcache.rs3
6 files changed, 912 insertions, 850 deletions
diff --git a/src/libextra/crypto/cryptoutil.rs b/src/libextra/crypto/cryptoutil.rs
new file mode 100644
index 00000000000..43e3b5c89af
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T: Int + Unsigned>(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<T: Int + Unsigned + Clone>(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
+        <T: Int + Unsigned + Clone>
+        (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 <T: FixedBuffer> 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<D: Digest>(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 0efd88fdf50..217573a4135 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<D: Digest> 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 7ede1978495..86a89d79f1a 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 d713e192972..d92a4be43c3 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 80dee18f89b..e6134bb340c 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 4cfe7727354..d28407a984a 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};