aboutsummaryrefslogtreecommitdiff
path: root/openssl
diff options
context:
space:
mode:
Diffstat (limited to 'openssl')
-rw-r--r--openssl/Cargo.toml5
-rw-r--r--openssl/src/crypto/mod.rs2
-rw-r--r--openssl/src/crypto/pkcs5.rs109
-rw-r--r--openssl/src/crypto/pkey.rs32
-rw-r--r--openssl/src/crypto/symm.rs43
-rw-r--r--openssl/src/crypto/symm_internal.rs26
-rw-r--r--openssl/src/lib.rs2
-rw-r--r--openssl/src/nid.rs2
-rw-r--r--openssl/src/ssl/mod.rs145
-rw-r--r--openssl/src/ssl/tests.rs15
-rw-r--r--openssl/src/x509/extension.rs212
-rw-r--r--openssl/src/x509/mod.rs242
-rw-r--r--openssl/src/x509/tests.rs46
-rwxr-xr-xopenssl/test/build.sh2
14 files changed, 683 insertions, 200 deletions
diff --git a/openssl/Cargo.toml b/openssl/Cargo.toml
index 8a6b4ddc..b8bc357d 100644
--- a/openssl/Cargo.toml
+++ b/openssl/Cargo.toml
@@ -1,11 +1,11 @@
[package]
name = "openssl"
-version = "0.6.4"
+version = "0.6.5"
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.6.4/openssl"
+documentation = "https://sfackler.github.io/rust-openssl/doc/v0.6.5/openssl"
readme = "../README.md"
keywords = ["crypto", "tls", "ssl", "dtls"]
@@ -16,6 +16,7 @@ dtlsv1 = ["openssl-sys/dtlsv1"]
dtlsv1_2 = ["openssl-sys/dtlsv1_2"]
sslv2 = ["openssl-sys/sslv2"]
aes_xts = ["openssl-sys/aes_xts"]
+aes_ctr = ["openssl-sys/aes_ctr"]
npn = ["openssl-sys/npn"]
alpn = ["openssl-sys/alpn"]
diff --git a/openssl/src/crypto/mod.rs b/openssl/src/crypto/mod.rs
index e695de33..a33c5eb8 100644
--- a/openssl/src/crypto/mod.rs
+++ b/openssl/src/crypto/mod.rs
@@ -22,3 +22,5 @@ pub mod pkey;
pub mod rand;
pub mod symm;
pub mod memcmp;
+
+mod symm_internal; \ No newline at end of file
diff --git a/openssl/src/crypto/pkcs5.rs b/openssl/src/crypto/pkcs5.rs
index b101c3ed..b5f69732 100644
--- a/openssl/src/crypto/pkcs5.rs
+++ b/openssl/src/crypto/pkcs5.rs
@@ -1,6 +1,69 @@
use libc::c_int;
+use std::ptr::null;
+
+use crypto::symm_internal::evpc;
+use crypto::hash;
+use crypto::symm;
use ffi;
+#[derive(Clone, Eq, PartialEq, Hash, Debug)]
+pub struct KeyIvPair
+{
+ pub key: Vec<u8>,
+ pub iv: Vec<u8>
+}
+
+/// Derives a key and an IV from various parameters.
+///
+/// If specified `salt` must be 8 bytes in length.
+///
+/// If the total key and IV length is less than 16 bytes and MD5 is used then
+/// the algorithm is compatible with the key derivation algorithm from PKCS#5
+/// v1.5 or PBKDF1 from PKCS#5 v2.0.
+///
+/// New applications should not use this and instead use `pbkdf2_hmac_sha1` or
+/// another more modern key derivation algorithm.
+pub fn evp_bytes_to_key_pbkdf1_compatible(typ: symm::Type, message_digest_type: hash::Type,
+ data: &[u8], salt: Option<&[u8]>,
+ count: u32) -> KeyIvPair {
+
+ unsafe {
+
+ let salt_ptr = match salt {
+ Some(salt) => {
+ assert_eq!(salt.len(), ffi::PKCS5_SALT_LEN as usize);
+ salt.as_ptr()
+ },
+ None => null()
+ };
+
+ ffi::init();
+
+ let (evp, keylen, _) = evpc(typ);
+
+ let message_digest = message_digest_type.evp_md();
+
+ let mut key = vec![0; keylen as usize];
+ let mut iv = vec![0; keylen as usize];
+
+
+ let ret: c_int = ffi::EVP_BytesToKey(evp,
+ message_digest,
+ salt_ptr,
+ data.as_ptr(),
+ data.len() as c_int,
+ count as c_int,
+ key.as_mut_ptr(),
+ iv.as_mut_ptr());
+ assert!(ret == keylen as c_int);
+
+ KeyIvPair {
+ key: key,
+ iv: iv
+ }
+ }
+}
+
/// Derives a key from a password and salt using the PBKDF2-HMAC-SHA1 algorithm.
pub fn pbkdf2_hmac_sha1(pass: &str, salt: &[u8], iter: usize, keylen: usize) -> Vec<u8> {
unsafe {
@@ -27,6 +90,9 @@ pub fn pbkdf2_hmac_sha1(pass: &str, salt: &[u8], iter: usize, keylen: usize) ->
#[cfg(test)]
mod tests {
+ use crypto::hash;
+ use crypto::symm;
+
// Test vectors from
// http://tools.ietf.org/html/draft-josefsson-pbkdf2-test-vectors-06
#[test]
@@ -116,4 +182,47 @@ mod tests {
)
);
}
+
+ #[test]
+ fn test_evp_bytes_to_key_pbkdf1_compatible() {
+ let salt = [
+ 16_u8, 34_u8, 19_u8, 23_u8, 141_u8, 4_u8, 207_u8, 221_u8
+ ];
+
+ let data = [
+ 143_u8, 210_u8, 75_u8, 63_u8, 214_u8, 179_u8, 155_u8,
+ 241_u8, 242_u8, 31_u8, 154_u8, 56_u8, 198_u8, 145_u8, 192_u8, 64_u8,
+ 2_u8, 245_u8, 167_u8, 220_u8, 55_u8, 119_u8, 233_u8, 136_u8, 139_u8,
+ 27_u8, 71_u8, 242_u8, 119_u8, 175_u8, 65_u8, 207_u8
+ ];
+
+
+
+ let expected_key = vec![
+ 249_u8, 115_u8, 114_u8, 97_u8, 32_u8, 213_u8, 165_u8, 146_u8, 58_u8,
+ 87_u8, 234_u8, 3_u8, 43_u8, 250_u8, 97_u8, 114_u8, 26_u8, 98_u8,
+ 245_u8, 246_u8, 238_u8, 177_u8, 229_u8, 161_u8, 183_u8, 224_u8,
+ 174_u8, 3_u8, 6_u8, 244_u8, 236_u8, 255_u8
+ ];
+ let expected_iv = vec![
+ 4_u8, 223_u8, 153_u8, 219_u8, 28_u8, 142_u8, 234_u8, 68_u8, 227_u8,
+ 69_u8, 98_u8, 107_u8, 208_u8, 14_u8, 236_u8, 60_u8, 0_u8, 0_u8,
+ 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8,
+ 0_u8, 0_u8, 0_u8
+ ];
+
+ assert_eq!(
+ super::evp_bytes_to_key_pbkdf1_compatible(
+ symm::Type::AES_256_CBC,
+ hash::Type::SHA1,
+ &data,
+ Some(&salt),
+ 1
+ ),
+ super::KeyIvPair {
+ key: expected_key,
+ iv: expected_iv
+ }
+ );
+ }
}
diff --git a/openssl/src/crypto/pkey.rs b/openssl/src/crypto/pkey.rs
index 1474e53c..48308381 100644
--- a/openssl/src/crypto/pkey.rs
+++ b/openssl/src/crypto/pkey.rs
@@ -182,6 +182,17 @@ impl PKey {
writer.write_all(&buf).map_err(StreamError)
}
+ /// Stores public key as a PEM
+ pub fn write_pub_pem<W: Write>(&self, writer: &mut W/*, password: Option<String>*/) -> Result<(), SslError> {
+ let mut mem_bio = try!(MemBio::new());
+ unsafe {
+ try_ssl!(ffi::PEM_write_bio_PUBKEY(mem_bio.get_handle(), self.evp))
+ }
+ let mut buf = vec![];
+ try!(mem_bio.read_to_end(&mut buf).map_err(StreamError));
+ writer.write_all(&buf).map_err(StreamError)
+ }
+
/**
* Returns the size of the public key modulus.
*/
@@ -500,4 +511,25 @@ mod tests {
assert!(!k0.public_eq(&p1));
assert!(!p0.public_eq(&k1));
}
+
+ #[test]
+ fn test_pem() {
+ let key_path = Path::new("test/key.pem");
+ let mut file = File::open(&key_path)
+ .ok()
+ .expect("Failed to open `test/key.pem`");
+
+ let key = super::PKey::private_key_from_pem(&mut file).unwrap();
+
+ let mut priv_key = Vec::new();
+ let mut pub_key = Vec::new();
+
+ key.write_pem(&mut priv_key).unwrap();
+ key.write_pub_pem(&mut pub_key).unwrap();
+
+ // As a super-simple verification, just check that the buffers contain
+ // the `PRIVATE KEY` or `PUBLIC KEY` strings.
+ assert!(priv_key.windows(11).any(|s| s == b"PRIVATE KEY"));
+ assert!(pub_key.windows(10).any(|s| s == b"PUBLIC KEY"));
+ }
}
diff --git a/openssl/src/crypto/symm.rs b/openssl/src/crypto/symm.rs
index 82c668ba..226b2cbf 100644
--- a/openssl/src/crypto/symm.rs
+++ b/openssl/src/crypto/symm.rs
@@ -2,6 +2,7 @@ use std::iter::repeat;
use std::convert::AsRef;
use libc::{c_int};
+use crypto::symm_internal::evpc;
use ffi;
#[derive(Copy, Clone)]
@@ -18,7 +19,8 @@ pub enum Type {
/// Requires the `aes_xts` feature
#[cfg(feature = "aes_xts")]
AES_128_XTS,
- // AES_128_CTR,
+ #[cfg(feature = "aes_ctr")]
+ AES_128_CTR,
//AES_128_GCM,
AES_256_ECB,
@@ -26,33 +28,13 @@ pub enum Type {
/// Requires the `aes_xts` feature
#[cfg(feature = "aes_xts")]
AES_256_XTS,
- // AES_256_CTR,
+ #[cfg(feature = "aes_ctr")]
+ AES_256_CTR,
//AES_256_GCM,
RC4_128,
}
-fn evpc(t: Type) -> (*const ffi::EVP_CIPHER, u32, u32) {
- unsafe {
- match t {
- Type::AES_128_ECB => (ffi::EVP_aes_128_ecb(), 16, 16),
- Type::AES_128_CBC => (ffi::EVP_aes_128_cbc(), 16, 16),
- #[cfg(feature = "aes_xts")]
- Type::AES_128_XTS => (ffi::EVP_aes_128_xts(), 32, 16),
- // AES_128_CTR => (EVP_aes_128_ctr(), 16, 0),
- //AES_128_GCM => (EVP_aes_128_gcm(), 16, 16),
-
- Type::AES_256_ECB => (ffi::EVP_aes_256_ecb(), 32, 16),
- Type::AES_256_CBC => (ffi::EVP_aes_256_cbc(), 32, 16),
- #[cfg(feature = "aes_xts")]
- Type::AES_256_XTS => (ffi::EVP_aes_256_xts(), 64, 16),
- // AES_256_CTR => (EVP_aes_256_ctr(), 32, 0),
- //AES_256_GCM => (EVP_aes_256_gcm(), 32, 16),
-
- Type::RC4_128 => (ffi::EVP_rc4(), 16, 0),
- }
- }
-}
/// Represents a symmetric cipher context.
pub struct Crypter {
@@ -288,16 +270,17 @@ mod tests {
cipher_test(super::Type::AES_256_XTS, pt, ct, key, iv);
}
- /*#[test]
+ #[test]
+ #[cfg(feature = "aes_ctr")]
fn test_aes128_ctr() {
- let pt = ~"6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710";
- let ct = ~"874D6191B620E3261BEF6864990DB6CE9806F66B7970FDFF8617187BB9FFFDFF5AE4DF3EDBD5D35E5B4F09020DB03EAB1E031DDA2FBE03D1792170A0F3009CEE";
- let key = ~"2B7E151628AED2A6ABF7158809CF4F3C";
- let iv = ~"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF";
+ let pt = "6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710";
+ let ct = "874D6191B620E3261BEF6864990DB6CE9806F66B7970FDFF8617187BB9FFFDFF5AE4DF3EDBD5D35E5B4F09020DB03EAB1E031DDA2FBE03D1792170A0F3009CEE";
+ let key = "2B7E151628AED2A6ABF7158809CF4F3C";
+ let iv = "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF";
- cipher_test(super::AES_128_CTR, pt, ct, key, iv);
- }*/
+ cipher_test(super::Type::AES_128_CTR, pt, ct, key, iv);
+ }
/*#[test]
fn test_aes128_gcm() {
diff --git a/openssl/src/crypto/symm_internal.rs b/openssl/src/crypto/symm_internal.rs
new file mode 100644
index 00000000..c42efb79
--- /dev/null
+++ b/openssl/src/crypto/symm_internal.rs
@@ -0,0 +1,26 @@
+use crypto::symm;
+use ffi;
+
+pub fn evpc(t: symm::Type) -> (*const ffi::EVP_CIPHER, u32, u32) {
+ unsafe {
+ match t {
+ symm::Type::AES_128_ECB => (ffi::EVP_aes_128_ecb(), 16, 16),
+ symm::Type::AES_128_CBC => (ffi::EVP_aes_128_cbc(), 16, 16),
+ #[cfg(feature = "aes_xts")]
+ symm::Type::AES_128_XTS => (ffi::EVP_aes_128_xts(), 32, 16),
+ #[cfg(feature = "aes_ctr")]
+ symm::Type::AES_128_CTR => (ffi::EVP_aes_128_ctr(), 16, 0),
+ //AES_128_GCM => (EVP_aes_128_gcm(), 16, 16),
+
+ symm::Type::AES_256_ECB => (ffi::EVP_aes_256_ecb(), 32, 16),
+ symm::Type::AES_256_CBC => (ffi::EVP_aes_256_cbc(), 32, 16),
+ #[cfg(feature = "aes_xts")]
+ symm::Type::AES_256_XTS => (ffi::EVP_aes_256_xts(), 64, 16),
+ #[cfg(feature = "aes_ctr")]
+ symm::Type::AES_256_CTR => (ffi::EVP_aes_256_ctr(), 32, 0),
+ //AES_256_GCM => (EVP_aes_256_gcm(), 32, 16),
+
+ symm::Type::RC4_128 => (ffi::EVP_rc4(), 16, 0),
+ }
+ }
+} \ No newline at end of file
diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs
index 62d18dce..17e625b9 100644
--- a/openssl/src/lib.rs
+++ b/openssl/src/lib.rs
@@ -1,4 +1,4 @@
-#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.6.4")]
+#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.6.5")]
#[macro_use]
extern crate bitflags;
diff --git a/openssl/src/nid.rs b/openssl/src/nid.rs
index c5b9e277..81cc4975 100644
--- a/openssl/src/nid.rs
+++ b/openssl/src/nid.rs
@@ -1,5 +1,5 @@
#[allow(non_camel_case_types)]
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Hash, PartialEq, Eq)]
#[repr(usize)]
pub enum Nid {
Undefined,
diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs
index 88ba9af4..35180d3a 100644
--- a/openssl/src/ssl/mod.rs
+++ b/openssl/src/ssl/mod.rs
@@ -6,6 +6,7 @@ use std::fmt;
use std::io;
use std::io::prelude::*;
use std::mem;
+use std::str;
use std::net;
use std::path::Path;
use std::ptr;
@@ -30,7 +31,9 @@ mod tests;
static mut VERIFY_IDX: c_int = -1;
-fn init() {
+/// Manually initialize SSL.
+/// It is optional to call this function and safe to do so more than once.
+pub fn init() {
static mut INIT: Once = ONCE_INIT;
unsafe {
@@ -46,35 +49,52 @@ fn init() {
}
bitflags! {
- flags SslContextOptions: c_long {
- const SSL_OP_LEGACY_SERVER_CONNECT = 0x00000004,
- const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = 0x00000008,
- const SSL_OP_TLSEXT_PADDING = 0x00000010,
- const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = 0x00000020,
- const SSL_OP_SAFARI_ECDHE_ECDSA_BUG = 0x00000040,
- const SSL_OP_SSLEAY_080_CLIENT_DH_BUG = 0x00000080,
- const SSL_OP_TLS_D5_BUG = 0x00000100,
- const SSL_OP_TLS_BLOCK_PADDING_BUG = 0x00000200,
- const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = 0x00000800,
- const SSL_OP_ALL = 0x80000BFF,
- const SSL_OP_NO_QUERY_MTU = 0x00001000,
- const SSL_OP_COOKIE_EXCHANGE = 0x00002000,
- const SSL_OP_NO_TICKET = 0x00004000,
- const SSL_OP_CISCO_ANYCONNECT = 0x00008000,
- const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000,
- const SSL_OP_NO_COMPRESSION = 0x00020000,
- const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 0x00040000,
- const SSL_OP_SINGLE_ECDH_USE = 0x00080000,
- const SSL_OP_SINGLE_DH_USE = 0x00100000,
- const SSL_OP_CIPHER_SERVER_PREFERENCE = 0x00400000,
- const SSL_OP_TLS_ROLLBACK_BUG = 0x00800000,
- const SSL_OP_NO_SSLV2 = 0x00000000,
- const SSL_OP_NO_SSLV3 = 0x02000000,
- const SSL_OP_NO_TLSV1 = 0x04000000,
- const SSL_OP_NO_TLSV1_2 = 0x08000000,
- const SSL_OP_NO_TLSV1_1 = 0x10000000,
- const SSL_OP_NO_DTLSV1 = 0x04000000,
- const SSL_OP_NO_DTLSV1_2 = 0x08000000
+ flags SslContextOptions: u64 {
+ const SSL_OP_MICROSOFT_SESS_ID_BUG = ffi::SSL_OP_MICROSOFT_SESS_ID_BUG,
+ const SSL_OP_NETSCAPE_CHALLENGE_BUG = ffi::SSL_OP_NETSCAPE_CHALLENGE_BUG,
+ const SSL_OP_LEGACY_SERVER_CONNECT = ffi::SSL_OP_LEGACY_SERVER_CONNECT,
+ const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = ffi::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG,
+ const SSL_OP_TLSEXT_PADDING = ffi::SSL_OP_TLSEXT_PADDING,
+ const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = ffi::SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER,
+ const SSL_OP_SAFARI_ECDHE_ECDSA_BUG = ffi::SSL_OP_SAFARI_ECDHE_ECDSA_BUG,
+ const SSL_OP_SSLEAY_080_CLIENT_DH_BUG = ffi::SSL_OP_SSLEAY_080_CLIENT_DH_BUG,
+ const SSL_OP_TLS_D5_BUG = ffi::SSL_OP_TLS_D5_BUG,
+ const SSL_OP_TLS_BLOCK_PADDING_BUG = ffi::SSL_OP_TLS_BLOCK_PADDING_BUG,
+ const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = ffi::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS,
+ const SSL_OP_NO_QUERY_MTU = ffi::SSL_OP_NO_QUERY_MTU,
+ const SSL_OP_COOKIE_EXCHANGE = ffi::SSL_OP_COOKIE_EXCHANGE,
+ const SSL_OP_NO_TICKET = ffi::SSL_OP_NO_TICKET,
+ const SSL_OP_CISCO_ANYCONNECT = ffi::SSL_OP_CISCO_ANYCONNECT,
+ const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = ffi::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION,
+ const SSL_OP_NO_COMPRESSION = ffi::SSL_OP_NO_COMPRESSION,
+ const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = ffi::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION,
+ const SSL_OP_SINGLE_ECDH_USE = ffi::SSL_OP_SINGLE_ECDH_USE,
+ const SSL_OP_SINGLE_DH_USE = ffi::SSL_OP_SINGLE_DH_USE,
+ const SSL_OP_CIPHER_SERVER_PREFERENCE = ffi::SSL_OP_CIPHER_SERVER_PREFERENCE,
+ const SSL_OP_TLS_ROLLBACK_BUG = ffi::SSL_OP_TLS_ROLLBACK_BUG,
+ const SSL_OP_NO_SSLV2 = ffi::SSL_OP_NO_SSLv2,
+ const SSL_OP_NO_SSLV3 = ffi::SSL_OP_NO_SSLv3,
+ const SSL_OP_NO_DTLSV1 = ffi::SSL_OP_NO_DTLSv1,
+ const SSL_OP_NO_TLSV1 = ffi::SSL_OP_NO_TLSv1,
+ const SSL_OP_NO_DTLSV1_2 = ffi::SSL_OP_NO_DTLSv1_2,
+ const SSL_OP_NO_TLSV1_2 = ffi::SSL_OP_NO_TLSv1_2,
+ const SSL_OP_NO_TLSV1_1 = ffi::SSL_OP_NO_TLSv1_1,
+ const SSL_OP_NETSCAPE_CA_DN_BUG = ffi::SSL_OP_NETSCAPE_CA_DN_BUG,
+ const SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = ffi::SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG,
+ const SSL_OP_CRYPTOPRO_TLSEXT_BUG = ffi::SSL_OP_CRYPTOPRO_TLSEXT_BUG,
+ const SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG = ffi::SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG,
+ const SSL_OP_MSIE_SSLV2_RSA_PADDING = ffi::SSL_OP_MSIE_SSLV2_RSA_PADDING,
+ const SSL_OP_PKCS1_CHECK_1 = ffi::SSL_OP_PKCS1_CHECK_1,
+ const SSL_OP_PKCS1_CHECK_2 = ffi::SSL_OP_PKCS1_CHECK_2,
+ const SSL_OP_EPHEMERAL_RSA = ffi::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
+ |SSL_OP_SAFARI_ECDHE_ECDSA_BUG.bits|SSL_OP_SSLEAY_080_CLIENT_DH_BUG.bits
+ |SSL_OP_TLS_D5_BUG.bits|SSL_OP_TLS_BLOCK_PADDING_BUG.bits
+ |SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS.bits|SSL_OP_CRYPTOPRO_TLSEXT_BUG.bits,
+ const SSL_OP_NO_SSL_MASK = SSL_OP_NO_SSLV2.bits|SSL_OP_NO_SSLV3.bits|SSL_OP_NO_TLSV1.bits
+ |SSL_OP_NO_TLSV1_1.bits|SSL_OP_NO_TLSV1_2.bits,
}
}
@@ -124,6 +144,25 @@ impl SslMethod {
}
}
+ unsafe fn from_raw(method: *const ffi::SSL_METHOD) -> Option<SslMethod> {
+ match method {
+ #[cfg(feature = "sslv2")]
+ x if x == ffi::SSLv2_method() => Some(SslMethod::Sslv2),
+ x if x == ffi::SSLv3_method() => Some(SslMethod::Sslv3),
+ x if x == ffi::TLSv1_method() => Some(SslMethod::Tlsv1),
+ x if x == ffi::SSLv23_method() => Some(SslMethod::Sslv23),
+ #[cfg(feature = "tlsv1_1")]
+ x if x == ffi::TLSv1_1_method() => Some(SslMethod::Tlsv1_1),
+ #[cfg(feature = "tlsv1_2")]
+ x if x == ffi::TLSv1_2_method() => Some(SslMethod::Tlsv1_2),
+ #[cfg(feature = "dtlsv1")]
+ x if x == ffi::DTLSv1_method() => Some(SslMethod::Dtlsv1),
+ #[cfg(feature = "dtlsv1_2")]
+ x if x == ffi::DTLSv1_2_method() => Some(SslMethod::Dtlsv1_2),
+ _ => None,
+ }
+ }
+
#[cfg(feature = "dtlsv1")]
pub fn is_dtlsv1(&self) -> bool {
*self == SslMethod::Dtlsv1
@@ -652,6 +691,24 @@ impl Ssl {
Ok(ssl)
}
+ pub fn get_state_string(&self) -> &'static str {
+ let state = unsafe {
+ let ptr = ffi::SSL_state_string(self.ssl);
+ CStr::from_ptr(ptr)
+ };
+
+ str::from_utf8(state.to_bytes()).unwrap()
+ }
+
+ pub fn get_state_string_long(&self) -> &'static str {
+ let state = unsafe {
+ let ptr = ffi::SSL_state_string_long(self.ssl);
+ CStr::from_ptr(ptr)
+ };
+
+ str::from_utf8(state.to_bytes()).unwrap()
+ }
+
fn get_rbio<'a>(&'a self) -> MemBioRef<'a> {
unsafe { self.wrap_bio(ffi::SSL_get_rbio(self.ssl)) }
}
@@ -770,6 +827,13 @@ impl Ssl {
ffi::SSL_pending(self.ssl) as usize
}
}
+
+ pub fn get_ssl_method(&self) -> Option<SslMethod> {
+ unsafe {
+ let method = ffi::SSL_get_ssl_method(self.ssl);
+ SslMethod::from_raw(method)
+ }
+ }
}
macro_rules! make_LibSslError {
@@ -871,8 +935,16 @@ impl<S: Read+Write> IndirectStream<S> {
LibSslError::ErrorWantRead => {
try_ssl_stream!(self.flush());
let len = try_ssl_stream!(self.stream.read(&mut self.buf[..]));
+
+
if len == 0 {
- self.ssl.get_rbio().set_eof(true);
+ let method = self.ssl.get_ssl_method();
+
+ if method.map(|m| m.is_dtls()).unwrap_or(false) {
+ return Ok(0);
+ } else {
+ self.ssl.get_rbio().set_eof(true);
+ }
} else {
try_ssl_stream!(self.ssl.get_rbio().write_all(&self.buf[..len]));
}
@@ -993,6 +1065,9 @@ impl<S> DirectStream<S> {
err
}
}
+ LibSslError::ErrorWantWrite | LibSslError::ErrorWantRead => {
+ SslError::StreamError(io::Error::last_os_error())
+ }
err => panic!("unexpected error {:?} with ret {}", err, ret),
}
}
@@ -1260,6 +1335,14 @@ impl<S: Read+Write> SslStream<S> {
pub fn pending(&self) -> usize {
self.kind.ssl().pending()
}
+
+ pub fn get_state_string(&self) -> &'static str {
+ self.kind.ssl().get_state_string()
+ }
+
+ pub fn get_state_string_long(&self) -> &'static str {
+ self.kind.ssl().get_state_string_long()
+ }
}
impl<S: Read+Write> Read for SslStream<S> {
diff --git a/openssl/src/ssl/tests.rs b/openssl/src/ssl/tests.rs
index 8401836d..9198a642 100644
--- a/openssl/src/ssl/tests.rs
+++ b/openssl/src/ssl/tests.rs
@@ -51,7 +51,7 @@ macro_rules! run_test(
use std::net::TcpStream;
use ssl;
use ssl::SslMethod;
- use ssl::{SslContext, SslStream, VerifyCallback};
+ use ssl::{SslContext, Ssl, SslStream, VerifyCallback};
use ssl::SSL_VERIFY_PEER;
use crypto::hash::Type::SHA256;
use x509::X509StoreContext;
@@ -86,6 +86,11 @@ run_test!(new_sslstream, |method, stream| {
SslStream::connect_generic(&SslContext::new(method).unwrap(), stream).unwrap();
});
+run_test!(get_ssl_method, |method, _| {
+ let ssl = Ssl::new(&SslContext::new(method).unwrap()).unwrap();
+ assert_eq!(ssl.get_ssl_method(), Some(method));
+});
+
run_test!(verify_untrusted, |method, stream| {
let mut ctx = SslContext::new(method).unwrap();
ctx.set_verify(SSL_VERIFY_PEER, None);
@@ -390,6 +395,14 @@ fn test_pending() {
assert_eq!(pending, len);
}
+#[test]
+fn test_state() {
+ let tcp = TcpStream::connect("127.0.0.1:15418").unwrap();
+ let stream = SslStream::connect_generic(&SslContext::new(Sslv23).unwrap(), tcp).unwrap();
+ assert_eq!(stream.get_state_string(), "SSLOK ");
+ assert_eq!(stream.get_state_string_long(), "SSL negotiation finished successfully");
+}
+
/// Tests that connecting with the client using NPN, but the server not does not
/// break the existing connection behavior.
#[test]
diff --git a/openssl/src/x509/extension.rs b/openssl/src/x509/extension.rs
new file mode 100644
index 00000000..3faa0996
--- /dev/null
+++ b/openssl/src/x509/extension.rs
@@ -0,0 +1,212 @@
+use std::fmt;
+use nid::Nid;
+
+/// Type-only version of the `Extension` enum.
+///
+/// See the `Extension` documentation for more information on the different
+/// variants.
+#[derive(Clone,Hash,PartialEq,Eq)]
+pub enum ExtensionType {
+ KeyUsage,
+ ExtKeyUsage,
+ SubjectAltName,
+ IssuerAltName,
+ OtherNid(Nid),
+ OtherStr(String),
+}
+
+/// A X.509 v3 certificate extension.
+///
+/// Only one extension of each type is allow in a certificate.
+/// See RFC 3280 for more information about extensions.
+#[derive(Clone)]
+pub enum Extension {
+ /// The purposes of the key contained in the certificate
+ KeyUsage(Vec<KeyUsageOption>),
+ /// The extended purposes of the key contained in the certificate
+ ExtKeyUsage(Vec<ExtKeyUsageOption>),
+ /// Subject Alternative Names
+ SubjectAltName(Vec<(AltNameOption,String)>),
+ /// Issuer Alternative Names
+ IssuerAltName(Vec<(AltNameOption,String)>),
+ /// Arbitrary extensions by NID. See `man x509v3_config` for value syntax.
+ ///
+ /// You must not use this to add extensions which this enum can express directly.
+ ///
+ /// ```
+ /// use openssl::x509::extension::Extension::*;
+ /// use openssl::nid::Nid;
+ ///
+ /// # let generator = openssl::x509::X509Generator::new();
+ /// generator.add_extension(OtherNid(Nid::BasicConstraints,"critical,CA:TRUE".to_owned()));
+ /// ```
+ OtherNid(Nid,String),
+ /// Arbitrary extensions by OID string. See `man ASN1_generate_nconf` for value syntax.
+ ///
+ /// You must not use this to add extensions which this enum can express directly.
+ ///
+ /// ```
+ /// use openssl::x509::extension::Extension::*;
+ ///
+ /// # let generator = openssl::x509::X509Generator::new();
+ /// generator.add_extension(OtherStr("2.999.2".to_owned(),"ASN1:UTF8:example value".to_owned()));
+ /// ```
+ OtherStr(String,String),
+}
+
+impl Extension {
+ pub fn get_type(&self) -> ExtensionType {
+ match self {
+ &Extension::KeyUsage(_) => ExtensionType::KeyUsage,
+ &Extension::ExtKeyUsage(_) => ExtensionType::ExtKeyUsage,
+ &Extension::SubjectAltName(_) => ExtensionType::SubjectAltName,
+ &Extension::IssuerAltName(_) => ExtensionType::IssuerAltName,
+ &Extension::OtherNid(nid,_) => ExtensionType::OtherNid(nid),
+ &Extension::OtherStr(ref s,_) => ExtensionType::OtherStr(s.clone()),
+ }
+ }
+}
+
+impl ExtensionType {
+ pub fn get_nid(&self) -> Option<Nid> {
+ match self {
+ &ExtensionType::KeyUsage => Some(Nid::KeyUsage),
+ &ExtensionType::ExtKeyUsage => Some(Nid::ExtendedKeyUsage),
+ &ExtensionType::SubjectAltName => Some(Nid::SubjectAltName),
+ &ExtensionType::IssuerAltName => Some(Nid::IssuerAltName),
+ &ExtensionType::OtherNid(nid) => Some(nid),
+ &ExtensionType::OtherStr(_) => None,
+ }
+ }
+
+ pub fn get_name<'a>(&'a self) -> Option<&'a str> {
+ match self {
+ &ExtensionType::OtherStr(ref s) => Some(s),
+ _ => None,
+ }
+ }
+}
+
+// FIXME: This would be nicer as a method on Iterator<Item=ToString>. This can
+// eventually be replaced by the successor to std::slice::SliceConcatExt.connect
+fn join<I: Iterator<Item=T>,T: ToString>(iter: I, sep: &str) -> String {
+ iter.enumerate().fold(String::new(), |mut acc, (idx, v)| {
+ if idx > 0 { acc.push_str(sep) };
+ acc.push_str(&v.to_string());
+ acc
+ })
+}
+
+impl ToString for Extension {
+ fn to_string(&self) -> String {
+ match self {
+ &Extension::KeyUsage(ref purposes) => join(purposes.iter(),","),
+ &Extension::ExtKeyUsage(ref purposes) => join(purposes.iter(),","),
+ &Extension::SubjectAltName(ref names) => join(names.iter().map(|&(ref opt,ref val)|opt.to_string()+":"+&val),","),
+ &Extension::IssuerAltName(ref names) => join(names.iter().map(|&(ref opt,ref val)|opt.to_string()+":"+&val),","),
+ &Extension::OtherNid(_,ref value) => value.clone(),
+ &Extension::OtherStr(_,ref value) => value.clone(),
+ }
+ }
+}
+
+#[derive(Clone,Copy)]
+pub enum KeyUsageOption {
+ DigitalSignature,
+ NonRepudiation,
+ KeyEncipherment,
+ DataEncipherment,
+ KeyAgreement,
+ KeyCertSign,
+ CRLSign,
+ EncipherOnly,
+ DecipherOnly,
+}
+
+impl fmt::Display for KeyUsageOption {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ f.pad(match self {
+ &KeyUsageOption::DigitalSignature => "digitalSignature",
+ &KeyUsageOption::NonRepudiation => "nonRepudiation",
+ &KeyUsageOption::KeyEncipherment => "keyEncipherment",
+ &KeyUsageOption::DataEncipherment => "dataEncipherment",
+ &KeyUsageOption::KeyAgreement => "keyAgreement",
+ &KeyUsageOption::KeyCertSign => "keyCertSign",
+ &KeyUsageOption::CRLSign => "cRLSign",
+ &KeyUsageOption::EncipherOnly => "encipherOnly",
+ &KeyUsageOption::DecipherOnly => "decipherOnly",
+ })
+ }
+}
+
+#[derive(Clone)]
+pub enum ExtKeyUsageOption {
+ ServerAuth,
+ ClientAuth,
+ CodeSigning,
+ EmailProtection,
+ TimeStamping,
+ MsCodeInd,
+ MsCodeCom,
+ MsCtlSign,
+ MsSgc,
+ MsEfs,
+ NsSgc,
+ /// An arbitrary key usage by OID.
+ Other(String),
+}
+
+impl fmt::Display for ExtKeyUsageOption {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ f.pad(match self {
+ &ExtKeyUsageOption::ServerAuth => "serverAuth",
+ &ExtKeyUsageOption::ClientAuth => "clientAuth",
+ &ExtKeyUsageOption::CodeSigning => "codeSigning",
+ &ExtKeyUsageOption::EmailProtection => "emailProtection",
+ &ExtKeyUsageOption::TimeStamping => "timeStamping",
+ &ExtKeyUsageOption::MsCodeInd => "msCodeInd",
+ &ExtKeyUsageOption::MsCodeCom => "msCodeCom",
+ &ExtKeyUsageOption::MsCtlSign => "msCTLSign",
+ &ExtKeyUsageOption::MsSgc => "msSGC",
+ &ExtKeyUsageOption::MsEfs => "msEFS",
+ &ExtKeyUsageOption::NsSgc =>"nsSGC",
+ &ExtKeyUsageOption::Other(ref s) => &s[..],
+ })
+ }
+}
+
+#[derive(Clone, Copy)]
+pub enum AltNameOption {
+ /// The value is specified as OID;content. See `man ASN1_generate_nconf` for more information on the content syntax.
+ ///
+ /// ```
+ /// use openssl::x509::extension::Extension::*;
+ /// use openssl::x509::extension::AltNameOption::Other as OtherName;
+ ///
+ /// # let generator = openssl::x509::X509Generator::new();
+ /// generator.add_extension(SubjectAltName(vec![(OtherName,"2.999.3;ASN1:UTF8:some other name".to_owned())]));
+ /// ```
+ Other,
+ Email,
+ DNS,
+ //X400, // Not supported by OpenSSL
+ Directory,
+ //EDIParty, // Not supported by OpenSSL
+ URI,
+ IPAddress,
+ RegisteredID,
+}
+
+impl fmt::Display for AltNameOption {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ f.pad(match self {
+ &AltNameOption::Other => "otherName",
+ &AltNameOption::Email => "email",
+ &AltNameOption::DNS => "DNS",
+ &AltNameOption::Directory => "dirName",
+ &AltNameOption::URI => "URI",
+ &AltNameOption::IPAddress => "IP",
+ &AltNameOption::RegisteredID => "RID",
+ })
+ }
+}
diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs
index 5446f125..91daa66a 100644
--- a/openssl/src/x509/mod.rs
+++ b/openssl/src/x509/mod.rs
@@ -9,6 +9,7 @@ use std::ptr;
use std::ops::Deref;
use std::fmt;
use std::str;
+use std::collections::HashMap;
use asn1::{Asn1Time};
use bio::{MemBio};
@@ -20,6 +21,9 @@ use ffi;
use ssl::error::{SslError, StreamError};
use nid;
+pub mod extension;
+
+use self::extension::{ExtensionType,Extension};
#[cfg(test)]
mod tests;
@@ -98,92 +102,9 @@ impl X509StoreContext {
}
}
-#[doc(hidden)]
-trait AsStr<'a> {
- fn as_str(&self) -> &'a str;
-}
-
-#[derive(Clone, Copy)]
-pub enum KeyUsage {
- DigitalSignature,
- NonRepudiation,
- KeyEncipherment,
- DataEncipherment,
- KeyAgreement,
- KeyCertSign,
- CRLSign,
- EncipherOnly,
- DecipherOnly
-}
-
-impl AsStr<'static> for KeyUsage {
- fn as_str(&self) -> &'static str {
- match self {
- &KeyUsage::DigitalSignature => "digitalSignature",
- &KeyUsage::NonRepudiation => "nonRepudiation",
- &KeyUsage::KeyEncipherment => "keyEncipherment",
- &KeyUsage::DataEncipherment => "dataEncipherment",
- &KeyUsage::KeyAgreement => "keyAgreement",
- &KeyUsage::KeyCertSign => "keyCertSign",
- &KeyUsage::CRLSign => "cRLSign",
- &KeyUsage::EncipherOnly => "encipherOnly",
- &KeyUsage::DecipherOnly => "decipherOnly"
- }
- }
-}
-
-
-#[derive(Clone, Copy)]
-pub enum ExtKeyUsage {
- ServerAuth,
- ClientAuth,
- CodeSigning,
- EmailProtection,
- TimeStamping,
- MsCodeInd,
- MsCodeCom,
- MsCtlSign,
- MsSgc,
- MsEfs,
- NsSgc
-}
-
-impl AsStr<'static> for ExtKeyUsage {
- fn as_str(&self) -> &'static str {
- match self {
- &ExtKeyUsage::ServerAuth => "serverAuth",
- &ExtKeyUsage::ClientAuth => "clientAuth",
- &ExtKeyUsage::CodeSigning => "codeSigning",
- &ExtKeyUsage::EmailProtection => "emailProtection",
- &ExtKeyUsage::TimeStamping => "timeStamping",
- &ExtKeyUsage::MsCodeInd => "msCodeInd",
- &ExtKeyUsage::MsCodeCom => "msCodeCom",
- &ExtKeyUsage::MsCtlSign => "msCTLSign",
- &ExtKeyUsage::MsSgc => "msSGC",
- &ExtKeyUsage::MsEfs => "msEFS",
- &ExtKeyUsage::NsSgc =>"nsSGC"
- }
- }
-}
-
-
-// FIXME: a dirty hack as there is no way to
-// implement ToString for Vec as both are defined
-// in another crate
-#[doc(hidden)]
-trait ToStr {
- fn to_str(&self) -> String;
-}
-
-impl<'a, T: AsStr<'a>> ToStr for Vec<T> {
- fn to_str(&self) -> String {
- self.iter().enumerate().fold(String::new(), |mut acc, (idx, v)| {
- if idx > 0 { acc.push(',') };
- acc.push_str(v.as_str());
- acc
- })
- }
-}
+// Backwards-compatibility
+pub use self::extension::KeyUsageOption as KeyUsage;
+pub use self::extension::ExtKeyUsageOption as ExtKeyUsage;
#[allow(non_snake_case)]
/// Generator of private key/certificate pairs
@@ -224,9 +145,9 @@ impl<'a, T: AsStr<'a>> ToStr for Vec<T> {
pub struct X509Generator {
bits: u32,
days: u32,
- CN: String,
- key_usage: Vec<KeyUsage>,
- ext_key_usage: Vec<ExtKeyUsage>,
+ names: Vec<(String,String)>,
+ // RFC 3280 ยง4.2: A certificate MUST NOT include more than one instance of a particular extension.
+ extensions: HashMap<ExtensionType,Extension>,
hash_type: HashType,
}
@@ -244,9 +165,8 @@ impl X509Generator {
X509Generator {
bits: 1024,
days: 365,
- CN: "rust-openssl".to_string(),
- key_usage: Vec::new(),
- ext_key_usage: Vec::new(),
+ names: vec![],
+ extensions: HashMap::new(),
hash_type: HashType::SHA1
}
}
@@ -264,21 +184,88 @@ impl X509Generator {
}
#[allow(non_snake_case)]
- /// Sets Common Name of certificate
+ /// (deprecated) Sets Common Name of certificate
+ ///
+ /// This function is deprecated, use `X509Generator.add_name` instead.
+ /// Don't use this function AND the `add_name` method
pub fn set_CN(mut self, CN: &str) -> X509Generator {
- self.CN = CN.to_string();
+ match self.names.get_mut(0) {
+ Some(&mut(_,ref mut val)) => *val=CN.to_string(),
+ _ => {} /* would move push here, but borrow checker won't let me */
+ }
+ if self.names.len()==0 {
+ self.names.push(("CN".to_string(),CN.to_string()));
+ }
+ self
+ }
+
+ /// Add attribute to the name of the certificate
+ ///
+ /// ```
+ /// # let generator = openssl::x509::X509Generator::new();
+ /// generator.add_name("CN".to_string(),"example.com".to_string());
+ /// ```
+ pub fn add_name(mut self, attr_type: String, attr_value: String) -> X509Generator {
+ self.names.push((attr_type,attr_value));
self
}
- /// Sets what for certificate could be used
- pub fn set_usage(mut self, purposes: &[KeyUsage]) -> X509Generator {
- self.key_usage = purposes.to_vec();
+ /// Add multiple attributes to the name of the certificate
+ ///
+ /// ```
+ /// # let generator = openssl::x509::X509Generator::new();
+ /// generator.add_names(vec![("CN".to_string(),"example.com".to_string())]);
+ /// ```
+ pub fn add_names<I>(mut self, attrs: I) -> X509Generator
+ where I: IntoIterator<Item=(String,String)> {
+ self.names.extend(attrs);
+ self
+ }
+
+ /// (deprecated) Sets what for certificate could be used
+ ///
+ /// This function is deprecated, use `X509Generator.add_extension` instead.
+ pub fn set_usage(self, purposes: &[KeyUsage]) -> X509Generator {
+ self.add_extension(Extension::KeyUsage(purposes.to_owned()))
+ }
+
+ /// (deprecated) Sets allowed extended usage of certificate
+ ///
+ /// This function is deprecated, use `X509Generator.add_extension` instead.
+ pub fn set_ext_usage(self, purposes: &[ExtKeyUsage]) -> X509Generator {
+ self.add_extension(Extension::ExtKeyUsage(purposes.to_owned()))
+ }
+
+ /// Add an extension to a certificate
+ ///
+ /// If the extension already exists, it will be replaced.
+ ///
+ /// ```
+ /// use openssl::x509::extension::Extension::*;
+ /// use openssl::x509::extension::KeyUsageOption::*;
+ ///
+ /// # let generator = openssl::x509::X509Generator::new();
+ /// generator.add_extension(KeyUsage(vec![DigitalSignature, KeyEncipherment]));
+ /// ```
+ pub fn add_extension(mut self, ext: extension::Extension) -> X509Generator {
+ self.extensions.insert(ext.get_type(),ext);
self
}
- /// Sets allowed extended usage of certificate
- pub fn set_ext_usage(mut self, purposes: &[ExtKeyUsage]) -> X509Generator {
- self.ext_key_usage = purposes.to_vec();
+ /// Add multiple extensions to a certificate
+ ///
+ /// If any of the extensions already exist, they will be replaced.
+ ///
+ /// ```
+ /// use openssl::x509::extension::Extension::*;
+ /// use openssl::x509::extension::KeyUsageOption::*;
+ ///
+ /// # let generator = openssl::x509::X509Generator::new();
+ /// generator.add_extensions(vec![KeyUsage(vec![DigitalSignature, KeyEncipherment])]);
+ /// ```
+ pub fn add_extensions<I>(mut self, exts: I) -> X509Generator
+ where I: IntoIterator<Item=extension::Extension> {
+ self.extensions.extend(exts.into_iter().map(|ext|(ext.get_type(),ext)));
self
}
@@ -287,17 +274,25 @@ impl X509Generator {
self
}
- fn add_extension(x509: *mut ffi::X509, extension: c_int, value: &str) -> Result<(), SslError> {
+ fn add_extension_internal(x509: *mut ffi::X509, exttype: &extension::ExtensionType, value: &str) -> Result<(), SslError> {
unsafe {
let mut ctx: ffi::X509V3_CTX = mem::zeroed();
ffi::X509V3_set_ctx(&mut ctx, x509, x509,
ptr::null_mut(), ptr::null_mut(), 0);
let value = CString::new(value.as_bytes()).unwrap();
- let ext = ffi::X509V3_EXT_conf_nid(ptr::null_mut(),
+ let ext=match exttype.get_nid() {
+ Some(nid) => ffi::X509V3_EXT_conf_nid(ptr::null_mut(),
mem::transmute(&ctx),
- extension,
- value.as_ptr() as *mut c_char);
-
+ nid as c_int,
+ value.as_ptr() as *mut c_char),
+ None => {
+ let name=CString::new(exttype.get_name().unwrap().as_bytes()).unwrap();
+ ffi::X509V3_EXT_conf(ptr::null_mut(),
+ mem::transmute(&ctx),
+ name.as_ptr() as *mut c_char,
+ value.as_ptr() as *mut c_char)
+ }
+ };
let mut success = false;
if ext != ptr::null_mut() {
success = ffi::X509_add_ext(x509, ext, -1) != 0;
@@ -307,7 +302,7 @@ impl X509Generator {
}
}
- fn add_name(name: *mut ffi::X509_NAME, key: &str, value: &str) -> Result<(), SslError> {
+ fn add_name_internal(name: *mut ffi::X509_NAME, key: &str, value: &str) -> Result<(), SslError> {
let value_len = value.len() as c_int;
lift_ssl!(unsafe {
let key = CString::new(key.as_bytes()).unwrap();
@@ -373,17 +368,19 @@ impl X509Generator {
let name = ffi::X509_get_subject_name(x509.handle);
try_ssl_null!(name);
- try!(X509Generator::add_name(name, "CN", &self.CN));
- ffi::X509_set_issuer_name(x509.handle, name);
+ let default=[("CN","rust-openssl")];
+ let default_iter=&mut default.iter().map(|&(k,v)|(k,v));
+ let arg_iter=&mut self.names.iter().map(|&(ref k,ref v)|(&k[..],&v[..]));
+ let iter: &mut Iterator<Item=(&str,&str)> =
+ if self.names.len()==0 { default_iter } else { arg_iter };
- if self.key_usage.len() > 0 {
- try!(X509Generator::add_extension(x509.handle, ffi::NID_key_usage,
- &self.key_usage.to_str()));
+ for (key,val) in iter {
+ try!(X509Generator::add_name_internal(name, &key, &val));
}
+ ffi::X509_set_issuer_name(x509.handle, name);
- if self.ext_key_usage.len() > 0 {
- try!(X509Generator::add_extension(x509.handle, ffi::NID_ext_key_usage,
- &self.ext_key_usage.to_str()));
+ for (exttype,ext) in self.extensions.iter() {
+ try!(X509Generator::add_extension_internal(x509.handle, exttype, &ext.to_string()));
}
let hash_fn = self.hash_type.evp_md();
@@ -399,11 +396,20 @@ impl X509Generator {
Err(x) => return Err(x)
};
- let hash_fn = self.hash_type.evp_md();
- let req = unsafe { ffi::X509_to_X509_REQ(cert.handle, p_key.get_handle(), hash_fn) };
- try_ssl_null!(req);
+ unsafe {
+ let req = ffi::X509_to_X509_REQ(cert.handle, ptr::null_mut(), ptr::null());
+ try_ssl_null!(req);
+
+ let exts = ffi::X509_get_extensions(cert.handle);
+ if exts != ptr::null_mut() {
+ try_ssl!(ffi::X509_REQ_add_extensions(req,exts));
+ }
- Ok(X509Req::new(req))
+ let hash_fn = self.hash_type.evp_md();
+ try_ssl!(ffi::X509_REQ_sign(req, p_key.get_handle(), hash_fn));
+
+ Ok(X509Req::new(req))
+ }
}
}
diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs
index 4e1c4f15..692539ba 100644
--- a/openssl/src/x509/tests.rs
+++ b/openssl/src/x509/tests.rs
@@ -4,28 +4,32 @@ use std::path::Path;
use std::fs::File;
use crypto::hash::Type::{SHA256};
+use crypto::pkey::PKey;
use x509::{X509, X509Generator};
-use x509::KeyUsage::{DigitalSignature, KeyEncipherment};
-use x509::ExtKeyUsage::{ClientAuth, ServerAuth};
+use x509::extension::Extension::{KeyUsage,ExtKeyUsage,SubjectAltName,OtherNid,OtherStr};
+use x509::extension::AltNameOption as SAN;
+use x509::extension::KeyUsageOption::{DigitalSignature, KeyEncipherment};
+use x509::extension::ExtKeyUsageOption::{self, ClientAuth, ServerAuth};
use nid::Nid;
-#[test]
-fn test_cert_gen() {
- let gen = X509Generator::new()
+fn get_generator() -> X509Generator {
+ X509Generator::new()
.set_bitlength(2048)
.set_valid_period(365*2)
- .set_CN("test_me")
+ .add_name("CN".to_string(),"test_me".to_string())
.set_sign_hash(SHA256)
- .set_usage(&[DigitalSignature, KeyEncipherment])
- .set_ext_usage(&[ClientAuth, ServerAuth]);
-
- let res = gen.generate();
- assert!(res.is_ok());
-
- let (cert, pkey) = res.unwrap();
+ .add_extension(KeyUsage(vec![DigitalSignature, KeyEncipherment]))
+ .add_extension(ExtKeyUsage(vec![ClientAuth, ServerAuth, ExtKeyUsageOption::Other("2.999.1".to_owned())]))
+ .add_extension(SubjectAltName(vec![(SAN::DNS,"example.com".to_owned())]))
+ .add_extension(OtherNid(Nid::BasicConstraints,"critical,CA:TRUE".to_owned()))
+ .add_extension(OtherStr("2.999.2".to_owned(),"ASN1:UTF8:example value".to_owned()))
+}
- assert!(cert.write_pem(&mut io::sink()).is_ok());
- assert!(pkey.write_pem(&mut io::sink()).is_ok());
+#[test]
+fn test_cert_gen() {
+ let (cert, pkey) = get_generator().generate().unwrap();
+ cert.write_pem(&mut io::sink()).unwrap();
+ pkey.write_pem(&mut io::sink()).unwrap();
// FIXME: check data in result to be correct, needs implementation
// of X509 getters
@@ -34,6 +38,18 @@ fn test_cert_gen() {
}
#[test]
+fn test_req_gen() {
+ let mut pkey = PKey::new();
+ pkey.gen(512);
+
+ let req = get_generator().request(&pkey).unwrap();
+ req.write_pem(&mut io::sink()).unwrap();
+
+ // FIXME: check data in result to be correct, needs implementation
+ // of X509_REQ getters
+}
+
+#[test]
fn test_cert_loading() {
let cert_path = Path::new("test/cert.pem");
let mut file = File::open(&cert_path)
diff --git a/openssl/test/build.sh b/openssl/test/build.sh
index 27def60a..db517ab3 100755
--- a/openssl/test/build.sh
+++ b/openssl/test/build.sh
@@ -4,7 +4,7 @@ set -e
mkdir /tmp/openssl
cd /tmp/openssl
sudo apt-get install gcc make
-curl https://openssl.org/source/openssl-1.0.2-latest.tar.gz | tar --strip-components=1 -xzf -
+curl https://openssl.org/source/openssl-1.0.2d.tar.gz | tar --strip-components=1 -xzf -
./config --prefix=/usr/ shared
make
sudo make install