From 08e0c4ca9061c3dc0c951db0c08909689b04a310 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 13 Nov 2016 15:02:38 +0000 Subject: [PATCH] Some serialization support for EcKey --- openssl-sys/src/lib.rs | 15 ++++++-- openssl/src/dsa.rs | 35 ++++--------------- openssl/src/ec_key.rs | 28 ++++++++++++++- openssl/src/lib.rs | 42 ++--------------------- openssl/src/macros.rs | 77 ++++++++++++++++++++++++++++++++++++++++++ openssl/src/pkey.rs | 23 +++---------- openssl/src/rsa.rs | 30 ++++------------ openssl/src/util.rs | 31 ++++++++++++++--- 8 files changed, 161 insertions(+), 120 deletions(-) create mode 100644 openssl/src/macros.rs diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index f3ecdf8b..1b84b690 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -1513,11 +1513,19 @@ extern { user_data: *mut c_void) -> c_int; pub fn PEM_write_bio_DSA_PUBKEY(bp: *mut BIO, dsa: *mut DSA) -> c_int; - - pub fn PEM_write_bio_X509(bio: *mut BIO, x509: *mut X509) -> c_int; pub fn PEM_write_bio_X509_REQ(bio: *mut BIO, x509: *mut X509_REQ) -> c_int; + pub fn PEM_write_bio_ECPrivateKey(bio: *mut BIO, + key: *mut EC_KEY, + cipher: *const EVP_CIPHER, + kstr: *mut c_uchar, + klen: c_int, + callback: Option, + user_data: *mut c_void) + -> c_int; + pub fn PEM_read_bio_ECPrivateKey(bio: *mut BIO, key: *mut *mut EC_KEY, callback: Option, user_data: *mut c_void) -> *mut EC_KEY; + pub fn PKCS5_PBKDF2_HMAC_SHA1(pass: *const c_char, passlen: c_int, salt: *const u8, saltlen: c_int, iter: c_int, keylen: c_int, @@ -1744,6 +1752,9 @@ extern { pub fn d2i_DSAPrivateKey(a: *mut *mut DSA, pp: *mut *const c_uchar, length: c_long) -> *mut DSA; pub fn i2d_DSAPrivateKey(a: *const DSA, pp: *mut *mut c_uchar) -> c_int; + pub fn d2i_ECPrivateKey(k: *mut *mut EC_KEY, pp: *mut *const c_uchar, length: c_long) -> *mut EC_KEY; + pub fn i2d_ECPrivateKey(ec_key: *mut EC_KEY, pp: *mut *mut c_uchar) -> c_int; + pub fn d2i_X509(a: *mut *mut X509, pp: *mut *const c_uchar, length: c_long) -> *mut X509; pub fn i2d_X509_bio(b: *mut BIO, x: *mut X509) -> c_int; pub fn i2d_X509_REQ_bio(b: *mut BIO, x: *mut X509_REQ) -> c_int; diff --git a/openssl/src/dsa.rs b/openssl/src/dsa.rs index 72076775..962fcc9c 100644 --- a/openssl/src/dsa.rs +++ b/openssl/src/dsa.rs @@ -9,7 +9,7 @@ use bio::{MemBio, MemBioSlice}; use bn::BigNumRef; use {cvt, cvt_p}; use types::OpenSslTypeRef; -use util::{CallbackState, invoke_passwd_cb}; +use util::{CallbackState, invoke_passwd_cb_old}; type_!(Dsa, DsaRef, ffi::DSA, ffi::DSA_free); @@ -125,25 +125,9 @@ impl Dsa { } } - /// Reads a DSA private key from PEM formatted data. - pub fn private_key_from_pem(buf: &[u8]) -> Result { - ffi::init(); - let mem_bio = try!(MemBioSlice::new(buf)); + private_key_from_pem!(Dsa, ffi::PEM_read_bio_DSAPrivateKey); - unsafe { - let dsa = try!(cvt_p(ffi::PEM_read_bio_DSAPrivateKey(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut()))); - Ok(Dsa(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. + #[deprecated(since = "0.9.2", note = "use private_key_from_pem_callback")] pub fn private_key_from_pem_cb(buf: &[u8], pass_cb: F) -> Result where F: FnOnce(&mut [c_char]) -> usize { @@ -155,7 +139,7 @@ impl Dsa { let cb_ptr = &mut cb as *mut _ as *mut c_void; let dsa = try!(cvt_p(ffi::PEM_read_bio_DSAPrivateKey(mem_bio.as_ptr(), ptr::null_mut(), - Some(invoke_passwd_cb::), + Some(invoke_passwd_cb_old::), cb_ptr))); Ok(Dsa(dsa)) } @@ -235,8 +219,6 @@ mod compat { #[cfg(test)] mod test { - use libc::c_char; - use super::*; #[test] @@ -248,14 +230,9 @@ mod 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| { + Dsa::private_key_from_pem_callback(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; + password[..6].copy_from_slice(b"mypass"); 6 }) .unwrap(); diff --git a/openssl/src/ec_key.rs b/openssl/src/ec_key.rs index ad85dc5e..99d62ad3 100644 --- a/openssl/src/ec_key.rs +++ b/openssl/src/ec_key.rs @@ -1,11 +1,27 @@ use ffi; +use std::cmp; +use libc::c_long; +use std::ptr; -use {cvt_p, init}; +use {cvt, cvt_p, init}; use error::ErrorStack; use nid::Nid; +use types::OpenSslTypeRef; type_!(EcKey, EcKeyRef, ffi::EC_KEY, ffi::EC_KEY_free); +impl EcKeyRef { + /// Serializes the private key components to DER. + pub fn private_key_to_der(&self) -> Result, ErrorStack> { + unsafe { + let len = try!(cvt(ffi::i2d_ECPrivateKey(self.as_ptr(), ptr::null_mut()))); + let mut buf = vec![0; len as usize]; + try!(cvt(ffi::i2d_ECPrivateKey(self.as_ptr(), &mut buf.as_mut_ptr()))); + Ok(buf) + } + } +} + impl EcKey { pub fn new_by_curve_name(nid: Nid) -> Result { unsafe { @@ -13,6 +29,16 @@ impl EcKey { cvt_p(ffi::EC_KEY_new_by_curve_name(nid.as_raw())).map(EcKey) } } + /// Deserializes a DER-encoded private key. + pub fn private_key_from_der(der: &[u8]) -> Result { + unsafe { + init(); + let len = cmp::min(der.len(), c_long::max_value() as usize) as c_long; + cvt_p(ffi::d2i_ECPrivateKey(ptr::null_mut(), &mut der.as_ptr(), len)).map(EcKey) + } + } + + private_key_from_pem!(EcKey, ffi::PEM_read_bio_ECPrivateKey); } #[cfg(test)] diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index d3f99de7..c2c559dc 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -19,46 +19,8 @@ use libc::c_int; use error::ErrorStack; -macro_rules! type_ { - ($n:ident, $r:ident, $c:path, $d:path) => { - pub struct $n(*mut $c); - - impl ::types::OpenSslType for $n { - type CType = $c; - type Ref = $r; - - unsafe fn from_ptr(ptr: *mut $c) -> $n { - $n(ptr) - } - } - - impl Drop for $n { - fn drop(&mut self) { - unsafe { $d(self.0) } - } - } - - impl ::std::ops::Deref for $n { - type Target = $r; - - fn deref(&self) -> &$r { - unsafe { ::types::OpenSslTypeRef::from_ptr(self.0) } - } - } - - impl ::std::ops::DerefMut for $n { - fn deref_mut(&mut self) -> &mut $r { - unsafe { ::types::OpenSslTypeRef::from_ptr_mut(self.0) } - } - } - - pub struct $r(::util::Opaque); - - impl ::types::OpenSslTypeRef for $r { - type CType = $c; - } - } -} +#[macro_use] +mod macros; mod bio; mod util; diff --git a/openssl/src/macros.rs b/openssl/src/macros.rs new file mode 100644 index 00000000..0db3401c --- /dev/null +++ b/openssl/src/macros.rs @@ -0,0 +1,77 @@ + +macro_rules! type_ { + ($n:ident, $r:ident, $c:path, $d:path) => { + pub struct $n(*mut $c); + + impl ::types::OpenSslType for $n { + type CType = $c; + type Ref = $r; + + unsafe fn from_ptr(ptr: *mut $c) -> $n { + $n(ptr) + } + } + + impl Drop for $n { + fn drop(&mut self) { + unsafe { $d(self.0) } + } + } + + impl ::std::ops::Deref for $n { + type Target = $r; + + fn deref(&self) -> &$r { + unsafe { ::types::OpenSslTypeRef::from_ptr(self.0) } + } + } + + impl ::std::ops::DerefMut for $n { + fn deref_mut(&mut self) -> &mut $r { + unsafe { ::types::OpenSslTypeRef::from_ptr_mut(self.0) } + } + } + + pub struct $r(::util::Opaque); + + impl ::types::OpenSslTypeRef for $r { + type CType = $c; + } + } +} + +macro_rules! private_key_from_pem { + ($t:ident, $f:path) => { + /// Deserializes a PEM-formatted private key. + pub fn private_key_from_pem(pem: &[u8]) -> Result<$t, ::error::ErrorStack> { + unsafe { + ::init(); + let bio = try!(::bio::MemBioSlice::new(pem)); + cvt_p($f(bio.as_ptr(), ::std::ptr::null_mut(), None, ::std::ptr::null_mut())) + .map($t) + } + } + + /// Deserializes a PEM-formatted private key, using a callback to retrieve a password if the + /// key is encrypted. + /// + /// The callback should copy the password into the provided buffer and return the number of + /// bytes written. + pub fn private_key_from_pem_callback(pem: &[u8], + callback: F) + -> Result<$t, ::error::ErrorStack> + where F: FnOnce(&mut [u8]) -> usize + { + unsafe { + ffi::init(); + let mut cb = ::util::CallbackState::new(callback); + let bio = try!(::bio::MemBioSlice::new(pem)); + cvt_p($f(bio.as_ptr(), + ptr::null_mut(), + Some(::util::invoke_passwd_cb::), + &mut cb as *mut _ as *mut ::libc::c_void)) + .map($t) + } + } + } +} diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 43eadd43..a56633a2 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -10,7 +10,7 @@ use dsa::Dsa; use ec_key::EcKey; use rsa::Rsa; use error::ErrorStack; -use util::{CallbackState, invoke_passwd_cb}; +use util::{CallbackState, invoke_passwd_cb_old}; use types::{OpenSslType, OpenSslTypeRef}; type_!(PKey, PKeyRef, ffi::EVP_PKEY, ffi::EVP_PKEY_free); @@ -166,24 +166,9 @@ impl PKey { } } - /// Reads a private key from PEM. - pub fn private_key_from_pem(buf: &[u8]) -> Result { - ffi::init(); - let mem_bio = try!(MemBioSlice::new(buf)); - unsafe { - let evp = try!(cvt_p(ffi::PEM_read_bio_PrivateKey(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut()))); - Ok(PKey::from_ptr(evp)) - } - } + private_key_from_pem!(PKey, ffi::PEM_read_bio_PrivateKey); - /// 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. + #[deprecated(since = "0.9.2", note = "use private_key_from_pem_callback")] pub fn private_key_from_pem_cb(buf: &[u8], pass_cb: F) -> Result where F: FnOnce(&mut [c_char]) -> usize { @@ -193,7 +178,7 @@ impl PKey { unsafe { let evp = try!(cvt_p(ffi::PEM_read_bio_PrivateKey(mem_bio.as_ptr(), ptr::null_mut(), - Some(invoke_passwd_cb::), + Some(invoke_passwd_cb_old::), &mut cb as *mut _ as *mut c_void))); Ok(PKey::from_ptr(evp)) } diff --git a/openssl/src/rsa.rs b/openssl/src/rsa.rs index bd36570d..22668e19 100644 --- a/openssl/src/rsa.rs +++ b/openssl/src/rsa.rs @@ -9,7 +9,7 @@ use {cvt, cvt_p, cvt_n}; use bn::{BigNum, BigNumRef}; use bio::{MemBio, MemBioSlice}; use error::ErrorStack; -use util::{CallbackState, invoke_passwd_cb}; +use util::{CallbackState, invoke_passwd_cb_old}; use types::OpenSslTypeRef; /// Type of encryption padding to use. @@ -281,20 +281,9 @@ impl Rsa { } } - /// Reads an RSA private key from PEM formatted data. - pub fn private_key_from_pem(buf: &[u8]) -> Result { - ffi::init(); - let mem_bio = try!(MemBioSlice::new(buf)); - unsafe { - let rsa = try!(cvt_p(ffi::PEM_read_bio_RSAPrivateKey(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut()))); - Ok(Rsa(rsa)) - } - } + private_key_from_pem!(Rsa, ffi::PEM_read_bio_RSAPrivateKey); - /// Reads an RSA private key from PEM formatted data and supplies a password callback. + #[deprecated(since = "0.9.2", note = "use private_key_from_pem_callback")] pub fn private_key_from_pem_cb(buf: &[u8], pass_cb: F) -> Result where F: FnOnce(&mut [c_char]) -> usize { @@ -306,7 +295,7 @@ impl Rsa { let cb_ptr = &mut cb as *mut _ as *mut c_void; let rsa = try!(cvt_p(ffi::PEM_read_bio_RSAPrivateKey(mem_bio.as_ptr(), ptr::null_mut(), - Some(invoke_passwd_cb::), + Some(invoke_passwd_cb_old::), cb_ptr))); Ok(Rsa(rsa)) } @@ -429,22 +418,15 @@ mod compat { #[cfg(test)] mod test { - use libc::c_char; - use super::*; #[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| { + Rsa::private_key_from_pem_callback(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; + password[..6].copy_from_slice(b"mypass"); 6 }) .unwrap(); diff --git a/openssl/src/util.rs b/openssl/src/util.rs index 302bd316..dea94668 100644 --- a/openssl/src/util.rs +++ b/openssl/src/util.rs @@ -33,10 +33,7 @@ impl Drop for CallbackState { } } -/// Password callback function, passed to private key loading functions. -/// -/// `cb_state` is expected to be a pointer to a `CallbackState`. -pub unsafe extern "C" fn invoke_passwd_cb(buf: *mut c_char, +pub unsafe extern fn invoke_passwd_cb_old(buf: *mut c_char, size: c_int, _rwflag: c_int, cb_state: *mut c_void) @@ -46,9 +43,33 @@ pub unsafe extern "C" fn invoke_passwd_cb(buf: *mut c_char, let callback = &mut *(cb_state as *mut CallbackState); let result = panic::catch_unwind(AssertUnwindSafe(|| { - // build a `i8` slice to pass to the user callback let pass_slice = slice::from_raw_parts_mut(buf, size as usize); + callback.cb.take().unwrap()(pass_slice) + })); + match result { + Ok(len) => len as c_int, + Err(err) => { + callback.panic = Some(err); + 0 + } + } +} + +/// Password callback function, passed to private key loading functions. +/// +/// `cb_state` is expected to be a pointer to a `CallbackState`. +pub unsafe extern fn invoke_passwd_cb(buf: *mut c_char, + size: c_int, + _rwflag: c_int, + cb_state: *mut c_void) + -> c_int + where F: FnOnce(&mut [u8]) -> usize +{ + let callback = &mut *(cb_state as *mut CallbackState); + + let result = panic::catch_unwind(AssertUnwindSafe(|| { + let pass_slice = slice::from_raw_parts_mut(buf as *mut u8, size as usize); callback.cb.take().unwrap()(pass_slice) }));