use cryptoutil::{write_u32_be, read_u32v_be, add_bytes_to_bits, FixedBuffer, FixedBuffer64,
StandardPadding};
use digest::Digest;
const DIGEST_BUF_LEN: usize = 5;
const WORK_BUF_LEN: usize = 80;
const K0: u32 = 0x5A827999u32;
const K1: u32 = 0x6ED9EBA1u32;
const K2: u32 = 0x8F1BBCDCu32;
const K3: u32 = 0xCA62C1D6u32;
#[derive(Copy)]
pub struct Sha1 {
h: [u32; DIGEST_BUF_LEN],
length_bits: u64,
buffer: FixedBuffer64,
computed: bool,
}
fn add_input(st: &mut Sha1, msg: &[u8]) {
assert!((!st.computed));
st.length_bits = add_bytes_to_bits(st.length_bits, msg.len() as u64);
let st_h = &mut st.h;
st.buffer.input(msg, |d: &[u8]| {process_msg_block(d, &mut *st_h); });
}
fn process_msg_block(data: &[u8], h: &mut [u32; DIGEST_BUF_LEN]) {
let mut w = [0u32; WORK_BUF_LEN];
read_u32v_be(&mut w[0..16], data);
let mut 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 = 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 {
temp = circular_shift(5, a) + (b & c | !b & d) + e + w[t] + K0;
e = d;
d = c;
c = circular_shift(30, b);
b = a;
a = temp;
t += 1;
}
while t < 40 {
temp = circular_shift(5, a) + (b ^ c ^ d) + e + w[t] + K1;
e = d;
d = c;
c = circular_shift(30, b);
b = a;
a = temp;
t += 1;
}
while t < 60 {
temp =
circular_shift(5, a) + (b & c | b & d | c & d) + e + w[t] +
K2;
e = d;
d = c;
c = circular_shift(30, b);
b = a;
a = temp;
t += 1;
}
while t < 80 {
temp = circular_shift(5, a) + (b ^ c ^ d) + e + w[t] + K3;
e = d;
d = c;
c = circular_shift(30, b);
b = a;
a = temp;
t += 1;
}
h[0] += a;
h[1] += b;
h[2] += c;
h[3] += d;
h[4] += e;
}
fn circular_shift(bits: u32, word: u32) -> u32 {
word << bits as usize | word >> (32u32 - bits) as usize
}
fn mk_result(st: &mut Sha1, rs: &mut [u8]) {
if !st.computed {
let st_h = &mut st.h;
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(), st_h);
st.computed = true;
}
write_u32_be(&mut rs[0..4], st.h[0]);
write_u32_be(&mut rs[4..8], st.h[1]);
write_u32_be(&mut rs[8..12], st.h[2]);
write_u32_be(&mut rs[12..16], st.h[3]);
write_u32_be(&mut rs[16..20], st.h[4]);
}
impl Sha1 {
pub fn new() -> Sha1 {
let mut st = Sha1 {
h: [0u32; DIGEST_BUF_LEN],
length_bits: 0u64,
buffer: FixedBuffer64::new(),
computed: false,
};
st.reset();
st
}
}
impl Digest for Sha1 {
fn reset(&mut self) {
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;
}
fn input(&mut self, msg: &[u8]) { add_input(self, msg); }
fn result(&mut self, out: &mut [u8]) { mk_result(self, out) }
fn output_bits(&self) -> usize { 160 }
fn block_size(&self) -> usize { 64 }
}
#[cfg(test)]
mod tests {
use cryptoutil::test::test_digest_1million_random;
use digest::Digest;
use sha1::Sha1;
#[derive(Clone)]
struct Test {
input: &'static str,
output: Vec<u8>,
output_str: &'static str,
}
#[test]
fn test() {
let tests = vec![
Test {
input: "abc",
output: vec![
0xA9u8, 0x99u8, 0x3Eu8, 0x36u8,
0x47u8, 0x06u8, 0x81u8, 0x6Au8,
0xBAu8, 0x3Eu8, 0x25u8, 0x71u8,
0x78u8, 0x50u8, 0xC2u8, 0x6Cu8,
0x9Cu8, 0xD0u8, 0xD8u8, 0x9Du8,
],
output_str: "a9993e364706816aba3e25717850c26c9cd0d89d"
},
Test {
input:
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
output: vec![
0x84u8, 0x98u8, 0x3Eu8, 0x44u8,
0x1Cu8, 0x3Bu8, 0xD2u8, 0x6Eu8,
0xBAu8, 0xAEu8, 0x4Au8, 0xA1u8,
0xF9u8, 0x51u8, 0x29u8, 0xE5u8,
0xE5u8, 0x46u8, 0x70u8, 0xF1u8,
],
output_str: "84983e441c3bd26ebaae4aa1f95129e5e54670f1"
},
Test {
input: "The quick brown fox jumps over the lazy dog",
output: vec![
0x2fu8, 0xd4u8, 0xe1u8, 0xc6u8,
0x7au8, 0x2du8, 0x28u8, 0xfcu8,
0xedu8, 0x84u8, 0x9eu8, 0xe1u8,
0xbbu8, 0x76u8, 0xe7u8, 0x39u8,
0x1bu8, 0x93u8, 0xebu8, 0x12u8,
],
output_str: "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12",
},
Test {
input: "The quick brown fox jumps over the lazy cog",
output: vec![
0xdeu8, 0x9fu8, 0x2cu8, 0x7fu8,
0xd2u8, 0x5eu8, 0x1bu8, 0x3au8,
0xfau8, 0xd3u8, 0xe8u8, 0x5au8,
0x0bu8, 0xd1u8, 0x7du8, 0x9bu8,
0x10u8, 0x0du8, 0xb4u8, 0xb3u8,
],
output_str: "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3",
},
];
let mut out = [0u8; 20];
let mut sh = box Sha1::new();
for t in tests.iter() {
(*sh).input_str(t.input);
sh.result(&mut out);
assert!(t.output[] == out[]);
let out_str = (*sh).result_str();
assert_eq!(out_str.len(), 40);
assert!(&out_str[] == t.output_str);
sh.reset();
}
for t in tests.iter() {
let len = t.input.len();
let mut left = len;
while left > 0 {
let take = (left + 1) / 2;
(*sh).input_str(&t.input[len - left..take + len - left]);
left = left - take;
}
sh.result(&mut out);
assert!(t.output[] == out[]);
let out_str = (*sh).result_str();
assert_eq!(out_str.len(), 40);
assert!(&out_str[] == t.output_str);
sh.reset();
}
}
#[test]
fn test_1million_random_sha1() {
let mut sh = Sha1::new();
test_digest_1million_random(
&mut sh,
64,
"34aa973cd4c4daa4f61eeb2bdbad27316534016f");
}
}
#[cfg(test)]
mod bench {
use test::Bencher;
use digest::Digest;
use sha1::Sha1;
#[bench]
pub fn sha1_10(bh: & mut Bencher) {
let mut sh = Sha1::new();
let bytes = [1u8; 10];
bh.iter( || {
sh.input(&bytes);
});
bh.bytes = bytes.len() as u64;
}
#[bench]
pub fn sha1_1k(bh: & mut Bencher) {
let mut sh = Sha1::new();
let bytes = [1u8; 1024];
bh.iter( || {
sh.input(&bytes);
});
bh.bytes = bytes.len() as u64;
}
#[bench]
pub fn sha1_64k(bh: & mut Bencher) {
let mut sh = Sha1::new();
let bytes = [1u8; 65536];
bh.iter( || {
sh.input(&bytes);
});
bh.bytes = bytes.len() as u64;
}
}