aboutsummaryrefslogtreecommitdiff
path: root/hash.rs
blob: 6e0af07acda6b7cd266f616b3b48dac341f104ef (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use libc::c_uint;

pub enum HashType {
    MD5,
    SHA1,
    SHA224,
    SHA256,
    SHA384,
    SHA512
}

#[allow(non_camel_case_types)]
type EVP_MD_CTX = *libc::c_void;

#[allow(non_camel_case_types)]
type EVP_MD = *libc::c_void;

#[link_name = "crypto"]
#[abi = "cdecl"]
extern mod libcrypto {
    fn EVP_MD_CTX_create() -> EVP_MD_CTX;

    fn EVP_md5() -> EVP_MD;
    fn EVP_sha1() -> EVP_MD;
    fn EVP_sha224() -> EVP_MD;
    fn EVP_sha256() -> EVP_MD;
    fn EVP_sha384() -> EVP_MD;
    fn EVP_sha512() -> EVP_MD;

    fn EVP_DigestInit(ctx: EVP_MD_CTX, typ: EVP_MD);
    fn EVP_DigestUpdate(ctx: EVP_MD_CTX, data: *u8, n: c_uint);
    fn EVP_DigestFinal(ctx: EVP_MD_CTX, res: *mut u8, n: *u32);
}

fn evpmd(t: HashType) -> (EVP_MD, uint) {
    match t {
        MD5 => (libcrypto::EVP_md5(), 16u),
        SHA1 => (libcrypto::EVP_sha1(), 20u),
        SHA224 => (libcrypto::EVP_sha224(), 28u),
        SHA256 => (libcrypto::EVP_sha256(), 32u),
        SHA384 => (libcrypto::EVP_sha384(), 48u),
        SHA512 => (libcrypto::EVP_sha512(), 64u),
    }
}

pub struct Hasher {
    priv evp: EVP_MD,
    priv ctx: EVP_MD_CTX,
    priv len: uint,
}

pub fn Hasher(ht: HashType) -> Hasher {
    let ctx = libcrypto::EVP_MD_CTX_create();
    let (evp, mdlen) = evpmd(ht);
    let h = Hasher { evp: evp, ctx: ctx, len: mdlen };
    h.init();
    h
}

pub impl Hasher {
    /// Initializes this hasher
    fn init() unsafe {
        libcrypto::EVP_DigestInit(self.ctx, self.evp);
    }

    /// Update this hasher with more input bytes
    fn update(data: &[u8]) unsafe {
        do vec::as_imm_buf(data) |pdata, len| {
            libcrypto::EVP_DigestUpdate(self.ctx, pdata, len as c_uint)
        }
    }

    /**
     * Return the digest of all bytes added to this hasher since its last
     * initialization
     */
    fn final() -> ~[u8] unsafe {
        let mut res = vec::from_elem(self.len, 0u8);
        do vec::as_mut_buf(res) |pres, _len| {
            libcrypto::EVP_DigestFinal(self.ctx, pres, ptr::null());
        }
        res
    }
}

/**
 * Hashes the supplied input data using hash t, returning the resulting hash
 * value
 */
pub fn hash(t: HashType, data: &[u8]) -> ~[u8] unsafe {
    let h = Hasher(t);
    h.update(data);
    h.final()
}

#[cfg(test)]
mod tests {
    // Test vectors from http://www.nsrl.nist.gov/testdata/
    #[test]
    fn test_md5() {
        let s0 = ~[0x61u8, 0x62u8, 0x63u8];
        let d0 = 
           ~[0x90u8, 0x01u8, 0x50u8, 0x98u8, 0x3cu8, 0xd2u8, 0x4fu8, 0xb0u8,
             0xd6u8, 0x96u8, 0x3fu8, 0x7du8, 0x28u8, 0xe1u8, 0x7fu8, 0x72u8];
        assert(hash(MD5, s0) == d0);
    }

    #[test]
    fn test_sha1() {
        let s0 = ~[0x61u8, 0x62u8, 0x63u8];
        let d0 =
           ~[0xa9u8, 0x99u8, 0x3eu8, 0x36u8, 0x47u8, 0x06u8, 0x81u8, 0x6au8,
             0xbau8, 0x3eu8, 0x25u8, 0x71u8, 0x78u8, 0x50u8, 0xc2u8, 0x6cu8,
             0x9cu8, 0xd0u8, 0xd8u8, 0x9du8];
        assert(hash(SHA1, s0) == d0);
    }

    #[test]
    fn test_sha256() {
        let s0 = ~[0x61u8, 0x62u8, 0x63u8];
        let d0 =
           ~[0xbau8, 0x78u8, 0x16u8, 0xbfu8, 0x8fu8, 0x01u8, 0xcfu8, 0xeau8,
             0x41u8, 0x41u8, 0x40u8, 0xdeu8, 0x5du8, 0xaeu8, 0x22u8, 0x23u8,
             0xb0u8, 0x03u8, 0x61u8, 0xa3u8, 0x96u8, 0x17u8, 0x7au8, 0x9cu8,
             0xb4u8, 0x10u8, 0xffu8, 0x61u8, 0xf2u8, 0x00u8, 0x15u8, 0xadu8];
        assert(hash(SHA256, s0) == d0);
    }
}