diff options
| author | Brian Anderson <[email protected]> | 2011-02-21 00:56:09 -0500 |
|---|---|---|
| committer | Brian Anderson <[email protected]> | 2011-02-21 01:00:43 -0500 |
| commit | cbe8d222a82c14485cf416c4b85fd5e58363c0e0 (patch) | |
| tree | 2242c55ef08cab1d754b2ebe02c6f86bd33ccc88 /src/lib | |
| parent | Implement expr_put. Un-XFAIL foreach-simple.rs; iters w/o env access work. (diff) | |
| download | rust-cbe8d222a82c14485cf416c4b85fd5e58363c0e0.tar.xz rust-cbe8d222a82c14485cf416c4b85fd5e58363c0e0.zip | |
Implement sha-1 in standard library. Closes #228
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/sha1.rs | 284 | ||||
| -rw-r--r-- | src/lib/std.rc | 1 |
2 files changed, 285 insertions, 0 deletions
diff --git a/src/lib/sha1.rs b/src/lib/sha1.rs new file mode 100644 index 00000000..8be969aa --- /dev/null +++ b/src/lib/sha1.rs @@ -0,0 +1,284 @@ +/* + * 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. + */ + +import std._vec; +import std._str; + +export sha1; +export mk_sha1; + +state type sha1 = state obj { + // Provide message input as bytes + fn input(&vec[u8]); + + // Provide message input as string + fn input_str(&str); + + // Read the digest as a vector of 20 bytes. After + // calling this no further input may provided + // until reset is called + fn result() -> vec[u8]; + + // Reset the sha1 state for reuse. This is called + // automatically during construction + fn reset(); +}; + +// Some unexported constants +const uint digest_buf_len = 5; +const uint msg_block_len = 64; + +// Builds a sha1 object +fn mk_sha1() -> sha1 { + + state type sha1state = rec(vec[mutable u32] h, + mutable u32 len_low, + mutable u32 len_high, + vec[mutable u8] msg_block, + mutable uint msg_block_idx, + mutable bool computed); + + impure fn add_input(&sha1state st, &vec[u8] msg) { + // FIXME: Should be typestate precondition + check (!st.computed); + + for (u8 element in msg) { + 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 + fail; + } + } + + if (st.msg_block_idx == msg_block_len) { + process_msg_block(st); + } + } + } + + impure fn process_msg_block(&sha1state st) { + + // FIXME: Make precondition + check (_vec.len[mutable u32](st.h) == digest_buf_len); + + // Constants + auto k = vec(0x5A827999u32, + 0x6ED9EBA1u32, + 0x8F1BBCDCu32, + 0xCA62C1D6u32); + + let int t; // Loop counter + let vec[mutable u32] w = _vec.init_elt[mutable u32](0u32, 80u); + + // Initialize the first 16 words of the vector w + t = 0; + while (t < 16) { + w.(t) = (st.msg_block.(t * 4) as u32) << 24u32; + w.(t) = w.(t) | ((st.msg_block.(t * 4 + 1) as u32) << 16u32); + w.(t) = w.(t) | ((st.msg_block.(t * 4 + 2) as u32) << 8u32); + w.(t) = w.(t) | (st.msg_block.(t * 4 + 3) as u32); + t += 1; + } + + // Initialize the rest of vector w + while (t < 80) { + auto val = w.(t-3) ^ w.(t-8) ^ w.(t-14) ^ w.(t-16); + w.(t) = circular_shift(1u32, val); + t += 1; + } + + auto a = st.h.(0); + auto b = st.h.(1); + auto c = st.h.(2); + auto d = st.h.(3); + auto e = st.h.(4); + + let u32 temp; + + t = 0; + while (t < 20) { + temp = circular_shift(5u32, a) + + ((b & c) | ((~b) & d)) + e + w.(t) + k.(0); + 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) + k.(1); + 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) + k.(2); + 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) + k.(3); + 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(u32 bits, u32 word) -> u32 { + // FIXME: This is a workaround for a rustboot + // "unrecognized quads" codegen bug + auto bits_hack = bits; + ret (word << bits_hack) | (word >> (32u32 - bits)); + } + + impure fn mk_result(&sha1state st) -> vec[u8] { + if (!st.computed) { + pad_msg(st); + st.computed = true; + } + + let vec[u8] res = vec(); + for (u32 hpart in st.h) { + res += (hpart >> 24u32) & 0xFFu32 as u8; + res += (hpart >> 16u32) & 0xFFu32 as u8; + res += (hpart >> 8u32) & 0xFFu32 as u8; + res += hpart & 0xFFu32 as u8; + } + ret res; + } + + /* + * 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 message_block array accordingly. It will also + * call ProcessMessageBlock() appropriately. When it returns, it + * can be assumed that the message digest has been computed. + */ + impure fn pad_msg(&sha1state st) { + // FIXME: Should be a precondition + check (_vec.len[mutable u8](st.msg_block) == 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); + } + + state obj sha1(sha1state st) { + + fn reset() { + // FIXME: Should be typestate precondition + check (_vec.len[mutable u32](st.h) == digest_buf_len); + + st.len_low = 0u32; + st.len_high = 0u32; + st.msg_block_idx = 0u; + + st.h.(0) = 0x67452301u32; + st.h.(1) = 0xEFCDAB89u32; + st.h.(2) = 0x98BADCFEu32; + st.h.(3) = 0x10325476u32; + st.h.(4) = 0xC3D2E1F0u32; + + st.computed = false; + } + + fn input(&vec[u8] msg) { + add_input(st, msg); + } + + fn input_str(&str msg) { + add_input(st, _str.bytes(msg)); + } + + fn result() -> vec[u8] { + ret mk_result(st); + } + } + + auto st = rec(h = _vec.init_elt[mutable u32](0u32, digest_buf_len), + mutable len_low = 0u32, + mutable len_high = 0u32, + msg_block = _vec.init_elt[mutable u8](0u8, msg_block_len), + mutable msg_block_idx = 0u, + mutable computed = false); + auto sh = sha1(st); + sh.reset(); + ret sh; +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: diff --git a/src/lib/std.rc b/src/lib/std.rc index 102aa4d1..3500c4b3 100644 --- a/src/lib/std.rc +++ b/src/lib/std.rc @@ -57,6 +57,7 @@ mod dbg; mod bitv; mod sort; mod path; +mod sha1; // Local Variables: // mode: rust; |