aboutsummaryrefslogtreecommitdiff
path: root/openssl
diff options
context:
space:
mode:
Diffstat (limited to 'openssl')
-rw-r--r--openssl/Cargo.toml9
-rw-r--r--openssl/src/bn/mod.rs12
-rw-r--r--openssl/src/crypto/mod.rs1
-rw-r--r--openssl/src/crypto/pkey.rs33
-rw-r--r--openssl/src/crypto/rsa.rs93
-rw-r--r--openssl/src/lib.rs2
-rw-r--r--openssl/src/nid.rs3
-rw-r--r--openssl/src/ssl/tests/mod.rs5
-rw-r--r--openssl/src/x509/mod.rs83
-rw-r--r--openssl/src/x509/tests.rs41
-rwxr-xr-xopenssl/test/build.sh2
-rw-r--r--openssl/test/nid_uid_test_cert.pem24
12 files changed, 270 insertions, 38 deletions
diff --git a/openssl/Cargo.toml b/openssl/Cargo.toml
index 55ae0d76..cd85d062 100644
--- a/openssl/Cargo.toml
+++ b/openssl/Cargo.toml
@@ -1,14 +1,15 @@
[package]
name = "openssl"
-version = "0.7.5"
+version = "0.7.6"
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.5/openssl"
+documentation = "https://sfackler.github.io/rust-openssl/doc/v0.7.6/openssl"
readme = "../README.md"
keywords = ["crypto", "tls", "ssl", "dtls"]
build = "build.rs"
+exclude = ["test/*"]
[features]
tlsv1_2 = ["openssl-sys/tlsv1_2"]
@@ -31,8 +32,8 @@ nightly = []
bitflags = ">= 0.2, < 0.4"
lazy_static = "0.1"
libc = "0.2"
-openssl-sys = { version = "0.7.5", path = "../openssl-sys" }
-openssl-sys-extras = { version = "0.7.5", path = "../openssl-sys-extras" }
+openssl-sys = { version = "0.7.6", path = "../openssl-sys" }
+openssl-sys-extras = { version = "0.7.6", path = "../openssl-sys-extras" }
[build-dependencies]
gcc = "0.3"
diff --git a/openssl/src/bn/mod.rs b/openssl/src/bn/mod.rs
index 51a49241..00a0a0ca 100644
--- a/openssl/src/bn/mod.rs
+++ b/openssl/src/bn/mod.rs
@@ -102,6 +102,18 @@ impl BigNum {
})
}
+ pub unsafe fn new_from_ffi(orig: *mut ffi::BIGNUM) -> Result<BigNum, SslError> {
+ if orig.is_null() {
+ panic!("Null Pointer was supplied to BigNum::new_from_ffi");
+ }
+ let r = ffi::BN_dup(orig);
+ if r.is_null() {
+ Err(SslError::get())
+ } else {
+ Ok(BigNum(r))
+ }
+ }
+
pub fn new_from_slice(n: &[u8]) -> Result<BigNum, SslError> {
BigNum::new().and_then(|v| unsafe {
try_ssl_null!(ffi::BN_bin2bn(n.as_ptr(), n.len() as c_int, v.raw()));
diff --git a/openssl/src/crypto/mod.rs b/openssl/src/crypto/mod.rs
index 0868ee95..bb77453f 100644
--- a/openssl/src/crypto/mod.rs
+++ b/openssl/src/crypto/mod.rs
@@ -21,5 +21,6 @@ pub mod pkey;
pub mod rand;
pub mod symm;
pub mod memcmp;
+pub mod rsa;
mod symm_internal;
diff --git a/openssl/src/crypto/pkey.rs b/openssl/src/crypto/pkey.rs
index 934a93ed..e556730d 100644
--- a/openssl/src/crypto/pkey.rs
+++ b/openssl/src/crypto/pkey.rs
@@ -9,6 +9,7 @@ use crypto::hash;
use crypto::hash::Type as HashType;
use ffi;
use ssl::error::{SslError, StreamError};
+use crypto::rsa::RSA;
#[derive(Copy, Clone)]
pub enum Parts {
@@ -100,7 +101,7 @@ impl PKey {
None,
ptr::null_mut()));
Ok(PKey {
- evp: evp,
+ evp: evp as *mut ffi::EVP_PKEY,
parts: Parts::Both,
})
}
@@ -119,7 +120,7 @@ impl PKey {
None,
ptr::null_mut()));
Ok(PKey {
- evp: evp,
+ evp: evp as *mut ffi::EVP_PKEY,
parts: Parts::Public,
})
}
@@ -129,18 +130,10 @@ impl PKey {
pub fn private_rsa_key_from_pem<R>(reader: &mut R) -> Result<PKey, SslError>
where R: Read
{
- let mut mem_bio = try!(MemBio::new());
- try!(io::copy(reader, &mut mem_bio).map_err(StreamError));
-
+ let rsa = try!(RSA::private_key_from_pem(reader));
unsafe {
- let rsa = try_ssl_null!(ffi::PEM_read_bio_RSAPrivateKey(mem_bio.get_handle(),
- ptr::null_mut(),
- None,
- ptr::null_mut()));
- let evp = ffi::EVP_PKEY_new();
- if ffi::EVP_PKEY_set1_RSA(evp, rsa) == 0 {
- return Err(SslError::get());
- }
+ let evp = try_ssl_null!(ffi::EVP_PKEY_new());
+ try_ssl!(ffi::EVP_PKEY_set1_RSA(evp, rsa.as_ptr()));
Ok(PKey {
evp: evp,
@@ -153,18 +146,10 @@ impl PKey {
pub fn public_rsa_key_from_pem<R>(reader: &mut R) -> Result<PKey, SslError>
where R: Read
{
- let mut mem_bio = try!(MemBio::new());
- try!(io::copy(reader, &mut mem_bio).map_err(StreamError));
-
+ let rsa = try!(RSA::public_key_from_pem(reader));
unsafe {
- let rsa = try_ssl_null!(ffi::PEM_read_bio_RSA_PUBKEY(mem_bio.get_handle(),
- ptr::null_mut(),
- None,
- ptr::null_mut()));
- let evp = ffi::EVP_PKEY_new();
- if ffi::EVP_PKEY_set1_RSA(evp, rsa) == 0 {
- return Err(SslError::get());
- }
+ let evp = try_ssl_null!(ffi::EVP_PKEY_new());
+ try_ssl!(ffi::EVP_PKEY_set1_RSA(evp, rsa.as_ptr()));
Ok(PKey {
evp: evp,
diff --git a/openssl/src/crypto/rsa.rs b/openssl/src/crypto/rsa.rs
new file mode 100644
index 00000000..ee0d9ec4
--- /dev/null
+++ b/openssl/src/crypto/rsa.rs
@@ -0,0 +1,93 @@
+use ffi;
+use std::fmt;
+use ssl::error::{SslError, StreamError};
+use std::ptr;
+use std::io::{self, Read};
+
+use bn::BigNum;
+use bio::MemBio;
+
+pub struct RSA(*mut ffi::RSA);
+
+impl Drop for RSA {
+ fn drop(&mut self) {
+ unsafe {
+ ffi::RSA_free(self.0);
+ }
+ }
+}
+
+impl RSA {
+ /// Reads an RSA private key from PEM formatted data.
+ pub fn private_key_from_pem<R>(reader: &mut R) -> Result<RSA, SslError>
+ where R: Read
+ {
+ let mut mem_bio = try!(MemBio::new());
+ try!(io::copy(reader, &mut mem_bio).map_err(StreamError));
+
+ unsafe {
+ let rsa = try_ssl_null!(ffi::PEM_read_bio_RSAPrivateKey(mem_bio.get_handle(),
+ ptr::null_mut(),
+ None,
+ ptr::null_mut()));
+ Ok(RSA(rsa))
+ }
+ }
+
+ /// Reads an RSA public key from PEM formatted data.
+ pub fn public_key_from_pem<R>(reader: &mut R) -> Result<RSA, SslError>
+ where R: Read
+ {
+ let mut mem_bio = try!(MemBio::new());
+ try!(io::copy(reader, &mut mem_bio).map_err(StreamError));
+
+ unsafe {
+ let rsa = try_ssl_null!(ffi::PEM_read_bio_RSA_PUBKEY(mem_bio.get_handle(),
+ ptr::null_mut(),
+ None,
+ ptr::null_mut()));
+ Ok(RSA(rsa))
+ }
+ }
+
+ pub fn as_ptr(&self) -> *mut ffi::RSA {
+ self.0
+ }
+
+ // The following getters are unsafe, since BigNum::new_from_ffi fails upon null pointers
+ pub fn n(&self) -> Result<BigNum, SslError> {
+ unsafe {
+ BigNum::new_from_ffi((*self.0).n)
+ }
+ }
+
+ pub fn d(&self) -> Result<BigNum, SslError> {
+ unsafe {
+ BigNum::new_from_ffi((*self.0).d)
+ }
+ }
+
+ pub fn e(&self) -> Result<BigNum, SslError> {
+ unsafe {
+ BigNum::new_from_ffi((*self.0).e)
+ }
+ }
+
+ pub fn p(&self) -> Result<BigNum, SslError> {
+ unsafe {
+ BigNum::new_from_ffi((*self.0).p)
+ }
+ }
+
+ pub fn q(&self) -> Result<BigNum, SslError> {
+ unsafe {
+ BigNum::new_from_ffi((*self.0).q)
+ }
+ }
+}
+
+impl fmt::Debug for RSA {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "RSA")
+ }
+}
diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs
index ac68fecb..f1b6d13a 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.7.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))]
#[macro_use]
diff --git a/openssl/src/nid.rs b/openssl/src/nid.rs
index e04b004a..bfcae15a 100644
--- a/openssl/src/nid.rs
+++ b/openssl/src/nid.rs
@@ -104,6 +104,7 @@ pub enum Nid {
G,
S,
I,
+ /// uniqueIdentifier
UID,
CrlDistributionPoints,
RSA_NP_MD5,
@@ -170,4 +171,6 @@ pub enum Nid {
ID_QT_UNOTICE,
RC2_64_CBC,
SMIMECaps,
+ /// Shown as UID in cert subject
+ UserId = 458
}
diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs
index f5a42536..be35d7ef 100644
--- a/openssl/src/ssl/tests/mod.rs
+++ b/openssl/src/ssl/tests/mod.rs
@@ -9,6 +9,7 @@ use std::net::{TcpStream, TcpListener, SocketAddr};
use std::path::Path;
use std::process::{Command, Child, Stdio, ChildStdin};
use std::thread;
+use std::time::Duration;
use net2::TcpStreamExt;
@@ -79,7 +80,7 @@ impl Server {
match TcpStream::connect(&addr) {
Ok(s) => return (server, s),
Err(ref e) if e.kind() == io::ErrorKind::ConnectionRefused => {
- thread::sleep_ms(100);
+ thread::sleep(Duration::from_millis(100));
}
Err(e) => panic!("wut: {}", e),
}
@@ -117,7 +118,7 @@ impl Server {
// Need to wait for the UDP socket to get bound in our child process,
// but don't currently have a great way to do that so just wait for a
// bit.
- thread::sleep_ms(100);
+ thread::sleep(Duration::from_millis(100));
let socket = UdpSocket::bind(next_addr()).unwrap();
socket.connect(&addr).unwrap();
(s, UdpConnected(socket))
diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs
index f31de89b..a69f61d5 100644
--- a/openssl/src/x509/mod.rs
+++ b/openssl/src/x509/mod.rs
@@ -146,8 +146,7 @@ pub struct X509Generator {
bits: u32,
days: u32,
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>,
+ extensions: Extensions,
hash_type: HashType,
}
@@ -166,7 +165,7 @@ impl X509Generator {
bits: 1024,
days: 365,
names: vec![],
- extensions: HashMap::new(),
+ extensions: Extensions::new(),
hash_type: HashType::SHA1,
}
}
@@ -219,7 +218,7 @@ impl X509Generator {
/// 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.extensions.add(ext);
self
}
@@ -237,7 +236,10 @@ impl X509Generator {
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)));
+ for ext in exts {
+ self.extensions.add(ext);
+ }
+
self
}
@@ -372,7 +374,7 @@ impl X509Generator {
ffi::X509_set_issuer_name(x509.handle, name);
for (exttype, ext) in self.extensions.iter() {
- try!(X509Generator::add_extension_internal(x509.handle, exttype, &ext.to_string()));
+ try!(X509Generator::add_extension_internal(x509.handle, &exttype, &ext.to_string()));
}
let hash_fn = self.hash_type.evp_md();
@@ -618,6 +620,75 @@ impl Drop for X509Req {
}
}
+/// A collection of X.509 extensions.
+///
+/// Upholds the invariant that a certificate MUST NOT include more than one
+/// instance of a particular extension, according to RFC 3280 §4.2. Also
+/// ensures that extensions are added to the certificate during signing
+/// in the order they were inserted, which is required for certain
+/// extensions like SubjectKeyIdentifier and AuthorityKeyIdentifier.
+struct Extensions {
+ /// The extensions contained in the collection.
+ extensions: Vec<Extension>,
+ /// A map of used to keep track of added extensions and their indexes in `self.extensions`.
+ indexes: HashMap<ExtensionType, usize>,
+}
+
+impl Extensions {
+ /// Creates a new `Extensions`.
+ pub fn new() -> Extensions {
+ Extensions {
+ extensions: vec![],
+ indexes: HashMap::new(),
+ }
+ }
+
+ /// Adds a new `Extension`, replacing any existing one of the same
+ /// `ExtensionType`.
+ pub fn add(&mut self, ext: Extension) {
+ let ext_type = ext.get_type();
+
+ if let Some(index) = self.indexes.get(&ext_type) {
+ self.extensions[*index] = ext;
+ return;
+ }
+
+ self.extensions.push(ext);
+ self.indexes.insert(ext_type, self.extensions.len() - 1);
+ }
+
+ /// Returns an `ExtensionsIter` for the collection.
+ pub fn iter(&self) -> ExtensionsIter {
+ ExtensionsIter {
+ current: 0,
+ extensions: &self.extensions,
+ }
+ }
+}
+
+/// An iterator that iterates over `(ExtensionType, Extension)` for each
+/// extension in the collection.
+struct ExtensionsIter<'a> {
+ current: usize,
+ extensions: &'a Vec<Extension>
+}
+
+impl<'a> Iterator for ExtensionsIter<'a> {
+ type Item = (ExtensionType, &'a Extension);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.current < self.extensions.len() {
+ let ext = &self.extensions[self.current];
+
+ self.current += 1;
+
+ Some((ext.get_type(), ext))
+ } else {
+ None
+ }
+ }
+}
+
macro_rules! make_validation_error(
($ok_val:ident, $($name:ident = $val:ident,)+) => (
#[derive(Copy, Clone)]
diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs
index 43ad0dec..69ad37f8 100644
--- a/openssl/src/x509/tests.rs
+++ b/openssl/src/x509/tests.rs
@@ -39,6 +39,30 @@ fn test_cert_gen() {
assert_eq!(pkey.save_pub(), cert.public_key().save_pub());
}
+/// SubjectKeyIdentifier must be added before AuthorityKeyIdentifier or OpenSSL
+/// is "unable to get issuer keyid." This test ensures the order of insertion
+/// for extensions is preserved when the cert is signed.
+#[test]
+fn test_cert_gen_extension_ordering() {
+ get_generator()
+ .add_extension(OtherNid(Nid::SubjectKeyIdentifier, "hash".to_owned()))
+ .add_extension(OtherNid(Nid::AuthorityKeyIdentifier, "keyid:always".to_owned()))
+ .generate()
+ .expect("Failed to generate cert with order-dependent extensions");
+}
+
+/// Proves that a passing result from `test_cert_gen_extension_ordering` is
+/// deterministic by reversing the order of extensions and asserting failure.
+#[test]
+fn test_cert_gen_extension_bad_ordering() {
+ let result = get_generator()
+ .add_extension(OtherNid(Nid::AuthorityKeyIdentifier, "keyid:always".to_owned()))
+ .add_extension(OtherNid(Nid::SubjectKeyIdentifier, "hash".to_owned()))
+ .generate();
+
+ assert!(result.is_err());
+}
+
#[test]
fn test_req_gen() {
let mut pkey = PKey::new();
@@ -116,3 +140,20 @@ fn test_nid_values() {
};
assert_eq!(&friendly as &str, "Example");
}
+
+#[test]
+fn test_nid_uid_value() {
+ let cert_path = Path::new("test/nid_uid_test_cert.pem");
+ let mut file = File::open(&cert_path)
+ .ok()
+ .expect("Failed to open `test/nid_uid_test_cert.pem`");
+
+ let cert = X509::from_pem(&mut file).ok().expect("Failed to load PEM");
+ let subject = cert.subject_name();
+
+ let cn = match subject.text_by_nid(Nid::UserId) {
+ Some(x) => x,
+ None => panic!("Failed to read UID from cert"),
+ };
+ assert_eq!(&cn as &str, "this is the userId");
+}
diff --git a/openssl/test/build.sh b/openssl/test/build.sh
index f52ded60..794738d5 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.2e.tar.gz | tar --strip-components=1 -xzf -
+curl https://openssl.org/source/openssl-1.0.2f.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/nid_uid_test_cert.pem b/openssl/test/nid_uid_test_cert.pem
new file mode 100644
index 00000000..de6722ab
--- /dev/null
+++ b/openssl/test/nid_uid_test_cert.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEGTCCAwGgAwIBAgIJAItKTzcGfL1lMA0GCSqGSIb3DQEBCwUAMIGiMSIwIAYK
+CZImiZPyLGQBAQwSdGhpcyBpcyB0aGUgdXNlcklkMQswCQYDVQQGEwJVUzETMBEG
+A1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU3Vubnl2YWxlMRUwEwYDVQQKDAxS
+dXN0IE9wZW5TU0wxDDAKBgNVBAsMA09TUzEhMB8GA1UEAwwYcnVzdC1vcGVuc3Ns
+LmV4YW1wbGUuY29tMB4XDTE2MDIwMjE3MjIwMVoXDTE2MDMwMzE3MjIwMVowgaIx
+IjAgBgoJkiaJk/IsZAEBDBJ0aGlzIGlzIHRoZSB1c2VySWQxCzAJBgNVBAYTAlVT
+MRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTdW5ueXZhbGUxFTATBgNV
+BAoMDFJ1c3QgT3BlblNTTDEMMAoGA1UECwwDT1NTMSEwHwYDVQQDDBhydXN0LW9w
+ZW5zc2wuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDa3Gc+IE5DOhTv1m5DZW8qKiyNLd7v4DaAYLXSsDuLs+9wJ+Bs+wlBfrg+PT0t
+EJlPaLL9IfD5eR3WpFu62TUexYhnJh+3vhCGsFHOXcTjtM+wy/dzZtOVh2wTzvqE
+/FHBGw1eG3Ww+RkSFbwYmtm8JhIN8ffYxGn2O0yQpxypf5hNPYrC81zX+52X2w1h
+jDYLpYt55w+e6q+iRRFk0tKiWHEqqh/r6UQQRpj2EeS+xTloZlO6h0nl2NPkVF3r
+CXBoT8Ittxr7sqcYqf8TAA0I4qZRYXKYehFmv/VkSt85CcURJ/zXeoJ1TpxSvQie
+2R9cRDkYROrIOAFbB/0mmHLBAgMBAAGjUDBOMB0GA1UdDgQWBBRKfPqtgrbdbTmH
+XR6RC/p8t/65GjAfBgNVHSMEGDAWgBRKfPqtgrbdbTmHXR6RC/p8t/65GjAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCKfeGRduGsIwKNiGcDUNkNrc7Z
+f8SWAmb/R6xiDfgjbhrtfBDowIZ5natEkTgf6kQPMJKyjg2NEM2uJWBc55rLOHIv
+es1wQOlYjfEUmFD3lTIt2TM/IUgXn2j+zV1CRkJthQLVFChXsidd0Bqq2fBjd3ad
+Yjzrxf3uOTBAs27koh2INNHfcUZCRsx8hP739zz2kw/r5NB/9iyENEyJKQvxo0jb
+oN0JK2joGZrWetDukQrqf032TsdkboW5JresYybbAD3326Ljp+hlT/3WINc+3nZJ
+Dn+pPMdpuZ5BUZ+u+XyNEPum3k3P3K19AF+zWYGooX0J1cmuCBrrqce20Lwy
+-----END CERTIFICATE-----