diff --git a/.travis.yml b/.travis.yml index 91c6ad62..0d76be3d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ addons: - gcc-arm-linux-gnueabihf rust: - nightly -- 1.8.0 +- 1.9.0 os: - osx - linux diff --git a/appveyor.yml b/appveyor.yml index f835d9f6..054b88f2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,8 +10,8 @@ environment: install: - ps: Start-FileDownload "http://slproweb.com/download/Win${env:BITS}OpenSSL-1_0_2h.exe" - Win%BITS%OpenSSL-1_0_2h.exe /SILENT /VERYSILENT /SP- /DIR="C:\OpenSSL" - - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.8.0-${env:TARGET}.exe" - - rust-1.8.0-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" + - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.9.0-${env:TARGET}.exe" + - rust-1.9.0-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin - SET PATH=%PATH%;C:\MinGW\bin - rustc -V diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 1a15fecb..2deef976 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -269,6 +269,7 @@ pub type PasswordCallback = extern "C" fn(buf: *mut c_char, size: c_int, pub const BIO_TYPE_NONE: c_int = 0; pub const BIO_CTRL_EOF: c_int = 2; +pub const BIO_CTRL_INFO: c_int = 3; pub const BIO_CTRL_FLUSH: c_int = 11; pub const BIO_C_SET_BUF_MEM_EOF_RETURN: c_int = 130; @@ -453,6 +454,11 @@ fn set_id_callback() { #[cfg(not(unix))] fn set_id_callback() {} +// macros +pub unsafe fn BIO_get_mem_data(b: *mut BIO, pp: *mut *mut c_char) -> c_long { + BIO_ctrl(b, BIO_CTRL_INFO, 0, pp as *mut c_void) +} + // True functions extern "C" { pub fn ASN1_INTEGER_set(dest: *mut ASN1_INTEGER, value: c_long) -> c_int; @@ -466,6 +472,7 @@ extern "C" { pub fn BIO_read(b: *mut BIO, buf: *mut c_void, len: c_int) -> c_int; pub fn BIO_write(b: *mut BIO, buf: *const c_void, len: c_int) -> c_int; pub fn BIO_s_mem() -> *const BIO_METHOD; + pub fn BIO_new_mem_buf(buf: *const c_void, len: c_int) -> *mut BIO; pub fn BN_new() -> *mut BIGNUM; pub fn BN_dup(n: *mut BIGNUM) -> *mut BIGNUM; @@ -741,6 +748,7 @@ extern "C" { pub fn SSL_get_wbio(ssl: *mut SSL) -> *mut BIO; pub fn SSL_accept(ssl: *mut SSL) -> c_int; pub fn SSL_connect(ssl: *mut SSL) -> c_int; + pub fn SSL_do_handshake(ssl: *mut SSL) -> c_int; pub fn SSL_ctrl(ssl: *mut SSL, cmd: c_int, larg: c_long, parg: *mut c_void) -> c_long; pub fn SSL_get_error(ssl: *mut SSL, ret: c_int) -> c_int; diff --git a/openssl/Cargo.toml b/openssl/Cargo.toml index 218969eb..3d4b098c 100644 --- a/openssl/Cargo.toml +++ b/openssl/Cargo.toml @@ -26,9 +26,6 @@ rfc5114 = ["openssl-sys/rfc5114"] ecdh_auto = ["openssl-sys-extras/ecdh_auto"] pkcs5_pbkdf2_hmac = ["openssl-sys/pkcs5_pbkdf2_hmac"] -nightly = [] -catch_unwind = [] - [dependencies] bitflags = ">= 0.5.0, < 0.8.0" lazy_static = "0.2" diff --git a/openssl/src/asn1/mod.rs b/openssl/src/asn1/mod.rs index d5561b62..202909ec 100644 --- a/openssl/src/asn1/mod.rs +++ b/openssl/src/asn1/mod.rs @@ -2,8 +2,7 @@ use libc::c_long; use std::ptr; use ffi; -use ssl::error::SslError; - +use error::ErrorStack; pub struct Asn1Time { handle: *mut ffi::ASN1_TIME, @@ -19,7 +18,7 @@ impl Asn1Time { } } - fn new_with_period(period: u64) -> Result { + fn new_with_period(period: u64) -> Result { ffi::init(); let handle = unsafe { @@ -29,7 +28,7 @@ impl Asn1Time { } /// Creates a new time on specified interval in days from now - pub fn days_from_now(days: u32) -> Result { + pub fn days_from_now(days: u32) -> Result { Asn1Time::new_with_period(days as u64 * 60 * 60 * 24) } diff --git a/openssl/src/bio.rs b/openssl/src/bio.rs new file mode 100644 index 00000000..841aca0e --- /dev/null +++ b/openssl/src/bio.rs @@ -0,0 +1,67 @@ +use std::marker::PhantomData; +use std::ptr; +use std::slice; +use libc::c_int; +use ffi; + +use error::ErrorStack; + +pub struct MemBioSlice<'a>(*mut ffi::BIO, PhantomData<&'a [u8]>); + +impl<'a> Drop for MemBioSlice<'a> { + fn drop(&mut self) { + unsafe { + ffi::BIO_free_all(self.0); + } + } +} + +impl<'a> MemBioSlice<'a> { + pub fn new(buf: &'a [u8]) -> Result, ErrorStack> { + ffi::init(); + + assert!(buf.len() <= c_int::max_value() as usize); + let bio = unsafe { + try_ssl_null!(ffi::BIO_new_mem_buf(buf.as_ptr() as *const _, buf.len() as c_int)) + }; + + Ok(MemBioSlice(bio, PhantomData)) + } + + pub fn get_handle(&self) -> *mut ffi::BIO { + self.0 + } +} + +pub struct MemBio(*mut ffi::BIO); + +impl Drop for MemBio { + fn drop(&mut self) { + unsafe { + ffi::BIO_free_all(self.0); + } + } +} + +impl MemBio { + pub fn new() -> Result { + ffi::init(); + + let bio = unsafe { + try_ssl_null!(ffi::BIO_new(ffi::BIO_s_mem())) + }; + Ok(MemBio(bio)) + } + + pub fn get_handle(&self) -> *mut ffi::BIO { + self.0 + } + + pub fn get_buf(&self) -> &[u8] { + unsafe { + let mut ptr = ptr::null_mut(); + let len = ffi::BIO_get_mem_data(self.0, &mut ptr); + slice::from_raw_parts(ptr as *const _ as *const _, len as usize) + } + } +} diff --git a/openssl/src/bio/mod.rs b/openssl/src/bio/mod.rs deleted file mode 100644 index 4c9b20b0..00000000 --- a/openssl/src/bio/mod.rs +++ /dev/null @@ -1,107 +0,0 @@ -use libc::{c_void, c_int}; -use std::io; -use std::io::prelude::*; -use std::ptr; -use std::cmp; - -use ffi; -use ffi_extras; -use ssl::error::SslError; - -pub struct MemBio { - bio: *mut ffi::BIO, - owned: bool, -} - -impl Drop for MemBio { - fn drop(&mut self) { - if self.owned { - unsafe { - ffi::BIO_free_all(self.bio); - } - } - } -} - -impl MemBio { - /// Creates a new owned memory based BIO - pub fn new() -> Result { - ffi::init(); - - let bio = unsafe { ffi::BIO_new(ffi::BIO_s_mem()) }; - try_ssl_null!(bio); - - Ok(MemBio { - bio: bio, - owned: true, - }) - } - - /// Returns a "borrow", i.e. it has no ownership - pub fn borrowed(bio: *mut ffi::BIO) -> MemBio { - MemBio { - bio: bio, - owned: false, - } - } - - /// Consumes current bio and returns wrapped value - /// Note that data ownership is lost and - /// should be managed manually - pub unsafe fn unwrap(mut self) -> *mut ffi::BIO { - self.owned = false; - self.bio - } - - /// Temporarily gets wrapped value - pub unsafe fn get_handle(&self) -> *mut ffi::BIO { - self.bio - } - - /// Sets the BIO's EOF state. - pub fn set_eof(&self, eof: bool) { - let v = if eof { - 0 - } else { - -1 - }; - unsafe { - ffi_extras::BIO_set_mem_eof_return(self.bio, v); - } - } -} - -impl Read for MemBio { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int; - let ret = unsafe { ffi::BIO_read(self.bio, buf.as_ptr() as *mut c_void, len) }; - - if ret <= 0 { - let is_eof = unsafe { ffi_extras::BIO_eof(self.bio) }; - if is_eof != 0 { - Ok(0) - } else { - Err(io::Error::new(io::ErrorKind::Other, SslError::get())) - } - } else { - Ok(ret as usize) - } - } -} - -impl Write for MemBio { - fn write(&mut self, buf: &[u8]) -> io::Result { - let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int; - let ret = unsafe { ffi::BIO_write(self.bio, buf.as_ptr() as *const c_void, len) }; - - if ret < 0 { - Err(io::Error::new(io::ErrorKind::Other, SslError::get())) - } else { - Ok(ret as usize) - } - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} diff --git a/openssl/src/bn/mod.rs b/openssl/src/bn/mod.rs index ab0e0f2e..d6febe51 100644 --- a/openssl/src/bn/mod.rs +++ b/openssl/src/bn/mod.rs @@ -4,7 +4,7 @@ use std::cmp::Ordering; use std::{fmt, ptr, mem}; use ffi; -use ssl::error::SslError; +use error::ErrorStack; /// A signed arbitrary-precision integer. /// @@ -30,7 +30,7 @@ macro_rules! with_ctx( ($name:ident, $action:block) => ({ let $name = ffi::BN_CTX_new(); if ($name).is_null() { - Err(SslError::get()) + Err(ErrorStack::get()) } else { let r = $action; ffi::BN_CTX_free($name); @@ -47,7 +47,7 @@ macro_rules! with_bn( if $action { Ok($name) } else { - Err(SslError::get()) + Err(ErrorStack::get()) } }, Err(err) => Err(err), @@ -62,13 +62,13 @@ macro_rules! with_bn_in_ctx( Ok($name) => { let $ctx_name = ffi::BN_CTX_new(); if ($ctx_name).is_null() { - Err(SslError::get()) + Err(ErrorStack::get()) } else { let r = if $action { Ok($name) } else { - Err(SslError::get()) + Err(ErrorStack::get()) }; ffi::BN_CTX_free($ctx_name); r @@ -81,7 +81,7 @@ macro_rules! with_bn_in_ctx( impl BigNum { /// Creates a new `BigNum` with the value 0. - pub fn new() -> Result { + pub fn new() -> Result { unsafe { ffi::init(); @@ -91,7 +91,7 @@ impl BigNum { } /// Creates a new `BigNum` with the given value. - pub fn new_from(n: u64) -> Result { + pub fn new_from(n: u64) -> Result { BigNum::new().and_then(|v| unsafe { try_ssl!(ffi::BN_set_word(v.raw(), n as c_ulong)); Ok(v) @@ -99,7 +99,7 @@ impl BigNum { } /// Creates a `BigNum` from a decimal string. - pub fn from_dec_str(s: &str) -> Result { + pub fn from_dec_str(s: &str) -> Result { BigNum::new().and_then(|v| unsafe { let c_str = CString::new(s.as_bytes()).unwrap(); try_ssl!(ffi::BN_dec2bn(v.raw_ptr(), c_str.as_ptr() as *const _)); @@ -108,7 +108,7 @@ impl BigNum { } /// Creates a `BigNum` from a hexadecimal string. - pub fn from_hex_str(s: &str) -> Result { + pub fn from_hex_str(s: &str) -> Result { BigNum::new().and_then(|v| unsafe { let c_str = CString::new(s.as_bytes()).unwrap(); try_ssl!(ffi::BN_hex2bn(v.raw_ptr(), c_str.as_ptr() as *const _)); @@ -116,13 +116,13 @@ impl BigNum { }) } - pub unsafe fn new_from_ffi(orig: *mut ffi::BIGNUM) -> Result { + pub unsafe fn new_from_ffi(orig: *mut ffi::BIGNUM) -> Result { if orig.is_null() { panic!("Null Pointer was supplied to BigNum::new_from_ffi"); } let r = ffi::BN_dup(orig); if r.is_null() { - Err(SslError::get()) + Err(ErrorStack::get()) } else { Ok(BigNum(r)) } @@ -136,7 +136,7 @@ impl BigNum { /// /// assert_eq!(bignum, BigNum::new_from(0x120034).unwrap()); /// ``` - pub fn new_from_slice(n: &[u8]) -> Result { + pub fn new_from_slice(n: &[u8]) -> Result { BigNum::new().and_then(|v| unsafe { try_ssl_null!(ffi::BN_bin2bn(n.as_ptr(), n.len() as c_int, v.raw())); Ok(v) @@ -153,7 +153,7 @@ impl BigNum { /// assert_eq!(n.checked_sqr().unwrap(), squared); /// assert_eq!(n * n, squared); /// ``` - pub fn checked_sqr(&self) -> Result { + pub fn checked_sqr(&self) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_sqr(r.raw(), self.raw(), ctx) == 1 @@ -162,7 +162,7 @@ impl BigNum { } /// Returns the unsigned remainder of the division `self / n`. - pub fn checked_nnmod(&self, n: &BigNum) -> Result { + pub fn checked_nnmod(&self, n: &BigNum) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_nnmod(r.raw(), self.raw(), n.raw(), ctx) == 1 @@ -181,7 +181,7 @@ impl BigNum { /// /// assert_eq!(s.checked_mod_add(a, n).unwrap(), result); /// ``` - pub fn checked_mod_add(&self, a: &BigNum, n: &BigNum) -> Result { + pub fn checked_mod_add(&self, a: &BigNum, n: &BigNum) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_mod_add(r.raw(), self.raw(), a.raw(), n.raw(), ctx) == 1 @@ -190,7 +190,7 @@ impl BigNum { } /// Equivalent to `(self - a) mod n`. - pub fn checked_mod_sub(&self, a: &BigNum, n: &BigNum) -> Result { + pub fn checked_mod_sub(&self, a: &BigNum, n: &BigNum) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_mod_sub(r.raw(), self.raw(), a.raw(), n.raw(), ctx) == 1 @@ -199,7 +199,7 @@ impl BigNum { } /// Equivalent to `(self * a) mod n`. - pub fn checked_mod_mul(&self, a: &BigNum, n: &BigNum) -> Result { + pub fn checked_mod_mul(&self, a: &BigNum, n: &BigNum) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_mod_mul(r.raw(), self.raw(), a.raw(), n.raw(), ctx) == 1 @@ -208,7 +208,7 @@ impl BigNum { } /// Equivalent to `self² mod n`. - pub fn checked_mod_sqr(&self, n: &BigNum) -> Result { + pub fn checked_mod_sqr(&self, n: &BigNum) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_mod_sqr(r.raw(), self.raw(), n.raw(), ctx) == 1 @@ -217,7 +217,7 @@ impl BigNum { } /// Raises `self` to the `p`th power. - pub fn checked_exp(&self, p: &BigNum) -> Result { + pub fn checked_exp(&self, p: &BigNum) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_exp(r.raw(), self.raw(), p.raw(), ctx) == 1 @@ -226,7 +226,7 @@ impl BigNum { } /// Equivalent to `self.checked_exp(p) mod n`. - pub fn checked_mod_exp(&self, p: &BigNum, n: &BigNum) -> Result { + pub fn checked_mod_exp(&self, p: &BigNum, n: &BigNum) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_mod_exp(r.raw(), self.raw(), p.raw(), n.raw(), ctx) == 1 @@ -236,7 +236,7 @@ impl BigNum { /// Calculates the modular multiplicative inverse of `self` modulo `n`, that is, an integer `r` /// such that `(self * r) % n == 1`. - pub fn checked_mod_inv(&self, n: &BigNum) -> Result { + pub fn checked_mod_inv(&self, n: &BigNum) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { !ffi::BN_mod_inverse(r.raw(), self.raw(), n.raw(), ctx).is_null() @@ -245,60 +245,60 @@ impl BigNum { } /// Add an `unsigned long` to `self`. This is more efficient than adding a `BigNum`. - pub fn add_word(&mut self, w: c_ulong) -> Result<(), SslError> { + pub fn add_word(&mut self, w: c_ulong) -> Result<(), ErrorStack> { unsafe { if ffi::BN_add_word(self.raw(), w) == 1 { Ok(()) } else { - Err(SslError::get()) + Err(ErrorStack::get()) } } } - pub fn sub_word(&mut self, w: c_ulong) -> Result<(), SslError> { + pub fn sub_word(&mut self, w: c_ulong) -> Result<(), ErrorStack> { unsafe { if ffi::BN_sub_word(self.raw(), w) == 1 { Ok(()) } else { - Err(SslError::get()) + Err(ErrorStack::get()) } } } - pub fn mul_word(&mut self, w: c_ulong) -> Result<(), SslError> { + pub fn mul_word(&mut self, w: c_ulong) -> Result<(), ErrorStack> { unsafe { if ffi::BN_mul_word(self.raw(), w) == 1 { Ok(()) } else { - Err(SslError::get()) + Err(ErrorStack::get()) } } } - pub fn div_word(&mut self, w: c_ulong) -> Result { + pub fn div_word(&mut self, w: c_ulong) -> Result { unsafe { let result = ffi::BN_div_word(self.raw(), w); if result != !0 as c_ulong { Ok(result) } else { - Err(SslError::get()) + Err(ErrorStack::get()) } } } - pub fn mod_word(&self, w: c_ulong) -> Result { + pub fn mod_word(&self, w: c_ulong) -> Result { unsafe { let result = ffi::BN_mod_word(self.raw(), w); if result != !0 as c_ulong { Ok(result) } else { - Err(SslError::get()) + Err(ErrorStack::get()) } } } /// Computes the greatest common denominator of `self` and `a`. - pub fn checked_gcd(&self, a: &BigNum) -> Result { + pub fn checked_gcd(&self, a: &BigNum) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_gcd(r.raw(), self.raw(), a.raw(), ctx) == 1 @@ -318,7 +318,7 @@ impl BigNum { safe: bool, add: Option<&BigNum>, rem: Option<&BigNum>) - -> Result { + -> Result { unsafe { with_bn_in_ctx!(r, ctx, { let add_arg = add.map(|a| a.raw()).unwrap_or(ptr::null_mut()); @@ -341,7 +341,7 @@ impl BigNum { /// # Return Value /// /// Returns `true` if `self` is prime with an error probability of less than `0.25 ^ checks`. - pub fn is_prime(&self, checks: i32) -> Result { + pub fn is_prime(&self, checks: i32) -> Result { unsafe { with_ctx!(ctx, { Ok(ffi::BN_is_prime_ex(self.raw(), checks as c_int, ctx, ptr::null()) == 1) @@ -358,7 +358,7 @@ impl BigNum { /// # Return Value /// /// Returns `true` if `self` is prime with an error probability of less than `0.25 ^ checks`. - pub fn is_prime_fast(&self, checks: i32, do_trial_division: bool) -> Result { + pub fn is_prime_fast(&self, checks: i32, do_trial_division: bool) -> Result { unsafe { with_ctx!(ctx, { Ok(ffi::BN_is_prime_fasttest_ex(self.raw(), @@ -377,7 +377,7 @@ impl BigNum { /// * `bits`: Length of the number in bits. /// * `prop`: The desired properties of the number. /// * `odd`: If `true`, the generated number will be odd. - pub fn checked_new_random(bits: i32, prop: RNGProperty, odd: bool) -> Result { + pub fn checked_new_random(bits: i32, prop: RNGProperty, odd: bool) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_rand(r.raw(), bits as c_int, prop as c_int, odd as c_int) == 1 @@ -389,7 +389,7 @@ impl BigNum { pub fn checked_new_pseudo_random(bits: i32, prop: RNGProperty, odd: bool) - -> Result { + -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_pseudo_rand(r.raw(), bits as c_int, prop as c_int, odd as c_int) == 1 @@ -399,7 +399,7 @@ impl BigNum { /// Generates a cryptographically strong pseudo-random `BigNum` `r` in the range /// `0 <= r < self`. - pub fn checked_rand_in_range(&self) -> Result { + pub fn checked_rand_in_range(&self) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_rand_range(r.raw(), self.raw()) == 1 @@ -408,7 +408,7 @@ impl BigNum { } /// The cryptographically weak counterpart to `checked_rand_in_range`. - pub fn checked_pseudo_rand_in_range(&self) -> Result { + pub fn checked_pseudo_rand_in_range(&self) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_pseudo_rand_range(r.raw(), self.raw()) == 1 @@ -419,12 +419,12 @@ impl BigNum { /// Sets bit `n`. Equivalent to `self |= (1 << n)`. /// /// When setting a bit outside of `self`, it is expanded. - pub fn set_bit(&mut self, n: i32) -> Result<(), SslError> { + pub fn set_bit(&mut self, n: i32) -> Result<(), ErrorStack> { unsafe { if ffi::BN_set_bit(self.raw(), n as c_int) == 1 { Ok(()) } else { - Err(SslError::get()) + Err(ErrorStack::get()) } } } @@ -432,12 +432,12 @@ impl BigNum { /// Clears bit `n`, setting it to 0. Equivalent to `self &= ~(1 << n)`. /// /// When clearing a bit outside of `self`, an error is returned. - pub fn clear_bit(&mut self, n: i32) -> Result<(), SslError> { + pub fn clear_bit(&mut self, n: i32) -> Result<(), ErrorStack> { unsafe { if ffi::BN_clear_bit(self.raw(), n as c_int) == 1 { Ok(()) } else { - Err(SslError::get()) + Err(ErrorStack::get()) } } } @@ -450,12 +450,12 @@ impl BigNum { /// Truncates `self` to the lowest `n` bits. /// /// An error occurs if `self` is already shorter than `n` bits. - pub fn mask_bits(&mut self, n: i32) -> Result<(), SslError> { + pub fn mask_bits(&mut self, n: i32) -> Result<(), ErrorStack> { unsafe { if ffi::BN_mask_bits(self.raw(), n as c_int) == 1 { Ok(()) } else { - Err(SslError::get()) + Err(ErrorStack::get()) } } } @@ -478,7 +478,7 @@ impl BigNum { /// // (-8) << 1 == -16 /// assert_eq!(s.checked_shl1().unwrap(), result); /// ``` - pub fn checked_shl1(&self) -> Result { + pub fn checked_shl1(&self) -> Result { unsafe { with_bn!(r, { ffi::BN_lshift1(r.raw(), self.raw()) == 1 @@ -487,7 +487,7 @@ impl BigNum { } /// Returns `self`, shifted right by 1 bit. `self` may be negative. - pub fn checked_shr1(&self) -> Result { + pub fn checked_shr1(&self) -> Result { unsafe { with_bn!(r, { ffi::BN_rshift1(r.raw(), self.raw()) == 1 @@ -495,7 +495,7 @@ impl BigNum { } } - pub fn checked_add(&self, a: &BigNum) -> Result { + pub fn checked_add(&self, a: &BigNum) -> Result { unsafe { with_bn!(r, { ffi::BN_add(r.raw(), self.raw(), a.raw()) == 1 @@ -503,7 +503,7 @@ impl BigNum { } } - pub fn checked_sub(&self, a: &BigNum) -> Result { + pub fn checked_sub(&self, a: &BigNum) -> Result { unsafe { with_bn!(r, { ffi::BN_sub(r.raw(), self.raw(), a.raw()) == 1 @@ -511,7 +511,7 @@ impl BigNum { } } - pub fn checked_mul(&self, a: &BigNum) -> Result { + pub fn checked_mul(&self, a: &BigNum) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_mul(r.raw(), self.raw(), a.raw(), ctx) == 1 @@ -519,7 +519,7 @@ impl BigNum { } } - pub fn checked_div(&self, a: &BigNum) -> Result { + pub fn checked_div(&self, a: &BigNum) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_div(r.raw(), ptr::null_mut(), self.raw(), a.raw(), ctx) == 1 @@ -527,7 +527,7 @@ impl BigNum { } } - pub fn checked_mod(&self, a: &BigNum) -> Result { + pub fn checked_mod(&self, a: &BigNum) -> Result { unsafe { with_bn_in_ctx!(r, ctx, { ffi::BN_div(ptr::null_mut(), r.raw(), self.raw(), a.raw(), ctx) == 1 @@ -535,7 +535,7 @@ impl BigNum { } } - pub fn checked_shl(&self, a: &i32) -> Result { + pub fn checked_shl(&self, a: &i32) -> Result { unsafe { with_bn!(r, { ffi::BN_lshift(r.raw(), self.raw(), *a as c_int) == 1 @@ -543,7 +543,7 @@ impl BigNum { } } - pub fn checked_shr(&self, a: &i32) -> Result { + pub fn checked_shr(&self, a: &i32) -> Result { unsafe { with_bn!(r, { ffi::BN_rshift(r.raw(), self.raw(), *a as c_int) == 1 diff --git a/openssl/src/crypto/dsa.rs b/openssl/src/crypto/dsa.rs index 7e384ae3..de35893b 100644 --- a/openssl/src/crypto/dsa.rs +++ b/openssl/src/crypto/dsa.rs @@ -1,25 +1,21 @@ use ffi; use std::fmt; -use ssl::error::{SslError, StreamError}; +use error::ErrorStack; use std::ptr; -use std::io::{self, Read, Write}; -use libc::{c_uint, c_int}; +use libc::{c_uint, c_int, c_char, c_void}; use bn::BigNum; -use bio::MemBio; +use bio::{MemBio, MemBioSlice}; use crypto::hash; use crypto::HashTypeInternals; - -#[cfg(feature = "catch_unwind")] -use libc::{c_char, c_void}; -#[cfg(feature = "catch_unwind")] use crypto::util::{CallbackState, invoke_passwd_cb}; + /// Builder for upfront DSA parameter generateration pub struct DSAParams(*mut ffi::DSA); impl DSAParams { - pub fn with_size(size: usize) -> Result { + pub fn with_size(size: usize) -> Result { unsafe { // Wrap it so that if we panic we'll call the dtor let dsa = DSAParams(try_ssl_null!(ffi::DSA_new())); @@ -30,7 +26,7 @@ impl DSAParams { } /// Generate a key pair from the initialized parameters - pub fn generate(self) -> Result { + pub fn generate(self) -> Result { unsafe { try_ssl!(ffi::DSA_generate_key(self.0)); let dsa = DSA(self.0); @@ -66,17 +62,15 @@ impl DSA { /// Generate a DSA key pair /// For more complicated key generation scenarios see the `DSAParams` type - pub fn generate(size: usize) -> Result { + pub fn generate(size: usize) -> Result { let params = try!(DSAParams::with_size(size)); params.generate() } /// Reads a DSA private key from PEM formatted data. - pub fn private_key_from_pem(reader: &mut R) -> Result - where R: Read - { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); + pub fn private_key_from_pem(buf: &[u8]) -> Result { + ffi::init(); + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { let dsa = try_ssl_null!(ffi::PEM_read_bio_DSAPrivateKey(mem_bio.get_handle(), @@ -94,15 +88,12 @@ impl DSA { /// /// The callback will be passed the password buffer and should return the number of characters /// placed into the buffer. - /// - /// Requires the `catch_unwind` feature. - #[cfg(feature = "catch_unwind")] - pub fn private_key_from_pem_cb(reader: &mut R, pass_cb: F) -> Result - where R: Read, F: FnOnce(&mut [c_char]) -> usize + pub fn private_key_from_pem_cb(buf: &[u8], pass_cb: F) -> Result + where F: FnOnce(&mut [c_char]) -> usize { + ffi::init(); let mut cb = CallbackState::new(pass_cb); - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { let cb_ptr = &mut cb as *mut _ as *mut c_void; @@ -117,11 +108,10 @@ impl DSA { } /// Writes an DSA private key as unencrypted PEM formatted data - pub fn private_key_to_pem(&self, writer: &mut W) -> Result<(), SslError> - where W: Write + pub fn private_key_to_pem(&self) -> Result, ErrorStack> { assert!(self.has_private_key()); - let mut mem_bio = try!(MemBio::new()); + let mem_bio = try!(MemBio::new()); unsafe { try_ssl!(ffi::PEM_write_bio_DSAPrivateKey(mem_bio.get_handle(), self.0, @@ -129,18 +119,15 @@ impl DSA { None, ptr::null_mut())) }; - - try!(io::copy(&mut mem_bio, writer).map_err(StreamError)); - Ok(()) + Ok(mem_bio.get_buf().to_owned()) } /// Reads an DSA public key from PEM formatted data. - pub fn public_key_from_pem(reader: &mut R) -> Result - where R: Read + pub fn public_key_from_pem(buf: &[u8]) -> Result { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); + ffi::init(); + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { let dsa = try_ssl_null!(ffi::PEM_read_bio_DSA_PUBKEY(mem_bio.get_handle(), ptr::null_mut(), @@ -151,28 +138,23 @@ impl DSA { } /// Writes an DSA public key as PEM formatted data - pub fn public_key_to_pem(&self, writer: &mut W) -> Result<(), SslError> - where W: Write - { - let mut mem_bio = try!(MemBio::new()); - + pub fn public_key_to_pem(&self) -> Result, ErrorStack> { + let mem_bio = try!(MemBio::new()); unsafe { try_ssl!(ffi::PEM_write_bio_DSA_PUBKEY(mem_bio.get_handle(), self.0)) }; - - try!(io::copy(&mut mem_bio, writer).map_err(StreamError)); - Ok(()) + Ok(mem_bio.get_buf().to_owned()) } - pub fn size(&self) -> Result { + pub fn size(&self) -> Option { if self.has_q() { - unsafe { Ok(ffi::DSA_size(self.0) as isize) } + unsafe { Some(ffi::DSA_size(self.0) as u32) } } else { - Err(SslError::OpenSslErrors(vec![])) + None } } - pub fn sign(&self, hash: hash::Type, message: &[u8]) -> Result, SslError> { - let k_len = try!(self.size()) as c_uint; - let mut sig = vec![0;k_len as usize]; + pub fn sign(&self, hash: hash::Type, message: &[u8]) -> Result, ErrorStack> { + let k_len = self.size().expect("DSA missing a q") as c_uint; + let mut sig = vec![0; k_len as usize]; let mut sig_len = k_len; assert!(self.has_private_key()); @@ -189,7 +171,7 @@ impl DSA { } } - pub fn verify(&self, hash: hash::Type, message: &[u8], sig: &[u8]) -> Result { + pub fn verify(&self, hash: hash::Type, message: &[u8], sig: &[u8]) -> Result { unsafe { let result = ffi::DSA_verify(hash.as_nid() as c_int, message.as_ptr(), @@ -208,7 +190,7 @@ impl DSA { } // The following getters are unsafe, since BigNum::new_from_ffi fails upon null pointers - pub fn p(&self) -> Result { + pub fn p(&self) -> Result { unsafe { BigNum::new_from_ffi((*self.0).p) } } @@ -216,7 +198,7 @@ impl DSA { unsafe { !(*self.0).p.is_null() } } - pub fn q(&self) -> Result { + pub fn q(&self) -> Result { unsafe { BigNum::new_from_ffi((*self.0).q) } } @@ -224,7 +206,7 @@ impl DSA { unsafe { !(*self.0).q.is_null() } } - pub fn g(&self) -> Result { + pub fn g(&self) -> Result { unsafe { BigNum::new_from_ffi((*self.0).g) } } @@ -249,19 +231,18 @@ impl fmt::Debug for DSA { #[cfg(test)] mod test { - use std::fs::File; - use std::io::{Write, Cursor}; + use std::io::Write; + use libc::c_char; + use super::*; use crypto::hash::*; #[test] pub fn test_generate() { let key = DSA::generate(1024).unwrap(); - let mut priv_buf = Cursor::new(vec![]); - let mut pub_buf = Cursor::new(vec![]); - key.public_key_to_pem(&mut pub_buf).unwrap(); - key.private_key_to_pem(&mut priv_buf).unwrap(); + key.public_key_to_pem().unwrap(); + key.private_key_to_pem().unwrap(); let input: Vec = (0..25).cycle().take(1024).collect(); @@ -281,13 +262,13 @@ mod test { let input: Vec = (0..25).cycle().take(1024).collect(); let private_key = { - let mut buffer = File::open("test/dsa.pem").unwrap(); - DSA::private_key_from_pem(&mut buffer).unwrap() + let key = include_bytes!("../../test/dsa.pem"); + DSA::private_key_from_pem(key).unwrap() }; let public_key = { - let mut buffer = File::open("test/dsa.pem.pub").unwrap(); - DSA::public_key_from_pem(&mut buffer).unwrap() + let key = include_bytes!("../../test/dsa.pem.pub"); + DSA::public_key_from_pem(key).unwrap() }; let digest = { @@ -305,13 +286,13 @@ mod test { pub fn test_sign_verify_fail() { let input: Vec = (0..25).cycle().take(128).collect(); let private_key = { - let mut buffer = File::open("test/dsa.pem").unwrap(); - DSA::private_key_from_pem(&mut buffer).unwrap() + let key = include_bytes!("../../test/dsa.pem"); + DSA::private_key_from_pem(key).unwrap() }; let public_key = { - let mut buffer = File::open("test/dsa.pem.pub").unwrap(); - DSA::public_key_from_pem(&mut buffer).unwrap() + let key = include_bytes!("../../test/dsa.pem.pub"); + DSA::public_key_from_pem(key).unwrap() }; let digest = { @@ -331,18 +312,17 @@ mod test { } #[test] - #[cfg(feature = "catch_unwind")] pub fn test_password() { let mut password_queried = false; - let mut buffer = File::open("test/dsa-encrypted.pem").unwrap(); - DSA::private_key_from_pem_cb(&mut buffer, |password| { + let key = include_bytes!("../../test/dsa-encrypted.pem"); + DSA::private_key_from_pem_cb(key, |password| { password_queried = true; - password[0] = b'm' as _; - password[1] = b'y' as _; - password[2] = b'p' as _; - password[3] = b'a' as _; - password[4] = b's' as _; - password[5] = b's' as _; + password[0] = b'm' as c_char; + password[1] = b'y' as c_char; + password[2] = b'p' as c_char; + password[3] = b'a' as c_char; + password[4] = b's' as c_char; + password[5] = b's' as c_char; 6 }).unwrap(); diff --git a/openssl/src/crypto/mod.rs b/openssl/src/crypto/mod.rs index 6a34bd59..5b891ce7 100644 --- a/openssl/src/crypto/mod.rs +++ b/openssl/src/crypto/mod.rs @@ -25,7 +25,6 @@ pub mod symm; pub mod memcmp; pub mod rsa; pub mod dsa; -#[cfg(feature = "catch_unwind")] mod util; mod symm_internal; diff --git a/openssl/src/crypto/pkey.rs b/openssl/src/crypto/pkey.rs index 15744047..ab9a4a95 100644 --- a/openssl/src/crypto/pkey.rs +++ b/openssl/src/crypto/pkey.rs @@ -1,21 +1,15 @@ -use libc::{c_int, c_uint, c_ulong}; -use std::io; -use std::io::prelude::*; +use libc::{c_int, c_uint, c_ulong, c_void, c_char}; use std::iter::repeat; use std::mem; use std::ptr; -use bio::MemBio; +use bio::{MemBio, MemBioSlice}; use crypto::HashTypeInternals; use crypto::hash; use crypto::hash::Type as HashType; use ffi; -use ssl::error::{SslError, StreamError}; use crypto::rsa::RSA; - -#[cfg(feature = "catch_unwind")] -use libc::{c_void, c_char}; -#[cfg(feature = "catch_unwind")] +use error::ErrorStack; use crypto::util::{CallbackState, invoke_passwd_cb}; #[derive(Copy, Clone)] @@ -80,17 +74,14 @@ impl PKey { } /// Reads private key from PEM, takes ownership of handle - pub fn private_key_from_pem(reader: &mut R) -> Result - where R: Read - { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); - + pub fn private_key_from_pem(buf: &[u8]) -> Result { + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { let evp = try_ssl_null!(ffi::PEM_read_bio_PrivateKey(mem_bio.get_handle(), ptr::null_mut(), None, ptr::null_mut())); + Ok(PKey { evp: evp as *mut ffi::EVP_PKEY, parts: Parts::Both, @@ -103,17 +94,11 @@ impl PKey { /// /// The callback will be passed the password buffer and should return the number of characters /// placed into the buffer. - /// - /// Requires the `catch_unwind` feature. - #[cfg(feature = "catch_unwind")] - pub fn private_key_from_pem_cb(reader: &mut R, pass_cb: F) -> Result - where R: Read, F: FnOnce(&mut [c_char]) -> usize + pub fn private_key_from_pem_cb(buf: &[u8], pass_cb: F) -> Result + where F: FnOnce(&mut [c_char]) -> usize { let mut cb = CallbackState::new(pass_cb); - - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); - + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { let evp = try_ssl_null!(ffi::PEM_read_bio_PrivateKey(mem_bio.get_handle(), ptr::null_mut(), @@ -128,12 +113,8 @@ impl PKey { } /// Reads public key from PEM, takes ownership of handle - pub fn public_key_from_pem(reader: &mut R) -> Result - where R: Read - { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); - + pub fn public_key_from_pem(buf: &[u8]) -> Result { + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { let evp = try_ssl_null!(ffi::PEM_read_bio_PUBKEY(mem_bio.get_handle(), ptr::null_mut(), @@ -147,13 +128,13 @@ impl PKey { } /// Reads an RSA private key from PEM, takes ownership of handle - pub fn private_rsa_key_from_pem(reader: &mut R) -> Result - where R: Read - { - let rsa = try!(RSA::private_key_from_pem(reader)); + pub fn private_rsa_key_from_pem(buf: &[u8]) -> Result { + let rsa = try!(RSA::private_key_from_pem(buf)); unsafe { let evp = try_ssl_null!(ffi::EVP_PKEY_new()); - try_ssl!(ffi::EVP_PKEY_set1_RSA(evp, rsa.as_ptr())); + if ffi::EVP_PKEY_set1_RSA(evp, rsa.as_ptr()) == 0 { + return Err(ErrorStack::get()); + } Ok(PKey { evp: evp, @@ -163,13 +144,13 @@ impl PKey { } /// Reads an RSA public key from PEM, takes ownership of handle - pub fn public_rsa_key_from_pem(reader: &mut R) -> Result - where R: Read - { - let rsa = try!(RSA::public_key_from_pem(reader)); + pub fn public_rsa_key_from_pem(buf: &[u8]) -> Result { + let rsa = try!(RSA::public_key_from_pem(buf)); unsafe { let evp = try_ssl_null!(ffi::EVP_PKEY_new()); - try_ssl!(ffi::EVP_PKEY_set1_RSA(evp, rsa.as_ptr())); + if ffi::EVP_PKEY_set1_RSA(evp, rsa.as_ptr()) == 0 { + return Err(ErrorStack::get()); + } Ok(PKey { evp: evp, @@ -282,10 +263,8 @@ impl PKey { /// Stores private key as a PEM // FIXME: also add password and encryption - pub fn write_pem(&self, - writer: &mut W /* , password: Option */) - -> Result<(), SslError> { - let mut mem_bio = try!(MemBio::new()); + pub fn write_pem(&self) -> Result, ErrorStack> { + let mem_bio = try!(MemBio::new()); unsafe { try_ssl!(ffi::PEM_write_bio_PrivateKey(mem_bio.get_handle(), self.evp, @@ -296,20 +275,14 @@ impl PKey { ptr::null_mut())); } - let mut buf = vec![]; - try!(mem_bio.read_to_end(&mut buf).map_err(StreamError)); - writer.write_all(&buf).map_err(StreamError) + Ok(mem_bio.get_buf().to_owned()) } /// Stores public key as a PEM - pub fn write_pub_pem(&self, - writer: &mut W /* , password: Option */) - -> Result<(), SslError> { - let mut mem_bio = try!(MemBio::new()); + pub fn write_pub_pem(&self) -> Result, ErrorStack> { + let mem_bio = try!(MemBio::new()); unsafe { try_ssl!(ffi::PEM_write_bio_PUBKEY(mem_bio.get_handle(), self.evp)) } - let mut buf = vec![]; - try!(mem_bio.read_to_end(&mut buf).map_err(StreamError)); - writer.write_all(&buf).map_err(StreamError) + Ok(mem_bio.get_buf().to_owned()) } /** @@ -394,7 +367,7 @@ impl PKey { openssl_padding_code(padding)); if rv < 0 as c_int { - // println!("{:?}", SslError::get()); + // println!("{:?}", ErrorStack::get()); vec![] } else { r.truncate(rv as usize); @@ -650,8 +623,6 @@ impl Clone for PKey { #[cfg(test)] mod tests { - use std::path::Path; - use std::fs::File; use crypto::hash::Type::{MD5, SHA1}; use crypto::rsa::RSA; @@ -695,42 +666,26 @@ mod tests { #[test] fn test_private_key_from_pem() { - let key_path = Path::new("test/key.pem"); - let mut file = File::open(&key_path) - .ok() - .expect("Failed to open `test/key.pem`"); - - super::PKey::private_key_from_pem(&mut file).unwrap(); + let key = include_bytes!("../../test/key.pem"); + super::PKey::private_key_from_pem(key).unwrap(); } #[test] fn test_public_key_from_pem() { - let key_path = Path::new("test/key.pem.pub"); - let mut file = File::open(&key_path) - .ok() - .expect("Failed to open `test/key.pem.pub`"); - - super::PKey::public_key_from_pem(&mut file).unwrap(); + let key = include_bytes!("../../test/key.pem.pub"); + super::PKey::public_key_from_pem(key).unwrap(); } #[test] fn test_private_rsa_key_from_pem() { - let key_path = Path::new("test/key.pem"); - let mut file = File::open(&key_path) - .ok() - .expect("Failed to open `test/key.pem`"); - - super::PKey::private_rsa_key_from_pem(&mut file).unwrap(); + let key = include_bytes!("../../test/key.pem"); + super::PKey::private_rsa_key_from_pem(key).unwrap(); } #[test] fn test_public_rsa_key_from_pem() { - let key_path = Path::new("test/key.pem.pub"); - let mut file = File::open(&key_path) - .ok() - .expect("Failed to open `test/key.pem.pub`"); - - super::PKey::public_rsa_key_from_pem(&mut file).unwrap(); + let key = include_bytes!("../../test/key.pem.pub"); + super::PKey::public_rsa_key_from_pem(key).unwrap(); } #[test] @@ -821,18 +776,11 @@ mod tests { #[test] fn test_pem() { - let key_path = Path::new("test/key.pem"); - let mut file = File::open(&key_path) - .ok() - .expect("Failed to open `test/key.pem`"); + let key = include_bytes!("../../test/key.pem"); + let key = super::PKey::private_key_from_pem(key).unwrap(); - let key = super::PKey::private_key_from_pem(&mut file).unwrap(); - - let mut priv_key = Vec::new(); - let mut pub_key = Vec::new(); - - key.write_pem(&mut priv_key).unwrap(); - key.write_pub_pem(&mut pub_key).unwrap(); + let priv_key = key.write_pem().unwrap(); + let pub_key = key.write_pub_pem().unwrap(); // As a super-simple verification, just check that the buffers contain // the `PRIVATE KEY` or `PUBLIC KEY` strings. diff --git a/openssl/src/crypto/rsa.rs b/openssl/src/crypto/rsa.rs index 2b563a7a..226b2aab 100644 --- a/openssl/src/crypto/rsa.rs +++ b/openssl/src/crypto/rsa.rs @@ -1,18 +1,13 @@ use ffi; use std::fmt; -use ssl::error::{SslError, StreamError}; use std::ptr; -use std::io::{self, Read, Write}; -use libc::c_int; +use libc::{c_int, c_void, c_char}; use bn::BigNum; -use bio::MemBio; +use bio::{MemBio, MemBioSlice}; +use error::ErrorStack; use crypto::HashTypeInternals; use crypto::hash; - -#[cfg(feature = "catch_unwind")] -use libc::{c_void, c_char}; -#[cfg(feature = "catch_unwind")] use crypto::util::{CallbackState, invoke_passwd_cb}; pub struct RSA(*mut ffi::RSA); @@ -28,7 +23,7 @@ impl Drop for RSA { impl RSA { /// only useful for associating the key material directly with the key, it's safer to use /// the supplied load and save methods for DER formatted keys. - pub fn from_public_components(n: BigNum, e: BigNum) -> Result { + pub fn from_public_components(n: BigNum, e: BigNum) -> Result { unsafe { let rsa = try_ssl_null!(ffi::RSA_new()); (*rsa).n = n.into_raw(); @@ -45,7 +40,7 @@ impl RSA { dp: BigNum, dq: BigNum, qi: BigNum) - -> Result { + -> Result { unsafe { let rsa = try_ssl_null!(ffi::RSA_new()); (*rsa).n = n.into_raw(); @@ -66,12 +61,8 @@ impl RSA { } /// Reads an RSA private key from PEM formatted data. - pub fn private_key_from_pem(reader: &mut R) -> Result - where R: Read - { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); - + pub fn private_key_from_pem(buf: &[u8]) -> Result { + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { let rsa = try_ssl_null!(ffi::PEM_read_bio_RSAPrivateKey(mem_bio.get_handle(), ptr::null_mut(), @@ -82,16 +73,11 @@ impl RSA { } /// Reads an RSA private key from PEM formatted data and supplies a password callback. - /// - /// Requires the `catch_unwind` feature. - #[cfg(feature = "catch_unwind")] - pub fn private_key_from_pem_cb(reader: &mut R, pass_cb: F) -> Result - where R: Read, F: FnOnce(&mut [c_char]) -> usize + pub fn private_key_from_pem_cb(buf: &[u8], pass_cb: F) -> Result + where F: FnOnce(&mut [c_char]) -> usize { let mut cb = CallbackState::new(pass_cb); - - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { let cb_ptr = &mut cb as *mut _ as *mut c_void; @@ -105,10 +91,8 @@ impl RSA { } /// Writes an RSA private key as unencrypted PEM formatted data - pub fn private_key_to_pem(&self, writer: &mut W) -> Result<(), SslError> - where W: Write - { - let mut mem_bio = try!(MemBio::new()); + pub fn private_key_to_pem(&self) -> Result, ErrorStack> { + let mem_bio = try!(MemBio::new()); unsafe { try_ssl!(ffi::PEM_write_bio_RSAPrivateKey(mem_bio.get_handle(), @@ -119,17 +103,12 @@ impl RSA { None, ptr::null_mut())); } - try!(io::copy(&mut mem_bio, writer).map_err(StreamError)); - Ok(()) + Ok(mem_bio.get_buf().to_owned()) } /// Reads an RSA public key from PEM formatted data. - pub fn public_key_from_pem(reader: &mut R) -> Result - where R: Read - { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); - + pub fn public_key_from_pem(buf: &[u8]) -> Result { + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { let rsa = try_ssl_null!(ffi::PEM_read_bio_RSA_PUBKEY(mem_bio.get_handle(), ptr::null_mut(), @@ -140,30 +119,27 @@ impl RSA { } /// Writes an RSA public key as PEM formatted data - pub fn public_key_to_pem(&self, writer: &mut W) -> Result<(), SslError> - where W: Write - { - let mut mem_bio = try!(MemBio::new()); + pub fn public_key_to_pem(&self) -> Result, ErrorStack> { + let mem_bio = try!(MemBio::new()); unsafe { try_ssl!(ffi::PEM_write_bio_RSA_PUBKEY(mem_bio.get_handle(), self.0)) }; - try!(io::copy(&mut mem_bio, writer).map_err(StreamError)); - Ok(()) + Ok(mem_bio.get_buf().to_owned()) } - pub fn size(&self) -> Result { + pub fn size(&self) -> Option { if self.has_n() { - unsafe { Ok(ffi::RSA_size(self.0) as u32) } + unsafe { Some(ffi::RSA_size(self.0) as u32) } } else { - Err(SslError::OpenSslErrors(vec![])) + None } } - pub fn sign(&self, hash: hash::Type, message: &[u8]) -> Result, SslError> { - let k_len = try!(self.size()); - let mut sig = vec![0;k_len as usize]; + pub fn sign(&self, hash: hash::Type, message: &[u8]) -> Result, ErrorStack> { + let k_len = self.size().expect("RSA missing an n"); + let mut sig = vec![0; k_len as usize]; let mut sig_len = k_len; unsafe { @@ -178,7 +154,7 @@ impl RSA { } } - pub fn verify(&self, hash: hash::Type, message: &[u8], sig: &[u8]) -> Result { + pub fn verify(&self, hash: hash::Type, message: &[u8], sig: &[u8]) -> Result { unsafe { let result = ffi::RSA_verify(hash.as_nid() as c_int, message.as_ptr(), @@ -196,32 +172,42 @@ impl RSA { } // The following getters are unsafe, since BigNum::new_from_ffi fails upon null pointers - pub fn n(&self) -> Result { - unsafe { BigNum::new_from_ffi((*self.0).n) } + pub fn n(&self) -> Result { + unsafe { + BigNum::new_from_ffi((*self.0).n) + } } pub fn has_n(&self) -> bool { unsafe { !(*self.0).n.is_null() } } - pub fn d(&self) -> Result { - unsafe { BigNum::new_from_ffi((*self.0).d) } + pub fn d(&self) -> Result { + unsafe { + BigNum::new_from_ffi((*self.0).d) + } } - pub fn e(&self) -> Result { - unsafe { BigNum::new_from_ffi((*self.0).e) } + pub fn e(&self) -> Result { + unsafe { + BigNum::new_from_ffi((*self.0).e) + } } pub fn has_e(&self) -> bool { unsafe { !(*self.0).e.is_null() } } - pub fn p(&self) -> Result { - unsafe { BigNum::new_from_ffi((*self.0).p) } + pub fn p(&self) -> Result { + unsafe { + BigNum::new_from_ffi((*self.0).p) + } } - pub fn q(&self) -> Result { - unsafe { BigNum::new_from_ffi((*self.0).q) } + pub fn q(&self) -> Result { + unsafe { + BigNum::new_from_ffi((*self.0).q) + } } } @@ -233,8 +219,9 @@ impl fmt::Debug for RSA { #[cfg(test)] mod test { - use std::fs::File; use std::io::Write; + use libc::c_char; + use super::*; use crypto::hash::*; @@ -266,8 +253,8 @@ mod test { #[test] pub fn test_sign() { - let mut buffer = File::open("test/rsa.pem").unwrap(); - let private_key = RSA::private_key_from_pem(&mut buffer).unwrap(); + let key = include_bytes!("../../test/rsa.pem"); + let private_key = RSA::private_key_from_pem(key).unwrap(); let mut sha = Hasher::new(Type::SHA256); sha.write_all(&signing_input_rs256()).unwrap(); @@ -280,8 +267,8 @@ mod test { #[test] pub fn test_verify() { - let mut buffer = File::open("test/rsa.pem.pub").unwrap(); - let public_key = RSA::public_key_from_pem(&mut buffer).unwrap(); + let key = include_bytes!("../../test/rsa.pem.pub"); + let public_key = RSA::public_key_from_pem(key).unwrap(); let mut sha = Hasher::new(Type::SHA256); sha.write_all(&signing_input_rs256()).unwrap(); @@ -293,18 +280,17 @@ mod test { } #[test] - #[cfg(feature = "catch_unwind")] pub fn test_password() { let mut password_queried = false; - let mut buffer = File::open("test/rsa-encrypted.pem").unwrap(); - RSA::private_key_from_pem_cb(&mut buffer, |password| { + let key = include_bytes!("../../test/rsa-encrypted.pem"); + RSA::private_key_from_pem_cb(key, |password| { password_queried = true; - password[0] = b'm' as _; - password[1] = b'y' as _; - password[2] = b'p' as _; - password[3] = b'a' as _; - password[4] = b's' as _; - password[5] = b's' as _; + password[0] = b'm' as c_char; + password[1] = b'y' as c_char; + password[2] = b'p' as c_char; + password[3] = b'a' as c_char; + password[4] = b's' as c_char; + password[5] = b's' as c_char; 6 }).unwrap(); diff --git a/openssl/src/dh/mod.rs b/openssl/src/dh/mod.rs index d2f26c3f..df6f1b0a 100644 --- a/openssl/src/dh/mod.rs +++ b/openssl/src/dh/mod.rs @@ -1,8 +1,6 @@ use ffi; -use std::io; -use std::io::prelude::*; -use ssl::error::{SslError, StreamError}; -use bio::MemBio; +use error::ErrorStack; +use bio::MemBioSlice; use bn::BigNum; use std::mem; use std::ptr; @@ -10,7 +8,7 @@ use std::ptr; pub struct DH(*mut ffi::DH); impl DH { - pub fn from_params(p: BigNum, g: BigNum, q: BigNum) -> Result { + pub fn from_params(p: BigNum, g: BigNum, q: BigNum) -> Result { let dh = try_ssl_null!(unsafe { ffi::DH_new_from_params(p.raw(), g.raw(), q.raw()) }); mem::forget(p); mem::forget(g); @@ -18,11 +16,8 @@ impl DH { Ok(DH(dh)) } - pub fn from_pem(reader: &mut R) -> Result - where R: Read - { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); + pub fn from_pem(buf: &[u8]) -> Result { + let mem_bio = try!(MemBioSlice::new(buf)); let dh = unsafe { ffi::PEM_read_bio_DHparams(mem_bio.get_handle(), ptr::null_mut(), None, ptr::null_mut()) }; @@ -31,19 +26,19 @@ impl DH { } #[cfg(feature = "rfc5114")] - pub fn get_1024_160() -> Result { + pub fn get_1024_160() -> Result { let dh = try_ssl_null!(unsafe { ffi::DH_get_1024_160() }); Ok(DH(dh)) } #[cfg(feature = "rfc5114")] - pub fn get_2048_224() -> Result { + pub fn get_2048_224() -> Result { let dh = try_ssl_null!(unsafe { ffi::DH_get_2048_224() }); Ok(DH(dh)) } #[cfg(feature = "rfc5114")] - pub fn get_2048_256() -> Result { + pub fn get_2048_256() -> Result { let dh = try_ssl_null!(unsafe { ffi::DH_get_2048_256() }); Ok(DH(dh)) } @@ -71,8 +66,6 @@ impl Drop for DH { #[cfg(test)] mod tests { - use std::fs::File; - use std::path::Path; use super::DH; use bn::BigNum; use ssl::SslContext; @@ -81,7 +74,7 @@ mod tests { #[test] #[cfg(feature = "rfc5114")] fn test_dh_rfc5114() { - let ctx = SslContext::new(Sslv23).unwrap(); + let mut ctx = SslContext::new(Sslv23).unwrap(); let dh1 = DH::get_1024_160().unwrap(); ctx.set_tmp_dh(dh1).unwrap(); let dh2 = DH::get_2048_224().unwrap(); @@ -92,7 +85,7 @@ mod tests { #[test] fn test_dh() { - let ctx = SslContext::new(Sslv23).unwrap(); + let mut ctx = SslContext::new(Sslv23).unwrap(); let p = BigNum::from_hex_str("87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435\ E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF429\ 6D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C02\ @@ -122,12 +115,9 @@ mod tests { #[test] fn test_dh_from_pem() { - let ctx = SslContext::new(Sslv23).unwrap(); - let pem_path = Path::new("test/dhparams.pem"); - let mut file = File::open(&pem_path) - .ok() - .expect("Failed to open `test/dhparams.pem`"); - let dh = DH::from_pem(&mut file).ok().expect("Failed to load PEM"); + let mut ctx = SslContext::new(Sslv23).unwrap(); + let params = include_bytes!("../../test/dhparams.pem"); + let dh = DH::from_pem(params).ok().expect("Failed to load PEM"); ctx.set_tmp_dh(dh).unwrap(); } } diff --git a/openssl/src/error.rs b/openssl/src/error.rs new file mode 100644 index 00000000..5fa542c2 --- /dev/null +++ b/openssl/src/error.rs @@ -0,0 +1,137 @@ +use libc::c_ulong; +use std::fmt; +use std::error; +use std::ffi::CStr; +use std::io; +use std::str; + +use ffi; + +#[derive(Debug)] +pub struct ErrorStack(Vec); + +impl ErrorStack { + /// Returns the contents of the OpenSSL error stack. + pub fn get() -> ErrorStack { + let mut vec = vec![]; + while let Some(err) = Error::get() { + vec.push(err); + } + ErrorStack(vec) + } +} + +impl ErrorStack { + /// Returns the errors in the stack. + pub fn errors(&self) -> &[Error] { + &self.0 + } +} + +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 { + try!(fmt.write_str(", ")); + first = false; + } + try!(write!(fmt, "{}", err)); + } + Ok(()) + } +} + +impl error::Error for ErrorStack { + fn description(&self) -> &str { + "An OpenSSL error stack" + } +} + +impl From for io::Error { + fn from(e: ErrorStack) -> io::Error { + io::Error::new(io::ErrorKind::Other, e) + } +} + +/// An error reported from OpenSSL. +pub struct Error(c_ulong); + +impl Error { + /// Returns the first error on the OpenSSL error stack. + pub fn get() -> Option { + ffi::init(); + + match unsafe { ffi::ERR_get_error() } { + 0 => None, + err => Some((Error(err))), + } + } + + /// Returns the raw OpenSSL error code for this error. + pub fn error_code(&self) -> c_ulong { + self.0 + } + + /// Returns the name of the library reporting the error. + pub fn library(&self) -> &'static str { + get_lib(self.0) + } + + /// Returns the name of the function reporting the error. + pub fn function(&self) -> &'static str { + get_func(self.0) + } + + /// Returns the reason for the error. + pub fn reason(&self) -> &'static str { + get_reason(self.0) + } +} + +impl fmt::Debug for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Error") + .field("library", &self.library()) + .field("function", &self.function()) + .field("reason", &self.reason()) + .finish() + } +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(&self.reason()) + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + "An OpenSSL error" + } +} + +fn get_lib(err: c_ulong) -> &'static str { + unsafe { + let cstr = ffi::ERR_lib_error_string(err); + let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); + str::from_utf8(bytes).unwrap() + } +} + +fn get_func(err: c_ulong) -> &'static str { + unsafe { + let cstr = ffi::ERR_func_error_string(err); + let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); + str::from_utf8(bytes).unwrap() + } +} + +fn get_reason(err: c_ulong) -> &'static str { + unsafe { + let cstr = ffi::ERR_reason_error_string(err); + let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); + str::from_utf8(bytes).unwrap() + } +} + diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index 690ab32b..4cd76613 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -1,5 +1,4 @@ #![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.7.14")] -#![cfg_attr(feature = "nightly", feature(const_fn))] #[macro_use] extern crate bitflags; @@ -18,11 +17,12 @@ extern crate net2; mod macros; pub mod asn1; +mod bio; pub mod bn; -pub mod bio; pub mod crypto; pub mod dh; -pub mod ssl; -pub mod x509; +pub mod error; pub mod nid; +pub mod ssl; pub mod version; +pub mod x509; diff --git a/openssl/src/macros.rs b/openssl/src/macros.rs index 3e4bb429..35221f1c 100644 --- a/openssl/src/macros.rs +++ b/openssl/src/macros.rs @@ -13,7 +13,7 @@ macro_rules! try_ssl_stream { macro_rules! try_ssl_if { ($e:expr) => ( if $e { - return Err(SslError::get()) + return Err(::error::ErrorStack::get().into()) } ) } @@ -45,7 +45,7 @@ macro_rules! try_ssl_null{ macro_rules! lift_ssl_if{ ($e:expr) => ( { if $e { - Err(SslError::get()) + Err(::error::ErrorStack::get().into()) } else { Ok(()) } diff --git a/openssl/src/ssl/bio.rs b/openssl/src/ssl/bio.rs index b6f20cf2..3ced1c95 100644 --- a/openssl/src/ssl/bio.rs +++ b/openssl/src/ssl/bio.rs @@ -9,7 +9,7 @@ use std::ptr; use std::slice; use std::sync::Arc; -use ssl::error::SslError; +use error::ErrorStack; pub struct StreamState { pub stream: S, @@ -39,7 +39,7 @@ impl BioMethod { unsafe impl Send for BioMethod {} -pub fn new(stream: S) -> Result<(*mut BIO, Arc), SslError> { +pub fn new(stream: S) -> Result<(*mut BIO, Arc), ErrorStack> { let method = Arc::new(BioMethod::new::()); let state = Box::new(StreamState { diff --git a/openssl/src/ssl/error.rs b/openssl/src/ssl/error.rs index 2459a473..95213361 100644 --- a/openssl/src/ssl/error.rs +++ b/openssl/src/ssl/error.rs @@ -1,15 +1,8 @@ -pub use self::SslError::*; -pub use self::OpensslError::*; - -use libc::c_ulong; use std::error; use std::error::Error as StdError; use std::fmt; -use std::ffi::CStr; use std::io; -use std::str; - -use ffi; +use error::ErrorStack; /// An SSL error. #[derive(Debug)] @@ -27,30 +20,16 @@ pub enum Error { /// An error reported by the underlying stream. Stream(io::Error), /// An error in the OpenSSL library. - Ssl(Vec), + Ssl(ErrorStack), } impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { try!(fmt.write_str(self.description())); - match *self { - Error::Stream(ref err) => write!(fmt, ": {}", err), - Error::WantRead(ref err) => write!(fmt, ": {}", err), - Error::WantWrite(ref err) => write!(fmt, ": {}", err), - Error::Ssl(ref errs) => { - let mut first = true; - for err in errs { - if first { - try!(fmt.write_str(": ")); - first = false; - } else { - try!(fmt.write_str(", ")); - } - try!(fmt.write_str(&err.reason())) - } - Ok(()) - } - _ => Ok(()), + if let Some(err) = self.cause() { + write!(fmt, ": {}", err) + } else { + Ok(()) } } } @@ -72,247 +51,14 @@ impl error::Error for Error { Error::WantRead(ref err) => Some(err), Error::WantWrite(ref err) => Some(err), Error::Stream(ref err) => Some(err), + Error::Ssl(ref err) => Some(err), _ => None, } } } -/// An error reported from OpenSSL. -pub struct OpenSslError(c_ulong); - -impl OpenSslError { - /// Returns the contents of the OpenSSL error stack. - pub fn get_stack() -> Vec { - ffi::init(); - - let mut errs = vec![]; - loop { - match unsafe { ffi::ERR_get_error() } { - 0 => break, - err => errs.push(OpenSslError(err)), - } - } - errs - } - - /// Returns the raw OpenSSL error code for this error. - pub fn error_code(&self) -> c_ulong { - self.0 - } - - /// Returns the name of the library reporting the error. - pub fn library(&self) -> &'static str { - get_lib(self.0) - } - - /// Returns the name of the function reporting the error. - pub fn function(&self) -> &'static str { - get_func(self.0) - } - - /// Returns the reason for the error. - pub fn reason(&self) -> &'static str { - get_reason(self.0) +impl From for Error { + fn from(e: ErrorStack) -> Error { + Error::Ssl(e) } } - -impl fmt::Debug for OpenSslError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("OpenSslError") - .field("library", &self.library()) - .field("function", &self.function()) - .field("reason", &self.reason()) - .finish() - } -} - -impl fmt::Display for OpenSslError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(&self.reason()) - } -} - -impl error::Error for OpenSslError { - fn description(&self) -> &str { - "An OpenSSL error" - } -} - -/// An SSL error -#[derive(Debug)] -pub enum SslError { - /// The underlying stream reported an error - StreamError(io::Error), - /// The SSL session has been closed by the other end - SslSessionClosed, - /// An error in the OpenSSL library - OpenSslErrors(Vec), -} - -/// An error on a nonblocking stream. -#[derive(Debug)] -pub enum NonblockingSslError { - /// A standard SSL error occurred. - SslError(SslError), - /// The OpenSSL library wants data from the remote socket; - /// the caller should wait for read readiness. - WantRead, - /// The OpenSSL library wants to send data to the remote socket; - /// the caller should wait for write readiness. - WantWrite, -} - -impl fmt::Display for SslError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - try!(fmt.write_str(error::Error::description(self))); - if let OpenSslErrors(ref errs) = *self { - let mut first = true; - for err in errs { - if first { - try!(fmt.write_str(": ")); - first = false; - } else { - try!(fmt.write_str(", ")); - } - match *err { - UnknownError { ref reason, .. } => try!(fmt.write_str(reason)), - } - } - } - - Ok(()) - } -} - -impl error::Error for SslError { - fn description(&self) -> &str { - match *self { - StreamError(_) => "The underlying stream reported an error", - SslSessionClosed => "The SSL session has been closed by the other end", - OpenSslErrors(_) => "An error in the OpenSSL library", - } - } - - fn cause(&self) -> Option<&error::Error> { - match *self { - StreamError(ref err) => Some(err as &error::Error), - _ => None, - } - } -} - -impl fmt::Display for NonblockingSslError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(error::Error::description(self)) - } -} - -impl error::Error for NonblockingSslError { - fn description(&self) -> &str { - match *self { - NonblockingSslError::SslError(ref e) => e.description(), - NonblockingSslError::WantRead => { - "The OpenSSL library wants data from the remote socket" - } - NonblockingSslError::WantWrite => { - "The OpenSSL library want to send data to the remote socket" - } - } - } - - fn cause(&self) -> Option<&error::Error> { - match *self { - NonblockingSslError::SslError(ref e) => e.cause(), - _ => None, - } - } -} - -impl From for NonblockingSslError { - fn from(e: SslError) -> NonblockingSslError { - NonblockingSslError::SslError(e) - } -} - -/// An error from the OpenSSL library -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum OpensslError { - /// An unknown error - UnknownError { - /// The library reporting the error - library: String, - /// The function reporting the error - function: String, - /// The reason for the error - reason: String, - }, -} - -impl OpensslError { - pub fn from_error_code(err: c_ulong) -> OpensslError { - ffi::init(); - UnknownError { - library: get_lib(err).to_owned(), - function: get_func(err).to_owned(), - reason: get_reason(err).to_owned(), - } - } -} - -fn get_lib(err: c_ulong) -> &'static str { - unsafe { - let cstr = ffi::ERR_lib_error_string(err); - let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); - str::from_utf8(bytes).unwrap() - } -} - -fn get_func(err: c_ulong) -> &'static str { - unsafe { - let cstr = ffi::ERR_func_error_string(err); - let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); - str::from_utf8(bytes).unwrap() - } -} - -fn get_reason(err: c_ulong) -> &'static str { - unsafe { - let cstr = ffi::ERR_reason_error_string(err); - let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); - str::from_utf8(bytes).unwrap() - } -} - -impl SslError { - /// Creates a new `OpenSslErrors` with the current contents of the error - /// stack. - pub fn get() -> SslError { - let mut errs = vec![]; - loop { - match unsafe { ffi::ERR_get_error() } { - 0 => break, - err => errs.push(OpensslError::from_error_code(err)), - } - } - OpenSslErrors(errs) - } - - /// Creates an `SslError` from the raw numeric error code. - pub fn from_error(err: c_ulong) -> SslError { - OpenSslErrors(vec![OpensslError::from_error_code(err)]) - } -} - -#[test] -fn test_uknown_error_should_have_correct_messages() { - let errs = match SslError::from_error(336032784) { - OpenSslErrors(errs) => errs, - _ => panic!("This should always be an `OpenSslErrors` variant."), - }; - - let UnknownError { ref library, ref function, ref reason } = errs[0]; - - assert_eq!(&library[..], "SSL routines"); - assert_eq!(&function[..], "SSL23_GET_SERVER_HELLO"); - assert_eq!(&reason[..], "sslv3 alert handshake failure"); -} diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 0252b114..3d1ec6e5 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -5,9 +5,9 @@ use std::ffi::{CStr, CString}; use std::fmt; use std::io; use std::io::prelude::*; +use std::error as stderror; use std::mem; use std::str; -use std::net; use std::path::Path; use std::ptr; use std::sync::{Once, ONCE_INIT, Mutex, Arc}; @@ -18,17 +18,13 @@ use libc::{c_uchar, c_uint}; #[cfg(any(feature = "npn", feature = "alpn"))] use std::slice; use std::marker::PhantomData; -#[cfg(unix)] -use std::os::unix::io::{AsRawFd, RawFd}; -#[cfg(windows)] -use std::os::windows::io::{AsRawSocket, RawSocket}; use ffi; use ffi_extras; use dh::DH; -use ssl::error::{NonblockingSslError, SslError, OpenSslError, OpensslError}; use x509::{X509StoreContext, X509FileType, X509}; use crypto::pkey::PKey; +use error::ErrorStack; pub mod error; mod bio; @@ -45,27 +41,11 @@ extern "C" { fn rust_SSL_CTX_clone(cxt: *mut ffi::SSL_CTX); } -static mut VERIFY_IDX: c_int = -1; -static mut SNI_IDX: c_int = -1; - /// Manually initialize SSL. /// It is optional to call this function and safe to do so more than once. pub fn init() { static mut INIT: Once = ONCE_INIT; - - unsafe { - INIT.call_once(|| { - ffi::init(); - - let verify_idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None, None, None); - assert!(verify_idx >= 0); - VERIFY_IDX = verify_idx; - - let sni_idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None, None, None); - assert!(sni_idx >= 0); - SNI_IDX = sni_idx; - }); - } + unsafe { INIT.call_once(|| ffi::init()); } } bitflags! { @@ -186,30 +166,6 @@ impl SslMethod { _ => None, } } - - #[cfg(feature = "dtlsv1")] - pub fn is_dtlsv1(&self) -> bool { - *self == SslMethod::Dtlsv1 - } - - #[cfg(feature = "dtlsv1_2")] - pub fn is_dtlsv1_2(&self) -> bool { - *self == SslMethod::Dtlsv1_2 - } - - pub fn is_dtls(&self) -> bool { - self.is_dtlsv1() || self.is_dtlsv1_2() - } - - #[cfg(not(feature = "dtlsv1"))] - pub fn is_dtlsv1(&self) -> bool { - false - } - - #[cfg(not(feature = "dtlsv1_2"))] - pub fn is_dtlsv1_2(&self) -> bool { - false - } } /// Determines the type of certificate verification used @@ -292,47 +248,19 @@ fn get_new_ssl_idx() -> c_int { } } -extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int { - 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 ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); - let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, VERIFY_IDX); - let verify: Option = mem::transmute(verify); - - let ctx = X509StoreContext::new(x509_ctx); - - match verify { - None => preverify_ok, - Some(verify) => verify(preverify_ok != 0, &ctx) as c_int, - } - } -} - -extern "C" fn raw_verify_with_data(preverify_ok: c_int, - x509_ctx: *mut ffi::X509_STORE_CTX) - -> c_int - where T: Any + 'static +extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int + where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send { 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 ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); - - let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, VERIFY_IDX); - let verify: Option> = mem::transmute(verify); - - let data = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::()); - let data: &T = mem::transmute(data); + let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::()); + let verify: &F = mem::transmute(verify); let ctx = X509StoreContext::new(x509_ctx); - let res = match verify { - None => preverify_ok, - Some(verify) => verify(preverify_ok != 0, &ctx, data) as c_int, - }; - - res + verify(preverify_ok != 0, &ctx) as c_int } } @@ -351,50 +279,31 @@ extern "C" fn ssl_raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_ST } } -extern "C" fn raw_sni(ssl: *mut ffi::SSL, ad: &mut c_int, _arg: *mut c_void) -> c_int { - unsafe { - let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); - let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, SNI_IDX); - let callback: Option = mem::transmute(callback); - rust_SSL_clone(ssl); - let mut s = Ssl { ssl: ssl }; - - let res = match callback { - None => ffi::SSL_TLSEXT_ERR_ALERT_FATAL, - Some(callback) => callback(&mut s, ad), - }; - - res - } -} - -extern "C" fn raw_sni_with_data(ssl: *mut ffi::SSL, ad: &mut c_int, arg: *mut c_void) -> c_int - where T: Any + 'static +extern "C" fn raw_sni(ssl: *mut ffi::SSL, al: *mut c_int, _arg: *mut c_void) -> c_int + where F: Fn(&mut Ssl) -> Result<(), SniError> + Any + 'static + Sync + Send { unsafe { let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); - - let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, SNI_IDX); - let callback: Option> = mem::transmute(callback); + let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::()); + let callback: &F = mem::transmute(callback); rust_SSL_clone(ssl); - let mut s = Ssl { ssl: ssl }; + let mut ssl = Ssl { ssl: ssl }; - let data: &T = mem::transmute(arg); - - let res = match callback { - None => ffi::SSL_TLSEXT_ERR_ALERT_FATAL, - Some(callback) => callback(&mut s, ad, &*data), - }; - - // Since data might be required on the next verification - // it is time to forget about it and avoid dropping - // data will be freed once OpenSSL considers it is time - // to free all context data - res + match callback(&mut ssl) { + Ok(()) => ffi::SSL_TLSEXT_ERR_OK, + Err(SniError::Fatal(e)) => { + *al = e; + ffi::SSL_TLSEXT_ERR_ALERT_FATAL + } + Err(SniError::Warning(e)) => { + *al = e; + ffi::SSL_TLSEXT_ERR_ALERT_WARNING + } + Err(SniError::NoAck) => ffi::SSL_TLSEXT_ERR_NOACK, + } } } - #[cfg(any(feature = "npn", feature = "alpn"))] unsafe fn select_proto_using(ssl: *mut ffi::SSL, out: *mut *mut c_uchar, @@ -499,24 +408,18 @@ fn ssl_encode_byte_strings(strings: &[&[u8]]) -> Vec { enc } -/// The signature of functions that can be used to manually verify certificates -pub type VerifyCallback = fn(preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool; - -/// The signature of functions that can be used to manually verify certificates -/// when user-data should be carried for all verification process -pub type VerifyCallbackData = fn(preverify_ok: bool, x509_ctx: &X509StoreContext, data: &T) - -> bool; - -/// The signature of functions that can be used to choose the context depending on the server name -pub type ServerNameCallback = fn(ssl: &mut Ssl, ad: &mut i32) -> i32; - -pub type ServerNameCallbackData = fn(ssl: &mut Ssl, ad: &mut i32, data: &T) -> i32; +/// An error returned from an SNI callback. +pub enum SniError { + Fatal(c_int), + Warning(c_int), + NoAck, +} // FIXME: macro may be instead of inlining? #[inline] -fn wrap_ssl_result(res: c_int) -> Result<(), SslError> { +fn wrap_ssl_result(res: c_int) -> Result<(), ErrorStack> { if res == 0 { - Err(SslError::get()) + Err(ErrorStack::get()) } else { Ok(()) } @@ -559,80 +462,56 @@ impl SslContext { } /// Creates a new SSL context. - pub fn new(method: SslMethod) -> Result { + pub fn new(method: SslMethod) -> Result { init(); let ctx = try_ssl_null!(unsafe { ffi::SSL_CTX_new(method.to_raw()) }); - let ctx = SslContext { ctx: ctx }; + let mut ctx = SslContext { ctx: ctx }; + match method { + #[cfg(feature = "dtlsv1")] + SslMethod::Dtlsv1 => ctx.set_read_ahead(1), + #[cfg(feature = "dtlsv1_2")] + SslMethod::Dtlsv1_2 => ctx.set_read_ahead(1), + _ => {} + } // this is a bit dubious (?) try!(ctx.set_mode(ffi::SSL_MODE_AUTO_RETRY | ffi::SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)); - if method.is_dtls() { - ctx.set_read_ahead(1); - } - Ok(ctx) } /// Configures the certificate verification method for new connections. - pub fn set_verify(&mut self, mode: SslVerifyMode, verify: Option) { + pub fn set_verify(&mut self, mode: SslVerifyMode) { unsafe { - ffi::SSL_CTX_set_ex_data(self.ctx, VERIFY_IDX, mem::transmute(verify)); - let f: extern "C" fn(c_int, *mut ffi::X509_STORE_CTX) -> c_int = raw_verify; - - ffi::SSL_CTX_set_verify(self.ctx, mode.bits as c_int, Some(f)); + ffi::SSL_CTX_set_verify(self.ctx, mode.bits as c_int, None); } } - /// Configures the certificate verification method for new connections also - /// carrying supplied data. - // Note: no option because there is no point to set data without providing - // a function handling it - pub fn set_verify_with_data(&mut self, - mode: SslVerifyMode, - verify: VerifyCallbackData, - data: T) - where T: Any + 'static + /// Configures the certificate verification method for new connections and + /// registers a verification callback. + pub fn set_verify_callback(&mut self, mode: SslVerifyMode, verify: F) + where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send { - let data = Box::new(data); unsafe { - ffi::SSL_CTX_set_ex_data(self.ctx, VERIFY_IDX, mem::transmute(Some(verify))); - ffi::SSL_CTX_set_ex_data(self.ctx, get_verify_data_idx::(), mem::transmute(data)); - let f: extern "C" fn(c_int, *mut ffi::X509_STORE_CTX) -> c_int = - raw_verify_with_data::; - - ffi::SSL_CTX_set_verify(self.ctx, mode.bits as c_int, Some(f)); + let verify = Box::new(verify); + ffi::SSL_CTX_set_ex_data(self.ctx, get_verify_data_idx::(), mem::transmute(verify)); + ffi::SSL_CTX_set_verify(self.ctx, mode.bits as c_int, Some(raw_verify::)); } } /// Configures the server name indication (SNI) callback for new connections /// - /// obtain the server name with `get_servername` then set the corresponding context + /// Obtain the server name with `servername` then set the corresponding context /// with `set_ssl_context` - pub fn set_servername_callback(&mut self, callback: Option) { - unsafe { - ffi::SSL_CTX_set_ex_data(self.ctx, SNI_IDX, mem::transmute(callback)); - let f: extern "C" fn(_, _, _) -> _ = raw_sni; - let f: extern "C" fn() = mem::transmute(f); - ffi_extras::SSL_CTX_set_tlsext_servername_callback(self.ctx, Some(f)); - } - } - - /// Configures the server name indication (SNI) callback for new connections - /// carrying supplied data - pub fn set_servername_callback_with_data(&mut self, - callback: ServerNameCallbackData, - data: T) - where T: Any + 'static + pub fn set_servername_callback(&mut self, callback: F) + where F: Fn(&mut Ssl) -> Result<(), SniError> + Any + 'static + Sync + Send { - let data = Box::new(data); unsafe { - ffi::SSL_CTX_set_ex_data(self.ctx, SNI_IDX, mem::transmute(Some(callback))); - - ffi_extras::SSL_CTX_set_tlsext_servername_arg(self.ctx, mem::transmute(data)); - let f: extern "C" fn(_, _, _) -> _ = raw_sni_with_data::; + let callback = Box::new(callback); + ffi::SSL_CTX_set_ex_data(self.ctx, get_verify_data_idx::(), mem::transmute(callback)); + let f: extern "C" fn(_, _, _) -> _ = raw_sni::; let f: extern "C" fn() = mem::transmute(f); ffi_extras::SSL_CTX_set_tlsext_servername_callback(self.ctx, Some(f)); } @@ -645,18 +524,18 @@ impl SslContext { } } - pub fn set_read_ahead(&self, m: u32) { + pub fn set_read_ahead(&mut self, m: u32) { unsafe { ffi_extras::SSL_CTX_set_read_ahead(self.ctx, m as c_long); } } - fn set_mode(&self, mode: c_long) -> Result<(), SslError> { + fn set_mode(&mut self, mode: c_long) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi_extras::SSL_CTX_set_mode(self.ctx, mode) as c_int }) } - pub fn set_tmp_dh(&self, dh: DH) -> Result<(), SslError> { - wrap_ssl_result(unsafe { ffi_extras::SSL_CTX_set_tmp_dh(self.ctx, dh.raw()) as c_int }) + pub fn set_tmp_dh(&mut self, dh: DH) -> Result<(), ErrorStack> { + wrap_ssl_result(unsafe { ffi_extras::SSL_CTX_set_tmp_dh(self.ctx, dh.raw()) as i32 }) } /// Use the default locations of trusted certificates for verification. @@ -664,13 +543,13 @@ impl SslContext { /// 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_verify_paths(&mut self) -> Result<(), SslError> { + pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi::SSL_CTX_set_default_verify_paths(self.ctx) }) } #[allow(non_snake_case)] /// Specifies the file that contains trusted CA certificates. - pub fn set_CA_file>(&mut self, file: P) -> Result<(), SslError> { + pub fn set_CA_file>(&mut self, file: P) -> Result<(), ErrorStack> { let file = CString::new(file.as_ref().as_os_str().to_str().expect("invalid utf8")).unwrap(); wrap_ssl_result(unsafe { ffi::SSL_CTX_load_verify_locations(self.ctx, file.as_ptr() as *const _, ptr::null()) @@ -685,7 +564,7 @@ impl SslContext { /// /// This value should be set when using client certificates, or each request will fail /// handshake and need to be restarted. - pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), SslError> { + pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi::SSL_CTX_set_session_id_context(self.ctx, sid_ctx.as_ptr(), sid_ctx.len() as u32) }) @@ -695,7 +574,7 @@ impl SslContext { pub fn set_certificate_file>(&mut self, file: P, file_type: X509FileType) - -> Result<(), SslError> { + -> Result<(), ErrorStack> { let file = CString::new(file.as_ref().as_os_str().to_str().expect("invalid utf8")).unwrap(); wrap_ssl_result(unsafe { ffi::SSL_CTX_use_certificate_file(self.ctx, @@ -708,7 +587,7 @@ impl SslContext { pub fn set_certificate_chain_file>(&mut self, file: P, file_type: X509FileType) - -> Result<(), SslError> { + -> Result<(), ErrorStack> { let file = CString::new(file.as_ref().as_os_str().to_str().expect("invalid utf8")).unwrap(); wrap_ssl_result(unsafe { ffi::SSL_CTX_use_certificate_chain_file(self.ctx, @@ -718,13 +597,13 @@ impl SslContext { } /// Specifies the certificate - pub fn set_certificate(&mut self, cert: &X509) -> Result<(), SslError> { + pub fn set_certificate(&mut self, cert: &X509) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi::SSL_CTX_use_certificate(self.ctx, cert.get_handle()) }) } /// Adds a certificate to the certificate chain presented together with the /// certificate specified using set_certificate() - pub fn add_extra_chain_cert(&mut self, cert: &X509) -> Result<(), SslError> { + pub fn add_extra_chain_cert(&mut self, cert: &X509) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi_extras::SSL_CTX_add_extra_chain_cert(self.ctx, cert.get_handle()) as c_int }) @@ -734,7 +613,7 @@ impl SslContext { pub fn set_private_key_file>(&mut self, file: P, file_type: X509FileType) - -> Result<(), SslError> { + -> Result<(), ErrorStack> { let file = CString::new(file.as_ref().as_os_str().to_str().expect("invalid utf8")).unwrap(); wrap_ssl_result(unsafe { ffi::SSL_CTX_use_PrivateKey_file(self.ctx, @@ -744,16 +623,16 @@ impl SslContext { } /// Specifies the private key - pub fn set_private_key(&mut self, key: &PKey) -> Result<(), SslError> { + pub fn set_private_key(&mut self, key: &PKey) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi::SSL_CTX_use_PrivateKey(self.ctx, key.get_handle()) }) } /// Check consistency of private key and certificate - pub fn check_private_key(&mut self) -> Result<(), SslError> { + pub fn check_private_key(&mut self) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi::SSL_CTX_check_private_key(self.ctx) }) } - pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), SslError> { + pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { let cipher_list = CString::new(cipher_list).unwrap(); ffi::SSL_CTX_set_cipher_list(self.ctx, cipher_list.as_ptr() as *const _) @@ -765,7 +644,7 @@ impl SslContext { /// /// This method requires OpenSSL >= 1.0.2 or LibreSSL and the `ecdh_auto` feature. #[cfg(feature = "ecdh_auto")] - pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), SslError> { + pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> { wrap_ssl_result(unsafe { ffi_extras::SSL_CTX_set_ecdh_auto(self.ctx, onoff as c_int) }) } @@ -775,7 +654,7 @@ impl SslContext { SslContextOptions::from_bits(ret).unwrap() } - pub fn get_options(&mut self) -> SslContextOptions { + pub fn options(&self) -> SslContextOptions { let ret = unsafe { ffi_extras::SSL_CTX_get_options(self.ctx) }; SslContextOptions::from_bits(ret).unwrap() } @@ -935,17 +814,8 @@ impl Drop for Ssl { } } -impl Clone for Ssl { - /// # Deprecated - fn clone(&self) -> Ssl { - unsafe { rust_SSL_clone(self.ssl) }; - Ssl { ssl: self.ssl } - - } -} - impl Ssl { - pub fn new(ctx: &SslContext) -> Result { + pub fn new(ctx: &SslContext) -> Result { let ssl = try_ssl_null!(unsafe { ffi::SSL_new(ctx.ctx) }); let ssl = Ssl { ssl: ssl }; Ok(ssl) @@ -963,6 +833,10 @@ impl Ssl { unsafe { ffi::SSL_accept(self.ssl) } } + fn handshake(&self) -> c_int { + unsafe { ffi::SSL_do_handshake(self.ssl) } + } + fn read(&self, buf: &mut [u8]) -> c_int { let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int; unsafe { ffi::SSL_read(self.ssl, buf.as_ptr() as *mut c_void, len) } @@ -973,12 +847,8 @@ impl Ssl { unsafe { ffi::SSL_write(self.ssl, buf.as_ptr() as *const c_void, len) } } - fn get_error(&self, ret: c_int) -> LibSslError { - let err = unsafe { ffi::SSL_get_error(self.ssl, ret) }; - match LibSslError::from_i32(err as i32) { - Some(err) => err, - None => unreachable!(), - } + fn get_error(&self, ret: c_int) -> c_int { + unsafe { ffi::SSL_get_error(self.ssl, ret) } } /// Sets the verification mode to be used during the handshake process. @@ -1007,7 +877,7 @@ impl Ssl { } } - pub fn get_current_cipher<'a>(&'a self) -> Option> { + pub fn current_cipher<'a>(&'a self) -> Option> { unsafe { let ptr = ffi::SSL_get_current_cipher(self.ssl); @@ -1041,7 +911,7 @@ impl Ssl { } /// Sets the host name to be used with SNI (Server Name Indication). - pub fn set_hostname(&self, hostname: &str) -> Result<(), SslError> { + pub fn set_hostname(&self, hostname: &str) -> Result<(), ErrorStack> { let cstr = CString::new(hostname).unwrap(); let ret = unsafe { ffi_extras::SSL_set_tlsext_host_name(self.ssl, cstr.as_ptr() as *const _) @@ -1049,7 +919,7 @@ impl Ssl { // For this case, 0 indicates failure. if ret == 0 { - Err(SslError::get()) + Err(ErrorStack::get()) } else { Ok(()) } @@ -1147,7 +1017,7 @@ impl Ssl { Some(s) } - pub fn get_ssl_method(&self) -> Option { + pub fn ssl_method(&self) -> Option { unsafe { let method = ffi::SSL_get_ssl_method(self.ssl); SslMethod::from_raw(method) @@ -1155,7 +1025,7 @@ impl Ssl { } /// Returns the server's name for the current connection - pub fn get_servername(&self) -> Option { + pub fn servername(&self) -> Option { let name = unsafe { ffi::SSL_get_servername(self.ssl, ffi::TLSEXT_NAMETYPE_host_name) }; if name == ptr::null() { return None; @@ -1164,25 +1034,16 @@ impl Ssl { unsafe { String::from_utf8(CStr::from_ptr(name as *const _).to_bytes().to_vec()).ok() } } - /// change the context corresponding to the current connection - /// - /// Returns a clone of the SslContext @ctx (ie: the new context). The old context is freed. - pub fn set_ssl_context(&self, ctx: &SslContext) -> SslContext { - // If duplication of @ctx's cert fails, this returns NULL. This _appears_ to only occur on - // allocation failures (meaning panicing is probably appropriate), but it might be nice to - // propogate the error. - assert!(unsafe { ffi::SSL_set_SSL_CTX(self.ssl, ctx.ctx) } != ptr::null_mut()); - - // FIXME: we return this reference here for compatibility, but it isn't actually required. - // This should be removed when a api-incompatabile version is to be released. - // - // ffi:SSL_set_SSL_CTX() returns copy of the ctx pointer passed to it, so it's easier for - // us to do the clone directly. - ctx.clone() + /// Changes the context corresponding to the current connection. + pub fn set_ssl_context(&self, ctx: &SslContext) -> Result<(), ErrorStack> { + unsafe { + try_ssl_null!(ffi::SSL_set_SSL_CTX(self.ssl, ctx.ctx)); + } + Ok(()) } - /// obtain the context corresponding to the current connection - pub fn get_ssl_context(&self) -> SslContext { + /// Returns the context corresponding to the current connection + pub fn ssl_context(&self) -> SslContext { unsafe { let ssl_ctx = ffi::SSL_get_SSL_CTX(self.ssl); SslContext::new_ref(ssl_ctx) @@ -1190,37 +1051,6 @@ impl Ssl { } } -macro_rules! make_LibSslError { - ($($variant:ident = $value:ident),+) => { - #[derive(Debug)] - #[repr(i32)] - enum LibSslError { - $($variant = ffi::$value),+ - } - - impl LibSslError { - fn from_i32(val: i32) -> Option { - match val { - $(ffi::$value => Some(LibSslError::$variant),)+ - _ => None - } - } - } - } -} - -make_LibSslError! { - ErrorNone = SSL_ERROR_NONE, - ErrorSsl = SSL_ERROR_SSL, - ErrorWantRead = SSL_ERROR_WANT_READ, - ErrorWantWrite = SSL_ERROR_WANT_WRITE, - ErrorWantX509Lookup = SSL_ERROR_WANT_X509_LOOKUP, - ErrorSyscall = SSL_ERROR_SYSCALL, - ErrorZeroReturn = SSL_ERROR_ZERO_RETURN, - ErrorWantConnect = SSL_ERROR_WANT_CONNECT, - ErrorWantAccept = SSL_ERROR_WANT_ACCEPT -} - /// A stream wrapper which handles SSL encryption for an underlying stream. pub struct SslStream { ssl: Ssl, @@ -1228,19 +1058,7 @@ pub struct SslStream { _p: PhantomData, } -/// # Deprecated -/// -/// This method does not behave as expected and will be removed in a future -/// release. -impl Clone for SslStream { - fn clone(&self) -> SslStream { - SslStream { - ssl: self.ssl.clone(), - _method: self._method.clone(), - _p: PhantomData, - } - } -} +unsafe impl Send for SslStream {} impl fmt::Debug for SslStream where S: fmt::Debug @@ -1253,20 +1071,6 @@ impl fmt::Debug for SslStream } } -#[cfg(unix)] -impl AsRawFd for SslStream { - fn as_raw_fd(&self) -> RawFd { - self.get_ref().as_raw_fd() - } -} - -#[cfg(windows)] -impl AsRawSocket for SslStream { - fn as_raw_socket(&self) -> RawSocket { - self.get_ref().as_raw_socket() - } -} - impl SslStream { fn new_base(ssl: Ssl, stream: S) -> Self { unsafe { @@ -1282,49 +1086,53 @@ impl SslStream { } /// Creates an SSL/TLS client operating over the provided stream. - pub fn connect(ssl: T, stream: S) -> Result { - let ssl = try!(ssl.into_ssl()); + pub fn connect(ssl: T, stream: S) + -> Result>{ + let ssl = try!(ssl.into_ssl().map_err(|e| { + HandshakeError::Failure(Error::Ssl(e)) + })); let mut stream = Self::new_base(ssl, stream); let ret = stream.ssl.connect(); if ret > 0 { Ok(stream) } else { - match stream.make_old_error(ret) { - Some(err) => Err(err), - None => Ok(stream), + match stream.make_error(ret) { + e @ Error::WantWrite(_) | + e @ Error::WantRead(_) => { + Err(HandshakeError::Interrupted(MidHandshakeSslStream { + stream: stream, + error: e, + })) + } + err => Err(HandshakeError::Failure(err)), } } } /// Creates an SSL/TLS server operating over the provided stream. - pub fn accept(ssl: T, stream: S) -> Result { - let ssl = try!(ssl.into_ssl()); + pub fn accept(ssl: T, stream: S) + -> Result> { + let ssl = try!(ssl.into_ssl().map_err(|e| { + HandshakeError::Failure(Error::Ssl(e)) + })); let mut stream = Self::new_base(ssl, stream); let ret = stream.ssl.accept(); if ret > 0 { Ok(stream) } else { - match stream.make_old_error(ret) { - Some(err) => Err(err), - None => Ok(stream), + match stream.make_error(ret) { + e @ Error::WantWrite(_) | + e @ Error::WantRead(_) => { + Err(HandshakeError::Interrupted(MidHandshakeSslStream { + stream: stream, + error: e, + })) + } + err => Err(HandshakeError::Failure(err)), } } } - /// ### Deprecated - /// - /// Use `connect`. - pub fn connect_generic(ssl: T, stream: S) -> Result, SslError> { - Self::connect(ssl, stream) - } - - /// ### Deprecated - /// - /// Use `accept`. - pub fn accept_generic(ssl: T, stream: S) -> Result, SslError> { - Self::accept(ssl, stream) - } - /// Like `read`, but returns an `ssl::Error` rather than an `io::Error`. /// /// This is particularly useful with a nonblocking socket, where the error @@ -1352,15 +1160,96 @@ impl SslStream { } } +/// An error or intermediate state after a TLS handshake attempt. +#[derive(Debug)] +pub enum HandshakeError { + /// The handshake failed. + Failure(Error), + /// The handshake was interrupted midway through. + Interrupted(MidHandshakeSslStream), +} + +impl stderror::Error for HandshakeError { + fn description(&self) -> &str { + match *self { + HandshakeError::Failure(ref e) => e.description(), + HandshakeError::Interrupted(ref e) => e.error.description(), + } + } + + fn cause(&self) -> Option<&stderror::Error> { + match *self { + HandshakeError::Failure(ref e) => Some(e), + HandshakeError::Interrupted(ref e) => Some(&e.error), + } + } +} + +impl fmt::Display for HandshakeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(f.write_str(stderror::Error::description(self))); + if let Some(e) = stderror::Error::cause(self) { + try!(write!(f, ": {}", e)); + } + Ok(()) + } +} + +/// An SSL stream midway through the handshake process. +#[derive(Debug)] +pub struct MidHandshakeSslStream { + stream: SslStream, + error: Error, +} + +impl MidHandshakeSslStream { + /// Returns a shared reference to the inner stream. + pub fn get_ref(&self) -> &S { + self.stream.get_ref() + } + + /// Returns a mutable reference to the inner stream. + pub fn get_mut(&mut self) -> &mut S { + self.stream.get_mut() + } + + /// Returns a shared reference to the `SslContext` of the stream. + pub fn ssl(&self) -> &Ssl { + self.stream.ssl() + } + + /// Returns the underlying error which interrupted this handshake. + pub fn error(&self) -> &Error { + &self.error + } + + /// Restarts the handshake process. + pub fn handshake(mut self) -> Result, HandshakeError> { + let ret = self.stream.ssl.handshake(); + if ret > 0 { + Ok(self.stream) + } else { + match self.stream.make_error(ret) { + e @ Error::WantWrite(_) | + e @ Error::WantRead(_) => { + self.error = e; + Err(HandshakeError::Interrupted(self)) + } + err => Err(HandshakeError::Failure(err)), + } + } + } +} + impl SslStream { fn make_error(&mut self, ret: c_int) -> Error { self.check_panic(); match self.ssl.get_error(ret) { - LibSslError::ErrorSsl => Error::Ssl(OpenSslError::get_stack()), - LibSslError::ErrorSyscall => { - let errs = OpenSslError::get_stack(); - if errs.is_empty() { + ffi::SSL_ERROR_SSL => Error::Ssl(ErrorStack::get()), + 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")) @@ -1371,44 +1260,12 @@ impl SslStream { Error::Ssl(errs) } } - LibSslError::ErrorZeroReturn => Error::ZeroReturn, - LibSslError::ErrorWantWrite => Error::WantWrite(self.get_bio_error()), - LibSslError::ErrorWantRead => Error::WantRead(self.get_bio_error()), + 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()), err => { - Error::Stream(io::Error::new(io::ErrorKind::Other, - format!("unexpected error {:?}", err))) - } - } - } - - fn make_old_error(&mut self, ret: c_int) -> Option { - self.check_panic(); - - match self.ssl.get_error(ret) { - LibSslError::ErrorSsl => Some(SslError::get()), - LibSslError::ErrorSyscall => { - let err = SslError::get(); - let count = match err { - SslError::OpenSslErrors(ref v) => v.len(), - _ => unreachable!(), - }; - if count == 0 { - if ret == 0 { - Some(SslError::StreamError(io::Error::new(io::ErrorKind::ConnectionAborted, - "unexpected EOF observed"))) - } else { - Some(SslError::StreamError(self.get_bio_error())) - } - } else { - Some(err) - } - } - LibSslError::ErrorZeroReturn => Some(SslError::SslSessionClosed), - LibSslError::ErrorWantWrite | - LibSslError::ErrorWantRead => None, - err => { - Some(SslError::StreamError(io::Error::new(io::ErrorKind::Other, - format!("unexpected error {:?}", err)))) + Error::Stream(io::Error::new(io::ErrorKind::InvalidData, + format!("unexpected error {}", err))) } } } @@ -1461,20 +1318,6 @@ impl SslStream { } } -impl SslStream<::std::net::TcpStream> { - /// # Deprecated - /// - /// This method does not behave as expected and will be removed in a future - /// release. - pub fn try_clone(&self) -> io::Result> { - Ok(SslStream { - ssl: self.ssl.clone(), - _method: self._method.clone(), - _p: PhantomData, - }) - } -} - impl Read for SslStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { match self.ssl_read(buf) { @@ -1506,222 +1349,17 @@ impl Write for SslStream { } pub trait IntoSsl { - fn into_ssl(self) -> Result; + fn into_ssl(self) -> Result; } impl IntoSsl for Ssl { - fn into_ssl(self) -> Result { + fn into_ssl(self) -> Result { Ok(self) } } impl<'a> IntoSsl for &'a SslContext { - fn into_ssl(self) -> Result { + fn into_ssl(self) -> Result { Ssl::new(self) } } - -/// A utility type to help in cases where the use of SSL is decided at runtime. -#[derive(Debug)] -pub enum MaybeSslStream - where S: Read + Write -{ - /// A connection using SSL - Ssl(SslStream), - /// A connection not using SSL - Normal(S), -} - -impl Read for MaybeSslStream - where S: Read + Write -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result { - match *self { - MaybeSslStream::Ssl(ref mut s) => s.read(buf), - MaybeSslStream::Normal(ref mut s) => s.read(buf), - } - } -} - -impl Write for MaybeSslStream - where S: Read + Write -{ - fn write(&mut self, buf: &[u8]) -> io::Result { - match *self { - MaybeSslStream::Ssl(ref mut s) => s.write(buf), - MaybeSslStream::Normal(ref mut s) => s.write(buf), - } - } - - fn flush(&mut self) -> io::Result<()> { - match *self { - MaybeSslStream::Ssl(ref mut s) => s.flush(), - MaybeSslStream::Normal(ref mut s) => s.flush(), - } - } -} - -impl MaybeSslStream - where S: Read + Write -{ - /// Returns a reference to the underlying stream. - pub fn get_ref(&self) -> &S { - match *self { - MaybeSslStream::Ssl(ref s) => s.get_ref(), - MaybeSslStream::Normal(ref s) => s, - } - } - - /// Returns a mutable reference to the underlying stream. - /// - /// ## Warning - /// - /// It is inadvisable to read from or write to the underlying stream. - pub fn get_mut(&mut self) -> &mut S { - match *self { - MaybeSslStream::Ssl(ref mut s) => s.get_mut(), - MaybeSslStream::Normal(ref mut s) => s, - } - } -} - -impl MaybeSslStream { - /// Like `TcpStream::try_clone`. - pub fn try_clone(&self) -> io::Result> { - match *self { - MaybeSslStream::Ssl(ref s) => s.try_clone().map(MaybeSslStream::Ssl), - MaybeSslStream::Normal(ref s) => s.try_clone().map(MaybeSslStream::Normal), - } - } -} - -/// # Deprecated -/// -/// Use `SslStream` with `ssl_read` and `ssl_write`. -pub struct NonblockingSslStream(SslStream); - -impl Clone for NonblockingSslStream { - fn clone(&self) -> Self { - NonblockingSslStream(self.0.clone()) - } -} - -#[cfg(unix)] -impl AsRawFd for NonblockingSslStream { - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -#[cfg(windows)] -impl AsRawSocket for NonblockingSslStream { - fn as_raw_socket(&self) -> RawSocket { - self.0.as_raw_socket() - } -} - -impl NonblockingSslStream { - pub fn try_clone(&self) -> io::Result> { - self.0.try_clone().map(NonblockingSslStream) - } -} - -impl NonblockingSslStream { - /// Returns a reference to the underlying stream. - pub fn get_ref(&self) -> &S { - self.0.get_ref() - } - - /// Returns a mutable reference to the underlying stream. - /// - /// ## Warning - /// - /// It is inadvisable to read from or write to the underlying stream as it - /// will most likely corrupt the SSL session. - pub fn get_mut(&mut self) -> &mut S { - self.0.get_mut() - } - - /// Returns a reference to the Ssl. - pub fn ssl(&self) -> &Ssl { - self.0.ssl() - } -} - -impl NonblockingSslStream { - /// Create a new nonblocking client ssl connection on wrapped `stream`. - /// - /// Note that this method will most likely not actually complete the SSL - /// handshake because doing so requires several round trips; the handshake will - /// be completed in subsequent read/write calls managed by your event loop. - pub fn connect(ssl: T, stream: S) -> Result, SslError> { - SslStream::connect(ssl, stream).map(NonblockingSslStream) - } - - /// Create a new nonblocking server ssl connection on wrapped `stream`. - /// - /// Note that this method will most likely not actually complete the SSL - /// handshake because doing so requires several round trips; the handshake will - /// be completed in subsequent read/write calls managed by your event loop. - pub fn accept(ssl: T, stream: S) -> Result, SslError> { - SslStream::accept(ssl, stream).map(NonblockingSslStream) - } - - fn convert_err(&self, err: Error) -> NonblockingSslError { - match err { - Error::ZeroReturn => SslError::SslSessionClosed.into(), - Error::WantRead(_) => NonblockingSslError::WantRead, - Error::WantWrite(_) => NonblockingSslError::WantWrite, - Error::WantX509Lookup => unreachable!(), - Error::Stream(e) => SslError::StreamError(e).into(), - Error::Ssl(e) => { - SslError::OpenSslErrors(e.iter() - .map(|e| OpensslError::from_error_code(e.error_code())) - .collect()) - .into() - } - } - } - - /// Read bytes from the SSL stream into `buf`. - /// - /// Given the SSL state machine, this method may return either `WantWrite` - /// or `WantRead` to indicate that your event loop should respectively wait - /// for write or read readiness on the underlying stream. Upon readiness, - /// repeat your `read()` call with the same arguments each time until you - /// receive an `Ok(count)`. - /// - /// An `SslError` return value, is terminal; do not re-attempt your read. - /// - /// As expected of a nonblocking API, this method will never block your - /// thread on I/O. - /// - /// On a return value of `Ok(count)`, count is the number of decrypted - /// plaintext bytes copied into the `buf` slice. - pub fn read(&mut self, buf: &mut [u8]) -> Result { - match self.0.ssl_read(buf) { - Ok(n) => Ok(n), - Err(Error::ZeroReturn) => Ok(0), - Err(e) => Err(self.convert_err(e)), - } - } - - /// Write bytes from `buf` to the SSL stream. - /// - /// Given the SSL state machine, this method may return either `WantWrite` - /// or `WantRead` to indicate that your event loop should respectively wait - /// for write or read readiness on the underlying stream. Upon readiness, - /// repeat your `write()` call with the same arguments each time until you - /// receive an `Ok(count)`. - /// - /// An `SslError` return value, is terminal; do not re-attempt your write. - /// - /// As expected of a nonblocking API, this method will never block your - /// thread on I/O. - /// - /// Given a return value of `Ok(count)`, count is the number of plaintext bytes - /// from the `buf` slice that were encrypted and written onto the stream. - pub fn write(&mut self, buf: &[u8]) -> Result { - self.0.ssl_write(buf).map_err(|e| self.convert_err(e)) - } -} diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs index 5339f27e..b774b383 100644 --- a/openssl/src/ssl/tests/mod.rs +++ b/openssl/src/ssl/tests/mod.rs @@ -17,9 +17,9 @@ use crypto::hash::Type::SHA256; use ssl; use ssl::SSL_VERIFY_PEER; use ssl::SslMethod::Sslv23; -use ssl::SslMethod; -use ssl::error::NonblockingSslError; -use ssl::{SslContext, SslStream, VerifyCallback, NonblockingSslStream}; +use ssl::{SslMethod, HandshakeError}; +use ssl::error::Error; +use ssl::{SslContext, SslStream}; use x509::X509StoreContext; use x509::X509FileType; use x509::X509; @@ -133,6 +133,7 @@ impl Drop for Server { } #[cfg(feature = "dtlsv1")] +#[derive(Debug)] struct UdpConnected(UdpSocket); #[cfg(feature = "dtlsv1")] @@ -194,9 +195,9 @@ macro_rules! run_test( use std::net::TcpStream; use ssl; use ssl::SslMethod; - use ssl::{SslContext, Ssl, SslStream, VerifyCallback}; + use ssl::{SslContext, Ssl, SslStream}; use ssl::SSL_VERIFY_PEER; - use crypto::hash::Type::SHA1; + use crypto::hash::Type::{SHA1, SHA256}; use x509::X509StoreContext; use serialize::hex::FromHex; use super::Server; @@ -222,19 +223,19 @@ run_test!(new_ctx, |method, _| { }); run_test!(new_sslstream, |method, stream| { - SslStream::connect_generic(&SslContext::new(method).unwrap(), stream).unwrap(); + SslStream::connect(&SslContext::new(method).unwrap(), stream).unwrap(); }); run_test!(get_ssl_method, |method, _| { let ssl = Ssl::new(&SslContext::new(method).unwrap()).unwrap(); - assert_eq!(ssl.get_ssl_method(), Some(method)); + assert_eq!(ssl.ssl_method(), Some(method)); }); run_test!(verify_untrusted, |method, stream| { let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); - match SslStream::connect_generic(&ctx, stream) { + match SslStream::connect(&ctx, stream) { Ok(_) => panic!("expected failure"), Err(err) => println!("error {:?}", err), } @@ -242,127 +243,95 @@ run_test!(verify_untrusted, |method, stream| { run_test!(verify_trusted, |method, stream| { let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - match SslStream::connect_generic(&ctx, stream) { + match SslStream::connect(&ctx, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err), } }); run_test!(verify_untrusted_callback_override_ok, |method, stream| { - fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool { - true - } - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, _| true); - match SslStream::connect_generic(&ctx, stream) { + match SslStream::connect(&ctx, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err), } }); run_test!(verify_untrusted_callback_override_bad, |method, stream| { - fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool { - false - } - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, _| false); - assert!(SslStream::connect_generic(&ctx, stream).is_err()); + assert!(SslStream::connect(&ctx, stream).is_err()); }); run_test!(verify_trusted_callback_override_ok, |method, stream| { - fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool { - true - } - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, _| true); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - match SslStream::connect_generic(&ctx, stream) { + match SslStream::connect(&ctx, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err), } }); run_test!(verify_trusted_callback_override_bad, |method, stream| { - fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool { - false - } - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, _| false); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - assert!(SslStream::connect_generic(&ctx, stream).is_err()); + assert!(SslStream::connect(&ctx, stream).is_err()); }); run_test!(verify_callback_load_certs, |method, stream| { - fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { + let mut ctx = SslContext::new(method).unwrap(); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, x509_ctx| { assert!(x509_ctx.get_current_cert().is_some()); true - } + }); - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); - - assert!(SslStream::connect_generic(&ctx, stream).is_ok()); + assert!(SslStream::connect(&ctx, stream).is_ok()); }); run_test!(verify_trusted_get_error_ok, |method, stream| { - fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { + let mut ctx = SslContext::new(method).unwrap(); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, x509_ctx| { assert!(x509_ctx.get_error().is_none()); true - } - - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); + }); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - assert!(SslStream::connect_generic(&ctx, stream).is_ok()); + assert!(SslStream::connect(&ctx, stream).is_ok()); }); run_test!(verify_trusted_get_error_err, |method, stream| { - fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { + let mut ctx = SslContext::new(method).unwrap(); + ctx.set_verify_callback(SSL_VERIFY_PEER, |_, x509_ctx| { assert!(x509_ctx.get_error().is_some()); false - } + }); - let mut ctx = SslContext::new(method).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, Some(callback as VerifyCallback)); - - assert!(SslStream::connect_generic(&ctx, stream).is_err()); + assert!(SslStream::connect(&ctx, stream).is_err()); }); run_test!(verify_callback_data, |method, stream| { - fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext, node_id: &Vec) -> bool { - let cert = x509_ctx.get_current_cert(); - match cert { - None => false, - Some(cert) => { - let fingerprint = cert.fingerprint(SHA1).unwrap(); - &fingerprint == node_id - } - } - } let mut ctx = SslContext::new(method).unwrap(); // Node id was generated as SHA256 hash of certificate "test/cert.pem" @@ -371,10 +340,19 @@ run_test!(verify_callback_data, |method, stream| { // Please update if "test/cert.pem" will ever change let node_hash_str = "E19427DAC79FBE758394945276A6E4F15F0BEBE6"; let node_id = node_hash_str.from_hex().unwrap(); - ctx.set_verify_with_data(SSL_VERIFY_PEER, callback, node_id); + ctx.set_verify_callback(SSL_VERIFY_PEER, move |_preverify_ok, x509_ctx| { + let cert = x509_ctx.get_current_cert(); + match cert { + None => false, + Some(cert) => { + let fingerprint = cert.fingerprint(SHA1).unwrap(); + fingerprint == node_id + } + } + }); ctx.set_verify_depth(1); - match SslStream::connect_generic(&ctx, stream) { + match SslStream::connect(&ctx, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err), } @@ -402,7 +380,7 @@ run_test!(ssl_verify_callback, |method, stream| { } }); - match SslStream::connect_generic(ssl, stream) { + match SslStream::connect(ssl, stream) { Ok(_) => (), Err(err) => panic!("Expected success, got {:?}", err), } @@ -419,14 +397,14 @@ fn test_write_hits_stream() { let guard = thread::spawn(move || { let ctx = SslContext::new(Sslv23).unwrap(); let stream = TcpStream::connect(addr).unwrap(); - let mut stream = SslStream::connect_generic(&ctx, stream).unwrap(); + let mut stream = SslStream::connect(&ctx, stream).unwrap(); stream.write_all(b"hello").unwrap(); stream }); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM).unwrap(); ctx.set_private_key_file(&Path::new("test/key.pem"), X509FileType::PEM).unwrap(); let stream = listener.accept().unwrap().0; @@ -440,17 +418,10 @@ fn test_write_hits_stream() { #[test] fn test_set_certificate_and_private_key() { - let key_path = Path::new("test/key.pem"); - let cert_path = Path::new("test/cert.pem"); - let mut key_file = File::open(&key_path) - .ok() - .expect("Failed to open `test/key.pem`"); - let mut cert_file = File::open(&cert_path) - .ok() - .expect("Failed to open `test/cert.pem`"); - - let key = PKey::private_key_from_pem(&mut key_file).unwrap(); - let cert = X509::from_pem(&mut cert_file).unwrap(); + let key = include_bytes!("../../../test/key.pem"); + let key = PKey::private_key_from_pem(key).unwrap(); + let cert = include_bytes!("../../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); let mut ctx = SslContext::new(Sslv23).unwrap(); ctx.set_private_key(&key).unwrap(); @@ -460,8 +431,8 @@ fn test_set_certificate_and_private_key() { } run_test!(get_ctx_options, |method, _| { - let mut ctx = SslContext::new(method).unwrap(); - ctx.get_options(); + let ctx = SslContext::new(method).unwrap(); + ctx.options(); }); run_test!(set_ctx_options, |method, _| { @@ -480,7 +451,7 @@ run_test!(clear_ctx_options, |method, _| { #[test] fn test_write() { let (_s, stream) = Server::new(); - let mut stream = SslStream::connect_generic(&SslContext::new(Sslv23).unwrap(), stream).unwrap(); + let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), stream).unwrap(); stream.write_all("hello".as_bytes()).unwrap(); stream.flush().unwrap(); stream.write_all(" there".as_bytes()).unwrap(); @@ -498,7 +469,7 @@ fn test_write_direct() { } run_test!(get_peer_certificate, |method, stream| { - let stream = SslStream::connect_generic(&SslContext::new(method).unwrap(), stream).unwrap(); + let stream = SslStream::connect(&SslContext::new(method).unwrap(), stream).unwrap(); let cert = stream.ssl().peer_certificate().unwrap(); let fingerprint = cert.fingerprint(SHA1).unwrap(); let node_hash_str = "E19427DAC79FBE758394945276A6E4F15F0BEBE6"; @@ -511,7 +482,7 @@ run_test!(get_peer_certificate, |method, stream| { fn test_write_dtlsv1() { let (_s, stream) = Server::new_dtlsv1(iter::repeat("y\n")); - let mut stream = SslStream::connect_generic(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap(); + let mut stream = SslStream::connect(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap(); stream.write_all(b"hello").unwrap(); stream.flush().unwrap(); stream.write_all(b" there").unwrap(); @@ -521,7 +492,7 @@ fn test_write_dtlsv1() { #[test] fn test_read() { let (_s, tcp) = Server::new(); - let mut stream = SslStream::connect_generic(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); + let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); stream.flush().unwrap(); io::copy(&mut stream, &mut io::sink()).ok().expect("read error"); @@ -539,7 +510,7 @@ fn test_read_direct() { #[test] fn test_pending() { let (_s, tcp) = Server::new(); - let mut stream = SslStream::connect_generic(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); + let mut stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); stream.flush().unwrap(); @@ -562,7 +533,7 @@ fn test_pending() { #[test] fn test_state() { let (_s, tcp) = Server::new(); - let stream = SslStream::connect_generic(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); + let stream = SslStream::connect(&SslContext::new(Sslv23).unwrap(), tcp).unwrap(); assert_eq!(stream.ssl().state_string(), "SSLOK "); assert_eq!(stream.ssl().state_string_long(), "SSL negotiation finished successfully"); @@ -575,7 +546,7 @@ fn test_state() { fn test_connect_with_unilateral_alpn() { let (_s, stream) = Server::new(); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} @@ -597,13 +568,13 @@ fn test_connect_with_unilateral_alpn() { fn test_connect_with_unilateral_npn() { let (_s, stream) = Server::new(); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - let stream = match SslStream::connect_generic(&ctx, stream) { + let stream = match SslStream::connect(&ctx, stream) { Ok(stream) => stream, Err(err) => panic!("Expected success, got {:?}", err), }; @@ -619,7 +590,7 @@ fn test_connect_with_unilateral_npn() { fn test_connect_with_alpn_successful_multiple_matching() { let (_s, stream) = Server::new_alpn(); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"spdy/3.1", b"http/1.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} @@ -641,13 +612,13 @@ fn test_connect_with_alpn_successful_multiple_matching() { fn test_connect_with_npn_successful_multiple_matching() { let (_s, stream) = Server::new_alpn(); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_npn_protocols(&[b"spdy/3.1", b"http/1.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - let stream = match SslStream::connect_generic(&ctx, stream) { + let stream = match SslStream::connect(&ctx, stream) { Ok(stream) => stream, Err(err) => panic!("Expected success, got {:?}", err), }; @@ -664,7 +635,7 @@ fn test_connect_with_npn_successful_multiple_matching() { fn test_connect_with_alpn_successful_single_match() { let (_s, stream) = Server::new_alpn(); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"spdy/3.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} @@ -688,13 +659,13 @@ fn test_connect_with_alpn_successful_single_match() { fn test_connect_with_npn_successful_single_match() { let (_s, stream) = Server::new_alpn(); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_npn_protocols(&[b"spdy/3.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} Err(err) => panic!("Unexpected error {:?}", err), } - let stream = match SslStream::connect_generic(&ctx, stream) { + let stream = match SslStream::connect(&ctx, stream) { Ok(stream) => stream, Err(err) => panic!("Expected success, got {:?}", err), }; @@ -713,7 +684,7 @@ fn test_npn_server_advertise_multiple() { // We create a different context instance for the server... let listener_ctx = { let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_npn_protocols(&[b"http/1.1", b"spdy/3.1"]); assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM) .is_ok()); @@ -728,7 +699,7 @@ fn test_npn_server_advertise_multiple() { }); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_npn_protocols(&[b"spdy/3.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} @@ -736,7 +707,7 @@ fn test_npn_server_advertise_multiple() { } // Now connect to the socket and make sure the protocol negotiation works... let stream = TcpStream::connect(localhost).unwrap(); - let stream = match SslStream::connect_generic(&ctx, stream) { + let stream = match SslStream::connect(&ctx, stream) { Ok(stream) => stream, Err(err) => panic!("Expected success, got {:?}", err), }; @@ -754,7 +725,7 @@ fn test_alpn_server_advertise_multiple() { // We create a different context instance for the server... let listener_ctx = { let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]); assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM) .is_ok()); @@ -769,7 +740,7 @@ fn test_alpn_server_advertise_multiple() { }); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"spdy/3.1"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} @@ -795,7 +766,7 @@ fn test_alpn_server_select_none() { // We create a different context instance for the server... let listener_ctx = { let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"http/1.1", b"spdy/3.1"]); assert!(ctx.set_certificate_file(&Path::new("test/cert.pem"), X509FileType::PEM) .is_ok()); @@ -810,7 +781,7 @@ fn test_alpn_server_select_none() { }); let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); ctx.set_alpn_protocols(&[b"http/2"]); match ctx.set_CA_file(&Path::new("test/cert.pem")) { Ok(_) => {} @@ -838,7 +809,7 @@ mod dtlsv1 { use crypto::hash::Type::SHA256; use ssl::SslMethod; use ssl::SslMethod::Dtlsv1; - use ssl::{SslContext, SslStream, VerifyCallback}; + use ssl::{SslContext, SslStream}; use ssl::SSL_VERIFY_PEER; use x509::X509StoreContext; @@ -855,7 +826,7 @@ mod dtlsv1 { fn test_read_dtlsv1() { let (_s, stream) = Server::new_dtlsv1(Some("hello")); - let mut stream = SslStream::connect_generic(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap(); + let mut stream = SslStream::connect(&SslContext::new(Dtlsv1).unwrap(), stream).unwrap(); let mut buf = [0u8; 100]; assert!(stream.read(&mut buf).is_ok()); } @@ -864,15 +835,15 @@ fn test_read_dtlsv1() { #[cfg(feature = "sslv2")] fn test_sslv2_connect_failure() { let (_s, tcp) = Server::new_tcp(&["-no_ssl2", "-www"]); - SslStream::connect_generic(&SslContext::new(Sslv2).unwrap(), tcp) + SslStream::connect(&SslContext::new(Sslv2).unwrap(), tcp) .err() .unwrap(); } -fn wait_io(stream: &NonblockingSslStream, read: bool, timeout_ms: u32) -> bool { +fn wait_io(stream: &TcpStream, read: bool, timeout_ms: u32) -> bool { unsafe { let mut set: select::fd_set = mem::zeroed(); - select::fd_set(&mut set, stream.get_ref()); + select::fd_set(&mut set, stream); let write = if read { 0 as *mut _ @@ -884,7 +855,19 @@ fn wait_io(stream: &NonblockingSslStream, read: bool, timeout_ms: u32 } else { &mut set as *mut _ }; - select::select(stream.get_ref(), read, write, 0 as *mut _, timeout_ms).unwrap() + select::select(stream, read, write, 0 as *mut _, timeout_ms).unwrap() + } +} + +fn handshake(res: Result, HandshakeError>) + -> SslStream { + match res { + Ok(s) => s, + Err(HandshakeError::Interrupted(s)) => { + wait_io(s.get_ref(), true, 1_000); + handshake(s.handshake()) + } + Err(err) => panic!("error on handshake {:?}", err), } } @@ -893,7 +876,7 @@ fn test_write_nonblocking() { let (_s, stream) = Server::new(); stream.set_nonblocking(true).unwrap(); let cx = SslContext::new(Sslv23).unwrap(); - let mut stream = NonblockingSslStream::connect(&cx, stream).unwrap(); + let mut stream = handshake(SslStream::connect(&cx, stream)); let mut iterations = 0; loop { @@ -903,16 +886,16 @@ fn test_write_nonblocking() { // openssl. panic!("Too many read/write round trips in handshake!!"); } - let result = stream.write(b"hello"); + let result = stream.ssl_write(b"hello"); match result { Ok(_) => { break; } - Err(NonblockingSslError::WantRead) => { - assert!(wait_io(&stream, true, 1000)); + Err(Error::WantRead(_)) => { + assert!(wait_io(stream.get_ref(), true, 1000)); } - Err(NonblockingSslError::WantWrite) => { - assert!(wait_io(&stream, false, 1000)); + Err(Error::WantWrite(_)) => { + assert!(wait_io(stream.get_ref(), false, 1000)); } Err(other) => { panic!("Unexpected SSL Error: {:?}", other); @@ -930,7 +913,7 @@ fn test_read_nonblocking() { let (_s, stream) = Server::new(); stream.set_nonblocking(true).unwrap(); let cx = SslContext::new(Sslv23).unwrap(); - let mut stream = NonblockingSslStream::connect(&cx, stream).unwrap(); + let mut stream = handshake(SslStream::connect(&cx, stream)); let mut iterations = 0; loop { @@ -940,17 +923,17 @@ fn test_read_nonblocking() { // openssl. panic!("Too many read/write round trips in handshake!!"); } - let result = stream.write(b"GET /\r\n\r\n"); + let result = stream.ssl_write(b"GET /\r\n\r\n"); match result { Ok(n) => { assert_eq!(n, 9); break; } - Err(NonblockingSslError::WantRead) => { - assert!(wait_io(&stream, true, 1000)); + Err(Error::WantRead(..)) => { + assert!(wait_io(stream.get_ref(), true, 1000)); } - Err(NonblockingSslError::WantWrite) => { - assert!(wait_io(&stream, false, 1000)); + Err(Error::WantWrite(..)) => { + assert!(wait_io(stream.get_ref(), false, 1000)); } Err(other) => { panic!("Unexpected SSL Error: {:?}", other); @@ -958,7 +941,7 @@ fn test_read_nonblocking() { } } let mut input_buffer = [0u8; 1500]; - let result = stream.read(&mut input_buffer); + let result = stream.ssl_read(&mut input_buffer); let bytes_read = match result { Ok(n) => { // This branch is unlikely, but on an overloaded VM with @@ -966,8 +949,8 @@ fn test_read_nonblocking() { // be in the receive buffer before we issue the read() syscall... n } - Err(NonblockingSslError::WantRead) => { - assert!(wait_io(&stream, true, 3000)); + Err(Error::WantRead(..)) => { + assert!(wait_io(stream.get_ref(), true, 3000)); // Second read should return application data. stream.read(&mut input_buffer).unwrap() } @@ -979,14 +962,6 @@ fn test_read_nonblocking() { assert_eq!(&input_buffer[..5], b"HTTP/"); } -#[test] -fn broken_try_clone_doesnt_crash() { - let context = SslContext::new(SslMethod::Sslv23).unwrap(); - let inner = TcpStream::connect("example.com:443").unwrap(); - let stream1 = SslStream::connect(&context, inner).unwrap(); - let _stream2 = stream1.try_clone().unwrap(); -} - #[test] #[should_panic(expected = "blammo")] #[cfg(feature = "nightly")] @@ -1093,7 +1068,7 @@ fn refcount_ssl_context() { fn default_verify_paths() { let mut ctx = SslContext::new(SslMethod::Sslv23).unwrap(); ctx.set_default_verify_paths().unwrap(); - ctx.set_verify(SSL_VERIFY_PEER, None); + ctx.set_verify(SSL_VERIFY_PEER); let s = TcpStream::connect("google.com:443").unwrap(); let mut socket = SslStream::connect(&ctx, s).unwrap(); diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index c9d1772d..64a61df0 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -1,6 +1,4 @@ use libc::{c_char, c_int, c_long, c_ulong, c_uint, c_void}; -use std::io; -use std::io::prelude::*; use std::cmp::Ordering; use std::ffi::CString; use std::iter::repeat; @@ -14,15 +12,15 @@ use std::collections::HashMap; use std::marker::PhantomData; use asn1::Asn1Time; -use bio::MemBio; +use bio::{MemBio, MemBioSlice}; use crypto::hash; use crypto::hash::Type as HashType; use crypto::pkey::{PKey, Parts}; use crypto::rand::rand_bytes; use ffi; use ffi_extras; -use ssl::error::{SslError, StreamError}; use nid::Nid; +use error::ErrorStack; pub mod extension; @@ -116,13 +114,6 @@ impl X509StoreContext { /// # Example /// /// ``` -/// # #[allow(unstable)] -/// # fn main() { -/// use std::fs; -/// use std::fs::File; -/// use std::io::prelude::*; -/// use std::path::Path; -/// /// use openssl::crypto::hash::Type; /// use openssl::x509::X509Generator; /// use openssl::x509::extension::{Extension, KeyUsageOption}; @@ -135,17 +126,8 @@ impl X509StoreContext { /// .add_extension(Extension::KeyUsage(vec![KeyUsageOption::DigitalSignature])); /// /// let (cert, pkey) = gen.generate().unwrap(); -/// -/// let cert_path = "doc_cert.pem"; -/// let mut file = File::create(cert_path).unwrap(); -/// assert!(cert.write_pem(&mut file).is_ok()); -/// # let _ = fs::remove_file(cert_path); -/// -/// let pkey_path = "doc_key.pem"; -/// let mut file = File::create(pkey_path).unwrap(); -/// assert!(pkey.write_pem(&mut file).is_ok()); -/// # let _ = fs::remove_file(pkey_path); -/// # } +/// let cert_pem = cert.write_pem().unwrap(); +/// let pkey_pem = pkey.write_pem().unwrap(); /// ``` pub struct X509Generator { bits: u32, @@ -256,7 +238,7 @@ impl X509Generator { fn add_extension_internal(x509: *mut ffi::X509, exttype: &extension::ExtensionType, value: &str) - -> Result<(), SslError> { + -> Result<(), ErrorStack> { unsafe { let mut ctx: ffi::X509V3_CTX = mem::zeroed(); ffi::X509V3_set_ctx(&mut ctx, x509, x509, ptr::null_mut(), ptr::null_mut(), 0); @@ -288,7 +270,7 @@ impl X509Generator { fn add_name_internal(name: *mut ffi::X509_NAME, key: &str, value: &str) - -> Result<(), SslError> { + -> Result<(), ErrorStack> { let value_len = value.len() as c_int; lift_ssl!(unsafe { let key = CString::new(key.as_bytes()).unwrap(); @@ -319,7 +301,7 @@ impl X509Generator { } /// Generates a private key and a self-signed certificate and returns them - pub fn generate<'a>(&self) -> Result<(X509<'a>, PKey), SslError> { + pub fn generate<'a>(&self) -> Result<(X509<'a>, PKey), ErrorStack> { ffi::init(); let mut p_key = PKey::new(); @@ -331,7 +313,7 @@ impl X509Generator { /// Sets the certificate public-key, then self-sign and return it /// Note: That the bit-length of the private key is used (set_bitlength is ignored) - pub fn sign<'a>(&self, p_key: &PKey) -> Result, SslError> { + pub fn sign<'a>(&self, p_key: &PKey) -> Result, ErrorStack> { ffi::init(); unsafe { @@ -391,7 +373,7 @@ impl X509Generator { } /// Obtain a certificate signing request (CSR) - pub fn request(&self, p_key: &PKey) -> Result { + pub fn request(&self, p_key: &PKey) -> Result { let cert = match self.sign(p_key) { Ok(c) => c, Err(x) => return Err(x), @@ -444,12 +426,8 @@ impl<'ctx> X509<'ctx> { } /// Reads certificate from PEM, takes ownership of handle - pub fn from_pem(reader: &mut R) -> Result, SslError> - where R: Read - { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); - + pub fn from_pem(buf: &[u8]) -> Result, ErrorStack> { + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { let handle = try_ssl_null!(ffi::PEM_read_bio_X509(mem_bio.get_handle(), ptr::null_mut(), @@ -523,25 +501,21 @@ impl<'ctx> X509<'ctx> { } /// Writes certificate as PEM - pub fn write_pem(&self, writer: &mut W) -> Result<(), SslError> - where W: Write - { - let mut mem_bio = try!(MemBio::new()); + pub fn write_pem(&self) -> Result, ErrorStack> { + let mem_bio = try!(MemBio::new()); unsafe { try_ssl!(ffi::PEM_write_bio_X509(mem_bio.get_handle(), self.handle)); } - io::copy(&mut mem_bio, writer).map_err(StreamError).map(|_| ()) + Ok(mem_bio.get_buf().to_owned()) } /// Returns a DER serialized form of the certificate - pub fn save_der(&self) -> Result, SslError> { - let mut mem_bio = try!(MemBio::new()); + pub fn save_der(&self) -> Result, ErrorStack> { + let mem_bio = try!(MemBio::new()); unsafe { ffi::i2d_X509_bio(mem_bio.get_handle(), self.handle); } - let mut v = Vec::new(); - try!(io::copy(&mut mem_bio, &mut v).map_err(StreamError)); - Ok(v) + Ok(mem_bio.get_buf().to_owned()) } } @@ -627,12 +601,8 @@ impl X509Req { } /// Reads CSR from PEM - pub fn from_pem(reader: &mut R) -> Result - where R: Read - { - let mut mem_bio = try!(MemBio::new()); - try!(io::copy(reader, &mut mem_bio).map_err(StreamError)); - + pub fn from_pem(buf: &[u8]) -> Result { + let mem_bio = try!(MemBioSlice::new(buf)); unsafe { let handle = try_ssl_null!(ffi::PEM_read_bio_X509_REQ(mem_bio.get_handle(), ptr::null_mut(), @@ -643,25 +613,21 @@ impl X509Req { } /// Writes CSR as PEM - pub fn write_pem(&self, writer: &mut W) -> Result<(), SslError> - where W: Write - { - let mut mem_bio = try!(MemBio::new()); - unsafe { - try_ssl!(ffi::PEM_write_bio_X509_REQ(mem_bio.get_handle(), self.handle)); + pub fn write_pem(&self) -> Result, ErrorStack> { + let mem_bio = try!(MemBio::new()); + if unsafe { ffi::PEM_write_bio_X509_REQ(mem_bio.get_handle(), self.handle) } != 1 { + return Err(ErrorStack::get()); } - io::copy(&mut mem_bio, writer).map_err(StreamError).map(|_| ()) + Ok(mem_bio.get_buf().to_owned()) } /// Returns a DER serialized form of the CSR - pub fn save_der(&self) -> Result, SslError> { - let mut mem_bio = try!(MemBio::new()); + pub fn save_der(&self) -> Result, ErrorStack> { + let mem_bio = try!(MemBio::new()); unsafe { ffi::i2d_X509_REQ_bio(mem_bio.get_handle(), self.handle); } - let mut v = Vec::new(); - try!(io::copy(&mut mem_bio, &mut v).map_err(StreamError)); - Ok(v) + Ok(mem_bio.get_buf().to_owned()) } } diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index 5d9b30ab..167ca8cf 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -1,7 +1,4 @@ use serialize::hex::FromHex; -use std::io; -use std::path::Path; -use std::fs::File; use crypto::hash::Type::SHA1; use crypto::pkey::PKey; @@ -30,8 +27,8 @@ fn get_generator() -> X509Generator { #[test] fn test_cert_gen() { let (cert, pkey) = get_generator().generate().unwrap(); - cert.write_pem(&mut io::sink()).unwrap(); - pkey.write_pem(&mut io::sink()).unwrap(); + cert.write_pem().unwrap(); + pkey.write_pem().unwrap(); // FIXME: check data in result to be correct, needs implementation // of X509 getters @@ -70,7 +67,7 @@ fn test_req_gen() { pkey.gen(512); let req = get_generator().request(&pkey).unwrap(); - req.write_pem(&mut io::sink()).unwrap(); + req.write_pem().unwrap(); // FIXME: check data in result to be correct, needs implementation // of X509_REQ getters @@ -78,12 +75,8 @@ fn test_req_gen() { #[test] fn test_cert_loading() { - let cert_path = Path::new("test/cert.pem"); - let mut file = File::open(&cert_path) - .ok() - .expect("Failed to open `test/cert.pem`"); - - let cert = X509::from_pem(&mut file).ok().expect("Failed to load PEM"); + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).ok().expect("Failed to load PEM"); let fingerprint = cert.fingerprint(SHA1).unwrap(); let hash_str = "E19427DAC79FBE758394945276A6E4F15F0BEBE6"; @@ -94,12 +87,8 @@ fn test_cert_loading() { #[test] fn test_save_der() { - let cert_path = Path::new("test/cert.pem"); - let mut file = File::open(&cert_path) - .ok() - .expect("Failed to open `test/cert.pem`"); - - let cert = X509::from_pem(&mut file).ok().expect("Failed to load PEM"); + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).ok().expect("Failed to load PEM"); let der = cert.save_der().unwrap(); assert!(!der.is_empty()); @@ -107,12 +96,8 @@ fn test_save_der() { #[test] fn test_subject_read_cn() { - let cert_path = Path::new("test/cert.pem"); - let mut file = File::open(&cert_path) - .ok() - .expect("Failed to open `test/cert.pem`"); - - let cert = X509::from_pem(&mut file).ok().expect("Failed to load PEM"); + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).ok().expect("Failed to load PEM"); let subject = cert.subject_name(); let cn = match subject.text_by_nid(Nid::CN) { Some(x) => x, @@ -124,12 +109,8 @@ fn test_subject_read_cn() { #[test] fn test_nid_values() { - let cert_path = Path::new("test/nid_test_cert.pem"); - let mut file = File::open(&cert_path) - .ok() - .expect("Failed to open `test/nid_test_cert.pem`"); - - let cert = X509::from_pem(&mut file).ok().expect("Failed to load PEM"); + let cert = include_bytes!("../../test/nid_test_cert.pem"); + let cert = X509::from_pem(cert).ok().expect("Failed to load PEM"); let subject = cert.subject_name(); let cn = match subject.text_by_nid(Nid::CN) { @@ -153,12 +134,8 @@ fn test_nid_values() { #[test] fn test_nid_uid_value() { - let cert_path = Path::new("test/nid_uid_test_cert.pem"); - let mut file = File::open(&cert_path) - .ok() - .expect("Failed to open `test/nid_uid_test_cert.pem`"); - - let cert = X509::from_pem(&mut file).ok().expect("Failed to load PEM"); + let cert = include_bytes!("../../test/nid_uid_test_cert.pem"); + let cert = X509::from_pem(cert).ok().expect("Failed to load PEM"); let subject = cert.subject_name(); let cn = match subject.text_by_nid(Nid::UserId) { @@ -170,8 +147,8 @@ fn test_nid_uid_value() { #[test] fn test_subject_alt_name() { - let mut file = File::open("test/alt_name_cert.pem").unwrap(); - let cert = X509::from_pem(&mut file).unwrap(); + let cert = include_bytes!("../../test/alt_name_cert.pem"); + let cert = X509::from_pem(cert).ok().expect("Failed to load PEM"); let subject_alt_names = cert.subject_alt_names().unwrap(); assert_eq!(3, subject_alt_names.len()); @@ -184,8 +161,8 @@ fn test_subject_alt_name() { #[test] fn test_subject_alt_name_iter() { - let mut file = File::open("test/alt_name_cert.pem").unwrap(); - let cert = X509::from_pem(&mut file).unwrap(); + let cert = include_bytes!("../../test/alt_name_cert.pem"); + let cert = X509::from_pem(cert).ok().expect("Failed to load PEM"); let subject_alt_names = cert.subject_alt_names().unwrap(); let mut subject_alt_names_iter = subject_alt_names.iter(); diff --git a/openssl/test/run.sh b/openssl/test/run.sh index 85603773..cbd46cc6 100755 --- a/openssl/test/run.sh +++ b/openssl/test/run.sh @@ -7,10 +7,6 @@ if [ "$TEST_FEATURES" == "true" ]; then FEATURES="tlsv1_2 tlsv1_1 dtlsv1 dtlsv1_2 sslv3 aes_xts aes_ctr npn alpn rfc5114 ecdh_auto pkcs5_pbkdf2_hmac" fi -if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then - FEATURES="$FEATURES nightly catch_unwind" -fi - if [ "$TRAVIS_OS_NAME" != "osx" ]; then export OPENSSL_LIB_DIR=$HOME/openssl/lib export OPENSSL_INCLUDE_DIR=$HOME/openssl/include