aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml7
-rw-r--r--README.md2
-rw-r--r--appveyor.yml8
-rw-r--r--openssl-sys-extras/Cargo.toml6
-rw-r--r--openssl-sys-extras/src/lib.rs2
-rw-r--r--openssl-sys-extras/src/openssl_shim.c26
-rw-r--r--openssl-sys/Cargo.toml4
-rw-r--r--openssl-sys/src/lib.rs84
-rw-r--r--openssl/Cargo.toml12
-rw-r--r--openssl/src/c_helpers.c4
-rw-r--r--openssl/src/crypto/pkey.rs54
-rw-r--r--openssl/src/crypto/rsa.rs160
-rw-r--r--openssl/src/crypto/symm.rs25
-rw-r--r--openssl/src/crypto/symm_internal.rs3
-rw-r--r--openssl/src/lib.rs5
-rw-r--r--openssl/src/nid.rs58
-rw-r--r--openssl/src/ssl/bio.rs100
-rw-r--r--openssl/src/ssl/mod.rs185
-rw-r--r--openssl/src/ssl/tests/mod.rs58
-rw-r--r--openssl/src/version.rs89
-rw-r--r--openssl/src/x509/extension.rs3
-rw-r--r--openssl/src/x509/mod.rs163
-rw-r--r--openssl/src/x509/tests.rs41
-rw-r--r--openssl/test/alt_name_cert.pem25
-rwxr-xr-xopenssl/test/build.sh2
-rw-r--r--openssl/test/cert.pem41
-rw-r--r--openssl/test/key.pem55
-rw-r--r--openssl/test/rsa.pem27
-rw-r--r--openssl/test/rsa.pem.pub9
29 files changed, 981 insertions, 277 deletions
diff --git a/.travis.yml b/.travis.yml
index 36b926d4..8dae13c4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,8 +6,7 @@ addons:
- gcc-arm-linux-gnueabihf
rust:
- nightly
-- beta
-- 1.4.0
+- 1.7.0
os:
- osx
- linux
@@ -19,11 +18,13 @@ matrix:
# include:
# - os: linux
# env: TARGET=arm-unknown-linux-gnueabihf TEST_FEATURES=true
- # rust: 1.4.0
+ # rust: 1.7.0
exclude:
- os: osx
env: TEST_FEATURES=true
before_install:
- ./openssl/test/build.sh
script:
+- cargo fetch --manifest-path openssl/Cargo.toml # generate a cargo.lock
+- cargo update --manifest-path openssl/Cargo.toml -p bitflags --precise 0.5.0
- ./openssl/test/run.sh
diff --git a/README.md b/README.md
index bdc8fc2c..fd8cc2ee 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[![Build Status](https://travis-ci.org/sfackler/rust-openssl.svg?branch=master)](https://travis-ci.org/sfackler/rust-openssl)
-[Documentation](https://sfackler.github.io/rust-openssl/doc/v0.7.6/openssl).
+[Documentation](https://sfackler.github.io/rust-openssl/doc/v0.7.11/openssl).
## Building
diff --git a/appveyor.yml b/appveyor.yml
index fff3cff7..f835d9f6 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,10 +8,10 @@ environment:
# - TARGET: x86_64-pc-windows-msvc
# BITS: 64
install:
- - ps: Start-FileDownload "http://slproweb.com/download/Win${env:BITS}OpenSSL-1_0_2g.exe"
- - Win%BITS%OpenSSL-1_0_2g.exe /SILENT /VERYSILENT /SP- /DIR="C:\OpenSSL"
- - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.5.0-${env:TARGET}.exe"
- - rust-1.5.0-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
+ - 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"
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
- SET PATH=%PATH%;C:\MinGW\bin
- rustc -V
diff --git a/openssl-sys-extras/Cargo.toml b/openssl-sys-extras/Cargo.toml
index 0607d3bb..14f09247 100644
--- a/openssl-sys-extras/Cargo.toml
+++ b/openssl-sys-extras/Cargo.toml
@@ -1,11 +1,11 @@
[package]
name = "openssl-sys-extras"
-version = "0.7.6"
+version = "0.7.11"
authors = ["Steven Fackler <[email protected]>"]
license = "MIT"
description = "Extra FFI bindings to OpenSSL that require a C shim"
repository = "https://github.com/sfackler/rust-openssl"
-documentation = "https://sfackler.github.io/rust-openssl/doc/v0.7.6/openssl_sys_extras"
+documentation = "https://sfackler.github.io/rust-openssl/doc/v0.7.11/openssl_sys_extras"
build = "build.rs"
[features]
@@ -13,7 +13,7 @@ ecdh_auto = []
[dependencies]
libc = "0.2"
-openssl-sys = { version = "0.7.6", path = "../openssl-sys" }
+openssl-sys = { version = "0.7.11", path = "../openssl-sys" }
[build-dependencies]
gcc = "0.3"
diff --git a/openssl-sys-extras/src/lib.rs b/openssl-sys-extras/src/lib.rs
index 9647929a..a2acbd55 100644
--- a/openssl-sys-extras/src/lib.rs
+++ b/openssl-sys-extras/src/lib.rs
@@ -1,5 +1,5 @@
#![allow(non_upper_case_globals, non_snake_case)]
-#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.7.6")]
+#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.7.11")]
extern crate openssl_sys;
extern crate libc;
diff --git a/openssl-sys-extras/src/openssl_shim.c b/openssl-sys-extras/src/openssl_shim.c
index a4b40280..11df1ca6 100644
--- a/openssl-sys-extras/src/openssl_shim.c
+++ b/openssl-sys-extras/src/openssl_shim.c
@@ -3,32 +3,6 @@
#include <openssl/dh.h>
#include <openssl/bn.h>
-#if defined(__APPLE__) || defined(__linux)
-
-#include <pthread.h>
-#include <openssl/crypto.h>
-
-unsigned long thread_id()
-{
- return (unsigned long) pthread_self();
-}
-
-void rust_openssl_set_id_callback() {
- CRYPTO_set_id_callback(thread_id);
-}
-
-#else
-// Openssl already handles Windows directly, so we don't
-// need to explicitly set it
-
-void rust_openssl_set_id_callback() {
- // We don't know how to set the callback for arbitrary OSes
- // Let openssl use its defaults and hope they work.
-}
-
-#endif
-
-
#if OPENSSL_VERSION_NUMBER < 0x10000000L
// Copied from openssl crypto/hmac/hmac.c
int HMAC_CTX_copy(HMAC_CTX *dctx, HMAC_CTX *sctx)
diff --git a/openssl-sys/Cargo.toml b/openssl-sys/Cargo.toml
index c8410104..9ad5c456 100644
--- a/openssl-sys/Cargo.toml
+++ b/openssl-sys/Cargo.toml
@@ -1,12 +1,12 @@
[package]
name = "openssl-sys"
-version = "0.7.6"
+version = "0.7.11"
authors = ["Alex Crichton <[email protected]>",
"Steven Fackler <[email protected]>"]
license = "MIT"
description = "FFI bindings to OpenSSL"
repository = "https://github.com/sfackler/rust-openssl"
-documentation = "https://sfackler.github.io/rust-openssl/doc/v0.7.6/openssl_sys"
+documentation = "https://sfackler.github.io/rust-openssl/doc/v0.7.11/openssl_sys"
links = "openssl"
build = "build.rs"
diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs
index d638b38a..b6f55832 100644
--- a/openssl-sys/src/lib.rs
+++ b/openssl-sys/src/lib.rs
@@ -1,6 +1,6 @@
#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)]
#![allow(dead_code)]
-#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.7.6")]
+#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.7.11")]
extern crate libc;
@@ -34,10 +34,23 @@ pub type X509_NAME = c_void;
pub type X509_NAME_ENTRY = c_void;
pub type X509_REQ = c_void;
pub type X509_STORE_CTX = c_void;
-pub type stack_st_X509_EXTENSION = c_void;
-pub type stack_st_void = c_void;
pub type bio_st = c_void;
+#[repr(C)]
+pub struct stack_st_X509_EXTENSION {
+ pub stack: _STACK,
+}
+
+#[repr(C)]
+pub struct stack_st_GENERAL_NAME {
+ pub stack: _STACK,
+}
+
+#[repr(C)]
+pub struct stack_st_void {
+ pub stack: _STACK,
+}
+
pub type bio_info_cb = Option<unsafe extern "C" fn(*mut BIO,
c_int,
*const c_char,
@@ -64,6 +77,15 @@ pub struct BIO_METHOD {
unsafe impl Sync for BIO_METHOD {}
#[repr(C)]
+pub struct _STACK {
+ pub num: c_int,
+ pub data: *mut *mut c_char,
+ pub sorted: c_int,
+ pub num_alloc: c_int,
+ pub comp: Option<unsafe extern "C" fn(*const c_void, *const c_void)>,
+}
+
+#[repr(C)]
pub struct RSA {
pub pad: c_int,
pub version: c_long,
@@ -178,6 +200,17 @@ pub struct X509V3_CTX {
// Maybe more here
}
+#[repr(C)]
+pub struct GENERAL_NAME {
+ pub type_: c_int,
+ pub d: *mut c_void,
+}
+
+impl Copy for GENERAL_NAME {}
+impl Clone for GENERAL_NAME {
+ fn clone(&self) -> GENERAL_NAME { *self }
+}
+
impl Copy for X509V3_CTX {}
impl Clone for X509V3_CTX {
fn clone(&self) -> X509V3_CTX { *self }
@@ -259,6 +292,12 @@ pub const SSL_TLSEXT_ERR_ALERT_WARNING: c_int = 1;
pub const SSL_TLSEXT_ERR_ALERT_FATAL: c_int = 2;
pub const SSL_TLSEXT_ERR_NOACK: c_int = 3;
+pub const SSLEAY_VERSION : c_int = 0;
+pub const SSLEAY_CFLAGS : c_int = 2;
+pub const SSLEAY_BUILT_ON : c_int = 3;
+pub const SSLEAY_PLATFORM : c_int = 4;
+pub const SSLEAY_DIR : c_int = 5;
+
#[cfg(any(feature = "npn", feature = "alpn"))]
pub const OPENSSL_NPN_UNSUPPORTED: c_int = 0;
#[cfg(any(feature = "npn", feature = "alpn"))]
@@ -327,6 +366,16 @@ pub const X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE: c_int = 45;
pub const X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: c_int = 53;
pub const X509_V_OK: c_int = 0;
+pub const GEN_OTHERNAME: c_int = 0;
+pub const GEN_EMAIL: c_int = 1;
+pub const GEN_DNS: c_int = 2;
+pub const GEN_X400: c_int = 3;
+pub const GEN_DIRNAME: c_int = 4;
+pub const GEN_EDIPARTY: c_int = 5;
+pub const GEN_URI: c_int = 6;
+pub const GEN_IPADD: c_int = 7;
+pub const GEN_RID: c_int = 8;
+
static mut MUTEXES: *mut Vec<Mutex<()>> = 0 as *mut Vec<Mutex<()>>;
static mut GUARDS: *mut Vec<Option<MutexGuard<'static, ()>>> = 0 as *mut Vec<Option<MutexGuard<'static, ()>>>;
@@ -521,6 +570,9 @@ extern "C" {
pub fn EVP_aes_256_cfb8() -> *const EVP_CIPHER;
pub fn EVP_rc4() -> *const EVP_CIPHER;
+ pub fn EVP_des_cbc() -> *const EVP_CIPHER;
+ pub fn EVP_des_ecb() -> *const EVP_CIPHER;
+
pub fn EVP_BytesToKey(typ: *const EVP_CIPHER, md: *const EVP_MD,
salt: *const u8, data: *const u8, datalen: c_int,
count: c_int, key: *mut u8, iv: *mut u8) -> c_int;
@@ -548,6 +600,7 @@ extern "C" {
pub fn EVP_PKEY_new() -> *mut EVP_PKEY;
pub fn EVP_PKEY_free(k: *mut EVP_PKEY);
pub fn EVP_PKEY_assign(pkey: *mut EVP_PKEY, typ: c_int, key: *const c_void) -> c_int;
+ pub fn EVP_PKEY_copy_parameters(to: *mut EVP_PKEY, from: *const EVP_PKEY) -> c_int;
pub fn EVP_PKEY_get1_RSA(k: *mut EVP_PKEY) -> *mut RSA;
pub fn EVP_PKEY_set1_RSA(k: *mut EVP_PKEY, r: *mut RSA) -> c_int;
pub fn EVP_PKEY_cmp(a: *const EVP_PKEY, b: *const EVP_PKEY) -> c_int;
@@ -574,6 +627,12 @@ extern "C" {
callback: Option<PasswordCallback>,
user_data: *mut c_void) -> c_int;
pub fn PEM_write_bio_PUBKEY(bp: *mut BIO, x: *mut EVP_PKEY) -> c_int;
+ pub fn PEM_write_bio_RSAPrivateKey(bp: *mut BIO, rsa: *mut RSA, cipher: *const EVP_CIPHER,
+ kstr: *mut c_char, klen: c_int,
+ callback: Option<PasswordCallback>,
+ user_data: *mut c_void) -> c_int;
+ pub fn PEM_write_bio_RSAPublicKey(bp: *mut BIO, rsa: *mut RSA) -> c_int;
+ pub fn PEM_write_bio_RSA_PUBKEY(bp: *mut BIO, rsa: *mut RSA) -> c_int;
pub fn PEM_write_bio_X509(bio: *mut BIO, x509: *mut X509) -> c_int;
pub fn PEM_write_bio_X509_REQ(bio: *mut BIO, x509: *mut X509_REQ) -> c_int;
@@ -647,6 +706,16 @@ extern "C" {
pub fn SSL_get_version(ssl: *mut SSL) -> *const c_char;
pub fn SSL_state_string(ssl: *mut SSL) -> *const c_char;
pub fn SSL_state_string_long(ssl: *mut SSL) -> *const c_char;
+ pub fn SSL_set_verify(ssl: *mut SSL,
+ mode: c_int,
+ verify_callback: Option<extern fn(c_int, *mut X509_STORE_CTX) -> c_int>);
+ pub fn SSL_get_ex_new_index(argl: c_long, argp: *const c_void,
+ new_func: Option<CRYPTO_EX_new>,
+ dup_func: Option<CRYPTO_EX_dup>,
+ free_func: Option<CRYPTO_EX_free>)
+ -> c_int;
+ pub fn SSL_set_ex_data(ssl: *mut SSL, idx: c_int, data: *mut c_void) -> c_int;
+ pub fn SSL_get_ex_data(ssl: *mut SSL, idx: c_int) -> *mut c_void;
pub fn SSL_get_servername(ssl: *const SSL, name_type: c_long) -> *const c_char;
@@ -666,6 +735,7 @@ extern "C" {
pub fn SSL_CTX_set_verify_depth(ctx: *mut SSL_CTX, depth: c_int);
pub fn SSL_CTX_load_verify_locations(ctx: *mut SSL_CTX, CAfile: *const c_char,
CApath: *const c_char) -> c_int;
+ pub fn SSL_CTX_set_default_verify_paths(ctx: *mut SSL_CTX) -> c_int;
pub fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *const c_void,
new_func: Option<CRYPTO_EX_new>,
dup_func: Option<CRYPTO_EX_dup>,
@@ -674,6 +744,7 @@ extern "C" {
pub fn SSL_CTX_set_ex_data(ctx: *mut SSL_CTX, idx: c_int, data: *mut c_void)
-> c_int;
pub fn SSL_CTX_get_ex_data(ctx: *mut SSL_CTX, idx: c_int) -> *mut c_void;
+ pub fn SSL_CTX_set_session_id_context(ssl: *mut SSL_CTX, sid_ctx: *const c_uchar, sid_ctx_len: c_uint) -> c_int;
pub fn SSL_CTX_use_certificate_file(ctx: *mut SSL_CTX, cert_file: *const c_char, file_type: c_int) -> c_int;
pub fn SSL_CTX_use_certificate_chain_file(ctx: *mut SSL_CTX, cert_chain_file: *const c_char, file_type: c_int) -> c_int;
@@ -742,6 +813,7 @@ extern "C" {
pub fn X509_sign(x: *mut X509, pkey: *mut EVP_PKEY, md: *const EVP_MD) -> c_int;
pub fn X509_get_pubkey(x: *mut X509) -> *mut EVP_PKEY;
pub fn X509_to_X509_REQ(x: *mut X509, pkey: *mut EVP_PKEY, md: *const EVP_MD) -> *mut X509_REQ;
+ pub fn X509_get_ext_d2i(x: *mut X509, nid: c_int, crit: *mut c_int, idx: *mut c_int) -> *mut c_void;
pub fn X509_EXTENSION_free(ext: *mut X509_EXTENSION);
@@ -751,10 +823,13 @@ extern "C" {
pub fn X509_NAME_ENTRY_get_data(ne: *mut X509_NAME_ENTRY) -> *mut ASN1_STRING;
pub fn ASN1_STRING_to_UTF8(out: *mut *mut c_char, s: *mut ASN1_STRING) -> c_int;
+ pub fn ASN1_STRING_length(x: *mut ASN1_STRING) -> c_int;
+ pub fn ASN1_STRING_data(x: *mut ASN1_STRING) -> *mut c_uchar;
pub fn X509_STORE_CTX_get_current_cert(ct: *mut X509_STORE_CTX) -> *mut X509;
pub fn X509_STORE_CTX_get_error(ctx: *mut X509_STORE_CTX) -> c_int;
pub fn X509_STORE_CTX_get_ex_data(ctx: *mut X509_STORE_CTX, idx: c_int) -> *mut c_void;
+ pub fn X509_STORE_CTX_get_error_depth(ctx: *mut X509_STORE_CTX) -> c_int;
pub fn X509V3_EXT_conf_nid(conf: *mut c_void, ctx: *mut X509V3_CTX, ext_nid: c_int, value: *mut c_char) -> *mut X509_EXTENSION;
pub fn X509V3_EXT_conf(conf: *mut c_void, ctx: *mut X509V3_CTX, name: *mut c_char, value: *mut c_char) -> *mut X509_EXTENSION;
@@ -767,6 +842,9 @@ extern "C" {
pub fn d2i_RSA_PUBKEY(k: *const *mut RSA, buf: *const *const u8, len: c_uint) -> *mut RSA;
pub fn i2d_RSAPrivateKey(k: *mut RSA, buf: *const *mut u8) -> c_int;
pub fn d2i_RSAPrivateKey(k: *const *mut RSA, buf: *const *const u8, len: c_uint) -> *mut RSA;
+
+ pub fn SSLeay() -> c_long;
+ pub fn SSLeay_version(key: c_int) -> *const c_char;
}
pub mod probe;
diff --git a/openssl/Cargo.toml b/openssl/Cargo.toml
index 10d8ea4b..3f5a4388 100644
--- a/openssl/Cargo.toml
+++ b/openssl/Cargo.toml
@@ -1,11 +1,11 @@
[package]
name = "openssl"
-version = "0.7.6"
+version = "0.7.11"
authors = ["Steven Fackler <[email protected]>"]
license = "Apache-2.0"
description = "OpenSSL bindings"
repository = "https://github.com/sfackler/rust-openssl"
-documentation = "https://sfackler.github.io/rust-openssl/doc/v0.7.6/openssl"
+documentation = "https://sfackler.github.io/rust-openssl/doc/v0.7.11/openssl"
readme = "../README.md"
keywords = ["crypto", "tls", "ssl", "dtls"]
build = "build.rs"
@@ -29,11 +29,11 @@ pkcs5_pbkdf2_hmac = ["openssl-sys/pkcs5_pbkdf2_hmac"]
nightly = []
[dependencies]
-bitflags = "0.4"
-lazy_static = "0.1"
+bitflags = ">= 0.5.0, < 0.8.0"
+lazy_static = "0.2"
libc = "0.2"
-openssl-sys = { version = "0.7.6", path = "../openssl-sys" }
-openssl-sys-extras = { version = "0.7.6", path = "../openssl-sys-extras" }
+openssl-sys = { version = "0.7.11", path = "../openssl-sys" }
+openssl-sys-extras = { version = "0.7.11", path = "../openssl-sys-extras" }
[build-dependencies]
gcc = "0.3"
diff --git a/openssl/src/c_helpers.c b/openssl/src/c_helpers.c
index 1b48565e..e884bebd 100644
--- a/openssl/src/c_helpers.c
+++ b/openssl/src/c_helpers.c
@@ -8,10 +8,6 @@ void rust_SSL_CTX_clone(SSL_CTX *ctx) {
CRYPTO_add(&ctx->references,1,CRYPTO_LOCK_SSL_CTX);
}
-void rust_EVP_PKEY_clone(EVP_PKEY *pkey) {
- CRYPTO_add(&pkey->references,1,CRYPTO_LOCK_EVP_PKEY);
-}
-
void rust_X509_clone(X509 *x509) {
CRYPTO_add(&x509->references,1,CRYPTO_LOCK_X509);
}
diff --git a/openssl/src/crypto/pkey.rs b/openssl/src/crypto/pkey.rs
index cafd50ad..ba0a16b6 100644
--- a/openssl/src/crypto/pkey.rs
+++ b/openssl/src/crypto/pkey.rs
@@ -53,10 +53,6 @@ fn openssl_hash_nid(hash: HashType) -> c_int {
}
}
-extern "C" {
- fn rust_EVP_PKEY_clone(pkey: *mut ffi::EVP_PKEY);
-}
-
pub struct PKey {
evp: *mut ffi::EVP_PKEY,
parts: Parts,
@@ -613,11 +609,19 @@ impl Drop for PKey {
impl Clone for PKey {
fn clone(&self) -> Self {
- unsafe {
- rust_EVP_PKEY_clone(self.evp);
+ let mut pkey = PKey::from_handle(unsafe { ffi::EVP_PKEY_new() }, self.parts);
+ // copy by encoding to DER and back
+ match self.parts {
+ Parts::Public => {
+ pkey.load_pub(&self.save_pub()[..]);
+ },
+ Parts::Both => {
+ pkey.load_priv(&self.save_priv()[..]);
+ },
+ Parts::Neither => {
+ },
}
-
- PKey::from_handle(self.evp, self.parts)
+ pkey
}
}
@@ -866,4 +870,38 @@ mod tests {
pkey.load_pub(&[]);
pkey.verify(&[], &[]);
}
+
+ #[test]
+ fn test_pkey_clone_creates_copy() {
+ let mut pkey = super::PKey::new();
+ pkey.gen(512);
+ let old_pkey_n = pkey.get_rsa().n().unwrap();
+
+ let mut pkey2 = pkey.clone();
+ pkey2.gen(512);
+
+ assert!(old_pkey_n == pkey.get_rsa().n().unwrap());
+ }
+
+ #[test]
+ fn test_pkey_clone_copies_private() {
+ let mut pkey = super::PKey::new();
+ pkey.gen(512);
+
+ let pkey2 = pkey.clone();
+
+ assert!(pkey.get_rsa().q().unwrap() == pkey2.get_rsa().q().unwrap());
+ }
+
+ #[test]
+ fn test_pkey_clone_copies_public() {
+ let mut pkey = super::PKey::new();
+ pkey.gen(512);
+ let mut pub_key = super::PKey::new();
+ pub_key.load_pub(&pkey.save_pub()[..]);
+
+ let pub_key2 = pub_key.clone();
+
+ assert!(pub_key.get_rsa().n().unwrap() == pub_key2.get_rsa().n().unwrap());
+ }
}
diff --git a/openssl/src/crypto/rsa.rs b/openssl/src/crypto/rsa.rs
index 6fcb5b07..021d450b 100644
--- a/openssl/src/crypto/rsa.rs
+++ b/openssl/src/crypto/rsa.rs
@@ -2,10 +2,11 @@ use ffi;
use std::fmt;
use ssl::error::{SslError, StreamError};
use std::ptr;
-use std::io::{self, Read};
+use std::io::{self, Read, Write};
use bn::BigNum;
use bio::MemBio;
+use nid::Nid;
pub struct RSA(*mut ffi::RSA);
@@ -28,6 +29,21 @@ impl RSA {
Ok(RSA(rsa))
}
}
+
+ pub fn from_private_components(n: BigNum, e: BigNum, d: BigNum, p: BigNum, q: BigNum, dp: BigNum, dq: BigNum, qi: BigNum) -> Result<RSA, SslError> {
+ unsafe {
+ let rsa = try_ssl_null!(ffi::RSA_new());
+ (*rsa).n = n.into_raw();
+ (*rsa).e = e.into_raw();
+ (*rsa).d = d.into_raw();
+ (*rsa).p = p.into_raw();
+ (*rsa).q = q.into_raw();
+ (*rsa).dmp1 = dp.into_raw();
+ (*rsa).dmq1 = dq.into_raw();
+ (*rsa).iqmp = qi.into_raw();
+ Ok(RSA(rsa))
+ }
+ }
/// the caller should assert that the rsa pointer is valid.
pub unsafe fn from_raw(rsa: *mut ffi::RSA) -> RSA {
@@ -49,6 +65,25 @@ impl RSA {
Ok(RSA(rsa))
}
}
+
+ /// Writes an RSA private key as unencrypted PEM formatted data
+ pub fn private_key_to_pem<W>(&self, writer: &mut W) -> Result<(), SslError>
+ where W: Write
+ {
+ let mut mem_bio = try!(MemBio::new());
+
+ let result = unsafe {
+ ffi::PEM_write_bio_RSAPrivateKey(mem_bio.get_handle(), self.0, ptr::null(), ptr::null_mut(), 0, None, ptr::null_mut())
+ };
+
+ if result == 1 {
+ try!(io::copy(&mut mem_bio, writer).map_err(StreamError));
+
+ Ok(())
+ } else {
+ Err(SslError::OpenSslErrors(vec![]))
+ }
+ }
/// Reads an RSA public key from PEM formatted data.
pub fn public_key_from_pem<R>(reader: &mut R) -> Result<RSA, SslError>
@@ -65,6 +100,60 @@ impl RSA {
Ok(RSA(rsa))
}
}
+
+ /// Writes an RSA public key as PEM formatted data
+ pub fn public_key_to_pem<W>(&self, writer: &mut W) -> Result<(), SslError>
+ where W: Write
+ {
+ let mut mem_bio = try!(MemBio::new());
+
+ let result = unsafe {
+ ffi::PEM_write_bio_RSA_PUBKEY(mem_bio.get_handle(), self.0)
+ };
+
+ if result == 1 {
+ try!(io::copy(&mut mem_bio, writer).map_err(StreamError));
+
+ Ok(())
+ } else {
+ Err(SslError::OpenSslErrors(vec![]))
+ }
+ }
+
+ pub fn size(&self) -> Result<u32, SslError> {
+ if self.has_n() {
+ unsafe {
+ Ok(ffi::RSA_size(self.0) as u32)
+ }
+ } else {
+ Err(SslError::OpenSslErrors(vec![]))
+ }
+ }
+
+ pub fn sign(&self, hash_id: Nid, message: &[u8]) -> Result<Vec<u8>, SslError> {
+ let k_len = try!(self.size());
+ let mut sig = vec![0;k_len as usize];
+ let mut sig_len = k_len;
+
+ unsafe {
+ let result = ffi::RSA_sign(hash_id as i32, message.as_ptr(), message.len() as u32, sig.as_mut_ptr(), &mut sig_len, self.0);
+ assert!(sig_len == k_len);
+
+ if result == 1 {
+ Ok(sig)
+ } else {
+ Err(SslError::OpenSslErrors(vec![]))
+ }
+ }
+ }
+
+ pub fn verify(&self, hash_id: Nid, message: &[u8], sig: &[u8]) -> Result<bool, SslError> {
+ unsafe {
+ let result = ffi::RSA_verify(hash_id as i32, message.as_ptr(), message.len() as u32, sig.as_ptr(), sig.len() as u32, self.0);
+
+ Ok(result == 1)
+ }
+ }
pub fn as_ptr(&self) -> *mut ffi::RSA {
self.0
@@ -119,3 +208,72 @@ impl fmt::Debug for RSA {
write!(f, "RSA")
}
}
+
+#[cfg(test)]
+mod test {
+ use nid;
+ use std::fs::File;
+ use std::io::Write;
+ use super::*;
+ use crypto::hash::*;
+
+ fn signing_input_rs256() -> Vec<u8> {
+ vec![101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 122, 73,
+ 49, 78, 105, 74, 57, 46, 101, 121, 74, 112, 99, 51, 77, 105, 79, 105,
+ 74, 113, 98, 50, 85, 105, 76, 65, 48, 75, 73, 67, 74, 108, 101, 72,
+ 65, 105, 79, 106, 69, 122, 77, 68, 65, 52, 77, 84, 107, 122, 79, 68,
+ 65, 115, 68, 81, 111, 103, 73, 109, 104, 48, 100, 72, 65, 54, 76,
+ 121, 57, 108, 101, 71, 70, 116, 99, 71, 120, 108, 76, 109, 78, 118,
+ 98, 83, 57, 112, 99, 49, 57, 121, 98, 50, 57, 48, 73, 106, 112, 48,
+ 99, 110, 86, 108, 102, 81]
+ }
+
+ fn signature_rs256() -> Vec<u8> {
+ vec![112, 46, 33, 137, 67, 232, 143, 209, 30, 181, 216, 45, 191, 120, 69,
+ 243, 65, 6, 174, 27, 129, 255, 247, 115, 17, 22, 173, 209, 113, 125,
+ 131, 101, 109, 66, 10, 253, 60, 150, 238, 221, 115, 162, 102, 62, 81,
+ 102, 104, 123, 0, 11, 135, 34, 110, 1, 135, 237, 16, 115, 249, 69,
+ 229, 130, 173, 252, 239, 22, 216, 90, 121, 142, 232, 198, 109, 219,
+ 61, 184, 151, 91, 23, 208, 148, 2, 190, 237, 213, 217, 217, 112, 7,
+ 16, 141, 178, 129, 96, 213, 248, 4, 12, 167, 68, 87, 98, 184, 31,
+ 190, 127, 249, 217, 46, 10, 231, 111, 36, 242, 91, 51, 187, 230, 244,
+ 74, 230, 30, 177, 4, 10, 203, 32, 4, 77, 62, 249, 18, 142, 212, 1,
+ 48, 121, 91, 212, 189, 59, 65, 238, 202, 208, 102, 171, 101, 25, 129,
+ 253, 228, 141, 247, 127, 55, 45, 195, 139, 159, 175, 221, 59, 239,
+ 177, 139, 93, 163, 204, 60, 46, 176, 47, 158, 58, 65, 214, 18, 202,
+ 173, 21, 145, 18, 115, 160, 95, 35, 185, 232, 56, 250, 175, 132, 157,
+ 105, 132, 41, 239, 90, 30, 136, 121, 130, 54, 195, 212, 14, 96, 69,
+ 34, 165, 68, 200, 242, 122, 122, 45, 184, 6, 99, 209, 108, 247, 202,
+ 234, 86, 222, 64, 92, 178, 33, 90, 69, 178, 194, 85, 102, 181, 90,
+ 193, 167, 72, 160, 112, 223, 200, 163, 42, 70, 149, 67, 208, 25, 238,
+ 251, 71]
+ }
+
+ #[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 mut sha = Hasher::new(Type::SHA256);
+ sha.write_all(&signing_input_rs256()).unwrap();
+ let digest = sha.finish();
+
+ let result = private_key.sign(nid::Nid::SHA256, &digest).unwrap();
+
+ assert_eq!(result, signature_rs256());
+ }
+
+ #[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 mut sha = Hasher::new(Type::SHA256);
+ sha.write_all(&signing_input_rs256()).unwrap();
+ let digest = sha.finish();
+
+ let result = public_key.verify(nid::Nid::SHA256, &digest, &signature_rs256()).unwrap();
+
+ assert!(result);
+ }
+} \ No newline at end of file
diff --git a/openssl/src/crypto/symm.rs b/openssl/src/crypto/symm.rs
index c0e845dc..935980f3 100644
--- a/openssl/src/crypto/symm.rs
+++ b/openssl/src/crypto/symm.rs
@@ -37,6 +37,9 @@ pub enum Type {
AES_256_CFB128,
AES_256_CFB8,
+ DES_CBC,
+ DES_ECB,
+
RC4_128,
}
@@ -362,4 +365,26 @@ mod tests {
cipher_test(super::Type::AES_256_CFB8, pt, ct, key, iv);
}
+
+ #[test]
+ fn test_des_cbc() {
+
+ let pt = "54686973206973206120746573742e";
+ let ct = "6f2867cfefda048a4046ef7e556c7132";
+ let key = "7cb66337f3d3c0fe";
+ let iv = "0001020304050607";
+
+ cipher_test(super::Type::DES_CBC, pt, ct, key, iv);
+ }
+
+ #[test]
+ fn test_des_ecb() {
+
+ let pt = "54686973206973206120746573742e";
+ let ct = "0050ab8aecec758843fe157b4dde938c";
+ let key = "7cb66337f3d3c0fe";
+ let iv = "0001020304050607";
+
+ cipher_test(super::Type::DES_ECB, pt, ct, key, iv);
+ }
}
diff --git a/openssl/src/crypto/symm_internal.rs b/openssl/src/crypto/symm_internal.rs
index 5c457f3f..ba01e1c1 100644
--- a/openssl/src/crypto/symm_internal.rs
+++ b/openssl/src/crypto/symm_internal.rs
@@ -26,6 +26,9 @@ pub fn evpc(t: symm::Type) -> (*const ffi::EVP_CIPHER, u32, u32) {
symm::Type::AES_256_CFB128 => (ffi::EVP_aes_256_cfb128(), 32, 16),
symm::Type::AES_256_CFB8 => (ffi::EVP_aes_256_cfb8(), 32, 16),
+ symm::Type::DES_CBC => (ffi::EVP_des_cbc(), 8, 8),
+ symm::Type::DES_ECB => (ffi::EVP_des_ecb(), 8, 8),
+
symm::Type::RC4_128 => (ffi::EVP_rc4(), 16, 0),
}
}
diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs
index f1b6d13a..f3be24b1 100644
--- a/openssl/src/lib.rs
+++ b/openssl/src/lib.rs
@@ -1,5 +1,5 @@
-#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.7.6")]
-#![cfg_attr(feature = "nightly", feature(const_fn, recover, panic_propagate))]
+#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.7.11")]
+#![cfg_attr(feature = "nightly", feature(const_fn))]
#[macro_use]
extern crate bitflags;
@@ -25,3 +25,4 @@ pub mod dh;
pub mod ssl;
pub mod x509;
pub mod nid;
+pub mod version;
diff --git a/openssl/src/nid.rs b/openssl/src/nid.rs
index bfcae15a..21ef18e0 100644
--- a/openssl/src/nid.rs
+++ b/openssl/src/nid.rs
@@ -2,7 +2,7 @@
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
#[repr(usize)]
pub enum Nid {
- Undefined,
+ Undefined, // 0
Rsadsi,
Pkcs,
MD2,
@@ -12,7 +12,7 @@ pub enum Nid {
RsaEncryption,
RSA_MD2,
RSA_MD5,
- PBE_MD2_DES,
+ PBE_MD2_DES, // 10
X500,
x509,
CN,
@@ -22,7 +22,7 @@ pub enum Nid {
O,
OU,
RSA,
- Pkcs7,
+ Pkcs7, // 20
Pkcs7_data,
Pkcs7_signedData,
Pkcs7_envelopedData,
@@ -32,7 +32,7 @@ pub enum Nid {
Pkcs3,
DhKeyAgreement,
DES_ECB,
- DES_CFB,
+ DES_CFB, // 30
DES_CBC,
DES_EDE,
DES_EDE3,
@@ -42,7 +42,7 @@ pub enum Nid {
RC2_CBC,
RC2_ECB,
RC2_CFB,
- RC2_OFB,
+ RC2_OFB, // 40
SHA,
RSA_SHA,
DES_EDE_CBC,
@@ -52,7 +52,7 @@ pub enum Nid {
Pkcs9,
Email,
UnstructuredName,
- ContentType,
+ ContentType, // 50
MessageDigest,
SigningTime,
CounterSignature,
@@ -62,7 +62,7 @@ pub enum Nid {
Netscape,
NetscapeCertExtention,
NetscapeDatatype,
- DES_EDE_CFB64,
+ DES_EDE_CFB64, // 60
DES_EDE3_CFB64,
DES_EDE_OFB64,
DES_EDE3_OFB64,
@@ -72,7 +72,7 @@ pub enum Nid {
DSA_OLD,
PBE_SHA1_RC2_64,
PBKDF2,
- DSA_SHA1_OLD,
+ DSA_SHA1_OLD, // 70
NetscapeCertType,
NetscapeBaseUrl,
NetscapeRevocationUrl,
@@ -82,7 +82,7 @@ pub enum Nid {
NetscapeSSLServerName,
NetscapeComment,
NetscapeCertSequence,
- DESX_CBC,
+ DESX_CBC, // 80
ID_CE,
SubjectKeyIdentifier,
KeyUsage,
@@ -92,7 +92,7 @@ pub enum Nid {
BasicConstraints,
CrlNumber,
CertificatePolicies,
- AuthorityKeyIdentifier,
+ AuthorityKeyIdentifier, // 90
BF_CBC,
BF_ECB,
BF_CFB,
@@ -102,7 +102,7 @@ pub enum Nid {
RC4_40,
RC2_40_CBC,
G,
- S,
+ S, // 100
I,
/// uniqueIdentifier
UID,
@@ -113,7 +113,7 @@ pub enum Nid {
D,
CAST5_CBC,
CAST5_ECB,
- CAST5_CFB,
+ CAST5_CFB, // 110
CAST5_OFB,
PbeWithMD5AndCast5CBC,
DSA_SHA1,
@@ -123,7 +123,7 @@ pub enum Nid {
RIPEMD160,
// 118 missing
RSA_RIPEMD160 = 119,
- RC5_CBC,
+ RC5_CBC, // 120
RC5_ECB,
RC5_CFB,
RC5_OFB,
@@ -133,7 +133,7 @@ pub enum Nid {
PKIX,
ID_KP,
ServerAuth,
- ClientAuth,
+ ClientAuth, // 130
CodeSigning,
EmailProtection,
TimeStamping,
@@ -143,7 +143,7 @@ pub enum Nid {
MsSGC,
MsEFS,
NsSGC,
- DeltaCRL,
+ DeltaCRL, // 140
CRLReason,
InvalidityDate,
SXNetID,
@@ -153,7 +153,7 @@ pub enum Nid {
PBE_SHA1_2DES,
PBE_SHA1_RC2_128,
PBE_SHA1_RC2_40,
- KeyBag,
+ KeyBag, // 150
Pkcs8ShroudedKeyBag,
CertBag,
CrlBag,
@@ -163,7 +163,7 @@ pub enum Nid {
LocalKeyID,
X509Certificate,
SdsiCertificate,
- X509Crl,
+ X509Crl, // 160
PBES2,
PBMAC1,
HmacWithSha1,
@@ -171,6 +171,28 @@ pub enum Nid {
ID_QT_UNOTICE,
RC2_64_CBC,
SMIMECaps,
+ PBE_MD2_RC2_64,
+ PBE_MD5_RC2_64,
+ PBE_SHA1_DES,
+ MicrosoftExtensionRequest,
+ ExtensionRequest,
+ Name,
+ DnQualifier,
+ IdPe,
+ IdAd,
+ AuthorityInfoAccess,
+ OCSP,
+ CaIssuers,
+ OCSPSigning, // 180
+
+ // 181 and up are from openssl's obj_mac.h
+
+
/// Shown as UID in cert subject
- UserId = 458
+ UserId = 458,
+
+
+ SHA256 = 672,
+ SHA384,
+ SHA512,
}
diff --git a/openssl/src/ssl/bio.rs b/openssl/src/ssl/bio.rs
index aa445562..e53545d7 100644
--- a/openssl/src/ssl/bio.rs
+++ b/openssl/src/ssl/bio.rs
@@ -1,5 +1,5 @@
use libc::{c_char, c_int, c_long, c_void, strlen};
-use ffi::{BIO, BIO_METHOD, BIO_CTRL_FLUSH, BIO_TYPE_NONE, BIO_new};
+use ffi::{self, BIO, BIO_CTRL_FLUSH, BIO_TYPE_NONE, BIO_new};
use ffi_extras::{BIO_clear_retry_flags, BIO_set_retry_read, BIO_set_retry_write};
use std::any::Any;
use std::io;
@@ -11,28 +11,36 @@ use std::sync::Arc;
use ssl::error::SslError;
-// "rust"
-const NAME: [c_char; 5] = [114, 117, 115, 116, 0];
-
pub struct StreamState<S> {
pub stream: S,
pub error: Option<io::Error>,
pub panic: Option<Box<Any + Send>>,
}
-pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, Arc<BIO_METHOD>), SslError> {
- let method = Arc::new(BIO_METHOD {
- type_: BIO_TYPE_NONE,
- name: &NAME[0],
- bwrite: Some(bwrite::<S>),
- bread: Some(bread::<S>),
- bputs: Some(bputs::<S>),
- bgets: None,
- ctrl: Some(ctrl::<S>),
- create: Some(create),
- destroy: Some(destroy::<S>),
- callback_ctrl: None,
- });
+/// Safe wrapper for BIO_METHOD
+pub struct BioMethod(ffi::BIO_METHOD);
+
+impl BioMethod {
+ pub fn new<S: Read + Write>() -> BioMethod {
+ BioMethod(ffi::BIO_METHOD {
+ type_: BIO_TYPE_NONE,
+ name: b"rust\0".as_ptr() as *const _,
+ bwrite: Some(bwrite::<S>),
+ bread: Some(bread::<S>),
+ bputs: Some(bputs::<S>),
+ bgets: None,
+ ctrl: Some(ctrl::<S>),
+ create: Some(create),
+ destroy: Some(destroy::<S>),
+ callback_ctrl: None,
+ })
+ }
+}
+
+unsafe impl Send for BioMethod {}
+
+pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, Arc<BioMethod>), SslError> {
+ let method = Arc::new(BioMethod::new::<S>());
let state = Box::new(StreamState {
stream: stream,
@@ -41,7 +49,7 @@ pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, Arc<BIO_METHOD>), Ss
});
unsafe {
- let bio = try_ssl_null!(BIO_new(&*method));
+ let bio = try_ssl_null!(BIO_new(&method.0));
(*bio).ptr = Box::into_raw(state) as *mut _;
(*bio).init = 1;
@@ -74,56 +82,22 @@ unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S> {
}
#[cfg(feature = "nightly")]
-fn recover<F, T>(f: F) -> Result<T, Box<Any + Send>> where F: FnOnce() -> T + ::std::panic::RecoverSafe {
- ::std::panic::recover(f)
+fn catch_unwind<F, T>(f: F) -> Result<T, Box<Any + Send>> where F: FnOnce() -> T {
+ ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(f))
}
#[cfg(not(feature = "nightly"))]
-fn recover<F, T>(f: F) -> Result<T, Box<Any + Send>> where F: FnOnce() -> T {
+fn catch_unwind<F, T>(f: F) -> Result<T, Box<Any + Send>> where F: FnOnce() -> T {
Ok(f())
}
-#[cfg(feature = "nightly")]
-use std::panic::AssertRecoverSafe;
-
-#[cfg(not(feature = "nightly"))]
-struct AssertRecoverSafe<T>(T);
-
-#[cfg(not(feature = "nightly"))]
-impl<T> AssertRecoverSafe<T> {
- fn new(t: T) -> Self {
- AssertRecoverSafe(t)
- }
-}
-
-#[cfg(not(feature = "nightly"))]
-impl<T> ::std::ops::Deref for AssertRecoverSafe<T> {
- type Target = T;
-
- fn deref(&self) -> &T {
- &self.0
- }
-}
-
-#[cfg(not(feature = "nightly"))]
-impl<T> ::std::ops::DerefMut for AssertRecoverSafe<T> {
- fn deref_mut(&mut self) -> &mut T {
- &mut self.0
- }
-}
-
unsafe extern "C" fn bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int {
BIO_clear_retry_flags(bio);
let state = state::<S>(bio);
let buf = slice::from_raw_parts(buf as *const _, len as usize);
- let result = {
- let mut youre_not_my_supervisor = AssertRecoverSafe::new(&mut *state);
- recover(move || youre_not_my_supervisor.stream.write(buf))
- };
-
- match result {
+ match catch_unwind(|| state.stream.write(buf)) {
Ok(Ok(len)) => len as c_int,
Ok(Err(err)) => {
if retriable_error(&err) {
@@ -145,13 +119,7 @@ unsafe extern "C" fn bread<S: Read>(bio: *mut BIO, buf: *mut c_char, len: c_int)
let state = state::<S>(bio);
let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize);
- let result = {
- let mut youre_not_my_supervisor = AssertRecoverSafe::new(&mut *state);
- let mut fuuuu = AssertRecoverSafe::new(buf);
- recover(move || youre_not_my_supervisor.stream.read(&mut *fuuuu))
- };
-
- match result {
+ match catch_unwind(|| state.stream.read(buf)) {
Ok(Ok(len)) => len as c_int,
Ok(Err(err)) => {
if retriable_error(&err) {
@@ -185,12 +153,8 @@ unsafe extern "C" fn ctrl<S: Write>(bio: *mut BIO,
-> c_long {
if cmd == BIO_CTRL_FLUSH {
let state = state::<S>(bio);
- let result = {
- let mut youre_not_my_supervisor = AssertRecoverSafe::new(&mut *state);
- recover(move || youre_not_my_supervisor.stream.flush())
- };
- match result {
+ match catch_unwind(|| state.stream.flush()) {
Ok(Ok(())) => 1,
Ok(Err(err)) => {
state.error = Some(err);
diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs
index 574a324b..aa785142 100644
--- a/openssl/src/ssl/mod.rs
+++ b/openssl/src/ssl/mod.rs
@@ -35,6 +35,8 @@ mod bio;
#[cfg(test)]
mod tests;
+use self::bio::BioMethod;
+
#[doc(inline)]
pub use ssl::error::Error;
@@ -67,44 +69,44 @@ pub fn init() {
}
bitflags! {
- flags SslContextOptions: u64 {
- const SSL_OP_MICROSOFT_SESS_ID_BUG = ffi_extras::SSL_OP_MICROSOFT_SESS_ID_BUG,
- const SSL_OP_NETSCAPE_CHALLENGE_BUG = ffi_extras::SSL_OP_NETSCAPE_CHALLENGE_BUG,
- const SSL_OP_LEGACY_SERVER_CONNECT = ffi_extras::SSL_OP_LEGACY_SERVER_CONNECT,
- const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = ffi_extras::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG,
- const SSL_OP_TLSEXT_PADDING = ffi_extras::SSL_OP_TLSEXT_PADDING,
- const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = ffi_extras::SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER,
- const SSL_OP_SAFARI_ECDHE_ECDSA_BUG = ffi_extras::SSL_OP_SAFARI_ECDHE_ECDSA_BUG,
- const SSL_OP_SSLEAY_080_CLIENT_DH_BUG = ffi_extras::SSL_OP_SSLEAY_080_CLIENT_DH_BUG,
- const SSL_OP_TLS_D5_BUG = ffi_extras::SSL_OP_TLS_D5_BUG,
- const SSL_OP_TLS_BLOCK_PADDING_BUG = ffi_extras::SSL_OP_TLS_BLOCK_PADDING_BUG,
- const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = ffi_extras::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS,
- const SSL_OP_NO_QUERY_MTU = ffi_extras::SSL_OP_NO_QUERY_MTU,
- const SSL_OP_COOKIE_EXCHANGE = ffi_extras::SSL_OP_COOKIE_EXCHANGE,
- const SSL_OP_NO_TICKET = ffi_extras::SSL_OP_NO_TICKET,
- const SSL_OP_CISCO_ANYCONNECT = ffi_extras::SSL_OP_CISCO_ANYCONNECT,
- const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = ffi_extras::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION,
- const SSL_OP_NO_COMPRESSION = ffi_extras::SSL_OP_NO_COMPRESSION,
- const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = ffi_extras::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION,
- const SSL_OP_SINGLE_ECDH_USE = ffi_extras::SSL_OP_SINGLE_ECDH_USE,
- const SSL_OP_SINGLE_DH_USE = ffi_extras::SSL_OP_SINGLE_DH_USE,
- const SSL_OP_CIPHER_SERVER_PREFERENCE = ffi_extras::SSL_OP_CIPHER_SERVER_PREFERENCE,
- const SSL_OP_TLS_ROLLBACK_BUG = ffi_extras::SSL_OP_TLS_ROLLBACK_BUG,
- const SSL_OP_NO_SSLV2 = ffi_extras::SSL_OP_NO_SSLv2,
- const SSL_OP_NO_SSLV3 = ffi_extras::SSL_OP_NO_SSLv3,
- const SSL_OP_NO_DTLSV1 = ffi_extras::SSL_OP_NO_DTLSv1,
- const SSL_OP_NO_TLSV1 = ffi_extras::SSL_OP_NO_TLSv1,
- const SSL_OP_NO_DTLSV1_2 = ffi_extras::SSL_OP_NO_DTLSv1_2,
- const SSL_OP_NO_TLSV1_2 = ffi_extras::SSL_OP_NO_TLSv1_2,
- const SSL_OP_NO_TLSV1_1 = ffi_extras::SSL_OP_NO_TLSv1_1,
- const SSL_OP_NETSCAPE_CA_DN_BUG = ffi_extras::SSL_OP_NETSCAPE_CA_DN_BUG,
- const SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = ffi_extras::SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG,
- const SSL_OP_CRYPTOPRO_TLSEXT_BUG = ffi_extras::SSL_OP_CRYPTOPRO_TLSEXT_BUG,
- const SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG = ffi_extras::SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG,
- const SSL_OP_MSIE_SSLV2_RSA_PADDING = ffi_extras::SSL_OP_MSIE_SSLV2_RSA_PADDING,
- const SSL_OP_PKCS1_CHECK_1 = ffi_extras::SSL_OP_PKCS1_CHECK_1,
- const SSL_OP_PKCS1_CHECK_2 = ffi_extras::SSL_OP_PKCS1_CHECK_2,
- const SSL_OP_EPHEMERAL_RSA = ffi_extras::SSL_OP_EPHEMERAL_RSA,
+ pub flags SslContextOptions: u64 {
+ const SSL_OP_MICROSOFT_SESS_ID_BUG = ::ffi_extras::SSL_OP_MICROSOFT_SESS_ID_BUG,
+ const SSL_OP_NETSCAPE_CHALLENGE_BUG = ::ffi_extras::SSL_OP_NETSCAPE_CHALLENGE_BUG,
+ const SSL_OP_LEGACY_SERVER_CONNECT = ::ffi_extras::SSL_OP_LEGACY_SERVER_CONNECT,
+ const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = ::ffi_extras::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG,
+ const SSL_OP_TLSEXT_PADDING = ::ffi_extras::SSL_OP_TLSEXT_PADDING,
+ const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = ::ffi_extras::SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER,
+ const SSL_OP_SAFARI_ECDHE_ECDSA_BUG = ::ffi_extras::SSL_OP_SAFARI_ECDHE_ECDSA_BUG,
+ const SSL_OP_SSLEAY_080_CLIENT_DH_BUG = ::ffi_extras::SSL_OP_SSLEAY_080_CLIENT_DH_BUG,
+ const SSL_OP_TLS_D5_BUG = ::ffi_extras::SSL_OP_TLS_D5_BUG,
+ const SSL_OP_TLS_BLOCK_PADDING_BUG = ::ffi_extras::SSL_OP_TLS_BLOCK_PADDING_BUG,
+ const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = ::ffi_extras::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS,
+ const SSL_OP_NO_QUERY_MTU = ::ffi_extras::SSL_OP_NO_QUERY_MTU,
+ const SSL_OP_COOKIE_EXCHANGE = ::ffi_extras::SSL_OP_COOKIE_EXCHANGE,
+ const SSL_OP_NO_TICKET = ::ffi_extras::SSL_OP_NO_TICKET,
+ const SSL_OP_CISCO_ANYCONNECT = ::ffi_extras::SSL_OP_CISCO_ANYCONNECT,
+ const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = ::ffi_extras::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION,
+ const SSL_OP_NO_COMPRESSION = ::ffi_extras::SSL_OP_NO_COMPRESSION,
+ const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = ::ffi_extras::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION,
+ const SSL_OP_SINGLE_ECDH_USE = ::ffi_extras::SSL_OP_SINGLE_ECDH_USE,
+ const SSL_OP_SINGLE_DH_USE = ::ffi_extras::SSL_OP_SINGLE_DH_USE,
+ const SSL_OP_CIPHER_SERVER_PREFERENCE = ::ffi_extras::SSL_OP_CIPHER_SERVER_PREFERENCE,
+ const SSL_OP_TLS_ROLLBACK_BUG = ::ffi_extras::SSL_OP_TLS_ROLLBACK_BUG,
+ const SSL_OP_NO_SSLV2 = ::ffi_extras::SSL_OP_NO_SSLv2,
+ const SSL_OP_NO_SSLV3 = ::ffi_extras::SSL_OP_NO_SSLv3,
+ const SSL_OP_NO_DTLSV1 = ::ffi_extras::SSL_OP_NO_DTLSv1,
+ const SSL_OP_NO_TLSV1 = ::ffi_extras::SSL_OP_NO_TLSv1,
+ const SSL_OP_NO_DTLSV1_2 = ::ffi_extras::SSL_OP_NO_DTLSv1_2,
+ const SSL_OP_NO_TLSV1_2 = ::ffi_extras::SSL_OP_NO_TLSv1_2,
+ const SSL_OP_NO_TLSV1_1 = ::ffi_extras::SSL_OP_NO_TLSv1_1,
+ const SSL_OP_NETSCAPE_CA_DN_BUG = ::ffi_extras::SSL_OP_NETSCAPE_CA_DN_BUG,
+ const SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = ::ffi_extras::SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG,
+ const SSL_OP_CRYPTOPRO_TLSEXT_BUG = ::ffi_extras::SSL_OP_CRYPTOPRO_TLSEXT_BUG,
+ const SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG = ::ffi_extras::SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG,
+ const SSL_OP_MSIE_SSLV2_RSA_PADDING = ::ffi_extras::SSL_OP_MSIE_SSLV2_RSA_PADDING,
+ const SSL_OP_PKCS1_CHECK_1 = ::ffi_extras::SSL_OP_PKCS1_CHECK_1,
+ const SSL_OP_PKCS1_CHECK_2 = ::ffi_extras::SSL_OP_PKCS1_CHECK_2,
+ const SSL_OP_EPHEMERAL_RSA = ::ffi_extras::SSL_OP_EPHEMERAL_RSA,
const SSL_OP_ALL = SSL_OP_MICROSOFT_SESS_ID_BUG.bits|SSL_OP_NETSCAPE_CHALLENGE_BUG.bits
|SSL_OP_LEGACY_SERVER_CONNECT.bits|SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG.bits
|SSL_OP_TLSEXT_PADDING.bits|SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER.bits
@@ -212,19 +214,20 @@ impl SslMethod {
/// Determines the type of certificate verification used
bitflags! {
- flags SslVerifyMode: i32 {
+ pub flags SslVerifyMode: i32 {
/// Verify that the server's certificate is trusted
- const SSL_VERIFY_PEER = ffi::SSL_VERIFY_PEER,
+ const SSL_VERIFY_PEER = ::ffi::SSL_VERIFY_PEER,
/// Do not verify the server's certificate
- const SSL_VERIFY_NONE = ffi::SSL_VERIFY_NONE,
+ const SSL_VERIFY_NONE = ::ffi::SSL_VERIFY_NONE,
/// Terminate handshake if client did not return a certificate.
/// Use together with SSL_VERIFY_PEER.
- const SSL_VERIFY_FAIL_IF_NO_PEER_CERT = ffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ const SSL_VERIFY_FAIL_IF_NO_PEER_CERT = ::ffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
}
}
lazy_static! {
static ref INDEXES: Mutex<HashMap<TypeId, c_int>> = Mutex::new(HashMap::new());
+ static ref SSL_INDEXES: Mutex<HashMap<TypeId, c_int>> = Mutex::new(HashMap::new());
}
// Creates a static index for user data of type T
@@ -234,6 +237,10 @@ fn get_verify_data_idx<T: Any + 'static>() -> c_int {
*INDEXES.lock().unwrap().entry(TypeId::of::<T>()).or_insert_with(|| get_new_idx::<T>())
}
+fn get_ssl_verify_data_idx<T: Any + 'static>() -> c_int {
+ *SSL_INDEXES.lock().unwrap().entry(TypeId::of::<T>()).or_insert_with(|| get_new_ssl_idx::<T>())
+}
+
#[cfg(feature = "npn")]
lazy_static! {
static ref NPN_PROTOS_IDX: c_int = get_new_idx::<Vec<u8>>();
@@ -265,6 +272,26 @@ fn get_new_idx<T>() -> c_int {
}
}
+fn get_new_ssl_idx<T>() -> c_int {
+ extern "C" fn free_data_box<T>(_parent: *mut c_void,
+ ptr: *mut c_void,
+ _ad: *mut ffi::CRYPTO_EX_DATA,
+ _idx: c_int,
+ _argl: c_long,
+ _argp: *mut c_void) {
+ if !ptr.is_null() {
+ let _: Box<T> = unsafe { mem::transmute(ptr) };
+ }
+ }
+
+ unsafe {
+ let f: ffi::CRYPTO_EX_free = free_data_box::<T>;
+ let idx = ffi::SSL_get_ex_new_index(0, ptr::null(), None, None, Some(f));
+ assert!(idx >= 0);
+ idx
+ }
+}
+
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();
@@ -309,6 +336,21 @@ extern "C" fn raw_verify_with_data<T>(preverify_ok: c_int,
}
}
+extern "C" fn ssl_raw_verify<F>(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 verify = ffi::SSL_get_ex_data(ssl, get_ssl_verify_data_idx::<F>());
+ let verify: &F = mem::transmute(verify);
+
+ let ctx = X509StoreContext::new(x509_ctx);
+
+ verify(preverify_ok != 0, &ctx) as c_int
+ }
+}
+
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);
@@ -610,6 +652,15 @@ impl SslContext {
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.
+ ///
+ /// 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> {
+ 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<P: AsRef<Path>>(&mut self, file: P) -> Result<(), SslError> {
@@ -619,6 +670,20 @@ impl SslContext {
})
}
+ /// Set the context identifier for sessions
+ ///
+ /// This value identifies the server's session cache to a clients, telling them when they're
+ /// able to reuse sessions. Should be set to a unique value per server, unless multiple servers
+ /// share a session cache.
+ ///
+ /// 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> {
+ wrap_ssl_result(unsafe {
+ ffi::SSL_CTX_set_session_id_context(self.ctx, sid_ctx.as_ptr(), sid_ctx.len() as u32)
+ })
+ }
+
/// Specifies the file that contains certificate
pub fn set_certificate_file<P: AsRef<Path>>(&mut self,
file: P,
@@ -823,11 +888,11 @@ impl <'a> SslCipher<'a> {
pub fn description(&self) -> Option<String> {
unsafe {
// SSL_CIPHER_description requires a buffer of at least 128 bytes.
- let mut buf = [0i8; 128];
- let desc_ptr = ffi::SSL_CIPHER_description(self.cipher, &mut buf[0], 128);
+ let mut buf = [0; 128];
+ let desc_ptr = ffi::SSL_CIPHER_description(self.cipher, buf.as_mut_ptr(), 128);
if !desc_ptr.is_null() {
- String::from_utf8(CStr::from_ptr(desc_ptr).to_bytes().to_vec()).ok()
+ String::from_utf8(CStr::from_ptr(desc_ptr as *const _).to_bytes().to_vec()).ok()
} else {
None
}
@@ -903,6 +968,30 @@ impl Ssl {
}
}
+ /// Sets the verification mode to be used during the handshake process.
+ ///
+ /// Use `set_verify_callback` to additionally add a callback.
+ pub fn set_verify(&mut self, mode: SslVerifyMode) {
+ unsafe { ffi::SSL_set_verify(self.ssl, mode.bits as c_int, None) }
+ }
+
+ /// Sets the certificate verification callback to be used during the
+ /// handshake process.
+ ///
+ /// The callback is provided with a boolean indicating if the
+ /// preveification process was successful, and an object providing access
+ /// to the certificate chain. It should return `true` if the certificate
+ /// chain is valid and `false` otherwise.
+ pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F)
+ where F: Fn(bool, &X509StoreContext) -> bool + Any + 'static + Sync + Send
+ {
+ unsafe {
+ let verify = Box::new(verify);
+ ffi::SSL_set_ex_data(self.ssl, get_ssl_verify_data_idx::<F>(), mem::transmute(verify));
+ ffi::SSL_set_verify(self.ssl, mode.bits as c_int, Some(ssl_raw_verify::<F>));
+ }
+ }
+
pub fn get_current_cipher<'a>(&'a self) -> Option<SslCipher<'a>> {
unsafe {
let ptr = ffi::SSL_get_current_cipher(self.ssl);
@@ -1117,12 +1206,10 @@ make_LibSslError! {
/// A stream wrapper which handles SSL encryption for an underlying stream.
pub struct SslStream<S> {
ssl: Ssl,
- _method: Arc<ffi::BIO_METHOD>, // NOTE: this *must* be after the Ssl field so things drop right
+ _method: Arc<BioMethod>, // NOTE: this *must* be after the Ssl field so things drop right
_p: PhantomData<S>,
}
-unsafe impl<S: Send> Send for SslStream<S> {}
-
/// # Deprecated
///
/// This method does not behave as expected and will be removed in a future
@@ -1309,7 +1396,7 @@ impl<S> SslStream<S> {
#[cfg(feature = "nightly")]
fn check_panic(&mut self) {
if let Some(err) = unsafe { bio::take_panic::<S>(self.ssl.get_raw_rbio()) } {
- ::std::panic::propagate(err)
+ ::std::panic::resume_unwind(err)
}
}
diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs
index be35d7ef..ccdc44e4 100644
--- a/openssl/src/ssl/tests/mod.rs
+++ b/openssl/src/ssl/tests/mod.rs
@@ -196,7 +196,7 @@ macro_rules! run_test(
use ssl::SslMethod;
use ssl::{SslContext, Ssl, SslStream, VerifyCallback};
use ssl::SSL_VERIFY_PEER;
- use crypto::hash::Type::SHA256;
+ use crypto::hash::Type::SHA1;
use x509::X509StoreContext;
use serialize::hex::FromHex;
use super::Server;
@@ -359,7 +359,7 @@ run_test!(verify_callback_data, |method, stream| {
match cert {
None => false,
Some(cert) => {
- let fingerprint = cert.fingerprint(SHA256).unwrap();
+ let fingerprint = cert.fingerprint(SHA1).unwrap();
&fingerprint == node_id
}
}
@@ -370,7 +370,7 @@ run_test!(verify_callback_data, |method, stream| {
// in DER format.
// Command: openssl x509 -in test/cert.pem -outform DER | openssl dgst -sha256
// Please update if "test/cert.pem" will ever change
- let node_hash_str = "db400bb62f1b1f29c3b8f323b8f7d9dea724fdcd67104ef549c772ae3749655b";
+ 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_depth(1);
@@ -381,6 +381,36 @@ run_test!(verify_callback_data, |method, stream| {
}
});
+run_test!(ssl_verify_callback, |method, stream| {
+ use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+ use ssl::IntoSsl;
+
+ static CHECKED: AtomicUsize = ATOMIC_USIZE_INIT;
+
+ let ctx = SslContext::new(method).unwrap();
+ let mut ssl = ctx.into_ssl().unwrap();
+
+ let node_hash_str = "E19427DAC79FBE758394945276A6E4F15F0BEBE6";
+ let node_id = node_hash_str.from_hex().unwrap();
+ ssl.set_verify_callback(SSL_VERIFY_PEER, move |_, x509| {
+ CHECKED.store(1, Ordering::SeqCst);
+ match x509.get_current_cert() {
+ None => false,
+ Some(cert) => {
+ let fingerprint = cert.fingerprint(SHA1).unwrap();
+ fingerprint == node_id
+ }
+ }
+ });
+
+ match SslStream::connect_generic(ssl, stream) {
+ Ok(_) => (),
+ Err(err) => panic!("Expected success, got {:?}", err)
+ }
+
+ assert_eq!(CHECKED.load(Ordering::SeqCst), 1);
+});
+
// Make sure every write call translates to a write call to the underlying socket.
#[test]
fn test_write_hits_stream() {
@@ -472,8 +502,8 @@ run_test!(get_peer_certificate, |method, stream| {
let stream = SslStream::connect_generic(&SslContext::new(method).unwrap(),
stream).unwrap();
let cert = stream.ssl().peer_certificate().unwrap();
- let fingerprint = cert.fingerprint(SHA256).unwrap();
- let node_hash_str = "db400bb62f1b1f29c3b8f323b8f7d9dea724fdcd67104ef549c772ae3749655b";
+ let fingerprint = cert.fingerprint(SHA1).unwrap();
+ let node_hash_str = "E19427DAC79FBE758394945276A6E4F15F0BEBE6";
let node_id = node_hash_str.from_hex().unwrap();
assert_eq!(node_id, fingerprint)
});
@@ -1059,3 +1089,21 @@ fn refcount_ssl_context() {
let _new_ctx_b = ssl.set_ssl_context(&new_ctx_a);
}
}
+
+#[test]
+#[cfg_attr(windows, ignore)] // don't have a trusted CA list easily available :(
+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);
+ let s = TcpStream::connect("google.com:443").unwrap();
+ let mut socket = SslStream::connect(&ctx, s).unwrap();
+
+ socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
+ let mut result = vec![];
+ socket.read_to_end(&mut result).unwrap();
+
+ println!("{}", String::from_utf8_lossy(&result));
+ assert!(result.starts_with(b"HTTP/1.0"));
+ assert!(result.ends_with(b"</HTML>\r\n") || result.ends_with(b"</html>"));
+}
diff --git a/openssl/src/version.rs b/openssl/src/version.rs
new file mode 100644
index 00000000..323cf1e2
--- /dev/null
+++ b/openssl/src/version.rs
@@ -0,0 +1,89 @@
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+use ffi;
+use std::ffi::CStr;
+
+/// OPENSSL_VERSION_NUMBER is a numeric release version identifier:
+///
+/// `MNNFFPPS: major minor fix patch status`
+///
+/// The status nibble has one of the values 0 for development, 1 to e for betas 1 to 14, and f for release.
+///
+/// for example
+///
+/// `0x000906000 == 0.9.6 dev`
+/// `0x000906023 == 0.9.6b beta 3`
+/// `0x00090605f == 0.9.6e release`
+///
+/// Versions prior to 0.9.3 have identifiers < 0x0930. Versions between 0.9.3 and 0.9.5 had a version identifier with this interpretation:
+///
+/// `MMNNFFRBB major minor fix final beta/patch`
+///
+/// for example
+///
+/// `0x000904100 == 0.9.4 release`
+/// `0x000905000 == 0.9.5 dev`
+///
+/// Version 0.9.5a had an interim interpretation that is like the current one, except the patch level got the highest bit set, to keep continuity. The number was therefore 0x0090581f
+///
+/// The return value of this function can be compared to the macro to make sure that the correct version of the library has been loaded, especially when using DLLs on Windows systems.
+pub fn number() -> i64 {
+ unsafe { ffi::SSLeay() as i64 }
+}
+
+
+/// The text variant of the version number and the release date. For example, "OpenSSL 0.9.5a 1 Apr 2000".
+pub fn version() -> &'static str {
+ unsafe { CStr::from_ptr(ffi::SSLeay_version(ffi::SSLEAY_VERSION)).to_str().unwrap() }
+}
+
+/// The compiler flags set for the compilation process in the form "compiler: ..." if available or
+/// "compiler: information not available" otherwise.
+pub fn c_flags() -> &'static str {
+ unsafe { CStr::from_ptr(ffi::SSLeay_version(ffi::SSLEAY_CFLAGS)).to_str().unwrap() }
+}
+
+/// The date of the build process in the form "built on: ..." if available or "built on: date not available" otherwise.
+pub fn built_on() -> &'static str {
+ unsafe { CStr::from_ptr(ffi::SSLeay_version(ffi::SSLEAY_BUILT_ON)).to_str().unwrap() }
+}
+
+/// The "Configure" target of the library build in the form "platform: ..." if available or "platform: information not available" otherwise.
+pub fn platform() -> &'static str {
+ unsafe { CStr::from_ptr(ffi::SSLeay_version(ffi::SSLEAY_PLATFORM)).to_str().unwrap() }
+}
+
+/// The "OPENSSLDIR" setting of the library build in the form "OPENSSLDIR: "..."" if available or "OPENSSLDIR: N/A" otherwise.
+pub fn dir() -> &'static str {
+ unsafe { CStr::from_ptr(ffi::SSLeay_version(ffi::SSLEAY_DIR)).to_str().unwrap() }
+}
+
+/// This test ensures that we do not segfault when calling the functions of this module
+/// and that the strings respect a reasonable format.
+#[test]
+fn test_versions() {
+ println!("Number: '{}'", number());
+ println!("Version: '{}'", version());
+ println!("C flags: '{}'", c_flags());
+ println!("Built on: '{}'", built_on());
+ println!("Platform: '{}'", platform());
+ println!("Dir: '{}'", dir());
+
+ assert!(number() > 0);
+ assert!(version().starts_with("OpenSSL"));
+ assert!(c_flags().starts_with("compiler:"));
+ assert!(built_on().starts_with("built on:"));
+ assert!(dir().starts_with("OPENSSLDIR:"));
+} \ No newline at end of file
diff --git a/openssl/src/x509/extension.rs b/openssl/src/x509/extension.rs
index 88cb64a2..99ef62c1 100644
--- a/openssl/src/x509/extension.rs
+++ b/openssl/src/x509/extension.rs
@@ -1,4 +1,5 @@
use std::fmt;
+
use nid::Nid;
/// Type-only version of the `Extension` enum.
@@ -79,7 +80,7 @@ impl ExtensionType {
}
}
- pub fn get_name<'a>(&'a self) -> Option<&'a str> {
+ pub fn get_name(&self) -> Option<&str> {
match self {
&ExtensionType::OtherStr(ref s) => Some(s),
_ => None,
diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs
index a69f61d5..0a242a15 100644
--- a/openssl/src/x509/mod.rs
+++ b/openssl/src/x509/mod.rs
@@ -2,14 +2,16 @@ 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, CStr};
+use std::ffi::CString;
use std::iter::repeat;
use std::mem;
use std::ptr;
use std::ops::Deref;
use std::fmt;
use std::str;
+use std::slice;
use std::collections::HashMap;
+use std::marker::PhantomData;
use asn1::Asn1Time;
use bio::MemBio;
@@ -20,7 +22,7 @@ use crypto::rand::rand_bytes;
use ffi;
use ffi_extras;
use ssl::error::{SslError, StreamError};
-use nid;
+use nid::Nid;
pub mod extension;
@@ -29,14 +31,12 @@ use self::extension::{ExtensionType, Extension};
#[cfg(test)]
mod tests;
-pub struct SslString {
- s: &'static str,
-}
+pub struct SslString(&'static str);
impl<'s> Drop for SslString {
fn drop(&mut self) {
unsafe {
- ffi::CRYPTO_free(self.s.as_ptr() as *mut c_void);
+ ffi::CRYPTO_free(self.0.as_ptr() as *mut c_void);
}
}
}
@@ -45,25 +45,26 @@ impl Deref for SslString {
type Target = str;
fn deref(&self) -> &str {
- self.s
+ self.0
}
}
impl SslString {
- unsafe fn new(buf: *const c_char) -> SslString {
- SslString { s: str::from_utf8(CStr::from_ptr(buf as *const _).to_bytes()).unwrap() }
+ unsafe fn new(buf: *const c_char, len: c_int) -> SslString {
+ let slice = slice::from_raw_parts(buf as *const _, len as usize);
+ SslString(str::from_utf8_unchecked(slice))
}
}
impl fmt::Display for SslString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(self.s, f)
+ fmt::Display::fmt(self.0, f)
}
}
impl fmt::Debug for SslString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Debug::fmt(self.s, f)
+ fmt::Debug::fmt(self.0, f)
}
}
@@ -103,6 +104,10 @@ impl X509StoreContext {
})
}
}
+
+ pub fn error_depth(&self) -> u32 {
+ unsafe { ffi::X509_STORE_CTX_get_error_depth(self.ctx) as u32 }
+ }
}
#[allow(non_snake_case)]
@@ -464,6 +469,24 @@ impl<'ctx> X509<'ctx> {
}
}
+ /// Returns this certificate's SAN entries, if they exist.
+ pub fn subject_alt_names<'a>(&'a self) -> Option<GeneralNames<'a>> {
+ unsafe {
+ let stack = ffi::X509_get_ext_d2i(self.handle,
+ Nid::SubjectAltName as c_int,
+ ptr::null_mut(),
+ ptr::null_mut());
+ if stack.is_null() {
+ return None;
+ }
+
+ Some(GeneralNames {
+ stack: stack as *const _,
+ m: PhantomData,
+ })
+ }
+ }
+
pub fn public_key(&self) -> PKey {
let pkey = unsafe { ffi::X509_get_pubkey(self.handle) };
assert!(!pkey.is_null());
@@ -544,7 +567,7 @@ pub struct X509NameEntry<'x> {
}
impl<'x> X509Name<'x> {
- pub fn text_by_nid(&self, nid: nid::Nid) -> Option<SslString> {
+ pub fn text_by_nid(&self, nid: Nid) -> Option<SslString> {
unsafe {
let loc = ffi::X509_NAME_get_index_by_NID(self.name, nid as c_int, -1);
if loc == -1 {
@@ -570,7 +593,7 @@ impl<'x> X509Name<'x> {
assert!(!str_from_asn1.is_null());
- Some(SslString::new(str_from_asn1))
+ Some(SslString::new(str_from_asn1, len))
}
}
}
@@ -766,6 +789,120 @@ make_validation_error!(X509_V_OK,
X509ApplicationVerification = X509_V_ERR_APPLICATION_VERIFICATION,
);
+/// A collection of OpenSSL `GENERAL_NAME`s.
+pub struct GeneralNames<'a> {
+ stack: *const ffi::stack_st_GENERAL_NAME,
+ m: PhantomData<&'a ()>,
+}
+
+impl<'a> GeneralNames<'a> {
+ /// Returns the number of `GeneralName`s in this structure.
+ pub fn len(&self) -> usize {
+ unsafe {
+ (*self.stack).stack.num as usize
+ }
+ }
+
+ /// Returns the specified `GeneralName`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `idx` is not less than `len()`.
+ pub fn get(&self, idx: usize) -> GeneralName<'a> {
+ unsafe {
+ assert!(idx < self.len());
+
+ GeneralName {
+ name: *(*self.stack).stack.data.offset(idx as isize) as *const ffi::GENERAL_NAME,
+ m: PhantomData,
+ }
+ }
+ }
+
+ /// Returns an iterator over the `GeneralName`s in this structure.
+ pub fn iter(&self) -> GeneralNamesIter {
+ GeneralNamesIter {
+ names: self,
+ idx: 0
+ }
+ }
+}
+
+impl<'a> IntoIterator for &'a GeneralNames<'a> {
+ type Item = GeneralName<'a>;
+ type IntoIter = GeneralNamesIter<'a>;
+
+ fn into_iter(self) -> GeneralNamesIter<'a> {
+ self.iter()
+ }
+}
+
+/// An iterator over OpenSSL `GENERAL_NAME`s.
+pub struct GeneralNamesIter<'a> {
+ names: &'a GeneralNames<'a>,
+ idx: usize,
+}
+
+impl<'a> Iterator for GeneralNamesIter<'a> {
+ type Item = GeneralName<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.idx < self.names.len() {
+ let name = self.names.get(self.idx);
+ self.idx += 1;
+ Some(name)
+ } else {
+ None
+ }
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let size = self.names.len() - self.idx;
+ (size, Some(size))
+ }
+}
+
+impl<'a> ExactSizeIterator for GeneralNamesIter<'a> {}
+
+/// An OpenSSL `GENERAL_NAME`.
+pub struct GeneralName<'a> {
+ name: *const ffi::GENERAL_NAME,
+ m: PhantomData<&'a ()>,
+}
+
+impl<'a> GeneralName<'a> {
+ /// Returns the contents of this `GeneralName` if it is a `dNSName`.
+ pub fn dnsname(&self) -> Option<&str> {
+ unsafe {
+ if (*self.name).type_ != ffi::GEN_DNS {
+ return None;
+ }
+
+ let ptr = ffi::ASN1_STRING_data((*self.name).d as *mut _);
+ let len = ffi::ASN1_STRING_length((*self.name).d as *mut _);
+
+ let slice = slice::from_raw_parts(ptr as *const u8, len as usize);
+ // dNSNames are stated to be ASCII (specifically IA5). Hopefully
+ // OpenSSL checks that when loading a certificate but if not we'll
+ // use this instead of from_utf8_unchecked just in case.
+ str::from_utf8(slice).ok()
+ }
+ }
+
+ /// Returns the contents of this `GeneralName` if it is an `iPAddress`.
+ pub fn ipaddress(&self) -> Option<&[u8]> {
+ unsafe {
+ if (*self.name).type_ != ffi::GEN_IPADD {
+ return None;
+ }
+
+ let ptr = ffi::ASN1_STRING_data((*self.name).d as *mut _);
+ let len = ffi::ASN1_STRING_length((*self.name).d as *mut _);
+
+ Some(slice::from_raw_parts(ptr as *const u8, len as usize))
+ }
+ }
+}
#[test]
fn test_negative_serial() {
diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs
index 69ad37f8..744aba9e 100644
--- a/openssl/src/x509/tests.rs
+++ b/openssl/src/x509/tests.rs
@@ -3,7 +3,7 @@ use std::io;
use std::path::Path;
use std::fs::File;
-use crypto::hash::Type::SHA256;
+use crypto::hash::Type::SHA1;
use crypto::pkey::PKey;
use x509::{X509, X509Generator};
use x509::extension::Extension::{KeyUsage, ExtKeyUsage, SubjectAltName, OtherNid, OtherStr};
@@ -17,7 +17,7 @@ fn get_generator() -> X509Generator {
.set_bitlength(2048)
.set_valid_period(365 * 2)
.add_name("CN".to_string(), "test_me".to_string())
- .set_sign_hash(SHA256)
+ .set_sign_hash(SHA1)
.add_extension(KeyUsage(vec![DigitalSignature, KeyEncipherment]))
.add_extension(ExtKeyUsage(vec![ClientAuth,
ServerAuth,
@@ -83,13 +83,9 @@ fn test_cert_loading() {
.expect("Failed to open `test/cert.pem`");
let cert = X509::from_pem(&mut file).ok().expect("Failed to load PEM");
- let fingerprint = cert.fingerprint(SHA256).unwrap();
+ let fingerprint = cert.fingerprint(SHA1).unwrap();
- // Hash was generated as SHA256 hash of certificate "test/cert.pem"
- // in DER format.
- // Command: openssl x509 -in test/cert.pem -outform DER | openssl dgst -sha256
- // Please update if "test/cert.pem" will ever change
- let hash_str = "db400bb62f1b1f29c3b8f323b8f7d9dea724fdcd67104ef549c772ae3749655b";
+ let hash_str = "E19427DAC79FBE758394945276A6E4F15F0BEBE6";
let hash_vec = hash_str.from_hex().unwrap();
assert_eq!(fingerprint, hash_vec);
@@ -109,7 +105,7 @@ fn test_subject_read_cn() {
None => panic!("Failed to read CN from cert"),
};
- assert_eq!(&cn as &str, "test_cert")
+ assert_eq!(&cn as &str, "foobar.com")
}
#[test]
@@ -157,3 +153,30 @@ fn test_nid_uid_value() {
};
assert_eq!(&cn as &str, "this is the userId");
}
+
+#[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 subject_alt_names = cert.subject_alt_names().unwrap();
+ assert_eq!(3, subject_alt_names.len());
+ assert_eq!(Some("foobar.com"), subject_alt_names.get(0).dnsname());
+ assert_eq!(subject_alt_names.get(1).ipaddress(), Some(&[127, 0, 0, 1][..]));
+ assert_eq!(subject_alt_names.get(2).ipaddress(),
+ Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..]));
+}
+
+#[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 subject_alt_names = cert.subject_alt_names().unwrap();
+ let mut subject_alt_names_iter = subject_alt_names.iter();
+ assert_eq!(subject_alt_names_iter.next().unwrap().dnsname(), Some("foobar.com"));
+ assert_eq!(subject_alt_names_iter.next().unwrap().ipaddress(), Some(&[127, 0, 0, 1][..]));
+ assert_eq!(subject_alt_names_iter.next().unwrap().ipaddress(),
+ Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..]));
+ assert!(subject_alt_names_iter.next().is_none());
+}
diff --git a/openssl/test/alt_name_cert.pem b/openssl/test/alt_name_cert.pem
new file mode 100644
index 00000000..9f75f125
--- /dev/null
+++ b/openssl/test/alt_name_cert.pem
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEOjCCAyKgAwIBAgIJAJz42fzGUJGeMA0GCSqGSIb3DQEBCwUAMH8xCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJOWTERMA8GA1UEBwwITmV3IFlvcmsxFTATBgNVBAoM
+DEV4YW1wbGUsIExMQzEYMBYGA1UEAwwPRXhhbXBsZSBDb21wYW55MR8wHQYJKoZI
+hvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMB4XDTE2MDQzMDA0MDg1NloXDTE3MDQz
+MDA0MDg1NlowfzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMREwDwYDVQQHDAhO
+ZXcgWW9yazEVMBMGA1UECgwMRXhhbXBsZSwgTExDMRgwFgYDVQQDDA9FeGFtcGxl
+IENvbXBhbnkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDggl2TbtO5Ewi/q8kV56xK6HBpwsj9
+wBoqGi6hkKm/8lhLTkuUG6WbEUepi7n9d7tjI9hwYN7MKtppAnS+d+Zh6sKMgLJn
+hONkbQBJkYWwuIxRVXORCdyZDNzXP1rlb6ynmj6mItuPTRVNNMaZP+24fgXtwGk8
+P2nqA1ONbmyaP27txV+Rd8fmQvW3vSmq7iDob661TOtLZRqqVRpnLDGpLXTCptYz
+dLN1nDWKjBUFpPGDxvfcSE3Yf9LaQM2uDHRygSgTFusbwarAGrAk8krsm/Tiaumx
+Ls74MY6OEoLnPbEi5epWLqPmoE1nxrvYLtaWh3TTET3H72yL0+1PZTkpAgMBAAGj
+gbgwgbUwHQYDVR0OBBYEFAIcHhTPUqVdK85u47vo8z0viJGPMB8GA1UdIwQYMBaA
+FAIcHhTPUqVdK85u47vo8z0viJGPMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMC0G
+A1UdEQQmMCSCCmZvb2Jhci5jb22HBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwLAYJ
+YIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMA0GCSqG
+SIb3DQEBCwUAA4IBAQDeYsuJaxbnxR2wDRSbxMpPp2b6fHPxC1vArKTSrQ/X+5s7
+YcQ29jkzD8FbET8iPsCOn/IECBiDKOpckkO6dBWM05ma9HHzWjQOJ7Lo6gEsvk4d
++M/jJz5IaJ7hOxp1hGqwNQ+PJQOZMmlruNcOzPU36qaWJ03+NYOKar5VpIrRxCNc
+uehTArmJqDLQPfgETEhMYfpkqf3s/cGb1uyeCpzgIRPpf4Ki1Oys5cV/BqIn7n5g
+7sUrhXboYL4+eYt5V4rcc4rLI5J5IP/a1Z+Z6UVH+Mbiyl0iD8aRr/bo9WvKih3C
+2LBO0Apl0tkXUOMWp7G0UYHVEndwPjZnVoM42f11
+-----END CERTIFICATE-----
diff --git a/openssl/test/build.sh b/openssl/test/build.sh
index a4c19c05..982cc5cb 100755
--- a/openssl/test/build.sh
+++ b/openssl/test/build.sh
@@ -15,7 +15,7 @@ fi
mkdir /tmp/openssl
cd /tmp/openssl
-curl https://openssl.org/source/openssl-1.0.2g.tar.gz | tar --strip-components=1 -xzf -
+curl https://openssl.org/source/openssl-1.0.2h.tar.gz | tar --strip-components=1 -xzf -
./Configure --prefix=$HOME/openssl shared --cross-compile-prefix=$CROSS $OS_COMPILER
make
make install
diff --git a/openssl/test/cert.pem b/openssl/test/cert.pem
index 0ca22e3d..b8c5ce01 100644
--- a/openssl/test/cert.pem
+++ b/openssl/test/cert.pem
@@ -1,24 +1,21 @@
-----BEGIN CERTIFICATE-----
-MIID9DCCAtygAwIBAgIJALuA8gDKi5EzMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMUCXRlc3RfY2VydDAeFw0xNTA1MTExNzI0
-MThaFw0xNjA1MTAxNzI0MThaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21l
-LVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
-BAMUCXRlc3RfY2VydDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANYg
-8apqh+nHYBL/KS9SLtDmhTR2s+H3mk7avXACahMEt/Eapsft5YOJ8JAkPbkoyd91
-QtxbrDF1M3rtTfWwHrf4jCeZ6kt2CSvYyJCPP/7JbWPdMDsYFBtoBOEVMdjOLr4R
-8tAar2t1Gu63Mu7LWeYvyFKqJpDkP30/k+OED79l7rOQg6eVN+Qjr1wx3VSBhovs
-UShGnSRw5YWiKnvvUnHwq2eXkwXFOdkhRNsTpWgV+EZsOdunczGbEG4qa+iwJjod
-/b8O9R1rIr12mwgT2U3GHNpJU0I+Qu2063nONndhMfES3md6QCIlKMVYDSU/Wli5
-sLxTxykXWv548Ib8Yv8CAwEAAaOBvjCBuzAdBgNVHQ4EFgQUGec/TF+Ok3BoEAqX
-yvEi9+Emy9QwgYsGA1UdIwSBgzCBgIAUGec/TF+Ok3BoEAqXyvEi9+Emy9ShXaRb
-MFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJ
-bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMUCXRlc3RfY2VydIIJALuA
-8gDKi5EzMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBALwDb82BBTfz
-X+7rENv+f76X+DkHjt6QzCr9f1PYpbIle5/XaxCp+Rkz1BGZTmPRPURpAzweznlG
-kY5KydZ1+XdxPHh2zReBS00KxMElmlMKWp7nOnIKohzcQxTFp4VZ7smb9ymXYdYQ
-nRqaLIM1AKsj+0ulqsSbKggIuUZBvfrAdC3J6//3CXSdvQLJGzYPtTLUddQLN9pv
-GXrdNmvK3BjP+kokAjTt+DMMbIIZZitUVjKXqB7WS/Mqss1P2mdagJmOaD2mL3pY
-HDCCpquJ1SKN3g33hOA0pMRxGskyJI8x71mnej0StbUV9GdX0wN5ziSb/ccF2GYi
-Q+MS7OxsTEY=
+MIIDhzCCAm+gAwIBAgIJAKyxk8nkmAtWMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCmZvb2Jhci5jb20wHhcNMTYwNTE2MDUw
+NTAwWhcNMjYwNTE0MDUwNTAwWjBaMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29t
+ZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYD
+VQQDDApmb29iYXIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+qPQljESzF6NQhf4jkYfQeDYbSRf/LUfT5RvebDb8lrkEP/I33r/vMxK6ZcXy5LdK
+SanKImRvIPTVNJFOqOU/v9UIGXJQgKGWktCasZqKNmJP9ULI9eqZzAXNdLkg5Olf
+WiUl9bysDjVTUsIhwNTIV/ou1n+/ytJ4qvpO4TpIZXhZFoGbVKuNYF4dVXzroJGu
+1JLWJ5PZqwWwDI5mpaGTZ9qTDAEMVYOE4Yi5t877lqr1wEls1GXOyAHdRmzeALQ7
+obNudnqhPROIkx5OxdeMAEtSVqr+uuoUXhh65mSRsdMUEzPbzw9RzebdlNyk34Tv
+5k5QFFlcoPbQrTs26CoLNQIDAQABo1AwTjAdBgNVHQ4EFgQUtnMvYaVLoe9ILBWx
+n/PcNC+8rDAwHwYDVR0jBBgwFoAUtnMvYaVLoe9ILBWxn/PcNC+8rDAwDAYDVR0T
+BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAALVDDD2f25h5ytSkoUqQilybeRDg
+bPhTEEC83NWg2snV1yGtwO3zZ+hvX+J/RqOn33ER/RnQCZTB9FGPj566IbLwLSAE
+y83GDsbsFEWCL8yN4Q3dQVub7D3HZ5PBtGpBxC7brvJD7OnR3n75QOFC+OaGKUCo
+16XulVsB3IQsXdzL4GwoUqWGWaUyf5MkzFruBma16QetK5J10R42skeXssjvqupv
+qUQZxzGOzIGuLTBvJrtFxtoTCu+oZV942wGmuyvLwqRfzIODLNcGLS6lGJudXJPT
+Vapaj6maldL3qe1X4bxvtglnpdlrOJ65E3YEC1gcD1KUvfO5vItKrP1FbA==
-----END CERTIFICATE-----
diff --git a/openssl/test/key.pem b/openssl/test/key.pem
index 8ee80a8c..d381795d 100644
--- a/openssl/test/key.pem
+++ b/openssl/test/key.pem
@@ -1,27 +1,28 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEpQIBAAKCAQEA1iDxqmqH6cdgEv8pL1Iu0OaFNHaz4feaTtq9cAJqEwS38Rqm
-x+3lg4nwkCQ9uSjJ33VC3FusMXUzeu1N9bAet/iMJ5nqS3YJK9jIkI8//sltY90w
-OxgUG2gE4RUx2M4uvhHy0Bqva3Ua7rcy7stZ5i/IUqomkOQ/fT+T44QPv2Xus5CD
-p5U35COvXDHdVIGGi+xRKEadJHDlhaIqe+9ScfCrZ5eTBcU52SFE2xOlaBX4Rmw5
-26dzMZsQbipr6LAmOh39vw71HWsivXabCBPZTcYc2klTQj5C7bTrec42d2Ex8RLe
-Z3pAIiUoxVgNJT9aWLmwvFPHKRda/njwhvxi/wIDAQABAoIBAQC918dqx7hoVBOh
-xAfHpJ1NKJPAx90D4no0n0qFHB7fbbeHU5G6f/iUfp+BrB/tIXSZYWU96SjpUHer
-7OjJgrQ5d2sLUTKgZK4M6c4oHFkok30gpOI2AksRYU+yHxBqn6JhcZhNWNtd8h1G
-t7W4cSHrK0H3yFMY8sQ3Tz7W4Cb2ENIN4wBKeQnmk4k9icG5yz0xLI/vPxqujKij
-JWMbk2jNllaUvDgsTAg/MFCzlJH98lzRrX/dhG+q6Rt4S18xac71PMGyUaWduyeC
-BHGhHW34361zGQYyeG6eWroTQhvwMHjNxvzA/RiTgHBXV5oRvd9BPPoUS53HYQJO
-mzNnfZvBAoGBAPF3VVw+RqG/+OyEkuqE20H35/oSB8tPC6QL/WjWUFwIcSfnkX7H
-WmdHBfqCAZlAOqOYgO3iWcgbcOhnggP9J6ssooErt6M7s7hPG/WXwSf0DoCUgtQF
-9OrcGNBvwtBMqm82hb8u4IpXUApMYF48MkjbaKSmHU3HQe0sIDese+wlAoGBAOME
-YJgtl6t3rcaJ2jzHfEsCTCtCBs2vS9ido73xF3QHTafll/sKMp5bA0mgNhSsphhs
-mJCfOl2Cr+0oN90zkumFe4bNIbWQOL/q+HeFwebK5/oWlg/NyvFmMm7LmniFPpBD
-NJHqV/ehn8Xuw4LheSG1Bg/aRH5NtoKDcG8+4ZdTAoGAFkTHHoavxOMLdeSUGATA
-o8jVH/7hsSJNFIf2iuCY8KPmq6Nzi5mfAL9QEdZDh3qg7c12tnmVhhrhws0o9G04
-Z1Tqd7csbGVpIapKDdA9BA5B+CG6HwudlrtNnotwD/3CChehJgyQsLF0tD5u9MHg
-cU+qyuR292FU9yaGohvKIfECgYEAjX7c9fz029rsZSLm85sizV3RO+UbeHgaPhmD
-RZBPnfIvZMalw8LHagwwMGO7UYeKvw5wyTN1nXMnVBoNN8I9f2/DXnHc4N3TgUtj
-MpwcD03I6QfK4G7UX0HjjUs6LIRgSmqZCZmW2rHSc/wtwBXo+ilqbdcNeevWJeLm
-4W/ADCECgYEA7RGbA6qzHtjVS4Nc9rE1tcv4a7p0EvnMXZhIkDZtJqPpkpyOZllm
-cTuMCGisw0SEIcSoH1pWRDschbIlOlsn1Mhnb/IczqfHZjb4hmX7Cnlc8WP6TBqI
-vma9anlEWW7eIOn5jkY3liQpQ7GJPkCjWuRzne26iV/LIsYj48602Z8=
------END RSA PRIVATE KEY-----
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCo9CWMRLMXo1CF
+/iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0
+kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNS
+wiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAM
+jmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iT
+Hk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCt
+OzboKgs1AgMBAAECggEBAKLj6IOJBKXolczpzb8UkyAjAkGBektcseV07gelJ/fk
+3z0LuWPv5p12E/HlXB24vU2x/ikUbbP3eMsawRzDEahQqmNmPEkYAYUAy/Qpi9GN
+DYvn3LqDec4jVgeQKS+p9H2DzUpTogp8zR2//yzbuWBg2+F//xh7vU0S0RQCziPM
+x7RSBgbhxSfChfEJbS2sDnzfh0jRQmoY95iFv7puet1FJtzdZ4fgCd1RqmC2lFM5
+H0eZtN/Cz19lieVs0b996DErdEBqClVZO00eYbRozCDaBzRU3ybB/dMrGJxhkkXm
+wb3kWMtziH9qOYsostuHIFu8eKFLloKxFnq2R4DGxOECgYEA2KUIZISOeGJSBcLJ
+JAUK2gvgXPNo4HHWIwOA9xeN3ZJlsnPlffXQNnm6t1st1V2gfMm9I2n0m/F0y2B/
+n/XGSa8bghfPA9l0c2h58lkL3JQJR/paa8ycTz+YZPrznEyN7Qa0RrJXUvZv9lQL
+Hc3+FHcSHgMqDV2f2bHAEu9YGi0CgYEAx6VEIPNvrHFgjo/jk1RTuk+m0xEWQsZL
+Cs+izQMr2TaeJn8LG+93AvFuYn0J0nT3WuStLPrUg8i4IhSS6lf1tId5ivIZPm4r
+YwMyblBJXhnHbk7Uqodjfw/3s6V2HAu++B7hTdyVr9DFuST9uv4m8bkPV8rfX1jE
+I2rAPVWvgikCgYB+wNAQP547wQrMZBLbCDg5KwmyWJfb+b6X7czexOEz6humNTjo
+YZHYzY/5B1fhpk3ntQD8X1nGg5caBvOk21+QbOtjShrM3cXMYCw5JvBRtitX+Zo9
+yBEMLOE0877ki8XeEDYZxu5gk98d+D4oygUGZEQtWxyXhVepPt5qNa8OYQKBgQDH
+RVgZI6KFlqzv3wMh3PutbS9wYQ+9GrtwUQuIYe/0YSW9+vSVr5E0qNKrD28sV39F
+hBauXLady0yvB6YUrjMbPFW+sCMuQzyfGWPO4+g3OrfqjFiM1ZIkE0YEU9Tt7XNx
+qTDtTI1D7bhNMnTnniI1B6ge0und+3XafAThs5L48QKBgQCTTpfqMt8kU3tcI9sf
+0MK03y7kA76d5uw0pZbWFy7KI4qnzWutCzb+FMPWWsoFtLJLPZy//u/ZCUVFVa4d
+0Y/ASNQIESVPXFLAltlLo4MSmsg1vCBsbviEEaPeEjvMrgki93pYtd/aOSgkYC1T
+mEq154s5rmqh+h+XRIf7Au0SLw==
+-----END PRIVATE KEY-----
diff --git a/openssl/test/rsa.pem b/openssl/test/rsa.pem
new file mode 100644
index 00000000..d8185fed
--- /dev/null
+++ b/openssl/test/rsa.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd/wWJcyQoTbji9k0
+l8W26mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL+yRT+SFd2lZS+pC
+gNMsD1W/YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb/7OMg0LOL+bSf63kpaSHSX
+ndS5z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uD
+Zlxvb3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxXFvUK+DWNmoudF8NAco9/h9iaGNj8
+q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQIDAQABAoIBABKucaRpzQorw35S
+bEUAVx8dYXUdZOlJcHtiWQ+dC6V8ljxAHj/PLyzTveyI5QO/xkObCyjIL303l2cf
+UhPu2MFaJdjVzqACXuOrLot/eSFvxjvqVidTtAZExqFRJ9mylUVAoLvhowVWmC1O
+n95fZCXxTUtxNEG1Xcc7m0rtzJKs45J+N/V9DP1edYH6USyPSWGp6wuA+KgHRnKK
+Vf9GRx80JQY7nVNkL17eHoTWEwga+lwi0FEoW9Y7lDtWXYmKBWhUE+U8PGxlJf8f
+40493HDw1WRQ/aSLoS4QTp3rn7gYgeHEvfJdkkf0UMhlknlo53M09EFPdadQ4TlU
+bjqKc50CgYEA4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH/5IB3jw3bcxGn6QLvnE
+tfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw/Py5PJdTJNPY8cQn7ouZ2KKDcmnPG
+BY5t7yLc1QlQ5xHdwW1VhvKn+nXqhJTBgIPgtldC+KDV5z+y2XDwGUcCgYEAuQPE
+fgmVtjL0Uyyx88GZFF1fOunH3+7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYs
+p1ZSe7zFYHj7C6ul7TjeLQeZD/YwD66t62wDmpe/HlB+TnBA+njbglfIsRLtXlnD
+zQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdcCgYAHAp9XcCSrn8wVkMVkKdb7
+DOX4IKjzdahm+ctDAJN4O/y7OW5FKebvUjdAIt2GuoTZ71iTG+7F0F+lP88jtjP4
+U4qe7VHoewl4MKOfXZKTe+YCS1XbNvfgwJ3Ltyl1OH9hWvu2yza7q+d5PCsDzqtm
+27kxuvULVeya+TEdAB1ijQKBgQCH/3r6YrVH/uCWGy6bzV1nGNOdjKc9tmkfOJmN
+54dxdixdpozCQ6U4OxZrsj3FcOhHBsqAHvX2uuYjagqvo3cOj1TRqNocX40omfCC
+Mx3bD1yPPf/6TI2XECva/ggqEY2mYzmIiA5LVVmc5nrybr+lssFKneeyxN2Wq93S
+0iJMdQKBgCGHewxzoa1r8ZMD0LETNrToK423K377UCYqXfg5XMclbrjPbEC3YI1Z
+NqMtuhdBJqUnBi6tjKMF+34Xf0CUN8ncuXGO2CAYvO8PdyCixHX52ybaDjy1FtCE
+6yUXjoKNXKvUm7MWGsAYH6f4IegOetN5NvmUMFStCSkh7ixZLkN1
+-----END RSA PRIVATE KEY-----
diff --git a/openssl/test/rsa.pem.pub b/openssl/test/rsa.pem.pub
new file mode 100644
index 00000000..093f2ba1
--- /dev/null
+++ b/openssl/test/rsa.pem.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAofgWCuLjybRlzo0tZWJj
+NiuSfb4p4fAkd/wWJcyQoTbji9k0l8W26mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEz
+P1Pt0Bm4d4QlL+yRT+SFd2lZS+pCgNMsD1W/YpRPEwOWvG6b32690r2jZ47soMZo
+9wGzjb/7OMg0LOL+bSf63kpaSHSXndS5z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTB
+EMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxX
+FvUK+DWNmoudF8NAco9/h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXp
+oQIDAQAB
+-----END PUBLIC KEY-----