Loading openssl/Cargo.toml +1 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ ecdh_auto = ["openssl-sys-extras/ecdh_auto"] pkcs5_pbkdf2_hmac = ["openssl-sys/pkcs5_pbkdf2_hmac"] nightly = [] catch_unwind = [] [dependencies] bitflags = ">= 0.5.0, < 0.8.0" Loading openssl/src/crypto/mod.rs +2 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ pub mod rand; pub mod symm; pub mod memcmp; pub mod rsa; #[cfg(feature = "catch_unwind")] mod util; mod symm_internal; Loading openssl/src/crypto/pkey.rs +34 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,11 @@ use ffi; use ssl::error::{SslError, StreamError}; use crypto::rsa::RSA; #[cfg(feature = "catch_unwind")] use libc::{c_void, c_char}; #[cfg(feature = "catch_unwind")] use crypto::util::{CallbackState, invoke_passwd_cb}; #[derive(Copy, Clone)] pub enum Parts { Neither, Loading Loading @@ -93,6 +98,35 @@ impl PKey { } } /// 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. /// /// Requires the `catch_unwind` feature. #[cfg(feature = "catch_unwind")] pub fn private_key_from_pem_cb<R, F>(reader: &mut R, pass_cb: F) -> Result<PKey, SslError> where R: Read, F: FnOnce(&mut [c_char]) -> usize { let mut cb = CallbackState::new(pass_cb); 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_PrivateKey(mem_bio.get_handle(), ptr::null_mut(), Some(invoke_passwd_cb::<F>), &mut cb as *mut _ as *mut c_void)); 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 Loading openssl/src/crypto/rsa.rs +47 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,11 @@ use bio::MemBio; use crypto::HashTypeInternals; use crypto::hash; #[cfg(feature = "catch_unwind")] use libc::{c_void, c_char}; #[cfg(feature = "catch_unwind")] use crypto::util::{CallbackState, invoke_passwd_cb}; pub struct RSA(*mut ffi::RSA); impl Drop for RSA { Loading Loading @@ -76,6 +81,29 @@ impl RSA { } } /// Reads an RSA private key from PEM formatted data and supplies a password callback. /// /// Requires the `catch_unwind` feature. #[cfg(feature = "catch_unwind")] pub fn private_key_from_pem_cb<R, F>(reader: &mut R, pass_cb: F) -> Result<RSA, SslError> where R: Read, F: FnOnce(&mut [c_char]) -> usize { let mut cb = CallbackState::new(pass_cb); let mut mem_bio = try!(MemBio::new()); try!(io::copy(reader, &mut mem_bio).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.get_handle(), ptr::null_mut(), Some(invoke_passwd_cb::<F>), cb_ptr)); Ok(RSA(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 Loading Loading @@ -277,4 +305,23 @@ mod test { assert!(result); } #[test] #[cfg(feature = "catch_unwind")] pub fn test_password() { let mut password_queried = false; let mut buffer = File::open("test/rsa-encrypted.pem").unwrap(); RSA::private_key_from_pem_cb(&mut buffer, |password| { password_queried = true; password[0] = b'm' as _; password[1] = b'y' as _; password[2] = b'p' as _; password[3] = b'a' as _; password[4] = b's' as _; password[5] = b's' as _; 6 }).unwrap(); assert!(password_queried); } } openssl/src/crypto/util.rs 0 → 100644 +58 −0 Original line number Diff line number Diff line 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; } } Loading
openssl/Cargo.toml +1 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ ecdh_auto = ["openssl-sys-extras/ecdh_auto"] pkcs5_pbkdf2_hmac = ["openssl-sys/pkcs5_pbkdf2_hmac"] nightly = [] catch_unwind = [] [dependencies] bitflags = ">= 0.5.0, < 0.8.0" Loading
openssl/src/crypto/mod.rs +2 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ pub mod rand; pub mod symm; pub mod memcmp; pub mod rsa; #[cfg(feature = "catch_unwind")] mod util; mod symm_internal; Loading
openssl/src/crypto/pkey.rs +34 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,11 @@ use ffi; use ssl::error::{SslError, StreamError}; use crypto::rsa::RSA; #[cfg(feature = "catch_unwind")] use libc::{c_void, c_char}; #[cfg(feature = "catch_unwind")] use crypto::util::{CallbackState, invoke_passwd_cb}; #[derive(Copy, Clone)] pub enum Parts { Neither, Loading Loading @@ -93,6 +98,35 @@ impl PKey { } } /// 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. /// /// Requires the `catch_unwind` feature. #[cfg(feature = "catch_unwind")] pub fn private_key_from_pem_cb<R, F>(reader: &mut R, pass_cb: F) -> Result<PKey, SslError> where R: Read, F: FnOnce(&mut [c_char]) -> usize { let mut cb = CallbackState::new(pass_cb); 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_PrivateKey(mem_bio.get_handle(), ptr::null_mut(), Some(invoke_passwd_cb::<F>), &mut cb as *mut _ as *mut c_void)); 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 Loading
openssl/src/crypto/rsa.rs +47 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,11 @@ use bio::MemBio; use crypto::HashTypeInternals; use crypto::hash; #[cfg(feature = "catch_unwind")] use libc::{c_void, c_char}; #[cfg(feature = "catch_unwind")] use crypto::util::{CallbackState, invoke_passwd_cb}; pub struct RSA(*mut ffi::RSA); impl Drop for RSA { Loading Loading @@ -76,6 +81,29 @@ impl RSA { } } /// Reads an RSA private key from PEM formatted data and supplies a password callback. /// /// Requires the `catch_unwind` feature. #[cfg(feature = "catch_unwind")] pub fn private_key_from_pem_cb<R, F>(reader: &mut R, pass_cb: F) -> Result<RSA, SslError> where R: Read, F: FnOnce(&mut [c_char]) -> usize { let mut cb = CallbackState::new(pass_cb); let mut mem_bio = try!(MemBio::new()); try!(io::copy(reader, &mut mem_bio).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.get_handle(), ptr::null_mut(), Some(invoke_passwd_cb::<F>), cb_ptr)); Ok(RSA(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 Loading Loading @@ -277,4 +305,23 @@ mod test { assert!(result); } #[test] #[cfg(feature = "catch_unwind")] pub fn test_password() { let mut password_queried = false; let mut buffer = File::open("test/rsa-encrypted.pem").unwrap(); RSA::private_key_from_pem_cb(&mut buffer, |password| { password_queried = true; password[0] = b'm' as _; password[1] = b'y' as _; password[2] = b'p' as _; password[3] = b'a' as _; password[4] = b's' as _; password[5] = b's' as _; 6 }).unwrap(); assert!(password_queried); } }
openssl/src/crypto/util.rs 0 → 100644 +58 −0 Original line number Diff line number Diff line 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; } }