summaryrefslogtreecommitdiff
path: root/src/crypto/hash.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto/hash.rs')
-rw-r--r--src/crypto/hash.rs187
1 files changed, 187 insertions, 0 deletions
diff --git a/src/crypto/hash.rs b/src/crypto/hash.rs
new file mode 100644
index 00000000..1a1b6e8a
--- /dev/null
+++ b/src/crypto/hash.rs
@@ -0,0 +1,187 @@
+use libc;
+use libc::c_uint;
+use std::ptr;
+
+pub enum HashType {
+ MD5,
+ SHA1,
+ SHA224,
+ SHA256,
+ SHA384,
+ SHA512
+}
+
+#[allow(dead_code)]
+#[allow(non_camel_case_types)]
+pub struct EVP_MD_CTX {
+ digest: *mut EVP_MD,
+ engine: *mut libc::c_void,
+ flags: libc::c_ulong,
+ md_data: *mut libc::c_void,
+ pctx: *mut EVP_PKEY_CTX,
+ update: *mut libc::c_void
+}
+
+#[allow(non_camel_case_types)]
+pub struct EVP_MD;
+
+#[allow(non_camel_case_types)]
+pub struct EVP_PKEY_CTX;
+
+#[link(name = "crypto")]
+extern {
+ fn EVP_MD_CTX_create() -> *mut EVP_MD_CTX;
+ fn EVP_MD_CTX_destroy(ctx: *mut EVP_MD_CTX);
+
+ fn EVP_md5() -> *const EVP_MD;
+ fn EVP_sha1() -> *const EVP_MD;
+ fn EVP_sha224() -> *const EVP_MD;
+ fn EVP_sha256() -> *const EVP_MD;
+ fn EVP_sha384() -> *const EVP_MD;
+ fn EVP_sha512() -> *const EVP_MD;
+
+ fn EVP_DigestInit(ctx: *mut EVP_MD_CTX, typ: *const EVP_MD);
+ fn EVP_DigestUpdate(ctx: *mut EVP_MD_CTX, data: *const u8, n: c_uint);
+ fn EVP_DigestFinal(ctx: *mut EVP_MD_CTX, res: *mut u8, n: *mut u32);
+}
+
+pub fn evpmd(t: HashType) -> (*const EVP_MD, uint) {
+ unsafe {
+ match t {
+ MD5 => (EVP_md5(), 16u),
+ SHA1 => (EVP_sha1(), 20u),
+ SHA224 => (EVP_sha224(), 28u),
+ SHA256 => (EVP_sha256(), 32u),
+ SHA384 => (EVP_sha384(), 48u),
+ SHA512 => (EVP_sha512(), 64u),
+ }
+ }
+}
+
+#[allow(dead_code)]
+pub struct Hasher {
+ evp: *const EVP_MD,
+ ctx: *mut EVP_MD_CTX,
+ len: uint,
+}
+
+impl Hasher {
+ pub fn new(ht: HashType) -> Hasher {
+ let ctx = unsafe { EVP_MD_CTX_create() };
+ let (evp, mdlen) = evpmd(ht);
+ unsafe {
+ EVP_DigestInit(ctx, evp);
+ }
+
+ Hasher { evp: evp, ctx: ctx, len: mdlen }
+ }
+
+ /// Update this hasher with more input bytes
+ pub fn update(&self, data: &[u8]) {
+ unsafe {
+ EVP_DigestUpdate(self.ctx, data.as_ptr(), data.len() as c_uint)
+ }
+ }
+
+ /**
+ * Return the digest of all bytes added to this hasher since its last
+ * initialization
+ */
+ pub fn final(&self) -> Vec<u8> {
+ unsafe {
+ let mut res = Vec::from_elem(self.len, 0u8);
+ EVP_DigestFinal(self.ctx, res.as_mut_ptr(), ptr::mut_null());
+ res
+ }
+ }
+}
+
+impl Drop for Hasher {
+ fn drop(&mut self) {
+ unsafe {
+ EVP_MD_CTX_destroy(self.ctx);
+ }
+ }
+}
+
+/**
+ * Hashes the supplied input data using hash t, returning the resulting hash
+ * value
+ */
+pub fn hash(t: HashType, data: &[u8]) -> Vec<u8> {
+ let h = Hasher::new(t);
+ h.update(data);
+ h.final()
+}
+
+#[cfg(test)]
+mod tests {
+ use serialize::hex::{FromHex, ToHex};
+
+ struct HashTest {
+ input: Vec<u8>,
+ expected_output: String
+ }
+
+ fn HashTest(input: &str, output: &str) -> HashTest {
+ HashTest { input: input.from_hex().unwrap(),
+ expected_output: output.to_string() }
+ }
+
+ fn hash_test(hashtype: super::HashType, hashtest: &HashTest) {
+ let calced_raw = super::hash(hashtype, hashtest.input.as_slice());
+
+ let calced = calced_raw.as_slice().to_hex().into_string();
+
+ if calced != hashtest.expected_output {
+ println!("Test failed - {} != {}", calced, hashtest.expected_output);
+ }
+
+ assert!(calced == hashtest.expected_output);
+ }
+
+ // Test vectors from http://www.nsrl.nist.gov/testdata/
+ #[test]
+ fn test_md5() {
+ let tests = [
+ HashTest("", "d41d8cd98f00b204e9800998ecf8427e"),
+ HashTest("7F", "83acb6e67e50e31db6ed341dd2de1595"),
+ HashTest("EC9C", "0b07f0d4ca797d8ac58874f887cb0b68"),
+ HashTest("FEE57A", "e0d583171eb06d56198fc0ef22173907"),
+ HashTest("42F497E0", "7c430f178aefdf1487fee7144e9641e2"),
+ HashTest("C53B777F1C", "75ef141d64cb37ec423da2d9d440c925"),
+ HashTest("89D5B576327B", "ebbaf15eb0ed784c6faa9dc32831bf33"),
+ HashTest("5D4CCE781EB190", "ce175c4b08172019f05e6b5279889f2c"),
+ HashTest("81901FE94932D7B9", "cd4d2f62b8cdb3a0cf968a735a239281"),
+ HashTest("C9FFDEE7788EFB4EC9", "e0841a231ab698db30c6c0f3f246c014"),
+ HashTest("66AC4B7EBA95E53DC10B", "a3b3cea71910d9af56742aa0bb2fe329"),
+ HashTest("A510CD18F7A56852EB0319", "577e216843dd11573574d3fb209b97d8"),
+ HashTest("AAED18DBE8938C19ED734A8D", "6f80fb775f27e0a4ce5c2f42fc72c5f1")];
+
+ for test in tests.iter() {
+ hash_test(super::MD5, test);
+ }
+ }
+
+ #[test]
+ fn test_sha1() {
+ let tests = [
+ HashTest("616263", "a9993e364706816aba3e25717850c26c9cd0d89d"),
+ ];
+
+ for test in tests.iter() {
+ hash_test(super::SHA1, test);
+ }
+ }
+
+ #[test]
+ fn test_sha256() {
+ let tests = [
+ HashTest("616263", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
+ ];
+
+ for test in tests.iter() {
+ hash_test(super::SHA256, test);
+ }
+ }
+}