diff options
| author | Chris Cole <[email protected]> | 2014-11-29 19:47:09 -0500 |
|---|---|---|
| committer | Chris Cole <[email protected]> | 2014-11-29 19:47:09 -0500 |
| commit | 5f76f1cb62c70af4bbf064ea5c27c69544f04cea (patch) | |
| tree | 6c90d43f6bfdf1b2b83094c1bfc559bfbdecf554 /src/crypto | |
| parent | Added mod_mul. (diff) | |
| parent | Make SslStream Cloneable (diff) | |
| download | rust-openssl-5f76f1cb62c70af4bbf064ea5c27c69544f04cea.tar.xz rust-openssl-5f76f1cb62c70af4bbf064ea5c27c69544f04cea.zip | |
Merge remote-tracking branch 'upstream/master'
Conflicts:
src/bn/mod.rs
Diffstat (limited to 'src/crypto')
| -rw-r--r-- | src/crypto/hash.rs | 169 | ||||
| -rw-r--r-- | src/crypto/hmac.rs | 66 | ||||
| -rw-r--r-- | src/crypto/memcmp.rs | 39 | ||||
| -rw-r--r-- | src/crypto/mod.rs | 1 | ||||
| -rw-r--r-- | src/crypto/pkcs5.rs | 15 | ||||
| -rw-r--r-- | src/crypto/pkey.rs | 204 | ||||
| -rw-r--r-- | src/crypto/rand.rs | 11 | ||||
| -rw-r--r-- | src/crypto/symm.rs | 153 |
8 files changed, 358 insertions, 300 deletions
diff --git a/src/crypto/hash.rs b/src/crypto/hash.rs index 4a4031e2..b5d0eab5 100644 --- a/src/crypto/hash.rs +++ b/src/crypto/hash.rs @@ -1,6 +1,8 @@ -use libc; use libc::c_uint; use std::ptr; +use std::io; + +use ffi; pub enum HashType { MD5, @@ -12,80 +14,75 @@ pub enum HashType { RIPEMD160 } -#[allow(dead_code)] -#[allow(non_camel_case_types)] -#[repr(C)] -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 +pub fn evpmd(t: HashType) -> (*const ffi::EVP_MD, uint) { + unsafe { + match t { + HashType::MD5 => (ffi::EVP_md5(), 16u), + HashType::SHA1 => (ffi::EVP_sha1(), 20u), + HashType::SHA224 => (ffi::EVP_sha224(), 28u), + HashType::SHA256 => (ffi::EVP_sha256(), 32u), + HashType::SHA384 => (ffi::EVP_sha384(), 48u), + HashType::SHA512 => (ffi::EVP_sha512(), 64u), + HashType::RIPEMD160 => (ffi::EVP_ripemd160(), 20u), + } + } } -#[allow(non_camel_case_types)] -#[repr(C)] -pub struct EVP_MD; - -#[allow(non_camel_case_types)] -#[repr(C)] -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_ripemd160() -> *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 struct HasherContext { + ptr: *mut ffi::EVP_MD_CTX } -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), - RIPEMD160 => (EVP_ripemd160(), 20u), +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 EVP_MD, - ctx: *mut EVP_MD_CTX, + evp: *const ffi::EVP_MD, + ctx: HasherContext, len: uint, } +impl io::Writer for Hasher { + fn write(&mut self, buf: &[u8]) -> io::IoResult<()> { + self.update(buf); + Ok(()) + } +} + impl Hasher { pub fn new(ht: HashType) -> Hasher { - let ctx = unsafe { EVP_MD_CTX_create() }; + let ctx = HasherContext::new(); + Hasher::with_context(ctx, ht) + } + + pub fn with_context(ctx: HasherContext, ht: HashType) -> Hasher { let (evp, mdlen) = evpmd(ht); unsafe { - 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 { - 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) } } @@ -93,20 +90,21 @@ impl Hasher { * 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::null_mut()); - res - } + pub fn finalize(self) -> Vec<u8> { + 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<u8>, HasherContext) { + let mut res = Vec::from_elem(self.len, 0u8); unsafe { - EVP_MD_CTX_destroy(self.ctx); - } + ffi::EVP_DigestFinal_ex(self.ctx.ptr, res.as_mut_ptr(), ptr::null_mut()) + }; + (res, self.ctx) } } @@ -115,9 +113,9 @@ impl Drop for Hasher { * value */ pub fn hash(t: HashType, data: &[u8]) -> Vec<u8> { - let h = Hasher::new(t); + let mut h = Hasher::new(t); h.update(data); - h.final() + h.finalize() } #[cfg(test)] @@ -135,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<u8>, hashtest: &HashTest) { let calced = calced_raw.as_slice().to_hex().into_string(); if calced != hashtest.expected_output { @@ -147,6 +143,28 @@ 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<u8> { + let mut h = super::Hasher::new(t); + h.write(data).unwrap(); + h.finalize() + } + // Test vectors from http://www.nsrl.nist.gov/testdata/ #[test] fn test_md5() { @@ -165,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::MD5, test); + ctx = hash_reuse_test(ctx, super::HashType::MD5, test); } } @@ -177,7 +197,7 @@ mod tests { ]; for test in tests.iter() { - hash_test(super::SHA1, test); + hash_test(super::HashType::SHA1, test); } } @@ -188,7 +208,7 @@ mod tests { ]; for test in tests.iter() { - hash_test(super::SHA256, test); + hash_test(super::HashType::SHA256, test); } } @@ -199,7 +219,14 @@ mod tests { ]; for test in tests.iter() { - hash_test(super::RIPEMD160, test); + hash_test(super::HashType::RIPEMD160, test); } } + + #[test] + fn test_writer() { + let tv = "rust-openssl".as_bytes(); + let ht = super::HashType::RIPEMD160; + assert!(hash_writer(ht, tv) == super::hash(ht, tv)); + } } diff --git a/src/crypto/hmac.rs b/src/crypto/hmac.rs index 5bf62586..a7a854b7 100644 --- a/src/crypto/hmac.rs +++ b/src/crypto/hmac.rs @@ -14,50 +14,30 @@ * limitations under the License. */ -use libc::{c_uchar, c_int, c_uint}; -use crypto::hash; - -#[allow(dead_code)] -#[allow(non_camel_case_types)] -#[repr(C)] -pub struct HMAC_CTX { - md: *mut hash::EVP_MD, - md_ctx: hash::EVP_MD_CTX, - i_ctx: hash::EVP_MD_CTX, - o_ctx: hash::EVP_MD_CTX, - key_length: c_uint, - key: [c_uchar, ..128] -} +use libc::{c_int, c_uint}; -#[link(name = "crypto")] -extern { - fn HMAC_CTX_init(ctx: *mut HMAC_CTX); - fn HMAC_Init_ex(ctx: *mut HMAC_CTX, key: *const u8, keylen: c_int, md: *const hash::EVP_MD, imple: *const ENGINE); - fn HMAC_Update(ctx: *mut HMAC_CTX, input: *const u8, len: c_uint); - fn HMAC_Final(ctx: *mut HMAC_CTX, output: *mut u8, len: *mut c_uint); -} - -#[allow(non_camel_case_types)] -#[repr(C)] -struct ENGINE; +use crypto::hash; +use ffi; pub struct HMAC { - ctx: HMAC_CTX, + ctx: ffi::HMAC_CTX, len: uint, } #[allow(non_snake_case)] pub fn HMAC(ht: hash::HashType, key: &[u8]) -> HMAC { unsafe { + ffi::init(); + let (evp, mdlen) = hash::evpmd(ht); - let mut ctx : HMAC_CTX = ::std::mem::uninitialized(); + let mut ctx : ffi::HMAC_CTX = ::std::mem::uninitialized(); - HMAC_CTX_init(&mut ctx); - HMAC_Init_ex(&mut ctx, - key.as_ptr(), - key.len() as c_int, - evp, 0 as *const _); + ffi::HMAC_CTX_init(&mut ctx); + ffi::HMAC_Init_ex(&mut ctx, + key.as_ptr(), + key.len() as c_int, + evp, 0 as *const _); HMAC { ctx: ctx, len: mdlen } } @@ -66,15 +46,15 @@ pub fn HMAC(ht: hash::HashType, key: &[u8]) -> HMAC { impl HMAC { pub fn update(&mut self, data: &[u8]) { unsafe { - HMAC_Update(&mut self.ctx, data.as_ptr(), data.len() as c_uint) + ffi::HMAC_Update(&mut self.ctx, data.as_ptr(), data.len() as c_uint) } } - pub fn final(&mut self) -> Vec<u8> { + pub fn finalize(&mut self) -> Vec<u8> { unsafe { let mut res = Vec::from_elem(self.len, 0u8); let mut outlen = 0; - HMAC_Final(&mut self.ctx, res.as_mut_ptr(), &mut outlen); + ffi::HMAC_Final(&mut self.ctx, res.as_mut_ptr(), &mut outlen); assert!(self.len == outlen as uint) res } @@ -84,7 +64,7 @@ impl HMAC { #[cfg(test)] mod tests { use serialize::hex::FromHex; - use crypto::hash::{HashType, MD5, SHA1, SHA224, SHA256, SHA384, SHA512}; + use crypto::hash::HashType::{mod, MD5, SHA1, SHA224, SHA256, SHA384, SHA512}; use super::HMAC; #[test] @@ -116,7 +96,7 @@ mod tests { for &(ref key, ref data, ref res) in tests.iter() { let mut hmac = HMAC(MD5, key.as_slice()); hmac.update(data.as_slice()); - assert_eq!(hmac.final(), *res); + assert_eq!(hmac.finalize(), *res); } } @@ -149,7 +129,7 @@ mod tests { for &(ref key, ref data, ref res) in tests.iter() { let mut hmac = HMAC(SHA1, key.as_slice()); hmac.update(data.as_slice()); - assert_eq!(hmac.final(), *res); + assert_eq!(hmac.finalize(), *res); } } @@ -173,7 +153,7 @@ mod tests { for (&(ref key, ref data), res) in tests.iter().zip(results.iter()) { let mut hmac = HMAC(ty, key.as_slice()); hmac.update(data.as_slice()); - assert_eq!(hmac.final(), *res); + assert_eq!(hmac.finalize(), *res); } } @@ -187,7 +167,7 @@ mod tests { "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e".from_hex().unwrap(), "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1".from_hex().unwrap() ]; - test_sha2(SHA224, results); + test_sha2(SHA224, &results); } #[test] @@ -200,7 +180,7 @@ mod tests { "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54".from_hex().unwrap(), "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2".from_hex().unwrap() ]; - test_sha2(SHA256, results); + test_sha2(SHA256, &results); } #[test] @@ -225,7 +205,7 @@ mod tests { 602420feb0b8fb9adccebb82461e99c5\ a678cc31e799176d3860e6110c46523e".from_hex().unwrap() ]; - test_sha2(SHA384, results); + test_sha2(SHA384, &results); } #[test] @@ -256,6 +236,6 @@ mod tests { b6022cac3c4982b10d5eeb55c3e4de15\ 134676fb6de0446065c97440fa8c6a58".from_hex().unwrap() ]; - test_sha2(SHA512, results); + test_sha2(SHA512, &results); } } diff --git a/src/crypto/memcmp.rs b/src/crypto/memcmp.rs new file mode 100644 index 00000000..299effa9 --- /dev/null +++ b/src/crypto/memcmp.rs @@ -0,0 +1,39 @@ +use libc::size_t; +use ffi; + +/// Returns `true` iff `a` and `b` contain the same bytes. +/// +/// This operation takes an amount of time dependent on the length of the two +/// arrays given, but is independent of the contents of a and b. +/// +/// # Failure +/// +/// This function will panic the current task if `a` and `b` do not have the same +/// length. +pub fn eq(a: &[u8], b: &[u8]) -> bool { + assert!(a.len() == b.len()); + let ret = unsafe { + ffi::CRYPTO_memcmp(a.as_ptr() as *const _, + b.as_ptr() as *const _, + a.len() as size_t) + }; + ret == 0 +} + +#[cfg(test)] +mod tests { + use super::eq; + + #[test] + fn test_eq() { + assert!(eq(&[], &[])); + assert!(eq(&[1], &[1])); + assert!(!eq(&[1, 2, 3], &[1, 2, 4])); + } + + #[test] + #[should_fail] + fn test_diff_lens() { + eq(&[], &[1]); + } +} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index d7c62f98..e695de33 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -21,3 +21,4 @@ pub mod pkcs5; pub mod pkey; pub mod rand; pub mod symm; +pub mod memcmp; diff --git a/src/crypto/pkcs5.rs b/src/crypto/pkcs5.rs index b795d84a..6efd23ff 100644 --- a/src/crypto/pkcs5.rs +++ b/src/crypto/pkcs5.rs @@ -1,12 +1,5 @@ use libc::c_int; - -#[link(name = "crypto")] -extern { - fn PKCS5_PBKDF2_HMAC_SHA1(pass: *const u8, passlen: c_int, - salt: *const u8, saltlen: c_int, - iter: c_int, keylen: c_int, - out: *mut u8) -> c_int; -} +use ffi; /// Derives a key from a password and salt using the PBKDF2-HMAC-SHA1 algorithm. pub fn pbkdf2_hmac_sha1(pass: &str, salt: &[u8], iter: uint, keylen: uint) -> Vec<u8> { @@ -16,13 +9,15 @@ pub fn pbkdf2_hmac_sha1(pass: &str, salt: &[u8], iter: uint, keylen: uint) -> Ve let mut out = Vec::with_capacity(keylen); - let r = PKCS5_PBKDF2_HMAC_SHA1( + ffi::init(); + + let r = ffi::PKCS5_PBKDF2_HMAC_SHA1( pass.as_ptr(), pass.len() as c_int, salt.as_ptr(), salt.len() as c_int, iter as c_int, keylen as c_int, out.as_mut_ptr()); - if r != 1 { fail!(); } + if r != 1 { panic!(); } out.set_len(keylen); diff --git a/src/crypto/pkey.rs b/src/crypto/pkey.rs index d4a98713..146d2aa3 100644 --- a/src/crypto/pkey.rs +++ b/src/crypto/pkey.rs @@ -1,40 +1,11 @@ -use libc::{c_char, c_int, c_uint}; -use libc; +use libc::{c_int, c_uint}; use std::mem; use std::ptr; -use crypto::hash::{HashType, MD5, SHA1, SHA224, SHA256, SHA384, SHA512, RIPEMD160}; - -#[allow(non_camel_case_types)] -pub type EVP_PKEY = *mut libc::c_void; - -#[allow(non_camel_case_types)] -pub type RSA = *mut libc::c_void; - -#[link(name = "crypto")] -extern { - fn EVP_PKEY_new() -> *mut EVP_PKEY; - fn EVP_PKEY_free(k: *mut EVP_PKEY); - fn EVP_PKEY_assign(pkey: *mut EVP_PKEY, typ: c_int, key: *const c_char) -> c_int; - fn EVP_PKEY_get1_RSA(k: *mut EVP_PKEY) -> *mut RSA; - fn EVP_PKEY_set1_RSA(k: *mut EVP_PKEY, r: *mut RSA) -> c_int; - - fn i2d_RSA_PUBKEY(k: *mut RSA, buf: *const *mut u8) -> c_int; - fn d2i_RSA_PUBKEY(k: *const *mut RSA, buf: *const *const u8, len: c_uint) -> *mut RSA; - fn i2d_RSAPrivateKey(k: *mut RSA, buf: *const *mut u8) -> c_int; - fn d2i_RSAPrivateKey(k: *const *mut RSA, buf: *const *const u8, len: c_uint) -> *mut RSA; - - fn RSA_generate_key(modsz: c_uint, e: c_uint, cb: *const u8, cbarg: *const u8) -> *mut RSA; - fn RSA_size(k: *mut RSA) -> c_uint; - - fn RSA_public_encrypt(flen: c_uint, from: *const u8, to: *mut u8, k: *mut RSA, - pad: c_int) -> c_int; - fn RSA_private_decrypt(flen: c_uint, from: *const u8, to: *mut u8, k: *mut RSA, - pad: c_int) -> c_int; - fn RSA_sign(t: c_int, m: *const u8, mlen: c_uint, sig: *mut u8, siglen: *mut c_uint, - k: *mut RSA) -> c_int; - fn RSA_verify(t: c_int, m: *const u8, mlen: c_uint, sig: *const u8, siglen: c_uint, - k: *mut RSA) -> c_int; -} +use bio::{MemBio}; +use crypto::hash::HashType; +use ffi; +use ssl::error::{SslError, StreamError}; + enum Parts { Neither, @@ -58,25 +29,25 @@ pub enum EncryptionPadding { fn openssl_padding_code(padding: EncryptionPadding) -> c_int { match padding { - OAEP => 4, - PKCS1v15 => 1 + EncryptionPadding::OAEP => 4, + EncryptionPadding::PKCS1v15 => 1 } } fn openssl_hash_nid(hash: HashType) -> c_int { match hash { - MD5 => 4, // NID_md5, - SHA1 => 64, // NID_sha1 - SHA224 => 675, // NID_sha224 - SHA256 => 672, // NID_sha256 - SHA384 => 673, // NID_sha384 - SHA512 => 674, // NID_sha512 - RIPEMD160 => 117, // NID_ripemd160 + HashType::MD5 => 4, // NID_md5, + HashType::SHA1 => 64, // NID_sha1 + HashType::SHA224 => 675, // NID_sha224 + HashType::SHA256 => 672, // NID_sha256 + HashType::SHA384 => 673, // NID_sha384 + HashType::SHA512 => 674, // NID_sha512 + HashType::RIPEMD160 => 117, // NID_ripemd160 } } pub struct PKey { - evp: *mut EVP_PKEY, + evp: *mut ffi::EVP_PKEY, parts: Parts, } @@ -84,16 +55,18 @@ pub struct PKey { impl PKey { pub fn new() -> PKey { unsafe { + ffi::init(); + PKey { - evp: EVP_PKEY_new(), - parts: Neither, + evp: ffi::EVP_PKEY_new(), + parts: Parts::Neither, } } } - fn _tostr(&self, f: unsafe extern "C" fn(*mut RSA, *const *mut u8) -> c_int) -> Vec<u8> { + fn _tostr(&self, f: unsafe extern "C" fn(*mut ffi::RSA, *const *mut u8) -> c_int) -> Vec<u8> { unsafe { - let rsa = EVP_PKEY_get1_RSA(self.evp); + let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); let len = f(rsa, ptr::null()); if len < 0 as c_int { return vec!(); } let mut s = Vec::from_elem(len as uint, 0u8); @@ -105,17 +78,17 @@ impl PKey { } } - fn _fromstr(&mut self, s: &[u8], f: unsafe extern "C" fn(*const *mut RSA, *const *const u8, c_uint) -> *mut RSA) { + fn _fromstr(&mut self, s: &[u8], f: unsafe extern "C" fn(*const *mut ffi::RSA, *const *const u8, c_uint) -> *mut ffi::RSA) { unsafe { let rsa = ptr::null_mut(); f(&rsa, &s.as_ptr(), s.len() as c_uint); - EVP_PKEY_set1_RSA(self.evp, rsa); + ffi::EVP_PKEY_set1_RSA(self.evp, rsa); } } pub fn gen(&mut self, keysz: uint) { unsafe { - let rsa = RSA_generate_key( + let rsa = ffi::RSA_generate_key( keysz as c_uint, 65537u as c_uint, ptr::null(), @@ -123,12 +96,12 @@ impl PKey { ); // XXX: 6 == NID_rsaEncryption - EVP_PKEY_assign( + ffi::EVP_PKEY_assign( self.evp, 6 as c_int, mem::transmute(rsa)); - self.parts = Both; + self.parts = Parts::Both; } } @@ -136,15 +109,15 @@ impl PKey { * Returns a serialized form of the public key, suitable for load_pub(). */ pub fn save_pub(&self) -> Vec<u8> { - self._tostr(i2d_RSA_PUBKEY) + self._tostr(ffi::i2d_RSA_PUBKEY) } /** * Loads a serialized form of the public key, as produced by save_pub(). */ pub fn load_pub(&mut self, s: &[u8]) { - self._fromstr(s, d2i_RSA_PUBKEY); - self.parts = Public; + self._fromstr(s, ffi::d2i_RSA_PUBKEY); + self.parts = Parts::Public; } /** @@ -152,15 +125,28 @@ impl PKey { * load_priv(). */ pub fn save_priv(&self) -> Vec<u8> { - self._tostr(i2d_RSAPrivateKey) + self._tostr(ffi::i2d_RSAPrivateKey) } /** * Loads a serialized form of the public and private keys, as produced by * save_priv(). */ pub fn load_priv(&mut self, s: &[u8]) { - self._fromstr(s, d2i_RSAPrivateKey); - self.parts = Both; + self._fromstr(s, ffi::d2i_RSAPrivateKey); + self.parts = Parts::Both; + } + + /// Stores private key as a PEM + // FIXME: also add password and encryption + pub fn write_pem(&self, writer: &mut Writer/*, password: Option<String>*/) -> Result<(), SslError> { + let mut mem_bio = try!(MemBio::new()); + unsafe { + try_ssl!(ffi::PEM_write_bio_PrivateKey(mem_bio.get_handle(), self.evp, ptr::null(), + ptr::null_mut(), -1, None, ptr::null_mut())); + + } + let buf = try!(mem_bio.read_to_end().map_err(StreamError)); + writer.write(buf.as_slice()).map_err(StreamError) } /** @@ -168,7 +154,7 @@ impl PKey { */ pub fn size(&self) -> uint { unsafe { - RSA_size(EVP_PKEY_get1_RSA(self.evp)) as uint + ffi::RSA_size(ffi::EVP_PKEY_get1_RSA(self.evp)) as uint } } @@ -177,24 +163,24 @@ impl PKey { */ pub fn can(&self, r: Role) -> bool { match r { - Encrypt => + Role::Encrypt => match self.parts { - Neither => false, + Parts::Neither => false, _ => true, }, - Verify => + Role::Verify => match self.parts { - Neither => false, + Parts::Neither => false, _ => true, }, - Decrypt => + Role::Decrypt => match self.parts { - Both => true, + Parts::Both => true, _ => false, }, - Sign => + Role::Sign => match self.parts { - Both => true, + Parts::Both => true, _ => false, }, } @@ -206,8 +192,8 @@ impl PKey { */ pub fn max_data(&self) -> uint { unsafe { - let rsa = EVP_PKEY_get1_RSA(self.evp); - let len = RSA_size(rsa); + let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); + let len = ffi::RSA_size(rsa); // 41 comes from RSA_public_encrypt(3) for OAEP len as uint - 41u @@ -216,14 +202,14 @@ impl PKey { pub fn encrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec<u8> { unsafe { - let rsa = EVP_PKEY_get1_RSA(self.evp); - let len = RSA_size(rsa); + let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); + let len = ffi::RSA_size(rsa); assert!(s.len() < self.max_data()); let mut r = Vec::from_elem(len as uint + 1u, 0u8); - let rv = RSA_public_encrypt( + let rv = ffi::RSA_public_encrypt( s.len() as c_uint, s.as_ptr(), r.as_mut_ptr(), @@ -241,14 +227,14 @@ impl PKey { pub fn decrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec<u8> { unsafe { - let rsa = EVP_PKEY_get1_RSA(self.evp); - let len = RSA_size(rsa); + let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); + let len = ffi::RSA_size(rsa); - assert_eq!(s.len() as c_uint, RSA_size(rsa)); + assert_eq!(s.len() as c_uint, ffi::RSA_size(rsa)); let mut r = Vec::from_elem(len as uint + 1u, 0u8); - let rv = RSA_private_decrypt( + let rv = ffi::RSA_private_decrypt( s.len() as c_uint, s.as_ptr(), r.as_mut_ptr(), @@ -268,32 +254,32 @@ impl PKey { * Encrypts data using OAEP padding, returning the encrypted data. The * supplied data must not be larger than max_data(). */ - pub fn encrypt(&self, s: &[u8]) -> Vec<u8> { self.encrypt_with_padding(s, OAEP) } + pub fn encrypt(&self, s: &[u8]) -> Vec<u8> { self.encrypt_with_padding(s, EncryptionPadding::OAEP) } /** * Decrypts data, expecting OAEP padding, returning the decrypted data. */ - pub fn decrypt(&self, s: &[u8]) -> Vec<u8> { self.decrypt_with_padding(s, OAEP) } + pub fn decrypt(&self, s: &[u8]) -> Vec<u8> { self.decrypt_with_padding(s, EncryptionPadding::OAEP) } /** * Signs data, using OpenSSL's default scheme and sha256. Unlike encrypt(), * can process an arbitrary amount of data; returns the signature. */ - pub fn sign(&self, s: &[u8]) -> Vec<u8> { self.sign_with_hash(s, SHA256) } + pub fn sign(&self, s: &[u8]) -> Vec<u8> { self.sign_with_hash(s, HashType::SHA256) } /** * Verifies a signature s (using OpenSSL's default scheme and sha256) on a * message m. Returns true if the signature is valid, and false otherwise. */ - pub fn verify(&self, m: &[u8], s: &[u8]) -> bool { self.verify_with_hash(m, s, SHA256) } + pub fn verify(&self, m: &[u8], s: &[u8]) -> bool { self.verify_with_hash(m, s, HashType::SHA256) } pub fn sign_with_hash(&self, s: &[u8], hash: HashType) -> Vec<u8> { unsafe { - let rsa = EVP_PKEY_get1_RSA(self.evp); - let mut len = RSA_size(rsa); + let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); + let mut len = ffi::RSA_size(rsa); let mut r = Vec::from_elem(len as uint + 1u, 0u8); - let rv = RSA_sign( + let rv = ffi::RSA_sign( openssl_hash_nid(hash), s.as_ptr(), s.len() as c_uint, @@ -312,9 +298,9 @@ impl PKey { pub fn verify_with_hash(&self, m: &[u8], s: &[u8], hash: HashType) -> bool { unsafe { - let rsa = EVP_PKEY_get1_RSA(self.evp); + let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - let rv = RSA_verify( + let rv = ffi::RSA_verify( openssl_hash_nid(hash), m.as_ptr(), m.len() as c_uint, @@ -326,19 +312,23 @@ impl PKey { rv == 1 as c_int } } + + pub unsafe fn get_handle(&self) -> *mut ffi::EVP_PKEY { + return self.evp + } } impl Drop for PKey { fn drop(&mut self) { unsafe { - EVP_PKEY_free(self.evp); + ffi::EVP_PKEY_free(self.evp); } } } #[cfg(test)] mod tests { - use crypto::hash::{MD5, SHA1}; + use crypto::hash::HashType::{MD5, SHA1}; #[test] fn test_gen_pub() { @@ -348,14 +338,14 @@ mod tests { k1.load_pub(k0.save_pub().as_slice()); assert_eq!(k0.save_pub(), k1.save_pub()); assert_eq!(k0.size(), k1.size()); - assert!(k0.can(super::Encrypt)); - assert!(k0.can(super::Decrypt)); - assert!(k0.can(super::Verify)); - assert!(k0.can(super::Sign)); - assert!(k1.can(super::Encrypt)); - assert!(!k1.can(super::Decrypt)); - assert!(k1.can(super::Verify)); - assert!(!k1.can(super::Sign)); + assert!(k0.can(super::Role::Encrypt)); + assert!(k0.can(super::Role::Decrypt)); + assert!(k0.can(super::Role::Verify)); + assert!(k0.can(super::Role::Sign)); + assert!(k1.can(super::Role::Encrypt)); + assert!(!k1.can(super::Role::Decrypt)); + assert!(k1.can(super::Role::Verify)); + assert!(!k1.can(super::Role::Sign)); } #[test] @@ -366,14 +356,14 @@ mod tests { k1.load_priv(k0.save_priv().as_slice()); assert_eq!(k0.save_priv(), k1.save_priv()); assert_eq!(k0.size(), k1.size()); - assert!(k0.can(super::Encrypt)); - assert!(k0.can(super::Decrypt)); - assert!(k0.can(super::Verify)); - assert!(k0.can(super::Sign)); - assert!(k1.can(super::Encrypt)); - assert!(k1.can(super::Decrypt)); - assert!(k1.can(super::Verify)); - assert!(k1.can(super::Sign)); + assert!(k0.can(super::Role::Encrypt)); + assert!(k0.can(super::Role::Decrypt)); + assert!(k0.can(super::Role::Verify)); + assert!(k0.can(super::Role::Sign)); + assert!(k1.can(super::Role::Encrypt)); + assert!(k1.can(super::Role::Decrypt)); + assert!(k1.can(super::Role::Verify)); + assert!(k1.can(super::Role::Sign)); } #[test] @@ -395,8 +385,8 @@ mod tests { let msg = vec!(0xdeu8, 0xadu8, 0xd0u8, 0x0du8); k0.gen(512u); k1.load_pub(k0.save_pub().as_slice()); - let emsg = k1.encrypt_with_padding(msg.as_slice(), super::PKCS1v15); - let dmsg = k0.decrypt_with_padding(emsg.as_slice(), super::PKCS1v15); + let emsg = k1.encrypt_with_padding(msg.as_slice(), super::EncryptionPadding::PKCS1v15); + let dmsg = k0.decrypt_with_padding(emsg.as_slice(), super::EncryptionPadding::PKCS1v15); assert!(msg == dmsg); } diff --git a/src/crypto/rand.rs b/src/crypto/rand.rs index 9db87fcd..92d38f2c 100644 --- a/src/crypto/rand.rs +++ b/src/crypto/rand.rs @@ -1,16 +1,13 @@ use libc::c_int; - -#[link(name = "crypto")] -extern { - fn RAND_bytes(buf: *mut u8, num: c_int) -> c_int; -} +use ffi; pub fn rand_bytes(len: uint) -> Vec<u8> { unsafe { let mut out = Vec::with_capacity(len); - let r = RAND_bytes(out.as_mut_ptr(), len as c_int); - if r != 1 as c_int { fail!() } + ffi::init(); + let r = ffi::RAND_bytes(out.as_mut_ptr(), len as c_int); + if r != 1 as c_int { panic!() } out.set_len(len); diff --git a/src/crypto/symm.rs b/src/crypto/symm.rs index 6bba7267..998d351c 100644 --- a/src/crypto/symm.rs +++ b/src/crypto/symm.rs @@ -1,36 +1,6 @@ -use libc::{c_int, c_uint}; -use libc; +use libc::{c_int}; -#[allow(non_camel_case_types)] -pub type EVP_CIPHER_CTX = *mut libc::c_void; - -#[allow(non_camel_case_types)] -pub type EVP_CIPHER = *mut libc::c_void; - -#[link(name = "crypto")] -extern { - fn EVP_CIPHER_CTX_new() -> EVP_CIPHER_CTX; - fn EVP_CIPHER_CTX_set_padding(ctx: EVP_CIPHER_CTX, padding: c_int); - fn EVP_CIPHER_CTX_free(ctx: EVP_CIPHER_CTX); - - fn EVP_aes_128_ecb() -> EVP_CIPHER; - fn EVP_aes_128_cbc() -> EVP_CIPHER; - // fn EVP_aes_128_ctr() -> EVP_CIPHER; - // fn EVP_aes_128_gcm() -> EVP_CIPHER; - - fn EVP_aes_256_ecb() -> EVP_CIPHER; - fn EVP_aes_256_cbc() -> EVP_CIPHER; - // fn EVP_aes_256_ctr() -> EVP_CIPHER; - // fn EVP_aes_256_gcm() -> EVP_CIPHER; - - fn EVP_rc4() -> EVP_CIPHER; - - fn EVP_CipherInit(ctx: EVP_CIPHER_CTX, evp: EVP_CIPHER, - key: *const u8, iv: *const u8, mode: c_int); - fn EVP_CipherUpdate(ctx: EVP_CIPHER_CTX, outbuf: *mut u8, - outlen: &mut c_uint, inbuf: *const u8, inlen: c_int); - fn EVP_CipherFinal(ctx: EVP_CIPHER_CTX, res: *mut u8, len: &mut c_int); -} +use ffi; pub enum Mode { Encrypt, @@ -41,46 +11,58 @@ pub enum Mode { pub enum Type { AES_128_ECB, AES_128_CBC, + /// Requires the `aes_xts` feature + #[cfg(feature = "aes_xts")] + AES_128_XTS, // AES_128_CTR, //AES_128_GCM, AES_256_ECB, AES_256_CBC, + /// Requires the `aes_xts` feature + #[cfg(feature = "aes_xts")] + AES_256_XTS, // AES_256_CTR, //AES_256_GCM, RC4_128, } -fn evpc(t: Type) -> (EVP_CIPHER, uint, uint) { +fn evpc(t: Type) -> (*const ffi::EVP_CIPHER, uint, uint) { unsafe { match t { - AES_128_ECB => (EVP_aes_128_ecb(), 16u, 16u), - AES_128_CBC => (EVP_aes_128_cbc(), 16u, 16u), + Type::AES_128_ECB => (ffi::EVP_aes_128_ecb(), 16u, 16u), + Type::AES_128_CBC => (ffi::EVP_aes_128_cbc(), 16u, 16u), + #[cfg(feature = "aes_xts")] + Type::AES_128_XTS => (ffi::EVP_aes_128_xts(), 32u, 16u), // AES_128_CTR => (EVP_aes_128_ctr(), 16u, 0u), //AES_128_GCM => (EVP_aes_128_gcm(), 16u, 16u), - AES_256_ECB => (EVP_aes_256_ecb(), 32u, 16u), - AES_256_CBC => (EVP_aes_256_cbc(), 32u, 16u), + Type::AES_256_ECB => (ffi::EVP_aes_256_ecb(), 32u, 16u), + Type::AES_256_CBC => (ffi::EVP_aes_256_cbc(), 32u, 16u), + #[cfg(feature = "aes_xts")] + Type::AES_256_XTS => (ffi::EVP_aes_256_xts(), 64u, 16u), // AES_256_CTR => (EVP_aes_256_ctr(), 32u, 0u), //AES_256_GCM => (EVP_aes_256_gcm(), 32u, 16u), - RC4_128 => (EVP_rc4(), 16u, 0u), + Type::RC4_128 => (ffi::EVP_rc4(), 16u, 0u), } } } /// Represents a symmetric cipher context. pub struct Crypter { - evp: EVP_CIPHER, - ctx: EVP_CIPHER_CTX, + evp: *const ffi::EVP_CIPHER, + ctx: *mut ffi::EVP_CIPHER_CTX, keylen: uint, blocksize: uint } impl Crypter { pub fn new(t: Type) -> Crypter { - let ctx = unsafe { EVP_CIPHER_CTX_new() }; + ffi::init(); + + let ctx = unsafe { ffi::EVP_CIPHER_CTX_new() }; let (evp, keylen, blocksz) = evpc(t); Crypter { evp: evp, ctx: ctx, keylen: keylen, blocksize: blocksz } } @@ -93,7 +75,7 @@ impl Crypter { if self.blocksize > 0 { unsafe { let v = if padding { 1 as c_int } else { 0 }; - EVP_CIPHER_CTX_set_padding(self.ctx, v); + ffi::EVP_CIPHER_CTX_set_padding(self.ctx, v); } } } @@ -104,12 +86,12 @@ impl Crypter { pub fn init(&self, mode: Mode, key: &[u8], iv: Vec<u8>) { unsafe { let mode = match mode { - Encrypt => 1 as c_int, - Decrypt => 0 as c_int, + Mode::Encrypt => 1 as c_int, + Mode::Decrypt => 0 as c_int, }; assert_eq!(key.len(), self.keylen); - EVP_CipherInit( + ffi::EVP_CipherInit( self.ctx, self.evp, key.as_ptr(), @@ -128,7 +110,7 @@ impl Crypter { let mut res = Vec::from_elem(data.len() + self.blocksize, 0u8); let mut reslen = (data.len() + self.blocksize) as u32; - EVP_CipherUpdate( + ffi::EVP_CipherUpdate( self.ctx, res.as_mut_ptr(), &mut reslen, @@ -144,12 +126,12 @@ impl Crypter { /** * Finish crypting. Returns the remaining partial block of output, if any. */ - pub fn final(&self) -> Vec<u8> { + pub fn finalize(&self) -> Vec<u8> { unsafe { let mut res = Vec::from_elem(self.blocksize, 0u8); let mut reslen = self.blocksize as c_int; - EVP_CipherFinal(self.ctx, + ffi::EVP_CipherFinal(self.ctx, res.as_mut_ptr(), &mut reslen); @@ -162,7 +144,7 @@ impl Crypter { impl Drop for Crypter { fn drop(&mut self) { unsafe { - EVP_CIPHER_CTX_free(self.ctx); + ffi::EVP_CIPHER_CTX_free(self.ctx); } } } @@ -173,9 +155,9 @@ impl Drop for Crypter { */ pub fn encrypt(t: Type, key: &[u8], iv: Vec<u8>, data: &[u8]) -> Vec<u8> { let c = Crypter::new(t); - c.init(Encrypt, key, iv); + c.init(Mode::Encrypt, key, iv); let mut r = c.update(data); - let rest = c.final(); + let rest = c.finalize(); r.extend(rest.into_iter()); r } @@ -186,9 +168,9 @@ pub fn encrypt(t: Type, key: &[u8], iv: Vec<u8>, data: &[u8]) -> Vec<u8> { */ pub fn decrypt(t: Type, key: &[u8], iv: Vec<u8>, data: &[u8]) -> Vec<u8> { let c = Crypter::new(t); - c.init(Decrypt, key, iv); + c.init(Mode::Decrypt, key, iv); let mut r = c.update(data); - let rest = c.final(); + let rest = c.finalize(); r.extend(rest.into_iter()); r } @@ -212,28 +194,62 @@ mod tests { let c0 = vec!(0x8eu8, 0xa2u8, 0xb7u8, 0xcau8, 0x51u8, 0x67u8, 0x45u8, 0xbfu8, 0xeau8, 0xfcu8, 0x49u8, 0x90u8, 0x4bu8, 0x49u8, 0x60u8, 0x89u8); - let c = super::Crypter::new(super::AES_256_ECB); - c.init(super::Encrypt, k0.as_slice(), vec![]); + let c = super::Crypter::new(super::Type::AES_256_ECB); + c.init(super::Mode::Encrypt, k0.as_slice(), vec![]); c.pad(false); let mut r0 = c.update(p0.as_slice()); - r0.extend(c.final().into_iter()); + r0.extend(c.finalize().into_iter()); assert!(r0 == c0); - c.init(super::Decrypt, k0.as_slice(), vec![]); + c.init(super::Mode::Decrypt, k0.as_slice(), vec![]); c.pad(false); let mut p1 = c.update(r0.as_slice()); - p1.extend(c.final().into_iter()); + p1.extend(c.finalize().into_iter()); assert!(p1 == p0); } + #[test] + fn test_aes_256_cbc_decrypt() { + let cr = super::Crypter::new(super::Type::AES_256_CBC); + let iv = vec![ + 4_u8, 223_u8, 153_u8, 219_u8, 28_u8, 142_u8, 234_u8, 68_u8, 227_u8, + 69_u8, 98_u8, 107_u8, 208_u8, 14_u8, 236_u8, 60_u8, 0_u8, 0_u8, + 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, + 0_u8, 0_u8, 0_u8 + ]; + let data = [ + 143_u8, 210_u8, 75_u8, 63_u8, 214_u8, 179_u8, 155_u8, + 241_u8, 242_u8, 31_u8, 154_u8, 56_u8, 198_u8, 145_u8, 192_u8, 64_u8, + 2_u8, 245_u8, 167_u8, 220_u8, 55_u8, 119_u8, 233_u8, 136_u8, 139_u8, + 27_u8, 71_u8, 242_u8, 119_u8, 175_u8, 65_u8, 207_u8 + ]; + let ciphered_data = [ + 0x4a_u8, 0x2e_u8, 0xe5_u8, 0x6_u8, 0xbf_u8, 0xcf_u8, 0xf2_u8, 0xd7_u8, + 0xea_u8, 0x2d_u8, 0xb1_u8, 0x85_u8, 0x6c_u8, 0x93_u8, 0x65_u8, 0x6f_u8 + ]; + cr.init(super::Mode::Decrypt, &data, iv); + cr.pad(false); + let unciphered_data_1 = cr.update(&ciphered_data); + let unciphered_data_2 = cr.finalize(); + + let expected_unciphered_data = b"I love turtles.\x01"; + + assert!(unciphered_data_2.len() == 0); + + assert_eq!( + unciphered_data_1.as_slice(), + expected_unciphered_data + ); + } + fn cipher_test(ciphertype: super::Type, pt: &str, ct: &str, key: &str, iv: &str) { use serialize::hex::ToHex; let cipher = super::Crypter::new(ciphertype); - cipher.init(super::Encrypt, key.from_hex().unwrap().as_slice(), iv.from_hex().unwrap()); + cipher.init(super::Mode::Encrypt, key.from_hex().unwrap().as_slice(), iv.from_hex().unwrap()); let expected = ct.from_hex().unwrap().as_slice().to_vec(); let mut computed = cipher.update(pt.from_hex().unwrap().as_slice()); - computed.extend(cipher.final().into_iter()); + computed.extend(cipher.finalize().into_iter()); if computed != expected { println!("Computed: {}", computed.as_slice().to_hex()); @@ -242,7 +258,7 @@ mod tests { println!("Lengths differ: {} in computed vs {} expected", computed.len(), expected.len()); } - fail!("test failure"); + panic!("test failure"); } } @@ -254,7 +270,20 @@ mod tests { let key = "97CD440324DA5FD1F7955C1C13B6B466"; let iv = ""; - cipher_test(super::RC4_128, pt, ct, key, iv); + cipher_test(super::Type::RC4_128, pt, ct, key, iv); + } + + #[test] + #[cfg(feature = "aes_xts")] + fn test_aes256_xts() { + // Test case 174 from + // http://csrc.nist.gov/groups/STM/cavp/documents/aes/XTSTestVectors.zip + let pt = "77f4ef63d734ebd028508da66c22cdebdd52ecd6ee2ab0a50bc8ad0cfd692ca5fcd4e6dedc45df7f6503f462611dc542"; + let ct = "ce7d905a7776ac72f240d22aafed5e4eb7566cdc7211220e970da634ce015f131a5ecb8d400bc9e84f0b81d8725dbbc7"; + let key = "b6bfef891f83b5ff073f2231267be51eb084b791fa19a154399c0684c8b2dfcb37de77d28bbda3b4180026ad640b74243b3133e7b9fae629403f6733423dae28"; + let iv = "db200efb7eaaa737dbdf40babb68953f"; + + cipher_test(super::Type::AES_256_XTS, pt, ct, key, iv); } /*#[test] |