diff options
Diffstat (limited to 'openssl/src/crypto')
| -rw-r--r-- | openssl/src/crypto/dsa.rs | 338 | ||||
| -rw-r--r-- | openssl/src/crypto/hash.rs | 150 | ||||
| -rw-r--r-- | openssl/src/crypto/hmac.rs | 162 | ||||
| -rw-r--r-- | openssl/src/crypto/mod.rs | 11 | ||||
| -rw-r--r-- | openssl/src/crypto/pkcs5.rs | 179 | ||||
| -rw-r--r-- | openssl/src/crypto/pkey.rs | 876 | ||||
| -rw-r--r-- | openssl/src/crypto/rand.rs | 20 | ||||
| -rw-r--r-- | openssl/src/crypto/rsa.rs | 303 | ||||
| -rw-r--r-- | openssl/src/crypto/symm.rs | 329 | ||||
| -rw-r--r-- | openssl/src/crypto/symm_internal.rs | 35 | ||||
| -rw-r--r-- | openssl/src/crypto/util.rs | 58 |
11 files changed, 1097 insertions, 1364 deletions
diff --git a/openssl/src/crypto/dsa.rs b/openssl/src/crypto/dsa.rs new file mode 100644 index 00000000..97ba7a97 --- /dev/null +++ b/openssl/src/crypto/dsa.rs @@ -0,0 +1,338 @@ +use ffi; +use std::fmt; +use error::ErrorStack; +use std::ptr; +use libc::{c_uint, c_int, c_char, c_void}; + +use bn::BigNumRef; +use bio::{MemBio, MemBioSlice}; +use crypto::hash; +use HashTypeInternals; +use crypto::util::{CallbackState, invoke_passwd_cb}; + + +/// Builder for upfront DSA parameter generateration +pub struct DSAParams(*mut ffi::DSA); + +impl DSAParams { + pub fn with_size(size: u32) -> Result<DSAParams, ErrorStack> { + unsafe { + // Wrap it so that if we panic we'll call the dtor + let dsa = DSAParams(try_ssl_null!(ffi::DSA_new())); + try_ssl!(ffi::DSA_generate_parameters_ex(dsa.0, size as c_int, ptr::null(), 0, + ptr::null_mut(), ptr::null_mut(), ptr::null())); + Ok(dsa) + } + } + + /// Generate a key pair from the initialized parameters + pub fn generate(self) -> Result<DSA, ErrorStack> { + unsafe { + try_ssl!(ffi::DSA_generate_key(self.0)); + let dsa = DSA(self.0); + ::std::mem::forget(self); + Ok(dsa) + } + } +} + +impl Drop for DSAParams { + fn drop(&mut self) { + unsafe { + ffi::DSA_free(self.0); + } + } +} + +pub struct DSA(*mut ffi::DSA); + +impl Drop for DSA { + fn drop(&mut self) { + unsafe { + ffi::DSA_free(self.0); + } + } +} + +impl DSA { + pub unsafe fn from_ptr(dsa: *mut ffi::DSA) -> DSA { + DSA(dsa) + } + + /// Generate a DSA key pair + /// For more complicated key generation scenarios see the `DSAParams` type + pub fn generate(size: u32) -> Result<DSA, ErrorStack> { + let params = try!(DSAParams::with_size(size)); + params.generate() + } + + /// Reads a DSA private key from PEM formatted data. + pub fn private_key_from_pem(buf: &[u8]) -> Result<DSA, ErrorStack> { + ffi::init(); + let mem_bio = try!(MemBioSlice::new(buf)); + + unsafe { + let dsa = try_ssl_null!(ffi::PEM_read_bio_DSAPrivateKey(mem_bio.as_ptr(), + ptr::null_mut(), + None, + ptr::null_mut())); + let dsa = DSA(dsa); + assert!(dsa.has_private_key()); + Ok(dsa) + } + } + + /// Read a private key from PEM supplying a password callback to be invoked if the private key + /// is encrypted. + /// + /// The callback will be passed the password buffer and should return the number of characters + /// placed into the buffer. + pub fn private_key_from_pem_cb<F>(buf: &[u8], pass_cb: F) -> Result<DSA, ErrorStack> + where F: FnOnce(&mut [c_char]) -> usize + { + ffi::init(); + let mut cb = CallbackState::new(pass_cb); + let mem_bio = try!(MemBioSlice::new(buf)); + + unsafe { + let cb_ptr = &mut cb as *mut _ as *mut c_void; + let dsa = try_ssl_null!(ffi::PEM_read_bio_DSAPrivateKey(mem_bio.as_ptr(), + ptr::null_mut(), + Some(invoke_passwd_cb::<F>), + cb_ptr)); + let dsa = DSA(dsa); + assert!(dsa.has_private_key()); + Ok(dsa) + } + } + + /// Writes an DSA private key as unencrypted PEM formatted data + pub fn private_key_to_pem(&self) -> Result<Vec<u8>, ErrorStack> + { + assert!(self.has_private_key()); + let mem_bio = try!(MemBio::new()); + + unsafe { + try_ssl!(ffi::PEM_write_bio_DSAPrivateKey(mem_bio.as_ptr(), self.0, + ptr::null(), ptr::null_mut(), 0, + None, ptr::null_mut())) + }; + + Ok(mem_bio.get_buf().to_owned()) + } + + /// Reads an DSA public key from PEM formatted data. + pub fn public_key_from_pem(buf: &[u8]) -> Result<DSA, ErrorStack> + { + ffi::init(); + + let mem_bio = try!(MemBioSlice::new(buf)); + unsafe { + let dsa = try_ssl_null!(ffi::PEM_read_bio_DSA_PUBKEY(mem_bio.as_ptr(), + ptr::null_mut(), + None, + ptr::null_mut())); + Ok(DSA(dsa)) + } + } + + /// Writes an DSA public key as PEM formatted data + pub fn public_key_to_pem(&self) -> Result<Vec<u8>, ErrorStack> { + let mem_bio = try!(MemBio::new()); + unsafe { try_ssl!(ffi::PEM_write_bio_DSA_PUBKEY(mem_bio.as_ptr(), self.0)) }; + Ok(mem_bio.get_buf().to_owned()) + } + + pub fn size(&self) -> Option<u32> { + if self.q().is_some() { + unsafe { Some(ffi::DSA_size(self.0) as u32) } + } else { + None + } + } + + pub fn sign(&self, hash: hash::Type, message: &[u8]) -> Result<Vec<u8>, ErrorStack> { + let k_len = self.size().expect("DSA missing a q") as c_uint; + let mut sig = vec![0; k_len as usize]; + let mut sig_len = k_len; + assert!(self.has_private_key()); + + unsafe { + try_ssl!(ffi::DSA_sign(hash.as_nid() as c_int, + message.as_ptr(), + message.len() as c_int, + sig.as_mut_ptr(), + &mut sig_len, + self.0)); + sig.set_len(sig_len as usize); + sig.shrink_to_fit(); + Ok(sig) + } + } + + pub fn verify(&self, hash: hash::Type, message: &[u8], sig: &[u8]) -> Result<bool, ErrorStack> { + unsafe { + let result = ffi::DSA_verify(hash.as_nid() as c_int, + message.as_ptr(), + message.len() as c_int, + sig.as_ptr(), + sig.len() as c_int, + self.0); + + try_ssl_if!(result == -1); + Ok(result == 1) + } + } + + pub fn as_ptr(&self) -> *mut ffi::DSA { + self.0 + } + + pub fn p<'a>(&'a self) -> Option<BigNumRef<'a>> { + unsafe { + let p = (*self.0).p; + if p.is_null() { + None + } else { + Some(BigNumRef::from_ptr((*self.0).p)) + } + } + } + + pub fn q<'a>(&'a self) -> Option<BigNumRef<'a>> { + unsafe { + let q = (*self.0).q; + if q.is_null() { + None + } else { + Some(BigNumRef::from_ptr((*self.0).q)) + } + } + } + + pub fn g<'a>(&'a self) -> Option<BigNumRef<'a>> { + unsafe { + let g = (*self.0).g; + if g.is_null() { + None + } else { + Some(BigNumRef::from_ptr((*self.0).g)) + } + } + } + + pub fn has_public_key(&self) -> bool { + unsafe { !(*self.0).pub_key.is_null() } + } + + pub fn has_private_key(&self) -> bool { + unsafe { !(*self.0).priv_key.is_null() } + } +} + +impl fmt::Debug for DSA { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DSA") + } +} + +#[cfg(test)] +mod test { + use std::io::Write; + use libc::c_char; + + use super::*; + use crypto::hash::*; + + #[test] + pub fn test_generate() { + let key = DSA::generate(1024).unwrap(); + + key.public_key_to_pem().unwrap(); + key.private_key_to_pem().unwrap(); + + let input: Vec<u8> = (0..25).cycle().take(1024).collect(); + + let digest = { + let mut sha = Hasher::new(Type::SHA1).unwrap(); + sha.write_all(&input).unwrap(); + sha.finish().unwrap() + }; + + let sig = key.sign(Type::SHA1, &digest).unwrap(); + let verified = key.verify(Type::SHA1, &digest, &sig).unwrap(); + assert!(verified); + } + + #[test] + pub fn test_sign_verify() { + let input: Vec<u8> = (0..25).cycle().take(1024).collect(); + + let private_key = { + let key = include_bytes!("../../test/dsa.pem"); + DSA::private_key_from_pem(key).unwrap() + }; + + let public_key = { + let key = include_bytes!("../../test/dsa.pem.pub"); + DSA::public_key_from_pem(key).unwrap() + }; + + let digest = { + let mut sha = Hasher::new(Type::SHA1).unwrap(); + sha.write_all(&input).unwrap(); + sha.finish().unwrap() + }; + + let sig = private_key.sign(Type::SHA1, &digest).unwrap(); + let verified = public_key.verify(Type::SHA1, &digest, &sig).unwrap(); + assert!(verified); + } + + #[test] + pub fn test_sign_verify_fail() { + let input: Vec<u8> = (0..25).cycle().take(128).collect(); + let private_key = { + let key = include_bytes!("../../test/dsa.pem"); + DSA::private_key_from_pem(key).unwrap() + }; + + let public_key = { + let key = include_bytes!("../../test/dsa.pem.pub"); + DSA::public_key_from_pem(key).unwrap() + }; + + let digest = { + let mut sha = Hasher::new(Type::SHA1).unwrap(); + sha.write_all(&input).unwrap(); + sha.finish().unwrap() + }; + + let mut sig = private_key.sign(Type::SHA1, &digest).unwrap(); + // tamper with the sig this should cause a failure + let len = sig.len(); + sig[len / 2] = 0; + sig[len - 1] = 0; + if let Ok(true) = public_key.verify(Type::SHA1, &digest, &sig) { + panic!("Tampered with signatures should not verify!"); + } + } + + #[test] + pub fn test_password() { + let mut password_queried = false; + let key = include_bytes!("../../test/dsa-encrypted.pem"); + DSA::private_key_from_pem_cb(key, |password| { + password_queried = true; + password[0] = b'm' as c_char; + password[1] = b'y' as c_char; + password[2] = b'p' as c_char; + password[3] = b'a' as c_char; + password[4] = b's' as c_char; + password[5] = b's' as c_char; + 6 + }).unwrap(); + + assert!(password_queried); + } +} diff --git a/openssl/src/crypto/hash.rs b/openssl/src/crypto/hash.rs index 69d3a350..207a55f5 100644 --- a/openssl/src/crypto/hash.rs +++ b/openssl/src/crypto/hash.rs @@ -1,10 +1,12 @@ use libc::c_uint; -use std::iter::repeat; use std::io::prelude::*; use std::io; +use std::ptr; +use std::cmp; use ffi; -use crypto::HashTypeInternals; +use HashTypeInternals; +use error::ErrorStack; use nid::Nid; /// Message digest (hash) type. @@ -31,26 +33,8 @@ impl HashTypeInternals for Type { Type::RIPEMD160 => Nid::RIPEMD160, } } -} -impl Type { - /// Returns the length of the message digest. - #[inline] - pub fn md_len(&self) -> usize { - match *self { - Type::MD5 => 16, - Type::SHA1 => 20, - Type::SHA224 => 28, - Type::SHA256 => 32, - Type::SHA384 => 48, - Type::SHA512 => 64, - Type::RIPEMD160 => 20, - } - } - - /// Internal interface subject to removal. - #[inline] - pub fn evp_md(&self) -> *const ffi::EVP_MD { + fn evp_md(&self) -> *const ffi::EVP_MD { unsafe { match *self { Type::MD5 => ffi::EVP_md5(), @@ -84,21 +68,20 @@ use self::State::*; /// use openssl::crypto::hash::{hash, Type}; /// let data = b"\x42\xF4\x97\xE0"; /// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; -/// let res = hash(Type::MD5, data); +/// let res = hash(Type::MD5, data).unwrap(); /// assert_eq!(res, spec); /// ``` /// /// Use the `Write` trait to supply the input in chunks. /// /// ``` -/// use std::io::prelude::*; /// use openssl::crypto::hash::{Hasher, Type}; /// let data = [b"\x42\xF4", b"\x97\xE0"]; /// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; -/// let mut h = Hasher::new(Type::MD5); -/// h.write_all(data[0]); -/// h.write_all(data[1]); -/// let res = h.finish(); +/// let mut h = Hasher::new(Type::MD5).unwrap(); +/// h.update(data[0]).unwrap(); +/// h.update(data[1]).unwrap(); +/// let res = h.finish().unwrap(); /// assert_eq!(res, spec); /// ``` /// @@ -116,14 +99,10 @@ pub struct Hasher { impl Hasher { /// Creates a new `Hasher` with the specified hash type. - pub fn new(ty: Type) -> Hasher { + pub fn new(ty: Type) -> Result<Hasher, ErrorStack> { ffi::init(); - let ctx = unsafe { - let r = ffi::EVP_MD_CTX_create(); - assert!(!r.is_null()); - r - }; + let ctx = unsafe { try_ssl_null!(ffi::EVP_MD_CTX_create()) }; let md = ty.evp_md(); let mut h = Hasher { @@ -132,67 +111,60 @@ impl Hasher { type_: ty, state: Finalized, }; - h.init(); - h + try!(h.init()); + Ok(h) } - #[inline] - fn init(&mut self) { + fn init(&mut self) -> Result<(), ErrorStack> { match self.state { - Reset => return, + Reset => return Ok(()), Updated => { - self.finalize(); + try!(self.finish()); } Finalized => (), } - unsafe { - let r = ffi::EVP_DigestInit_ex(self.ctx, self.md, 0 as *const _); - assert_eq!(r, 1); - } + unsafe { try_ssl!(ffi::EVP_DigestInit_ex(self.ctx, self.md, 0 as *const _)); } self.state = Reset; + Ok(()) } - #[inline] - fn update(&mut self, data: &[u8]) { + /// Feeds data into the hasher. + pub fn update(&mut self, mut data: &[u8]) -> Result<(), ErrorStack> { if self.state == Finalized { - self.init(); + try!(self.init()); } - unsafe { - let r = ffi::EVP_DigestUpdate(self.ctx, data.as_ptr(), data.len() as c_uint); - assert_eq!(r, 1); + while !data.is_empty() { + let len = cmp::min(data.len(), c_uint::max_value() as usize); + unsafe { + try_ssl!(ffi::EVP_DigestUpdate(self.ctx, data.as_ptr(), len as c_uint)); + } + data = &data[len..]; } self.state = Updated; + Ok(()) } - #[inline] - fn finalize(&mut self) -> Vec<u8> { + /// Returns the hash of the data written since creation or + /// the last `finish` and resets the hasher. + pub fn finish(&mut self) -> Result<Vec<u8>, ErrorStack> { if self.state == Finalized { - self.init(); + try!(self.init()); } - let md_len = self.type_.md_len(); - let mut res: Vec<u8> = repeat(0).take(md_len).collect(); unsafe { - let mut len = 0; - let r = ffi::EVP_DigestFinal_ex(self.ctx, res.as_mut_ptr(), &mut len); + let mut len = ffi::EVP_MAX_MD_SIZE; + let mut res = vec![0; len as usize]; + try_ssl!(ffi::EVP_DigestFinal_ex(self.ctx, res.as_mut_ptr(), &mut len)); + res.truncate(len as usize); self.state = Finalized; - assert_eq!(len as usize, md_len); - assert_eq!(r, 1); + Ok(res) } - res - } - - /// Returns the hash of the data written since creation or - /// the last `finish` and resets the hasher. - #[inline] - pub fn finish(&mut self) -> Vec<u8> { - self.finalize() } } impl Write for Hasher { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - self.update(buf); + try!(self.update(buf)); Ok(buf.len()) } @@ -223,9 +195,7 @@ impl Drop for Hasher { fn drop(&mut self) { unsafe { if self.state != Finalized { - let mut buf: Vec<u8> = repeat(0).take(self.type_.md_len()).collect(); - let mut len = 0; - ffi::EVP_DigestFinal_ex(self.ctx, buf.as_mut_ptr(), &mut len); + drop(self.finish()); } ffi::EVP_MD_CTX_destroy(self.ctx); } @@ -233,9 +203,9 @@ impl Drop for Hasher { } /// Computes the hash of the `data` with the hash `t`. -pub fn hash(t: Type, data: &[u8]) -> Vec<u8> { - let mut h = Hasher::new(t); - let _ = h.write_all(data); +pub fn hash(t: Type, data: &[u8]) -> Result<Vec<u8>, ErrorStack> { + let mut h = try!(Hasher::new(t)); + try!(h.update(data)); h.finish() } @@ -246,13 +216,13 @@ mod tests { use std::io::prelude::*; fn hash_test(hashtype: Type, hashtest: &(&str, &str)) { - let res = hash(hashtype, &*hashtest.0.from_hex().unwrap()); + let res = hash(hashtype, &*hashtest.0.from_hex().unwrap()).unwrap(); assert_eq!(res.to_hex(), hashtest.1); } fn hash_recycle_test(h: &mut Hasher, hashtest: &(&str, &str)) { - let _ = h.write_all(&*hashtest.0.from_hex().unwrap()); - let res = h.finish(); + let _ = h.write_all(&*hashtest.0.from_hex().unwrap()).unwrap(); + let res = h.finish().unwrap(); assert_eq!(res.to_hex(), hashtest.1); } @@ -294,7 +264,7 @@ mod tests { #[test] fn test_md5_recycle() { - let mut h = Hasher::new(Type::MD5); + let mut h = Hasher::new(Type::MD5).unwrap(); for test in md5_tests.iter() { hash_recycle_test(&mut h, test); } @@ -302,11 +272,11 @@ mod tests { #[test] fn test_finish_twice() { - let mut h = Hasher::new(Type::MD5); - let _ = h.write_all(&*md5_tests[6].0.from_hex().unwrap()); - let _ = h.finish(); - let res = h.finish(); - let null = hash(Type::MD5, &[]); + let mut h = Hasher::new(Type::MD5).unwrap(); + h.write_all(&*md5_tests[6].0.from_hex().unwrap()).unwrap(); + h.finish().unwrap(); + let res = h.finish().unwrap(); + let null = hash(Type::MD5, &[]).unwrap(); assert_eq!(res, null); } @@ -316,26 +286,26 @@ mod tests { let inp = md5_tests[i].0.from_hex().unwrap(); assert!(inp.len() > 2); let p = inp.len() / 2; - let h0 = Hasher::new(Type::MD5); + let h0 = Hasher::new(Type::MD5).unwrap(); println!("Clone a new hasher"); let mut h1 = h0.clone(); - let _ = h1.write_all(&inp[..p]); + h1.write_all(&inp[..p]).unwrap(); { println!("Clone an updated hasher"); let mut h2 = h1.clone(); - let _ = h2.write_all(&inp[p..]); - let res = h2.finish(); + h2.write_all(&inp[p..]).unwrap(); + let res = h2.finish().unwrap(); assert_eq!(res.to_hex(), md5_tests[i].1); } - let _ = h1.write_all(&inp[p..]); - let res = h1.finish(); + h1.write_all(&inp[p..]).unwrap(); + let res = h1.finish().unwrap(); assert_eq!(res.to_hex(), md5_tests[i].1); println!("Clone a finished hasher"); let mut h3 = h1.clone(); - let _ = h3.write_all(&*md5_tests[i + 1].0.from_hex().unwrap()); - let res = h3.finish(); + h3.write_all(&*md5_tests[i + 1].0.from_hex().unwrap()).unwrap(); + let res = h3.finish().unwrap(); assert_eq!(res.to_hex(), md5_tests[i + 1].1); } diff --git a/openssl/src/crypto/hmac.rs b/openssl/src/crypto/hmac.rs index 143c7b75..857be339 100644 --- a/openssl/src/crypto/hmac.rs +++ b/openssl/src/crypto/hmac.rs @@ -14,13 +14,15 @@ // use libc::{c_int, c_uint}; -use std::iter::repeat; use std::io; use std::io::prelude::*; +use std::cmp; +use ffi; +use HashTypeInternals; use crypto::hash::Type; -use ffi; -use ffi_extras; +use error::ErrorStack; +use c_helpers; #[derive(PartialEq, Copy, Clone)] enum State { @@ -33,6 +35,8 @@ use self::State::*; /// Provides HMAC computation. /// +/// Requires the `hmac` feature. +/// /// # Examples /// /// Calculate a HMAC in one go. @@ -43,34 +47,32 @@ use self::State::*; /// let key = b"Jefe"; /// let data = b"what do ya want for nothing?"; /// let spec = b"\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38"; -/// let res = hmac(Type::MD5, key, data); +/// let res = hmac(Type::MD5, key, data).unwrap(); /// assert_eq!(res, spec); /// ``` /// /// Use the `Write` trait to supply the input in chunks. /// /// ``` -/// use std::io::prelude::*; /// use openssl::crypto::hash::Type; /// use openssl::crypto::hmac::HMAC; /// let key = b"Jefe"; /// let data: &[&[u8]] = &[b"what do ya ", b"want for nothing?"]; /// let spec = b"\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38"; -/// let mut h = HMAC::new(Type::MD5, &*key); -/// h.write_all(data[0]); -/// h.write_all(data[1]); -/// let res = h.finish(); +/// let mut h = HMAC::new(Type::MD5, &*key).unwrap(); +/// h.update(data[0]).unwrap(); +/// h.update(data[1]).unwrap(); +/// let res = h.finish().unwrap(); /// assert_eq!(res, spec); /// ``` pub struct HMAC { ctx: ffi::HMAC_CTX, - type_: Type, state: State, } impl HMAC { /// Creates a new `HMAC` with the specified hash type using the `key`. - pub fn new(ty: Type, key: &[u8]) -> HMAC { + pub fn new(ty: Type, key: &[u8]) -> Result<HMAC, ErrorStack> { ffi::init(); let ctx = unsafe { @@ -82,89 +84,81 @@ impl HMAC { let mut h = HMAC { ctx: ctx, - type_: ty, state: Finalized, }; - h.init_once(md, key); - h + try!(h.init_once(md, key)); + Ok(h) } - #[inline] - fn init_once(&mut self, md: *const ffi::EVP_MD, key: &[u8]) { + fn init_once(&mut self, md: *const ffi::EVP_MD, key: &[u8]) -> Result<(), ErrorStack> { unsafe { - let r = ffi_extras::HMAC_Init_ex(&mut self.ctx, - key.as_ptr(), - key.len() as c_int, - md, - 0 as *const _); - assert_eq!(r, 1); + try_ssl!(c_helpers::rust_HMAC_Init_ex(&mut self.ctx, + key.as_ptr() as *const _, + key.len() as c_int, + md, + 0 as *mut _)); } self.state = Reset; + Ok(()) } - #[inline] - fn init(&mut self) { + fn init(&mut self) -> Result<(), ErrorStack> { match self.state { - Reset => return, + Reset => return Ok(()), Updated => { - self.finalize(); + try!(self.finish()); } Finalized => (), } // If the key and/or md is not supplied it's reused from the last time // avoiding redundant initializations unsafe { - let r = ffi_extras::HMAC_Init_ex(&mut self.ctx, - 0 as *const _, - 0, - 0 as *const _, - 0 as *const _); - assert_eq!(r, 1); + try_ssl!(c_helpers::rust_HMAC_Init_ex(&mut self.ctx, + 0 as *const _, + 0, + 0 as *const _, + 0 as *mut _)); } self.state = Reset; + Ok(()) } - #[inline] - fn update(&mut self, data: &[u8]) { + pub fn update(&mut self, mut data: &[u8]) -> Result<(), ErrorStack> { if self.state == Finalized { - self.init(); + try!(self.init()); } - unsafe { - let r = ffi_extras::HMAC_Update(&mut self.ctx, data.as_ptr(), data.len() as c_uint); - assert_eq!(r, 1); + while !data.is_empty() { + let len = cmp::min(data.len(), c_uint::max_value() as usize); + unsafe { + try_ssl!(c_helpers::rust_HMAC_Update(&mut self.ctx, data.as_ptr(), len as c_uint)); + } + data = &data[len..]; } self.state = Updated; + Ok(()) } - #[inline] - fn finalize(&mut self) -> Vec<u8> { + /// Returns the hash of the data written since creation or + /// the last `finish` and resets the hasher. + pub fn finish(&mut self) -> Result<Vec<u8>, ErrorStack> { if self.state == Finalized { - self.init(); + try!(self.init()); } - let md_len = self.type_.md_len(); - let mut res: Vec<u8> = repeat(0).take(md_len).collect(); unsafe { - let mut len = 0; - let r = ffi_extras::HMAC_Final(&mut self.ctx, res.as_mut_ptr(), &mut len); + let mut len = ffi::EVP_MAX_MD_SIZE; + let mut res = vec![0; len as usize]; + try_ssl!(c_helpers::rust_HMAC_Final(&mut self.ctx, res.as_mut_ptr(), &mut len)); + res.truncate(len as usize); self.state = Finalized; - assert_eq!(len as usize, md_len); - assert_eq!(r, 1); + Ok(res) } - res - } - - /// Returns the hash of the data written since creation or - /// the last `finish` and resets the hasher. - #[inline] - pub fn finish(&mut self) -> Vec<u8> { - self.finalize() } } impl Write for HMAC { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - self.update(buf); + try!(self.update(buf)); Ok(buf.len()) } @@ -173,17 +167,18 @@ impl Write for HMAC { } } +#[cfg(feature = "hmac_clone")] impl Clone for HMAC { + /// Requires the `hmac_clone` feature. fn clone(&self) -> HMAC { let mut ctx: ffi::HMAC_CTX; unsafe { ctx = ::std::mem::uninitialized(); - let r = ffi_extras::HMAC_CTX_copy(&mut ctx, &self.ctx); + let r = ffi::HMAC_CTX_copy(&mut ctx, &self.ctx); assert_eq!(r, 1); } HMAC { ctx: ctx, - type_: self.type_, state: self.state, } } @@ -193,9 +188,7 @@ impl Drop for HMAC { fn drop(&mut self) { unsafe { if self.state != Finalized { - let mut buf: Vec<u8> = repeat(0).take(self.type_.md_len()).collect(); - let mut len = 0; - ffi_extras::HMAC_Final(&mut self.ctx, buf.as_mut_ptr(), &mut len); + drop(self.finish()); } ffi::HMAC_CTX_cleanup(&mut self.ctx); } @@ -203,9 +196,9 @@ impl Drop for HMAC { } /// Computes the HMAC of the `data` with the hash `t` and `key`. -pub fn hmac(t: Type, key: &[u8], data: &[u8]) -> Vec<u8> { - let mut h = HMAC::new(t, key); - let _ = h.write_all(data); +pub fn hmac(t: Type, key: &[u8], data: &[u8]) -> Result<Vec<u8>, ErrorStack> { + let mut h = try!(HMAC::new(t, key)); + try!(h.update(data)); h.finish() } @@ -220,14 +213,14 @@ mod tests { fn test_hmac(ty: Type, tests: &[(Vec<u8>, Vec<u8>, Vec<u8>)]) { for &(ref key, ref data, ref res) in tests.iter() { - assert_eq!(hmac(ty, &**key, &**data), *res); + assert_eq!(hmac(ty, &**key, &**data).unwrap(), *res); } } fn test_hmac_recycle(h: &mut HMAC, test: &(Vec<u8>, Vec<u8>, Vec<u8>)) { let &(_, ref data, ref res) = test; - let _ = h.write_all(&**data); - assert_eq!(h.finish(), *res); + h.write_all(&**data).unwrap(); + assert_eq!(h.finish().unwrap(), *res); } #[test] @@ -273,7 +266,7 @@ mod tests { .to_vec(), "6f630fad67cda0ee1fb1f562db3aa53e".from_hex().unwrap())]; - let mut h = HMAC::new(MD5, &*tests[0].0); + let mut h = HMAC::new(MD5, &*tests[0].0).unwrap(); for i in 0..100usize { let test = &tests[i % 2]; test_hmac_recycle(&mut h, test); @@ -287,15 +280,16 @@ mod tests { b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd".from_hex().unwrap()); - let mut h = HMAC::new(Type::MD5, &*test.0); - let _ = h.write_all(&*test.1); - let _ = h.finish(); - let res = h.finish(); - let null = hmac(Type::MD5, &*test.0, &[]); + let mut h = HMAC::new(Type::MD5, &*test.0).unwrap(); + h.write_all(&*test.1).unwrap(); + h.finish().unwrap(); + let res = h.finish().unwrap(); + let null = hmac(Type::MD5, &*test.0, &[]).unwrap(); assert_eq!(res, null); } #[test] + #[cfg(feature = "hmac_clone")] fn test_clone() { let tests: [(Vec<u8>, Vec<u8>, Vec<u8>); 2] = [(repeat(0xaa_u8).take(80).collect(), @@ -307,26 +301,26 @@ mod tests { .to_vec(), "6f630fad67cda0ee1fb1f562db3aa53e".from_hex().unwrap())]; let p = tests[0].0.len() / 2; - let h0 = HMAC::new(Type::MD5, &*tests[0].0); + let h0 = HMAC::new(Type::MD5, &*tests[0].0).unwrap(); println!("Clone a new hmac"); let mut h1 = h0.clone(); - let _ = h1.write_all(&tests[0].1[..p]); + h1.write_all(&tests[0].1[..p]).unwrap(); { println!("Clone an updated hmac"); let mut h2 = h1.clone(); - let _ = h2.write_all(&tests[0].1[p..]); - let res = h2.finish(); + h2.write_all(&tests[0].1[p..]).unwrap(); + let res = h2.finish().unwrap(); assert_eq!(res, tests[0].2); } - let _ = h1.write_all(&tests[0].1[p..]); - let res = h1.finish(); + h1.write_all(&tests[0].1[p..]).unwrap(); + let res = h1.finish().unwrap(); assert_eq!(res, tests[0].2); println!("Clone a finished hmac"); let mut h3 = h1.clone(); - let _ = h3.write_all(&*tests[1].1); - let res = h3.finish(); + h3.write_all(&*tests[1].1).unwrap(); + let res = h3.finish().unwrap(); assert_eq!(res, tests[1].2); } @@ -373,7 +367,7 @@ mod tests { .to_vec(), "e8e99d0f45237d786d6bbaa7965c7808bbff1a91".from_hex().unwrap())]; - let mut h = HMAC::new(SHA1, &*tests[0].0); + let mut h = HMAC::new(SHA1, &*tests[0].0).unwrap(); for i in 0..100usize { let test = &tests[i % 2]; test_hmac_recycle(&mut h, test); @@ -399,11 +393,11 @@ mod tests { .to_vec())]; for (&(ref key, ref data), res) in tests.iter().zip(results.iter()) { - assert_eq!(hmac(ty, &**key, &**data), *res); + assert_eq!(hmac(ty, &**key, &**data).unwrap(), *res); } // recycle test - let mut h = HMAC::new(ty, &*tests[5].0); + let mut h = HMAC::new(ty, &*tests[5].0).unwrap(); for i in 0..100usize { let test = &tests[4 + i % 2]; let tup = (test.0.clone(), test.1.clone(), results[4 + i % 2].clone()); diff --git a/openssl/src/crypto/mod.rs b/openssl/src/crypto/mod.rs index 95b27022..93aba9eb 100644 --- a/openssl/src/crypto/mod.rs +++ b/openssl/src/crypto/mod.rs @@ -14,9 +14,8 @@ // limitations under the License. // -use nid::Nid; - pub mod hash; +#[cfg(feature = "hmac")] pub mod hmac; pub mod pkcs5; pub mod pkey; @@ -24,9 +23,5 @@ pub mod rand; pub mod symm; pub mod memcmp; pub mod rsa; - -mod symm_internal; - -trait HashTypeInternals { - fn as_nid(&self) -> Nid; -} +pub mod dsa; +mod util; diff --git a/openssl/src/crypto/pkcs5.rs b/openssl/src/crypto/pkcs5.rs index 0ba005cc..ef84fbe1 100644 --- a/openssl/src/crypto/pkcs5.rs +++ b/openssl/src/crypto/pkcs5.rs @@ -1,10 +1,11 @@ use libc::c_int; -use std::ptr::null; +use std::ptr; +use ffi; -use crypto::symm_internal::evpc; +use HashTypeInternals; use crypto::hash; use crypto::symm; -use ffi; +use error::ErrorStack; #[derive(Clone, Eq, PartialEq, Hash, Debug)] pub struct KeyIvPair { @@ -16,125 +17,102 @@ pub struct KeyIvPair { /// /// If specified `salt` must be 8 bytes in length. /// -/// If the total key and IV length is less than 16 bytes and MD5 is used then -/// the algorithm is compatible with the key derivation algorithm from PKCS#5 +/// If the total key and IV length is less than 16 bytes and MD5 is used then +/// the algorithm is compatible with the key derivation algorithm from PKCS#5 /// v1.5 or PBKDF1 from PKCS#5 v2.0. /// -/// New applications should not use this and instead use `pbkdf2_hmac_sha1` or +/// New applications should not use this and instead use `pbkdf2_hmac_sha1` or /// another more modern key derivation algorithm. pub fn evp_bytes_to_key_pbkdf1_compatible(typ: symm::Type, message_digest_type: hash::Type, data: &[u8], salt: Option<&[u8]>, count: u32) - -> KeyIvPair { - + -> Result<KeyIvPair, ErrorStack> { unsafe { - let salt_ptr = match salt { Some(salt) => { assert_eq!(salt.len(), ffi::PKCS5_SALT_LEN as usize); salt.as_ptr() } - None => null(), + None => ptr::null(), }; ffi::init(); - let (evp, keylen, _) = evpc(typ); - - let message_digest = message_digest_type.evp_md(); - - let mut key = vec![0; keylen as usize]; - let mut iv = vec![0; keylen as usize]; + let typ = typ.as_ptr(); + let message_digest_type = message_digest_type.evp_md(); + + let len = ffi::EVP_BytesToKey(typ, + message_digest_type, + salt_ptr, + data.as_ptr(), + data.len() as c_int, + count as c_int, + ptr::null_mut(), + ptr::null_mut()); + if len == 0 { + return Err(ErrorStack::get()); + } + let mut key = vec![0; len as usize]; + let mut iv = vec![0; len as usize]; - let ret: c_int = ffi::EVP_BytesToKey(evp, - message_digest, - salt_ptr, - data.as_ptr(), - data.len() as c_int, - count as c_int, - key.as_mut_ptr(), - iv.as_mut_ptr()); - assert!(ret == keylen as c_int); + try_ssl!(ffi::EVP_BytesToKey(typ, + message_digest_type, + salt_ptr, + data.as_ptr(), + data.len() as c_int, + count as c_int, + key.as_mut_ptr(), + iv.as_mut_ptr())); - KeyIvPair { key: key, iv: iv } + Ok(KeyIvPair { key: key, iv: iv }) } } /// Derives a key from a password and salt using the PBKDF2-HMAC-SHA1 algorithm. -pub fn pbkdf2_hmac_sha1(pass: &str, salt: &[u8], iter: usize, keylen: usize) -> Vec<u8> { +pub fn pbkdf2_hmac_sha1(pass: &[u8], + salt: &[u8], + iter: usize, + keylen: usize) + -> Result<Vec<u8>, ErrorStack> { unsafe { - assert!(iter >= 1); - assert!(keylen >= 1); - - let mut out = Vec::with_capacity(keylen); + let mut out = vec![0; keylen]; 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 { - panic!(); - } - - out.set_len(keylen); - - out + try_ssl!(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())); + Ok(out) } } -/// Derives a key from a password and salt using the PBKDF2-HMAC-SHA256 algorithm. -#[cfg(feature = "pkcs5_pbkdf2_hmac")] -pub fn pbkdf2_hmac_sha256(pass: &str, salt: &[u8], iter: usize, keylen: usize) -> Vec<u8> { - pbkdf2_hmac_sha(pass, salt, iter, unsafe { ffi::EVP_sha256() }, keylen) -} - -/// Derives a key from a password and salt using the PBKDF2-HMAC-SHA512 algorithm. -#[cfg(feature = "pkcs5_pbkdf2_hmac")] -pub fn pbkdf2_hmac_sha512(pass: &str, salt: &[u8], iter: usize, keylen: usize) -> Vec<u8> { - pbkdf2_hmac_sha(pass, salt, iter, unsafe { ffi::EVP_sha512() }, keylen) -} - /// Derives a key from a password and salt using the PBKDF2-HMAC algorithm with a digest function. #[cfg(feature = "pkcs5_pbkdf2_hmac")] -fn pbkdf2_hmac_sha(pass: &str, +pub fn pbkdf2_hmac(pass: &[u8], salt: &[u8], iter: usize, - digest: *const ffi::EVP_MD, + hash: hash::Type, keylen: usize) - -> Vec<u8> { + -> Result<Vec<u8>, ErrorStack> { unsafe { - assert!(iter >= 1); - assert!(keylen >= 1); - - let mut out = Vec::with_capacity(keylen); - + let mut out = vec![0; keylen]; ffi::init(); - - let r = ffi::PKCS5_PBKDF2_HMAC(pass.as_ptr(), - pass.len() as c_int, - salt.as_ptr(), - salt.len() as c_int, - iter as c_int, - digest, - keylen as c_int, - out.as_mut_ptr()); - - if r != 1 { - panic!(); - } - - out.set_len(keylen); - - out + try_ssl!(ffi::PKCS5_PBKDF2_HMAC(pass.as_ptr(), + pass.len() as c_int, + salt.as_ptr(), + salt.len() as c_int, + iter as c_int, + hash.evp_md(), + keylen as c_int, + out.as_mut_ptr())); + Ok(out) } } @@ -147,36 +125,36 @@ mod tests { // http://tools.ietf.org/html/draft-josefsson-pbkdf2-test-vectors-06 #[test] fn test_pbkdf2_hmac_sha1() { - assert_eq!(super::pbkdf2_hmac_sha1("password", "salt".as_bytes(), 1, 20), + assert_eq!(super::pbkdf2_hmac_sha1(b"password", b"salt", 1, 20).unwrap(), vec![0x0c_u8, 0x60_u8, 0xc8_u8, 0x0f_u8, 0x96_u8, 0x1f_u8, 0x0e_u8, 0x71_u8, 0xf3_u8, 0xa9_u8, 0xb5_u8, 0x24_u8, 0xaf_u8, 0x60_u8, 0x12_u8, 0x06_u8, 0x2f_u8, 0xe0_u8, 0x37_u8, 0xa6_u8]); - assert_eq!(super::pbkdf2_hmac_sha1("password", "salt".as_bytes(), 2, 20), + assert_eq!(super::pbkdf2_hmac_sha1(b"password", b"salt", 2, 20).unwrap(), vec![0xea_u8, 0x6c_u8, 0x01_u8, 0x4d_u8, 0xc7_u8, 0x2d_u8, 0x6f_u8, 0x8c_u8, 0xcd_u8, 0x1e_u8, 0xd9_u8, 0x2a_u8, 0xce_u8, 0x1d_u8, 0x41_u8, 0xf0_u8, 0xd8_u8, 0xde_u8, 0x89_u8, 0x57_u8]); - assert_eq!(super::pbkdf2_hmac_sha1("password", "salt".as_bytes(), 4096, 20), + assert_eq!(super::pbkdf2_hmac_sha1(b"password", b"salt", 4096, 20).unwrap(), vec![0x4b_u8, 0x00_u8, 0x79_u8, 0x01_u8, 0xb7_u8, 0x65_u8, 0x48_u8, 0x9a_u8, 0xbe_u8, 0xad_u8, 0x49_u8, 0xd9_u8, 0x26_u8, 0xf7_u8, 0x21_u8, 0xd0_u8, 0x65_u8, 0xa4_u8, 0x29_u8, 0xc1_u8]); - assert_eq!(super::pbkdf2_hmac_sha1("password", "salt".as_bytes(), 16777216, 20), + assert_eq!(super::pbkdf2_hmac_sha1(b"password", b"salt", 16777216, 20).unwrap(), vec![0xee_u8, 0xfe_u8, 0x3d_u8, 0x61_u8, 0xcd_u8, 0x4d_u8, 0xa4_u8, 0xe4_u8, 0xe9_u8, 0x94_u8, 0x5b_u8, 0x3d_u8, 0x6b_u8, 0xa2_u8, 0x15_u8, 0x8c_u8, 0x26_u8, 0x34_u8, 0xe9_u8, 0x84_u8]); - assert_eq!(super::pbkdf2_hmac_sha1("passwordPASSWORDpassword", - "saltSALTsaltSALTsaltSALTsaltSALTsalt".as_bytes(), + assert_eq!(super::pbkdf2_hmac_sha1(b"passwordPASSWORDpassword", + b"saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, - 25), + 25).unwrap(), vec![0x3d_u8, 0x2e_u8, 0xec_u8, 0x4f_u8, 0xe4_u8, 0x1c_u8, 0x84_u8, 0x9b_u8, 0x80_u8, 0xc8_u8, 0xd8_u8, 0x36_u8, 0x62_u8, 0xc0_u8, 0xe4_u8, 0x4a_u8, 0x8b_u8, 0x29_u8, 0x1a_u8, 0x96_u8, 0x4c_u8, 0xf2_u8, 0xf0_u8, 0x70_u8, 0x38_u8]); - assert_eq!(super::pbkdf2_hmac_sha1("pass\x00word", "sa\x00lt".as_bytes(), 4096, 16), + assert_eq!(super::pbkdf2_hmac_sha1(b"pass\x00word", b"sa\x00lt", 4096, 16).unwrap(), vec![0x56_u8, 0xfa_u8, 0x6a_u8, 0xa7_u8, 0x55_u8, 0x48_u8, 0x09_u8, 0x9d_u8, 0xcc_u8, 0x37_u8, 0xd7_u8, 0xf0_u8, 0x34_u8, 0x25_u8, 0xe0_u8, 0xc3_u8]); } @@ -186,11 +164,11 @@ mod tests { #[test] #[cfg(feature = "pkcs5_pbkdf2_hmac")] fn test_pbkdf2_hmac_sha256() { - assert_eq!(super::pbkdf2_hmac_sha256("passwd", "salt".as_bytes(), 1, 16), + assert_eq!(super::pbkdf2_hmac(b"passwd", b"salt", 1, hash::Type::SHA256, 16).unwrap(), vec![0x55_u8, 0xac_u8, 0x04_u8, 0x6e_u8, 0x56_u8, 0xe3_u8, 0x08_u8, 0x9f_u8, 0xec_u8, 0x16_u8, 0x91_u8, 0xc2_u8, 0x25_u8, 0x44_u8, 0xb6_u8, 0x05_u8]); - assert_eq!(super::pbkdf2_hmac_sha256("Password", "NaCl".as_bytes(), 80000, 16), + assert_eq!(super::pbkdf2_hmac(b"Password", b"NaCl", 80000, hash::Type::SHA256, 16).unwrap(), vec![0x4d_u8, 0xdc_u8, 0xd8_u8, 0xf6_u8, 0x0b_u8, 0x98_u8, 0xbe_u8, 0x21_u8, 0x83_u8, 0x0c_u8, 0xee_u8, 0x5e_u8, 0xf2_u8, 0x27_u8, 0x01_u8, 0xf9_u8]); } @@ -200,7 +178,7 @@ mod tests { #[test] #[cfg(feature = "pkcs5_pbkdf2_hmac")] fn test_pbkdf2_hmac_sha512() { - assert_eq!(super::pbkdf2_hmac_sha512("password", "NaCL".as_bytes(), 1, 64), + assert_eq!(super::pbkdf2_hmac(b"password", b"NaCL", 1, hash::Type::SHA512, 64).unwrap(), vec![0x73_u8, 0xde_u8, 0xcf_u8, 0xa5_u8, 0x8a_u8, 0xa2_u8, 0xe8_u8, 0x4f_u8, 0x94_u8, 0x77_u8, 0x1a_u8, 0x75_u8, 0x73_u8, 0x6b_u8, 0xb8_u8, 0x8b_u8, 0xd3_u8, 0xc7_u8, 0xb3_u8, 0x82_u8, 0x70_u8, 0xcf_u8, 0xb5_u8, 0x0c_u8, @@ -210,7 +188,7 @@ mod tests { 0x60_u8, 0x60_u8, 0xa0_u8, 0x9f_u8, 0x76_u8, 0x41_u8, 0x5e_u8, 0x9f_u8, 0x71_u8, 0xea_u8, 0x47_u8, 0xf9_u8, 0xe9_u8, 0x06_u8, 0x43_u8, 0x06_u8]); - assert_eq!(super::pbkdf2_hmac_sha512("pass\0word", "sa\0lt".as_bytes(), 1, 64), + assert_eq!(super::pbkdf2_hmac(b"pass\0word", b"sa\0lt", 1, hash::Type::SHA512, 64).unwrap(), vec![0x71_u8, 0xa0_u8, 0xec_u8, 0x84_u8, 0x2a_u8, 0xbd_u8, 0x5c_u8, 0x67_u8, 0x8b_u8, 0xcf_u8, 0xd1_u8, 0x45_u8, 0xf0_u8, 0x9d_u8, 0x83_u8, 0x52_u8, 0x2f_u8, 0x93_u8, 0x36_u8, 0x15_u8, 0x60_u8, 0x56_u8, 0x3c_u8, 0x4d_u8, @@ -220,10 +198,11 @@ mod tests { 0xb2_u8, 0x0f_u8, 0x06_u8, 0xbc_u8, 0x53_u8, 0x5e_u8, 0x5a_u8, 0xb5_u8, 0x44_u8, 0x0d_u8, 0xf7_u8, 0xe8_u8, 0x78_u8, 0x29_u8, 0x6f_u8, 0xa7_u8]); - assert_eq!(super::pbkdf2_hmac_sha512("passwordPASSWORDpassword", - "salt\0\0\0".as_bytes(), - 50, - 64), + assert_eq!(super::pbkdf2_hmac(b"passwordPASSWORDpassword", + b"salt\0\0\0", + 50, + hash::Type::SHA512, + 64).unwrap(), vec![0x01_u8, 0x68_u8, 0x71_u8, 0xa4_u8, 0xc4_u8, 0xb7_u8, 0x5f_u8, 0x96_u8, 0x85_u8, 0x7f_u8, 0xd2_u8, 0xb9_u8, 0xf8_u8, 0xca_u8, 0x28_u8, 0x02_u8, 0x3b_u8, 0x30_u8, 0xee_u8, 0x2a_u8, 0x39_u8, 0xf5_u8, 0xad_u8, 0xca_u8, @@ -257,7 +236,7 @@ mod tests { hash::Type::SHA1, &data, Some(&salt), - 1), + 1).unwrap(), super::KeyIvPair { key: expected_key, iv: expected_iv, diff --git a/openssl/src/crypto/pkey.rs b/openssl/src/crypto/pkey.rs index c4111860..a2a6f9c1 100644 --- a/openssl/src/crypto/pkey.rs +++ b/openssl/src/crypto/pkey.rs @@ -1,260 +1,107 @@ -use libc::{c_int, c_uint, c_ulong}; -use std::io; -use std::io::prelude::*; -use std::iter::repeat; -use std::mem; +use libc::{c_void, c_char}; use std::ptr; -use bio::MemBio; - -use crypto::HashTypeInternals; -use crypto::hash; -use crypto::hash::Type as HashType; +use std::mem; use ffi; -use ssl::error::{SslError, StreamError}; -use crypto::rsa::RSA; - -#[derive(Copy, Clone)] -pub enum Parts { - Neither, - Public, - Both, -} - -/// Represents a role an asymmetric key might be appropriate for. -#[derive(Copy, Clone)] -pub enum Role { - Encrypt, - Decrypt, - Sign, - Verify, -} - -/// Type of encryption padding to use. -#[derive(Copy, Clone)] -pub enum EncryptionPadding { - OAEP, - PKCS1v15, -} -fn openssl_padding_code(padding: EncryptionPadding) -> c_int { - match padding { - EncryptionPadding::OAEP => 4, - EncryptionPadding::PKCS1v15 => 1, - } -} +use bio::{MemBio, MemBioSlice}; +use crypto::rsa::RSA; +use error::ErrorStack; +use crypto::util::{CallbackState, invoke_passwd_cb}; -pub struct PKey { - evp: *mut ffi::EVP_PKEY, - parts: Parts, -} +pub struct PKey(*mut ffi::EVP_PKEY); unsafe impl Send for PKey {} unsafe impl Sync for PKey {} /// Represents a public key, optionally with a private key attached. impl PKey { - pub fn new() -> PKey { + /// Create a new `PKey` containing an RSA key. + pub fn from_rsa(rsa: RSA) -> Result<PKey, ErrorStack> { unsafe { - ffi::init(); - - PKey { - evp: ffi::EVP_PKEY_new(), - parts: Parts::Neither, - } + let evp = try_ssl_null!(ffi::EVP_PKEY_new()); + let pkey = PKey(evp); + try_ssl!(ffi::EVP_PKEY_assign(pkey.0, ffi::EVP_PKEY_RSA, rsa.as_ptr() as *mut _)); + mem::forget(rsa); + Ok(pkey) } } - pub fn from_handle(handle: *mut ffi::EVP_PKEY, parts: Parts) -> PKey { - ffi::init(); - assert!(!handle.is_null()); - - PKey { - evp: handle, - parts: parts, - } + pub unsafe fn from_ptr(handle: *mut ffi::EVP_PKEY) -> PKey { + PKey(handle) } /// Reads private key from PEM, takes ownership of handle - pub fn private_key_from_pem<R>(reader: &mut R) -> Result<PKey, SslError> - where R: Read - { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); - + pub fn private_key_from_pem(buf: &[u8]) -> Result<PKey, ErrorStack> { + ffi::init(); + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { - let evp = try_ssl_null!(ffi::PEM_read_bio_PrivateKey(mem_bio.get_handle(), + let evp = try_ssl_null!(ffi::PEM_read_bio_PrivateKey(mem_bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut())); - Ok(PKey { - evp: evp as *mut ffi::EVP_PKEY, - parts: Parts::Both, - }) - } - } - - /// Reads public key from PEM, takes ownership of handle - pub fn public_key_from_pem<R>(reader: &mut R) -> Result<PKey, SslError> - where R: Read - { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); - - unsafe { - let evp = try_ssl_null!(ffi::PEM_read_bio_PUBKEY(mem_bio.get_handle(), - ptr::null_mut(), - None, - ptr::null_mut())); - Ok(PKey { - evp: evp as *mut ffi::EVP_PKEY, - parts: Parts::Public, - }) - } - } - - /// Reads an RSA private key from PEM, takes ownership of handle - pub fn private_rsa_key_from_pem<R>(reader: &mut R) -> Result<PKey, SslError> - where R: Read - { - let rsa = try!(RSA::private_key_from_pem(reader)); - unsafe { - let evp = try_ssl_null!(ffi::EVP_PKEY_new()); - try_ssl!(ffi::EVP_PKEY_set1_RSA(evp, rsa.as_ptr())); - - Ok(PKey { - evp: evp, - parts: Parts::Public, - }) + Ok(PKey::from_ptr(evp)) } } - /// Reads an RSA public key from PEM, takes ownership of handle - pub fn public_rsa_key_from_pem<R>(reader: &mut R) -> Result<PKey, SslError> - where R: Read + /// Read a private key from PEM, supplying a password callback to be invoked if the private key + /// is encrypted. + /// + /// The callback will be passed the password buffer and should return the number of characters + /// placed into the buffer. + pub fn private_key_from_pem_cb<F>(buf: &[u8], pass_cb: F) -> Result<PKey, ErrorStack> + where F: FnOnce(&mut [c_char]) -> usize { - let rsa = try!(RSA::public_key_from_pem(reader)); - unsafe { - let evp = try_ssl_null!(ffi::EVP_PKEY_new()); - try_ssl!(ffi::EVP_PKEY_set1_RSA(evp, rsa.as_ptr())); - - Ok(PKey { - evp: evp, - parts: Parts::Public, - }) - } - } - - fn _tostr(&self, f: unsafe extern "C" fn(*mut ffi::RSA, *const *mut u8) -> c_int) -> Vec<u8> { - unsafe { - 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 = repeat(0u8).take(len as usize).collect::<Vec<_>>(); - - let r = f(rsa, &s.as_mut_ptr()); - ffi::RSA_free(rsa); - - s.truncate(r as usize); - s - } - } - - fn _fromstr(&mut self, - s: &[u8], - f: unsafe extern "C" fn(*const *mut ffi::RSA, *const *const u8, c_uint) - -> *mut ffi::RSA) - -> bool { + ffi::init(); + let mut cb = CallbackState::new(pass_cb); + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { - let rsa = ptr::null_mut(); - f(&rsa, &s.as_ptr(), s.len() as c_uint); - if !rsa.is_null() { - ffi::EVP_PKEY_set1_RSA(self.evp, rsa) == 1 - } else { - false - } + let evp = try_ssl_null!(ffi::PEM_read_bio_PrivateKey(mem_bio.as_ptr(), + ptr::null_mut(), + Some(invoke_passwd_cb::<F>), + &mut cb as *mut _ as *mut c_void)); + Ok(PKey::from_ptr(evp)) } } - pub fn gen(&mut self, keysz: usize) { + /// Reads public key from PEM, takes ownership of handle + pub fn public_key_from_pem(buf: &[u8]) -> Result<PKey, ErrorStack> { + ffi::init(); + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { - let rsa = ffi::RSA_generate_key(keysz as c_int, - 65537 as c_ulong, - ptr::null(), - ptr::null()); - - // XXX: 6 == NID_rsaEncryption - ffi::EVP_PKEY_assign(self.evp, 6 as c_int, mem::transmute(rsa)); - - self.parts = Parts::Both; + let evp = try_ssl_null!(ffi::PEM_read_bio_PUBKEY(mem_bio.as_ptr(), + ptr::null_mut(), + None, + ptr::null_mut())); + Ok(PKey::from_ptr(evp)) } } /// assign RSA key to this pkey - pub fn set_rsa(&mut self, rsa: &RSA) { + pub fn set_rsa(&mut self, rsa: &RSA) -> Result<(), ErrorStack> { unsafe { // this needs to be a reference as the set1_RSA ups the reference count let rsa_ptr = rsa.as_ptr(); - if ffi::EVP_PKEY_set1_RSA(self.evp, rsa_ptr) == 1 { - if rsa.has_e() && rsa.has_n() { - self.parts = Parts::Public; - } - } + try_ssl!(ffi::EVP_PKEY_set1_RSA(self.0, rsa_ptr)); + Ok(()) } } - /// get a reference to the interal RSA key for direct access to the key components - pub fn get_rsa(&self) -> RSA { + /// Get a reference to the interal RSA key for direct access to the key components + pub fn get_rsa(&self) -> Result<RSA, ErrorStack> { unsafe { - let evp_pkey: *mut ffi::EVP_PKEY = self.evp; + let rsa = try_ssl_null!(ffi::EVP_PKEY_get1_RSA(self.0)); // this is safe as the ffi increments a reference counter to the internal key - RSA::from_raw(ffi::EVP_PKEY_get1_RSA(evp_pkey)) - } - } - - /** - * Returns a DER serialized form of the public key, suitable for load_pub(). - */ - pub fn save_pub(&self) -> Vec<u8> { - self._tostr(ffi::i2d_RSA_PUBKEY) - } - - /** - * Loads a DER serialized form of the public key, as produced by save_pub(). - */ - pub fn load_pub(&mut self, s: &[u8]) { - if self._fromstr(s, ffi::d2i_RSA_PUBKEY) { - self.parts = Parts::Public; - } - } - - /** - * Returns a serialized form of the public and private keys, suitable for - * load_priv(). - */ - pub fn save_priv(&self) -> Vec<u8> { - 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]) { - if self._fromstr(s, ffi::d2i_RSAPrivateKey) { - self.parts = Parts::Both; + Ok(RSA::from_ptr(rsa)) } } /// Stores private key as a PEM // FIXME: also add password and encryption - pub fn write_pem<W: Write>(&self, - writer: &mut W /* , password: Option<String> */) - -> Result<(), SslError> { - let mut mem_bio = try!(MemBio::new()); + pub fn private_key_to_pem(&self) -> Result<Vec<u8>, ErrorStack> { + let mem_bio = try!(MemBio::new()); unsafe { - try_ssl!(ffi::PEM_write_bio_PrivateKey(mem_bio.get_handle(), - self.evp, + try_ssl!(ffi::PEM_write_bio_PrivateKey(mem_bio.as_ptr(), + self.0, ptr::null(), ptr::null_mut(), -1, @@ -262,635 +109,58 @@ impl PKey { ptr::null_mut())); } - let mut buf = vec![]; - try!(mem_bio.read_to_end(&mut buf).map_err(StreamError)); - writer.write_all(&buf).map_err(StreamError) + Ok(mem_bio.get_buf().to_owned()) } /// Stores public key as a PEM - pub fn write_pub_pem<W: Write>(&self, - writer: &mut W /* , password: Option<String> */) - -> Result<(), SslError> { - let mut mem_bio = try!(MemBio::new()); - unsafe { try_ssl!(ffi::PEM_write_bio_PUBKEY(mem_bio.get_handle(), self.evp)) } - let mut buf = vec![]; - try!(mem_bio.read_to_end(&mut buf).map_err(StreamError)); - writer.write_all(&buf).map_err(StreamError) - } - - /** - * Returns the size of the public key modulus. - */ - pub fn size(&self) -> usize { - unsafe { - let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - if rsa.is_null() { - 0 - } else { - ffi::RSA_size(rsa) as usize - } - } - } - - /** - * Returns whether this pkey object can perform the specified role. - */ - pub fn can(&self, r: Role) -> bool { - match r { - Role::Encrypt => { - match self.parts { - Parts::Neither => false, - _ => true, - } - } - Role::Verify => { - match self.parts { - Parts::Neither => false, - _ => true, - } - } - Role::Decrypt => { - match self.parts { - Parts::Both => true, - _ => false, - } - } - Role::Sign => { - match self.parts { - Parts::Both => true, - _ => false, - } - } - } - } - - /** - * Returns the maximum amount of data that can be encrypted by an encrypt() - * call. - */ - pub fn max_data(&self) -> usize { - unsafe { - let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - if rsa.is_null() { - return 0; - } - let len = ffi::RSA_size(rsa); - - // 41 comes from RSA_public_encrypt(3) for OAEP - len as usize - 41 - } - } - - pub fn private_encrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec<u8> { - unsafe { - let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - if rsa.is_null() { - panic!("Could not get RSA key for encryption"); - } - let len = ffi::RSA_size(rsa); - - assert!(s.len() < self.max_data()); - - let mut r = repeat(0u8).take(len as usize + 1).collect::<Vec<_>>(); - - let rv = ffi::RSA_private_encrypt(s.len() as c_int, - s.as_ptr(), - r.as_mut_ptr(), - rsa, - openssl_padding_code(padding)); - - if rv < 0 as c_int { - // println!("{:?}", SslError::get()); - vec![] - } else { - r.truncate(rv as usize); - r - } - } - } - - pub fn public_encrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec<u8> { - unsafe { - let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - if rsa.is_null() { - panic!("Could not get RSA key for encryption"); - } - let len = ffi::RSA_size(rsa); - - assert!(s.len() < self.max_data()); - - let mut r = repeat(0u8).take(len as usize + 1).collect::<Vec<_>>(); - - let rv = ffi::RSA_public_encrypt(s.len() as c_int, - s.as_ptr(), - r.as_mut_ptr(), - rsa, - openssl_padding_code(padding)); - - if rv < 0 as c_int { - vec![] - } else { - r.truncate(rv as usize); - r - } - } - } - - pub fn private_decrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec<u8> { - unsafe { - let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - if rsa.is_null() { - panic!("Could not get RSA key for decryption"); - } - let len = ffi::RSA_size(rsa); - - assert_eq!(s.len() as c_int, ffi::RSA_size(rsa)); - - let mut r = repeat(0u8).take(len as usize + 1).collect::<Vec<_>>(); - - let rv = ffi::RSA_private_decrypt(s.len() as c_int, - s.as_ptr(), - r.as_mut_ptr(), - rsa, - openssl_padding_code(padding)); - - if rv < 0 as c_int { - vec![] - } else { - r.truncate(rv as usize); - r - } - } - } - - pub fn public_decrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec<u8> { - unsafe { - let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - if rsa.is_null() { - panic!("Could not get RSA key for decryption"); - } - let len = ffi::RSA_size(rsa); - - assert_eq!(s.len() as c_int, ffi::RSA_size(rsa)); - - let mut r = repeat(0u8).take(len as usize + 1).collect::<Vec<_>>(); - - let rv = ffi::RSA_public_decrypt(s.len() as c_int, - s.as_ptr(), - r.as_mut_ptr(), - rsa, - openssl_padding_code(padding)); - - if rv < 0 as c_int { - vec![] - } else { - r.truncate(rv as usize); - r - } - } - } - - /** - * Encrypts data with the public key, 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.public_encrypt_with_padding(s, EncryptionPadding::OAEP) - } - - /** - * Encrypts data with the public key, using provided padding, returning the encrypted data. The - * supplied data must not be larger than max_data(). - */ - pub fn encrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec<u8> { - self.public_encrypt_with_padding(s, padding) - } - - /** - * Encrypts data with the public key, using OAEP padding, returning the encrypted data. The - * supplied data must not be larger than max_data(). - */ - pub fn public_encrypt(&self, s: &[u8]) -> Vec<u8> { - self.public_encrypt_with_padding(s, EncryptionPadding::OAEP) - } - - /** - * Decrypts data with the public key, using PKCS1v15 padding, returning the decrypted data. - */ - pub fn public_decrypt(&self, s: &[u8]) -> Vec<u8> { - self.public_decrypt_with_padding(s, EncryptionPadding::PKCS1v15) - } - - /** - * Decrypts data with the private key, expecting OAEP padding, returning the decrypted data. - */ - pub fn decrypt(&self, s: &[u8]) -> Vec<u8> { - self.private_decrypt_with_padding(s, EncryptionPadding::OAEP) - } - - /** - * Decrypts data with the private key, using provided padding, returning the encrypted data. The - * supplied data must not be larger than max_data(). - */ - pub fn decrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec<u8> { - self.private_decrypt_with_padding(s, padding) - } - - /** - * Decrypts data with the private key, expecting OAEP padding, returning the decrypted data. - */ - pub fn private_decrypt(&self, s: &[u8]) -> Vec<u8> { - self.private_decrypt_with_padding(s, EncryptionPadding::OAEP) - } - - /** - * Encrypts data with the private key, using PKCS1v15 padding, returning the encrypted data. The - * supplied data must not be larger than max_data(). - */ - pub fn private_encrypt(&self, s: &[u8]) -> Vec<u8> { - self.private_encrypt_with_padding(s, EncryptionPadding::PKCS1v15) - } - - /** - * Signs data, using OpenSSL's default scheme and adding sha256 ASN.1 information to the - * signature. - * The bytes to sign must be the result of a sha256 hashing; - * returns the signature. - */ - 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 the SHA256 hash of a - * message. - * Returns true if the signature is valid, and false otherwise. - */ - pub fn verify(&self, h: &[u8], s: &[u8]) -> bool { - self.verify_with_hash(h, s, HashType::SHA256) - } - - /** - * Signs data, using OpenSSL's default scheme and add ASN.1 information for the given hash type to the - * signature. - * The bytes to sign must be the result of this type of hashing; - * returns the signature. - */ - pub fn sign_with_hash(&self, s: &[u8], hash: hash::Type) -> Vec<u8> { - unsafe { - let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - if rsa.is_null() { - panic!("Could not get RSA key for signing"); - } - let len = ffi::RSA_size(rsa); - let mut r = repeat(0u8).take(len as usize + 1).collect::<Vec<_>>(); - - let mut len = 0; - let rv = ffi::RSA_sign(hash.as_nid() as c_int, - s.as_ptr(), - s.len() as c_uint, - r.as_mut_ptr(), - &mut len, - rsa); - - if rv < 0 as c_int { - vec![] - } else { - r.truncate(len as usize); - r - } - } - } - - pub fn verify_with_hash(&self, h: &[u8], s: &[u8], hash: hash::Type) -> bool { - unsafe { - let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - if rsa.is_null() { - panic!("Could not get RSA key for verification"); - } - - let rv = ffi::RSA_verify(hash.as_nid() as c_int, - h.as_ptr(), - h.len() as c_uint, - s.as_ptr(), - s.len() as c_uint, - rsa); - - rv == 1 as c_int - } + pub fn public_key_to_pem(&self) -> Result<Vec<u8>, ErrorStack> { + let mem_bio = try!(MemBio::new()); + unsafe { try_ssl!(ffi::PEM_write_bio_PUBKEY(mem_bio.as_ptr(), self.0)) } + Ok(mem_bio.get_buf().to_owned()) } - pub unsafe fn get_handle(&self) -> *mut ffi::EVP_PKEY { - return self.evp; + pub fn as_ptr(&self) -> *mut ffi::EVP_PKEY { + return self.0; } pub fn public_eq(&self, other: &PKey) -> bool { - unsafe { ffi::EVP_PKEY_cmp(self.evp, other.evp) == 1 } + unsafe { ffi::EVP_PKEY_cmp(self.0, other.0) == 1 } } } impl Drop for PKey { fn drop(&mut self) { unsafe { - ffi::EVP_PKEY_free(self.evp); - } - } -} - -impl Clone for PKey { - fn clone(&self) -> Self { - let mut pkey = PKey::from_handle(unsafe { ffi::EVP_PKEY_new() }, self.parts); - // copy by encoding to DER and back - match self.parts { - Parts::Public => { - pkey.load_pub(&self.save_pub()[..]); - } - Parts::Both => { - pkey.load_priv(&self.save_priv()[..]); - } - Parts::Neither => {} + ffi::EVP_PKEY_free(self.0); } - pkey } } #[cfg(test)] mod tests { - use std::path::Path; - use std::fs::File; - use crypto::hash::Type::{MD5, SHA1}; - use crypto::rsa::RSA; - - #[test] - fn test_gen_pub() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - k0.gen(512); - k1.load_pub(&k0.save_pub()); - assert_eq!(k0.save_pub(), k1.save_pub()); - assert!(k0.public_eq(&k1)); - assert_eq!(k0.size(), k1.size()); - 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] - fn test_gen_priv() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - k0.gen(512); - k1.load_priv(&k0.save_priv()); - assert_eq!(k0.save_priv(), k1.save_priv()); - assert!(k0.public_eq(&k1)); - assert_eq!(k0.size(), k1.size()); - 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] fn test_private_key_from_pem() { - let key_path = Path::new("test/key.pem"); - let mut file = File::open(&key_path) - .ok() - .expect("Failed to open `test/key.pem`"); - - super::PKey::private_key_from_pem(&mut file).unwrap(); + let key = include_bytes!("../../test/key.pem"); + super::PKey::private_key_from_pem(key).unwrap(); } #[test] fn test_public_key_from_pem() { - let key_path = Path::new("test/key.pem.pub"); - let mut file = File::open(&key_path) - .ok() - .expect("Failed to open `test/key.pem.pub`"); - - super::PKey::public_key_from_pem(&mut file).unwrap(); - } - - #[test] - fn test_private_rsa_key_from_pem() { - let key_path = Path::new("test/key.pem"); - let mut file = File::open(&key_path) - .ok() - .expect("Failed to open `test/key.pem`"); - - super::PKey::private_rsa_key_from_pem(&mut file).unwrap(); - } - - #[test] - fn test_public_rsa_key_from_pem() { - let key_path = Path::new("test/key.pem.pub"); - let mut file = File::open(&key_path) - .ok() - .expect("Failed to open `test/key.pem.pub`"); - - super::PKey::public_rsa_key_from_pem(&mut file).unwrap(); - } - - #[test] - fn test_private_encrypt() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - let msg = vec![0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; - k0.gen(512); - k1.load_pub(&k0.save_pub()); - let emsg = k0.private_encrypt(&msg); - let dmsg = k1.public_decrypt(&emsg); - assert!(msg == dmsg); - } - - #[test] - fn test_public_encrypt() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - let msg = vec![0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; - k0.gen(512); - k1.load_pub(&k0.save_pub()); - let emsg = k1.public_encrypt(&msg); - let dmsg = k0.private_decrypt(&emsg); - assert!(msg == dmsg); - } - - #[test] - fn test_public_encrypt_pkcs() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - let msg = vec![0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; - k0.gen(512); - k1.load_pub(&k0.save_pub()); - let emsg = k1.public_encrypt_with_padding(&msg, super::EncryptionPadding::PKCS1v15); - let dmsg = k0.private_decrypt_with_padding(&emsg, super::EncryptionPadding::PKCS1v15); - assert!(msg == dmsg); - } - - #[test] - fn test_sign() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - let msg = vec![0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; - k0.gen(512); - k1.load_pub(&k0.save_pub()); - let sig = k0.sign(&msg); - let rv = k1.verify(&msg, &sig); - assert!(rv == true); - } - - #[test] - fn test_sign_hashes() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - let msg = vec![0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; - k0.gen(512); - k1.load_pub(&k0.save_pub()); - - let sig = k0.sign_with_hash(&msg, MD5); - - assert!(k1.verify_with_hash(&msg, &sig, MD5)); - assert!(!k1.verify_with_hash(&msg, &sig, SHA1)); - } - - #[test] - fn test_eq() { - let mut k0 = super::PKey::new(); - let mut p0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - let mut p1 = super::PKey::new(); - k0.gen(512); - k1.gen(512); - p0.load_pub(&k0.save_pub()); - p1.load_pub(&k1.save_pub()); - - assert!(k0.public_eq(&k0)); - assert!(k1.public_eq(&k1)); - assert!(p0.public_eq(&p0)); - assert!(p1.public_eq(&p1)); - assert!(k0.public_eq(&p0)); - assert!(k1.public_eq(&p1)); - - assert!(!k0.public_eq(&k1)); - assert!(!p0.public_eq(&p1)); - assert!(!k0.public_eq(&p1)); - assert!(!p0.public_eq(&k1)); + let key = include_bytes!("../../test/key.pem.pub"); + super::PKey::public_key_from_pem(key).unwrap(); } #[test] fn test_pem() { - let key_path = Path::new("test/key.pem"); - let mut file = File::open(&key_path) - .ok() - .expect("Failed to open `test/key.pem`"); - - let key = super::PKey::private_key_from_pem(&mut file).unwrap(); + let key = include_bytes!("../../test/key.pem"); + let key = super::PKey::private_key_from_pem(key).unwrap(); - let mut priv_key = Vec::new(); - let mut pub_key = Vec::new(); - - key.write_pem(&mut priv_key).unwrap(); - key.write_pub_pem(&mut pub_key).unwrap(); + let priv_key = key.private_key_to_pem().unwrap(); + let pub_key = key.public_key_to_pem().unwrap(); // As a super-simple verification, just check that the buffers contain // the `PRIVATE KEY` or `PUBLIC KEY` strings. assert!(priv_key.windows(11).any(|s| s == b"PRIVATE KEY")); assert!(pub_key.windows(10).any(|s| s == b"PUBLIC KEY")); } - - #[test] - fn test_public_key_from_raw() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - let msg = vec![0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; - - k0.gen(512); - let sig = k0.sign(&msg); - - let r0 = k0.get_rsa(); - let r1 = RSA::from_public_components(r0.n().expect("n"), r0.e().expect("e")).expect("r1"); - k1.set_rsa(&r1); - - assert!(k1.can(super::Role::Encrypt)); - assert!(!k1.can(super::Role::Decrypt)); - assert!(k1.can(super::Role::Verify)); - assert!(!k1.can(super::Role::Sign)); - - let rv = k1.verify(&msg, &sig); - assert!(rv == true); - } - - #[test] - #[should_panic(expected = "Could not get RSA key for encryption")] - fn test_nokey_encrypt() { - let mut pkey = super::PKey::new(); - pkey.load_pub(&[]); - pkey.encrypt(&[]); - } - - #[test] - #[should_panic(expected = "Could not get RSA key for decryption")] - fn test_nokey_decrypt() { - let mut pkey = super::PKey::new(); - pkey.load_priv(&[]); - pkey.decrypt(&[]); - } - - #[test] - #[should_panic(expected = "Could not get RSA key for signing")] - fn test_nokey_sign() { - let mut pkey = super::PKey::new(); - pkey.load_priv(&[]); - pkey.sign(&[]); - } - - #[test] - #[should_panic(expected = "Could not get RSA key for verification")] - fn test_nokey_verify() { - let mut pkey = super::PKey::new(); - pkey.load_pub(&[]); - pkey.verify(&[], &[]); - } - - #[test] - fn test_pkey_clone_creates_copy() { - let mut pkey = super::PKey::new(); - pkey.gen(512); - let old_pkey_n = pkey.get_rsa().n().unwrap(); - - let mut pkey2 = pkey.clone(); - pkey2.gen(512); - - assert!(old_pkey_n == pkey.get_rsa().n().unwrap()); - } - - #[test] - fn test_pkey_clone_copies_private() { - let mut pkey = super::PKey::new(); - pkey.gen(512); - - let pkey2 = pkey.clone(); - - assert!(pkey.get_rsa().q().unwrap() == pkey2.get_rsa().q().unwrap()); - } - - #[test] - fn test_pkey_clone_copies_public() { - let mut pkey = super::PKey::new(); - pkey.gen(512); - let mut pub_key = super::PKey::new(); - pub_key.load_pub(&pkey.save_pub()[..]); - - let pub_key2 = pub_key.clone(); - - assert!(pub_key.get_rsa().n().unwrap() == pub_key2.get_rsa().n().unwrap()); - } } diff --git a/openssl/src/crypto/rand.rs b/openssl/src/crypto/rand.rs index ba57a8a1..519449e9 100644 --- a/openssl/src/crypto/rand.rs +++ b/openssl/src/crypto/rand.rs @@ -1,19 +1,13 @@ use libc::c_int; use ffi; +use error::ErrorStack; -pub fn rand_bytes(len: usize) -> Vec<u8> { +pub fn rand_bytes(buf: &mut [u8]) -> Result<(), ErrorStack> { unsafe { - let mut out = Vec::with_capacity(len); - 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); - - out + assert!(buf.len() <= c_int::max_value() as usize); + try_ssl_if!(ffi::RAND_bytes(buf.as_mut_ptr(), buf.len() as c_int) != 1); + Ok(()) } } @@ -23,7 +17,7 @@ mod tests { #[test] fn test_rand_bytes() { - let bytes = rand_bytes(32); - println!("{:?}", bytes); + let mut buf = [0; 32]; + rand_bytes(&mut buf).unwrap(); } } diff --git a/openssl/src/crypto/rsa.rs b/openssl/src/crypto/rsa.rs index 52b8590e..feb66a6f 100644 --- a/openssl/src/crypto/rsa.rs +++ b/openssl/src/crypto/rsa.rs @@ -1,14 +1,15 @@ use ffi; use std::fmt; -use ssl::error::{SslError, StreamError}; use std::ptr; -use std::io::{self, Read, Write}; -use libc::c_int; +use std::mem; +use libc::{c_int, c_void, c_char, c_ulong}; -use bn::BigNum; -use bio::MemBio; -use crypto::HashTypeInternals; +use bn::{BigNum, BigNumRef}; +use bio::{MemBio, MemBioSlice}; +use error::ErrorStack; +use HashTypeInternals; use crypto::hash; +use crypto::util::{CallbackState, invoke_passwd_cb}; pub struct RSA(*mut ffi::RSA); @@ -23,11 +24,13 @@ impl Drop for RSA { impl RSA { /// only useful for associating the key material directly with the key, it's safer to use /// the supplied load and save methods for DER formatted keys. - pub fn from_public_components(n: BigNum, e: BigNum) -> Result<RSA, SslError> { + pub fn from_public_components(n: BigNum, e: BigNum) -> Result<RSA, ErrorStack> { unsafe { let rsa = try_ssl_null!(ffi::RSA_new()); - (*rsa).n = n.into_raw(); - (*rsa).e = e.into_raw(); + (*rsa).n = n.as_ptr(); + (*rsa).e = e.as_ptr(); + mem::forget(n); + mem::forget(e); Ok(RSA(rsa)) } } @@ -40,35 +43,53 @@ impl RSA { dp: BigNum, dq: BigNum, qi: BigNum) - -> Result<RSA, SslError> { + -> Result<RSA, ErrorStack> { unsafe { let rsa = try_ssl_null!(ffi::RSA_new()); - (*rsa).n = n.into_raw(); - (*rsa).e = e.into_raw(); - (*rsa).d = d.into_raw(); - (*rsa).p = p.into_raw(); - (*rsa).q = q.into_raw(); - (*rsa).dmp1 = dp.into_raw(); - (*rsa).dmq1 = dq.into_raw(); - (*rsa).iqmp = qi.into_raw(); + (*rsa).n = n.as_ptr(); + (*rsa).e = e.as_ptr(); + (*rsa).d = d.as_ptr(); + (*rsa).p = p.as_ptr(); + (*rsa).q = q.as_ptr(); + (*rsa).dmp1 = dp.as_ptr(); + (*rsa).dmq1 = dq.as_ptr(); + (*rsa).iqmp = qi.as_ptr(); + mem::forget(n); + mem::forget(e); + mem::forget(d); + mem::forget(p); + mem::forget(q); + mem::forget(dp); + mem::forget(dq); + mem::forget(qi); Ok(RSA(rsa)) } } - /// the caller should assert that the rsa pointer is valid. - pub unsafe fn from_raw(rsa: *mut ffi::RSA) -> RSA { + pub unsafe fn from_ptr(rsa: *mut ffi::RSA) -> RSA { RSA(rsa) } - /// Reads an RSA private key from PEM formatted data. - pub fn private_key_from_pem<R>(reader: &mut R) -> Result<RSA, SslError> - where R: Read - { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); + /// Generates a public/private key pair with the specified size. + /// + /// The public exponent will be 65537. + pub fn generate(bits: u32) -> Result<RSA, ErrorStack> { + unsafe { + let rsa = try_ssl_null!(ffi::RSA_new()); + let rsa = RSA(rsa); + let e = try!(BigNum::new_from(ffi::RSA_F4 as c_ulong)); + + try_ssl!(ffi::RSA_generate_key_ex(rsa.0, bits as c_int, e.as_ptr(), ptr::null_mut())); + Ok(rsa) + } + } + + /// Reads an RSA private key from PEM formatted data. + pub fn private_key_from_pem(buf: &[u8]) -> Result<RSA, ErrorStack> { + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { - let rsa = try_ssl_null!(ffi::PEM_read_bio_RSAPrivateKey(mem_bio.get_handle(), + let rsa = try_ssl_null!(ffi::PEM_read_bio_RSAPrivateKey(mem_bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut())); @@ -76,40 +97,29 @@ impl RSA { } } - /// Writes an RSA private key as unencrypted PEM formatted data - pub fn private_key_to_pem<W>(&self, writer: &mut W) -> Result<(), SslError> - where W: Write + /// Reads an RSA private key from PEM formatted data and supplies a password callback. + pub fn private_key_from_pem_cb<F>(buf: &[u8], pass_cb: F) -> Result<RSA, ErrorStack> + where F: FnOnce(&mut [c_char]) -> usize { - let mut mem_bio = try!(MemBio::new()); + let mut cb = CallbackState::new(pass_cb); + let mem_bio = try!(MemBioSlice::new(buf)); - let result = unsafe { - ffi::PEM_write_bio_RSAPrivateKey(mem_bio.get_handle(), - self.0, - ptr::null(), - ptr::null_mut(), - 0, - None, - ptr::null_mut()) - }; - - if result == 1 { - try!(io::copy(&mut mem_bio, writer).map_err(StreamError)); + unsafe { + let cb_ptr = &mut cb as *mut _ as *mut c_void; + let rsa = try_ssl_null!(ffi::PEM_read_bio_RSAPrivateKey(mem_bio.as_ptr(), + ptr::null_mut(), + Some(invoke_passwd_cb::<F>), + cb_ptr)); - Ok(()) - } else { - Err(SslError::OpenSslErrors(vec![])) + Ok(RSA(rsa)) } } /// Reads an RSA public key from PEM formatted data. - pub fn public_key_from_pem<R>(reader: &mut R) -> Result<RSA, SslError> - where R: Read - { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); - + pub fn public_key_from_pem(buf: &[u8]) -> Result<RSA, ErrorStack> { + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { - let rsa = try_ssl_null!(ffi::PEM_read_bio_RSA_PUBKEY(mem_bio.get_handle(), + let rsa = try_ssl_null!(ffi::PEM_read_bio_RSA_PUBKEY(mem_bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut())); @@ -117,97 +127,127 @@ impl RSA { } } - /// Writes an RSA public key as PEM formatted data - pub fn public_key_to_pem<W>(&self, writer: &mut W) -> Result<(), SslError> - where W: Write - { - let mut mem_bio = try!(MemBio::new()); + /// Writes an RSA private key as unencrypted PEM formatted data + pub fn private_key_to_pem(&self) -> Result<Vec<u8>, ErrorStack> { + let mem_bio = try!(MemBio::new()); + + unsafe { + try_ssl!(ffi::PEM_write_bio_RSAPrivateKey(mem_bio.as_ptr(), + self.0, + ptr::null(), + ptr::null_mut(), + 0, + None, + ptr::null_mut())); + } + Ok(mem_bio.get_buf().to_owned()) + } - let result = unsafe { ffi::PEM_write_bio_RSA_PUBKEY(mem_bio.get_handle(), self.0) }; + /// Writes an RSA public key as PEM formatted data + pub fn public_key_to_pem(&self) -> Result<Vec<u8>, ErrorStack> { + let mem_bio = try!(MemBio::new()); - if result == 1 { - try!(io::copy(&mut mem_bio, writer).map_err(StreamError)); + unsafe { + try_ssl!(ffi::PEM_write_bio_RSA_PUBKEY(mem_bio.as_ptr(), self.0)) + }; - Ok(()) - } else { - Err(SslError::OpenSslErrors(vec![])) - } + Ok(mem_bio.get_buf().to_owned()) } - pub fn size(&self) -> Result<u32, SslError> { - if self.has_n() { - unsafe { Ok(ffi::RSA_size(self.0) as u32) } + pub fn size(&self) -> Option<u32> { + if self.n().is_some() { + unsafe { Some(ffi::RSA_size(self.0) as u32) } } else { - Err(SslError::OpenSslErrors(vec![])) + None } } - pub fn sign(&self, hash: hash::Type, message: &[u8]) -> Result<Vec<u8>, SslError> { - let k_len = try!(self.size()); - let mut sig = vec![0;k_len as usize]; + pub fn sign(&self, hash: hash::Type, message: &[u8]) -> Result<Vec<u8>, ErrorStack> { + let k_len = self.size().expect("RSA missing an n"); + let mut sig = vec![0; k_len as usize]; let mut sig_len = k_len; unsafe { - let result = ffi::RSA_sign(hash.as_nid() as c_int, - message.as_ptr(), - message.len() as u32, - sig.as_mut_ptr(), - &mut sig_len, - self.0); + try_ssl!(ffi::RSA_sign(hash.as_nid() as c_int, + message.as_ptr(), + message.len() as u32, + sig.as_mut_ptr(), + &mut sig_len, + self.0)); assert!(sig_len == k_len); - - if result == 1 { - Ok(sig) - } else { - Err(SslError::OpenSslErrors(vec![])) - } + Ok(sig) } } - pub fn verify(&self, hash: hash::Type, message: &[u8], sig: &[u8]) -> Result<bool, SslError> { + pub fn verify(&self, hash: hash::Type, message: &[u8], sig: &[u8]) -> Result<(), ErrorStack> { unsafe { - let result = ffi::RSA_verify(hash.as_nid() as c_int, - message.as_ptr(), - message.len() as u32, - sig.as_ptr(), - sig.len() as u32, - self.0); - - Ok(result == 1) + try_ssl!(ffi::RSA_verify(hash.as_nid() as c_int, + message.as_ptr(), + message.len() as u32, + sig.as_ptr(), + sig.len() as u32, + self.0)); } + Ok(()) } pub fn as_ptr(&self) -> *mut ffi::RSA { self.0 } - // The following getters are unsafe, since BigNum::new_from_ffi fails upon null pointers - pub fn n(&self) -> Result<BigNum, SslError> { - unsafe { BigNum::new_from_ffi((*self.0).n) } - } - - pub fn has_n(&self) -> bool { - unsafe { !(*self.0).n.is_null() } - } - - pub fn d(&self) -> Result<BigNum, SslError> { - unsafe { BigNum::new_from_ffi((*self.0).d) } + pub fn n<'a>(&'a self) -> Option<BigNumRef<'a>> { + unsafe { + let n = (*self.0).n; + if n.is_null() { + None + } else { + Some(BigNumRef::from_ptr(n)) + } + } } - pub fn e(&self) -> Result<BigNum, SslError> { - unsafe { BigNum::new_from_ffi((*self.0).e) } + pub fn d<'a>(&self) -> Option<BigNumRef<'a>> { + unsafe { + let d = (*self.0).d; + if d.is_null() { + None + } else { + Some(BigNumRef::from_ptr(d)) + } + } } - pub fn has_e(&self) -> bool { - unsafe { !(*self.0).e.is_null() } + pub fn e<'a>(&'a self) -> Option<BigNumRef<'a>> { + unsafe { + let e = (*self.0).e; + if e.is_null() { + None + } else { + Some(BigNumRef::from_ptr(e)) + } + } } - pub fn p(&self) -> Result<BigNum, SslError> { - unsafe { BigNum::new_from_ffi((*self.0).p) } + pub fn p<'a>(&'a self) -> Option<BigNumRef<'a>> { + unsafe { + let p = (*self.0).p; + if p.is_null() { + None + } else { + Some(BigNumRef::from_ptr(p)) + } + } } - pub fn q(&self) -> Result<BigNum, SslError> { - unsafe { BigNum::new_from_ffi((*self.0).q) } + pub fn q<'a>(&'a self) -> Option<BigNumRef<'a>> { + unsafe { + let q = (*self.0).q; + if q.is_null() { + None + } else { + Some(BigNumRef::from_ptr(q)) + } + } } } @@ -219,8 +259,9 @@ impl fmt::Debug for RSA { #[cfg(test)] mod test { - use std::fs::File; use std::io::Write; + use libc::c_char; + use super::*; use crypto::hash::*; @@ -252,12 +293,12 @@ mod test { #[test] pub fn test_sign() { - let mut buffer = File::open("test/rsa.pem").unwrap(); - let private_key = RSA::private_key_from_pem(&mut buffer).unwrap(); + let key = include_bytes!("../../test/rsa.pem"); + let private_key = RSA::private_key_from_pem(key).unwrap(); - let mut sha = Hasher::new(Type::SHA256); + let mut sha = Hasher::new(Type::SHA256).unwrap(); sha.write_all(&signing_input_rs256()).unwrap(); - let digest = sha.finish(); + let digest = sha.finish().unwrap(); let result = private_key.sign(Type::SHA256, &digest).unwrap(); @@ -266,15 +307,31 @@ mod test { #[test] pub fn test_verify() { - let mut buffer = File::open("test/rsa.pem.pub").unwrap(); - let public_key = RSA::public_key_from_pem(&mut buffer).unwrap(); + let key = include_bytes!("../../test/rsa.pem.pub"); + let public_key = RSA::public_key_from_pem(key).unwrap(); - let mut sha = Hasher::new(Type::SHA256); + let mut sha = Hasher::new(Type::SHA256).unwrap(); sha.write_all(&signing_input_rs256()).unwrap(); - let digest = sha.finish(); + let digest = sha.finish().unwrap(); - let result = public_key.verify(Type::SHA256, &digest, &signature_rs256()).unwrap(); + assert!(public_key.verify(Type::SHA256, &digest, &signature_rs256()).is_ok()); + } - assert!(result); + #[test] + pub fn test_password() { + let mut password_queried = false; + let key = include_bytes!("../../test/rsa-encrypted.pem"); + RSA::private_key_from_pem_cb(key, |password| { + password_queried = true; + password[0] = b'm' as c_char; + password[1] = b'y' as c_char; + password[2] = b'p' as c_char; + password[3] = b'a' as c_char; + password[4] = b's' as c_char; + password[5] = b's' as c_char; + 6 + }).unwrap(); + + assert!(password_queried); } } diff --git a/openssl/src/crypto/symm.rs b/openssl/src/crypto/symm.rs index 935980f3..93764e4d 100644 --- a/openssl/src/crypto/symm.rs +++ b/openssl/src/crypto/symm.rs @@ -1,9 +1,10 @@ -use std::iter::repeat; +use std::cmp; +use std::ptr; use libc::c_int; - -use crypto::symm_internal::evpc; use ffi; +use error::ErrorStack; + #[derive(Copy, Clone)] pub enum Mode { Encrypt, @@ -43,94 +44,184 @@ pub enum Type { RC4_128, } +impl Type { + pub fn as_ptr(&self) -> *const ffi::EVP_CIPHER { + unsafe { + match *self { + Type::AES_128_ECB => ffi::EVP_aes_128_ecb(), + Type::AES_128_CBC => ffi::EVP_aes_128_cbc(), + #[cfg(feature = "aes_xts")] + Type::AES_128_XTS => ffi::EVP_aes_128_xts(), + #[cfg(feature = "aes_ctr")] + Type::AES_128_CTR => ffi::EVP_aes_128_ctr(), + // AES_128_GCM => (EVP_aes_128_gcm(), 16, 16), + Type::AES_128_CFB1 => ffi::EVP_aes_128_cfb1(), + Type::AES_128_CFB128 => ffi::EVP_aes_128_cfb128(), + Type::AES_128_CFB8 => ffi::EVP_aes_128_cfb8(), + + Type::AES_256_ECB => ffi::EVP_aes_256_ecb(), + Type::AES_256_CBC => ffi::EVP_aes_256_cbc(), + #[cfg(feature = "aes_xts")] + Type::AES_256_XTS => ffi::EVP_aes_256_xts(), + #[cfg(feature = "aes_ctr")] + Type::AES_256_CTR => ffi::EVP_aes_256_ctr(), + // AES_256_GCM => (EVP_aes_256_gcm(), 32, 16), + Type::AES_256_CFB1 => ffi::EVP_aes_256_cfb1(), + Type::AES_256_CFB128 => ffi::EVP_aes_256_cfb128(), + Type::AES_256_CFB8 => ffi::EVP_aes_256_cfb8(), + + Type::DES_CBC => ffi::EVP_des_cbc(), + Type::DES_ECB => ffi::EVP_des_ecb(), + + Type::RC4_128 => ffi::EVP_rc4(), + } + } + } + + /// Returns the length of keys used with this cipher. + pub fn key_len(&self) -> usize { + unsafe { + ffi::EVP_CIPHER_key_length(self.as_ptr()) as usize + } + } + + /// Returns the length of the IV used with this cipher, or `None` if the + /// cipher does not use an IV. + pub fn iv_len(&self) -> Option<usize> { + unsafe { + let len = ffi::EVP_CIPHER_iv_length(self.as_ptr()) as usize; + if len == 0 { + None + } else { + Some(len) + } + } + } + + /// Returns the block size of the cipher. + /// + /// # Note + /// + /// Stream ciphers such as RC4 have a block size of 1. + pub fn block_size(&self) -> usize { + unsafe { + ffi::EVP_CIPHER_block_size(self.as_ptr()) as usize + } + } +} /// Represents a symmetric cipher context. pub struct Crypter { - evp: *const ffi::EVP_CIPHER, ctx: *mut ffi::EVP_CIPHER_CTX, - keylen: u32, - blocksize: u32, + block_size: usize, } impl Crypter { - pub fn new(t: Type) -> Crypter { + /// Creates a new `Crypter`. + /// + /// # Panics + /// + /// Panics if an IV is required by the cipher but not provided, or if the + /// IV's length does not match the expected length (see `Type::iv_len`). + pub fn new(t: Type, mode: Mode, key: &[u8], iv: Option<&[u8]>) -> Result<Crypter, ErrorStack> { 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, - } - } - - /** - * Enables or disables padding. If padding is disabled, total amount of - * data encrypted must be a multiple of block size. - */ - pub fn pad(&self, padding: bool) { - if self.blocksize > 0 { - unsafe { - let v = if padding { - 1 as c_int - } else { - 0 - }; - ffi::EVP_CIPHER_CTX_set_padding(self.ctx, v); - } - } - } - - /** - * Initializes this crypter. - */ - pub fn init(&self, mode: Mode, key: &[u8], iv: &[u8]) { unsafe { + let ctx = try_ssl_null!(ffi::EVP_CIPHER_CTX_new()); + let crypter = Crypter { + ctx: ctx, + block_size: t.block_size(), + }; + let mode = match mode { - Mode::Encrypt => 1 as c_int, - Mode::Decrypt => 0 as c_int, + Mode::Encrypt => 1, + Mode::Decrypt => 0, }; - assert_eq!(key.len(), self.keylen as usize); - ffi::EVP_CipherInit(self.ctx, self.evp, key.as_ptr(), iv.as_ptr(), mode); + try_ssl!(ffi::EVP_CipherInit_ex(crypter.ctx, + t.as_ptr(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + mode)); + + assert!(key.len() <= c_int::max_value() as usize); + try_ssl!(ffi::EVP_CIPHER_CTX_set_key_length(crypter.ctx, key.len() as c_int)); + + let key = key.as_ptr() as *mut _; + let iv = match (iv, t.iv_len()) { + (Some(iv), Some(len)) => { + assert!(iv.len() == len); + iv.as_ptr() as *mut _ + } + (Some(_), None) | (None, None) => ptr::null_mut(), + (None, Some(_)) => panic!("an IV is required for this cipher"), + }; + try_ssl!(ffi::EVP_CipherInit_ex(crypter.ctx, + ptr::null(), + ptr::null_mut(), + key, + iv, + mode)); + + Ok(crypter) } } - /** - * Update this crypter with more data to encrypt or decrypt. Returns - * encrypted or decrypted bytes. - */ - pub fn update(&self, data: &[u8]) -> Vec<u8> { + /// Enables or disables padding. + /// + /// If padding is disabled, total amount of data encrypted/decrypted must + /// be a multiple of the cipher's block size. + pub fn pad(&mut self, padding: bool) { + unsafe { ffi::EVP_CIPHER_CTX_set_padding(self.ctx, padding as c_int); } + } + + /// Feeds data from `input` through the cipher, writing encrypted/decrypted + /// bytes into `output`. + /// + /// The number of bytes written to `output` is returned. Note that this may + /// not be equal to the length of `input`. + /// + /// # Panics + /// + /// Panics if `output.len() < input.len() + block_size` where + /// `block_size` is the block size of the cipher (see `Type::block_size`), + /// or if `output.len() > c_int::max_value()`. + pub fn update(&mut self, input: &[u8], output: &mut [u8]) -> Result<usize, ErrorStack> { unsafe { - let sum = data.len() + (self.blocksize as usize); - let mut res = repeat(0u8).take(sum).collect::<Vec<_>>(); - let mut reslen = sum as c_int; - - ffi::EVP_CipherUpdate(self.ctx, - res.as_mut_ptr(), - &mut reslen, - data.as_ptr(), - data.len() as c_int); - - res.truncate(reslen as usize); - res + assert!(output.len() >= input.len() + self.block_size); + assert!(output.len() <= c_int::max_value() as usize); + let mut outl = output.len() as c_int; + let inl = input.len() as c_int; + + try_ssl!(ffi::EVP_CipherUpdate(self.ctx, + output.as_mut_ptr(), + &mut outl, + input.as_ptr(), + inl)); + + Ok(outl as usize) } } - /** - * Finish crypting. Returns the remaining partial block of output, if any. - */ - pub fn finalize(&self) -> Vec<u8> { + /// Finishes the encryption/decryption process, writing any remaining data + /// to `output`. + /// + /// The number of bytes written to `output` is returned. + /// + /// `update` should not be called after this method. + /// + /// # Panics + /// + /// Panics if `output` is less than the cipher's block size. + pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> { unsafe { - let mut res = repeat(0u8).take(self.blocksize as usize).collect::<Vec<_>>(); - let mut reslen = self.blocksize as c_int; + assert!(output.len() >= self.block_size); + let mut outl = cmp::min(output.len(), c_int::max_value() as usize) as c_int; - ffi::EVP_CipherFinal(self.ctx, res.as_mut_ptr(), &mut reslen); + try_ssl!(ffi::EVP_CipherFinal(self.ctx, output.as_mut_ptr(), &mut outl)); - res.truncate(reslen as usize); - res + Ok(outl as usize) } } } @@ -147,31 +238,43 @@ impl Drop for Crypter { * Encrypts data, using the specified crypter type in encrypt mode with the * specified key and iv; returns the resulting (encrypted) data. */ -pub fn encrypt(t: Type, key: &[u8], iv: &[u8], data: &[u8]) -> Vec<u8> { - let c = Crypter::new(t); - c.init(Mode::Encrypt, key, iv); - let mut r = c.update(data); - let rest = c.finalize(); - r.extend(rest.into_iter()); - r +pub fn encrypt(t: Type, + key: &[u8], + iv: Option<&[u8]>, + data: &[u8]) + -> Result<Vec<u8>, ErrorStack> { + cipher(t, Mode::Encrypt, key, iv, data) } /** * Decrypts data, using the specified crypter type in decrypt mode with the * specified key and iv; returns the resulting (decrypted) data. */ -pub fn decrypt(t: Type, key: &[u8], iv: &[u8], data: &[u8]) -> Vec<u8> { - let c = Crypter::new(t); - c.init(Mode::Decrypt, key, iv); - let mut r = c.update(data); - let rest = c.finalize(); - r.extend(rest.into_iter()); - r +pub fn decrypt(t: Type, + key: &[u8], + iv: Option<&[u8]>, + data: &[u8]) + -> Result<Vec<u8>, ErrorStack> { + cipher(t, Mode::Decrypt, key, iv, data) +} + +fn cipher(t: Type, + mode: Mode, + key: &[u8], + iv: Option<&[u8]>, + data: &[u8]) + -> Result<Vec<u8>, ErrorStack> { + let mut c = try!(Crypter::new(t, mode, key, iv)); + let mut out = vec![0; data.len() + t.block_size()]; + let count = try!(c.update(data, &mut out)); + let rest = try!(c.finalize(&mut out[count..])); + out.truncate(count + rest); + Ok(out) } #[cfg(test)] mod tests { - use serialize::hex::FromHex; + use serialize::hex::{FromHex, ToHex}; // Test vectors from FIPS-197: // http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf @@ -185,25 +288,33 @@ mod tests { 0xaau8, 0xbbu8, 0xccu8, 0xddu8, 0xeeu8, 0xffu8]; let c0 = [0x8eu8, 0xa2u8, 0xb7u8, 0xcau8, 0x51u8, 0x67u8, 0x45u8, 0xbfu8, 0xeau8, 0xfcu8, 0x49u8, 0x90u8, 0x4bu8, 0x49u8, 0x60u8, 0x89u8]; - let c = super::Crypter::new(super::Type::AES_256_ECB); - c.init(super::Mode::Encrypt, &k0, &[]); + let mut c = super::Crypter::new(super::Type::AES_256_ECB, + super::Mode::Encrypt, + &k0, + None).unwrap(); c.pad(false); - let mut r0 = c.update(&p0); - r0.extend(c.finalize().into_iter()); - assert!(r0 == c0); - c.init(super::Mode::Decrypt, &k0, &[]); + let mut r0 = vec![0; c0.len() + super::Type::AES_256_ECB.block_size()]; + let count = c.update(&p0, &mut r0).unwrap(); + let rest = c.finalize(&mut r0[count..]).unwrap(); + r0.truncate(count + rest); + assert_eq!(r0.to_hex(), c0.to_hex()); + + let mut c = super::Crypter::new(super::Type::AES_256_ECB, + super::Mode::Decrypt, + &k0, + None).unwrap(); c.pad(false); - let mut p1 = c.update(&r0); - p1.extend(c.finalize().into_iter()); - assert!(p1 == p0); + let mut p1 = vec![0; r0.len() + super::Type::AES_256_ECB.block_size()]; + let count = c.update(&r0, &mut p1).unwrap(); + let rest = c.finalize(&mut p1[count..]).unwrap(); + p1.truncate(count + rest); + assert_eq!(p1.to_hex(), p0.to_hex()); } #[test] fn test_aes_256_cbc_decrypt() { - let cr = super::Crypter::new(super::Type::AES_256_CBC); let iv = [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]; + 98_u8, 107_u8, 208_u8, 14_u8, 236_u8, 60_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, @@ -211,29 +322,31 @@ mod tests { 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); + let mut cr = super::Crypter::new(super::Type::AES_256_CBC, + super::Mode::Decrypt, + &data, + Some(&iv)).unwrap(); cr.pad(false); - let unciphered_data_1 = cr.update(&ciphered_data); - let unciphered_data_2 = cr.finalize(); + let mut unciphered_data = vec![0; data.len() + super::Type::AES_256_CBC.block_size()]; + let count = cr.update(&ciphered_data, &mut unciphered_data).unwrap(); + let rest = cr.finalize(&mut unciphered_data[count..]).unwrap(); + unciphered_data.truncate(count + rest); let expected_unciphered_data = b"I love turtles.\x01"; - assert!(unciphered_data_2.len() == 0); - - assert_eq!(&unciphered_data_1, expected_unciphered_data); + assert_eq!(&unciphered_data, 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::Mode::Encrypt, - &key.from_hex().unwrap(), - &iv.from_hex().unwrap()); + let pt = pt.from_hex().unwrap(); + let ct = ct.from_hex().unwrap(); + let key = key.from_hex().unwrap(); + let iv = iv.from_hex().unwrap(); - let expected = ct.from_hex().unwrap(); - let mut computed = cipher.update(&pt.from_hex().unwrap()); - computed.extend(cipher.finalize().into_iter()); + let computed = super::decrypt(ciphertype, &key, Some(&iv), &ct).unwrap(); + let expected = pt; if computed != expected { println!("Computed: {}", computed.to_hex()); diff --git a/openssl/src/crypto/symm_internal.rs b/openssl/src/crypto/symm_internal.rs deleted file mode 100644 index ba01e1c1..00000000 --- a/openssl/src/crypto/symm_internal.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crypto::symm; -use ffi; - -pub fn evpc(t: symm::Type) -> (*const ffi::EVP_CIPHER, u32, u32) { - unsafe { - match t { - symm::Type::AES_128_ECB => (ffi::EVP_aes_128_ecb(), 16, 16), - symm::Type::AES_128_CBC => (ffi::EVP_aes_128_cbc(), 16, 16), - #[cfg(feature = "aes_xts")] - symm::Type::AES_128_XTS => (ffi::EVP_aes_128_xts(), 32, 16), - #[cfg(feature = "aes_ctr")] - symm::Type::AES_128_CTR => (ffi::EVP_aes_128_ctr(), 16, 0), - // AES_128_GCM => (EVP_aes_128_gcm(), 16, 16), - symm::Type::AES_128_CFB1 => (ffi::EVP_aes_128_cfb1(), 16, 16), - symm::Type::AES_128_CFB128 => (ffi::EVP_aes_128_cfb128(), 16, 16), - symm::Type::AES_128_CFB8 => (ffi::EVP_aes_128_cfb8(), 16, 16), - - symm::Type::AES_256_ECB => (ffi::EVP_aes_256_ecb(), 32, 16), - symm::Type::AES_256_CBC => (ffi::EVP_aes_256_cbc(), 32, 16), - #[cfg(feature = "aes_xts")] - symm::Type::AES_256_XTS => (ffi::EVP_aes_256_xts(), 64, 16), - #[cfg(feature = "aes_ctr")] - symm::Type::AES_256_CTR => (ffi::EVP_aes_256_ctr(), 32, 0), - // AES_256_GCM => (EVP_aes_256_gcm(), 32, 16), - symm::Type::AES_256_CFB1 => (ffi::EVP_aes_256_cfb1(), 32, 16), - symm::Type::AES_256_CFB128 => (ffi::EVP_aes_256_cfb128(), 32, 16), - symm::Type::AES_256_CFB8 => (ffi::EVP_aes_256_cfb8(), 32, 16), - - symm::Type::DES_CBC => (ffi::EVP_des_cbc(), 8, 8), - symm::Type::DES_ECB => (ffi::EVP_des_ecb(), 8, 8), - - symm::Type::RC4_128 => (ffi::EVP_rc4(), 16, 0), - } - } -} diff --git a/openssl/src/crypto/util.rs b/openssl/src/crypto/util.rs new file mode 100644 index 00000000..be72aa59 --- /dev/null +++ b/openssl/src/crypto/util.rs @@ -0,0 +1,58 @@ +use libc::{c_int, c_char, c_void}; + +use std::any::Any; +use std::panic; +use std::slice; + +/// Wraps a user-supplied callback and a slot for panics thrown inside the callback (while FFI +/// frames are on the stack). +/// +/// When dropped, checks if the callback has panicked, and resumes unwinding if so. +pub struct CallbackState<F> { + /// The user callback. Taken out of the `Option` when called. + cb: Option<F>, + /// If the callback panics, we place the panic object here, to be re-thrown once OpenSSL + /// returns. + panic: Option<Box<Any + Send + 'static>>, +} + +impl<F> CallbackState<F> { + pub fn new(callback: F) -> Self { + CallbackState { + cb: Some(callback), + panic: None, + } + } +} + +impl<F> Drop for CallbackState<F> { + fn drop(&mut self) { + if let Some(panic) = self.panic.take() { + panic::resume_unwind(panic); + } + } +} + +/// Password callback function, passed to private key loading functions. +/// +/// `cb_state` is expected to be a pointer to a `CallbackState`. +pub extern "C" fn invoke_passwd_cb<F>(buf: *mut c_char, + size: c_int, + _rwflag: c_int, + cb_state: *mut c_void) + -> c_int + where F: FnOnce(&mut [i8]) -> usize { + let result = panic::catch_unwind(|| { + // build a `i8` slice to pass to the user callback + let pass_slice = unsafe { slice::from_raw_parts_mut(buf, size as usize) }; + let callback = unsafe { &mut *(cb_state as *mut CallbackState<F>) }; + + callback.cb.take().unwrap()(pass_slice) + }); + + if let Ok(len) = result { + return len as c_int; + } else { + return 0; + } +} |