From 79a8ebb631e8c6225497cd4e4396ef843c4aa802 Mon Sep 17 00:00:00 2001 From: Chiu Yue Chun Date: Tue, 3 Oct 2017 23:59:16 +0800 Subject: [PATCH 01/15] Add examples and more documentation to symm --- openssl/src/symm.rs | 124 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 119 insertions(+), 5 deletions(-) diff --git a/openssl/src/symm.rs b/openssl/src/symm.rs index f593126a..41033a16 100644 --- a/openssl/src/symm.rs +++ b/openssl/src/symm.rs @@ -1,3 +1,4 @@ +//! High level interface to certain symmetric ciphers. use std::cmp; use std::ptr; use libc::c_int; @@ -12,6 +13,10 @@ pub enum Mode { Decrypt, } +/// Represent a particular cipher algorithm. See OpenSSL doc at [`EVP_EncryptInit`] for more +/// information on each algorithms. +/// +/// [`EVP_EncryptInit`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_EncryptInit.html #[derive(Copy, Clone)] pub struct Cipher(*const ffi::EVP_CIPHER); @@ -152,7 +157,64 @@ impl Cipher { } } -/// Represents a symmetric cipher context. +/// Represents a symmetric cipher context. Padding is enabled by default. +/// +/// # Examples +/// +/// Encrypt some plaintext in chunks, then decrypt the ciphertext back into plaintext, in AES 128 +/// CBC mode. +/// +/// ``` +/// use openssl::symm::{Cipher, Mode, Crypter}; +/// +/// let plaintexts: [&[u8]; 2] = [b"Some Stream of", b" Crypto Text"]; +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +/// let data_len = plaintexts.iter().fold(0, |sum, x| sum + x.len()); +/// +/// // Create a cipher context for encryption. +/// let mut encrypter = Crypter::new( +/// Cipher::aes_128_cbc(), +/// Mode::Encrypt, +/// key, +/// Some(iv)).unwrap(); +/// +/// let block_size = Cipher::aes_128_cbc().block_size(); +/// let mut ciphertext = vec![0; data_len + block_size]; +/// +/// // Encrypt 2 chunks of plaintexts successively. +/// let mut count = encrypter.update(plaintexts[0], &mut ciphertext).unwrap(); +/// count += encrypter.update(plaintexts[1], &mut ciphertext[count..]).unwrap(); +/// count += encrypter.finalize(&mut ciphertext[count..]).unwrap(); +/// ciphertext.truncate(count); +/// +/// assert_eq!( +/// b"\x0F\x21\x83\x7E\xB2\x88\x04\xAF\xD9\xCC\xE2\x03\x49\xB4\x88\xF6\xC4\x61\x0E\x32\x1C\xF9\ +/// \x0D\x66\xB1\xE6\x2C\x77\x76\x18\x8D\x99", +/// &ciphertext[..] +/// ); +/// +/// +/// // Let's pretend we don't know the plaintext, and now decrypt the ciphertext. +/// let data_len = ciphertext.len(); +/// let ciphertexts = [&ciphertext[..9], &ciphertext[9..]]; +/// +/// // Create a cipher context for decryption. +/// let mut decrypter = Crypter::new( +/// Cipher::aes_128_cbc(), +/// Mode::Decrypt, +/// key, +/// Some(iv)).unwrap(); +/// let mut plaintext = vec![0; data_len + block_size]; +/// +/// // Decrypt 2 chunks of ciphertexts successively. +/// let mut count = decrypter.update(ciphertexts[0], &mut plaintext).unwrap(); +/// count += decrypter.update(ciphertexts[1], &mut plaintext[count..]).unwrap(); +/// count += decrypter.finalize(&mut plaintext[count..]).unwrap(); +/// plaintext.truncate(count); +/// +/// assert_eq!(b"Some Stream of Crypto Text", &plaintext[..]); +/// ``` pub struct Crypter { ctx: *mut ffi::EVP_CIPHER_CTX, block_size: usize, @@ -358,8 +420,34 @@ impl Drop for Crypter { } } -/// Encrypts data, using the specified crypter type in encrypt mode with the -/// specified key and iv; returns the resulting (encrypted) data. +/// A convenient interface to `Crypter` to encrypt data, using the specified crypter type `t` in +/// encrypt mode with the specified key and iv; returns the resulting (encrypted) data. +/// +/// This function encrypts all data in one go. To encrypt a stream of data increamentally , use +/// `Crypter` instead. +/// +/// # Examples +/// +/// Encrypt data in AES128 CBC mode +/// +/// ``` +/// use openssl::symm::{encrypt, Cipher}; +/// +/// let cipher = Cipher::aes_128_cbc(); +/// let data = b"Some Crypto Text"; +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +/// let ciphertext = encrypt( +/// cipher, +/// key, +/// Some(iv), +/// data).unwrap(); +/// +/// assert_eq!( +/// b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\x87\x4D\ +/// \xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1", +/// &ciphertext[..]); +/// ``` pub fn encrypt( t: Cipher, key: &[u8], @@ -369,8 +457,34 @@ pub fn encrypt( cipher(t, Mode::Encrypt, key, iv, data) } -/// Decrypts data, using the specified crypter type in decrypt mode with the -/// specified key and iv; returns the resulting (decrypted) data. +/// A convenient interface to `Crypter` to decrypt data, using the specified crypter type `t` in +/// decrypt mode with the specified key and iv; returns the resulting (decrypted) data. +/// +/// This function decrypts all data in one go. To decrypt a stream of data increamentally, use +/// `Crypter` instead. +/// +/// # Examples +/// +/// Decrypt data in AES256 ECB mode +/// +/// ``` +/// use openssl::symm::{decrypt, Cipher}; +/// +/// let cipher = Cipher::aes_128_cbc(); +/// let data = b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\ +/// \x87\x4D\xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1"; +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +/// let ciphertext = decrypt( +/// cipher, +/// key, +/// Some(iv), +/// data).unwrap(); +/// +/// assert_eq!( +/// b"Some Crypto Text", +/// &ciphertext[..]); +/// ``` pub fn decrypt( t: Cipher, key: &[u8], From 76ecc13cec84e495edbb500811a3a476147b1e47 Mon Sep 17 00:00:00 2001 From: Chiu Yue Chun Date: Wed, 4 Oct 2017 00:05:30 +0800 Subject: [PATCH 02/15] Copy example to module level --- openssl/src/symm.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/openssl/src/symm.rs b/openssl/src/symm.rs index 41033a16..ee1b0621 100644 --- a/openssl/src/symm.rs +++ b/openssl/src/symm.rs @@ -1,4 +1,27 @@ //! High level interface to certain symmetric ciphers. +//! +//! # Examples +//! +//! Encrypt data in AES128 CBC mode +//! +//! ``` +//! use openssl::symm::{encrypt, Cipher}; +//! +//! let cipher = Cipher::aes_128_cbc(); +//! let data = b"Some Crypto Text"; +//! let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +//! let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +//! let ciphertext = encrypt( +//! cipher, +//! key, +//! Some(iv), +//! data).unwrap(); +//! +//! assert_eq!( +//! b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\x87\x4D\ +//! \xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1", +//! &ciphertext[..]); +//! ``` use std::cmp; use std::ptr; use libc::c_int; From ff53750cab6b6e20a3e681c3bf5ce0883d7102c9 Mon Sep 17 00:00:00 2001 From: Chiu Yue Chun Date: Wed, 4 Oct 2017 00:28:32 +0800 Subject: [PATCH 03/15] Additional notes on Crypter --- openssl/src/symm.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openssl/src/symm.rs b/openssl/src/symm.rs index ee1b0621..14ee392b 100644 --- a/openssl/src/symm.rs +++ b/openssl/src/symm.rs @@ -244,11 +244,13 @@ pub struct Crypter { } impl Crypter { - /// Creates a new `Crypter`. + /// Creates a new `Crypter`. The initialisation vector, `iv`, is not necesarry for certain + /// types of `Cipher`. /// /// # Panics /// - /// Panics if an IV is required by the cipher but not provided. + /// Panics if an IV is required by the cipher but not provided. Also make sure that the key + /// and IV size are appropriate for your cipher. pub fn new( t: Cipher, mode: Mode, From 75e6db6f00e1ee62f8b9f8d74e360696f367a76d Mon Sep 17 00:00:00 2001 From: BrianOn99 Date: Mon, 9 Oct 2017 11:14:27 +0800 Subject: [PATCH 04/15] Move doc details into another paragraph --- openssl/src/symm.rs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/openssl/src/symm.rs b/openssl/src/symm.rs index 14ee392b..39e0af66 100644 --- a/openssl/src/symm.rs +++ b/openssl/src/symm.rs @@ -36,8 +36,9 @@ pub enum Mode { Decrypt, } -/// Represent a particular cipher algorithm. See OpenSSL doc at [`EVP_EncryptInit`] for more -/// information on each algorithms. +/// Represents a particular cipher algorithm. +/// +/// See OpenSSL doc at [`EVP_EncryptInit`] for more information on each algorithms. /// /// [`EVP_EncryptInit`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_EncryptInit.html #[derive(Copy, Clone)] @@ -180,7 +181,9 @@ impl Cipher { } } -/// Represents a symmetric cipher context. Padding is enabled by default. +/// Represents a symmetric cipher context. +/// +/// Padding is enabled by default. /// /// # Examples /// @@ -445,11 +448,13 @@ impl Drop for Crypter { } } -/// A convenient interface to `Crypter` to encrypt data, using the specified crypter type `t` in -/// encrypt mode with the specified key and iv; returns the resulting (encrypted) data. +/// Encrypts data in one go, and returns the encrypted data. /// -/// This function encrypts all data in one go. To encrypt a stream of data increamentally , use -/// `Crypter` instead. +/// Data is encrypted using the specified cipher type `t` in encrypt mode with the specified `key` +/// and initailization vector `iv`. +/// +/// This is a convenient interface to `Crypter` to encrypt all data in one go. To encrypt a stream +/// of data increamentally , use `Crypter` instead. /// /// # Examples /// @@ -482,11 +487,13 @@ pub fn encrypt( cipher(t, Mode::Encrypt, key, iv, data) } -/// A convenient interface to `Crypter` to decrypt data, using the specified crypter type `t` in -/// decrypt mode with the specified key and iv; returns the resulting (decrypted) data. +/// Decrypts data in one go, and returns the decrypted data. /// -/// This function decrypts all data in one go. To decrypt a stream of data increamentally, use -/// `Crypter` instead. +/// Data is decrypted using the specified cipher type `t` in decrypt mode with the specified `key` +/// and initailization vector `iv`. +/// +/// This is a convenient interface to `Crypter` to decrypt all data in one go. To decrypt a stream +/// of data increamentally , use `Crypter` instead. /// /// # Examples /// From a62069cef90737558d14d440b0e14748f8b141fe Mon Sep 17 00:00:00 2001 From: Andy Gauge Date: Wed, 11 Oct 2017 13:04:53 -0700 Subject: [PATCH 05/15] Began EC documenation --- openssl/src/ec.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index 02be6d22..c6012050 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -1,3 +1,13 @@ +//! Elliptic Curve +//! +//! Cryptology relies on the difficulty of solving mathematical problems, such as the factor +//! of large integers composed of two large prime numbers and the discrete logarithm of a +//! random eliptic curve. This module provides low-level features of the latter. +//! Elliptic Curve protocols can provide the same security with smaller keys. +//! +//! There are 2 forms of elliptic curves, `Fp` and `F2^m`. These curves use irreducible +//! trinomial or pentanomial. Being a generic interface to a wide range of algorithms, +//! the cuves are generally referenced by [`EcGroup`] use ffi; use foreign_types::{ForeignType, ForeignTypeRef}; use std::ptr; From d5299a8d2b73b8351feacc0ca6e946aec7863751 Mon Sep 17 00:00:00 2001 From: Brian Vincent Date: Tue, 17 Oct 2017 20:06:35 -0500 Subject: [PATCH 06/15] Fixed a typo in an error message, WANT_WRITE -> WANT_READ --- openssl/src/ssl/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 01b49cb8..5a924a64 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -1805,7 +1805,7 @@ impl SslStream { None => { io::Error::new( io::ErrorKind::Other, - "BUG: got an SSL_ERROR_WANT_WRITE with no error in the BIO", + "BUG: got an SSL_ERROR_WANT_READ with no error in the BIO", ) } }; From df10bcf9605ef6b4aeb48d796f27cfc540d5cb86 Mon Sep 17 00:00:00 2001 From: Andy Gauge Date: Tue, 24 Oct 2017 16:43:01 -0700 Subject: [PATCH 07/15] Update documentation for EC module --- openssl/src/ec.rs | 210 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 204 insertions(+), 6 deletions(-) diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index c6012050..38c412fc 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -6,8 +6,31 @@ //! Elliptic Curve protocols can provide the same security with smaller keys. //! //! There are 2 forms of elliptic curves, `Fp` and `F2^m`. These curves use irreducible -//! trinomial or pentanomial. Being a generic interface to a wide range of algorithms, -//! the cuves are generally referenced by [`EcGroup`] +//! trinomial or pentanomial . Being a generic interface to a wide range of algorithms, +//! the cuves are generally referenced by [`EcGroup`]. There are many built in groups +//! found in [`Nid`]. +//! +//! OpenSSL Wiki explains the fields and curves in detail at [Eliptic Curve Cryptography]. +//! +//! [`EcGroup`]: struct.EcGroup.html +//! [`Nid`]: ../nid/struct.Nid.html +//! [Eliptic Curve Cryptography]: https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography +//! +//! # Examples +//! +//! ``` +//! use openssl::ec::{EcGroup, EcPoint}; +//! use openssl::nid; +//! use openssl::error::ErrorStack; +//! fn get_ec_point() -> Result< EcPoint, ErrorStack > { +//! let group = EcGroup::from_curve_name(nid::SECP224R1)?; +//! let point = EcPoint::new(&group)?; +//! Ok(point) +//! } +//! # fn main() { +//! # let _ = get_ec_point(); +//! # } +//! ``` use ffi; use foreign_types::{ForeignType, ForeignTypeRef}; use std::ptr; @@ -19,23 +42,59 @@ use bn::{BigNumRef, BigNumContextRef}; use error::ErrorStack; use nid::Nid; +/// Compressed conversion from point value (Default) pub const POINT_CONVERSION_COMPRESSED: PointConversionForm = PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_COMPRESSED); +/// Uncompressed conversion from point value (Binary curve default) pub const POINT_CONVERSION_UNCOMPRESSED: PointConversionForm = PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED); +/// Performs both compressed and uncompressed conversions pub const POINT_CONVERSION_HYBRID: PointConversionForm = PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_HYBRID); -// OPENSSL_EC_EXPLICIT_CURVE, but that was only added in 1.1. -// Man page documents that 0 can be used in older versions. +/// Curve defined using polynomial parameters +/// +/// Most applications use a named EC_GROUP curve, however, support +/// is included to explicitly define the curve used to calculate keys +/// This information would need to be known by both endpoint to make communication +/// effective. +/// +/// OPENSSL_EC_EXPLICIT_CURVE, but that was only added in 1.1. +/// Man page documents that 0 can be used in older versions. +/// +/// OpenSSL documentation at [`EC_GROUP`] +/// +/// [`EC_GROUP`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_seed_len.html pub const EXPLICIT_CURVE: Asn1Flag = Asn1Flag(0); + +/// Standard Curves +/// +/// Curves that make up the typical encryption use cases. The collection of curves +/// are well known but extensible. +/// +/// OpenSSL documentation at [`EC_GROUP`] +/// +/// [`EC_GROUP`]: https://www.openssl.org/docs/manmaster/man3/EC_GROUP_order_bits.html pub const NAMED_CURVE: Asn1Flag = Asn1Flag(ffi::OPENSSL_EC_NAMED_CURVE); +/// Compressed or Uncompressed conversion +/// +/// Conversion from the binary value of the point on the curve is performed in one of +/// compressed, uncompressed, or hybrid conversions. The default is compressed, except +/// for binary curves. +/// +/// Further documentation is available in the [X9.62] standard. +/// +/// [X9.62]: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf #[derive(Copy, Clone)] pub struct PointConversionForm(ffi::point_conversion_form_t); +/// Named Curve or Explicit +/// +/// This type acts as a boolean as to whether the EC_Group is named or +/// explicit. #[derive(Copy, Clone)] pub struct Asn1Flag(c_int); @@ -43,12 +102,35 @@ foreign_type! { type CType = ffi::EC_GROUP; fn drop = ffi::EC_GROUP_free; + /// Describes the curve + /// + /// A curve can be of the named curve type. These curves can be discovered + /// using openssl binary `openssl ecparam -list_curves`. Other operations + /// are available in the [wiki]. These named curves are available in the + /// [`Nid`] module. + /// + /// Curves can also be generated using prime field parameters or a binary field. + /// + /// Prime fields use the formula `y^2 mod p = x^3 + ax + b mod p`. Binary + /// fields use the formula `y^2 + xy = x^3 + ax^2 + b`. Named curves have + /// assured security. To prevent accidental vulnerabilities, they should + /// be prefered. + /// + /// [wiki]: https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations + /// [`Nid`]: ../nid/index.html pub struct EcGroup; + /// Reference to [`EcGroup`] + /// + /// [`EcGroup`]: struct.EcGroup.html pub struct EcGroupRef; } impl EcGroup { /// Returns the group of a standard named curve. + /// + /// OpenSSL documentation at [`EC_GROUP_new`]. + /// + /// [`EC_GROUP_new`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_new.html pub fn from_curve_name(nid: Nid) -> Result { unsafe { init(); @@ -59,6 +141,11 @@ impl EcGroup { impl EcGroupRef { /// Places the components of a curve over a prime field in the provided `BigNum`s. + /// The components make up the formula `y^2 mod p = x^3 + ax + b mod p`. + /// + /// OpenSSL documentation available at [`EC_GROUP_get_curve_GFp`] + /// + /// [`EC_GROUP_get_curve_GFp`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_curve_GFp.html pub fn components_gfp( &self, p: &mut BigNumRef, @@ -78,6 +165,15 @@ impl EcGroupRef { } /// Places the components of a curve over a binary field in the provided `BigNum`s. + /// The components make up the formula `y^2 + xy = x^3 + ax^2 + b`. + /// + /// In this form `p` relates to the irreducible polynomial. Each bit represents + /// a term in the polynomial. It will be set to 3 `1`s or 5 `1`s depending on + /// using a trinomial or pentanomial. + /// + /// OpenSSL documentation at [`EC_GROUP_get_curve_GF2m`]. + /// + /// [`EC_GROUP_get_curve_GF2m`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_curve_GF2m.html #[cfg(not(osslconf = "OPENSSL_NO_EC2M"))] pub fn components_gf2m( &self, @@ -98,11 +194,19 @@ impl EcGroupRef { } /// Returns the degree of the curve. + /// + /// OpenSSL documentation at [`EC_GROUP_get_degree`] + /// + /// [`EC_GROUP_get_degree`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_degree.html pub fn degree(&self) -> u32 { unsafe { ffi::EC_GROUP_get_degree(self.as_ptr()) as u32 } } /// Places the order of the curve in the provided `BigNum`. + /// + /// OpenSSL documentation at [`EC_GROUP_get_order`] + /// + /// [`EC_GROUP_get_order`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_order.html pub fn order( &self, order: &mut BigNumRef, @@ -133,12 +237,24 @@ foreign_type! { type CType = ffi::EC_POINT; fn drop = ffi::EC_POINT_free; + /// Represents a point on the curve + /// + /// OpenSSL documentation at [`EC_POINT_new`] + /// + /// [`EC_POINT_new`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_new.html pub struct EcPoint; + /// Reference to [`EcPoint`] + /// + /// [`EcPoint`]: struct.EcPoint.html pub struct EcPointRef; } impl EcPointRef { /// Computes `a + b`, storing the result in `self`. + /// + /// OpenSSL documentation at [`EC_POINT_add`] + /// + /// [`EC_POINT_add`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_add.html pub fn add( &mut self, group: &EcGroupRef, @@ -158,6 +274,10 @@ impl EcPointRef { } /// Computes `q * m`, storing the result in `self`. + /// + /// OpenSSL documentation at [`EC_POINT_mul`] + /// + /// [`EC_POINT_mul`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_mul.html pub fn mul( &mut self, group: &EcGroupRef, @@ -218,6 +338,10 @@ impl EcPointRef { } /// Inverts `self`. + /// + /// OpenSSL documentation at [`EC_POINT_invert`] + /// + /// [`EC_POINT_invert`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_invert.html pub fn invert(&mut self, group: &EcGroupRef, ctx: &BigNumContextRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::EC_POINT_invert( @@ -229,6 +353,10 @@ impl EcPointRef { } /// Serializes the point to a binary representation. + /// + /// OpenSSL documentation at [`EC_POINT_point2oct`] + /// + /// [`EC_POINT_point2oct`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_point2oct.html pub fn to_bytes( &self, group: &EcGroupRef, @@ -265,6 +393,10 @@ impl EcPointRef { } /// Determines if this point is equal to another. + /// + /// OpenSSL doucmentation at [`EC_POINT_cmp`] + /// + /// [`EC_POINT_cmp`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_cmp.html pub fn eq( &self, group: &EcGroupRef, @@ -282,7 +414,12 @@ impl EcPointRef { } } - /// Place affine coordinates of a curve over a prime field in the provided x and y BigNum's + /// Place affine coordinates of a curve over a prime field in the provided + /// `x` and `y` `BigNum`s + /// + /// OpenSSL documentation at [`EC_POINT_get_affine_coordinates_GFp`] + /// + /// [`EC_POINT_get_affine_coordinates_GFp`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_get_affine_coordinates_GFp.html pub fn affine_coordinates_gfp( &self, group: &EcGroupRef, @@ -301,7 +438,12 @@ impl EcPointRef { } } - /// Place affine coordinates of a curve over a binary field in the provided x and y BigNum's + /// Place affine coordinates of a curve over a binary field in the provided + /// `x` and `y` `BigNum`s + /// + /// OpenSSL documentation at [`EC_POINT_get_affine_coordinates_GF2m`] + /// + /// [`EC_POINT_get_affine_coordinates_GF2m`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_get_affine_coordinates_GF2m.html #[cfg(not(osslconf = "OPENSSL_NO_EC2M"))] pub fn affine_coordinates_gf2m( &self, @@ -324,10 +466,19 @@ impl EcPointRef { impl EcPoint { /// Creates a new point on the specified curve. + /// + /// OpenSSL documentation at [`EC_POINT_new`] + /// + /// [`EC_POINT_new`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_new.html pub fn new(group: &EcGroupRef) -> Result { unsafe { cvt_p(ffi::EC_POINT_new(group.as_ptr())).map(EcPoint) } } + /// Creates point from a binary representation + /// + /// OpenSSL documentation at [`EC_POINT_oct2point`] + /// + /// [`EC_POINT_oct2point`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_oct2point.html pub fn from_bytes( group: &EcGroupRef, buf: &[u8], @@ -351,7 +502,15 @@ foreign_type! { type CType = ffi::EC_KEY; fn drop = ffi::EC_KEY_free; + /// Public and optional Private key on the given curve + /// + /// OpenSSL documentation at [`EC_KEY_new`] + /// + /// [`EC_KEY_new`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_new.html pub struct EcKey; + /// Reference to [`EcKey`] + /// + /// [`EcKey`]: struct.EcKey.html pub struct EcKeyRef; } @@ -359,6 +518,11 @@ impl EcKeyRef { private_key_to_pem!(ffi::PEM_write_bio_ECPrivateKey); private_key_to_der!(ffi::i2d_ECPrivateKey); + /// Return [`EcGroup`] of the `EcKey` + /// + /// OpenSSL documentation at [`EC_KEY_get0_group`] + /// + /// [`EC_KEY_get0_group`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_get0_group.html pub fn group(&self) -> Option<&EcGroupRef> { unsafe { let ptr = ffi::EC_KEY_get0_group(self.as_ptr()); @@ -370,6 +534,11 @@ impl EcKeyRef { } } + /// Return [`EcPoint`] associated with the public key + /// + /// OpenSSL documentation at [`EC_KEY_get0_pubic_key`] + /// + /// [`EC_KEY_get0_pubic_key`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_get0_public_key.html pub fn public_key(&self) -> Option<&EcPointRef> { unsafe { let ptr = ffi::EC_KEY_get0_public_key(self.as_ptr()); @@ -381,6 +550,11 @@ impl EcKeyRef { } } + /// Return [`EcPoint`] associated with the private key + /// + /// OpenSSL documentation at [`EC_KEY_get0_private_key`] + /// + /// [`EC_KEY_get0_private_key`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_get0_private_key.html pub fn private_key(&self) -> Option<&BigNumRef> { unsafe { let ptr = ffi::EC_KEY_get0_private_key(self.as_ptr()); @@ -393,10 +567,15 @@ impl EcKeyRef { } /// Checks the key for validity. + /// + /// OpenSSL documenation at [`EC_KEY_check_key`] + /// + /// [`EC_KEY_check_key`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_check_key.html pub fn check_key(&self) -> Result<(), ErrorStack> { unsafe { cvt(ffi::EC_KEY_check_key(self.as_ptr())).map(|_| ()) } } + /// Create a copy of the `EcKey` to allow modification pub fn to_owned(&self) -> Result { unsafe { cvt_p(ffi::EC_KEY_dup(self.as_ptr())).map(EcKey) } } @@ -407,6 +586,10 @@ impl EcKey { /// /// It will not have an associated public or private key. This kind of key is primarily useful /// to be provided to the `set_tmp_ecdh` methods on `Ssl` and `SslContextBuilder`. + /// + /// OpenSSL documenation at [`EC_KEY_new_by_curve_name`] + /// + /// [`EC_KEY_new_by_curve_name`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_new_by_curve_name.html pub fn from_curve_name(nid: Nid) -> Result { unsafe { init(); @@ -467,11 +650,18 @@ foreign_type! { type CType = ffi::EC_KEY; fn drop = ffi::EC_KEY_free; + /// Builder pattern for key generation + /// + /// Returns a `EcKeyBuilder` to be consumed by `build` pub struct EcKeyBuilder; + /// Reference to [`EcKeyBuilder`] + /// + /// [`EcKeyBuilder`]: struct.EcKeyBuilder.html pub struct EcKeyBuilderRef; } impl EcKeyBuilder { + /// Creates an empty `EcKeyBuilder` to be chained with additonal methods pub fn new() -> Result { unsafe { init(); @@ -479,6 +669,9 @@ impl EcKeyBuilder { } } + /// Consume the `EcKeyBuilder` and return [`EcKey`] + /// + /// [`EcKey`]: struct.EcKey.html pub fn build(self) -> EcKey { unsafe { let key = EcKey::from_ptr(self.as_ptr()); @@ -489,10 +682,14 @@ impl EcKeyBuilder { } impl EcKeyBuilderRef { + /// Set the [`EcGroup`] explicitly + /// + /// [`EcGroup`]: struct.EcGroup.html pub fn set_group(&mut self, group: &EcGroupRef) -> Result<&mut EcKeyBuilderRef, ErrorStack> { unsafe { cvt(ffi::EC_KEY_set_group(self.as_ptr(), group.as_ptr())).map(|_| self) } } + /// Set public key to given `EcPoint` pub fn set_public_key( &mut self, public_key: &EcPointRef, @@ -505,6 +702,7 @@ impl EcKeyBuilderRef { } } + /// Generate public and private keys. pub fn generate_key(&mut self) -> Result<&mut EcKeyBuilderRef, ErrorStack> { unsafe { cvt(ffi::EC_KEY_generate_key(self.as_ptr())).map(|_| self) } } From 556f37168978ef807034cca2c4338714431a6fe0 Mon Sep 17 00:00:00 2001 From: Andy Gauge Date: Fri, 27 Oct 2017 16:59:36 -0700 Subject: [PATCH 08/15] Error documentation improvement --- openssl/src/error.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/openssl/src/error.rs b/openssl/src/error.rs index 9151c01b..e565c272 100644 --- a/openssl/src/error.rs +++ b/openssl/src/error.rs @@ -1,3 +1,20 @@ +//! Errors returned by OpenSSL library. +//! +//! OpenSSL errors are stored in an `ErrorStack`. Most methods in the crate +/// returns a `Result` type. +//! +//! # Examples +//! +//! ``` +//! use openssl::error::ErrorStack; +//! use openssl::bn::BigNum; +//! +//! let an_error = BigNum::from_dec_str("Cannot parse letters"); +//! match an_error { +//! Ok(_) => _, +//! Err(e) => println!("Parsing Error: {:?}", e), +//! } +//! ``` use libc::{c_ulong, c_char, c_int}; use std::fmt; use std::error; @@ -9,6 +26,9 @@ use std::borrow::Cow; use ffi; +/// Collection of [`Error`]s from OpenSSL. +/// +/// [`Error`]: struct.Error.html #[derive(Debug, Clone)] pub struct ErrorStack(Vec); From 97b046bf4f3959a17cb8da44f6b7e627e54ac328 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 4 Nov 2017 00:01:11 -0700 Subject: [PATCH 09/15] Bump CI openssl versions Also remove caching in appveyor to deal with corruption issue --- .circleci/config.yml | 4 ++-- appveyor.yml | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 39ef6c53..f20f866a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -68,10 +68,10 @@ job: &JOB openssl_110: &OPENSSL_110 LIBRARY: openssl - VERSION: 1.1.0f + VERSION: 1.1.0g openssl_102: &OPENSSL_102 LIBRARY: openssl - VERSION: 1.0.2l + VERSION: 1.0.2m openssl_101: &OPENSSL_101 LIBRARY: openssl VERSION: 1.0.1u diff --git a/appveyor.yml b/appveyor.yml index d9ba7558..bae5d621 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,20 +5,20 @@ environment: - TARGET: i686-pc-windows-gnu BITS: 32 MSYS2: 1 - OPENSSL_VERSION: 1_1_0f + OPENSSL_VERSION: 1_1_0g - TARGET: x86_64-pc-windows-msvc BITS: 64 - OPENSSL_VERSION: 1_1_0f + OPENSSL_VERSION: 1_1_0g OPENSSL_DIR: C:\OpenSSL # 1.0.2, 64/32 bit - TARGET: x86_64-pc-windows-gnu BITS: 64 MSYS2: 1 - OPENSSL_VERSION: 1_0_2L + OPENSSL_VERSION: 1_0_2m - TARGET: i686-pc-windows-msvc BITS: 32 - OPENSSL_VERSION: 1_0_2L + OPENSSL_VERSION: 1_0_2m OPENSSL_DIR: C:\OpenSSL - TARGET: x86_64-pc-windows-msvc VCPKG_DEFAULT_TRIPLET: x64-windows @@ -48,7 +48,3 @@ build: false test_script: - cargo run --manifest-path systest/Cargo.toml --target %TARGET% - cargo test --manifest-path openssl/Cargo.toml --target %TARGET% - -cache: - - target - - C:\Users\appveyor\.cargo\registry From a5c582a7dfd419a684ea617ccc5f8aa3090074db Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Sat, 4 Nov 2017 18:33:00 +0100 Subject: [PATCH 10/15] Update data-encoding major version --- openssl/Cargo.toml | 2 +- openssl/LICENSE | 2 +- openssl/src/ec.rs | 18 ++++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/openssl/Cargo.toml b/openssl/Cargo.toml index c93f7581..4f956c8e 100644 --- a/openssl/Cargo.toml +++ b/openssl/Cargo.toml @@ -31,4 +31,4 @@ tempdir = "0.3" winapi = "0.2" ws2_32-sys = "0.2" hex = "0.2" -data-encoding = "1.2" +data-encoding = "2.0" diff --git a/openssl/LICENSE b/openssl/LICENSE index 808903ae..f259067e 100644 --- a/openssl/LICENSE +++ b/openssl/LICENSE @@ -1,4 +1,4 @@ -Copyright 2011 Google Inc. +Copyright 2011-2017 Google Inc. 2013 Jack Lloyd 2013-2014 Steven Fackler diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index 02be6d22..5e158ae4 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -524,7 +524,7 @@ impl EcKeyBuilderRef { mod test { use bn::{BigNum, BigNumContext}; use nid; - use data_encoding; + use data_encoding::BASE64URL_NOPAD; use super::*; #[test] @@ -603,10 +603,10 @@ mod test { #[test] fn key_from_affine_coordinates() { let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); - let x = data_encoding::base64url::decode_nopad( + let x = BASE64URL_NOPAD.decode( "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4".as_bytes(), ).unwrap(); - let y = data_encoding::base64url::decode_nopad( + let y = BASE64URL_NOPAD.decode( "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM".as_bytes(), ).unwrap(); @@ -627,7 +627,7 @@ mod test { #[test] fn set_private_key() { let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); - let d = data_encoding::base64url::decode_nopad( + let d = BASE64URL_NOPAD.decode( "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE".as_bytes(), ).unwrap(); @@ -643,11 +643,13 @@ mod test { #[test] fn get_affine_coordinates() { - let raw_x = "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4"; - let raw_y = "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"; let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); - let x = data_encoding::base64url::decode_nopad(raw_x.as_bytes()).unwrap(); - let y = data_encoding::base64url::decode_nopad(raw_y.as_bytes()).unwrap(); + let x = BASE64URL_NOPAD.decode( + "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4".as_bytes(), + ).unwrap(); + let y = BASE64URL_NOPAD.decode( + "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM".as_bytes(), + ).unwrap(); let xbn = BigNum::from_slice(&x).unwrap(); let ybn = BigNum::from_slice(&y).unwrap(); From 829c805543b8ef4400857b57a99a9405723f6367 Mon Sep 17 00:00:00 2001 From: AndyGauge Date: Sat, 4 Nov 2017 12:24:24 -0700 Subject: [PATCH 11/15] fixed broken example and syntax error in module level documentation --- openssl/src/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openssl/src/error.rs b/openssl/src/error.rs index e565c272..5f3e2170 100644 --- a/openssl/src/error.rs +++ b/openssl/src/error.rs @@ -1,7 +1,7 @@ //! Errors returned by OpenSSL library. //! //! OpenSSL errors are stored in an `ErrorStack`. Most methods in the crate -/// returns a `Result` type. +//! returns a `Result` type. //! //! # Examples //! @@ -11,7 +11,7 @@ //! //! let an_error = BigNum::from_dec_str("Cannot parse letters"); //! match an_error { -//! Ok(_) => _, +//! Ok(_) => (), //! Err(e) => println!("Parsing Error: {:?}", e), //! } //! ``` From a1a3219483977bef2c059b1681932d95eb20180e Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 4 Nov 2017 13:32:18 -0700 Subject: [PATCH 12/15] Handle local retries OpenSSL can return SSL_ERROR_WANT_READ even on blocking sockets after renegotiation or heartbeats. Heartbeats ignore the flag that normally makes these things handled internally anyway on 1.0.2. To handle this more properly, we now have a special error type we use to signal this event. The `Read` and `Write` implementation automatically retry in this situation since that's what you normally want. People can use `ssl_read` and `ssl_write` if they want the lower level control. Closes #760 --- openssl/src/ssl/error.rs | 27 +++++++ openssl/src/ssl/mod.rs | 167 +++++++++++++++++---------------------- 2 files changed, 100 insertions(+), 94 deletions(-) diff --git a/openssl/src/ssl/error.rs b/openssl/src/ssl/error.rs index db78e2c8..2244fd7f 100644 --- a/openssl/src/ssl/error.rs +++ b/openssl/src/ssl/error.rs @@ -66,6 +66,33 @@ impl From for Error { } } +/// An error indicating that the operation can be immediately retried. +/// +/// OpenSSL's [`SSL_read`] and [`SSL_write`] functions can return `SSL_ERROR_WANT_READ` even when +/// the underlying socket is performing blocking IO in certain cases. When this happens, the +/// the operation can be immediately retried. +/// +/// To signal this event, the `io::Error` inside of [`Error::WantRead`] will be constructed around +/// a `RetryError`. +/// +/// [`SSL_read`]: https://www.openssl.org/docs/manmaster/man3/SSL_read.html +/// [`SSL_write`]: https://www.openssl.org/docs/manmaster/man3/SSL_write.html +/// [`Error::WantRead`]: enum.Error.html#variant.WantRead +#[derive(Debug)] +pub struct RetryError; + +impl fmt::Display for RetryError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(error::Error::description(self)) + } +} + +impl error::Error for RetryError { + fn description(&self) -> &str { + "operation must be retried" + } +} + /// An error or intermediate state after a TLS handshake attempt. #[derive(Debug)] pub enum HandshakeError { diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 5a924a64..62b617a9 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -72,7 +72,7 @@ //! ``` use ffi; use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; -use libc::{c_int, c_void, c_long, c_ulong}; +use libc::{c_int, c_long, c_ulong, c_void}; use libc::{c_uchar, c_uint}; use std::any::Any; use std::any::TypeId; @@ -93,12 +93,12 @@ use std::slice; use std::str; use std::sync::Mutex; -use {init, cvt, cvt_p, cvt_n}; +use {cvt, cvt_n, cvt_p, init}; use dh::{Dh, DhRef}; use ec::EcKeyRef; #[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))] use ec::EcKey; -use x509::{X509StoreContextRef, X509FileType, X509, X509Ref, X509VerifyError, X509Name}; +use x509::{X509, X509FileType, X509Name, X509Ref, X509StoreContextRef, X509VerifyError}; use x509::store::{X509StoreBuilderRef, X509StoreRef}; #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] use x509::store::X509Store; @@ -111,9 +111,9 @@ use stack::{Stack, StackRef}; use ssl::bio::BioMethod; use ssl::callbacks::*; -pub use ssl::connector::{SslConnectorBuilder, SslConnector, SslAcceptorBuilder, SslAcceptor, - ConnectConfiguration}; -pub use ssl::error::{Error, HandshakeError}; +pub use ssl::connector::{ConnectConfiguration, SslAcceptor, SslAcceptorBuilder, SslConnector, + SslConnectorBuilder}; +pub use ssl::error::{Error, HandshakeError, RetryError}; mod error; mod callbacks; @@ -416,10 +416,8 @@ impl SslContextBuilder { pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> { unsafe { let ptr = cert_store.as_ptr(); - cvt( - ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), ptr) as - c_int, - )?; + cvt(ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), ptr) + as c_int)?; mem::forget(cert_store); Ok(()) @@ -461,8 +459,9 @@ impl SslContextBuilder { pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> { unsafe { - cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as - c_int).map(|_| ()) + cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) + as c_int) + .map(|_| ()) } } @@ -579,10 +578,7 @@ impl SslContextBuilder { /// `set_certificate` to a trusted root. pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { unsafe { - cvt(ffi::SSL_CTX_add_extra_chain_cert( - self.as_ptr(), - cert.as_ptr(), - ) as c_int)?; + cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int)?; mem::forget(cert); Ok(()) } @@ -766,10 +762,9 @@ impl SslContextBuilder { Box::into_raw(callback) as *mut c_void, ); let f: unsafe extern "C" fn(_, _) -> _ = raw_tlsext_status::; - cvt(ffi::SSL_CTX_set_tlsext_status_cb( - self.as_ptr(), - Some(f), - ) as c_int).map(|_| ()) + cvt(ffi::SSL_CTX_set_tlsext_status_cb(self.as_ptr(), Some(f)) + as c_int) + .map(|_| ()) } } @@ -781,7 +776,8 @@ impl SslContextBuilder { #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] pub fn set_psk_callback(&mut self, callback: F) where - F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result + F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) + -> Result + Any + 'static + Sync @@ -1239,10 +1235,8 @@ impl SslRef { pub fn set_hostname(&mut self, hostname: &str) -> Result<(), ErrorStack> { let cstr = CString::new(hostname).unwrap(); unsafe { - cvt(ffi::SSL_set_tlsext_host_name( - self.as_ptr(), - cstr.as_ptr() as *mut _, - ) as c_int).map(|_| ()) + cvt(ffi::SSL_set_tlsext_host_name(self.as_ptr(), cstr.as_ptr() as *mut _) as c_int) + .map(|_| ()) } } @@ -1373,9 +1367,7 @@ impl SslRef { return None; } let meth = ffi::SSL_COMP_get_name(ptr); - Some( - str::from_utf8(CStr::from_ptr(meth as *const _).to_bytes()).unwrap(), - ) + Some(str::from_utf8(CStr::from_ptr(meth as *const _).to_bytes()).unwrap()) } } @@ -1392,9 +1384,7 @@ impl SslRef { return None; } - Some( - str::from_utf8(CStr::from_ptr(name as *const _).to_bytes()).unwrap(), - ) + Some(str::from_utf8(CStr::from_ptr(name as *const _).to_bytes()).unwrap()) } } @@ -1459,10 +1449,7 @@ impl SslRef { /// Sets the status response a client wishes the server to reply with. pub fn set_status_type(&mut self, type_: StatusType) -> Result<(), ErrorStack> { unsafe { - cvt(ffi::SSL_set_tlsext_status_type( - self.as_ptr(), - type_.as_raw(), - ) as c_int).map(|_| ()) + cvt(ffi::SSL_set_tlsext_status_type(self.as_ptr(), type_.as_raw()) as c_int).map(|_| ()) } } @@ -1494,7 +1481,8 @@ impl SslRef { self.as_ptr(), p as *mut c_uchar, response.len() as c_long, - ) as c_int).map(|_| ()) + ) as c_int) + .map(|_| ()) } } @@ -1561,19 +1549,16 @@ impl Ssl { Ok(stream) } else { match stream.make_error(ret) { - e @ Error::WantWrite(_) | - e @ Error::WantRead(_) => { + e @ Error::WantWrite(_) | e @ Error::WantRead(_) => { Err(HandshakeError::Interrupted(MidHandshakeSslStream { stream: stream, error: e, })) } - err => { - Err(HandshakeError::Failure(MidHandshakeSslStream { - stream: stream, - error: err, - })) - } + err => Err(HandshakeError::Failure(MidHandshakeSslStream { + stream: stream, + error: err, + })), } } } @@ -1594,19 +1579,16 @@ impl Ssl { Ok(stream) } else { match stream.make_error(ret) { - e @ Error::WantWrite(_) | - e @ Error::WantRead(_) => { + e @ Error::WantWrite(_) | e @ Error::WantRead(_) => { Err(HandshakeError::Interrupted(MidHandshakeSslStream { stream: stream, error: e, })) } - err => { - Err(HandshakeError::Failure(MidHandshakeSslStream { - stream: stream, - error: err, - })) - } + err => Err(HandshakeError::Failure(MidHandshakeSslStream { + stream: stream, + error: err, + })), } } } @@ -1652,8 +1634,7 @@ impl MidHandshakeSslStream { Ok(self.stream) } else { match self.stream.make_error(ret) { - e @ Error::WantWrite(_) | - e @ Error::WantRead(_) => { + e @ Error::WantWrite(_) | e @ Error::WantRead(_) => { self.error = e; Err(HandshakeError::Interrupted(self)) } @@ -1775,12 +1756,10 @@ impl SslStream { if errs.errors().is_empty() { match self.get_bio_error() { Some(err) => Error::Stream(err), - None => { - Error::Stream(io::Error::new( - io::ErrorKind::ConnectionAborted, - "unexpected EOF observed", - )) - } + None => Error::Stream(io::Error::new( + io::ErrorKind::ConnectionAborted, + "unexpected EOF observed", + )), } } else { Error::Ssl(errs) @@ -1790,33 +1769,24 @@ impl SslStream { ffi::SSL_ERROR_WANT_WRITE => { let err = match self.get_bio_error() { Some(err) => err, - None => { - io::Error::new( - io::ErrorKind::Other, - "BUG: got an SSL_ERROR_WANT_WRITE with no error in the BIO", - ) - } + None => io::Error::new( + io::ErrorKind::Other, + "BUG: got an SSL_ERROR_WANT_WRITE with no error in the BIO", + ), }; Error::WantWrite(err) } ffi::SSL_ERROR_WANT_READ => { let err = match self.get_bio_error() { Some(err) => err, - None => { - io::Error::new( - io::ErrorKind::Other, - "BUG: got an SSL_ERROR_WANT_READ with no error in the BIO", - ) - } + None => io::Error::new(io::ErrorKind::Other, RetryError), }; Error::WantRead(err) } - err => { - Error::Stream(io::Error::new( - io::ErrorKind::InvalidData, - format!("unexpected error {}", err), - )) - } + err => Error::Stream(io::Error::new( + io::ErrorKind::InvalidData, + format!("unexpected error {}", err), + )), } } @@ -1859,25 +1829,34 @@ impl SslStream { impl Read for SslStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { - match self.ssl_read(buf) { - Ok(n) => Ok(n), - Err(Error::ZeroReturn) => Ok(0), - Err(Error::Stream(e)) => Err(e), - Err(Error::WantRead(e)) => Err(e), - Err(Error::WantWrite(e)) => Err(e), - Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), + loop { + match self.ssl_read(buf) { + Ok(n) => return Ok(n), + Err(Error::ZeroReturn) => return Ok(0), + Err(Error::WantRead(ref e)) + if e.get_ref().map_or(false, |e| e.is::()) => {} + Err(Error::Stream(e)) | Err(Error::WantRead(e)) | Err(Error::WantWrite(e)) => { + return Err(e); + } + Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), + } } } } impl Write for SslStream { fn write(&mut self, buf: &[u8]) -> io::Result { - self.ssl_write(buf).map_err(|e| match e { - Error::Stream(e) => e, - Error::WantRead(e) => e, - Error::WantWrite(e) => e, - e => io::Error::new(io::ErrorKind::Other, e), - }) + loop { + match self.ssl_write(buf) { + Ok(n) => return Ok(n), + Err(Error::WantRead(ref e)) + if e.get_ref().map_or(false, |e| e.is::()) => {} + Err(Error::Stream(e)) | Err(Error::WantRead(e)) | Err(Error::WantWrite(e)) => { + return Err(e); + } + Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), + } + } } fn flush(&mut self) -> io::Result<()> { @@ -1902,8 +1881,8 @@ mod compat { use ffi; use libc::c_int; - pub use ffi::{SSL_CTX_get_options, SSL_CTX_set_options, SSL_CTX_clear_options, SSL_CTX_up_ref, - SSL_SESSION_get_master_key, SSL_is_server, SSL_SESSION_up_ref}; + pub use ffi::{SSL_CTX_clear_options, SSL_CTX_get_options, SSL_CTX_set_options, SSL_CTX_up_ref, + SSL_SESSION_get_master_key, SSL_SESSION_up_ref, SSL_is_server}; pub unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int { ffi::CRYPTO_get_ex_new_index( @@ -1942,7 +1921,7 @@ mod compat { use std::ptr; use ffi; - use libc::{self, c_long, c_ulong, c_int, size_t, c_uchar}; + use libc::{self, c_int, c_long, c_uchar, c_ulong, size_t}; pub unsafe fn SSL_CTX_get_options(ctx: *const ffi::SSL_CTX) -> c_ulong { ffi::SSL_CTX_ctrl(ctx as *mut _, ffi::SSL_CTRL_OPTIONS, 0, ptr::null_mut()) as c_ulong From 8830bd5daf3100039a767bfacefdc15d9fea05f4 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 5 Nov 2017 10:47:05 -0800 Subject: [PATCH 13/15] Add a couple of FIXMEs --- openssl/src/ssl/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 62b617a9..d3a5e428 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -368,6 +368,7 @@ impl SslContextBuilder { /// registers a verification callback. pub fn set_verify_callback(&mut self, mode: SslVerifyMode, verify: F) where + // FIXME should take a mutable reference to the store F: Fn(bool, &X509StoreContextRef) -> bool + Any + 'static + Sync + Send, { unsafe { @@ -1136,6 +1137,7 @@ impl SslRef { /// chain is valid and `false` otherwise. pub fn set_verify_callback(&mut self, mode: SslVerifyMode, verify: F) where + // FIXME should take a mutable reference to the x509 store F: Fn(bool, &X509StoreContextRef) -> bool + Any + 'static + Sync + Send, { unsafe { @@ -1649,6 +1651,7 @@ impl MidHandshakeSslStream { /// A stream wrapper which handles SSL encryption for an underlying stream. pub struct SslStream { + // FIXME use ManuallyDrop ssl: Ssl, _method: BioMethod, // NOTE: this *must* be after the Ssl field so things drop right _p: PhantomData, @@ -1699,6 +1702,7 @@ impl SslStream { Ok(ret as usize) } else { match self.make_error(ret) { + // FIXME only do this in read // Don't treat unexpected EOFs as errors when reading Error::Stream(ref e) if e.kind() == io::ErrorKind::ConnectionAborted => Ok(0), e => Err(e), From 6e66bf9c38653b3a06fb4efb96e71e9bada25d79 Mon Sep 17 00:00:00 2001 From: Greg V Date: Mon, 6 Nov 2017 16:19:16 +0300 Subject: [PATCH 14/15] Fix LibreSSL cms.h detection Also test without system OpenSSL headers when building OpenSSL on CircleCI --- .circleci/config.yml | 1 + systest/build.rs | 10 +++------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f20f866a..4e8695e8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -35,6 +35,7 @@ job: &JOB steps: - checkout - run: apt-get update + - run: apt-get remove -y libssl-dev - run: ./test/add_target.sh - *RESTORE_REGISTRY - run: cargo generate-lockfile diff --git a/systest/build.rs b/systest/build.rs index 7418cca0..af8ebbc1 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -5,6 +5,7 @@ use std::env; fn main() { let mut cfg = ctest::TestGenerator::new(); let target = env::var("TARGET").unwrap(); + let mut is_libressl = false; if let Ok(out) = env::var("DEP_OPENSSL_INCLUDE") { cfg.include(&out); @@ -24,6 +25,7 @@ fn main() { if let Ok(_) = env::var("DEP_OPENSSL_LIBRESSL") { cfg.cfg("libressl", None); + is_libressl = true; } else if let Ok(version) = env::var("DEP_OPENSSL_VERSION") { cfg.cfg(&format!("ossl{}", version), None); } @@ -41,12 +43,6 @@ fn main() { } } - let has_cms_h = if let Ok(version) = env::var("DEP_OPENSSL_LIBRESSL_VERSION") { - version != "261" && version != "262" - } else { - true - }; - cfg.header("openssl/comp.h") .header("openssl/dh.h") .header("openssl/ossl_typ.h") @@ -64,7 +60,7 @@ fn main() { .header("openssl/aes.h") .header("openssl/ocsp.h"); - if has_cms_h { + if !is_libressl { cfg.header("openssl/cms.h"); } From 5eea31676ee1f9013ea7be09278a7488104c4944 Mon Sep 17 00:00:00 2001 From: Brian Vincent Date: Mon, 16 Oct 2017 18:28:36 -0500 Subject: [PATCH 15/15] Add an example of making a CA and certs and verifying. --- openssl/examples/mk_certs.rs | 152 +++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 openssl/examples/mk_certs.rs diff --git a/openssl/examples/mk_certs.rs b/openssl/examples/mk_certs.rs new file mode 100644 index 00000000..c64dc007 --- /dev/null +++ b/openssl/examples/mk_certs.rs @@ -0,0 +1,152 @@ +//! A program that generates ca certs, certs verified by the ca, and public +//! and private keys. + +extern crate openssl; + +use openssl::asn1::Asn1Time; +use openssl::bn::{BigNum, MSB_MAYBE_ZERO}; +use openssl::error::ErrorStack; +use openssl::hash::MessageDigest; +use openssl::pkey::{PKey, PKeyRef}; +use openssl::rsa::Rsa; +use openssl::x509::{X509, X509Ref}; +use openssl::x509::{X509NameBuilder, X509Req, X509ReqBuilder}; +use openssl::x509::extension::{AuthorityKeyIdentifier, BasicConstraints, KeyUsage, + SubjectAlternativeName, SubjectKeyIdentifier}; + +/// Make a CA certificate and private key +fn mk_ca_cert() -> Result<(X509, PKey), ErrorStack> { + let rsa = Rsa::generate(2048)?; + let privkey = PKey::from_rsa(rsa)?; + + let mut x509_name = X509NameBuilder::new()?; + x509_name.append_entry_by_text("C", "US")?; + x509_name.append_entry_by_text("ST", "TX")?; + x509_name.append_entry_by_text("O", "Some CA organization")?; + x509_name.append_entry_by_text("CN", "ca test")?; + let x509_name = x509_name.build(); + + let mut cert_builder = X509::builder()?; + cert_builder.set_version(2)?; + let serial_number = { + let mut serial = BigNum::new()?; + serial.rand(159, MSB_MAYBE_ZERO, false)?; + serial.to_asn1_integer()? + }; + cert_builder.set_serial_number(&serial_number)?; + cert_builder.set_subject_name(&x509_name)?; + cert_builder.set_issuer_name(&x509_name)?; + cert_builder.set_pubkey(&privkey)?; + let not_before = Asn1Time::days_from_now(0)?; + cert_builder.set_not_before(¬_before)?; + let not_after = Asn1Time::days_from_now(365)?; + cert_builder.set_not_after(¬_after)?; + + cert_builder.append_extension(BasicConstraints::new().critical().ca().build()?)?; + cert_builder.append_extension(KeyUsage::new() + .critical() + .key_cert_sign() + .crl_sign() + .build()?)?; + + let subject_key_identifier = + SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?; + cert_builder.append_extension(subject_key_identifier)?; + + cert_builder.sign(&privkey, MessageDigest::sha256())?; + let cert = cert_builder.build(); + + Ok((cert, privkey)) +} + +/// Make a X509 request with the given private key +fn mk_request(privkey: &PKey) -> Result<(X509Req), ErrorStack> { + let mut req_builder = X509ReqBuilder::new()?; + req_builder.set_pubkey(&privkey)?; + + let mut x509_name = X509NameBuilder::new()?; + x509_name.append_entry_by_text("C", "US")?; + x509_name.append_entry_by_text("ST", "TX")?; + x509_name.append_entry_by_text("O", "Some organization")?; + x509_name.append_entry_by_text("CN", "www.example.com")?; + let x509_name = x509_name.build(); + req_builder.set_subject_name(&x509_name)?; + + req_builder.sign(&privkey, MessageDigest::sha256())?; + let req = req_builder.build(); + Ok(req) +} + +/// Make a certificate and private key signed by the given CA cert and private key +fn mk_ca_signed_cert(ca_cert: &X509Ref, ca_privkey: &PKeyRef) -> Result<(X509, PKey), ErrorStack> { + let rsa = Rsa::generate(2048)?; + let privkey = PKey::from_rsa(rsa)?; + + let req = mk_request(&privkey)?; + + let mut cert_builder = X509::builder()?; + cert_builder.set_version(2)?; + let serial_number = { + let mut serial = BigNum::new()?; + serial.rand(159, MSB_MAYBE_ZERO, false)?; + serial.to_asn1_integer()? + }; + cert_builder.set_serial_number(&serial_number)?; + cert_builder.set_subject_name(req.subject_name())?; + cert_builder.set_issuer_name(ca_cert.subject_name())?; + cert_builder.set_pubkey(&privkey)?; + let not_before = Asn1Time::days_from_now(0)?; + cert_builder.set_not_before(¬_before)?; + let not_after = Asn1Time::days_from_now(365)?; + cert_builder.set_not_after(¬_after)?; + + cert_builder.append_extension(BasicConstraints::new().build()?)?; + + cert_builder.append_extension(KeyUsage::new() + .critical() + .non_repudiation() + .digital_signature() + .key_encipherment() + .build()?)?; + + let subject_key_identifier = SubjectKeyIdentifier::new() + .build(&cert_builder.x509v3_context(Some(ca_cert), None))?; + cert_builder.append_extension(subject_key_identifier)?; + + let auth_key_identifier = AuthorityKeyIdentifier::new() + .keyid(false) + .issuer(false) + .build(&cert_builder.x509v3_context(Some(ca_cert), None))?; + cert_builder.append_extension(auth_key_identifier)?; + + let subject_alt_name = SubjectAlternativeName::new() + .dns("*.example.com") + .dns("hello.com") + .build(&cert_builder.x509v3_context(Some(ca_cert), None))?; + cert_builder.append_extension(subject_alt_name)?; + + cert_builder.sign(&ca_privkey, MessageDigest::sha256())?; + let cert = cert_builder.build(); + + Ok((cert, privkey)) +} + +fn real_main() -> Result<(), ErrorStack> { + let (ca_cert, ca_privkey) = mk_ca_cert()?; + let (cert, _privkey) = mk_ca_signed_cert(&ca_cert, &ca_privkey)?; + + // Verify that this cert was issued by this ca + match ca_cert.issued(&cert) { + Err(ver_err) => println!("Failed to verify certificate: {}", ver_err), + Ok(()) => println!("Certificate verified!"), + }; + + Ok(()) +} + +fn main() { + match real_main() { + Ok(()) => println!("Finished."), + Err(e) => println!("Error: {}", e), + }; +}