Skip to content

Commit cbe8d22

Browse files
committed
Implement sha-1 in standard library. Closes #228
1 parent b0c7439 commit cbe8d22

File tree

3 files changed

+400
-0
lines changed

3 files changed

+400
-0
lines changed

src/lib/sha1.rs

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
/*
2+
* A SHA-1 implementation derived from Paul E. Jones's reference
3+
* implementation, which is written for clarity, not speed. At some
4+
* point this will want to be rewritten.
5+
*/
6+
7+
import std._vec;
8+
import std._str;
9+
10+
export sha1;
11+
export mk_sha1;
12+
13+
state type sha1 = state obj {
14+
// Provide message input as bytes
15+
fn input(&vec[u8]);
16+
17+
// Provide message input as string
18+
fn input_str(&str);
19+
20+
// Read the digest as a vector of 20 bytes. After
21+
// calling this no further input may provided
22+
// until reset is called
23+
fn result() -> vec[u8];
24+
25+
// Reset the sha1 state for reuse. This is called
26+
// automatically during construction
27+
fn reset();
28+
};
29+
30+
// Some unexported constants
31+
const uint digest_buf_len = 5;
32+
const uint msg_block_len = 64;
33+
34+
// Builds a sha1 object
35+
fn mk_sha1() -> sha1 {
36+
37+
state type sha1state = rec(vec[mutable u32] h,
38+
mutable u32 len_low,
39+
mutable u32 len_high,
40+
vec[mutable u8] msg_block,
41+
mutable uint msg_block_idx,
42+
mutable bool computed);
43+
44+
impure fn add_input(&sha1state st, &vec[u8] msg) {
45+
// FIXME: Should be typestate precondition
46+
check (!st.computed);
47+
48+
for (u8 element in msg) {
49+
st.msg_block.(st.msg_block_idx) = element;
50+
st.msg_block_idx += 1u;
51+
52+
st.len_low += 8u32;
53+
if (st.len_low == 0u32) {
54+
st.len_high += 1u32;
55+
if (st.len_high == 0u32) {
56+
// FIXME: Need better failure mode
57+
fail;
58+
}
59+
}
60+
61+
if (st.msg_block_idx == msg_block_len) {
62+
process_msg_block(st);
63+
}
64+
}
65+
}
66+
67+
impure fn process_msg_block(&sha1state st) {
68+
69+
// FIXME: Make precondition
70+
check (_vec.len[mutable u32](st.h) == digest_buf_len);
71+
72+
// Constants
73+
auto k = vec(0x5A827999u32,
74+
0x6ED9EBA1u32,
75+
0x8F1BBCDCu32,
76+
0xCA62C1D6u32);
77+
78+
let int t; // Loop counter
79+
let vec[mutable u32] w = _vec.init_elt[mutable u32](0u32, 80u);
80+
81+
// Initialize the first 16 words of the vector w
82+
t = 0;
83+
while (t < 16) {
84+
w.(t) = (st.msg_block.(t * 4) as u32) << 24u32;
85+
w.(t) = w.(t) | ((st.msg_block.(t * 4 + 1) as u32) << 16u32);
86+
w.(t) = w.(t) | ((st.msg_block.(t * 4 + 2) as u32) << 8u32);
87+
w.(t) = w.(t) | (st.msg_block.(t * 4 + 3) as u32);
88+
t += 1;
89+
}
90+
91+
// Initialize the rest of vector w
92+
while (t < 80) {
93+
auto val = w.(t-3) ^ w.(t-8) ^ w.(t-14) ^ w.(t-16);
94+
w.(t) = circular_shift(1u32, val);
95+
t += 1;
96+
}
97+
98+
auto a = st.h.(0);
99+
auto b = st.h.(1);
100+
auto c = st.h.(2);
101+
auto d = st.h.(3);
102+
auto e = st.h.(4);
103+
104+
let u32 temp;
105+
106+
t = 0;
107+
while (t < 20) {
108+
temp = circular_shift(5u32, a)
109+
+ ((b & c) | ((~b) & d)) + e + w.(t) + k.(0);
110+
e = d;
111+
d = c;
112+
c = circular_shift(30u32, b);
113+
b = a;
114+
a = temp;
115+
t += 1;
116+
}
117+
118+
while (t < 40) {
119+
temp = circular_shift(5u32, a)
120+
+ (b ^ c ^ d) + e + w.(t) + k.(1);
121+
e = d;
122+
d = c;
123+
c = circular_shift(30u32, b);
124+
b = a;
125+
a = temp;
126+
t += 1;
127+
}
128+
129+
while (t < 60) {
130+
temp = circular_shift(5u32, a)
131+
+ ((b & c) | (b & d) | (c & d)) + e + w.(t) + k.(2);
132+
e = d;
133+
d = c;
134+
c = circular_shift(30u32, b);
135+
b = a;
136+
a = temp;
137+
t += 1;
138+
}
139+
140+
while (t < 80) {
141+
temp = circular_shift(5u32, a)
142+
+ (b ^ c ^ d) + e + w.(t) + k.(3);
143+
e = d;
144+
d = c;
145+
c = circular_shift(30u32, b);
146+
b = a;
147+
a = temp;
148+
t += 1;
149+
}
150+
151+
st.h.(0) = st.h.(0) + a;
152+
st.h.(1) = st.h.(1) + b;
153+
st.h.(2) = st.h.(2) + c;
154+
st.h.(3) = st.h.(3) + d;
155+
st.h.(4) = st.h.(4) + e;
156+
157+
st.msg_block_idx = 0u;
158+
}
159+
160+
fn circular_shift(u32 bits, u32 word) -> u32 {
161+
// FIXME: This is a workaround for a rustboot
162+
// "unrecognized quads" codegen bug
163+
auto bits_hack = bits;
164+
ret (word << bits_hack) | (word >> (32u32 - bits));
165+
}
166+
167+
impure fn mk_result(&sha1state st) -> vec[u8] {
168+
if (!st.computed) {
169+
pad_msg(st);
170+
st.computed = true;
171+
}
172+
173+
let vec[u8] res = vec();
174+
for (u32 hpart in st.h) {
175+
res += (hpart >> 24u32) & 0xFFu32 as u8;
176+
res += (hpart >> 16u32) & 0xFFu32 as u8;
177+
res += (hpart >> 8u32) & 0xFFu32 as u8;
178+
res += hpart & 0xFFu32 as u8;
179+
}
180+
ret res;
181+
}
182+
183+
/*
184+
* According to the standard, the message must be padded to an even
185+
* 512 bits. The first padding bit must be a '1'. The last 64 bits
186+
* represent the length of the original message. All bits in between
187+
* should be 0. This function will pad the message according to those
188+
* rules by filling the message_block array accordingly. It will also
189+
* call ProcessMessageBlock() appropriately. When it returns, it
190+
* can be assumed that the message digest has been computed.
191+
*/
192+
impure fn pad_msg(&sha1state st) {
193+
// FIXME: Should be a precondition
194+
check (_vec.len[mutable u8](st.msg_block) == msg_block_len);
195+
196+
/*
197+
* Check to see if the current message block is too small to hold
198+
* the initial padding bits and length. If so, we will pad the
199+
* block, process it, and then continue padding into a second block.
200+
*/
201+
if (st.msg_block_idx > 55u) {
202+
st.msg_block.(st.msg_block_idx) = 0x80u8;
203+
st.msg_block_idx += 1u;
204+
205+
while (st.msg_block_idx < msg_block_len) {
206+
st.msg_block.(st.msg_block_idx) = 0u8;
207+
st.msg_block_idx += 1u;
208+
}
209+
210+
process_msg_block(st);
211+
} else {
212+
st.msg_block.(st.msg_block_idx) = 0x80u8;
213+
st.msg_block_idx += 1u;
214+
}
215+
216+
while (st.msg_block_idx < 56u) {
217+
st.msg_block.(st.msg_block_idx) = 0u8;
218+
st.msg_block_idx += 1u;
219+
}
220+
221+
// Store the message length as the last 8 octets
222+
st.msg_block.(56) = (st.len_high >> 24u32) & 0xFFu32 as u8;
223+
st.msg_block.(57) = (st.len_high >> 16u32) & 0xFFu32 as u8;
224+
st.msg_block.(58) = (st.len_high >> 8u32) & 0xFFu32 as u8;
225+
st.msg_block.(59) = st.len_high & 0xFFu32 as u8;
226+
st.msg_block.(60) = (st.len_low >> 24u32) & 0xFFu32 as u8;
227+
st.msg_block.(61) = (st.len_low >> 16u32) & 0xFFu32 as u8;
228+
st.msg_block.(62) = (st.len_low >> 8u32) & 0xFFu32 as u8;
229+
st.msg_block.(63) = st.len_low & 0xFFu32 as u8;
230+
231+
process_msg_block(st);
232+
}
233+
234+
state obj sha1(sha1state st) {
235+
236+
fn reset() {
237+
// FIXME: Should be typestate precondition
238+
check (_vec.len[mutable u32](st.h) == digest_buf_len);
239+
240+
st.len_low = 0u32;
241+
st.len_high = 0u32;
242+
st.msg_block_idx = 0u;
243+
244+
st.h.(0) = 0x67452301u32;
245+
st.h.(1) = 0xEFCDAB89u32;
246+
st.h.(2) = 0x98BADCFEu32;
247+
st.h.(3) = 0x10325476u32;
248+
st.h.(4) = 0xC3D2E1F0u32;
249+
250+
st.computed = false;
251+
}
252+
253+
fn input(&vec[u8] msg) {
254+
add_input(st, msg);
255+
}
256+
257+
fn input_str(&str msg) {
258+
add_input(st, _str.bytes(msg));
259+
}
260+
261+
fn result() -> vec[u8] {
262+
ret mk_result(st);
263+
}
264+
}
265+
266+
auto st = rec(h = _vec.init_elt[mutable u32](0u32, digest_buf_len),
267+
mutable len_low = 0u32,
268+
mutable len_high = 0u32,
269+
msg_block = _vec.init_elt[mutable u8](0u8, msg_block_len),
270+
mutable msg_block_idx = 0u,
271+
mutable computed = false);
272+
auto sh = sha1(st);
273+
sh.reset();
274+
ret sh;
275+
}
276+
277+
// Local Variables:
278+
// mode: rust;
279+
// fill-column: 78;
280+
// indent-tabs-mode: nil
281+
// c-basic-offset: 4
282+
// buffer-file-coding-system: utf-8-unix
283+
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
284+
// End:

src/lib/std.rc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ mod dbg;
5757
mod bitv;
5858
mod sort;
5959
mod path;
60+
mod sha1;
6061

6162
// Local Variables:
6263
// mode: rust;

0 commit comments

Comments
 (0)