aboutsummaryrefslogtreecommitdiff
path: root/openssl/src
diff options
context:
space:
mode:
authorSteven Fackler <[email protected]>2017-02-11 10:13:00 -0800
committerSteven Fackler <[email protected]>2017-02-11 10:13:00 -0800
commitf2c69ae7e9e9ab6c843c1de842551bb624e7eb2c (patch)
treeb507d4f207a37720d118bb75d86665d2d9a5da2d /openssl/src
parentDocs (diff)
parentMerge pull request #568 from mredlek/x509_req_version_subject (diff)
downloadrust-openssl-f2c69ae7e9e9ab6c843c1de842551bb624e7eb2c.tar.xz
rust-openssl-f2c69ae7e9e9ab6c843c1de842551bb624e7eb2c.zip
Merge remote-tracking branch 'origin/master' into x509-builder
Diffstat (limited to 'openssl/src')
-rw-r--r--openssl/src/aes.rs115
-rw-r--r--openssl/src/asn1.rs117
-rw-r--r--openssl/src/bn.rs106
-rw-r--r--openssl/src/conf.rs8
-rw-r--r--openssl/src/crypto.rs61
-rw-r--r--openssl/src/dh.rs52
-rw-r--r--openssl/src/dsa.rs110
-rw-r--r--openssl/src/ec.rs484
-rw-r--r--openssl/src/ec_key.rs25
-rw-r--r--openssl/src/error.rs4
-rw-r--r--openssl/src/hash.rs16
-rw-r--r--openssl/src/lib.rs61
-rw-r--r--openssl/src/macros.rs216
-rw-r--r--openssl/src/ocsp.rs304
-rw-r--r--openssl/src/pkcs12.rs180
-rw-r--r--openssl/src/pkcs5.rs2
-rw-r--r--openssl/src/pkey.rs209
-rw-r--r--openssl/src/rsa.rs121
-rw-r--r--openssl/src/sign.rs140
-rw-r--r--openssl/src/ssl/bio.rs27
-rw-r--r--openssl/src/ssl/connector.rs143
-rw-r--r--openssl/src/ssl/mod.rs531
-rw-r--r--openssl/src/ssl/tests/mod.rs275
-rw-r--r--openssl/src/stack.rs20
-rw-r--r--openssl/src/string.rs80
-rw-r--r--openssl/src/symm.rs313
-rw-r--r--openssl/src/types.rs46
-rw-r--r--openssl/src/util.rs39
-rw-r--r--openssl/src/verify.rs10
-rw-r--r--openssl/src/version.rs14
-rw-r--r--openssl/src/x509/mod.rs263
-rw-r--r--openssl/src/x509/store.rs65
-rw-r--r--openssl/src/x509/tests.rs77
33 files changed, 3506 insertions, 728 deletions
diff --git a/openssl/src/aes.rs b/openssl/src/aes.rs
new file mode 100644
index 00000000..d226c515
--- /dev/null
+++ b/openssl/src/aes.rs
@@ -0,0 +1,115 @@
+//! Low level AES functionality
+//!
+//! The `symm` module should be used in preference to this module in most cases.
+use ffi;
+use std::mem;
+use libc::c_int;
+
+use symm::Mode;
+
+#[derive(Debug)]
+pub struct KeyError(());
+
+pub struct AesKey(ffi::AES_KEY);
+
+impl AesKey {
+ /// Prepares a key for encryption.
+ ///
+ /// # Failure
+ ///
+ /// Returns an error if the key is not 128, 192, or 256 bits.
+ pub fn new_encrypt(key: &[u8]) -> Result<AesKey, KeyError> {
+ unsafe {
+ assert!(key.len() <= c_int::max_value() as usize / 8);
+
+ let mut aes_key = mem::uninitialized();
+ let r = ffi::AES_set_encrypt_key(key.as_ptr() as *const _,
+ key.len() as c_int * 8,
+ &mut aes_key);
+ if r == 0 {
+ Ok(AesKey(aes_key))
+ } else {
+ Err(KeyError(()))
+ }
+ }
+ }
+
+ /// Prepares a key for decryption.
+ ///
+ /// # Failure
+ ///
+ /// Returns an error if the key is not 128, 192, or 256 bits.
+ pub fn new_decrypt(key: &[u8]) -> Result<AesKey, KeyError> {
+ unsafe {
+ assert!(key.len() <= c_int::max_value() as usize / 8);
+
+ let mut aes_key = mem::uninitialized();
+ let r = ffi::AES_set_decrypt_key(key.as_ptr() as *const _,
+ key.len() as c_int * 8,
+ &mut aes_key);
+
+ if r == 0 {
+ Ok(AesKey(aes_key))
+ } else {
+ Err(KeyError(()))
+ }
+ }
+ }
+}
+
+/// Performs AES IGE encryption or decryption
+///
+/// # Panics
+///
+/// Panics if `in_` is not the same length as `out`, if that length is not a multiple of 16, or if
+/// `iv` is not at least 32 bytes.
+pub fn aes_ige(in_: &[u8], out: &mut [u8], key: &AesKey, iv: &mut [u8], mode: Mode) {
+ unsafe {
+ assert!(in_.len() == out.len());
+ assert!(in_.len() % ffi::AES_BLOCK_SIZE as usize == 0);
+ assert!(iv.len() >= ffi::AES_BLOCK_SIZE as usize * 2);
+
+ let mode = match mode {
+ Mode::Encrypt => ffi::AES_ENCRYPT,
+ Mode::Decrypt => ffi::AES_DECRYPT,
+ };
+ ffi::AES_ige_encrypt(in_.as_ptr() as *const _,
+ out.as_mut_ptr() as *mut _,
+ in_.len(),
+ &key.0,
+ iv.as_mut_ptr() as *mut _,
+ mode);
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use hex::FromHex;
+
+ use symm::Mode;
+ use super::*;
+
+ // From https://www.mgp25.com/AESIGE/
+ #[test]
+ fn ige_vector_1() {
+ let raw_key = "000102030405060708090A0B0C0D0E0F";
+ let raw_iv = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F";
+ let raw_pt = "0000000000000000000000000000000000000000000000000000000000000000";
+ let raw_ct = "1A8519A6557BE652E9DA8E43DA4EF4453CF456B4CA488AA383C79C98B34797CB";
+
+ let key = AesKey::new_encrypt(&Vec::from_hex(raw_key).unwrap()).unwrap();
+ let mut iv = Vec::from_hex(raw_iv).unwrap();
+ let pt = Vec::from_hex(raw_pt).unwrap();
+ let ct = Vec::from_hex(raw_ct).unwrap();
+
+ let mut ct_actual = vec![0; ct.len()];
+ aes_ige(&pt, &mut ct_actual, &key, &mut iv, Mode::Encrypt);
+ assert_eq!(ct_actual, ct);
+
+ let key = AesKey::new_decrypt(&Vec::from_hex(raw_key).unwrap()).unwrap();
+ let mut iv = Vec::from_hex(raw_iv).unwrap();
+ let mut pt_actual = vec![0; pt.len()];
+ aes_ige(&ct, &mut pt_actual, &key, &mut iv, Mode::Decrypt);
+ assert_eq!(pt_actual, pt);
+ }
+}
diff --git a/openssl/src/asn1.rs b/openssl/src/asn1.rs
index 998f9878..a74f22e8 100644
--- a/openssl/src/asn1.rs
+++ b/openssl/src/asn1.rs
@@ -1,5 +1,6 @@
use ffi;
-use libc::c_long;
+use foreign_types::{ForeignType, ForeignTypeRef};
+use libc::{c_long, c_char, c_int};
use std::fmt;
use std::ptr;
use std::slice;
@@ -7,11 +8,35 @@ use std::str;
use {cvt, cvt_p};
use bio::MemBio;
-use crypto::CryptoString;
use error::ErrorStack;
-use types::{OpenSslType, OpenSslTypeRef};
+use nid::Nid;
+use string::OpensslString;
-type_!(Asn1Time, Asn1TimeRef, ffi::ASN1_TIME, ffi::ASN1_TIME_free);
+foreign_type! {
+ type CType = ffi::ASN1_GENERALIZEDTIME;
+ fn drop = ffi::ASN1_GENERALIZEDTIME_free;
+
+ pub struct Asn1GeneralizedTime;
+ pub struct Asn1GeneralizedTimeRef;
+}
+
+impl fmt::Display for Asn1GeneralizedTimeRef {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ unsafe {
+ let mem_bio = try!(MemBio::new());
+ try!(cvt(ffi::ASN1_GENERALIZEDTIME_print(mem_bio.as_ptr(), self.as_ptr())));
+ write!(f, "{}", str::from_utf8_unchecked(mem_bio.get_buf()))
+ }
+ }
+}
+
+foreign_type! {
+ type CType = ffi::ASN1_TIME;
+ fn drop = ffi::ASN1_TIME_free;
+
+ pub struct Asn1Time;
+ pub struct Asn1TimeRef;
+}
impl fmt::Display for Asn1TimeRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -39,10 +64,16 @@ impl Asn1Time {
}
}
-type_!(Asn1String, Asn1StringRef, ffi::ASN1_STRING, ffi::ASN1_STRING_free);
+foreign_type! {
+ type CType = ffi::ASN1_STRING;
+ fn drop = ffi::ASN1_STRING_free;
+
+ pub struct Asn1String;
+ pub struct Asn1StringRef;
+}
impl Asn1StringRef {
- pub fn as_utf8(&self) -> Result<CryptoString, ErrorStack> {
+ pub fn as_utf8(&self) -> Result<OpensslString, ErrorStack> {
unsafe {
let mut ptr = ptr::null_mut();
let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr());
@@ -50,7 +81,7 @@ impl Asn1StringRef {
return Err(ErrorStack::get());
}
- Ok(CryptoString::from_raw_parts(ptr, len as usize))
+ Ok(OpensslString::from_ptr(ptr as *mut c_char))
}
}
@@ -63,7 +94,77 @@ impl Asn1StringRef {
}
}
-type_!(Asn1Integer, Asn1IntegerRef, ffi::ASN1_INTEGER, ffi::ASN1_INTEGER_free);
+foreign_type! {
+ type CType = ffi::ASN1_INTEGER;
+ fn drop = ffi::ASN1_INTEGER_free;
+
+ pub struct Asn1Integer;
+ pub struct Asn1IntegerRef;
+}
+
+impl Asn1IntegerRef {
+ pub fn get(&self) -> i64 {
+ unsafe {
+ ::ffi::ASN1_INTEGER_get(self.as_ptr()) as i64
+ }
+ }
+
+ pub fn set(&mut self, value: i32) -> Result<(), ErrorStack>
+ {
+ unsafe {
+ cvt(::ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ())
+ }
+ }
+}
+
+foreign_type! {
+ type CType = ffi::ASN1_BIT_STRING;
+ fn drop = ffi::ASN1_BIT_STRING_free;
+
+ pub struct Asn1BitString;
+ pub struct Asn1BitStringRef;
+}
+
+impl Asn1BitStringRef {
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(ASN1_STRING_data(self.as_ptr() as *mut _), self.len()) }
+ }
+
+ pub fn len(&self) -> usize {
+ unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *mut _) as usize }
+ }
+}
+
+foreign_type! {
+ type CType = ffi::ASN1_OBJECT;
+ fn drop = ffi::ASN1_OBJECT_free;
+
+ pub struct Asn1Object;
+ pub struct Asn1ObjectRef;
+}
+
+impl Asn1ObjectRef {
+ /// Returns the NID associated with this OID.
+ pub fn nid(&self) -> Nid {
+ unsafe {
+ Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr()))
+ }
+ }
+}
+
+impl fmt::Display for Asn1ObjectRef {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ unsafe {
+ let mut buf = [0; 80];
+ let len = ffi::OBJ_obj2txt(buf.as_mut_ptr() as *mut _,
+ buf.len() as c_int,
+ self.as_ptr(),
+ 0);
+ let s = try!(str::from_utf8(&buf[..len as usize]).map_err(|_| fmt::Error));
+ fmt.write_str(s)
+ }
+ }
+}
#[cfg(any(ossl101, ossl102))]
use ffi::ASN1_STRING_data;
diff --git a/openssl/src/bn.rs b/openssl/src/bn.rs
index ec0b1aba..9557b5bb 100644
--- a/openssl/src/bn.rs
+++ b/openssl/src/bn.rs
@@ -1,4 +1,5 @@
use ffi;
+use foreign_types::{ForeignType, ForeignTypeRef};
use libc::c_int;
use std::cmp::Ordering;
use std::ffi::CString;
@@ -7,9 +8,23 @@ use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub, Deref};
use {cvt, cvt_p, cvt_n};
use asn1::Asn1Integer;
-use crypto::CryptoString;
use error::ErrorStack;
-use types::{OpenSslType, OpenSslTypeRef};
+use string::OpensslString;
+
+#[cfg(ossl10x)]
+use ffi::{get_rfc2409_prime_768 as BN_get_rfc2409_prime_768,
+ get_rfc2409_prime_1024 as BN_get_rfc2409_prime_1024,
+ get_rfc3526_prime_1536 as BN_get_rfc3526_prime_1536,
+ get_rfc3526_prime_2048 as BN_get_rfc3526_prime_2048,
+ get_rfc3526_prime_3072 as BN_get_rfc3526_prime_3072,
+ get_rfc3526_prime_4096 as BN_get_rfc3526_prime_4096,
+ get_rfc3526_prime_6144 as BN_get_rfc3526_prime_6144,
+ get_rfc3526_prime_8192 as BN_get_rfc3526_prime_8192};
+
+#[cfg(ossl110)]
+use ffi::{BN_get_rfc2409_prime_768, BN_get_rfc2409_prime_1024, BN_get_rfc3526_prime_1536,
+ BN_get_rfc3526_prime_2048, BN_get_rfc3526_prime_3072, BN_get_rfc3526_prime_4096,
+ BN_get_rfc3526_prime_6144, BN_get_rfc3526_prime_8192};
/// Options for the most significant bits of a randomly generated `BigNum`.
pub struct MsbOption(c_int);
@@ -26,7 +41,13 @@ pub const MSB_ONE: MsbOption = MsbOption(0);
/// of bits in the original numbers.
pub const TWO_MSB_ONE: MsbOption = MsbOption(1);
-type_!(BigNumContext, BigNumContextRef, ffi::BN_CTX, ffi::BN_CTX_free);
+foreign_type! {
+ type CType = ffi::BN_CTX;
+ fn drop = ffi::BN_CTX_free;
+
+ pub struct BigNumContext;
+ pub struct BigNumContextRef;
+}
impl BigNumContext {
/// Returns a new `BigNumContext`.
@@ -470,12 +491,12 @@ impl BigNumRef {
/// # use openssl::bn::BigNum;
/// let s = -BigNum::from_u32(12345).unwrap();
///
- /// assert_eq!(&*s.to_dec_str().unwrap(), "-12345");
+ /// assert_eq!(&**s.to_dec_str().unwrap(), "-12345");
/// ```
- pub fn to_dec_str(&self) -> Result<CryptoString, ErrorStack> {
+ pub fn to_dec_str(&self) -> Result<OpensslString, ErrorStack> {
unsafe {
let buf = try!(cvt_p(ffi::BN_bn2dec(self.as_ptr())));
- Ok(CryptoString::from_null_terminated(buf))
+ Ok(OpensslString::from_ptr(buf))
}
}
@@ -485,12 +506,12 @@ impl BigNumRef {
/// # use openssl::bn::BigNum;
/// let s = -BigNum::from_u32(0x99ff).unwrap();
///
- /// assert_eq!(&*s.to_hex_str().unwrap(), "-99FF");
+ /// assert_eq!(&**s.to_hex_str().unwrap(), "-99FF");
/// ```
- pub fn to_hex_str(&self) -> Result<CryptoString, ErrorStack> {
+ pub fn to_hex_str(&self) -> Result<OpensslString, ErrorStack> {
unsafe {
let buf = try!(cvt_p(ffi::BN_bn2hex(self.as_ptr())));
- Ok(CryptoString::from_null_terminated(buf))
+ Ok(OpensslString::from_ptr(buf))
}
}
@@ -503,7 +524,13 @@ impl BigNumRef {
}
}
-type_!(BigNum, BigNumRef, ffi::BIGNUM, ffi::BN_free);
+foreign_type! {
+ type CType = ffi::BIGNUM;
+ fn drop = ffi::BN_free;
+
+ pub struct BigNum;
+ pub struct BigNumRef;
+}
impl BigNum {
/// Creates a new `BigNum` with the value 0.
@@ -525,6 +552,7 @@ impl BigNum {
/// Creates a `BigNum` from a decimal string.
pub fn from_dec_str(s: &str) -> Result<BigNum, ErrorStack> {
unsafe {
+ ffi::init();
let c_str = CString::new(s.as_bytes()).unwrap();
let mut bn = ptr::null_mut();
try!(cvt(ffi::BN_dec2bn(&mut bn, c_str.as_ptr() as *const _)));
@@ -535,6 +563,7 @@ impl BigNum {
/// Creates a `BigNum` from a hexadecimal string.
pub fn from_hex_str(s: &str) -> Result<BigNum, ErrorStack> {
unsafe {
+ ffi::init();
let c_str = CString::new(s.as_bytes()).unwrap();
let mut bn = ptr::null_mut();
try!(cvt(ffi::BN_hex2bn(&mut bn, c_str.as_ptr() as *const _)));
@@ -542,6 +571,62 @@ impl BigNum {
}
}
+ pub fn get_rfc2409_prime_768() -> Result<BigNum, ErrorStack> {
+ unsafe {
+ ffi::init();
+ cvt_p(BN_get_rfc2409_prime_768(ptr::null_mut())).map(BigNum)
+ }
+ }
+
+ pub fn get_rfc2409_prime_1024() -> Result<BigNum, ErrorStack> {
+ unsafe {
+ ffi::init();
+ cvt_p(BN_get_rfc2409_prime_1024(ptr::null_mut())).map(BigNum)
+ }
+ }
+
+ pub fn get_rfc3526_prime_1536() -> Result<BigNum, ErrorStack> {
+ unsafe {
+ ffi::init();
+ cvt_p(BN_get_rfc3526_prime_1536(ptr::null_mut())).map(BigNum)
+ }
+ }
+
+ pub fn get_rfc3526_prime_2048() -> Result<BigNum, ErrorStack> {
+ unsafe {
+ ffi::init();
+ cvt_p(BN_get_rfc3526_prime_2048(ptr::null_mut())).map(BigNum)
+ }
+ }
+
+ pub fn get_rfc3526_prime_3072() -> Result<BigNum, ErrorStack> {
+ unsafe {
+ ffi::init();
+ cvt_p(BN_get_rfc3526_prime_3072(ptr::null_mut())).map(BigNum)
+ }
+ }
+
+ pub fn get_rfc3526_prime_4096() -> Result<BigNum, ErrorStack> {
+ unsafe {
+ ffi::init();
+ cvt_p(BN_get_rfc3526_prime_4096(ptr::null_mut())).map(BigNum)
+ }
+ }
+
+ pub fn get_rfc3526_prime_6144() -> Result<BigNum, ErrorStack> {
+ unsafe {
+ ffi::init();
+ cvt_p(BN_get_rfc3526_prime_6144(ptr::null_mut())).map(BigNum)
+ }
+ }
+
+ pub fn get_rfc3526_prime_8192() -> Result<BigNum, ErrorStack> {
+ unsafe {
+ ffi::init();
+ cvt_p(BN_get_rfc3526_prime_8192(ptr::null_mut())).map(BigNum)
+ }
+ }
+
/// Creates a new `BigNum` from an unsigned, big-endian encoded number of arbitrary length.
///
/// ```
@@ -552,6 +637,7 @@ impl BigNum {
/// ```
pub fn from_slice(n: &[u8]) -> Result<BigNum, ErrorStack> {
unsafe {
+ ffi::init();
assert!(n.len() <= c_int::max_value() as usize);
cvt_p(ffi::BN_bin2bn(n.as_ptr(), n.len() as c_int, ptr::null_mut()))
.map(|p| BigNum::from_ptr(p))
diff --git a/openssl/src/conf.rs b/openssl/src/conf.rs
index 5483955d..a648bb15 100644
--- a/openssl/src/conf.rs
+++ b/openssl/src/conf.rs
@@ -22,7 +22,13 @@ impl ConfMethod {
}
}
-type_!(Conf, ConfRef, ffi::CONF, ffi::NCONF_free);
+foreign_type! {
+ type CType = ffi::CONF;
+ fn drop = ffi::NCONF_free;
+
+ pub struct Conf;
+ pub struct ConfRef;
+}
impl Conf {
pub fn new(method: ConfMethod) -> Result<Conf, ErrorStack> {
diff --git a/openssl/src/crypto.rs b/openssl/src/crypto.rs
index ce83cbae..9853d99c 100644
--- a/openssl/src/crypto.rs
+++ b/openssl/src/crypto.rs
@@ -1,59 +1,4 @@
-use libc::{c_char, c_int, c_void};
-use std::fmt;
-use std::ffi::CStr;
-use std::slice;
-use std::ops::Deref;
-use std::str;
+use string::OpensslString;
-pub struct CryptoString(&'static str);
-
-impl Drop for CryptoString {
- fn drop(&mut self) {
- unsafe {
- CRYPTO_free(self.0.as_ptr() as *mut c_void,
- concat!(file!(), "\0").as_ptr() as *const c_char,
- line!() as c_int);
- }
- }
-}
-
-impl Deref for CryptoString {
- type Target = str;
-
- fn deref(&self) -> &str {
- self.0
- }
-}
-
-impl CryptoString {
- pub unsafe fn from_raw_parts(buf: *mut u8, len: usize) -> CryptoString {
- let slice = slice::from_raw_parts(buf, len);
- CryptoString(str::from_utf8_unchecked(slice))
- }
-
- pub unsafe fn from_null_terminated(buf: *mut c_char) -> CryptoString {
- let slice = CStr::from_ptr(buf).to_bytes();
- CryptoString(str::from_utf8_unchecked(slice))
- }
-}
-
-impl fmt::Display for CryptoString {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(self.0, f)
- }
-}
-
-impl fmt::Debug for CryptoString {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Debug::fmt(self.0, f)
- }
-}
-
-#[cfg(not(ossl110))]
-#[allow(non_snake_case)]
-unsafe fn CRYPTO_free(buf: *mut c_void, _: *const c_char, _: c_int) {
- ::ffi::CRYPTO_free(buf);
-}
-
-#[cfg(ossl110)]
-use ffi::CRYPTO_free;
+#[deprecated(note = "renamed to OpensslString", since = "0.9.7")]
+pub type CryptoString = OpensslString;
diff --git a/openssl/src/dh.rs b/openssl/src/dh.rs
index 5a07e50f..a7454150 100644
--- a/openssl/src/dh.rs
+++ b/openssl/src/dh.rs
@@ -1,32 +1,30 @@
-use ffi;
use error::ErrorStack;
-use bio::MemBioSlice;
-use std::ptr;
+use ffi;
+use foreign_types::ForeignTypeRef;
use std::mem;
+use std::ptr;
-use {cvt, cvt_p};
-use bio::MemBio;
+use {cvt, cvt_p, init};
use bn::BigNum;
-use types::OpenSslTypeRef;
-type_!(Dh, DhRef, ffi::DH, ffi::DH_free);
+foreign_type! {
+ type CType = ffi::DH;
+ fn drop = ffi::DH_free;
-impl DhRef {
- /// Encodes the parameters to PEM.
- pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
- let mem_bio = try!(MemBio::new());
+ pub struct Dh;
- unsafe {
- try!(cvt(ffi::PEM_write_bio_DHparams(mem_bio.as_ptr(), self.as_ptr())));
- }
+ pub struct DhRef;
+}
- Ok(mem_bio.get_buf().to_owned())
- }
+impl DhRef {
+ to_pem!(ffi::PEM_write_bio_DHparams);
+ to_der!(ffi::i2d_DHparams);
}
impl Dh {
pub fn from_params(p: BigNum, g: BigNum, q: BigNum) -> Result<Dh, ErrorStack> {
unsafe {
+ init();
let dh = Dh(try!(cvt_p(ffi::DH_new())));
try!(cvt(compat::DH_set0_pqg(dh.0, p.as_ptr(), q.as_ptr(), g.as_ptr())));
mem::forget((p, g, q));
@@ -34,16 +32,8 @@ impl Dh {
}
}
- pub fn from_pem(buf: &[u8]) -> Result<Dh, ErrorStack> {
- let mem_bio = try!(MemBioSlice::new(buf));
- unsafe {
- cvt_p(ffi::PEM_read_bio_DHparams(mem_bio.as_ptr(),
- ptr::null_mut(),
- None,
- ptr::null_mut()))
- .map(Dh)
- }
- }
+ from_pem!(Dh, ffi::PEM_read_bio_DHparams);
+ from_der!(Dh, ffi::d2i_DHparams);
/// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0.
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
@@ -139,7 +129,15 @@ mod tests {
fn test_dh_from_pem() {
let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
let params = include_bytes!("../test/dhparams.pem");
- let dh = Dh::from_pem(params).ok().expect("Failed to load PEM");
+ let dh = Dh::from_pem(params).unwrap();
ctx.set_tmp_dh(&dh).unwrap();
}
+
+ #[test]
+ fn test_dh_from_der() {
+ let params = include_bytes!("../test/dhparams.pem");
+ let dh = Dh::from_pem(params).unwrap();
+ let der = dh.to_der().unwrap();
+ Dh::from_der(&der).unwrap();
+ }
}
diff --git a/openssl/src/dsa.rs b/openssl/src/dsa.rs
index 9dd5669c..60a1afde 100644
--- a/openssl/src/dsa.rs
+++ b/openssl/src/dsa.rs
@@ -1,41 +1,31 @@
-use error::ErrorStack;
use ffi;
+use foreign_types::ForeignTypeRef;
use libc::{c_int, c_char, c_void};
use std::fmt;
use std::ptr;
-use bio::{MemBio, MemBioSlice};
-use bn::BigNumRef;
use {cvt, cvt_p};
-use types::OpenSslTypeRef;
-use util::{CallbackState, invoke_passwd_cb};
+use bio::MemBioSlice;
+use bn::BigNumRef;
+use error::ErrorStack;
+use util::{CallbackState, invoke_passwd_cb_old};
-type_!(Dsa, DsaRef, ffi::DSA, ffi::DSA_free);
+foreign_type! {
+ type CType = ffi::DSA;
+ fn drop = ffi::DSA_free;
-impl DsaRef {
- /// 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());
+ pub struct Dsa;
+ pub struct DsaRef;
+}
- unsafe {
- try!(cvt(ffi::PEM_write_bio_DSAPrivateKey(mem_bio.as_ptr(), self.as_ptr(),
- ptr::null(), ptr::null_mut(), 0,
- None, ptr::null_mut())))
- };
+impl DsaRef {
+ private_key_to_pem!(ffi::PEM_write_bio_DSAPrivateKey);
+ public_key_to_pem!(ffi::PEM_write_bio_DSA_PUBKEY);
- Ok(mem_bio.get_buf().to_owned())
- }
-
- /// 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!(cvt(ffi::PEM_write_bio_DSA_PUBKEY(mem_bio.as_ptr(), self.as_ptr())));
- }
- Ok(mem_bio.get_buf().to_owned())
- }
+ private_key_to_der!(ffi::i2d_DSAPrivateKey);
+ public_key_to_der!(ffi::i2d_DSAPublicKey);
+ // FIXME should return u32
pub fn size(&self) -> Option<u32> {
if self.q().is_some() {
unsafe { Some(ffi::DSA_size(self.as_ptr()) as u32) }
@@ -103,25 +93,12 @@ impl Dsa {
}
}
- /// 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));
+ private_key_from_pem!(Dsa, ffi::PEM_read_bio_DSAPrivateKey);
+ private_key_from_der!(Dsa, ffi::d2i_DSAPrivateKey);
+ public_key_from_pem!(Dsa, ffi::PEM_read_bio_DSA_PUBKEY);
+ public_key_from_der!(Dsa, ffi::d2i_DSAPublicKey);
- 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<F>(buf: &[u8], pass_cb: F) -> Result<Dsa, ErrorStack>
where F: FnOnce(&mut [c_char]) -> usize
{
@@ -133,25 +110,11 @@ 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::<F>),
+ Some(invoke_passwd_cb_old::<F>),
cb_ptr)));
Ok(Dsa(dsa))
}
}
-
- /// 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!(cvt_p(ffi::PEM_read_bio_DSA_PUBKEY(mem_bio.as_ptr(),
- ptr::null_mut(),
- None,
- ptr::null_mut())));
- Ok(Dsa(dsa))
- }
- }
}
impl fmt::Debug for Dsa {
@@ -193,7 +156,7 @@ mod compat {
#[cfg(test)]
mod test {
- use libc::c_char;
+ use symm::Cipher;
use super::*;
@@ -204,17 +167,26 @@ mod test {
#[test]
pub fn test_password() {
+ let key = include_bytes!("../test/dsa-encrypted.pem");
+ Dsa::private_key_from_pem_passphrase(key, b"mypass").unwrap();
+ }
+
+ #[test]
+ fn test_to_password() {
+ let key = Dsa::generate(2048).unwrap();
+ let pem = key.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar").unwrap();
+ Dsa::private_key_from_pem_passphrase(&pem, b"foobar").unwrap();
+ assert!(Dsa::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err());
+ }
+
+ #[test]
+ pub fn test_password_callback() {
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;
- 6
+ password[..6].copy_from_slice(b"mypass");
+ Ok(6)
})
.unwrap();
diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs
new file mode 100644
index 00000000..37815021
--- /dev/null
+++ b/openssl/src/ec.rs
@@ -0,0 +1,484 @@
+use ffi;
+use foreign_types::{ForeignType, ForeignTypeRef};
+use std::ptr;
+use std::mem;
+use libc::c_int;
+
+use {cvt, cvt_n, cvt_p, init};
+use bn::{BigNumRef, BigNumContextRef};
+use error::ErrorStack;
+use nid::Nid;
+
+pub const POINT_CONVERSION_COMPRESSED: PointConversionForm =
+ PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_COMPRESSED);
+
+pub const POINT_CONVERSION_UNCOMPRESSED: PointConversionForm =
+ PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED);
+
+pub const POINT_CONVERSION_HYBRID: PointConversionForm =
+ PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_HYBRID);
+
+// OPENSSL_EC_EXPLICIT_CURVE, but that was only added in 1.1.
+// Man page documents that 0 can be used in older versions.
+pub const EXPLICIT_CURVE: Asn1Flag = Asn1Flag(0);
+pub const NAMED_CURVE: Asn1Flag = Asn1Flag(ffi::OPENSSL_EC_NAMED_CURVE);
+
+#[derive(Copy, Clone)]
+pub struct PointConversionForm(ffi::point_conversion_form_t);
+
+#[derive(Copy, Clone)]
+pub struct Asn1Flag(c_int);
+
+foreign_type! {
+ type CType = ffi::EC_GROUP;
+ fn drop = ffi::EC_GROUP_free;
+
+ pub struct EcGroup;
+ pub struct EcGroupRef;
+}
+
+impl EcGroup {
+ /// Returns the group of a standard named curve.
+ pub fn from_curve_name(nid: Nid) -> Result<EcGroup, ErrorStack> {
+ unsafe {
+ init();
+ cvt_p(ffi::EC_GROUP_new_by_curve_name(nid.as_raw())).map(EcGroup)
+ }
+ }
+}
+
+impl EcGroupRef {
+ /// Places the components of a curve over a prime field in the provided `BigNum`s.
+ pub fn components_gfp(&self,
+ p: &mut BigNumRef,
+ a: &mut BigNumRef,
+ b: &mut BigNumRef,
+ ctx: &mut BigNumContextRef)
+ -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EC_GROUP_get_curve_GFp(self.as_ptr(),
+ p.as_ptr(),
+ a.as_ptr(),
+ b.as_ptr(),
+ ctx.as_ptr()))
+ .map(|_| ())
+ }
+ }
+
+ /// Places the components of a curve over a binary field in the provided `BigNum`s.
+ pub fn components_gf2m(&self,
+ p: &mut BigNumRef,
+ a: &mut BigNumRef,
+ b: &mut BigNumRef,
+ ctx: &mut BigNumContextRef)
+ -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EC_GROUP_get_curve_GF2m(self.as_ptr(),
+ p.as_ptr(),
+ a.as_ptr(),
+ b.as_ptr(),
+ ctx.as_ptr()))
+ .map(|_| ())
+ }
+ }
+
+ /// Returns the degree of the curve.
+ pub fn degree(&self) -> u32 {
+ unsafe { ffi::EC_GROUP_get_degree(self.as_ptr()) as u32 }
+ }
+
+ /// Places the order of the curve in the provided `BigNum`.
+ pub fn order(&self,
+ order: &mut BigNumRef,
+ ctx: &mut BigNumContextRef)
+ -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EC_GROUP_get_order(self.as_ptr(), order.as_ptr(), ctx.as_ptr())).map(|_| ())
+ }
+ }
+
+ /// Sets the flag determining if the group corresponds to a named curve or must be explicitly
+ /// parameterized.
+ ///
+ /// This defaults to `EXPLICIT_CURVE` in OpenSSL 1.0.1 and 1.0.2, but `NAMED_CURVE` in OpenSSL
+ /// 1.1.0.
+ pub fn set_asn1_flag(&mut self, flag: Asn1Flag) {
+ unsafe {
+ ffi::EC_GROUP_set_asn1_flag(self.as_ptr(), flag.0);
+ }
+ }
+}
+
+foreign_type! {
+ type CType = ffi::EC_POINT;
+ fn drop = ffi::EC_POINT_free;
+
+ pub struct EcPoint;
+ pub struct EcPointRef;
+}
+
+impl EcPointRef {
+ /// Computes `a + b`, storing the result in `self`.
+ pub fn add(&mut self,
+ group: &EcGroupRef,
+ a: &EcPointRef,
+ b: &EcPointRef,
+ ctx: &mut BigNumContextRef)
+ -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EC_POINT_add(group.as_ptr(),
+ self.as_ptr(),
+ a.as_ptr(),
+ b.as_ptr(),
+ ctx.as_ptr()))
+ .map(|_| ())
+ }
+ }
+
+ /// Computes `q * m`, storing the result in `self`.
+ pub fn mul(&mut self,
+ group: &EcGroupRef,
+ q: &EcPointRef,
+ m: &BigNumRef,
+ ctx: &BigNumContextRef)
+ -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EC_POINT_mul(group.as_ptr(),
+ self.as_ptr(),
+ ptr::null(),
+ q.as_ptr(),
+ m.as_ptr(),
+ ctx.as_ptr()))
+ .map(|_| ())
+ }
+ }
+
+ /// Computes `generator * n`, storing the result ing `self`.
+ pub fn mul_generator(&mut self,
+ group: &EcGroupRef,
+ n: &BigNumRef,
+ ctx: &BigNumContextRef)
+ -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EC_POINT_mul(group.as_ptr(),
+ self.as_ptr(),
+ n.as_ptr(),
+ ptr::null(),
+ ptr::null(),
+ ctx.as_ptr()))
+ .map(|_| ())
+ }
+ }
+
+ /// Computes `generator * n + q * m`, storing the result in `self`.
+ pub fn mul_full(&mut self,
+ group: &EcGroupRef,
+ n: &BigNumRef,
+ q: &EcPointRef,
+ m: &BigNumRef,
+ ctx: &mut BigNumContextRef)
+ -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EC_POINT_mul(group.as_ptr(),
+ self.as_ptr(),
+ n.as_ptr(),
+ q.as_ptr(),
+ m.as_ptr(),
+ ctx.as_ptr()))
+ .map(|_| ())
+ }
+ }
+
+ /// Inverts `self`.
+ pub fn invert(&mut self, group: &EcGroupRef, ctx: &BigNumContextRef) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::EC_POINT_invert(group.as_ptr(), self.as_ptr(), ctx.as_ptr())).map(|_| ())
+ }
+ }
+
+ /// Serializes the point to a binary representation.
+ pub fn to_bytes(&self,
+ group: &EcGroupRef,
+ form: PointConversionForm,
+ ctx: &mut BigNumContextRef)
+ -> Result<Vec<u8>, ErrorStack> {
+ unsafe {
+ let len = ffi::EC_POINT_point2oct(group.as_ptr(),
+ self.as_ptr(),
+ form.0,
+ ptr::null_mut(),
+ 0,
+ ctx.as_ptr());
+ if len == 0 {
+ return Err(ErrorStack::get());
+ }
+ let mut buf = vec![0; len];
+ let len = ffi::EC_POINT_point2oct(group.as_ptr(),
+ self.as_ptr(),
+ form.0,
+ buf.as_mut_ptr(),
+ len,
+ ctx.as_ptr());
+ if len == 0 {
+ Err(ErrorStack::get())
+ } else {
+ Ok(buf)
+ }
+ }
+ }
+
+ /// Determines if this point is equal to another.
+ pub fn eq(&self,
+ group: &EcGroupRef,
+ other: &EcPointRef,
+ ctx: &mut BigNumContextRef)
+ -> Result<bool, ErrorStack> {
+ unsafe {
+ let res = try!(cvt_n(ffi::EC_POINT_cmp(group.as_ptr(),
+ self.as_ptr(),
+ other.as_ptr(),
+ ctx.as_ptr())));
+ Ok(res == 0)
+ }
+ }
+}
+
+impl EcPoint {
+ /// Creates a new point on the specified curve.
+ pub fn new(group: &EcGroupRef) -> Result<EcPoint, ErrorStack> {
+ unsafe { cvt_p(ffi::EC_POINT_new(group.as_ptr())).map(EcPoint) }
+ }
+
+ pub fn from_bytes(group: &EcGroupRef,
+ buf: &[u8],
+ ctx: &mut BigNumContextRef)
+ -> Result<EcPoint, ErrorStack> {
+ let point = try!(EcPoint::new(group));
+ unsafe {
+ try!(cvt(ffi::EC_POINT_oct2point(group.as_ptr(),
+ point.as_ptr(),
+ buf.as_ptr(),
+ buf.len(),
+ ctx.as_ptr())));
+ }
+ Ok(point)
+ }
+}
+
+foreign_type! {
+ type CType = ffi::EC_KEY;
+ fn drop = ffi::EC_KEY_free;
+
+ pub struct EcKey;
+ pub struct EcKeyRef;
+}
+
+impl EcKeyRef {
+ private_key_to_pem!(ffi::PEM_write_bio_ECPrivateKey);
+ private_key_to_der!(ffi::i2d_ECPrivateKey);
+
+ pub fn group(&self) -> Option<&EcGroupRef> {
+ unsafe {
+ let ptr = ffi::EC_KEY_get0_group(self.as_ptr());
+ if ptr.is_null() {
+ None
+ } else {
+ Some(EcGroupRef::from_ptr(ptr as *mut _))
+ }
+ }
+ }
+
+ pub fn public_key(&self) -> Option<&EcPointRef> {
+ unsafe {
+ let ptr = ffi::EC_KEY_get0_public_key(self.as_ptr());
+ if ptr.is_null() {
+ None
+ } else {
+ Some(EcPointRef::from_ptr(ptr as *mut _))
+ }
+ }
+ }
+
+ pub fn private_key(&self) -> Option<&BigNumRef> {
+ unsafe {
+ let ptr = ffi::EC_KEY_get0_private_key(self.as_ptr());
+ if ptr.is_null() {
+ None
+ } else {
+ Some(BigNumRef::from_ptr(ptr as *mut _))
+ }
+ }
+ }
+
+ /// Checks the key for validity.
+ pub fn check_key(&self) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::EC_KEY_check_key(self.as_ptr())).map(|_| ()) }
+ }
+}
+
+impl EcKey {
+ /// Constructs an `EcKey` corresponding to a known curve.
+ ///
+ /// It will not have an associated public or private key. This kind of key is primarily useful
+ /// to be provided to the `set_tmp_ecdh` methods on `Ssl` and `SslContextBuilder`.
+ pub fn from_curve_name(nid: Nid) -> Result<EcKey, ErrorStack> {
+ unsafe {
+ init();
+ cvt_p(ffi::EC_KEY_new_by_curve_name(nid.as_raw())).map(EcKey)
+ }
+ }
+
+ /// Constructs an `EcKey` from the specified group with the associated `EcPoint`, public_key.
+ ///
+ /// This will only have the associated public_key.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use openssl::bn::BigNumContext;
+ /// use openssl::ec::*;
+ /// use openssl::nid;
+ /// use openssl::pkey::PKey;
+ ///
+ /// // get bytes from somewhere, i.e. this will not produce a valid key
+ /// let public_key: Vec<u8> = vec![];
+ ///
+ /// // create an EcKey from the binary form of a EcPoint
+ /// let group = EcGroup::from_curve_name(nid::SECP256K1).unwrap();
+ /// let mut ctx = BigNumContext::new().unwrap();
+ /// let point = EcPoint::from_bytes(&group, &public_key, &mut ctx).unwrap();
+ /// let key = EcKey::from_public_key(&group, &point);
+ /// ```
+ pub fn from_public_key(group: &EcGroupRef, public_key: &EcPointRef) -> Result<EcKey, ErrorStack> {
+ let mut builder = try!(EcKeyBuilder::new());
+ try!(builder.set_group(group));
+ try!(builder.set_public_key(public_key));
+ Ok(builder.build())
+ }
+
+ /// Generates a new public/private key pair on the specified curve.
+ pub fn generate(group: &EcGroupRef) -> Result<EcKey, ErrorStack> {
+ let mut builder = try!(EcKeyBuilder::new());
+ try!(builder.set_group(group));
+ try!(builder.generate_key());
+ Ok(builder.build())
+ }
+
+ #[deprecated(since = "0.9.2", note = "use from_curve_name")]
+ pub fn new_by_curve_name(nid: Nid) -> Result<EcKey, ErrorStack> {
+ EcKey::from_curve_name(nid)
+ }
+
+ private_key_from_pem!(EcKey, ffi::PEM_read_bio_ECPrivateKey);
+ private_key_from_der!(EcKey, ffi::d2i_ECPrivateKey);
+}
+
+
+foreign_type! {
+ type CType = ffi::EC_KEY;
+ fn drop = ffi::EC_KEY_free;
+
+ pub struct EcKeyBuilder;
+ pub struct EcKeyBuilderRef;
+}
+
+impl EcKeyBuilder {
+ pub fn new() -> Result<EcKeyBuilder, ErrorStack> {
+ unsafe {
+ init();
+ cvt_p(ffi::EC_KEY_new()).map(EcKeyBuilder)
+ }
+ }
+
+ pub fn build(self) -> EcKey {
+ unsafe {
+ let key = EcKey::from_ptr(self.as_ptr());
+ mem::forget(self);
+ key
+ }
+ }
+}
+
+impl EcKeyBuilderRef {
+ pub fn set_group(&mut self, group: &EcGroupRef) -> Result<&mut EcKeyBuilderRef, ErrorStack> {
+ unsafe {
+ cvt(ffi::EC_KEY_set_group(self.as_ptr(), group.as_ptr())).map(|_| self)
+ }
+ }
+
+ pub fn set_public_key(&mut self,
+ public_key: &EcPointRef)
+ -> Result<&mut EcKeyBuilderRef, ErrorStack> {
+ unsafe {
+ cvt(ffi::EC_KEY_set_public_key(self.as_ptr(), public_key.as_ptr())).map(|_| self)
+ }
+ }
+
+ pub fn generate_key(&mut self) -> Result<&mut EcKeyBuilderRef, ErrorStack> {
+ unsafe {
+ cvt(ffi::EC_KEY_generate_key(self.as_ptr())).map(|_| self)
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use bn::BigNumContext;
+ use nid;
+ use super::*;
+
+ #[test]
+ fn key_new_by_curve_name() {
+ EcKey::from_curve_name(nid::X9_62_PRIME256V1).unwrap();
+ }
+
+ #[test]
+ fn generate() {
+ let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap();
+ let key = EcKey::generate(&group).unwrap();
+ key.public_key().unwrap();
+ key.private_key().unwrap();
+ }
+
+ #[test]
+ fn point_new() {
+ let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap();
+ EcPoint::new(&group).unwrap();
+ }
+
+ #[test]
+ fn point_bytes() {
+ let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap();
+ let key = EcKey::generate(&group).unwrap();
+ let point = key.public_key().unwrap();
+ let mut ctx = BigNumContext::new().unwrap();
+ let bytes = point.to_bytes(&group, POINT_CONVERSION_COMPRESSED, &mut ctx).unwrap();
+ let point2 = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap();
+ assert!(point.eq(&group, &point2, &mut ctx).unwrap());
+ }
+
+ #[test]
+ fn mul_generator() {
+ let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap();
+ let key = EcKey::generate(&group).unwrap();
+ let mut ctx = BigNumContext::new().unwrap();
+ let mut public_key = EcPoint::new(&group).unwrap();
+ public_key.mul_generator(&group, key.private_key().unwrap(), &mut ctx).unwrap();
+ assert!(public_key.eq(&group, key.public_key().unwrap(), &mut ctx).unwrap());
+ }
+
+ #[test]
+ fn key_from_public_key() {
+ let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap();
+ let key = EcKey::generate(&group).unwrap();
+ let mut ctx = BigNumContext::new().unwrap();
+ let bytes = key.public_key().unwrap().to_bytes(&group, POINT_CONVERSION_COMPRESSED, &mut ctx).unwrap();
+
+ drop(key);
+ let public_key = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap();
+ let ec_key = EcKey::from_public_key(&group, &public_key).unwrap();
+ assert!(ec_key.check_key().is_ok());
+ assert!(ec_key.public_key().is_some());
+ assert!(ec_key.private_key().is_none());
+ }
+}
diff --git a/openssl/src/ec_key.rs b/openssl/src/ec_key.rs
index 41501c14..cb7c4996 100644
--- a/openssl/src/ec_key.rs
+++ b/openssl/src/ec_key.rs
@@ -1,24 +1,3 @@
-use ffi;
+#![deprecated(since = "0.9.2", note = "renamed to `ec`")]
-use cvt_p;
-use error::ErrorStack;
-use nid::Nid;
-
-type_!(EcKey, EcKeyRef, ffi::EC_KEY, ffi::EC_KEY_free);
-
-impl EcKey {
- pub fn new_by_curve_name(nid: Nid) -> Result<EcKey, ErrorStack> {
- unsafe { cvt_p(ffi::EC_KEY_new_by_curve_name(nid.as_raw())).map(EcKey) }
- }
-}
-
-#[cfg(test)]
-mod test {
- use nid;
- use super::*;
-
- #[test]
- fn new_by_curve_name() {
- EcKey::new_by_curve_name(nid::X9_62_PRIME256V1).unwrap();
- }
-}
+pub use ec::{EcKey, EcKeyRef};
diff --git a/openssl/src/error.rs b/openssl/src/error.rs
index 4dd219af..26b96408 100644
--- a/openssl/src/error.rs
+++ b/openssl/src/error.rs
@@ -32,11 +32,11 @@ impl fmt::Display for ErrorStack {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut first = true;
for err in &self.0 {
- if first {
+ if !first {
try!(fmt.write_str(", "));
- first = false;
}
try!(write!(fmt, "{}", err));
+ first = false;
}
Ok(())
}
diff --git a/openssl/src/hash.rs b/openssl/src/hash.rs
index 6a13371d..47d6dc7a 100644
--- a/openssl/src/hash.rs
+++ b/openssl/src/hash.rs
@@ -60,7 +60,7 @@ use self::State::*;
///
/// # Examples
///
-/// Calculate a hash in one go.
+/// Calculate a hash in one go:
///
/// ```
/// use openssl::hash::{hash, MessageDigest};
@@ -71,7 +71,7 @@ use self::State::*;
/// assert_eq!(res, spec);
/// ```
///
-/// Use the `Write` trait to supply the input in chunks.
+/// Supply the input in chunks:
///
/// ```
/// use openssl::hash::{Hasher, MessageDigest};
@@ -208,17 +208,17 @@ pub fn hash(t: MessageDigest, data: &[u8]) -> Result<Vec<u8>, ErrorStack> {
#[cfg(test)]
mod tests {
- use serialize::hex::{FromHex, ToHex};
+ use hex::{FromHex, ToHex};
use super::{hash, Hasher, MessageDigest};
use std::io::prelude::*;
fn hash_test(hashtype: MessageDigest, hashtest: &(&str, &str)) {
- let res = hash(hashtype, &*hashtest.0.from_hex().unwrap()).unwrap();
+ let res = hash(hashtype, &Vec::from_hex(hashtest.0).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()).unwrap();
+ let _ = h.write_all(&Vec::from_hex(hashtest.0).unwrap()).unwrap();
let res = h.finish().unwrap();
assert_eq!(res.to_hex(), hashtest.1);
}
@@ -258,7 +258,7 @@ mod tests {
#[test]
fn test_finish_twice() {
let mut h = Hasher::new(MessageDigest::md5()).unwrap();
- h.write_all(&*md5_tests[6].0.from_hex().unwrap()).unwrap();
+ h.write_all(&Vec::from_hex(md5_tests[6].0).unwrap()).unwrap();
h.finish().unwrap();
let res = h.finish().unwrap();
let null = hash(MessageDigest::md5(), &[]).unwrap();
@@ -268,7 +268,7 @@ mod tests {
#[test]
fn test_clone() {
let i = 7;
- let inp = md5_tests[i].0.from_hex().unwrap();
+ let inp = Vec::from_hex(md5_tests[i].0).unwrap();
assert!(inp.len() > 2);
let p = inp.len() / 2;
let h0 = Hasher::new(MessageDigest::md5()).unwrap();
@@ -289,7 +289,7 @@ mod tests {
println!("Clone a finished hasher");
let mut h3 = h1.clone();
- h3.write_all(&*md5_tests[i + 1].0.from_hex().unwrap()).unwrap();
+ h3.write_all(&Vec::from_hex(md5_tests[i + 1].0).unwrap()).unwrap();
let res = h3.finish().unwrap();
assert_eq!(res.to_hex(), md5_tests[i + 1].1);
}
diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs
index 5750b410..86de35d0 100644
--- a/openssl/src/lib.rs
+++ b/openssl/src/lib.rs
@@ -1,15 +1,16 @@
-#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.9.0")]
+#![doc(html_root_url="https://docs.rs/openssl/0.9.6")]
#[macro_use]
extern crate bitflags;
+#[macro_use]
+extern crate foreign_types;
extern crate libc;
#[macro_use]
extern crate lazy_static;
extern crate openssl_sys as ffi;
#[cfg(test)]
-extern crate rustc_serialize as serialize;
-
+extern crate hex;
#[cfg(test)]
extern crate tempdir;
@@ -20,76 +21,38 @@ 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)
- }
-
- fn as_ptr(&self) -> *mut $c {
- self.0
- }
- }
-
- 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;
+pub mod aes;
pub mod asn1;
pub mod bn;
pub mod conf;
pub mod crypto;
pub mod dh;
pub mod dsa;
+pub mod ec;
pub mod ec_key;
pub mod error;
pub mod hash;
pub mod memcmp;
pub mod nid;
+pub mod ocsp;
pub mod pkcs12;
pub mod pkcs5;
pub mod pkey;
pub mod rand;
-pub mod types;
pub mod rsa;
pub mod sign;
pub mod ssl;
+pub mod stack;
+pub mod string;
pub mod symm;
+pub mod types;
pub mod version;
pub mod x509;
-pub mod stack;
#[cfg(any(ossl102, ossl110))]
mod verify;
diff --git a/openssl/src/macros.rs b/openssl/src/macros.rs
new file mode 100644
index 00000000..b2fe0c18
--- /dev/null
+++ b/openssl/src/macros.rs
@@ -0,0 +1,216 @@
+
+macro_rules! private_key_from_pem {
+ ($t:ident, $f:path) => {
+ from_pem_inner!(/// Deserializes a PEM-formatted private key.
+ private_key_from_pem, $t, $f);
+
+ /// Deserializes a PEM-formatted private key, using the supplied password if the key is
+ /// encrypted.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `passphrase` contains an embedded null.
+ pub fn private_key_from_pem_passphrase(pem: &[u8],
+ passphrase: &[u8])
+ -> Result<$t, ::error::ErrorStack> {
+ unsafe {
+ ffi::init();
+ let bio = try!(::bio::MemBioSlice::new(pem));
+ let passphrase = ::std::ffi::CString::new(passphrase).unwrap();
+ cvt_p($f(bio.as_ptr(),
+ ptr::null_mut(),
+ None,
+ passphrase.as_ptr() as *const _ as *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<F>(pem: &[u8],
+ callback: F)
+ -> Result<$t, ::error::ErrorStack>
+ where F: FnOnce(&mut [u8]) -> Result<usize, ::error::ErrorStack>
+ {
+ 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::<F>),
+ &mut cb as *mut _ as *mut _))
+ .map($t)
+ }
+ }
+ }
+}
+
+macro_rules! private_key_to_pem {
+ ($f:path) => {
+ /// Serializes the private key to PEM.
+ pub fn private_key_to_pem(&self) -> Result<Vec<u8>, ::error::ErrorStack> {
+ unsafe {
+ let bio = try!(::bio::MemBio::new());
+ try!(cvt($f(bio.as_ptr(),
+ self.as_ptr(),
+ ptr::null(),
+ ptr::null_mut(),
+ -1,
+ None,
+ ptr::null_mut())));
+ Ok(bio.get_buf().to_owned())
+ }
+ }
+
+ /// Serializes the private key to PEM, encrypting it with the specified symmetric cipher and
+ /// passphrase.
+ pub fn private_key_to_pem_passphrase(&self,
+ cipher: ::symm::Cipher,
+ passphrase: &[u8])
+ -> Result<Vec<u8>, ::error::ErrorStack> {
+ unsafe {
+ let bio = try!(::bio::MemBio::new());
+ assert!(passphrase.len() <= ::libc::c_int::max_value() as usize);
+ try!(cvt($f(bio.as_ptr(),
+ self.as_ptr(),
+ cipher.as_ptr(),
+ passphrase.as_ptr() as *const _ as *mut _,
+ passphrase.len() as ::libc::c_int,
+ None,
+ ptr::null_mut())));
+ Ok(bio.get_buf().to_owned())
+ }
+ }
+ }
+}
+
+macro_rules! to_pem_inner {
+ (#[$m:meta] $n:ident, $f:path) => {
+ #[$m]
+ pub fn $n(&self) -> Result<Vec<u8>, ::error::ErrorStack> {
+ unsafe {
+ let bio = try!(::bio::MemBio::new());
+ try!(cvt($f(bio.as_ptr(), self.as_ptr())));
+ Ok(bio.get_buf().to_owned())
+ }
+ }
+ }
+}
+
+macro_rules! public_key_to_pem {
+ ($f:path) => {
+ to_pem_inner!(/// Serializes a public key to PEM.
+ public_key_to_pem, $f);
+ }
+}
+
+macro_rules! to_pem {
+ ($f:path) => {
+ to_pem_inner!(/// Serializes this value to PEM.
+ to_pem, $f);
+ }
+}
+
+macro_rules! to_der_inner {
+ (#[$m:meta] $n:ident, $f:path) => {
+ #[$m]
+ pub fn $n(&self) -> Result<Vec<u8>, ::error::ErrorStack> {
+ unsafe {
+ let len = try!(::cvt($f(::foreign_types::ForeignTypeRef::as_ptr(self),
+ ptr::null_mut())));
+ let mut buf = vec![0; len as usize];
+ try!(::cvt($f(::foreign_types::ForeignTypeRef::as_ptr(self),
+ &mut buf.as_mut_ptr())));
+ Ok(buf)
+ }
+ }
+ };
+}
+
+macro_rules! to_der {
+ ($f:path) => {
+ to_der_inner!(/// Serializes this value to DER.
+ to_der, $f);
+ }
+}
+
+macro_rules! private_key_to_der {
+ ($f:path) => {
+ to_der_inner!(/// Serializes the private key to DER.
+ private_key_to_der, $f);
+ }
+}
+
+macro_rules! public_key_to_der {
+ ($f:path) => {
+ to_der_inner!(/// Serializes the public key to DER.
+ public_key_to_der, $f);
+ }
+}
+
+macro_rules! from_der_inner {
+ (#[$m:meta] $n:ident, $t:ident, $f:path) => {
+ #[$m]
+ pub fn $n(der: &[u8]) -> Result<$t, ::error::ErrorStack> {
+ unsafe {
+ ::ffi::init();
+ let len = ::std::cmp::min(der.len(), ::libc::c_long::max_value() as usize) as ::libc::c_long;
+ ::cvt_p($f(::std::ptr::null_mut(), &mut der.as_ptr(), len))
+ .map($t)
+ }
+ }
+ }
+}
+
+macro_rules! from_der {
+ ($t:ident, $f:path) => {
+ from_der_inner!(/// Deserializes a value from DER-formatted data.
+ from_der, $t, $f);
+ }
+}
+
+macro_rules! private_key_from_der {
+ ($t:ident, $f:path) => {
+ from_der_inner!(/// Deserializes a private key from DER-formatted data.
+ private_key_from_der, $t, $f);
+ }
+}
+
+macro_rules! public_key_from_der {
+ ($t:ident, $f:path) => {
+ from_der_inner!(/// Deserializes a public key from DER-formatted data.
+ public_key_from_der, $t, $f);
+ }
+}
+
+macro_rules! from_pem_inner {
+ (#[$m:meta] $n:ident, $t:ident, $f:path) => {
+ #[$m]
+ pub fn $n(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)
+ }
+ }
+ }
+}
+
+macro_rules! public_key_from_pem {
+ ($t:ident, $f:path) => {
+ from_pem_inner!(/// Deserializes a public key from PEM-formatted data.
+ public_key_from_pem, $t, $f);
+ }
+}
+
+macro_rules! from_pem {
+ ($t:ident, $f:path) => {
+ from_pem_inner!(/// Deserializes a value from PEM-formatted data.
+ from_pem, $t, $f);
+ }
+}
diff --git a/openssl/src/ocsp.rs b/openssl/src/ocsp.rs
new file mode 100644
index 00000000..67d51838
--- /dev/null
+++ b/openssl/src/ocsp.rs
@@ -0,0 +1,304 @@
+use ffi;
+use foreign_types::ForeignTypeRef;
+use libc::{c_int, c_long, c_ulong};
+use std::ptr;
+use std::mem;
+
+use {cvt, cvt_p};
+use asn1::Asn1GeneralizedTimeRef;
+use error::ErrorStack;
+use hash::MessageDigest;
+use stack::StackRef;
+use x509::store::X509StoreRef;
+use x509::{X509, X509Ref};
+
+bitflags! {
+ pub flags Flag: c_ulong {
+ const FLAG_NO_CERTS = ffi::OCSP_NOCERTS,
+ const FLAG_NO_INTERN = ffi::OCSP_NOINTERN,
+ const FLAG_NO_CHAIN = ffi::OCSP_NOCHAIN,
+ const FLAG_NO_VERIFY = ffi::OCSP_NOVERIFY,
+ const FLAG_NO_EXPLICIT = ffi::OCSP_NOEXPLICIT,
+ const FLAG_NO_CA_SIGN = ffi::OCSP_NOCASIGN,
+ const FLAG_NO_DELEGATED = ffi::OCSP_NODELEGATED,
+ const FLAG_NO_CHECKS = ffi::OCSP_NOCHECKS,
+ const FLAG_TRUST_OTHER = ffi::OCSP_TRUSTOTHER,
+ const FLAG_RESPID_KEY = ffi::OCSP_RESPID_KEY,
+ const FLAG_NO_TIME = ffi::OCSP_NOTIME,
+ }
+}
+
+pub const RESPONSE_STATUS_SUCCESSFUL: OcspResponseStatus =
+ OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_SUCCESSFUL);
+pub const RESPONSE_STATUS_MALFORMED_REQUEST: OcspResponseStatus =
+ OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_MALFORMEDREQUEST);
+pub const RESPONSE_STATUS_INTERNAL_ERROR: OcspResponseStatus =
+ OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_INTERNALERROR);
+pub const RESPONSE_STATUS_TRY_LATER: OcspResponseStatus =
+ OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_TRYLATER);
+pub const RESPONSE_STATUS_SIG_REQUIRED: OcspResponseStatus =
+ OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_SIGREQUIRED);
+pub const RESPONSE_STATUS_UNAUTHORIZED: OcspResponseStatus =
+ OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_UNAUTHORIZED);
+
+pub const CERT_STATUS_GOOD: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_GOOD);
+pub const CERT_STATUS_REVOKED: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_REVOKED);
+pub const CERT_STATUS_UNKNOWN: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_UNKNOWN);
+
+pub const REVOKED_STATUS_NO_STATUS: OcspRevokedStatus =
+ OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_NOSTATUS);
+pub const REVOKED_STATUS_UNSPECIFIED: OcspRevokedStatus =
+ OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_UNSPECIFIED);
+pub const REVOKED_STATUS_KEY_COMPROMISE: OcspRevokedStatus =
+ OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_KEYCOMPROMISE);
+pub const REVOKED_STATUS_CA_COMPROMISE: OcspRevokedStatus =
+ OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CACOMPROMISE);
+pub const REVOKED_STATUS_AFFILIATION_CHANGED: OcspRevokedStatus =
+ OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_AFFILIATIONCHANGED);
+pub const REVOKED_STATUS_SUPERSEDED: OcspRevokedStatus =
+ OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_SUPERSEDED);
+pub const REVOKED_STATUS_CESSATION_OF_OPERATION: OcspRevokedStatus =
+ OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CESSATIONOFOPERATION);
+pub const REVOKED_STATUS_CERTIFICATE_HOLD: OcspRevokedStatus =
+ OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CERTIFICATEHOLD);
+pub const REVOKED_STATUS_REMOVE_FROM_CRL: OcspRevokedStatus =
+ OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_REMOVEFROMCRL);
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct OcspResponseStatus(c_int);
+
+impl OcspResponseStatus {
+ pub fn from_raw(raw: c_int) -> OcspResponseStatus {
+ OcspResponseStatus(raw)
+ }
+
+ pub fn as_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct OcspCertStatus(c_int);
+
+impl OcspCertStatus {
+ pub fn from_raw(raw: c_int) -> OcspCertStatus {
+ OcspCertStatus(raw)
+ }
+
+ pub fn as_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct OcspRevokedStatus(c_int);
+
+impl OcspRevokedStatus {
+ pub fn from_raw(raw: c_int) -> OcspRevokedStatus {
+ OcspRevokedStatus(raw)
+ }
+
+ pub fn as_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+pub struct Status<'a> {
+ /// The overall status of the response.
+ pub status: OcspCertStatus,
+ /// If `status` is `CERT_STATUS_REVOKED`, the reason for the revocation.
+ pub reason: OcspRevokedStatus,
+ /// If `status` is `CERT_STATUS_REVOKED`, the time at which the certificate was revoked.
+ pub revocation_time: Option<&'a Asn1GeneralizedTimeRef>,
+ /// The time that this revocation check was performed.
+ pub this_update: &'a Asn1GeneralizedTimeRef,
+ /// The time at which this revocation check expires.
+ pub next_update: &'a Asn1GeneralizedTimeRef,
+}
+
+impl<'a> Status<'a> {
+ /// Checks validity of the `this_update` and `next_update` fields.
+ ///
+ /// The `nsec` parameter specifies an amount of slack time that will be used when comparing
+ /// those times with the current time to account for delays and clock skew.
+ ///
+ /// The `maxsec` parameter limits the maximum age of the `this_update` parameter to prohibit
+ /// very old responses.
+ pub fn check_validity(&self, nsec: u32, maxsec: Option<u32>) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::OCSP_check_validity(self.this_update.as_ptr(),
+ self.next_update.as_ptr(),
+ nsec as c_long,
+ maxsec.map(|n| n as c_long).unwrap_or(-1)))
+ .map(|_| ())
+ }
+ }
+}
+
+foreign_type! {
+ type CType = ffi::OCSP_BASICRESP;
+ fn drop = ffi::OCSP_BASICRESP_free;
+
+ pub struct OcspBasicResponse;
+ pub struct OcspBasicResponseRef;
+}
+
+impl OcspBasicResponseRef {
+ /// Verifies the validity of the response.
+ ///
+ /// The `certs` parameter contains a set of certificates that will be searched when locating the
+ /// OCSP response signing certificate. Some responders do not include this in the response.
+ pub fn verify(&self,
+ certs: &StackRef<X509>,
+ store: &X509StoreRef,
+ flags: Flag)
+ -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::OCSP_basic_verify(self.as_ptr(), certs.as_ptr(), store.as_ptr(), flags.bits()))
+ .map(|_| ())
+ }
+ }
+
+ /// Looks up the status for the specified certificate ID.
+ pub fn find_status<'a>(&'a self, id: &OcspCertIdRef) -> Option<Status<'a>> {
+ unsafe {
+ let mut status = ffi::V_OCSP_CERTSTATUS_UNKNOWN;
+ let mut reason = ffi::OCSP_REVOKED_STATUS_NOSTATUS;
+ let mut revocation_time = ptr::null_mut();
+ let mut this_update = ptr::null_mut();
+ let mut next_update = ptr::null_mut();
+
+ let r = ffi::OCSP_resp_find_status(self.as_ptr(),
+ id.as_ptr(),
+ &mut status,
+ &mut reason,
+ &mut revocation_time,
+ &mut this_update,
+ &mut next_update);
+ if r == 1 {
+ let revocation_time = if revocation_time.is_null() {
+ None
+ } else {
+ Some(Asn1GeneralizedTimeRef::from_ptr(revocation_time))
+ };
+ Some(Status {
+ status: OcspCertStatus(status),
+ reason: OcspRevokedStatus(status),
+ revocation_time: revocation_time,
+ this_update: Asn1GeneralizedTimeRef::from_ptr(this_update),
+ next_update: Asn1GeneralizedTimeRef::from_ptr(next_update),
+ })
+ } else {
+ None
+ }
+ }
+ }
+}
+
+foreign_type! {
+ type CType = ffi::OCSP_CERTID;
+ fn drop = ffi::OCSP_CERTID_free;
+
+ pub struct OcspCertId;
+ pub struct OcspCertIdRef;
+}
+
+impl OcspCertId {
+ /// Constructs a certificate ID for certificate `subject`.
+ pub fn from_cert(digest: MessageDigest,
+ subject: &X509Ref,
+ issuer: &X509Ref)
+ -> Result<OcspCertId, ErrorStack> {
+ unsafe {
+ cvt_p(ffi::OCSP_cert_to_id(digest.as_ptr(), subject.as_ptr(), issuer.as_ptr()))
+ .map(OcspCertId)
+ }
+ }
+}
+
+foreign_type! {
+ type CType = ffi::OCSP_RESPONSE;
+ fn drop = ffi::OCSP_RESPONSE_free;
+
+ pub struct OcspResponse;
+ pub struct OcspResponseRef;
+}
+
+impl OcspResponse {
+ /// Creates an OCSP response from the status and optional body.
+ ///
+ /// A body should only be provided if `status` is `RESPONSE_STATUS_SUCCESSFUL`.
+ pub fn create(status: OcspResponseStatus,
+ body: Option<&OcspBasicResponseRef>)
+ -> Result<OcspResponse, ErrorStack> {
+ unsafe {
+ ffi::init();
+
+ cvt_p(ffi::OCSP_response_create(status.as_raw(),
+ body.map(|r| r.as_ptr()).unwrap_or(ptr::null_mut())))
+ .map(OcspResponse)
+ }
+ }
+
+ from_der!(OcspResponse, ffi::d2i_OCSP_RESPONSE);
+}
+
+impl OcspResponseRef {
+ to_der!(ffi::i2d_OCSP_RESPONSE);
+
+ /// Returns the status of the response.
+ pub fn status(&self) -> OcspResponseStatus {
+ unsafe {
+ OcspResponseStatus(ffi::OCSP_response_status(self.as_ptr()))
+ }
+ }
+
+ /// Returns the basic response.
+ ///
+ /// This will only succeed if `status()` returns `RESPONSE_STATUS_SUCCESSFUL`.
+ pub fn basic(&self) -> Result<OcspBasicResponse, ErrorStack> {
+ unsafe {
+ cvt_p(ffi::OCSP_response_get1_basic(self.as_ptr())).map(OcspBasicResponse)
+ }
+ }
+}
+
+foreign_type! {
+ type CType = ffi::OCSP_REQUEST;
+ fn drop = ffi::OCSP_REQUEST_free;
+
+ pub struct OcspRequest;
+ pub struct OcspRequestRef;
+}
+
+impl OcspRequest {
+ pub fn new() -> Result<OcspRequest, ErrorStack> {
+ unsafe {
+ ffi::init();
+
+ cvt_p(ffi::OCSP_REQUEST_new()).map(OcspRequest)
+ }
+ }
+
+ from_der!(OcspRequest, ffi::d2i_OCSP_REQUEST);
+}
+
+impl OcspRequestRef {
+ to_der!(ffi::i2d_OCSP_REQUEST);
+
+ pub fn add_id(&mut self, id: OcspCertId) -> Result<&mut OcspOneReqRef, ErrorStack> {
+ unsafe {
+ let ptr = try!(cvt_p(ffi::OCSP_request_add0_id(self.as_ptr(), id.as_ptr())));
+ mem::forget(id);
+ Ok(OcspOneReqRef::from_ptr_mut(ptr))
+ }
+ }
+}
+
+foreign_type! {
+ type CType = ffi::OCSP_ONEREQ;
+ fn drop = ffi::OCSP_ONEREQ_free;
+
+ pub struct OcspOneReq;
+ pub struct OcspOneReqRef;
+}
diff --git a/openssl/src/pkcs12.rs b/openssl/src/pkcs12.rs
index 1ef0bf3f..9f014af6 100644
--- a/openssl/src/pkcs12.rs
+++ b/openssl/src/pkcs12.rs
@@ -1,35 +1,31 @@
//! PKCS #12 archives.
use ffi;
-use libc::{c_long, c_uchar};
-use std::cmp;
+use foreign_types::{ForeignType, ForeignTypeRef};
+use libc::c_int;
use std::ptr;
use std::ffi::CString;
use {cvt, cvt_p};
-use pkey::PKey;
+use pkey::{PKey, PKeyRef};
use error::ErrorStack;
use x509::X509;
-use types::{OpenSslType, OpenSslTypeRef};
use stack::Stack;
+use nid;
-type_!(Pkcs12, Pkcs12Ref, ffi::PKCS12, ffi::PKCS12_free);
+foreign_type! {
+ type CType = ffi::PKCS12;
+ fn drop = ffi::PKCS12_free;
-impl Pkcs12 {
- /// Deserializes a `Pkcs12` structure from DER-encoded data.
- pub fn from_der(der: &[u8]) -> Result<Pkcs12, ErrorStack> {
- unsafe {
- ffi::init();
- let mut ptr = der.as_ptr() as *const c_uchar;
- let length = cmp::min(der.len(), c_long::max_value() as usize) as c_long;
- let p12 = try!(cvt_p(ffi::d2i_PKCS12(ptr::null_mut(), &mut ptr, length)));
- Ok(Pkcs12(p12))
- }
- }
+ pub struct Pkcs12;
+ pub struct Pkcs12Ref;
}
impl Pkcs12Ref {
+ to_der!(ffi::i2d_PKCS12);
+
/// Extracts the contents of the `Pkcs12`.
+ // FIXME should take an &[u8]
pub fn parse(&self, pass: &str) -> Result<ParsedPkcs12, ErrorStack> {
unsafe {
let pass = CString::new(pass).unwrap();
@@ -57,16 +53,132 @@ impl Pkcs12Ref {
}
}
+impl Pkcs12 {
+ from_der!(Pkcs12, ffi::d2i_PKCS12);
+
+ /// Creates a new builder for a protected pkcs12 certificate.
+ ///
+ /// This uses the defaults from the OpenSSL library:
+ ///
+ /// * `nid_key` - `nid::PBE_WITHSHA1AND3_KEY_TRIPLEDES_CBC`
+ /// * `nid_cert` - `nid::PBE_WITHSHA1AND40BITRC2_CBC`
+ /// * `iter` - `2048`
+ /// * `mac_iter` - `2048`
+ pub fn builder() -> Pkcs12Builder {
+ ffi::init();
+
+ Pkcs12Builder {
+ nid_key: nid::UNDEF, //nid::PBE_WITHSHA1AND3_KEY_TRIPLEDES_CBC,
+ nid_cert: nid::UNDEF, //nid::PBE_WITHSHA1AND40BITRC2_CBC,
+ iter: ffi::PKCS12_DEFAULT_ITER,
+ mac_iter: ffi::PKCS12_DEFAULT_ITER,
+ ca: None,
+ }
+ }
+}
+
pub struct ParsedPkcs12 {
pub pkey: PKey,
pub cert: X509,
pub chain: Stack<X509>,
}
+pub struct Pkcs12Builder {
+ nid_key: nid::Nid,
+ nid_cert: nid::Nid,
+ iter: c_int,
+ mac_iter: c_int,
+ ca: Option<Stack<X509>>,
+}
+
+impl Pkcs12Builder {
+ /// The encryption algorithm that should be used for the key
+ pub fn key_algorithm(&mut self, nid: nid::Nid) -> &mut Self {
+ self.nid_key = nid;
+ self
+ }
+
+ /// The encryption algorithm that should be used for the cert
+ pub fn cert_algorithm(&mut self, nid: nid::Nid) -> &mut Self {
+ self.nid_cert = nid;
+ self
+ }
+
+ /// Key iteration count, default is 2048 as of this writing
+ pub fn key_iter(&mut self, iter: u32) -> &mut Self {
+ self.iter = iter as c_int;
+ self
+ }
+
+ /// MAC iteration count, default is the same as key_iter.
+ ///
+ /// Old implementations don't understand MAC iterations greater than 1, (pre 1.0.1?), if such
+ /// compatibility is required this should be set to 1.
+ pub fn mac_iter(&mut self, mac_iter: u32) -> &mut Self {
+ self.mac_iter = mac_iter as c_int;
+ self
+ }
+
+ /// An additional set of certificates to include in the archive beyond the one provided to
+ /// `build`.
+ pub fn ca(&mut self, ca: Stack<X509>) -> &mut Self {
+ self.ca = Some(ca);
+ self
+ }
+
+ /// Builds the PKCS #12 object
+ ///
+ /// # Arguments
+ ///
+ /// * `password` - the password used to encrypt the key and certificate
+ /// * `friendly_name` - user defined name for the certificate
+ /// * `pkey` - key to store
+ /// * `cert` - certificate to store
+ pub fn build(self,
+ password: &str,
+ friendly_name: &str,
+ pkey: &PKeyRef,
+ cert: &X509) -> Result<Pkcs12, ErrorStack> {
+ unsafe {
+ let pass = CString::new(password).unwrap();
+ let friendly_name = CString::new(friendly_name).unwrap();
+ let pkey = pkey.as_ptr();
+ let cert = cert.as_ptr();
+ let ca = self.ca.as_ref().map(|ca| ca.as_ptr()).unwrap_or(ptr::null_mut());
+ let nid_key = self.nid_key.as_raw();
+ let nid_cert = self.nid_cert.as_raw();
+
+ // According to the OpenSSL docs, keytype is a non-standard extension for MSIE,
+ // It's values are KEY_SIG or KEY_EX, see the OpenSSL docs for more information:
+ // https://www.openssl.org/docs/man1.0.2/crypto/PKCS12_create.html
+ let keytype = 0;
+
+ cvt_p(ffi::PKCS12_create(pass.as_ptr() as *const _ as *mut _,
+ friendly_name.as_ptr() as *const _ as *mut _,
+ pkey,
+ cert,
+ ca,
+ nid_key,
+ nid_cert,
+ self.iter,
+ self.mac_iter,
+ keytype))
+ .map(Pkcs12)
+ }
+ }
+}
+
#[cfg(test)]
mod test {
use hash::MessageDigest;
- use serialize::hex::ToHex;
+ use hex::ToHex;
+
+ use asn1::Asn1Time;
+ use rsa::Rsa;
+ use pkey::PKey;
+ use nid;
+ use x509::{X509, X509Name};
+ use x509::extension::KeyUsage;
use super::*;
@@ -83,4 +195,38 @@ mod test {
assert_eq!(parsed.chain[0].fingerprint(MessageDigest::sha1()).unwrap().to_hex(),
"c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875");
}
+
+ #[test]
+ fn create() {
+ let subject_name = "ns.example.com";
+ let rsa = Rsa::generate(2048).unwrap();
+ let pkey = PKey::from_rsa(rsa).unwrap();
+
+ let mut name = X509Name::builder().unwrap();
+ name.append_entry_by_nid(nid::COMMONNAME, subject_name).unwrap();
+ let name = name.build();
+
+ let key_usage = KeyUsage::new().digital_signature().build().unwrap();;
+
+ let mut builder = X509::builder().unwrap();
+ builder.set_version(2).unwrap();
+ builder.set_not_before(&Asn1Time::days_from_now(0).unwrap()).unwrap();
+ builder.set_not_after(&Asn1Time::days_from_now(365).unwrap()).unwrap();
+ builder.set_subject_name(&name).unwrap();
+ builder.set_issuer_name(&name).unwrap();
+ builder.append_extension(key_usage).unwrap();
+ builder.set_pubkey(&pkey).unwrap();
+ builder.sign(&pkey, MessageDigest::sha256()).unwrap();
+ let cert = builder.build();
+
+ let pkcs12_builder = Pkcs12::builder();
+ let pkcs12 = pkcs12_builder.build("mypass", subject_name, &pkey, &cert).unwrap();
+ let der = pkcs12.to_der().unwrap();
+
+ let pkcs12 = Pkcs12::from_der(&der).unwrap();
+ let parsed = pkcs12.parse("mypass").unwrap();
+
+ assert_eq!(parsed.cert.fingerprint(MessageDigest::sha1()).unwrap(), cert.fingerprint(MessageDigest::sha1()).unwrap());
+ assert!(parsed.pkey.public_eq(&pkey));
+ }
}
diff --git a/openssl/src/pkcs5.rs b/openssl/src/pkcs5.rs
index 8d6dcce8..9e1c2d26 100644
--- a/openssl/src/pkcs5.rs
+++ b/openssl/src/pkcs5.rs
@@ -22,7 +22,7 @@ pub struct KeyIvPair {
/// v1.5 or PBKDF1 from PKCS#5 v2.0.
///
/// New applications should not use this and instead use
-/// `pkcs5_pbkdf2_hmac_sha1` or another more modern key derivation algorithm.
+/// `pbkdf2_hmac` or another more modern key derivation algorithm.
pub fn bytes_to_key(cipher: Cipher,
digest: MessageDigest,
data: &[u8],
diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs
index a1ebd695..5608dd51 100644
--- a/openssl/src/pkey.rs
+++ b/openssl/src/pkey.rs
@@ -2,62 +2,73 @@ use libc::{c_void, c_char, c_int};
use std::ptr;
use std::mem;
use ffi;
+use foreign_types::{Opaque, ForeignType, ForeignTypeRef};
use {cvt, cvt_p};
-use bio::{MemBio, MemBioSlice};
+use bio::MemBioSlice;
+use dh::Dh;
use dsa::Dsa;
-use rsa::Rsa;
+use ec::EcKey;
+use rsa::{Rsa, Padding};
use error::ErrorStack;
-use util::{CallbackState, invoke_passwd_cb};
-use types::{OpenSslType, OpenSslTypeRef};
+use util::{CallbackState, invoke_passwd_cb_old};
-type_!(PKey, PKeyRef, ffi::EVP_PKEY, ffi::EVP_PKEY_free);
+foreign_type! {
+ type CType = ffi::EVP_PKEY;
+ fn drop = ffi::EVP_PKEY_free;
+
+ pub struct PKey;
+ pub struct PKeyRef;
+}
impl PKeyRef {
- /// Get a reference to the interal RSA key for direct access to the key components
+ /// Returns a copy of the internal RSA key.
pub fn rsa(&self) -> Result<Rsa, ErrorStack> {
unsafe {
let rsa = try!(cvt_p(ffi::EVP_PKEY_get1_RSA(self.as_ptr())));
- // this is safe as the ffi increments a reference counter to the internal key
Ok(Rsa::from_ptr(rsa))
}
}
- /// Stores private key as a PEM
- // FIXME: also add password and encryption
- pub fn private_key_to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
- let mem_bio = try!(MemBio::new());
+ /// Returns a copy of the internal DSA key.
+ pub fn dsa(&self) -> Result<Dsa, ErrorStack> {
unsafe {
- try!(cvt(ffi::PEM_write_bio_PrivateKey(mem_bio.as_ptr(),
- self.as_ptr(),
- ptr::null(),
- ptr::null_mut(),
- -1,
- None,
- ptr::null_mut())));
-
+ let dsa = try!(cvt_p(ffi::EVP_PKEY_get1_DSA(self.as_ptr())));
+ Ok(Dsa::from_ptr(dsa))
}
- Ok(mem_bio.get_buf().to_owned())
}
- /// Encode public key in PEM format
- pub fn public_key_to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
- let mem_bio = try!(MemBio::new());
+ /// Returns a copy of the internal DH key.
+ pub fn dh(&self) -> Result<Dh, ErrorStack> {
unsafe {
- try!(cvt(ffi::PEM_write_bio_PUBKEY(mem_bio.as_ptr(), self.as_ptr())));
+ let dh = try!(cvt_p(ffi::EVP_PKEY_get1_DH(self.as_ptr())));
+ Ok(Dh::from_ptr(dh))
}
- Ok(mem_bio.get_buf().to_owned())
}
- /// Encode public key in DER format
- pub fn public_key_to_der(&self) -> Result<Vec<u8>, ErrorStack> {
- let mem_bio = try!(MemBio::new());
+ /// Returns a copy of the internal elliptic curve key.
+ pub fn ec_key(&self) -> Result<EcKey, ErrorStack> {
unsafe {
- try!(cvt(ffi::i2d_PUBKEY_bio(mem_bio.as_ptr(), self.as_ptr())));
+ let ec_key = try!(cvt_p(ffi::EVP_PKEY_get1_EC_KEY(self.as_ptr())));
+ Ok(EcKey::from_ptr(ec_key))
}
- Ok(mem_bio.get_buf().to_owned())
}
+ public_key_to_pem!(ffi::PEM_write_bio_PUBKEY);
+ private_key_to_pem!(ffi::PEM_write_bio_PKCS8PrivateKey);
+
+ private_key_to_der!(ffi::i2d_PrivateKey);
+ public_key_to_der!(ffi::i2d_PUBKEY);
+
+ /// Returns the size of the key.
+ ///
+ /// This corresponds to the bit length of the modulus of an RSA key, and the bit length of the
+ /// group order for an elliptic curve key, for example.
+ pub fn bits(&self) -> u32 {
+ unsafe { ffi::EVP_PKEY_bits(self.as_ptr()) as u32 }
+ }
+
+ /// Compares the public component of this key with another.
pub fn public_eq(&self, other: &PKeyRef) -> bool {
unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 }
}
@@ -67,7 +78,7 @@ unsafe impl Send for PKey {}
unsafe impl Sync for PKey {}
impl PKey {
- /// Create a new `PKey` containing an RSA key.
+ /// Creates a new `PKey` containing an RSA key.
pub fn from_rsa(rsa: Rsa) -> Result<PKey, ErrorStack> {
unsafe {
let evp = try!(cvt_p(ffi::EVP_PKEY_new()));
@@ -78,7 +89,7 @@ impl PKey {
}
}
- /// Create a new `PKey` containing a DSA key.
+ /// Creates a new `PKey` containing a DSA key.
pub fn from_dsa(dsa: Dsa) -> Result<PKey, ErrorStack> {
unsafe {
let evp = try!(cvt_p(ffi::EVP_PKEY_new()));
@@ -89,7 +100,32 @@ impl PKey {
}
}
- /// Create a new `PKey` containing an HMAC key.
+ /// Creates a new `PKey` containing a Diffie-Hellman key.
+ pub fn from_dh(dh: Dh) -> Result<PKey, ErrorStack> {
+ unsafe {
+ let evp = try!(cvt_p(ffi::EVP_PKEY_new()));
+ let pkey = PKey(evp);
+ try!(cvt(ffi::EVP_PKEY_assign(pkey.0, ffi::EVP_PKEY_DH, dh.as_ptr() as *mut _)));
+ mem::forget(dh);
+ Ok(pkey)
+ }
+ }
+
+ /// Creates a new `PKey` containing an elliptic curve key.
+ pub fn from_ec_key(ec_key: EcKey) -> Result<PKey, ErrorStack> {
+ unsafe {
+ let evp = try!(cvt_p(ffi::EVP_PKEY_new()));
+ let pkey = PKey(evp);
+ try!(cvt(ffi::EVP_PKEY_assign(pkey.0, ffi::EVP_PKEY_EC, ec_key.as_ptr() as *mut _)));
+ mem::forget(ec_key);
+ Ok(pkey)
+ }
+ }
+
+ /// Creates a new `PKey` containing an HMAC key.
+ ///
+ /// # Note
+ /// To compute HMAC values, use the `sign` module.
pub fn hmac(key: &[u8]) -> Result<PKey, ErrorStack> {
unsafe {
assert!(key.len() <= c_int::max_value() as usize);
@@ -101,24 +137,10 @@ impl PKey {
}
}
- /// Reads private key from PEM, takes ownership of handle
- pub fn private_key_from_pem(buf: &[u8]) -> Result<PKey, ErrorStack> {
- 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);
+ public_key_from_pem!(PKey, ffi::PEM_read_bio_PUBKEY);
- /// 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<F>(buf: &[u8], pass_cb: F) -> Result<PKey, ErrorStack>
where F: FnOnce(&mut [c_char]) -> usize
{
@@ -128,44 +150,72 @@ impl PKey {
unsafe {
let evp = try!(cvt_p(ffi::PEM_read_bio_PrivateKey(mem_bio.as_ptr(),
ptr::null_mut(),
- Some(invoke_passwd_cb::<F>),
+ Some(invoke_passwd_cb_old::<F>),
&mut cb as *mut _ as *mut c_void)));
Ok(PKey::from_ptr(evp))
}
}
+}
- /// 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));
+pub struct PKeyCtxRef(Opaque);
+
+impl PKeyCtxRef {
+ pub fn set_rsa_padding(&mut self, pad: Padding) -> Result<(), ErrorStack> {
unsafe {
- let evp = try!(cvt_p(ffi::PEM_read_bio_PUBKEY(mem_bio.as_ptr(),
- ptr::null_mut(),
- None,
- ptr::null_mut())));
- Ok(PKey::from_ptr(evp))
+ try!(cvt(ffi::EVP_PKEY_CTX_set_rsa_padding(self.as_ptr(), pad.as_raw())));
}
+ Ok(())
}
+
+ pub fn rsa_padding(&self) -> Result<Padding, ErrorStack> {
+ let mut pad: c_int = 0;
+ unsafe {
+ try!(cvt(ffi::EVP_PKEY_CTX_get_rsa_padding(self.as_ptr(), &mut pad)));
+ };
+ Ok(Padding::from_raw(pad))
+ }
+}
+
+impl ForeignTypeRef for PKeyCtxRef {
+ type CType = ffi::EVP_PKEY_CTX;
}
#[cfg(test)]
mod tests {
+ use symm::Cipher;
+ use dh::Dh;
+ use dsa::Dsa;
+ use ec::EcKey;
+ use rsa::Rsa;
+ use nid;
+
+ use super::*;
+
+ #[test]
+ fn test_to_password() {
+ let rsa = Rsa::generate(2048).unwrap();
+ let pkey = PKey::from_rsa(rsa).unwrap();
+ let pem = pkey.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar").unwrap();
+ PKey::private_key_from_pem_passphrase(&pem, b"foobar").unwrap();
+ assert!(PKey::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err());
+ }
+
#[test]
fn test_private_key_from_pem() {
let key = include_bytes!("../test/key.pem");
- super::PKey::private_key_from_pem(key).unwrap();
+ PKey::private_key_from_pem(key).unwrap();
}
#[test]
fn test_public_key_from_pem() {
let key = include_bytes!("../test/key.pem.pub");
- super::PKey::public_key_from_pem(key).unwrap();
+ PKey::public_key_from_pem(key).unwrap();
}
#[test]
fn test_pem() {
let key = include_bytes!("../test/key.pem");
- let key = super::PKey::private_key_from_pem(key).unwrap();
+ let key = PKey::private_key_from_pem(key).unwrap();
let priv_key = key.private_key_to_pem().unwrap();
let pub_key = key.public_key_to_pem().unwrap();
@@ -175,4 +225,37 @@ mod tests {
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_rsa_accessor() {
+ let rsa = Rsa::generate(2048).unwrap();
+ let pkey = PKey::from_rsa(rsa).unwrap();
+ pkey.rsa().unwrap();
+ assert!(pkey.dsa().is_err());
+ }
+
+ #[test]
+ fn test_dsa_accessor() {
+ let dsa = Dsa::generate(2048).unwrap();
+ let pkey = PKey::from_dsa(dsa).unwrap();
+ pkey.dsa().unwrap();
+ assert!(pkey.rsa().is_err());
+ }
+
+ #[test]
+ fn test_dh_accessor() {
+ let dh = include_bytes!("../test/dhparams.pem");
+ let dh = Dh::from_pem(dh).unwrap();
+ let pkey = PKey::from_dh(dh).unwrap();
+ pkey.dh().unwrap();
+ assert!(pkey.rsa().is_err());
+ }
+
+ #[test]
+ fn test_ec_key_accessor() {
+ let ec_key = EcKey::from_curve_name(nid::X9_62_PRIME256V1).unwrap();
+ let pkey = PKey::from_ec_key(ec_key).unwrap();
+ pkey.ec_key().unwrap();
+ assert!(pkey.rsa().is_err());
+ }
}
diff --git a/openssl/src/rsa.rs b/openssl/src/rsa.rs
index bd1d16d3..792f7070 100644
--- a/openssl/src/rsa.rs
+++ b/openssl/src/rsa.rs
@@ -3,52 +3,48 @@ use std::fmt;
use std::ptr;
use std::mem;
use libc::{c_int, c_void, c_char};
+use foreign_types::ForeignTypeRef;
use {cvt, cvt_p, cvt_n};
use bn::{BigNum, BigNumRef};
-use bio::{MemBio, MemBioSlice};
+use bio::MemBioSlice;
use error::ErrorStack;
-use util::{CallbackState, invoke_passwd_cb};
-use types::OpenSslTypeRef;
+use util::{CallbackState, invoke_passwd_cb_old};
/// Type of encryption padding to use.
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Padding(c_int);
+impl Padding {
+ pub fn from_raw(value: c_int) -> Padding {
+ Padding(value)
+ }
+
+ pub fn as_raw(&self) -> c_int {
+ self.0
+ }
+}
+
pub const NO_PADDING: Padding = Padding(ffi::RSA_NO_PADDING);
pub const PKCS1_PADDING: Padding = Padding(ffi::RSA_PKCS1_PADDING);
pub const PKCS1_OAEP_PADDING: Padding = Padding(ffi::RSA_PKCS1_OAEP_PADDING);
-type_!(Rsa, RsaRef, ffi::RSA, ffi::RSA_free);
+foreign_type! {
+ type CType = ffi::RSA;
+ fn drop = ffi::RSA_free;
-impl RsaRef {
- /// 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!(cvt(ffi::PEM_write_bio_RSAPrivateKey(mem_bio.as_ptr(),
- self.as_ptr(),
- ptr::null(),
- ptr::null_mut(),
- 0,
- None,
- ptr::null_mut())));
- }
- Ok(mem_bio.get_buf().to_owned())
- }
+ pub struct Rsa;
+ pub struct RsaRef;
+}
- /// 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());
+impl RsaRef {
+ private_key_to_pem!(ffi::PEM_write_bio_RSAPrivateKey);
+ public_key_to_pem!(ffi::PEM_write_bio_RSA_PUBKEY);
- unsafe {
- try!(cvt(ffi::PEM_write_bio_RSA_PUBKEY(mem_bio.as_ptr(), self.as_ptr())));
- }
-
- Ok(mem_bio.get_buf().to_owned())
- }
+ private_key_to_der!(ffi::i2d_RSAPrivateKey);
+ public_key_to_der!(ffi::i2d_RSA_PUBKEY);
+ // FIXME should return u32
pub fn size(&self) -> usize {
unsafe {
assert!(self.n().is_some());
@@ -250,6 +246,7 @@ impl Rsa {
///
/// The public exponent will be 65537.
pub fn generate(bits: u32) -> Result<Rsa, ErrorStack> {
+ ffi::init();
unsafe {
let rsa = Rsa(try!(cvt_p(ffi::RSA_new())));
let e = try!(BigNum::from_u32(ffi::RSA_F4 as u32));
@@ -258,22 +255,16 @@ impl 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!(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);
+ private_key_from_der!(Rsa, ffi::d2i_RSAPrivateKey);
+ public_key_from_pem!(Rsa, ffi::PEM_read_bio_RSA_PUBKEY);
+ public_key_from_der!(Rsa, ffi::d2i_RSA_PUBKEY);
- /// 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<F>(buf: &[u8], pass_cb: F) -> Result<Rsa, ErrorStack>
where F: FnOnce(&mut [c_char]) -> usize
{
+ ffi::init();
let mut cb = CallbackState::new(pass_cb);
let mem_bio = try!(MemBioSlice::new(buf));
@@ -281,23 +272,11 @@ 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::<F>),
+ Some(invoke_passwd_cb_old::<F>),
cb_ptr)));
Ok(Rsa(rsa))
}
}
-
- /// Reads an RSA public key from PEM formatted data.
- pub fn public_key_from_pem(buf: &[u8]) -> Result<Rsa, ErrorStack> {
- let mem_bio = try!(MemBioSlice::new(buf));
- unsafe {
- let rsa = try!(cvt_p(ffi::PEM_read_bio_RSA_PUBKEY(mem_bio.as_ptr(),
- ptr::null_mut(),
- None,
- ptr::null_mut())));
- Ok(Rsa(rsa))
- }
- }
}
impl fmt::Debug for Rsa {
@@ -380,26 +359,26 @@ mod compat {
}
}
-
#[cfg(test)]
mod test {
- use libc::c_char;
+ use symm::Cipher;
use super::*;
#[test]
- pub fn test_password() {
+ fn test_from_password() {
+ let key = include_bytes!("../test/rsa-encrypted.pem");
+ Rsa::private_key_from_pem_passphrase(key, b"mypass").unwrap();
+ }
+
+ #[test]
+ fn test_from_password_callback() {
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;
- 6
+ password[..6].copy_from_slice(b"mypass");
+ Ok(6)
})
.unwrap();
@@ -407,7 +386,15 @@ mod test {
}
#[test]
- pub fn test_public_encrypt_private_decrypt_with_padding() {
+ fn test_to_password() {
+ let key = Rsa::generate(2048).unwrap();
+ let pem = key.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar").unwrap();
+ Rsa::private_key_from_pem_passphrase(&pem, b"foobar").unwrap();
+ assert!(Rsa::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err());
+ }
+
+ #[test]
+ fn test_public_encrypt_private_decrypt_with_padding() {
let key = include_bytes!("../test/rsa.pem.pub");
let public_key = Rsa::public_key_from_pem(key).unwrap();
diff --git a/openssl/src/sign.rs b/openssl/src/sign.rs
index 4ca551b6..279f294d 100644
--- a/openssl/src/sign.rs
+++ b/openssl/src/sign.rs
@@ -35,12 +35,13 @@
//! assert!(verifier.finish(&signature).unwrap());
//! ```
//!
-//! Compute an HMAC (note that `Verifier` cannot be used with HMACs):
+//! Compute an HMAC:
//!
//! ```rust
-//! use openssl::sign::Signer;
-//! use openssl::pkey::PKey;
//! use openssl::hash::MessageDigest;
+//! use openssl::memcmp;
+//! use openssl::pkey::PKey;
+//! use openssl::sign::Signer;
//!
//! // Create a PKey
//! let key = PKey::hmac(b"my secret").unwrap();
@@ -53,29 +54,40 @@
//! signer.update(data).unwrap();
//! signer.update(data2).unwrap();
//! let hmac = signer.finish().unwrap();
+//!
+//! // `Verifier` cannot be used with HMACs; use the `memcmp::eq` function instead
+//! //
+//! // Do not simply check for equality with `==`!
+//! # let target = hmac.clone();
+//! assert!(memcmp::eq(&hmac, &target));
//! ```
use ffi;
+use foreign_types::ForeignTypeRef;
use std::io::{self, Write};
use std::marker::PhantomData;
use std::ptr;
use {cvt, cvt_p};
use hash::MessageDigest;
-use pkey::PKeyRef;
+use pkey::{PKeyRef, PKeyCtxRef};
use error::ErrorStack;
-use types::OpenSslTypeRef;
#[cfg(ossl110)]
use ffi::{EVP_MD_CTX_new, EVP_MD_CTX_free};
#[cfg(any(ossl101, ossl102))]
use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free};
-pub struct Signer<'a>(*mut ffi::EVP_MD_CTX, PhantomData<&'a PKeyRef>);
+pub struct Signer<'a> {
+ md_ctx: *mut ffi::EVP_MD_CTX,
+ pkey_ctx: *mut ffi::EVP_PKEY_CTX,
+ pkey_pd: PhantomData<&'a PKeyRef>,
+}
impl<'a> Drop for Signer<'a> {
fn drop(&mut self) {
+ // pkey_ctx is owned by the md_ctx, so no need to explicitly free it.
unsafe {
- EVP_MD_CTX_free(self.0);
+ EVP_MD_CTX_free(self.md_ctx);
}
}
}
@@ -86,8 +98,9 @@ impl<'a> Signer<'a> {
ffi::init();
let ctx = try!(cvt_p(EVP_MD_CTX_new()));
+ let mut pctx: *mut ffi::EVP_PKEY_CTX = ptr::null_mut();
let r = ffi::EVP_DigestSignInit(ctx,
- ptr::null_mut(),
+ &mut pctx,
type_.as_ptr(),
ptr::null_mut(),
pkey.as_ptr());
@@ -95,22 +108,37 @@ impl<'a> Signer<'a> {
EVP_MD_CTX_free(ctx);
return Err(ErrorStack::get());
}
- Ok(Signer(ctx, PhantomData))
+
+ assert!(!pctx.is_null());
+
+ Ok(Signer {
+ md_ctx: ctx,
+ pkey_ctx: pctx,
+ pkey_pd: PhantomData,
+ })
}
}
+ pub fn pkey_ctx(&self) -> &PKeyCtxRef {
+ unsafe { PKeyCtxRef::from_ptr(self.pkey_ctx) }
+ }
+
+ pub fn pkey_ctx_mut(&mut self) -> &mut PKeyCtxRef {
+ unsafe { PKeyCtxRef::from_ptr_mut(self.pkey_ctx) }
+ }
+
pub fn update(&mut self, buf: &[u8]) -> Result<(), ErrorStack> {
unsafe {
- cvt(ffi::EVP_DigestUpdate(self.0, buf.as_ptr() as *const _, buf.len())).map(|_| ())
+ cvt(ffi::EVP_DigestUpdate(self.md_ctx, buf.as_ptr() as *const _, buf.len())).map(|_| ())
}
}
pub fn finish(&self) -> Result<Vec<u8>, ErrorStack> {
unsafe {
let mut len = 0;
- try!(cvt(ffi::EVP_DigestSignFinal(self.0, ptr::null_mut(), &mut len)));
+ try!(cvt(ffi::EVP_DigestSignFinal(self.md_ctx, ptr::null_mut(), &mut len)));
let mut buf = vec![0; len];
- try!(cvt(ffi::EVP_DigestSignFinal(self.0, buf.as_mut_ptr() as *mut _, &mut len)));
+ try!(cvt(ffi::EVP_DigestSignFinal(self.md_ctx, buf.as_mut_ptr() as *mut _, &mut len)));
// The advertised length is not always equal to the real length for things like DSA
buf.truncate(len);
Ok(buf)
@@ -129,12 +157,17 @@ impl<'a> Write for Signer<'a> {
}
}
-pub struct Verifier<'a>(*mut ffi::EVP_MD_CTX, PhantomData<&'a PKeyRef>);
+pub struct Verifier<'a> {
+ md_ctx: *mut ffi::EVP_MD_CTX,
+ pkey_ctx: *mut ffi::EVP_PKEY_CTX,
+ pkey_pd: PhantomData<&'a PKeyRef>,
+}
impl<'a> Drop for Verifier<'a> {
fn drop(&mut self) {
+ // pkey_ctx is owned by the md_ctx, so no need to explicitly free it.
unsafe {
- EVP_MD_CTX_free(self.0);
+ EVP_MD_CTX_free(self.md_ctx);
}
}
}
@@ -145,8 +178,9 @@ impl<'a> Verifier<'a> {
ffi::init();
let ctx = try!(cvt_p(EVP_MD_CTX_new()));
+ let mut pctx: *mut ffi::EVP_PKEY_CTX = ptr::null_mut();
let r = ffi::EVP_DigestVerifyInit(ctx,
- ptr::null_mut(),
+ &mut pctx,
type_.as_ptr(),
ptr::null_mut(),
pkey.as_ptr());
@@ -155,19 +189,33 @@ impl<'a> Verifier<'a> {
return Err(ErrorStack::get());
}
- Ok(Verifier(ctx, PhantomData))
+ assert!(!pctx.is_null());
+
+ Ok(Verifier {
+ md_ctx: ctx,
+ pkey_ctx: pctx,
+ pkey_pd: PhantomData,
+ })
}
}
+ pub fn pkey_ctx(&self) -> &PKeyCtxRef {
+ unsafe { PKeyCtxRef::from_ptr(self.pkey_ctx) }
+ }
+
+ pub fn pkey_ctx_mut(&mut self) -> &mut PKeyCtxRef {
+ unsafe { PKeyCtxRef::from_ptr_mut(self.pkey_ctx) }
+ }
+
pub fn update(&mut self, buf: &[u8]) -> Result<(), ErrorStack> {
unsafe {
- cvt(ffi::EVP_DigestUpdate(self.0, buf.as_ptr() as *const _, buf.len())).map(|_| ())
+ cvt(ffi::EVP_DigestUpdate(self.md_ctx, buf.as_ptr() as *const _, buf.len())).map(|_| ())
}
}
pub fn finish(&self, signature: &[u8]) -> Result<bool, ErrorStack> {
unsafe {
- let r = EVP_DigestVerifyFinal(self.0, signature.as_ptr() as *const _, signature.len());
+ let r = EVP_DigestVerifyFinal(self.md_ctx, signature.as_ptr() as *const _, signature.len());
match r {
1 => Ok(true),
0 => {
@@ -205,12 +253,14 @@ unsafe fn EVP_DigestVerifyFinal(ctx: *mut ffi::EVP_MD_CTX,
#[cfg(test)]
mod test {
- use serialize::hex::FromHex;
+ use hex::FromHex;
use std::iter;
use hash::MessageDigest;
use sign::{Signer, Verifier};
- use rsa::Rsa;
+ use ec::{EcGroup, EcKey};
+ use nid;
+ use rsa::{Rsa, PKCS1_PADDING};
use dsa::Dsa;
use pkey::PKey;
@@ -245,6 +295,8 @@ mod test {
let pkey = PKey::from_rsa(private_key).unwrap();
let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap();
+ assert_eq!(signer.pkey_ctx_mut().rsa_padding().unwrap(), PKCS1_PADDING);
+ signer.pkey_ctx_mut().set_rsa_padding(PKCS1_PADDING).unwrap();
signer.update(INPUT).unwrap();
let result = signer.finish().unwrap();
@@ -258,6 +310,7 @@ mod test {
let pkey = PKey::from_rsa(private_key).unwrap();
let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey).unwrap();
+ assert_eq!(verifier.pkey_ctx_mut().rsa_padding().unwrap(), PKCS1_PADDING);
verifier.update(INPUT).unwrap();
assert!(verifier.finish(SIGNATURE).unwrap());
}
@@ -339,27 +392,27 @@ mod test {
let tests: [(Vec<u8>, Vec<u8>, Vec<u8>); 7] =
[(iter::repeat(0x0b_u8).take(16).collect(),
b"Hi There".to_vec(),
- "9294727a3638bb1c13f48ef8158bfc9d".from_hex().unwrap()),
+ Vec::from_hex("9294727a3638bb1c13f48ef8158bfc9d").unwrap()),
(b"Jefe".to_vec(),
b"what do ya want for nothing?".to_vec(),
- "750c783e6ab0b503eaa86e310a5db738".from_hex().unwrap()),
+ Vec::from_hex("750c783e6ab0b503eaa86e310a5db738").unwrap()),
(iter::repeat(0xaa_u8).take(16).collect(),
iter::repeat(0xdd_u8).take(50).collect(),
- "56be34521d144c88dbb8c733f0e8b3f6".from_hex().unwrap()),
- ("0102030405060708090a0b0c0d0e0f10111213141516171819".from_hex().unwrap(),
+ Vec::from_hex("56be34521d144c88dbb8c733f0e8b3f6").unwrap()),
+ (Vec::from_hex("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(),
iter::repeat(0xcd_u8).take(50).collect(),
- "697eaf0aca3a3aea3a75164746ffaa79".from_hex().unwrap()),
+ Vec::from_hex("697eaf0aca3a3aea3a75164746ffaa79").unwrap()),
(iter::repeat(0x0c_u8).take(16).collect(),
b"Test With Truncation".to_vec(),
- "56461ef2342edc00f9bab995690efd4c".from_hex().unwrap()),
+ Vec::from_hex("56461ef2342edc00f9bab995690efd4c").unwrap()),
(iter::repeat(0xaa_u8).take(80).collect(),
b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(),
- "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd".from_hex().unwrap()),
+ Vec::from_hex("6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd").unwrap()),
(iter::repeat(0xaa_u8).take(80).collect(),
b"Test Using Larger Than Block-Size Key \
and Larger Than One Block-Size Data"
.to_vec(),
- "6f630fad67cda0ee1fb1f562db3aa53e".from_hex().unwrap())];
+ Vec::from_hex("6f630fad67cda0ee1fb1f562db3aa53e").unwrap())];
test_hmac(MessageDigest::md5(), &tests);
}
@@ -370,28 +423,43 @@ mod test {
let tests: [(Vec<u8>, Vec<u8>, Vec<u8>); 7] =
[(iter::repeat(0x0b_u8).take(20).collect(),
b"Hi There".to_vec(),
- "b617318655057264e28bc0b6fb378c8ef146be00".from_hex().unwrap()),
+ Vec::from_hex("b617318655057264e28bc0b6fb378c8ef146be00").unwrap()),
(b"Jefe".to_vec(),
b"what do ya want for nothing?".to_vec(),
- "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79".from_hex().unwrap()),
+ Vec::from_hex("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79").unwrap()),
(iter::repeat(0xaa_u8).take(20).collect(),
iter::repeat(0xdd_u8).take(50).collect(),
- "125d7342b9ac11cd91a39af48aa17b4f63f175d3".from_hex().unwrap()),
- ("0102030405060708090a0b0c0d0e0f10111213141516171819".from_hex().unwrap(),
+ Vec::from_hex("125d7342b9ac11cd91a39af48aa17b4f63f175d3").unwrap()),
+ (Vec::from_hex("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(),
iter::repeat(0xcd_u8).take(50).collect(),
- "4c9007f4026250c6bc8414f9bf50c86c2d7235da".from_hex().unwrap()),
+ Vec::from_hex("4c9007f4026250c6bc8414f9bf50c86c2d7235da").unwrap()),
(iter::repeat(0x0c_u8).take(20).collect(),
b"Test With Truncation".to_vec(),
- "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04".from_hex().unwrap()),
+ Vec::from_hex("4c1a03424b55e07fe7f27be1d58bb9324a9a5a04").unwrap()),
(iter::repeat(0xaa_u8).take(80).collect(),
b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(),
- "aa4ae5e15272d00e95705637ce8a3b55ed402112".from_hex().unwrap()),
+ Vec::from_hex("aa4ae5e15272d00e95705637ce8a3b55ed402112").unwrap()),
(iter::repeat(0xaa_u8).take(80).collect(),
b"Test Using Larger Than Block-Size Key \
and Larger Than One Block-Size Data"
.to_vec(),
- "e8e99d0f45237d786d6bbaa7965c7808bbff1a91".from_hex().unwrap())];
+ Vec::from_hex("e8e99d0f45237d786d6bbaa7965c7808bbff1a91").unwrap())];
test_hmac(MessageDigest::sha1(), &tests);
}
+
+ #[test]
+ fn ec() {
+ let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap();
+ let key = EcKey::generate(&group).unwrap();
+ let key = PKey::from_ec_key(key).unwrap();
+
+ let mut signer = Signer::new(MessageDigest::sha256(), &key).unwrap();
+ signer.update(b"hello world").unwrap();
+ let signature = signer.finish().unwrap();
+
+ let mut verifier = Verifier::new(MessageDigest::sha256(), &key).unwrap();
+ verifier.update(b"hello world").unwrap();
+ assert!(verifier.finish(&signature).unwrap());
+ }
}
diff --git a/openssl/src/ssl/bio.rs b/openssl/src/ssl/bio.rs
index 486b4dba..4dc7cbd4 100644
--- a/openssl/src/ssl/bio.rs
+++ b/openssl/src/ssl/bio.rs
@@ -5,6 +5,7 @@ use std::any::Any;
use std::io;
use std::io::prelude::*;
use std::mem;
+use std::panic::{AssertUnwindSafe, catch_unwind};
use std::ptr;
use std::slice;
@@ -67,22 +68,16 @@ pub unsafe fn get_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S {
}
unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S> {
- mem::transmute(compat::BIO_get_data(bio))
+ &mut *(compat::BIO_get_data(bio) as *mut _)
}
-fn catch_unwind<F, T>(f: F) -> Result<T, Box<Any + Send>>
- where F: FnOnce() -> T
-{
- ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(f))
-}
-
-unsafe extern "C" fn bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int {
+unsafe extern fn bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int {
BIO_clear_retry_flags(bio);
let state = state::<S>(bio);
let buf = slice::from_raw_parts(buf as *const _, len as usize);
- match catch_unwind(|| state.stream.write(buf)) {
+ match catch_unwind(AssertUnwindSafe(|| state.stream.write(buf))) {
Ok(Ok(len)) => len as c_int,
Ok(Err(err)) => {
if retriable_error(&err) {
@@ -98,13 +93,13 @@ unsafe extern "C" fn bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_
}
}
-unsafe extern "C" fn bread<S: Read>(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int {
+unsafe extern fn bread<S: Read>(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int {
BIO_clear_retry_flags(bio);
let state = state::<S>(bio);
let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize);
- match catch_unwind(|| state.stream.read(buf)) {
+ match catch_unwind(AssertUnwindSafe(|| state.stream.read(buf))) {
Ok(Ok(len)) => len as c_int,
Ok(Err(err)) => {
if retriable_error(&err) {
@@ -128,11 +123,11 @@ fn retriable_error(err: &io::Error) -> bool {
}
}
-unsafe extern "C" fn bputs<S: Write>(bio: *mut BIO, s: *const c_char) -> c_int {
+unsafe extern fn bputs<S: Write>(bio: *mut BIO, s: *const c_char) -> c_int {
bwrite::<S>(bio, s, strlen(s) as c_int)
}
-unsafe extern "C" fn ctrl<S: Write>(bio: *mut BIO,
+unsafe extern fn ctrl<S: Write>(bio: *mut BIO,
cmd: c_int,
_num: c_long,
_ptr: *mut c_void)
@@ -140,7 +135,7 @@ unsafe extern "C" fn ctrl<S: Write>(bio: *mut BIO,
if cmd == BIO_CTRL_FLUSH {
let state = state::<S>(bio);
- match catch_unwind(|| state.stream.flush()) {
+ match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) {
Ok(Ok(())) => 1,
Ok(Err(err)) => {
state.error = Some(err);
@@ -156,7 +151,7 @@ unsafe extern "C" fn ctrl<S: Write>(bio: *mut BIO,
}
}
-unsafe extern "C" fn create(bio: *mut BIO) -> c_int {
+unsafe extern fn create(bio: *mut BIO) -> c_int {
compat::BIO_set_init(bio, 0);
compat::BIO_set_num(bio, 0);
compat::BIO_set_data(bio, ptr::null_mut());
@@ -164,7 +159,7 @@ unsafe extern "C" fn create(bio: *mut BIO) -> c_int {
1
}
-unsafe extern "C" fn destroy<S>(bio: *mut BIO) -> c_int {
+unsafe extern fn destroy<S>(bio: *mut BIO) -> c_int {
if bio.is_null() {
return 0;
}
diff --git a/openssl/src/ssl/connector.rs b/openssl/src/ssl/connector.rs
index 55177767..73d7b675 100644
--- a/openssl/src/ssl/connector.rs
+++ b/openssl/src/ssl/connector.rs
@@ -7,32 +7,26 @@ use ssl::{self, SslMethod, SslContextBuilder, SslContext, Ssl, SSL_VERIFY_PEER,
use pkey::PKeyRef;
use x509::X509Ref;
-// Serialized form of DH_get_2048_256
-#[cfg(any(ossl101, all(test, any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))))]
-const DHPARAM_PEM: &'static str = r#"
+// ffdhe2048 from https://wiki.mozilla.org/Security/Server_Side_TLS#ffdhe2048
+const DHPARAM_PEM: &'static str = "
-----BEGIN DH PARAMETERS-----
-MIICCQKCAQEAh6jmHbS2Zjz/u9GcZRlZmYzu9ghmDdDyXSzu1ENeOwDgDfjx1hlX
-1Pr330VhsqowFsPZETQJb6o79Cltgw6afCCeDGSXUXq9WoqdMGvPZ+2R+eZyW0dY
-wCLgse9Cdb97bFv8EdRfkIi5QfVOseWbuLw5oL8SMH9cT9twxYGyP3a2Osrhyqa3
-kC1SUmc1SIoO8TxtmlG/pKs62DR3llJNjvahZ7WkGCXZZ+FE5RQFZCUcysuD5rSG
-9rPKP3lxUGAmwLhX9omWKFbe1AEKvQvmIcOjlgpU5xDDdfJjddcBQQOktUMwwZiv
-EmEW0iduEXFfaTh3+tfvCcrbCUrpHhoVlwKCAQA/syybcxNNCy53UGZg7b1ITKex
-jyHvIFQH9Hk6GguhJRDbwVB3vkY//0/tSqwLtVW+OmwbDGtHsbw3c79+jG9ikBIo
-+MKMuxilWuMTQQAKZQGW+THHelfy3fRj5ensFEt3feYqqrioYorDdtKC1u04ZOZ5
-gkKOvIMdFDSPby+Rk7UEWvJ2cWTh38lnwfs/LlWkvRv/6DucgNBSuYXRguoK2yo7
-cxPT/hTISEseBSWIubfSu9LfAWGZ7NBuFVfNCRWzNTu7ZODsN3/QKDcN+StSx4kU
-KM3GfrYYS1I9HbJGwy9jB4SQ8A741kfRSNR5VFFeIyfP75jFgmZLTA9sxBZZ
+MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
+87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
+YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
+7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
+ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----
-"#;
+";
fn ctx(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
let mut ctx = try!(SslContextBuilder::new(method));
let mut opts = ssl::SSL_OP_ALL;
- opts |= ssl::SSL_OP_NO_TICKET;
- opts |= ssl::SSL_OP_NO_COMPRESSION;
opts &= !ssl::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG;
opts &= !ssl::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
+ opts |= ssl::SSL_OP_NO_TICKET;
+ opts |= ssl::SSL_OP_NO_COMPRESSION;
opts |= ssl::SSL_OP_NO_SSLV2;
opts |= ssl::SSL_OP_NO_SSLV3;
opts |= ssl::SSL_OP_SINGLE_DH_USE;
@@ -61,6 +55,7 @@ impl SslConnectorBuilder {
try!(ctx.set_cipher_list("ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:\
DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:\
RSA+AES:RSA+HIGH:!aNULL:!eNULL:!MD5:!3DES"));
+ ctx.set_verify(SSL_VERIFY_PEER);
Ok(SslConnectorBuilder(ctx))
}
@@ -88,6 +83,7 @@ impl SslConnectorBuilder {
///
/// OpenSSL's built in hostname verification is used when linking against OpenSSL 1.0.2 or 1.1.0,
/// and a custom implementation is used when linking against OpenSSL 1.0.1.
+#[derive(Clone)]
pub struct SslConnector(SslContext);
impl SslConnector {
@@ -103,6 +99,22 @@ impl SslConnector {
ssl.connect(stream)
}
+
+ /// Initiates a client-side TLS session on a stream without performing hostname verification.
+ ///
+ /// The verification configuration of the connector's `SslContext` is not overridden.
+ ///
+ /// # Warning
+ ///
+ /// You should think very carefully before you use this method. If hostname verification is not
+ /// used, *any* valid certificate for *any* site will be trusted for use from any other. This
+ /// introduces a significant vulnerability to man-in-the-middle attacks.
+ pub fn danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication<S>(
+ &self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
+ where S: Read + Write
+ {
+ try!(Ssl::new(&self.0)).connect(stream)
+ }
}
/// A builder for `SslAcceptor`s.
@@ -124,8 +136,32 @@ impl SslAcceptorBuilder {
where I: IntoIterator,
I::Item: AsRef<X509Ref>
{
+ let builder = try!(SslAcceptorBuilder::mozilla_intermediate_raw(method));
+ builder.finish_setup(private_key, certificate, chain)
+ }
+
+ /// Creates a new builder configured to connect to modern clients.
+ ///
+ /// This corresponds to the modern configuration of Mozilla's server side TLS recommendations.
+ /// See its [documentation][docs] for more details on specifics.
+ ///
+ /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
+ pub fn mozilla_modern<I>(method: SslMethod,
+ private_key: &PKeyRef,
+ certificate: &X509Ref,
+ chain: I)
+ -> Result<SslAcceptorBuilder, ErrorStack>
+ where I: IntoIterator,
+ I::Item: AsRef<X509Ref>
+ {
+ let builder = try!(SslAcceptorBuilder::mozilla_modern_raw(method));
+ builder.finish_setup(private_key, certificate, chain)
+ }
+
+ /// Like `mozilla_intermediate`, but does not load the certificate chain and private key.
+ pub fn mozilla_intermediate_raw(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> {
let mut ctx = try!(ctx(method));
- let dh = try!(get_dh());
+ let dh = try!(Dh::from_pem(DHPARAM_PEM.as_bytes()));
try!(ctx.set_tmp_dh(&dh));
try!(setup_curves(&mut ctx));
try!(ctx.set_cipher_list("ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
@@ -142,23 +178,11 @@ impl SslAcceptorBuilder {
EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:\
AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:\
DES-CBC3-SHA:!DSS"));
- SslAcceptorBuilder::finish_setup(ctx, private_key, certificate, chain)
+ Ok(SslAcceptorBuilder(ctx))
}
- /// Creates a new builder configured to connect to modern clients.
- ///
- /// This corresponds to the modern configuration of Mozilla's server side TLS recommendations.
- /// See its [documentation][docs] for more details on specifics.
- ///
- /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
- pub fn mozilla_modern<I>(method: SslMethod,
- private_key: &PKeyRef,
- certificate: &X509Ref,
- chain: I)
- -> Result<SslAcceptorBuilder, ErrorStack>
- where I: IntoIterator,
- I::Item: AsRef<X509Ref>
- {
+ /// Like `mozilla_modern`, but does not load the certificate chain and private key.
+ pub fn mozilla_modern_raw(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> {
let mut ctx = try!(ctx(method));
try!(setup_curves(&mut ctx));
try!(ctx.set_cipher_list("ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
@@ -166,10 +190,10 @@ impl SslAcceptorBuilder {
ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:\
ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"));
- SslAcceptorBuilder::finish_setup(ctx, private_key, certificate, chain)
+ Ok(SslAcceptorBuilder(ctx))
}
- fn finish_setup<I>(mut ctx: SslContextBuilder,
+ fn finish_setup<I>(mut self,
private_key: &PKeyRef,
certificate: &X509Ref,
chain: I)
@@ -177,13 +201,13 @@ impl SslAcceptorBuilder {
where I: IntoIterator,
I::Item: AsRef<X509Ref>
{
- try!(ctx.set_private_key(private_key));
- try!(ctx.set_certificate(certificate));
- try!(ctx.check_private_key());
+ try!(self.0.set_private_key(private_key));
+ try!(self.0.set_certificate(certificate));
+ try!(self.0.check_private_key());
for cert in chain {
- try!(ctx.add_extra_chain_cert(cert.as_ref().to_owned()));
+ try!(self.0.add_extra_chain_cert(cert.as_ref().to_owned()));
}
- Ok(SslAcceptorBuilder(ctx))
+ Ok(self)
}
/// Returns a shared reference to the inner `SslContextBuilder`.
@@ -203,27 +227,11 @@ impl SslAcceptorBuilder {
}
#[cfg(ossl101)]
-fn get_dh() -> Result<Dh, ErrorStack> {
- Dh::from_pem(DHPARAM_PEM.as_bytes())
-}
-
-#[cfg(not(ossl101))]
-fn get_dh() -> Result<Dh, ErrorStack> {
- use ffi;
-
- use cvt_p;
- use types::OpenSslType;
-
- // manually call into ffi to avoid forcing the features
- unsafe { cvt_p(ffi::DH_get_2048_256()).map(|p| Dh::from_ptr(p)) }
-}
-
-#[cfg(ossl101)]
fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> {
- use ec_key::EcKey;
+ use ec::EcKey;
use nid;
- let curve = try!(EcKey::new_by_curve_name(nid::X9_62_PRIME256V1));
+ let curve = try!(EcKey::from_curve_name(nid::X9_62_PRIME256V1));
ctx.set_tmp_ecdh(&curve)
}
@@ -241,6 +249,7 @@ fn setup_curves(_: &mut SslContextBuilder) -> Result<(), ErrorStack> {
///
/// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL
/// structures, configuring cipher suites, session options, and more.
+#[derive(Clone)]
pub struct SslAcceptor(SslContext);
impl SslAcceptor {
@@ -255,7 +264,9 @@ impl SslAcceptor {
#[cfg(any(ossl102, ossl110))]
fn setup_verify(ssl: &mut Ssl, domain: &str) -> Result<(), ErrorStack> {
- ssl.set_verify(SSL_VERIFY_PEER);
+ // pass a noop closure in here to ensure that we consistently override any callback on the
+ // context
+ ssl.set_verify_callback(SSL_VERIFY_PEER, |p, _| p);
let param = ssl._param_mut();
param.set_hostflags(::verify::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
param.set_host(domain)
@@ -380,7 +391,7 @@ mod verify {
// the same thing we do here.
//
// The Public Suffix (https://www.publicsuffix.org/) list could
- // potentically be used here, but it's both huge and updated frequently
+ // potentially be used here, but it's both huge and updated frequently
// enough that management would be a PITA.
if dot_idxs.next().is_none() {
return None;
@@ -437,15 +448,3 @@ mod verify {
}
}
}
-
-#[cfg(test)]
-mod test {
- #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
- #[test]
- fn check_dhparam() {
- use dh::Dh;
-
- let expected = String::from_utf8(Dh::get_2048_256().unwrap().to_pem().unwrap()).unwrap();
- assert_eq!(expected.trim(), super::DHPARAM_PEM.trim());
- }
-}
diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs
index 7060433f..5a65aa77 100644
--- a/openssl/src/ssl/mod.rs
+++ b/openssl/src/ssl/mod.rs
@@ -71,6 +71,7 @@
//! }
//! ```
use ffi;
+use foreign_types::{ForeignType, ForeignTypeRef};
use libc::{c_int, c_void, c_long, c_ulong};
use libc::{c_uchar, c_uint};
use std::any::Any;
@@ -84,6 +85,7 @@ use std::io::prelude::*;
use std::marker::PhantomData;
use std::mem;
use std::ops::{Deref, DerefMut};
+use std::panic::resume_unwind;
use std::path::Path;
use std::ptr;
use std::slice;
@@ -91,16 +93,18 @@ use std::str;
use std::sync::Mutex;
use {init, cvt, cvt_p};
-use dh::DhRef;
-use ec_key::EcKeyRef;
+use dh::{Dh, DhRef};
+use ec::EcKeyRef;
+#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
+use ec::EcKey;
use x509::{X509StoreContextRef, X509FileType, X509, X509Ref, X509VerifyError, X509Name};
+use x509::store::{X509StoreBuilderRef, X509StoreRef};
#[cfg(any(ossl102, ossl110))]
use verify::X509VerifyParamRef;
use pkey::PKeyRef;
use error::ErrorStack;
-use types::{OpenSslType, OpenSslTypeRef};
use util::Opaque;
-use stack::Stack;
+use stack::{Stack, StackRef};
mod error;
mod connector;
@@ -162,8 +166,11 @@ bitflags! {
const SSL_MODE_AUTO_RETRY = ffi::SSL_MODE_AUTO_RETRY,
const SSL_MODE_NO_AUTO_CHAIN = ffi::SSL_MODE_NO_AUTO_CHAIN,
const SSL_MODE_RELEASE_BUFFERS = ffi::SSL_MODE_RELEASE_BUFFERS,
+ #[cfg(not(libressl))]
const SSL_MODE_SEND_CLIENTHELLO_TIME = ffi::SSL_MODE_SEND_CLIENTHELLO_TIME,
+ #[cfg(not(libressl))]
const SSL_MODE_SEND_SERVERHELLO_TIME = ffi::SSL_MODE_SEND_SERVERHELLO_TIME,
+ #[cfg(not(libressl))]
const SSL_MODE_SEND_FALLBACK_SCSV = ffi::SSL_MODE_SEND_FALLBACK_SCSV,
}
}
@@ -210,6 +217,22 @@ bitflags! {
}
}
+#[derive(Copy, Clone)]
+pub struct StatusType(c_int);
+
+impl StatusType {
+ pub fn from_raw(raw: c_int) -> StatusType {
+ StatusType(raw)
+ }
+
+ pub fn as_raw(&self) -> c_int {
+ self.0
+ }
+}
+
+/// An OSCP status.
+pub const STATUS_TYPE_OCSP: StatusType = StatusType(ffi::TLSEXT_STATUSTYPE_ocsp);
+
lazy_static! {
static ref INDEXES: Mutex<HashMap<TypeId, c_int>> = Mutex::new(HashMap::new());
static ref SSL_INDEXES: Mutex<HashMap<TypeId, c_int>> = Mutex::new(HashMap::new());
@@ -218,11 +241,11 @@ lazy_static! {
// Creates a static index for user data of type T
// Registers a destructor for the data which will be called
// when context is freed
-fn get_verify_data_idx<T: Any + 'static>() -> c_int {
+fn get_callback_idx<T: Any + 'static>() -> c_int {
*INDEXES.lock().unwrap().entry(TypeId::of::<T>()).or_insert_with(|| get_new_idx::<T>())
}
-fn get_ssl_verify_data_idx<T: Any + 'static>() -> c_int {
+fn get_ssl_callback_idx<T: Any + 'static>() -> c_int {
*SSL_INDEXES.lock().unwrap().entry(TypeId::of::<T>()).or_insert_with(|| get_new_ssl_idx::<T>())
}
@@ -271,7 +294,7 @@ extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_
let idx = ffi::SSL_get_ex_data_X509_STORE_CTX_idx();
let ssl = ffi::X509_STORE_CTX_get_ex_data(x509_ctx, idx);
let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl as *const _);
- let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::<F>());
+ let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_callback_idx::<F>());
let verify: &F = &*(verify as *mut F);
let ctx = X509StoreContextRef::from_ptr(x509_ctx);
@@ -286,7 +309,7 @@ extern "C" fn ssl_raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_ST
unsafe {
let idx = ffi::SSL_get_ex_data_X509_STORE_CTX_idx();
let ssl = ffi::X509_STORE_CTX_get_ex_data(x509_ctx, idx);
- let verify = ffi::SSL_get_ex_data(ssl as *const _, get_ssl_verify_data_idx::<F>());
+ let verify = ffi::SSL_get_ex_data(ssl as *const _, get_ssl_callback_idx::<F>());
let verify: &F = &*(verify as *mut F);
let ctx = X509StoreContextRef::from_ptr(x509_ctx);
@@ -300,7 +323,7 @@ extern "C" fn raw_sni<F>(ssl: *mut ffi::SSL, al: *mut c_int, _arg: *mut c_void)
{
unsafe {
let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl);
- let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::<F>());
+ let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_callback_idx::<F>());
let callback: &F = &*(callback as *mut F);
let ssl = SslRef::from_ptr_mut(ssl);
@@ -372,6 +395,133 @@ extern "C" fn raw_alpn_select_cb(ssl: *mut ffi::SSL,
unsafe { select_proto_using(ssl, out as *mut _, outlen, inbuf, inlen, *ALPN_PROTOS_IDX) }
}
+unsafe extern fn raw_tmp_dh<F>(ssl: *mut ffi::SSL,
+ is_export: c_int,
+ keylength: c_int)
+ -> *mut ffi::DH
+ where F: Fn(&mut SslRef, bool, u32) -> Result<Dh, ErrorStack> + Any + 'static + Sync + Send
+{
+ let ctx = ffi::SSL_get_SSL_CTX(ssl);
+ let callback = ffi::SSL_CTX_get_ex_data(ctx, get_callback_idx::<F>());
+ let callback = &*(callback as *mut F);
+
+ let ssl = SslRef::from_ptr_mut(ssl);
+ match callback(ssl, is_export != 0, keylength as u32) {
+ Ok(dh) => {
+ let ptr = dh.as_ptr();
+ mem::forget(dh);
+ ptr
+ }
+ Err(_) => {
+ // FIXME reset error stack
+ ptr::null_mut()
+ }
+ }
+}
+
+#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
+unsafe extern fn raw_tmp_ecdh<F>(ssl: *mut ffi::SSL,
+ is_export: c_int,
+ keylength: c_int)
+ -> *mut ffi::EC_KEY
+ where F: Fn(&mut SslRef, bool, u32) -> Result<EcKey, ErrorStack> + Any + 'static + Sync + Send
+{
+ let ctx = ffi::SSL_get_SSL_CTX(ssl);
+ let callback = ffi::SSL_CTX_get_ex_data(ctx, get_callback_idx::<F>());
+ let callback = &*(callback as *mut F);
+
+ let ssl = SslRef::from_ptr_mut(ssl);
+ match callback(ssl, is_export != 0, keylength as u32) {
+ Ok(ec_key) => {
+ let ptr = ec_key.as_ptr();
+ mem::forget(ec_key);
+ ptr
+ }
+ Err(_) => {
+ // FIXME reset error stack
+ ptr::null_mut()
+ }
+ }
+}
+
+unsafe extern fn raw_tmp_dh_ssl<F>(ssl: *mut ffi::SSL,
+ is_export: c_int,
+ keylength: c_int)
+ -> *mut ffi::DH
+ where F: Fn(&mut SslRef, bool, u32) -> Result<Dh, ErrorStack> + Any + 'static + Sync + Send
+{
+ let callback = ffi::SSL_get_ex_data(ssl, get_ssl_callback_idx::<F>());
+ let callback = &*(callback as *mut F);
+
+ let ssl = SslRef::from_ptr_mut(ssl);
+ match callback(ssl, is_export != 0, keylength as u32) {
+ Ok(dh) => {
+ let ptr = dh.as_ptr();
+ mem::forget(dh);
+ ptr
+ }
+ Err(_) => {
+ // FIXME reset error stack
+ ptr::null_mut()
+ }
+ }
+}
+
+#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
+unsafe extern fn raw_tmp_ecdh_ssl<F>(ssl: *mut ffi::SSL,
+ is_export: c_int,
+ keylength: c_int)
+ -> *mut ffi::EC_KEY
+ where F: Fn(&mut SslRef, bool, u32) -> Result<EcKey, ErrorStack> + Any + 'static + Sync + Send
+{
+ let callback = ffi::SSL_get_ex_data(ssl, get_ssl_callback_idx::<F>());
+ let callback = &*(callback as *mut F);
+
+ let ssl = SslRef::from_ptr_mut(ssl);
+ match callback(ssl, is_export != 0, keylength as u32) {
+ Ok(ec_key) => {
+ let ptr = ec_key.as_ptr();
+ mem::forget(ec_key);
+ ptr
+ }
+ Err(_) => {
+ // FIXME reset error stack
+ ptr::null_mut()
+ }
+ }
+}
+
+unsafe extern fn raw_tlsext_status<F>(ssl: *mut ffi::SSL, _: *mut c_void) -> c_int
+ where F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + Any + 'static + Sync + Send
+{
+ let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl as *const _);
+ let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_callback_idx::<F>());
+ let callback = &*(callback as *mut F);
+
+ let ssl = SslRef::from_ptr_mut(ssl);
+ let ret = callback(ssl);
+
+ if ssl.is_server() {
+ match ret {
+ Ok(true) => ffi::SSL_TLSEXT_ERR_OK,
+ Ok(false) => ffi::SSL_TLSEXT_ERR_NOACK,
+ Err(_) => {
+ // FIXME reset error stack
+ ffi::SSL_TLSEXT_ERR_ALERT_FATAL
+ }
+ }
+ } else {
+ match ret {
+ Ok(true) => 1,
+ Ok(false) => 0,
+ Err(_) => {
+ // FIXME reset error stack
+ -1
+ }
+ }
+ }
+}
+
/// The function is given as the callback to `SSL_CTX_set_next_protos_advertised_cb`.
///
/// It causes the parameter `out` to point at a `*const c_uchar` instance that
@@ -471,7 +621,7 @@ impl SslContextBuilder {
unsafe {
let verify = Box::new(verify);
ffi::SSL_CTX_set_ex_data(self.as_ptr(),
- get_verify_data_idx::<F>(),
+ get_callback_idx::<F>(),
mem::transmute(verify));
ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, Some(raw_verify::<F>));
}
@@ -487,7 +637,7 @@ impl SslContextBuilder {
unsafe {
let callback = Box::new(callback);
ffi::SSL_CTX_set_ex_data(self.as_ptr(),
- get_verify_data_idx::<F>(),
+ get_callback_idx::<F>(),
mem::transmute(callback));
let f: extern "C" fn(_, _, _) -> _ = raw_sni::<F>;
let f: extern "C" fn() = mem::transmute(f);
@@ -519,10 +669,38 @@ impl SslContextBuilder {
unsafe { cvt(ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) }
}
+ pub fn set_tmp_dh_callback<F>(&mut self, callback: F)
+ where F: Fn(&mut SslRef, bool, u32) -> Result<Dh, ErrorStack> + Any + 'static + Sync + Send
+ {
+ unsafe {
+ let callback = Box::new(callback);
+ ffi::SSL_CTX_set_ex_data(self.as_ptr(),
+ get_callback_idx::<F>(),
+ Box::into_raw(callback) as *mut c_void);
+ let f: unsafe extern fn (_, _, _) -> _ = raw_tmp_dh::<F>;
+ ffi::SSL_CTX_set_tmp_dh_callback(self.as_ptr(), f);
+ }
+ }
+
pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) }
}
+ /// Requires the `v101` feature and OpenSSL 1.0.1, or the `v102` feature and OpenSSL 1.0.2.
+ #[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
+ pub fn set_tmp_ecdh_callback<F>(&mut self, callback: F)
+ where F: Fn(&mut SslRef, bool, u32) -> Result<EcKey, ErrorStack> + Any + 'static + Sync + Send
+ {
+ unsafe {
+ let callback = Box::new(callback);
+ ffi::SSL_CTX_set_ex_data(self.as_ptr(),
+ get_callback_idx::<F>(),
+ Box::into_raw(callback) as *mut c_void);
+ let f: unsafe extern fn(_, _, _) -> _ = raw_tmp_ecdh::<F>;
+ ffi::SSL_CTX_set_tmp_ecdh_callback(self.as_ptr(), f);
+ }
+ }
+
/// Use the default locations of trusted certificates for verification.
///
/// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR`
@@ -571,7 +749,7 @@ impl SslContextBuilder {
}
}
- /// Specifies the file that contains certificate
+ /// Loads a certificate from a file.
pub fn set_certificate_file<P: AsRef<Path>>(&mut self,
file: P,
file_type: X509FileType)
@@ -585,7 +763,11 @@ impl SslContextBuilder {
}
}
- /// Specifies the file that contains certificate chain
+ /// Loads a certificate chain from a file.
+ ///
+ /// The file should contain a sequence of PEM-formatted certificates, the first being the leaf
+ /// certificate, and the remainder forming the chain of certificates up to and including the
+ /// trusted root certificate.
pub fn set_certificate_chain_file<P: AsRef<Path>>(&mut self,
file: P)
-> Result<(), ErrorStack> {
@@ -596,13 +778,15 @@ impl SslContextBuilder {
}
}
- /// Specifies the certificate
+ /// Sets the certificate.
pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr())).map(|_| ()) }
}
- /// Adds a certificate to the certificate chain presented together with the
- /// certificate specified using set_certificate()
+ /// Appends a certificate to the certificate chain.
+ ///
+ /// This chain should contain all certificates necessary to go from the certificate specified by
+ /// `set_certificate` to a trusted root.
pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> {
unsafe {
try!(cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int));
@@ -611,7 +795,7 @@ impl SslContextBuilder {
}
}
- /// Specifies the file that contains private key
+ /// Loads the private key from a file.
pub fn set_private_key_file<P: AsRef<Path>>(&mut self,
file: P,
file_type: X509FileType)
@@ -625,11 +809,14 @@ impl SslContextBuilder {
}
}
- /// Specifies the private key
+ /// Sets the private key.
pub fn set_private_key(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_use_PrivateKey(self.as_ptr(), key.as_ptr())).map(|_| ()) }
}
+ /// Sets the cipher configuration.
+ ///
+ /// See `man 1 ciphers` for details on the format.
pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> {
let cipher_list = CString::new(cipher_list).unwrap();
unsafe {
@@ -638,17 +825,15 @@ impl SslContextBuilder {
}
}
- /// If `onoff` is set to `true`, enable ECDHE for key exchange with
- /// compatible clients, and automatically select an appropriate elliptic
- /// curve.
+ /// Enables ECDHE key exchange with an automatically chosen curve list.
///
/// Requires the `v102` feature and OpenSSL 1.0.2.
- #[cfg(all(feature = "v102", ossl102))]
+ #[cfg(all(feature = "v102", any(ossl102, libressl)))]
pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> {
self._set_ecdh_auto(onoff)
}
- #[cfg(ossl102)]
+ #[cfg(any(ossl102,libressl))]
fn _set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_set_ecdh_auto(self.as_ptr(), onoff as c_int)).map(|_| ()) }
}
@@ -739,6 +924,41 @@ impl SslContextBuilder {
unsafe { cvt(ffi::SSL_CTX_check_private_key(self.as_ptr())).map(|_| ()) }
}
+ /// Returns a shared reference to the context's certificate store.
+ pub fn cert_store(&self) -> &X509StoreBuilderRef {
+ unsafe { X509StoreBuilderRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) }
+ }
+
+ /// Returns a mutable reference to the context's certificate store.
+ pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef {
+ unsafe { X509StoreBuilderRef::from_ptr_mut(ffi::SSL_CTX_get_cert_store(self.as_ptr())) }
+ }
+
+ /// Sets the callback dealing with OCSP stapling.
+ ///
+ /// On the client side, this callback is responsible for validating the OCSP status response
+ /// returned by the server. The status may be retrieved with the `SslRef::ocsp_status` method.
+ /// A response of `Ok(true)` indicates that the OCSP status is valid, and a response of
+ /// `Ok(false)` indicates that the OCSP status is invalid and the handshake should be
+ /// terminated.
+ ///
+ /// On the server side, this callback is resopnsible for setting the OCSP status response to be
+ /// returned to clients. The status may be set with the `SslRef::set_ocsp_status` method. A
+ /// response of `Ok(true)` indicates that the OCSP status should be returned to the client, and
+ /// `Ok(false)` indicates that the status should not be returned to the client.
+ pub fn set_status_callback<F>(&mut self, callback: F) -> Result<(), ErrorStack>
+ where F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + Any + 'static + Sync + Send
+ {
+ unsafe {
+ let callback = Box::new(callback);
+ ffi::SSL_CTX_set_ex_data(self.as_ptr(),
+ get_callback_idx::<F>(),
+ Box::into_raw(callback) as *mut c_void);
+ let f: unsafe extern fn (_, _) -> _ = raw_tlsext_status::<F>;
+ cvt(ffi::SSL_CTX_set_tlsext_status_cb(self.as_ptr(), Some(f)) as c_int).map(|_| ())
+ }
+ }
+
pub fn build(self) -> SslContext {
let ctx = SslContext(self.0);
mem::forget(self);
@@ -746,7 +966,13 @@ impl SslContextBuilder {
}
}
-type_!(SslContext, SslContextRef, ffi::SSL_CTX, ffi::SSL_CTX_free);
+foreign_type! {
+ type CType = ffi::SSL_CTX;
+ fn drop = ffi::SSL_CTX_free;
+
+ pub struct SslContext;
+ pub struct SslContextRef;
+}
unsafe impl Send for SslContext {}
unsafe impl Sync for SslContext {}
@@ -803,6 +1029,22 @@ impl SslContextRef {
}
}
}
+
+ /// Returns the certificate store used for verification.
+ pub fn cert_store(&self) -> &X509StoreRef {
+ unsafe {
+ X509StoreRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr()))
+ }
+ }
+
+ pub fn extra_chain_certs(&self) -> &StackRef<X509> {
+ unsafe {
+ let mut chain = ptr::null_mut();
+ ffi::SSL_CTX_get_extra_chain_certs(self.as_ptr(), &mut chain);
+ assert!(!chain.is_null());
+ StackRef::from_ptr(chain)
+ }
+ }
}
pub struct CipherBits {
@@ -815,14 +1057,16 @@ pub struct CipherBits {
pub struct SslCipher(*mut ffi::SSL_CIPHER);
-impl OpenSslType for SslCipher {
+impl ForeignType for SslCipher {
type CType = ffi::SSL_CIPHER;
type Ref = SslCipherRef;
+ #[inline]
unsafe fn from_ptr(ptr: *mut ffi::SSL_CIPHER) -> SslCipher {
SslCipher(ptr)
}
+ #[inline]
fn as_ptr(&self) -> *mut ffi::SSL_CIPHER {
self.0
}
@@ -844,7 +1088,7 @@ impl DerefMut for SslCipher {
pub struct SslCipherRef(Opaque);
-impl OpenSslTypeRef for SslCipherRef {
+impl ForeignTypeRef for SslCipherRef {
type CType = ffi::SSL_CIPHER;
}
@@ -892,7 +1136,44 @@ impl SslCipherRef {
}
}
-type_!(Ssl, SslRef, ffi::SSL, ffi::SSL_free);
+foreign_type! {
+ type CType = ffi::SSL_SESSION;
+ fn drop = ffi::SSL_SESSION_free;
+
+ pub struct SslSession;
+ pub struct SslSessionRef;
+}
+
+impl SslSessionRef {
+ /// Returns the SSL session ID.
+ pub fn id(&self) -> &[u8] {
+ unsafe {
+ let mut len = 0;
+ let p = ffi::SSL_SESSION_get_id(self.as_ptr(), &mut len);
+ slice::from_raw_parts(p as *const u8, len as usize)
+ }
+ }
+
+ /// Returns the length of the master key.
+ pub fn master_key_len(&self) -> usize {
+ unsafe { compat::SSL_SESSION_get_master_key(self.as_ptr(), ptr::null_mut(), 0) }
+ }
+
+ /// Copies the master key into the provided buffer.
+ ///
+ /// Returns the number of bytes written.
+ pub fn master_key(&self, buf: &mut [u8]) -> usize {
+ unsafe { compat::SSL_SESSION_get_master_key(self.as_ptr(), buf.as_mut_ptr(), buf.len()) }
+ }
+}
+
+foreign_type! {
+ type CType = ffi::SSL;
+ fn drop = ffi::SSL_free;
+
+ pub struct Ssl;
+ pub struct SslRef;
+}
impl fmt::Debug for SslRef {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
@@ -944,12 +1225,58 @@ impl SslRef {
unsafe {
let verify = Box::new(verify);
ffi::SSL_set_ex_data(self.as_ptr(),
- get_ssl_verify_data_idx::<F>(),
+ get_ssl_callback_idx::<F>(),
mem::transmute(verify));
ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, Some(ssl_raw_verify::<F>));
}
}
+ pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) }
+ }
+
+ pub fn set_tmp_dh_callback<F>(&mut self, callback: F)
+ where F: Fn(&mut SslRef, bool, u32) -> Result<Dh, ErrorStack> + Any + 'static + Sync + Send
+ {
+ unsafe {
+ let callback = Box::new(callback);
+ ffi::SSL_set_ex_data(self.as_ptr(),
+ get_ssl_callback_idx::<F>(),
+ Box::into_raw(callback) as *mut c_void);
+ let f: unsafe extern fn (_, _, _) -> _ = raw_tmp_dh_ssl::<F>;
+ ffi::SSL_set_tmp_dh_callback(self.as_ptr(), f);
+ }
+ }
+
+ pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) }
+ }
+
+ /// Requires the `v101` feature and OpenSSL 1.0.1, or the `v102` feature and OpenSSL 1.0.2.
+ #[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
+ pub fn set_tmp_ecdh_callback<F>(&mut self, callback: F)
+ where F: Fn(&mut SslRef, bool, u32) -> Result<EcKey, ErrorStack> + Any + 'static + Sync + Send
+ {
+ unsafe {
+ let callback = Box::new(callback);
+ ffi::SSL_set_ex_data(self.as_ptr(),
+ get_ssl_callback_idx::<F>(),
+ Box::into_raw(callback) as *mut c_void);
+ let f: unsafe extern fn(_, _, _) -> _ = raw_tmp_ecdh_ssl::<F>;
+ ffi::SSL_set_tmp_ecdh_callback(self.as_ptr(), f);
+ }
+ }
+
+ /// If `onoff` is set to `true`, enable ECDHE for key exchange with
+ /// compatible clients, and automatically select an appropriate elliptic
+ /// curve.
+ ///
+ /// Requires the `v102` feature and OpenSSL 1.0.2.
+ #[cfg(all(feature = "v102", ossl102))]
+ pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> {
+ unsafe { cvt(ffi::SSL_set_ecdh_auto(self.as_ptr(), onoff as c_int)).map(|_| ()) }
+ }
+
pub fn current_cipher(&self) -> Option<&SslCipherRef> {
unsafe {
let ptr = ffi::SSL_get_current_cipher(self.as_ptr());
@@ -1151,6 +1478,61 @@ impl SslRef {
pub fn verify_result(&self) -> Option<X509VerifyError> {
unsafe { X509VerifyError::from_raw(ffi::SSL_get_verify_result(self.as_ptr())) }
}
+
+ /// Returns the SSL session.
+ pub fn session(&self) -> Option<&SslSessionRef> {
+ unsafe {
+ let p = ffi::SSL_get_session(self.as_ptr());
+ if p.is_null() {
+ None
+ } else {
+ Some(SslSessionRef::from_ptr(p))
+ }
+ }
+ }
+
+ /// Sets the status response a client wishes the server to reply with.
+ pub fn set_status_type(&mut self, type_: StatusType) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::SSL_set_tlsext_status_type(self.as_ptr(), type_.as_raw()) as c_int).map(|_| ())
+ }
+ }
+
+ /// Returns the server's OCSP response, if present.
+ pub fn ocsp_status(&self) -> Option<&[u8]> {
+ unsafe {
+ let mut p = ptr::null_mut();
+ let len = ffi::SSL_get_tlsext_status_ocsp_resp(self.as_ptr(), &mut p);
+
+ if len < 0 {
+ None
+ } else {
+ Some(slice::from_raw_parts(p as *const u8, len as usize))
+ }
+ }
+ }
+
+ /// Sets the OCSP response to be returned to the client.
+ pub fn set_ocsp_status(&mut self, response: &[u8]) -> Result<(), ErrorStack> {
+ unsafe {
+ assert!(response.len() <= c_int::max_value() as usize);
+ let p = try!(cvt_p(ffi::CRYPTO_malloc(response.len() as _,
+ concat!(file!(), "\0").as_ptr() as *const _,
+ line!() as c_int)));
+ ptr::copy_nonoverlapping(response.as_ptr(), p as *mut u8, response.len());
+ cvt(ffi::SSL_set_tlsext_status_ocsp_resp(self.as_ptr(),
+ p as *mut c_uchar,
+ response.len() as c_long) as c_int)
+ .map(|_| ())
+ }
+ }
+
+ /// Determines if this `Ssl` is configured for server-side or client-side use.
+ pub fn is_server(&self) -> bool {
+ unsafe {
+ compat::SSL_is_server(self.as_ptr()) != 0
+ }
+ }
}
unsafe impl Sync for Ssl {}
@@ -1326,11 +1708,24 @@ impl<S: Read + Write> SslStream<S> {
/// This is particularly useful with a nonblocking socket, where the error
/// value will identify if OpenSSL is waiting on read or write readiness.
pub fn ssl_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
+ // The intepretation of the return code here is a little odd with a
+ // zero-length write. OpenSSL will likely correctly report back to us
+ // that it read zero bytes, but zero is also the sentinel for "error".
+ // To avoid that confusion short-circuit that logic and return quickly
+ // if `buf` has a length of zero.
+ if buf.len() == 0 {
+ return Ok(0)
+ }
+
let ret = self.ssl.read(buf);
- if ret >= 0 {
+ if ret > 0 {
Ok(ret as usize)
} else {
- Err(self.make_error(ret))
+ match self.make_error(ret) {
+ // Don't treat unexpected EOFs as errors when reading
+ Error::Stream(ref e) if e.kind() == io::ErrorKind::ConnectionAborted => Ok(0),
+ e => Err(e),
+ }
}
}
@@ -1339,8 +1734,13 @@ impl<S: Read + Write> SslStream<S> {
/// This is particularly useful with a nonblocking socket, where the error
/// value will identify if OpenSSL is waiting on read or write readiness.
pub fn ssl_write(&mut self, buf: &[u8]) -> Result<usize, Error> {
+ // See above for why we short-circuit on zero-length buffers
+ if buf.len() == 0 {
+ return Ok(0)
+ }
+
let ret = self.ssl.write(buf);
- if ret >= 0 {
+ if ret > 0 {
Ok(ret as usize)
} else {
Err(self.make_error(ret))
@@ -1377,19 +1777,38 @@ impl<S> SslStream<S> {
ffi::SSL_ERROR_SYSCALL => {
let errs = ErrorStack::get();
if errs.errors().is_empty() {
- if ret == 0 {
- Error::Stream(io::Error::new(io::ErrorKind::ConnectionAborted,
- "unexpected EOF observed"))
- } else {
- Error::Stream(self.get_bio_error())
+ match self.get_bio_error() {
+ Some(err) => Error::Stream(err),
+ None => {
+ Error::Stream(io::Error::new(io::ErrorKind::ConnectionAborted,
+ "unexpected EOF observed"))
+ }
}
} else {
Error::Ssl(errs)
}
}
ffi::SSL_ERROR_ZERO_RETURN => Error::ZeroReturn,
- ffi::SSL_ERROR_WANT_WRITE => Error::WantWrite(self.get_bio_error()),
- ffi::SSL_ERROR_WANT_READ => Error::WantRead(self.get_bio_error()),
+ ffi::SSL_ERROR_WANT_WRITE => {
+ let err = match self.get_bio_error() {
+ Some(err) => err,
+ None => {
+ io::Error::new(io::ErrorKind::Other,
+ "BUG: got an SSL_ERROR_WANT_WRITE with no error in the BIO")
+ }
+ };
+ Error::WantWrite(err)
+ },
+ ffi::SSL_ERROR_WANT_READ => {
+ let err = match self.get_bio_error() {
+ Some(err) => err,
+ None => {
+ io::Error::new(io::ErrorKind::Other,
+ "BUG: got an SSL_ERROR_WANT_WRITE with no error in the BIO")
+ }
+ };
+ Error::WantRead(err)
+ },
err => {
Error::Stream(io::Error::new(io::ErrorKind::InvalidData,
format!("unexpected error {}", err)))
@@ -1399,19 +1818,12 @@ impl<S> SslStream<S> {
fn check_panic(&mut self) {
if let Some(err) = unsafe { bio::take_panic::<S>(self.ssl.get_raw_rbio()) } {
- ::std::panic::resume_unwind(err)
+ resume_unwind(err)
}
}
- fn get_bio_error(&mut self) -> io::Error {
- let error = unsafe { bio::take_error::<S>(self.ssl.get_raw_rbio()) };
- match error {
- Some(error) => error,
- None => {
- io::Error::new(io::ErrorKind::Other,
- "BUG: got an ErrorSyscall without an error in the BIO?")
- }
- }
+ fn get_bio_error(&mut self) -> Option<io::Error> {
+ unsafe { bio::take_error::<S>(self.ssl.get_raw_rbio()) }
}
/// Returns a reference to the underlying stream.
@@ -1488,8 +1900,8 @@ mod compat {
use ffi;
use libc::c_int;
- pub use ffi::{SSL_CTX_get_options, SSL_CTX_set_options};
- pub use ffi::{SSL_CTX_clear_options, SSL_CTX_up_ref};
+ pub use ffi::{SSL_CTX_get_options, SSL_CTX_set_options, SSL_CTX_clear_options, SSL_CTX_up_ref,
+ SSL_SESSION_get_master_key, SSL_is_server};
pub unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int {
ffi::CRYPTO_get_ex_new_index(ffi::CRYPTO_EX_INDEX_SSL_CTX,
@@ -1524,7 +1936,7 @@ mod compat {
use std::ptr;
use ffi;
- use libc::{self, c_long, c_ulong, c_int};
+ use libc::{self, c_long, c_ulong, c_int, size_t, c_uchar};
pub unsafe fn SSL_CTX_get_options(ctx: *const ffi::SSL_CTX) -> c_ulong {
ffi::SSL_CTX_ctrl(ctx as *mut _, ffi::SSL_CTRL_OPTIONS, 0, ptr::null_mut()) as c_ulong
@@ -1561,6 +1973,19 @@ mod compat {
0
}
+ pub unsafe fn SSL_SESSION_get_master_key(session: *const ffi::SSL_SESSION,
+ out: *mut c_uchar,
+ mut outlen: size_t) -> size_t {
+ if outlen == 0 {
+ return (*session).master_key_length as size_t;
+ }
+ if outlen > (*session).master_key_length as size_t {
+ outlen = (*session).master_key_length as size_t;
+ }
+ ptr::copy_nonoverlapping((*session).master_key.as_ptr(), out, outlen);
+ outlen
+ }
+
pub fn tls_method() -> *const ffi::SSL_METHOD {
unsafe { ffi::SSLv23_method() }
}
@@ -1568,4 +1993,8 @@ mod compat {
pub fn dtls_method() -> *const ffi::SSL_METHOD {
unsafe { ffi::DTLSv1_method() }
}
+
+ pub unsafe fn SSL_is_server(s: *mut ffi::SSL) -> c_int {
+ (*s).server
+ }
}
diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs
index 146d0806..9c00e3ed 100644
--- a/openssl/src/ssl/tests/mod.rs
+++ b/openssl/src/ssl/tests/mod.rs
@@ -9,17 +9,18 @@ use std::mem;
use std::net::{TcpStream, TcpListener, SocketAddr};
use std::path::Path;
use std::process::{Command, Child, Stdio, ChildStdin};
+use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
use std::thread;
use std::time::Duration;
-
use tempdir::TempDir;
+use dh::Dh;
use hash::MessageDigest;
+use ocsp::{OcspResponse, RESPONSE_STATUS_UNAUTHORIZED};
use ssl;
-use ssl::SSL_VERIFY_PEER;
-use ssl::{SslMethod, HandshakeError};
-use ssl::{SslContext, SslStream, Ssl, ShutdownResult, SslConnectorBuilder, SslAcceptorBuilder,
- Error};
+use ssl::{SslMethod, HandshakeError, SslContext, SslStream, Ssl, ShutdownResult,
+ SslConnectorBuilder, SslAcceptorBuilder, Error, SSL_VERIFY_PEER, SSL_VERIFY_NONE,
+ STATUS_TYPE_OCSP};
use x509::{X509StoreContext, X509, X509Name, X509_FILETYPE_PEM};
#[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))]
use x509::verify::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS;
@@ -29,6 +30,7 @@ use std::net::UdpSocket;
mod select;
+static ROOT_CERT: &'static [u8] = include_bytes!("../../../test/root-ca.pem");
static CERT: &'static [u8] = include_bytes!("../../../test/cert.pem");
static KEY: &'static [u8] = include_bytes!("../../../test/key.pem");
@@ -98,6 +100,7 @@ impl Server {
Server::new_tcp(&["-www"])
}
+ #[allow(dead_code)]
fn new_alpn() -> (Server, TcpStream) {
Server::new_tcp(&["-www",
"-nextprotoneg",
@@ -170,8 +173,8 @@ macro_rules! run_test(
use ssl::SSL_VERIFY_PEER;
use hash::MessageDigest;
use x509::X509StoreContext;
- use serialize::hex::FromHex;
- use types::OpenSslTypeRef;
+ use hex::FromHex;
+ use foreign_types::ForeignTypeRef;
use super::Server;
#[test]
@@ -181,7 +184,7 @@ macro_rules! run_test(
}
#[test]
- #[cfg_attr(any(windows, target_arch = "arm"), ignore)] // FIXME(#467)
+ #[cfg_attr(any(libressl, windows, target_arch = "arm"), ignore)] // FIXME(#467)
fn dtlsv1() {
let (_s, stream) = Server::new_dtlsv1(Some("hello"));
$blk(SslMethod::dtls(), stream);
@@ -302,7 +305,7 @@ run_test!(verify_callback_data, |method, stream| {
// Command: openssl x509 -in test/cert.pem -outform DER | openssl dgst -sha256
// Please update if "test/cert.pem" will ever change
let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584";
- let node_id = node_hash_str.from_hex().unwrap();
+ let node_id = Vec::from_hex(node_hash_str).unwrap();
ctx.set_verify_callback(SSL_VERIFY_PEER, move |_preverify_ok, x509_ctx| {
let cert = x509_ctx.current_cert();
match cert {
@@ -330,7 +333,7 @@ run_test!(ssl_verify_callback, |method, stream| {
let mut ssl = Ssl::new(&ctx.build()).unwrap();
let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584";
- let node_id = node_hash_str.from_hex().unwrap();
+ let node_id = Vec::from_hex(node_hash_str).unwrap();
ssl.set_verify_callback(SSL_VERIFY_PEER, move |_, x509| {
CHECKED.store(1, Ordering::SeqCst);
match x509.current_cert() {
@@ -421,18 +424,28 @@ fn test_write() {
stream.flush().unwrap();
}
+#[test]
+fn zero_length_buffers() {
+ let (_s, stream) = Server::new();
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ let mut stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap();
+
+ assert_eq!(stream.write(b"").unwrap(), 0);
+ assert_eq!(stream.read(&mut []).unwrap(), 0);
+}
+
run_test!(get_peer_certificate, |method, stream| {
let ctx = SslContext::builder(method).unwrap();
let stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap();
let cert = stream.ssl().peer_certificate().unwrap();
let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap();
let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584";
- let node_id = node_hash_str.from_hex().unwrap();
+ let node_id = Vec::from_hex(node_hash_str).unwrap();
assert_eq!(node_id, fingerprint)
});
#[test]
-#[cfg_attr(any(windows, target_arch = "arm"), ignore)] // FIXME(#467)
+#[cfg_attr(any(libressl, windows, target_arch = "arm"), ignore)] // FIXME(#467)
fn test_write_dtlsv1() {
let (_s, stream) = Server::new_dtlsv1(iter::repeat("y\n"));
let ctx = SslContext::builder(SslMethod::dtls()).unwrap();
@@ -771,7 +784,7 @@ fn test_alpn_server_select_none() {
}
#[test]
-#[cfg_attr(any(windows, target_arch = "arm"), ignore)] // FIXME(#467)
+#[cfg_attr(any(libressl, windows, target_arch = "arm"), ignore)] // FIXME(#467)
fn test_read_dtlsv1() {
let (_s, stream) = Server::new_dtlsv1(Some("hello"));
@@ -849,7 +862,7 @@ fn test_write_nonblocking() {
}
#[test]
-#[cfg_attr(any(windows, target_arch = "arm"), ignore)] // FIXME(#467)
+#[cfg_attr(any(libressl, windows, target_arch = "arm"), ignore)] // FIXME(#467)
fn test_read_nonblocking() {
let (_s, stream) = Server::new();
stream.set_nonblocking(true).unwrap();
@@ -1089,6 +1102,36 @@ fn connector_invalid_hostname() {
}
#[test]
+fn connector_invalid_no_hostname_verification() {
+ let connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap().build();
+
+ let s = TcpStream::connect("google.com:443").unwrap();
+ connector.danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication(s)
+ .unwrap();
+}
+
+#[test]
+fn connector_no_hostname_still_verifies() {
+ let (_s, tcp) = Server::new();
+
+ let connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap().build();
+
+ assert!(connector.danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication(tcp)
+ .is_err());
+}
+
+#[test]
+fn connector_no_hostname_can_disable_verify() {
+ let (_s, tcp) = Server::new();
+
+ let mut connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap();
+ connector.builder_mut().set_verify(SSL_VERIFY_NONE);
+ let connector = connector.build();
+
+ connector.danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication(tcp).unwrap();
+}
+
+#[test]
fn connector_client_server_mozilla_intermediate() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let port = listener.local_addr().unwrap().port();
@@ -1193,6 +1236,210 @@ fn client_ca_list() {
ctx.set_client_ca_list(names);
}
+#[test]
+fn cert_store() {
+ let (_s, tcp) = Server::new();
+
+ let cert = X509::from_pem(ROOT_CERT).unwrap();
+
+ let mut ctx = SslConnectorBuilder::new(SslMethod::tls()).unwrap();
+ ctx.builder_mut().cert_store_mut().add_cert(cert).unwrap();
+ let ctx = ctx.build();
+
+ ctx.connect("foobar.com", tcp).unwrap();
+}
+
+#[test]
+fn tmp_dh_callback() {
+ static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT;
+
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let port = listener.local_addr().unwrap().port();
+
+ thread::spawn(move ||{
+ let stream = listener.accept().unwrap().0;
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM).unwrap();
+ ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM).unwrap();
+ ctx.set_tmp_dh_callback(|_, _, _| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ let dh = include_bytes!("../../../test/dhparams.pem");
+ Dh::from_pem(dh)
+ });
+ let ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.accept(stream).unwrap();
+ });
+
+ let stream = TcpStream::connect(("127.0.0.1", port)).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_cipher_list("EDH").unwrap();
+ let ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.connect(stream).unwrap();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
+fn tmp_ecdh_callback() {
+ use ec::EcKey;
+ use nid;
+
+ static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT;
+
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let port = listener.local_addr().unwrap().port();
+
+ thread::spawn(move ||{
+ let stream = listener.accept().unwrap().0;
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM).unwrap();
+ ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM).unwrap();
+ ctx.set_tmp_ecdh_callback(|_, _, _| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ EcKey::new_by_curve_name(nid::X9_62_PRIME256V1)
+ });
+ let ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.accept(stream).unwrap();
+ });
+
+ let stream = TcpStream::connect(("127.0.0.1", port)).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_cipher_list("ECDH").unwrap();
+ let ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.connect(stream).unwrap();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+fn tmp_dh_callback_ssl() {
+ static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT;
+
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let port = listener.local_addr().unwrap().port();
+
+ thread::spawn(move ||{
+ let stream = listener.accept().unwrap().0;
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM).unwrap();
+ ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM).unwrap();
+ let mut ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.set_tmp_dh_callback(|_, _, _| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ let dh = include_bytes!("../../../test/dhparams.pem");
+ Dh::from_pem(dh)
+ });
+ ssl.accept(stream).unwrap();
+ });
+
+ let stream = TcpStream::connect(("127.0.0.1", port)).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_cipher_list("EDH").unwrap();
+ let ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.connect(stream).unwrap();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
+fn tmp_ecdh_callback_ssl() {
+ use ec::EcKey;
+ use nid;
+
+ static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT;
+
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let port = listener.local_addr().unwrap().port();
+
+ thread::spawn(move ||{
+ let stream = listener.accept().unwrap().0;
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM).unwrap();
+ ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM).unwrap();
+ let mut ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.set_tmp_ecdh_callback(|_, _, _| {
+ CALLED_BACK.store(true, Ordering::SeqCst);
+ EcKey::new_by_curve_name(nid::X9_62_PRIME256V1)
+ });
+ ssl.accept(stream).unwrap();
+ });
+
+ let stream = TcpStream::connect(("127.0.0.1", port)).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_cipher_list("ECDH").unwrap();
+ let ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.connect(stream).unwrap();
+
+ assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+fn idle_session() {
+ let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
+ let ssl = Ssl::new(&ctx).unwrap();
+ assert!(ssl.session().is_none());
+}
+
+#[test]
+fn active_session() {
+ let connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap().build();
+
+ let s = TcpStream::connect("google.com:443").unwrap();
+ let socket = connector.connect("google.com", s).unwrap();
+ let session = socket.ssl().session().unwrap();
+ let len = session.master_key_len();
+ let mut buf = vec![0; len - 1];
+ let copied = session.master_key(&mut buf);
+ assert_eq!(copied, buf.len());
+ let mut buf = vec![0; len + 1];
+ let copied = session.master_key(&mut buf);
+ assert_eq!(copied, len);
+}
+
+#[test]
+fn status_callbacks() {
+ static CALLED_BACK_SERVER: AtomicBool = ATOMIC_BOOL_INIT;
+ static CALLED_BACK_CLIENT: AtomicBool = ATOMIC_BOOL_INIT;
+
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let port = listener.local_addr().unwrap().port();
+
+ let guard = thread::spawn(move || {
+ let stream = listener.accept().unwrap().0;
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM).unwrap();
+ ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM).unwrap();
+ ctx.set_status_callback(|ssl| {
+ CALLED_BACK_SERVER.store(true, Ordering::SeqCst);
+ let response = OcspResponse::create(RESPONSE_STATUS_UNAUTHORIZED, None).unwrap();
+ let response = response.to_der().unwrap();
+ ssl.set_ocsp_status(&response).unwrap();
+ Ok(true)
+ }).unwrap();
+ let ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.accept(stream).unwrap();
+ });
+
+ let stream = TcpStream::connect(("127.0.0.1", port)).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+ ctx.set_status_callback(|ssl| {
+ CALLED_BACK_CLIENT.store(true, Ordering::SeqCst);
+ let response = OcspResponse::from_der(ssl.ocsp_status().unwrap()).unwrap();
+ assert_eq!(response.status(), RESPONSE_STATUS_UNAUTHORIZED);
+ Ok(true)
+ }).unwrap();
+ let mut ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.set_status_type(STATUS_TYPE_OCSP).unwrap();
+ ssl.connect(stream).unwrap();
+
+ assert!(CALLED_BACK_SERVER.load(Ordering::SeqCst));
+ assert!(CALLED_BACK_CLIENT.load(Ordering::SeqCst));
+
+ guard.join().unwrap();
+}
+
fn _check_kinds() {
fn is_send<T: Send>() {}
fn is_sync<T: Sync>() {}
diff --git a/openssl/src/stack.rs b/openssl/src/stack.rs
index 584ead8c..268afde7 100644
--- a/openssl/src/stack.rs
+++ b/openssl/src/stack.rs
@@ -1,15 +1,16 @@
-use std::ops::{Deref, DerefMut, Index, IndexMut};
-use std::iter;
+use foreign_types::{ForeignTypeRef, ForeignType};
+use libc::c_int;
use std::borrow::Borrow;
use std::convert::AsRef;
+use std::iter;
use std::marker::PhantomData;
-use libc::c_int;
use std::mem;
use ffi;
use {cvt, cvt_p};
use error::ErrorStack;
-use types::{OpenSslType, OpenSslTypeRef};
+use std::ops::{Deref, DerefMut, Index, IndexMut};
+
use util::Opaque;
#[cfg(ossl10x)]
@@ -22,9 +23,8 @@ use ffi::{OPENSSL_sk_pop, OPENSSL_sk_free, OPENSSL_sk_num, OPENSSL_sk_value, OPE
/// Trait implemented by types which can be placed in a stack.
///
-/// Like `OpenSslType`, it should not be implemented for any type outside
-/// of this crate.
-pub trait Stackable: OpenSslType {
+/// It should not be implemented for any type outside of this crate.
+pub trait Stackable: ForeignType {
/// The C stack type for this element.
///
/// Generally called `stack_st_{ELEMENT_TYPE}`, normally hidden by the
@@ -80,14 +80,16 @@ impl<T: Stackable> Borrow<StackRef<T>> for Stack<T> {
}
}
-impl<T: Stackable> OpenSslType for Stack<T> {
+impl<T: Stackable> ForeignType for Stack<T> {
type CType = T::StackType;
type Ref = StackRef<T>;
+ #[inline]
unsafe fn from_ptr(ptr: *mut T::StackType) -> Stack<T> {
Stack(ptr)
}
+ #[inline]
fn as_ptr(&self) -> *mut T::StackType {
self.0
}
@@ -152,7 +154,7 @@ impl<T: Stackable> ExactSizeIterator for IntoIter<T> {}
pub struct StackRef<T: Stackable>(Opaque, PhantomData<T>);
-impl<T: Stackable> OpenSslTypeRef for StackRef<T> {
+impl<T: Stackable> ForeignTypeRef for StackRef<T> {
type CType = T::StackType;
}
diff --git a/openssl/src/string.rs b/openssl/src/string.rs
new file mode 100644
index 00000000..4a1d3479
--- /dev/null
+++ b/openssl/src/string.rs
@@ -0,0 +1,80 @@
+use ffi;
+use foreign_types::{ForeignType, ForeignTypeRef};
+use libc::{c_char, c_void};
+use std::fmt;
+use std::ffi::CStr;
+use std::ops::Deref;
+use std::str;
+
+use stack::Stackable;
+
+foreign_type! {
+ type CType = c_char;
+ fn drop = free;
+
+ pub struct OpensslString;
+ pub struct OpensslStringRef;
+}
+
+impl OpensslString {
+ #[deprecated(note = "use from_ptr", since = "0.9.7")]
+ pub unsafe fn from_raw_parts(buf: *mut u8, _: usize) -> OpensslString {
+ OpensslString::from_ptr(buf as *mut c_char)
+ }
+
+ #[deprecated(note = "use from_ptr", since = "0.9.7")]
+ pub unsafe fn from_null_terminated(buf: *mut c_char) -> OpensslString {
+ OpensslString::from_ptr(buf)
+ }
+}
+
+impl fmt::Display for OpensslString {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&**self, f)
+ }
+}
+
+impl fmt::Debug for OpensslString {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(&**self, f)
+ }
+}
+
+impl Stackable for OpensslString {
+ type StackType = ffi::stack_st_OPENSSL_STRING;
+}
+
+impl Deref for OpensslStringRef {
+ type Target = str;
+
+ fn deref(&self) -> &str {
+ unsafe {
+ let slice = CStr::from_ptr(self.as_ptr()).to_bytes();
+ str::from_utf8_unchecked(slice)
+ }
+ }
+}
+
+impl fmt::Display for OpensslStringRef {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&**self, f)
+ }
+}
+
+impl fmt::Debug for OpensslStringRef {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(&**self, f)
+ }
+}
+
+#[cfg(not(ossl110))]
+unsafe fn free(buf: *mut c_char) {
+ ::ffi::CRYPTO_free(buf as *mut c_void);
+}
+
+#[cfg(ossl110)]
+unsafe fn free(buf: *mut c_char) {
+ ::ffi::CRYPTO_free(buf as *mut c_void,
+ concat!(file!(), "\0").as_ptr() as *const c_char,
+ line!() as ::libc::c_int);
+}
diff --git a/openssl/src/symm.rs b/openssl/src/symm.rs
index d4b15f28..5cf1ce0b 100644
--- a/openssl/src/symm.rs
+++ b/openssl/src/symm.rs
@@ -80,6 +80,22 @@ impl Cipher {
unsafe { Cipher(ffi::EVP_aes_256_gcm()) }
}
+ pub fn bf_cbc() -> Cipher {
+ unsafe { Cipher(ffi::EVP_bf_cbc()) }
+ }
+
+ pub fn bf_ecb() -> Cipher {
+ unsafe { Cipher(ffi::EVP_bf_ecb()) }
+ }
+
+ pub fn bf_cfb64() -> Cipher {
+ unsafe { Cipher(ffi::EVP_bf_cfb64()) }
+ }
+
+ pub fn bf_ofb() -> Cipher {
+ unsafe { Cipher(ffi::EVP_bf_ofb()) }
+ }
+
pub fn des_cbc() -> Cipher {
unsafe { Cipher(ffi::EVP_des_cbc()) }
}
@@ -92,6 +108,18 @@ impl Cipher {
unsafe { Cipher(ffi::EVP_rc4()) }
}
+ /// Requires the `v110` feature and OpenSSL 1.1.0.
+ #[cfg(all(ossl110, feature = "v110"))]
+ pub fn chacha20() -> Cipher {
+ unsafe { Cipher(ffi::EVP_chacha20()) }
+ }
+
+ /// Requires the `v110` feature and OpenSSL 1.1.0.
+ #[cfg(all(ossl110, feature = "v110"))]
+ pub fn chacha20_poly1305() -> Cipher {
+ unsafe { Cipher(ffi::EVP_chacha20_poly1305()) }
+ }
+
pub unsafe fn from_ptr(ptr: *const ffi::EVP_CIPHER) -> Cipher {
Cipher(ptr)
}
@@ -135,8 +163,7 @@ impl 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 `Cipher::iv_len`).
+ /// Panics if an IV is required by the cipher but not provided.
pub fn new(t: Cipher,
mode: Mode,
key: &[u8],
@@ -169,7 +196,13 @@ impl Crypter {
let key = key.as_ptr() as *mut _;
let iv = match (iv, t.iv_len()) {
(Some(iv), Some(len)) => {
- assert!(iv.len() == len);
+ if iv.len() != len {
+ assert!(iv.len() <= c_int::max_value() as usize);
+ try!(cvt(ffi::EVP_CIPHER_CTX_ctrl(crypter.ctx,
+ ffi::EVP_CTRL_GCM_SET_IVLEN,
+ iv.len() as c_int,
+ ptr::null_mut())));
+ }
iv.as_ptr() as *mut _
}
(Some(_), None) | (None, None) => ptr::null_mut(),
@@ -196,6 +229,39 @@ impl Crypter {
}
}
+ /// Sets the tag used to authenticate ciphertext in AEAD ciphers such as AES GCM.
+ ///
+ /// When decrypting cipher text using an AEAD cipher, this must be called before `finalize`.
+ pub fn set_tag(&mut self, tag: &[u8]) -> Result<(), ErrorStack> {
+ unsafe {
+ assert!(tag.len() <= c_int::max_value() as usize);
+ // NB: this constant is actually more general than just GCM.
+ cvt(ffi::EVP_CIPHER_CTX_ctrl(self.ctx,
+ ffi::EVP_CTRL_GCM_SET_TAG,
+ tag.len() as c_int,
+ tag.as_ptr() as *mut _))
+ .map(|_| ())
+ }
+ }
+
+ /// Feeds Additional Authenticated Data (AAD) through the cipher.
+ ///
+ /// This can only be used with AEAD ciphers such as AES GCM. Data fed in is not encrypted, but
+ /// is factored into the authentication tag. It must be called before the first call to
+ /// `update`.
+ pub fn aad_update(&mut self, input: &[u8]) -> Result<(), ErrorStack> {
+ unsafe {
+ assert!(input.len() <= c_int::max_value() as usize);
+ let mut len = 0;
+ cvt(ffi::EVP_CipherUpdate(self.ctx,
+ ptr::null_mut(),
+ &mut len,
+ input.as_ptr(),
+ input.len() as c_int))
+ .map(|_| ())
+ }
+ }
+
/// Feeds data from `input` through the cipher, writing encrypted/decrypted
/// bytes into `output`.
///
@@ -244,6 +310,25 @@ impl Crypter {
Ok(outl as usize)
}
}
+
+ /// Retrieves the authentication tag used to authenticate ciphertext in AEAD ciphers such
+ /// as AES GCM.
+ ///
+ /// When encrypting data with an AEAD cipher, this must be called after `finalize`.
+ ///
+ /// The size of the buffer indicates the required size of the tag. While some ciphers support a
+ /// range of tag sizes, it is recommended to pick the maximum size. For AES GCM, this is 16
+ /// bytes, for example.
+ pub fn get_tag(&self, tag: &mut [u8]) -> Result<(), ErrorStack> {
+ unsafe {
+ assert!(tag.len() <= c_int::max_value() as usize);
+ cvt(ffi::EVP_CIPHER_CTX_ctrl(self.ctx,
+ ffi::EVP_CTRL_GCM_GET_TAG,
+ tag.len() as c_int,
+ tag.as_mut_ptr() as *mut _))
+ .map(|_| ())
+ }
+ }
}
impl Drop for Crypter {
@@ -292,6 +377,52 @@ fn cipher(t: Cipher,
Ok(out)
}
+/// Like `encrypt`, but for AEAD ciphers such as AES GCM.
+///
+/// Additional Authenticated Data can be provided in the `aad` field, and the authentication tag
+/// will be copied into the `tag` field.
+///
+/// The size of the `tag` buffer indicates the required size of the tag. While some ciphers support
+/// a range of tag sizes, it is recommended to pick the maximum size. For AES GCM, this is 16 bytes,
+/// for example.
+pub fn encrypt_aead(t: Cipher,
+ key: &[u8],
+ iv: Option<&[u8]>,
+ aad: &[u8],
+ data: &[u8],
+ tag: &mut [u8])
+ -> Result<Vec<u8>, ErrorStack> {
+ let mut c = try!(Crypter::new(t, Mode::Encrypt, key, iv));
+ let mut out = vec![0; data.len() + t.block_size()];
+ try!(c.aad_update(aad));
+ let count = try!(c.update(data, &mut out));
+ let rest = try!(c.finalize(&mut out[count..]));
+ try!(c.get_tag(tag));
+ out.truncate(count + rest);
+ Ok(out)
+}
+
+/// Like `decrypt`, but for AEAD ciphers such as AES GCM.
+///
+/// Additional Authenticated Data can be provided in the `aad` field, and the authentication tag
+/// should be provided in the `tag` field.
+pub fn decrypt_aead(t: Cipher,
+ key: &[u8],
+ iv: Option<&[u8]>,
+ aad: &[u8],
+ data: &[u8],
+ tag: &[u8])
+ -> Result<Vec<u8>, ErrorStack> {
+ let mut c = try!(Crypter::new(t, Mode::Decrypt, key, iv));
+ let mut out = vec![0; data.len() + t.block_size()];
+ try!(c.aad_update(aad));
+ let count = try!(c.update(data, &mut out));
+ try!(c.set_tag(tag));
+ let rest = try!(c.finalize(&mut out[count..]));
+ out.truncate(count + rest);
+ Ok(out)
+}
+
#[cfg(ossl110)]
use ffi::{EVP_CIPHER_iv_length, EVP_CIPHER_block_size, EVP_CIPHER_key_length};
@@ -318,7 +449,8 @@ use self::compat::*;
#[cfg(test)]
mod tests {
- use serialize::hex::{FromHex, ToHex};
+ use hex::{FromHex, ToHex};
+ use super::*;
// Test vectors from FIPS-197:
// http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
@@ -385,12 +517,10 @@ mod tests {
}
fn cipher_test(ciphertype: super::Cipher, pt: &str, ct: &str, key: &str, iv: &str) {
- use serialize::hex::ToHex;
-
- 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 pt = Vec::from_hex(pt).unwrap();
+ let ct = Vec::from_hex(ct).unwrap();
+ let key = Vec::from_hex(key).unwrap();
+ let iv = Vec::from_hex(iv).unwrap();
let computed = super::decrypt(ciphertype, &key, Some(&iv), &ct).unwrap();
let expected = pt;
@@ -407,6 +537,35 @@ mod tests {
}
}
+ fn cipher_test_nopad(ciphertype: super::Cipher, pt: &str, ct: &str, key: &str, iv: &str) {
+ let pt = Vec::from_hex(pt).unwrap();
+ let ct = Vec::from_hex(ct).unwrap();
+ let key = Vec::from_hex(key).unwrap();
+ let iv = Vec::from_hex(iv).unwrap();
+
+ let computed = {
+ let mut c = Crypter::new(ciphertype, Mode::Decrypt, &key, Some(&iv)).unwrap();
+ c.pad(false);
+ let mut out = vec![0; ct.len() + ciphertype.block_size()];
+ let count = c.update(&ct, &mut out).unwrap();
+ let rest = c.finalize(&mut out[count..]).unwrap();
+ out.truncate(count + rest);
+ out
+ };
+ let expected = pt;
+
+ if computed != expected {
+ println!("Computed: {}", computed.to_hex());
+ println!("Expected: {}", expected.to_hex());
+ if computed.len() != expected.len() {
+ println!("Lengths differ: {} in computed vs {} expected",
+ computed.len(),
+ expected.len());
+ }
+ panic!("test failure");
+ }
+ }
+
#[test]
fn test_rc4() {
@@ -514,6 +673,51 @@ mod tests {
}
#[test]
+ fn test_bf_cbc() {
+ // https://www.schneier.com/code/vectors.txt
+
+ let pt = "37363534333231204E6F77206973207468652074696D6520666F722000000000";
+ let ct = "6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC";
+ let key = "0123456789ABCDEFF0E1D2C3B4A59687";
+ let iv = "FEDCBA9876543210";
+
+ cipher_test_nopad(super::Cipher::bf_cbc(), pt, ct, key, iv);
+ }
+
+ #[test]
+ fn test_bf_ecb() {
+
+ let pt = "5CD54CA83DEF57DA";
+ let ct = "B1B8CC0B250F09A0";
+ let key = "0131D9619DC1376E";
+ let iv = "0000000000000000";
+
+ cipher_test_nopad(super::Cipher::bf_ecb(), pt, ct, key, iv);
+ }
+
+ #[test]
+ fn test_bf_cfb64() {
+
+ let pt = "37363534333231204E6F77206973207468652074696D6520666F722000";
+ let ct = "E73214A2822139CAF26ECF6D2EB9E76E3DA3DE04D1517200519D57A6C3";
+ let key = "0123456789ABCDEFF0E1D2C3B4A59687";
+ let iv = "FEDCBA9876543210";
+
+ cipher_test_nopad(super::Cipher::bf_cfb64(), pt, ct, key, iv);
+ }
+
+ #[test]
+ fn test_bf_ofb() {
+
+ let pt = "37363534333231204E6F77206973207468652074696D6520666F722000";
+ let ct = "E73214A2822139CA62B343CC5B65587310DD908D0C241B2263C2CF80DA";
+ let key = "0123456789ABCDEFF0E1D2C3B4A59687";
+ let iv = "FEDCBA9876543210";
+
+ cipher_test_nopad(super::Cipher::bf_ofb(), pt, ct, key, iv);
+ }
+
+ #[test]
fn test_des_cbc() {
let pt = "54686973206973206120746573742e";
@@ -534,4 +738,93 @@ mod tests {
cipher_test(super::Cipher::des_ecb(), pt, ct, key, iv);
}
+
+ #[test]
+ fn test_aes128_gcm() {
+ let key = "0e00c76561d2bd9b40c3c15427e2b08f";
+ let iv =
+ "492cadaccd3ca3fbc9cf9f06eb3325c4e159850b0dbe98199b89b7af528806610b6f63998e1eae80c348e7\
+ 4cbb921d8326631631fc6a5d304f39166daf7ea15fa1977f101819adb510b50fe9932e12c5a85aa3fd1e73\
+ d8d760af218be829903a77c63359d75edd91b4f6ed5465a72662f5055999e059e7654a8edc921aa0d496";
+ let pt =
+ "fef03c2d7fb15bf0d2df18007d99f967c878ad59359034f7bb2c19af120685d78e32f6b8b83b032019956c\
+ a9c0195721476b85";
+ let aad =
+ "d8f1163d8c840292a2b2dacf4ac7c36aff8733f18fabb4fa5594544125e03d1e6e5d6d0fd61656c8d8f327\
+ c92839ae5539bb469c9257f109ebff85aad7bd220fdaa95c022dbd0c7bb2d878ad504122c943045d3c5eba\
+ 8f1f56c0";
+ let ct =
+ "4f6cf471be7cbd2575cd5a1747aea8fe9dea83e51936beac3e68f66206922060c697ffa7af80ad6bb68f2c\
+ f4fc97416ee52abe";
+ let tag = "e20b6655";
+
+ // this tag is smaller than you'd normally want, but I pulled this test from the part of
+ // the NIST test vectors that cover 4 byte tags.
+ let mut actual_tag = [0; 4];
+ let out = encrypt_aead(Cipher::aes_128_gcm(),
+ &Vec::from_hex(key).unwrap(),
+ Some(&Vec::from_hex(iv).unwrap()),
+ &Vec::from_hex(aad).unwrap(),
+ &Vec::from_hex(pt).unwrap(),
+ &mut actual_tag)
+ .unwrap();
+ assert_eq!(ct, out.to_hex());
+ assert_eq!(tag, actual_tag.to_hex());
+
+ let out = decrypt_aead(Cipher::aes_128_gcm(),
+ &Vec::from_hex(key).unwrap(),
+ Some(&Vec::from_hex(iv).unwrap()),
+ &Vec::from_hex(aad).unwrap(),
+ &Vec::from_hex(ct).unwrap(),
+ &Vec::from_hex(tag).unwrap()).unwrap();
+ assert_eq!(pt, out.to_hex());
+ }
+
+ #[test]
+ #[cfg(all(ossl110, feature = "v110"))]
+ fn test_chacha20() {
+ let key = "0000000000000000000000000000000000000000000000000000000000000000";
+ let iv = "00000000000000000000000000000000";
+ let pt = "000000000000000000000000000000000000000000000000000000000000000000000000000000000\
+ 00000000000000000000000000000000000000000000000";
+ let ct = "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7\
+ 724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586";
+
+ cipher_test(Cipher::chacha20(), pt, ct, key, iv);
+ }
+
+ #[test]
+ #[cfg(all(ossl110, feature = "v110"))]
+ fn test_chacha20_poly1305() {
+ let key = "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f";
+ let iv = "070000004041424344454647";
+ let aad = "50515253c0c1c2c3c4c5c6c7";
+ let pt = "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393\
+ a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f722074\
+ 6865206675747572652c2073756e73637265656e20776f756c642062652069742e";
+ let ct = "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca967128\
+ 2fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fa\
+ b324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116";
+ let tag = "1ae10b594f09e26a7e902ecbd0600691";
+
+ let mut actual_tag = [0; 16];
+ let out = encrypt_aead(Cipher::chacha20_poly1305(),
+ &Vec::from_hex(key).unwrap(),
+ Some(&Vec::from_hex(iv).unwrap()),
+ &Vec::from_hex(aad).unwrap(),
+ &Vec::from_hex(pt).unwrap(),
+ &mut actual_tag)
+ .unwrap();
+ assert_eq!(ct, out.to_hex());
+ assert_eq!(tag, actual_tag.to_hex());
+
+ let out = decrypt_aead(Cipher::chacha20_poly1305(),
+ &Vec::from_hex(key).unwrap(),
+ Some(&Vec::from_hex(iv).unwrap()),
+ &Vec::from_hex(aad).unwrap(),
+ &Vec::from_hex(ct).unwrap(),
+ &Vec::from_hex(tag).unwrap())
+ .unwrap();
+ assert_eq!(pt, out.to_hex());
+ }
}
diff --git a/openssl/src/types.rs b/openssl/src/types.rs
index a444529d..25ffe501 100644
--- a/openssl/src/types.rs
+++ b/openssl/src/types.rs
@@ -1,43 +1,5 @@
-//! Items used by other types.
+#[deprecated(note = "use foreign_types instead", since = "0.9.7")]
+pub use foreign_types::ForeignType as OpenSslType;
-/// A type implemented by wrappers over OpenSSL types.
-///
-/// This should not be implemented by anything outside of this crate; new methods may be added at
-/// any time.
-pub trait OpenSslType: Sized {
- /// The raw C type.
- type CType;
-
- /// The type representing a reference to this type.
- type Ref: OpenSslTypeRef<CType = Self::CType>;
-
- /// Constructs an instance of this type from its raw type.
- unsafe fn from_ptr(ptr: *mut Self::CType) -> Self;
-
- /// Returns a raw pointer to the wrapped value.
- fn as_ptr(&self) -> *mut Self::CType;
-}
-
-/// A trait implemented by types which reference borrowed OpenSSL types.
-///
-/// This should not be implemented by anything outside of this crate; new methods may be added at
-/// any time.
-pub trait OpenSslTypeRef: Sized {
- /// The raw C type.
- type CType;
-
- /// Constructs a shared instance of this type from its raw type.
- unsafe fn from_ptr<'a>(ptr: *mut Self::CType) -> &'a Self {
- &*(ptr as *mut _)
- }
-
- /// Constructs a mutable reference of this type from its raw type.
- unsafe fn from_ptr_mut<'a>(ptr: *mut Self::CType) -> &'a mut Self {
- &mut *(ptr as *mut _)
- }
-
- /// Returns a raw pointer to the wrapped value.
- fn as_ptr(&self) -> *mut Self::CType {
- self as *const _ as *mut _
- }
-}
+#[deprecated(note = "use foreign_types instead", since = "0.9.7")]
+pub use foreign_types::ForeignTypeRef as OpenSslTypeRef;
diff --git a/openssl/src/util.rs b/openssl/src/util.rs
index 302bd316..f4883976 100644
--- a/openssl/src/util.rs
+++ b/openssl/src/util.rs
@@ -4,6 +4,8 @@ use std::cell::UnsafeCell;
use std::panic::{self, AssertUnwindSafe};
use std::slice;
+use error::ErrorStack;
+
/// Wraps a user-supplied callback and a slot for panics thrown inside the callback (while FFI
/// frames are on the stack).
///
@@ -33,10 +35,7 @@ impl<F> Drop for CallbackState<F> {
}
}
-/// 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<F>(buf: *mut c_char,
+pub unsafe extern fn invoke_passwd_cb_old<F>(buf: *mut c_char,
size: c_int,
_rwflag: c_int,
cb_state: *mut c_void)
@@ -46,9 +45,7 @@ pub unsafe extern "C" fn invoke_passwd_cb<F>(buf: *mut c_char,
let callback = &mut *(cb_state as *mut CallbackState<F>);
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)
}));
@@ -61,6 +58,36 @@ pub unsafe extern "C" fn invoke_passwd_cb<F>(buf: *mut c_char,
}
}
+/// 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<F>(buf: *mut c_char,
+ size: c_int,
+ _rwflag: c_int,
+ cb_state: *mut c_void)
+ -> c_int
+ where F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack>
+{
+ let callback = &mut *(cb_state as *mut CallbackState<F>);
+
+ 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)
+ }));
+
+ match result {
+ Ok(Ok(len)) => len as c_int,
+ Ok(Err(_)) => {
+ // FIXME restore error stack
+ 0
+ }
+ Err(err) => {
+ callback.panic = Some(err);
+ 0
+ }
+ }
+}
+
/// This is intended to be used as the inner type for `FooRef` types converted from raw C pointers.
/// It has an `UnsafeCell` internally to inform the compiler about aliasability and doesn't
/// implement `Copy`, so it can't be dereferenced.
diff --git a/openssl/src/verify.rs b/openssl/src/verify.rs
index 2f070fe5..f554127a 100644
--- a/openssl/src/verify.rs
+++ b/openssl/src/verify.rs
@@ -1,9 +1,9 @@
use libc::c_uint;
use ffi;
+use foreign_types::ForeignTypeRef;
use cvt;
use error::ErrorStack;
-use types::OpenSslTypeRef;
bitflags! {
pub flags X509CheckFlags: c_uint {
@@ -19,7 +19,13 @@ bitflags! {
}
}
-type_!(X509VerifyParam, X509VerifyParamRef, ffi::X509_VERIFY_PARAM, ffi::X509_VERIFY_PARAM_free);
+foreign_type! {
+ type CType = ffi::X509_VERIFY_PARAM;
+ fn drop = ffi::X509_VERIFY_PARAM_free;
+
+ pub struct X509VerifyParam;
+ pub struct X509VerifyParamRef;
+}
impl X509VerifyParamRef {
pub fn set_hostflags(&mut self, hostflags: X509CheckFlags) {
diff --git a/openssl/src/version.rs b/openssl/src/version.rs
index d7db39a7..bf47695b 100644
--- a/openssl/src/version.rs
+++ b/openssl/src/version.rs
@@ -20,11 +20,8 @@ use ffi::{SSLEAY_VERSION as OPENSSL_VERSION, SSLEAY_CFLAGS as OPENSSL_CFLAGS,
SSLeay_version as OpenSSL_version};
#[cfg(ossl110)]
-use ffi::{OPENSSL_VERSION, OPENSSL_CFLAGS};
-#[cfg(ossl110)]
-use ffi::{OPENSSL_BUILT_ON, OPENSSL_PLATFORM, OPENSSL_DIR};
-#[cfg(ossl110)]
-use ffi::{OpenSSL_version_num, OpenSSL_version};
+use ffi::{OPENSSL_VERSION, OPENSSL_CFLAGS, OPENSSL_BUILT_ON, OPENSSL_PLATFORM, OPENSSL_DIR,
+ OpenSSL_version_num, OpenSSL_version};
/// OPENSSL_VERSION_NUMBER is a numeric release version identifier:
///
@@ -92,8 +89,13 @@ fn test_versions() {
println!("Platform: '{}'", platform());
println!("Dir: '{}'", dir());
+ #[cfg(not(libressl))]
+ fn expected_name() -> &'static str { "OpenSSL" }
+ #[cfg(libressl)]
+ fn expected_name() -> &'static str { "LibreSSL" }
+
assert!(number() > 0);
- assert!(version().starts_with("OpenSSL"));
+ assert!(version().starts_with(expected_name()));
assert!(c_flags().starts_with("compiler:"));
assert!(built_on().starts_with("built on:"));
assert!(dir().starts_with("OPENSSLDIR:"));
diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs
index e3a54d7d..6b65fbd2 100644
--- a/openssl/src/x509/mod.rs
+++ b/openssl/src/x509/mod.rs
@@ -1,7 +1,8 @@
#![allow(deprecated)]
use libc::{c_int, c_long};
+use ffi;
+use foreign_types::{ForeignType, ForeignTypeRef};
use std::borrow::Borrow;
-use std::cmp;
use std::collections::HashMap;
use std::error::Error;
use std::ffi::{CStr, CString};
@@ -14,17 +15,16 @@ use std::slice;
use std::str;
use {cvt, cvt_p};
-use asn1::{Asn1StringRef, Asn1Time, Asn1TimeRef, Asn1IntegerRef};
+use asn1::{Asn1StringRef, Asn1Time, Asn1TimeRef, Asn1BitStringRef, Asn1IntegerRef, Asn1ObjectRef};
+use bio::MemBioSlice;
use bn::{BigNum, MSB_MAYBE_ZERO};
-use bio::{MemBio, MemBioSlice};
use conf::ConfRef;
-use hash::MessageDigest;
-use pkey::{PKey, PKeyRef};
use error::ErrorStack;
-use ffi;
+use hash::MessageDigest;
use nid::{self, Nid};
-use types::{OpenSslType, OpenSslTypeRef};
+use pkey::{PKey, PKeyRef};
use stack::{Stack, StackRef, Stackable};
+use string::OpensslString;
#[cfg(ossl10x)]
use ffi::{X509_set_notBefore, X509_set_notAfter, ASN1_STRING_data, X509_STORE_CTX_get_chain};
@@ -39,6 +39,7 @@ pub mod verify;
use x509::extension::{ExtensionType, Extension};
pub mod extension;
+pub mod store;
#[cfg(test)]
mod tests;
@@ -55,7 +56,13 @@ pub const X509_FILETYPE_PEM: X509FileType = X509FileType(ffi::X509_FILETYPE_PEM)
pub const X509_FILETYPE_ASN1: X509FileType = X509FileType(ffi::X509_FILETYPE_ASN1);
pub const X509_FILETYPE_DEFAULT: X509FileType = X509FileType(ffi::X509_FILETYPE_DEFAULT);
-type_!(X509StoreContext, X509StoreContextRef, ffi::X509_STORE_CTX, ffi::X509_STORE_CTX_free);
+foreign_type! {
+ type CType = ffi::X509_STORE_CTX;
+ fn drop = ffi::X509_STORE_CTX_free;
+
+ pub struct X509StoreContext;
+ pub struct X509StoreContextRef;
+}
impl X509StoreContextRef {
pub fn error(&self) -> Option<X509VerifyError> {
@@ -376,7 +383,13 @@ impl X509Builder {
}
}
-type_!(X509, X509Ref, ffi::X509, ffi::X509_free);
+foreign_type! {
+ type CType = ffi::X509;
+ fn drop = ffi::X509_free;
+
+ pub struct X509;
+ pub struct X509Ref;
+}
impl X509Ref {
pub fn subject_name(&self) -> &X509NameRef {
@@ -420,8 +433,8 @@ impl X509Ref {
}
}
- /// Returns certificate Not After validity period.
- pub fn not_after<'a>(&'a self) -> &'a Asn1TimeRef {
+ /// Returns the certificate's Not After validity period.
+ pub fn not_after(&self) -> &Asn1TimeRef {
unsafe {
let date = compat::X509_get_notAfter(self.as_ptr());
assert!(!date.is_null());
@@ -429,8 +442,8 @@ impl X509Ref {
}
}
- /// Returns certificate Not Before validity period.
- pub fn not_before<'a>(&'a self) -> &'a Asn1TimeRef {
+ /// Returns the certificate's Not Before validity period.
+ pub fn not_before(&self) -> &Asn1TimeRef {
unsafe {
let date = compat::X509_get_notBefore(self.as_ptr());
assert!(!date.is_null());
@@ -438,23 +451,47 @@ impl X509Ref {
}
}
- /// Writes certificate as PEM
- pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
- let mem_bio = try!(MemBio::new());
+ /// Returns the certificate's signature
+ pub fn signature(&self) -> &Asn1BitStringRef {
+ unsafe {
+ let mut signature = ptr::null();
+ compat::X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr());
+ assert!(!signature.is_null());
+ Asn1BitStringRef::from_ptr(signature as *mut _)
+ }
+ }
+
+ /// Returns the certificate's signature algorithm.
+ pub fn signature_algorithm(&self) -> &X509AlgorithmRef {
+ unsafe {
+ let mut algor = ptr::null();
+ compat::X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr());
+ assert!(!algor.is_null());
+ X509AlgorithmRef::from_ptr(algor as *mut _)
+ }
+ }
+
+ /// Returns the list of OCSP responder URLs specified in the certificate's Authority Information
+ /// Access field.
+ pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> {
unsafe {
- try!(cvt(ffi::PEM_write_bio_X509(mem_bio.as_ptr(), self.as_ptr())));
+ cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p))
}
- Ok(mem_bio.get_buf().to_owned())
}
- /// Returns a DER serialized form of the certificate
- pub fn to_der(&self) -> Result<Vec<u8>, ErrorStack> {
- let mem_bio = try!(MemBio::new());
+ /// Checks that this certificate issued `subject`.
+ pub fn issued(&self, subject: &X509Ref) -> Result<(), X509VerifyError> {
unsafe {
- ffi::i2d_X509_bio(mem_bio.as_ptr(), self.as_ptr());
+ let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr());
+ match X509VerifyError::from_raw(r as c_long) {
+ Some(e) => Err(e),
+ None => Ok(()),
+ }
}
- Ok(mem_bio.get_buf().to_owned())
}
+
+ to_pem!(ffi::PEM_write_bio_X509);
+ to_der!(ffi::i2d_X509);
}
impl ToOwned for X509Ref {
@@ -474,25 +511,36 @@ impl X509 {
X509Builder::new()
}
- /// Reads a certificate from DER.
- pub fn from_der(buf: &[u8]) -> Result<X509, ErrorStack> {
- unsafe {
- let mut ptr = buf.as_ptr();
- let len = cmp::min(buf.len(), c_long::max_value() as usize) as c_long;
- let x509 = try!(cvt_p(ffi::d2i_X509(ptr::null_mut(), &mut ptr, len)));
- Ok(X509::from_ptr(x509))
- }
- }
+ from_pem!(X509, ffi::PEM_read_bio_X509);
+ from_der!(X509, ffi::d2i_X509);
- /// Reads a certificate from PEM.
- pub fn from_pem(buf: &[u8]) -> Result<X509, ErrorStack> {
- let mem_bio = try!(MemBioSlice::new(buf));
+ /// Deserializes a list of PEM-formatted certificates.
+ pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> {
unsafe {
- let handle = try!(cvt_p(ffi::PEM_read_bio_X509(mem_bio.as_ptr(),
- ptr::null_mut(),
- None,
- ptr::null_mut())));
- Ok(X509::from_ptr(handle))
+ ffi::init();
+ let bio = try!(MemBioSlice::new(pem));
+
+ let mut certs = vec![];
+ loop {
+ let r = ffi::PEM_read_bio_X509(bio.as_ptr(),
+ ptr::null_mut(),
+ None,
+ ptr::null_mut());
+ if r.is_null() {
+ let err = ffi::ERR_peek_last_error();
+ if ffi::ERR_GET_LIB(err) == ffi::ERR_LIB_PEM
+ && ffi::ERR_GET_REASON(err) == ffi::PEM_R_NO_START_LINE {
+ ffi::ERR_clear_error();
+ break;
+ }
+
+ return Err(ErrorStack::get());
+ } else {
+ certs.push(X509(r));
+ }
+ }
+
+ Ok(certs)
}
}
}
@@ -534,7 +582,13 @@ impl<'a> X509v3Context<'a> {
}
}
-type_!(X509Extension, X509ExtensionRef, ffi::X509_EXTENSION, ffi::X509_EXTENSION_free);
+foreign_type! {
+ type CType = ffi::X509_EXTENSION;
+ fn drop = ffi::X509_EXTENSION_free;
+
+ pub struct X509Extension;
+ pub struct X509ExtensionRef;
+}
impl Stackable for X509Extension {
type StackType = ffi::stack_st_X509_EXTENSION;
@@ -635,7 +689,13 @@ impl X509NameBuilder {
}
}
-type_!(X509Name, X509NameRef, ffi::X509_NAME, ffi::X509_NAME_free);
+foreign_type! {
+ type CType = ffi::X509_NAME;
+ fn drop = ffi::X509_NAME_free;
+
+ pub struct X509Name;
+ pub struct X509NameRef;
+}
impl X509Name {
/// Returns a new builder.
@@ -694,7 +754,13 @@ impl<'a> Iterator for X509NameEntries<'a> {
}
}
-type_!(X509NameEntry, X509NameEntryRef, ffi::X509_NAME_ENTRY, ffi::X509_NAME_ENTRY_free);
+foreign_type! {
+ type CType = ffi::X509_NAME_ENTRY;
+ fn drop = ffi::X509_NAME_ENTRY_free;
+
+ pub struct X509NameEntry;
+ pub struct X509NameEntryRef;
+}
impl X509NameEntryRef {
pub fn data(&self) -> &Asn1StringRef {
@@ -769,7 +835,13 @@ impl X509ReqBuilder {
}
}
-type_!(X509Req, X509ReqRef, ffi::X509_REQ, ffi::X509_REQ_free);
+foreign_type! {
+ type CType = ffi::X509_REQ;
+ fn drop = ffi::X509_REQ_free;
+
+ pub struct X509Req;
+ pub struct X509ReqRef;
+}
impl X509Req {
pub fn builder() -> Result<X509ReqBuilder, ErrorStack> {
@@ -787,25 +859,40 @@ impl X509Req {
Ok(X509Req::from_ptr(handle))
}
}
+
+ from_der!(X509Req, ffi::d2i_X509_REQ);
}
impl X509ReqRef {
- /// Writes CSR as PEM
- pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
- let mem_bio = try!(MemBio::new());
- if unsafe { ffi::PEM_write_bio_X509_REQ(mem_bio.as_ptr(), self.as_ptr()) } != 1 {
- return Err(ErrorStack::get());
+ to_pem!(ffi::PEM_write_bio_X509_REQ);
+ to_der!(ffi::i2d_X509_REQ);
+
+ pub fn version(&self) -> i32
+ {
+ unsafe {
+ compat::X509_REQ_get_version(self.as_ptr()) as i32
+ }
+ }
+
+ pub fn set_version(&mut self, value: i32) -> Result<(), ErrorStack>
+ {
+ unsafe {
+ cvt(ffi::X509_REQ_set_version(self.as_ptr(), value as c_long)).map(|_| ())
}
- Ok(mem_bio.get_buf().to_owned())
}
- /// Returns a DER serialized form of the CSR
- pub fn to_der(&self) -> Result<Vec<u8>, ErrorStack> {
- let mem_bio = try!(MemBio::new());
+ pub fn subject_name(&self) -> &X509NameRef {
unsafe {
- ffi::i2d_X509_REQ_bio(mem_bio.as_ptr(), self.as_ptr());
+ let name = compat::X509_REQ_get_subject_name(self.as_ptr());
+ assert!(!name.is_null());
+ X509NameRef::from_ptr(name)
+ }
+ }
+
+ pub fn set_subject_name(&mut self, value: &X509NameRef) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::X509_REQ_set_subject_name(self.as_ptr(), value.as_ptr())).map(|_| ())
}
- Ok(mem_bio.get_buf().to_owned())
}
}
@@ -932,7 +1019,13 @@ impl X509VerifyError {
}
}
-type_!(GeneralName, GeneralNameRef, ffi::GENERAL_NAME, ffi::GENERAL_NAME_free);
+foreign_type! {
+ type CType = ffi::GENERAL_NAME;
+ fn drop = ffi::GENERAL_NAME_free;
+
+ pub struct GeneralName;
+ pub struct GeneralNameRef;
+}
impl GeneralNameRef {
/// Returns the contents of this `GeneralName` if it is a `dNSName`.
@@ -972,18 +1065,42 @@ impl Stackable for GeneralName {
type StackType = ffi::stack_st_GENERAL_NAME;
}
+foreign_type! {
+ type CType = ffi::X509_ALGOR;
+ fn drop = ffi::X509_ALGOR_free;
+
+ pub struct X509Algorithm;
+ pub struct X509AlgorithmRef;
+}
+
+impl X509AlgorithmRef {
+ /// Returns the ASN.1 OID of this algorithm.
+ pub fn object(&self) -> &Asn1ObjectRef {
+ unsafe {
+ let mut oid = ptr::null();
+ compat::X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr());
+ assert!(!oid.is_null());
+ Asn1ObjectRef::from_ptr(oid as *mut _)
+ }
+ }
+}
+
#[cfg(ossl110)]
mod compat {
pub use ffi::X509_getm_notAfter as X509_get_notAfter;
pub use ffi::X509_getm_notBefore as X509_get_notBefore;
pub use ffi::X509_up_ref;
pub use ffi::X509_get0_extensions;
+ pub use ffi::X509_REQ_get_version;
+ pub use ffi::X509_REQ_get_subject_name;
+ pub use ffi::X509_get0_signature;
+ pub use ffi::X509_ALGOR_get0;
}
#[cfg(ossl10x)]
#[allow(bad_style)]
mod compat {
- use libc::c_int;
+ use libc::{c_int, c_void};
use ffi;
pub unsafe fn X509_get_notAfter(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME {
@@ -1011,4 +1128,36 @@ mod compat {
(*info).extensions
}
}
+
+ pub unsafe fn X509_REQ_get_version(x: *mut ffi::X509_REQ) -> ::libc::c_long
+ {
+ ::ffi::ASN1_INTEGER_get((*(*x).req_info).version)
+ }
+
+ pub unsafe fn X509_REQ_get_subject_name(x: *mut ffi::X509_REQ) -> *mut ::ffi::X509_NAME
+ {
+ (*(*x).req_info).subject
+ }
+
+ pub unsafe fn X509_get0_signature(psig: *mut *const ffi::ASN1_BIT_STRING,
+ palg: *mut *const ffi::X509_ALGOR,
+ x: *const ffi::X509) {
+ if !psig.is_null() {
+ *psig = (*x).signature;
+ }
+ if !palg.is_null() {
+ *palg = (*x).sig_alg;
+ }
+ }
+
+ pub unsafe fn X509_ALGOR_get0(paobj: *mut *const ffi::ASN1_OBJECT,
+ pptype: *mut c_int,
+ pval: *mut *mut c_void,
+ alg: *const ffi::X509_ALGOR) {
+ if !paobj.is_null() {
+ *paobj = (*alg).algorithm;
+ }
+ assert!(pptype.is_null());
+ assert!(pval.is_null());
+ }
}
diff --git a/openssl/src/x509/store.rs b/openssl/src/x509/store.rs
new file mode 100644
index 00000000..8b7a084b
--- /dev/null
+++ b/openssl/src/x509/store.rs
@@ -0,0 +1,65 @@
+use ffi;
+use foreign_types::ForeignTypeRef;
+use std::mem;
+
+use {cvt, cvt_p};
+use error::ErrorStack;
+use x509::X509;
+
+foreign_type! {
+ type CType = ffi::X509_STORE;
+ fn drop = ffi::X509_STORE_free;
+
+ pub struct X509StoreBuilder;
+ pub struct X509StoreBuilderRef;
+}
+
+impl X509StoreBuilder {
+ /// Returns a builder for a certificate store.
+ ///
+ /// The store is initially empty.
+ pub fn new() -> Result<X509StoreBuilder, ErrorStack> {
+ unsafe {
+ ffi::init();
+
+ cvt_p(ffi::X509_STORE_new()).map(X509StoreBuilder)
+ }
+ }
+
+ /// Constructs the `X509Store`.
+ pub fn build(self) -> X509Store {
+ let store = X509Store(self.0);
+ mem::forget(self);
+ store
+ }
+}
+
+impl X509StoreBuilderRef {
+ /// Adds a certificate to the certificate store.
+ pub fn add_cert(&mut self, cert: X509) -> Result<(), ErrorStack> {
+ unsafe {
+ let ptr = cert.as_ptr();
+ mem::forget(cert); // the cert will be freed inside of X509_STORE_add_cert on error
+ cvt(ffi::X509_STORE_add_cert(self.as_ptr(), ptr)).map(|_| ())
+ }
+ }
+
+ /// Load certificates from their default locations.
+ ///
+ /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR`
+ /// environment variables if present, or defaults specified at OpenSSL
+ /// build time otherwise.
+ pub fn set_default_paths(&mut self) -> Result<(), ErrorStack> {
+ unsafe {
+ cvt(ffi::X509_STORE_set_default_paths(self.as_ptr())).map(|_| ())
+ }
+ }
+}
+
+foreign_type! {
+ type CType = ffi::X509_STORE;
+ fn drop = ffi::X509_STORE_free;
+
+ pub struct X509Store;
+ pub struct X509StoreRef;
+}
diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs
index 514b8edc..2632eade 100644
--- a/openssl/src/x509/tests.rs
+++ b/openssl/src/x509/tests.rs
@@ -1,14 +1,17 @@
-use serialize::hex::FromHex;
+use hex::{FromHex, ToHex};
use asn1::Asn1Time;
use bn::{BigNum, MSB_MAYBE_ZERO};
+use ec::{NAMED_CURVE, EcGroup, EcKey};
use hash::MessageDigest;
+use nid::X9_62_PRIME256V1;
use pkey::PKey;
use rsa::Rsa;
use stack::Stack;
use x509::{X509, X509Generator, X509Name, X509Req};
use x509::extension::{Extension, BasicConstraints, KeyUsage, ExtendedKeyUsage,
SubjectKeyIdentifier, AuthorityKeyIdentifier, SubjectAlternativeName};
+use ssl::{SslMethod, SslContextBuilder};
use x509::extension::AltNameOption as SAN;
use x509::extension::KeyUsageOption::{DigitalSignature, KeyEncipherment};
use x509::extension::ExtKeyUsageOption::{self, ClientAuth, ServerAuth};
@@ -76,7 +79,12 @@ fn test_req_gen() {
let pkey = pkey();
let req = get_generator().request(&pkey).unwrap();
- req.to_pem().unwrap();
+ let reqpem = req.to_pem().unwrap();
+
+ let req = X509Req::from_pem(&reqpem).ok().expect("Failed to load PEM");
+ let cn = (*req).subject_name().entries_by_nid(nid::COMMONNAME).next().unwrap();
+ assert_eq!(0, (*req).version());
+ assert_eq!(cn.data().as_slice(), b"test_me");
// FIXME: check data in result to be correct, needs implementation
// of X509_REQ getters
@@ -89,7 +97,7 @@ fn test_cert_loading() {
let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap();
let hash_str = "59172d9313e84459bcff27f967e79e6e9217e584";
- let hash_vec = hash_str.from_hex().unwrap();
+ let hash_vec = Vec::from_hex(hash_str).unwrap();
assert_eq!(fingerprint, hash_vec);
}
@@ -136,7 +144,7 @@ fn test_nid_values() {
assert_eq!(email.data().as_slice(), b"[email protected]");
let friendly = subject.entries_by_nid(nid::FRIENDLYNAME).next().unwrap();
- assert_eq!(&*friendly.data().as_utf8().unwrap(), "Example");
+ assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example");
}
#[test]
@@ -260,3 +268,64 @@ fn x509_req_builder() {
builder.sign(&pkey, MessageDigest::sha256()).unwrap();
}
+
+#[test]
+fn test_stack_from_pem() {
+ let certs = include_bytes!("../../test/certs.pem");
+ let certs = X509::stack_from_pem(certs).unwrap();
+
+ assert_eq!(certs.len(), 2);
+ assert_eq!(certs[0].fingerprint(MessageDigest::sha1()).unwrap().to_hex(),
+ "59172d9313e84459bcff27f967e79e6e9217e584");
+ assert_eq!(certs[1].fingerprint(MessageDigest::sha1()).unwrap().to_hex(),
+ "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875");
+}
+
+#[test]
+fn issued() {
+ let cert = include_bytes!("../../test/cert.pem");
+ let cert = X509::from_pem(cert).unwrap();
+ let ca = include_bytes!("../../test/root-ca.pem");
+ let ca = X509::from_pem(ca).unwrap();
+
+ ca.issued(&cert).unwrap();
+ cert.issued(&cert).err().unwrap();
+}
+
+#[test]
+fn ecdsa_cert() {
+ let mut group = EcGroup::from_curve_name(X9_62_PRIME256V1).unwrap();
+ group.set_asn1_flag(NAMED_CURVE);
+ let key = EcKey::generate(&group).unwrap();
+ let key = PKey::from_ec_key(key).unwrap();
+
+ let cert = X509Generator::new()
+ .set_valid_period(365)
+ .add_name("CN".to_owned(), "TestServer".to_owned())
+ .set_sign_hash(MessageDigest::sha256())
+ .sign(&key)
+ .unwrap();
+
+ let mut ctx = SslContextBuilder::new(SslMethod::tls()).unwrap();
+ ctx.set_certificate(&cert).unwrap();
+ ctx.set_private_key(&key).unwrap();
+ ctx.check_private_key().unwrap();
+}
+
+#[test]
+fn signature() {
+ let cert = include_bytes!("../../test/cert.pem");
+ let cert = X509::from_pem(cert).unwrap();
+ let signature = cert.signature();
+ assert_eq!(signature.as_slice().to_hex(),
+ "4af607b889790b43470442cfa551cdb8b6d0b0340d2958f76b9e3ef6ad4992230cead6842587f0ecad5\
+ 78e6e11a221521e940187e3d6652de14e84e82f6671f097cc47932e022add3c0cb54a26bf27fa84c107\
+ 4971caa6bee2e42d34a5b066c427f2d452038082b8073993399548088429de034fdd589dcfb0dd33be7\
+ ebdfdf698a28d628a89568881d658151276bde333600969502c4e62e1d3470a683364dfb241f78d310a\
+ 89c119297df093eb36b7fd7540224f488806780305d1e79ffc938fe2275441726522ab36d88348e6c51\
+ f13dcc46b5e1cdac23c974fd5ef86aa41e91c9311655090a52333bc79687c748d833595d4c5f987508f\
+ e121997410d37c");
+ let algorithm = cert.signature_algorithm();
+ assert_eq!(algorithm.object().nid(), nid::SHA256WITHRSAENCRYPTION);
+ assert_eq!(algorithm.object().to_string(), "sha256WithRSAEncryption");
+}