diff options
Diffstat (limited to 'openssl')
| -rw-r--r-- | openssl/Cargo.toml | 5 | ||||
| -rw-r--r-- | openssl/src/crypto/mod.rs | 2 | ||||
| -rw-r--r-- | openssl/src/crypto/pkcs5.rs | 109 | ||||
| -rw-r--r-- | openssl/src/crypto/pkey.rs | 32 | ||||
| -rw-r--r-- | openssl/src/crypto/symm.rs | 43 | ||||
| -rw-r--r-- | openssl/src/crypto/symm_internal.rs | 26 | ||||
| -rw-r--r-- | openssl/src/lib.rs | 2 | ||||
| -rw-r--r-- | openssl/src/nid.rs | 2 | ||||
| -rw-r--r-- | openssl/src/ssl/mod.rs | 145 | ||||
| -rw-r--r-- | openssl/src/ssl/tests.rs | 15 | ||||
| -rw-r--r-- | openssl/src/x509/extension.rs | 212 | ||||
| -rw-r--r-- | openssl/src/x509/mod.rs | 242 | ||||
| -rw-r--r-- | openssl/src/x509/tests.rs | 46 | ||||
| -rwxr-xr-x | openssl/test/build.sh | 2 |
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 |