auto merge of #7207 : DaGenix/rust/sha2, r=pcwalton
This pull request contains an unoptimized implementation of most of the SHA-2 functions (everything but the variable output size versions). I also created a common trait (Digest) for all of the interesting methods for working with digests and updated the existing SHA-1 code to use it. Finally, while working with the SHA-1 code, I got rid of the use of @ types and type objects. I've tested all functions against the Wikipedia test vectors. Additionally, I tested the SHA-512, 384, and 256 variants against the Java implementations of those digests. I did my best to try to follow Rust conventions, but, there are so many different conventions in the code base right now that I'm not sure if I'm following the correct one or not. Anyway, I'm happy to rework if I didn't get the coding convention right (or if there are bugs!).
This commit is contained in:
commit
832fe32721
@ -78,6 +78,7 @@ Version 0.7 (July 2013)
|
||||
* extra: `BigInt`, `BigUint` implement numeric and comparison traits.
|
||||
* extra: `term` uses terminfo now, is more correct.
|
||||
* extra: `arc` functions converted to methods.
|
||||
* extra: Implementation of fixed output size variations of SHA-2.
|
||||
|
||||
* Tooling
|
||||
* `unused_unsafe` lint mode for detecting unnecessary `unsafe` blocks.
|
||||
|
88
src/libextra/crypto/digest.rs
Normal file
88
src/libextra/crypto/digest.rs
Normal file
@ -0,0 +1,88 @@
|
||||
// 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 core::prelude::*;
|
||||
|
||||
use core::uint;
|
||||
use core::vec;
|
||||
|
||||
/**
|
||||
* The Digest trait specifies an interface common to digest functions, such as SHA-1 and the SHA-2
|
||||
* family of digest functions.
|
||||
*/
|
||||
pub trait Digest {
|
||||
/**
|
||||
* Provide message data.
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * input - A vector of message data
|
||||
*/
|
||||
fn input(&mut self, input: &[u8]);
|
||||
|
||||
/**
|
||||
* Retrieve the digest result. This method may be called multiple times.
|
||||
*/
|
||||
fn result(&mut self, out: &mut [u8]);
|
||||
|
||||
/**
|
||||
* Reset the digest. This method must be called after result() and before supplying more
|
||||
* data.
|
||||
*/
|
||||
fn reset(&mut self);
|
||||
|
||||
/**
|
||||
* Get the output size in bits.
|
||||
*/
|
||||
fn output_bits(&self) -> uint;
|
||||
}
|
||||
|
||||
fn to_hex(rr: &[u8]) -> ~str {
|
||||
let mut s = ~"";
|
||||
for rr.iter().advance() |b| {
|
||||
let hex = uint::to_str_radix(*b as uint, 16u);
|
||||
if hex.len() == 1 {
|
||||
s += "0";
|
||||
}
|
||||
s += 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
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * in The string to feed into the digest
|
||||
*/
|
||||
fn input_str(&mut self, in: &str);
|
||||
|
||||
/**
|
||||
* 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, in: &str) {
|
||||
self.input(in.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);
|
||||
}
|
||||
}
|
370
src/libextra/crypto/sha1.rs
Normal file
370
src/libextra/crypto/sha1.rs
Normal file
@ -0,0 +1,370 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
/*!
|
||||
* An implementation of the SHA-1 cryptographic hash.
|
||||
*
|
||||
* First create a `sha1` object using the `sha1` constructor, then
|
||||
* feed it input using the `input` or `input_str` methods, which may be
|
||||
* called any number of times.
|
||||
*
|
||||
* After the entire input has been fed to the hash read the result using
|
||||
* the `result` or `result_str` methods.
|
||||
*
|
||||
* The `sha1` object may be reused to create multiple hashes by calling
|
||||
* the `reset` method.
|
||||
*/
|
||||
|
||||
use core::prelude::*;
|
||||
|
||||
use digest::Digest;
|
||||
|
||||
/*
|
||||
* A SHA-1 implementation derived from Paul E. Jones's reference
|
||||
* implementation, which is written for clarity, not speed. At some
|
||||
* point this will want to be rewritten.
|
||||
*/
|
||||
|
||||
// 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;
|
||||
static K2: u32 = 0x8F1BBCDCu32;
|
||||
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 computed: bool,
|
||||
priv work_buf: [u32, ..WORK_BUF_LEN]
|
||||
}
|
||||
|
||||
fn add_input(st: &mut Sha1, msg: &[u8]) {
|
||||
assert!((!st.computed));
|
||||
for msg.iter().advance |element| {
|
||||
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); }
|
||||
}
|
||||
}
|
||||
|
||||
fn process_msg_block(st: &mut Sha1) {
|
||||
let mut t: int; // Loop counter
|
||||
let mut w = st.work_buf;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Initialize the rest of vector w
|
||||
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 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
fn circular_shift(bits: u32, word: u32) -> u32 {
|
||||
return word << bits | word >> 32u32 - bits;
|
||||
}
|
||||
|
||||
fn mk_result(st: &mut Sha1, rs: &mut [u8]) {
|
||||
if !st.computed { pad_msg(st); st.computed = true; }
|
||||
let mut i = 0;
|
||||
for st.h.mut_iter().advance |ptr_hpart| {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
impl Sha1 {
|
||||
/// Construct a `sha` object
|
||||
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,
|
||||
computed: false,
|
||||
work_buf: [0u32, ..WORK_BUF_LEN]
|
||||
};
|
||||
st.reset();
|
||||
return st;
|
||||
}
|
||||
}
|
||||
|
||||
impl Digest for Sha1 {
|
||||
pub fn reset(&mut self) {
|
||||
self.len_low = 0;
|
||||
self.len_high = 0;
|
||||
self.msg_block_idx = 0;
|
||||
self.h[0] = 0x67452301u32;
|
||||
self.h[1] = 0xEFCDAB89u32;
|
||||
self.h[2] = 0x98BADCFEu32;
|
||||
self.h[3] = 0x10325476u32;
|
||||
self.h[4] = 0xC3D2E1F0u32;
|
||||
self.computed = false;
|
||||
}
|
||||
pub fn input(&mut self, msg: &[u8]) { add_input(self, msg); }
|
||||
pub fn result(&mut self, out: &mut [u8]) { return mk_result(self, out); }
|
||||
pub fn output_bits(&self) -> uint { 160 }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::vec;
|
||||
|
||||
use digest::{Digest, DigestUtil};
|
||||
use sha1::Sha1;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
struct Test {
|
||||
input: ~str,
|
||||
output: ~[u8],
|
||||
output_str: ~str,
|
||||
}
|
||||
|
||||
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 = ~[
|
||||
Test {
|
||||
input: ~"abc",
|
||||
output: ~[
|
||||
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:
|
||||
~"abcdbcdecdefdefgefghfghighij" +
|
||||
"hijkijkljklmklmnlmnomnopnopq",
|
||||
output: ~[
|
||||
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: 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
|
||||
|
||||
let wikipedia_tests = ~[
|
||||
Test {
|
||||
input: ~"The quick brown fox jumps over the lazy dog",
|
||||
output: ~[
|
||||
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: ~[
|
||||
0xdeu8, 0x9fu8, 0x2cu8, 0x7fu8,
|
||||
0xd2u8, 0x5eu8, 0x1bu8, 0x3au8,
|
||||
0xfau8, 0xd3u8, 0xe8u8, 0x5au8,
|
||||
0x0bu8, 0xd1u8, 0x7du8, 0x9bu8,
|
||||
0x10u8, 0x0du8, 0xb4u8, 0xb3u8,
|
||||
],
|
||||
output_str: ~"de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3",
|
||||
},
|
||||
];
|
||||
let tests = fips_180_1_tests + wikipedia_tests;
|
||||
|
||||
// Test that it works when accepting the message all at once
|
||||
|
||||
let mut out = [0u8, ..20];
|
||||
|
||||
let mut sh = ~Sha1::new();
|
||||
for tests.iter().advance |t| {
|
||||
(*sh).input_str(t.input);
|
||||
sh.result(out);
|
||||
assert!(vec::eq(t.output, out));
|
||||
|
||||
let out_str = (*sh).result_str();
|
||||
assert_eq!(out_str.len(), 40);
|
||||
assert!(out_str == t.output_str);
|
||||
|
||||
sh.reset();
|
||||
}
|
||||
|
||||
|
||||
// Test that it works when accepting the message in pieces
|
||||
for tests.iter().advance |t| {
|
||||
let len = t.input.len();
|
||||
let mut left = len;
|
||||
while left > 0u {
|
||||
let take = (left + 1u) / 2u;
|
||||
(*sh).input_str(t.input.slice(len - left, take + len - left));
|
||||
left = left - take;
|
||||
}
|
||||
sh.result(out);
|
||||
assert!(vec::eq(t.output, out));
|
||||
|
||||
let out_str = (*sh).result_str();
|
||||
assert_eq!(out_str.len(), 40);
|
||||
assert!(out_str == t.output_str);
|
||||
|
||||
sh.reset();
|
||||
}
|
||||
}
|
||||
}
|
1125
src/libextra/crypto/sha2.rs
Normal file
1125
src/libextra/crypto/sha2.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,410 +0,0 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
/*!
|
||||
* An implementation of the SHA-1 cryptographic hash.
|
||||
*
|
||||
* First create a `sha1` object using the `sha1` constructor, then
|
||||
* feed it input using the `input` or `input_str` methods, which may be
|
||||
* called any number of times.
|
||||
*
|
||||
* After the entire input has been fed to the hash read the result using
|
||||
* the `result` or `result_str` methods.
|
||||
*
|
||||
* The `sha1` object may be reused to create multiple hashes by calling
|
||||
* the `reset` method.
|
||||
*/
|
||||
|
||||
use core::prelude::*;
|
||||
|
||||
use core::uint;
|
||||
use core::vec;
|
||||
|
||||
/*
|
||||
* A SHA-1 implementation derived from Paul E. Jones's reference
|
||||
* implementation, which is written for clarity, not speed. At some
|
||||
* point this will want to be rewritten.
|
||||
*/
|
||||
|
||||
/// The SHA-1 interface
|
||||
trait Sha1 {
|
||||
/// Provide message input as bytes
|
||||
fn input(&mut self, &[u8]);
|
||||
/// Provide message input as string
|
||||
fn input_str(&mut self, &str);
|
||||
/**
|
||||
* Read the digest as a vector of 20 bytes. After calling this no further
|
||||
* input may be provided until reset is called.
|
||||
*/
|
||||
fn result(&mut self) -> ~[u8];
|
||||
/**
|
||||
* Read the digest as a hex string. After calling this no further
|
||||
* input may be provided until reset is called.
|
||||
*/
|
||||
fn result_str(&mut self) -> ~str;
|
||||
/// Reset the SHA-1 state for reuse
|
||||
fn reset(&mut self);
|
||||
}
|
||||
|
||||
// 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;
|
||||
static k2: u32 = 0x8F1BBCDCu32;
|
||||
static k3: u32 = 0xCA62C1D6u32;
|
||||
|
||||
|
||||
/// Construct a `sha` object
|
||||
pub fn sha1() -> @Sha1 {
|
||||
struct Sha1State
|
||||
{ h: ~[u32],
|
||||
len_low: u32,
|
||||
len_high: u32,
|
||||
msg_block: ~[u8],
|
||||
msg_block_idx: uint,
|
||||
computed: bool,
|
||||
work_buf: @mut ~[u32]};
|
||||
|
||||
fn add_input(st: &mut Sha1State, msg: &[u8]) {
|
||||
assert!((!st.computed));
|
||||
for msg.iter().advance |element| {
|
||||
st.msg_block[st.msg_block_idx] = *element;
|
||||
st.msg_block_idx += 1u;
|
||||
st.len_low += 8u32;
|
||||
if st.len_low == 0u32 {
|
||||
st.len_high += 1u32;
|
||||
if st.len_high == 0u32 {
|
||||
// FIXME: Need better failure mode (#2346)
|
||||
fail!();
|
||||
}
|
||||
}
|
||||
if st.msg_block_idx == msg_block_len { process_msg_block(st); }
|
||||
}
|
||||
}
|
||||
fn process_msg_block(st: &mut Sha1State) {
|
||||
assert_eq!(st.h.len(), digest_buf_len);
|
||||
assert_eq!(st.work_buf.len(), work_buf_len);
|
||||
let mut t: int; // Loop counter
|
||||
let w = st.work_buf;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Initialize the rest of vector w
|
||||
while t < 80 {
|
||||
let val = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
|
||||
w[t] = circular_shift(1u32, 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 temp: u32;
|
||||
t = 0;
|
||||
while t < 20 {
|
||||
temp = circular_shift(5u32, a) + (b & c | !b & d) + e + w[t] + k0;
|
||||
e = d;
|
||||
d = c;
|
||||
c = circular_shift(30u32, b);
|
||||
b = a;
|
||||
a = temp;
|
||||
t += 1;
|
||||
}
|
||||
while t < 40 {
|
||||
temp = circular_shift(5u32, a) + (b ^ c ^ d) + e + w[t] + k1;
|
||||
e = d;
|
||||
d = c;
|
||||
c = circular_shift(30u32, b);
|
||||
b = a;
|
||||
a = temp;
|
||||
t += 1;
|
||||
}
|
||||
while t < 60 {
|
||||
temp =
|
||||
circular_shift(5u32, a) + (b & c | b & d | c & d) + e + w[t] +
|
||||
k2;
|
||||
e = d;
|
||||
d = c;
|
||||
c = circular_shift(30u32, b);
|
||||
b = a;
|
||||
a = temp;
|
||||
t += 1;
|
||||
}
|
||||
while t < 80 {
|
||||
temp = circular_shift(5u32, a) + (b ^ c ^ d) + e + w[t] + k3;
|
||||
e = d;
|
||||
d = c;
|
||||
c = circular_shift(30u32, b);
|
||||
b = a;
|
||||
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 = 0u;
|
||||
}
|
||||
fn circular_shift(bits: u32, word: u32) -> u32 {
|
||||
return word << bits | word >> 32u32 - bits;
|
||||
}
|
||||
fn mk_result(st: &mut Sha1State) -> ~[u8] {
|
||||
if !(*st).computed { pad_msg(st); (*st).computed = true; }
|
||||
let mut rs: ~[u8] = ~[];
|
||||
for st.h.mut_iter().advance |ptr_hpart| {
|
||||
let hpart = *ptr_hpart;
|
||||
let a = (hpart >> 24u32 & 0xFFu32) as u8;
|
||||
let b = (hpart >> 16u32 & 0xFFu32) as u8;
|
||||
let c = (hpart >> 8u32 & 0xFFu32) as u8;
|
||||
let d = (hpart & 0xFFu32) as u8;
|
||||
rs = vec::append(copy rs, [a, b, c, d]);
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 Sha1State) {
|
||||
assert_eq!((*st).msg_block.len(), msg_block_len);
|
||||
|
||||
/*
|
||||
* 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 > 55u {
|
||||
(*st).msg_block[(*st).msg_block_idx] = 0x80u8;
|
||||
(*st).msg_block_idx += 1u;
|
||||
while (*st).msg_block_idx < msg_block_len {
|
||||
(*st).msg_block[(*st).msg_block_idx] = 0u8;
|
||||
(*st).msg_block_idx += 1u;
|
||||
}
|
||||
process_msg_block(st);
|
||||
} else {
|
||||
(*st).msg_block[(*st).msg_block_idx] = 0x80u8;
|
||||
(*st).msg_block_idx += 1u;
|
||||
}
|
||||
while (*st).msg_block_idx < 56u {
|
||||
(*st).msg_block[(*st).msg_block_idx] = 0u8;
|
||||
(*st).msg_block_idx += 1u;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
impl Sha1 for Sha1State {
|
||||
fn reset(&mut self) {
|
||||
assert_eq!(self.h.len(), digest_buf_len);
|
||||
self.len_low = 0u32;
|
||||
self.len_high = 0u32;
|
||||
self.msg_block_idx = 0u;
|
||||
self.h[0] = 0x67452301u32;
|
||||
self.h[1] = 0xEFCDAB89u32;
|
||||
self.h[2] = 0x98BADCFEu32;
|
||||
self.h[3] = 0x10325476u32;
|
||||
self.h[4] = 0xC3D2E1F0u32;
|
||||
self.computed = false;
|
||||
}
|
||||
fn input(&mut self, msg: &[u8]) { add_input(self, msg); }
|
||||
fn input_str(&mut self, msg: &str) {
|
||||
add_input(self, msg.as_bytes());
|
||||
}
|
||||
fn result(&mut self) -> ~[u8] { return mk_result(self); }
|
||||
fn result_str(&mut self) -> ~str {
|
||||
let rr = mk_result(self);
|
||||
let mut s = ~"";
|
||||
for rr.iter().advance |b| {
|
||||
let hex = uint::to_str_radix(*b as uint, 16u);
|
||||
if hex.len() == 1 {
|
||||
s += "0";
|
||||
}
|
||||
s += hex;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
let st = Sha1State {
|
||||
h: vec::from_elem(digest_buf_len, 0u32),
|
||||
len_low: 0u32,
|
||||
len_high: 0u32,
|
||||
msg_block: vec::from_elem(msg_block_len, 0u8),
|
||||
msg_block_idx: 0u,
|
||||
computed: false,
|
||||
work_buf: @mut vec::from_elem(work_buf_len, 0u32)
|
||||
};
|
||||
let mut sh = @st as @Sha1;
|
||||
sh.reset();
|
||||
return sh;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use sha1;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
struct Test {
|
||||
input: ~str,
|
||||
output: ~[u8],
|
||||
output_str: ~str,
|
||||
}
|
||||
|
||||
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 = ~[
|
||||
Test {
|
||||
input: ~"abc",
|
||||
output: ~[
|
||||
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:
|
||||
~"abcdbcdecdefdefgefghfghighij" +
|
||||
"hijkijkljklmklmnlmnomnopnopq",
|
||||
output: ~[
|
||||
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: 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
|
||||
|
||||
let wikipedia_tests = ~[
|
||||
Test {
|
||||
input: ~"The quick brown fox jumps over the lazy dog",
|
||||
output: ~[
|
||||
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: ~[
|
||||
0xdeu8, 0x9fu8, 0x2cu8, 0x7fu8,
|
||||
0xd2u8, 0x5eu8, 0x1bu8, 0x3au8,
|
||||
0xfau8, 0xd3u8, 0xe8u8, 0x5au8,
|
||||
0x0bu8, 0xd1u8, 0x7du8, 0x9bu8,
|
||||
0x10u8, 0x0du8, 0xb4u8, 0xb3u8,
|
||||
],
|
||||
output_str: ~"de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3",
|
||||
},
|
||||
];
|
||||
let tests = fips_180_1_tests + wikipedia_tests;
|
||||
fn check_vec_eq(v0: ~[u8], v1: ~[u8]) {
|
||||
assert_eq!(v0.len(), v1.len());
|
||||
let len = v0.len();
|
||||
let mut i = 0u;
|
||||
while i < len {
|
||||
let a = v0[i];
|
||||
let b = v1[i];
|
||||
assert_eq!(a, b);
|
||||
i += 1u;
|
||||
}
|
||||
}
|
||||
// Test that it works when accepting the message all at once
|
||||
|
||||
let mut sh = sha1::sha1();
|
||||
for tests.iter().advance |t| {
|
||||
sh.input_str(t.input);
|
||||
let out = sh.result();
|
||||
check_vec_eq(copy t.output, out);
|
||||
|
||||
let out_str = sh.result_str();
|
||||
assert_eq!(out_str.len(), 40);
|
||||
assert!(out_str == t.output_str);
|
||||
|
||||
sh.reset();
|
||||
}
|
||||
|
||||
|
||||
// Test that it works when accepting the message in pieces
|
||||
for tests.iter().advance |t| {
|
||||
let len = t.input.len();
|
||||
let mut left = len;
|
||||
while left > 0u {
|
||||
let take = (left + 1u) / 2u;
|
||||
sh.input_str(t.input.slice(len - left, take + len - left));
|
||||
left = left - take;
|
||||
}
|
||||
let out = sh.result();
|
||||
check_vec_eq(copy t.output, out);
|
||||
|
||||
let out_str = sh.result_str();
|
||||
assert_eq!(out_str.len(), 40);
|
||||
assert!(out_str == t.output_str);
|
||||
|
||||
sh.reset();
|
||||
}
|
||||
}
|
||||
}
|
@ -86,13 +86,20 @@ pub mod sort;
|
||||
pub mod dlist;
|
||||
pub mod treemap;
|
||||
|
||||
// Crypto
|
||||
#[path="crypto/digest.rs"]
|
||||
pub mod digest;
|
||||
#[path="crypto/sha1.rs"]
|
||||
pub mod sha1;
|
||||
#[path="crypto/sha2.rs"]
|
||||
pub mod sha2;
|
||||
|
||||
// And ... other stuff
|
||||
|
||||
pub mod ebml;
|
||||
pub mod dbg;
|
||||
pub mod getopts;
|
||||
pub mod json;
|
||||
pub mod sha1;
|
||||
pub mod md4;
|
||||
pub mod tempfile;
|
||||
pub mod term;
|
||||
|
@ -12,8 +12,9 @@
|
||||
|
||||
use core::prelude::*;
|
||||
|
||||
use digest::DigestUtil;
|
||||
use json;
|
||||
use sha1;
|
||||
use sha1::Sha1;
|
||||
use serialize::{Encoder, Encodable, Decoder, Decodable};
|
||||
use sort;
|
||||
|
||||
@ -248,16 +249,16 @@ fn json_decode<T:Decodable<json::Decoder>>(s: &str) -> T {
|
||||
}
|
||||
|
||||
fn digest<T:Encodable<json::Encoder>>(t: &T) -> ~str {
|
||||
let mut sha = sha1::sha1();
|
||||
sha.input_str(json_encode(t));
|
||||
sha.result_str()
|
||||
let mut sha = ~Sha1::new();
|
||||
(*sha).input_str(json_encode(t));
|
||||
(*sha).result_str()
|
||||
}
|
||||
|
||||
fn digest_file(path: &Path) -> ~str {
|
||||
let mut sha = sha1::sha1();
|
||||
let mut sha = ~Sha1::new();
|
||||
let s = io::read_whole_file_str(path);
|
||||
sha.input_str(*s.get_ref());
|
||||
sha.result_str()
|
||||
(*sha).input_str(*s.get_ref());
|
||||
(*sha).result_str()
|
||||
}
|
||||
|
||||
impl Context {
|
||||
|
Loading…
x
Reference in New Issue
Block a user