From 5fafe4fc60848d525e3edb37266bc0f762cb7963 Mon Sep 17 00:00:00 2001 From: Gleb Kozyrev Date: Thu, 20 Nov 2014 07:06:59 +0200 Subject: [PATCH] Hasher: static contract checking, context reuse - Store EVP_MD_CTX in a separate struct. - Add with_context() constructor that uses an existing context. - Switch to EVP_Digest(Init|Final)_ex for efficient context reuse. - Make update() borrow &mut self. - Make finalize() consume self. Add finalize_reuse() that also returns the context which can be passed to from_context() constructor for reuse. These changes let the type system prevent illegal calls to update() and finalize(). --- openssl-sys/src/lib.rs | 2 + src/crypto/hash.rs | 85 +++++++++++++++++++++++++++++++----------- 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 50186ae04..2b0c92921 100755 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -340,8 +340,10 @@ extern "C" { pub fn EVP_CipherFinal(ctx: *mut EVP_CIPHER_CTX, res: *mut u8, len: &mut c_int); pub fn EVP_DigestInit(ctx: *mut EVP_MD_CTX, typ: *const EVP_MD); + pub fn EVP_DigestInit_ex(ctx: *mut EVP_MD_CTX, typ: *const EVP_MD, imple: *const ENGINE); pub fn EVP_DigestUpdate(ctx: *mut EVP_MD_CTX, data: *const u8, n: c_uint); pub fn EVP_DigestFinal(ctx: *mut EVP_MD_CTX, res: *mut u8, n: *mut u32); + pub fn EVP_DigestFinal_ex(ctx: *mut EVP_MD_CTX, res: *mut u8, n: *mut u32); pub fn EVP_MD_CTX_create() -> *mut EVP_MD_CTX; pub fn EVP_MD_CTX_destroy(ctx: *mut EVP_MD_CTX); diff --git a/src/crypto/hash.rs b/src/crypto/hash.rs index 1587e55dd..b5d0eab5d 100644 --- a/src/crypto/hash.rs +++ b/src/crypto/hash.rs @@ -28,10 +28,32 @@ pub fn evpmd(t: HashType) -> (*const ffi::EVP_MD, uint) { } } +pub struct HasherContext { + ptr: *mut ffi::EVP_MD_CTX +} + +impl HasherContext { + pub fn new() -> HasherContext { + ffi::init(); + + unsafe { + HasherContext { ptr: ffi::EVP_MD_CTX_create() } + } + } +} + +impl Drop for HasherContext { + fn drop(&mut self) { + unsafe { + ffi::EVP_MD_CTX_destroy(self.ptr); + } + } +} + #[allow(dead_code)] pub struct Hasher { evp: *const ffi::EVP_MD, - ctx: *mut ffi::EVP_MD_CTX, + ctx: HasherContext, len: uint, } @@ -44,21 +66,23 @@ impl io::Writer for Hasher { impl Hasher { pub fn new(ht: HashType) -> Hasher { - ffi::init(); + let ctx = HasherContext::new(); + Hasher::with_context(ctx, ht) + } - let ctx = unsafe { ffi::EVP_MD_CTX_create() }; + pub fn with_context(ctx: HasherContext, ht: HashType) -> Hasher { let (evp, mdlen) = evpmd(ht); unsafe { - ffi::EVP_DigestInit(ctx, evp); + ffi::EVP_DigestInit_ex(ctx.ptr, evp, 0 as *const _); } Hasher { evp: evp, ctx: ctx, len: mdlen } } /// Update this hasher with more input bytes - pub fn update(&self, data: &[u8]) { + pub fn update(&mut self, data: &[u8]) { unsafe { - ffi::EVP_DigestUpdate(self.ctx, data.as_ptr(), data.len() as c_uint) + ffi::EVP_DigestUpdate(self.ctx.ptr, data.as_ptr(), data.len() as c_uint) } } @@ -66,20 +90,21 @@ impl Hasher { * Return the digest of all bytes added to this hasher since its last * initialization */ - pub fn finalize(&self) -> Vec { - unsafe { - let mut res = Vec::from_elem(self.len, 0u8); - ffi::EVP_DigestFinal(self.ctx, res.as_mut_ptr(), ptr::null_mut()); - res - } + pub fn finalize(self) -> Vec { + let (res, _) = self.finalize_reuse(); + res } -} -impl Drop for Hasher { - fn drop(&mut self) { + /** + * Return the digest of all bytes added to this hasher since its last + * initialization and its context for reuse + */ + pub fn finalize_reuse(self) -> (Vec, HasherContext) { + let mut res = Vec::from_elem(self.len, 0u8); unsafe { - ffi::EVP_MD_CTX_destroy(self.ctx); - } + ffi::EVP_DigestFinal_ex(self.ctx.ptr, res.as_mut_ptr(), ptr::null_mut()) + }; + (res, self.ctx) } } @@ -88,7 +113,7 @@ impl Drop for Hasher { * value */ pub fn hash(t: HashType, data: &[u8]) -> Vec { - let h = Hasher::new(t); + let mut h = Hasher::new(t); h.update(data); h.finalize() } @@ -108,9 +133,7 @@ mod tests { expected_output: output.to_string() } } - fn hash_test(hashtype: super::HashType, hashtest: &HashTest) { - let calced_raw = super::hash(hashtype, hashtest.input.as_slice()); - + fn compare(calced_raw: Vec, hashtest: &HashTest) { let calced = calced_raw.as_slice().to_hex().into_string(); if calced != hashtest.expected_output { @@ -120,6 +143,22 @@ mod tests { assert!(calced == hashtest.expected_output); } + fn hash_test(hashtype: super::HashType, hashtest: &HashTest) { + let calced_raw = super::hash(hashtype, hashtest.input.as_slice()); + compare(calced_raw, hashtest); + } + + fn hash_reuse_test(ctx: super::HasherContext, hashtype: super::HashType, + hashtest: &HashTest) -> super::HasherContext { + let mut h = super::Hasher::with_context(ctx, hashtype); + h.update(hashtest.input.as_slice()); + let (calced_raw, ctx) = h.finalize_reuse(); + + compare(calced_raw, hashtest); + + ctx + } + pub fn hash_writer(t: super::HashType, data: &[u8]) -> Vec { let mut h = super::Hasher::new(t); h.write(data).unwrap(); @@ -144,8 +183,10 @@ mod tests { HashTest("A510CD18F7A56852EB0319", "577e216843dd11573574d3fb209b97d8"), HashTest("AAED18DBE8938C19ED734A8D", "6f80fb775f27e0a4ce5c2f42fc72c5f1")]; + let mut ctx = super::HasherContext::new(); + for test in tests.iter() { - hash_test(super::HashType::MD5, test); + ctx = hash_reuse_test(ctx, super::HashType::MD5, test); } } -- GitLab