sha1.rs 8.4 KB
Newer Older
1 2


3 4 5 6 7 8 9 10
/*
 * 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.
 */
export sha1;
export mk_sha1;

11 12 13 14 15 16 17 18
type sha1 =
    obj {

            // Provide message input as bytes
            fn input(&vec[u8]) ;

            // Provide message input as string
            fn input_str(&str) ;
19

20 21 22 23
            // 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] ;
24

25 26
            // Same as above, just a hex-string version.
            fn result_str() -> str ;
27

28 29 30 31
            // Reset the sha1 state for reuse. This is called
            // automatically during construction
            fn reset() ;
    };
32

33 34

// Some unexported constants
35
const uint digest_buf_len = 5u;
36

37
const uint msg_block_len = 64u;
38

39
const uint work_buf_len = 80u;
40

41
const u32 k0 = 0x5A827999u32;
42

43
const u32 k1 = 0x6ED9EBA1u32;
44

45
const u32 k2 = 0x8F1BBCDCu32;
46

47 48
const u32 k3 = 0xCA62C1D6u32;

49

50 51
// Builds a sha1 object
fn mk_sha1() -> sha1 {
52 53 54 55 56 57 58 59
    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,
            vec[mutable u32] work_buf);
60

G
Graydon Hoare 已提交
61
    fn add_input(&sha1state st, &vec[u8] msg) {
62 63
        // FIXME: Should be typestate precondition

64
        assert (!st.computed);
65 66 67 68 69 70 71 72
        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
73

74 75 76
                    fail;
                }
            }
77
            if (st.msg_block_idx == msg_block_len) { process_msg_block(st); }
78 79
        }
    }
G
Graydon Hoare 已提交
80
    fn process_msg_block(&sha1state st) {
81
        // FIXME: Make precondition
82

83 84
        assert (vec::len(st.h) == digest_buf_len);
        assert (vec::len(st.work_buf) == work_buf_len);
85 86
        let int t; // Loop counter

87
        auto w = st.work_buf;
88
        // Initialize the first 16 words of the vector w
89

90 91
        t = 0;
        while (t < 16) {
92 93
            auto tmp;
            tmp = (st.msg_block.(t * 4) as u32) << 24u32;
94 95
            tmp = tmp | (st.msg_block.(t * 4 + 1) as u32) << 16u32;
            tmp = tmp | (st.msg_block.(t * 4 + 2) as u32) << 8u32;
96 97
            tmp = tmp | (st.msg_block.(t * 4 + 3) as u32);
            w.(t) = tmp;
98 99 100
            t += 1;
        }
        // Initialize the rest of vector w
101

102
        while (t < 80) {
103
            auto val = w.(t - 3) ^ w.(t - 8) ^ w.(t - 14) ^ w.(t - 16);
104 105 106 107 108 109 110 111 112 113 114
            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) {
115 116
            temp =
                circular_shift(5u32, a) + (b & c | !b & d) + e + w.(t) + k0;
117 118 119 120 121 122 123 124
            e = d;
            d = c;
            c = circular_shift(30u32, b);
            b = a;
            a = temp;
            t += 1;
        }
        while (t < 40) {
125
            temp = circular_shift(5u32, a) + (b ^ c ^ d) + e + w.(t) + k1;
126 127 128 129 130 131 132 133
            e = d;
            d = c;
            c = circular_shift(30u32, b);
            b = a;
            a = temp;
            t += 1;
        }
        while (t < 60) {
134 135 136
            temp =
                circular_shift(5u32, a) + (b & c | b & d | c & d) + e + w.(t)
                    + k2;
137 138 139 140 141 142 143 144
            e = d;
            d = c;
            c = circular_shift(30u32, b);
            b = a;
            a = temp;
            t += 1;
        }
        while (t < 80) {
145
            temp = circular_shift(5u32, a) + (b ^ c ^ d) + e + w.(t) + k3;
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
            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
163

164
        auto bits_hack = bits;
165
        ret word << bits_hack | word >> 32u32 - bits;
166
    }
G
Graydon Hoare 已提交
167
    fn mk_result(&sha1state st) -> vec[u8] {
168
        if (!st.computed) { pad_msg(st); st.computed = true; }
169
        let vec[u8] res = [];
170
        for (u32 hpart in st.h) {
171 172 173 174 175
            auto a = hpart >> 24u32 & 0xFFu32 as u8;
            auto b = hpart >> 16u32 & 0xFFu32 as u8;
            auto c = hpart >> 8u32 & 0xFFu32 as u8;
            auto d = hpart & 0xFFu32 as u8;
            res += [a, b, c, d];
176 177 178 179 180 181 182 183
        }
        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
184 185
     * rules by filling the msg_block vector accordingly.  It will also
     * call process_msg_block() appropriately.  When it returns, it
186 187
     * can be assumed that the message digest has been computed.
     */
188

G
Graydon Hoare 已提交
189
    fn pad_msg(&sha1state st) {
190 191
        // FIXME: Should be a precondition

192
        assert (vec::len(st.msg_block) == msg_block_len);
193 194 195 196 197
        /*
         * 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.
         */
198

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
        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
216 217 218 219

        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;
220
        st.msg_block.(59) = st.len_high & 0xFFu32 as u8;
221 222 223
        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;
224 225 226
        st.msg_block.(63) = st.len_low & 0xFFu32 as u8;
        process_msg_block(st);
    }
227
    obj sha1(sha1state st) {
228 229 230
        fn reset() {
            // FIXME: Should be typestate precondition

231
            assert (vec::len(st.h) == digest_buf_len);
232 233 234 235 236 237 238 239 240 241
            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;
        }
242 243 244
        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); }
245 246 247
        fn result_str() -> str {
            auto r = mk_result(st);
            auto s = "";
248
            for (u8 b in r) { s += uint::to_str(b as uint, 16u); }
249 250
            ret s;
        }
251
    }
252 253 254 255 256 257 258 259
    auto st =
        rec(h=vec::init_elt_mut[u32](0u32, digest_buf_len),
            mutable len_low=0u32,
            mutable len_high=0u32,
            msg_block=vec::init_elt_mut[u8](0u8, msg_block_len),
            mutable msg_block_idx=0u,
            mutable computed=false,
            work_buf=vec::init_elt_mut[u32](0u32, work_buf_len));
260 261 262 263 264 265 266 267 268 269
    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
270
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
271
// End: