From 524e8e3c5a59dd8ca6290c2fe7e338d8a7587a25 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Thu, 5 Jan 2017 16:15:25 +0100 Subject: [PATCH 1/6] libressl: mark unavailable flags as such These flags are not available in libressl (at least for version 2.4.4 which is the last stable version) Signed-off-by: Marc-Antoine Perennou --- openssl-sys/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 22af7c96..23fa032b 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -1206,15 +1206,15 @@ pub const X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE: c_int = 45; pub const X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: c_int = 53; pub const X509_V_OK: c_int = 0; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT: c_uint = 0x1; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const X509_CHECK_FLAG_NO_WILDCARDS: c_uint = 0x2; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS: c_uint = 0x4; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS: c_uint = 0x8; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS: c_uint = 0x10; pub const GEN_OTHERNAME: c_int = 0; From 0978f870956ff06dee2e7dcbb6a30c6cbfce6e1e Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Thu, 5 Jan 2017 16:15:53 +0100 Subject: [PATCH 2/6] libressl: make set_ecdh_auto available Signed-off-by: Marc-Antoine Perennou --- openssl/src/ssl/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 3eead8f2..722225e7 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -774,12 +774,12 @@ impl SslContextBuilder { /// curve. /// /// Requires the `v102` feature and OpenSSL 1.0.2. - #[cfg(all(feature = "v102", ossl102))] + #[cfg(all(feature = "v102", any(ossl102, libressl)))] pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> { self._set_ecdh_auto(onoff) } - #[cfg(ossl102)] + #[cfg(any(ossl102,libressl))] fn _set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_ecdh_auto(self.as_ptr(), onoff as c_int)).map(|_| ()) } } From d353b366811023049eb77189234abc8196f9372f Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 20 Jan 2017 22:34:30 +0000 Subject: [PATCH 3/6] Support AES IGE This is a special snowflake used only by Telegram apparently. Closes #523 --- openssl-sys/src/lib.rs | 17 ++++++ openssl/src/aes.rs | 115 +++++++++++++++++++++++++++++++++++++++++ openssl/src/lib.rs | 1 + systest/build.rs | 1 + 4 files changed, 134 insertions(+) create mode 100644 openssl/src/aes.rs diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 0cbd0da7..865061ee 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -76,6 +76,13 @@ pub enum point_conversion_form_t { POINT_CONVERSION_HYBRID = 6, } +#[repr(C)] +pub struct AES_KEY { + // There is some business with AES_LONG which is there to ensure the values here are 32 bits + rd_key: [u32; 4 * (AES_MAXNR as usize + 1)], + rounds: c_int, +} + #[repr(C)] pub struct GENERAL_NAME { pub type_: c_int, @@ -114,6 +121,12 @@ pub type PasswordCallback = unsafe extern fn(buf: *mut c_char, size: c_int, rwflag: c_int, user_data: *mut c_void) -> c_int; +pub const AES_ENCRYPT: c_int = 1; +pub const AES_DECRYPT: c_int = 0; + +pub const AES_MAXNR: c_int = 14; +pub const AES_BLOCK_SIZE: c_int = 16; + pub const BIO_TYPE_NONE: c_int = 0; pub const BIO_CTRL_EOF: c_int = 2; @@ -1368,6 +1381,10 @@ pub fn ERR_GET_REASON(l: c_ulong) -> c_int { } extern { + pub fn AES_set_encrypt_key(userKey: *const c_uchar, bits: c_int, key: *mut AES_KEY) -> c_int; + pub fn AES_set_decrypt_key(userKey: *const c_uchar, bits: c_int, key: *mut AES_KEY) -> c_int; + pub fn AES_ige_encrypt(in_: *const c_uchar, out: *mut c_uchar, length: size_t, key: *const AES_KEY, ivec: *mut c_uchar, enc: c_int); + pub fn ASN1_INTEGER_set(dest: *mut ASN1_INTEGER, value: c_long) -> c_int; pub fn ASN1_GENERALIZEDTIME_free(tm: *mut ASN1_GENERALIZEDTIME); pub fn ASN1_GENERALIZEDTIME_print(b: *mut BIO, tm: *const ASN1_GENERALIZEDTIME) -> c_int; diff --git a/openssl/src/aes.rs b/openssl/src/aes.rs new file mode 100644 index 00000000..d226c515 --- /dev/null +++ b/openssl/src/aes.rs @@ -0,0 +1,115 @@ +//! Low level AES functionality +//! +//! The `symm` module should be used in preference to this module in most cases. +use ffi; +use std::mem; +use libc::c_int; + +use symm::Mode; + +#[derive(Debug)] +pub struct KeyError(()); + +pub struct AesKey(ffi::AES_KEY); + +impl AesKey { + /// Prepares a key for encryption. + /// + /// # Failure + /// + /// Returns an error if the key is not 128, 192, or 256 bits. + pub fn new_encrypt(key: &[u8]) -> Result { + unsafe { + assert!(key.len() <= c_int::max_value() as usize / 8); + + let mut aes_key = mem::uninitialized(); + let r = ffi::AES_set_encrypt_key(key.as_ptr() as *const _, + key.len() as c_int * 8, + &mut aes_key); + if r == 0 { + Ok(AesKey(aes_key)) + } else { + Err(KeyError(())) + } + } + } + + /// Prepares a key for decryption. + /// + /// # Failure + /// + /// Returns an error if the key is not 128, 192, or 256 bits. + pub fn new_decrypt(key: &[u8]) -> Result { + unsafe { + assert!(key.len() <= c_int::max_value() as usize / 8); + + let mut aes_key = mem::uninitialized(); + let r = ffi::AES_set_decrypt_key(key.as_ptr() as *const _, + key.len() as c_int * 8, + &mut aes_key); + + if r == 0 { + Ok(AesKey(aes_key)) + } else { + Err(KeyError(())) + } + } + } +} + +/// Performs AES IGE encryption or decryption +/// +/// # Panics +/// +/// Panics if `in_` is not the same length as `out`, if that length is not a multiple of 16, or if +/// `iv` is not at least 32 bytes. +pub fn aes_ige(in_: &[u8], out: &mut [u8], key: &AesKey, iv: &mut [u8], mode: Mode) { + unsafe { + assert!(in_.len() == out.len()); + assert!(in_.len() % ffi::AES_BLOCK_SIZE as usize == 0); + assert!(iv.len() >= ffi::AES_BLOCK_SIZE as usize * 2); + + let mode = match mode { + Mode::Encrypt => ffi::AES_ENCRYPT, + Mode::Decrypt => ffi::AES_DECRYPT, + }; + ffi::AES_ige_encrypt(in_.as_ptr() as *const _, + out.as_mut_ptr() as *mut _, + in_.len(), + &key.0, + iv.as_mut_ptr() as *mut _, + mode); + } +} + +#[cfg(test)] +mod test { + use hex::FromHex; + + use symm::Mode; + use super::*; + + // From https://www.mgp25.com/AESIGE/ + #[test] + fn ige_vector_1() { + let raw_key = "000102030405060708090A0B0C0D0E0F"; + let raw_iv = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"; + let raw_pt = "0000000000000000000000000000000000000000000000000000000000000000"; + let raw_ct = "1A8519A6557BE652E9DA8E43DA4EF4453CF456B4CA488AA383C79C98B34797CB"; + + let key = AesKey::new_encrypt(&Vec::from_hex(raw_key).unwrap()).unwrap(); + let mut iv = Vec::from_hex(raw_iv).unwrap(); + let pt = Vec::from_hex(raw_pt).unwrap(); + let ct = Vec::from_hex(raw_ct).unwrap(); + + let mut ct_actual = vec![0; ct.len()]; + aes_ige(&pt, &mut ct_actual, &key, &mut iv, Mode::Encrypt); + assert_eq!(ct_actual, ct); + + let key = AesKey::new_decrypt(&Vec::from_hex(raw_key).unwrap()).unwrap(); + let mut iv = Vec::from_hex(raw_iv).unwrap(); + let mut pt_actual = vec![0; pt.len()]; + aes_ige(&ct, &mut pt_actual, &key, &mut iv, Mode::Decrypt); + assert_eq!(pt_actual, pt); + } +} diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index 9138896b..ea71a269 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -24,6 +24,7 @@ mod macros; mod bio; mod util; +pub mod aes; pub mod asn1; pub mod bn; pub mod crypto; diff --git a/systest/build.rs b/systest/build.rs index b12739f0..548d6080 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -47,6 +47,7 @@ fn main() { .header("openssl/rand.h") .header("openssl/pkcs12.h") .header("openssl/bn.h") + .header("openssl/aes.h") .header("openssl/ocsp.h"); cfg.type_name(|s, is_struct| { // Add some `*` on some callback parameters to get function pointer to From 26e159a5f07a36be24fc35221154fdaebcabdf02 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 21 Jan 2017 11:11:24 +0000 Subject: [PATCH 4/6] Support chacha20 and chacha20_poly1305 --- openssl-sys/src/ossl110.rs | 3 ++ openssl/src/crypto.rs | 1 - openssl/src/ocsp.rs | 2 +- openssl/src/symm.rs | 60 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/openssl-sys/src/ossl110.rs b/openssl-sys/src/ossl110.rs index 925b0d8a..75c6253e 100644 --- a/openssl-sys/src/ossl110.rs +++ b/openssl-sys/src/ossl110.rs @@ -68,6 +68,9 @@ extern { pub fn CRYPTO_malloc(num: size_t, file: *const c_char, line: c_int) -> *mut c_void; pub fn CRYPTO_free(buf: *mut c_void, file: *const c_char, line: c_int); + pub fn EVP_chacha20() -> *const ::EVP_CIPHER; + pub fn EVP_chacha20_poly1305() -> *const ::EVP_CIPHER; + pub fn HMAC_CTX_new() -> *mut HMAC_CTX; pub fn HMAC_CTX_free(ctx: *mut HMAC_CTX); diff --git a/openssl/src/crypto.rs b/openssl/src/crypto.rs index 49029318..9853d99c 100644 --- a/openssl/src/crypto.rs +++ b/openssl/src/crypto.rs @@ -2,4 +2,3 @@ use string::OpensslString; #[deprecated(note = "renamed to OpensslString", since = "0.9.7")] pub type CryptoString = OpensslString; - diff --git a/openssl/src/ocsp.rs b/openssl/src/ocsp.rs index bba5c561..708c3561 100644 --- a/openssl/src/ocsp.rs +++ b/openssl/src/ocsp.rs @@ -141,7 +141,7 @@ impl OcspBasicResponseRef { /// Verifies the validity of the response. /// /// The `certs` parameter contains a set of certificates that will be searched when locating the - /// OCSP response signing certificate. Some responders to not include this in the response. + /// OCSP response signing certificate. Some responders do not include this in the response. pub fn verify(&self, certs: &StackRef, store: &X509StoreRef, diff --git a/openssl/src/symm.rs b/openssl/src/symm.rs index 99ee4c67..5cf1ce0b 100644 --- a/openssl/src/symm.rs +++ b/openssl/src/symm.rs @@ -108,6 +108,18 @@ impl Cipher { unsafe { Cipher(ffi::EVP_rc4()) } } + /// Requires the `v110` feature and OpenSSL 1.1.0. + #[cfg(all(ossl110, feature = "v110"))] + pub fn chacha20() -> Cipher { + unsafe { Cipher(ffi::EVP_chacha20()) } + } + + /// Requires the `v110` feature and OpenSSL 1.1.0. + #[cfg(all(ossl110, feature = "v110"))] + pub fn chacha20_poly1305() -> Cipher { + unsafe { Cipher(ffi::EVP_chacha20_poly1305()) } + } + pub unsafe fn from_ptr(ptr: *const ffi::EVP_CIPHER) -> Cipher { Cipher(ptr) } @@ -767,4 +779,52 @@ mod tests { &Vec::from_hex(tag).unwrap()).unwrap(); assert_eq!(pt, out.to_hex()); } + + #[test] + #[cfg(all(ossl110, feature = "v110"))] + fn test_chacha20() { + let key = "0000000000000000000000000000000000000000000000000000000000000000"; + let iv = "00000000000000000000000000000000"; + let pt = "000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 00000000000000000000000000000000000000000000000"; + let ct = "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7\ + 724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"; + + cipher_test(Cipher::chacha20(), pt, ct, key, iv); + } + + #[test] + #[cfg(all(ossl110, feature = "v110"))] + fn test_chacha20_poly1305() { + let key = "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"; + let iv = "070000004041424344454647"; + let aad = "50515253c0c1c2c3c4c5c6c7"; + let pt = "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393\ + a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f722074\ + 6865206675747572652c2073756e73637265656e20776f756c642062652069742e"; + let ct = "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca967128\ + 2fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fa\ + b324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116"; + let tag = "1ae10b594f09e26a7e902ecbd0600691"; + + let mut actual_tag = [0; 16]; + let out = encrypt_aead(Cipher::chacha20_poly1305(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(pt).unwrap(), + &mut actual_tag) + .unwrap(); + assert_eq!(ct, out.to_hex()); + assert_eq!(tag, actual_tag.to_hex()); + + let out = decrypt_aead(Cipher::chacha20_poly1305(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(ct).unwrap(), + &Vec::from_hex(tag).unwrap()) + .unwrap(); + assert_eq!(pt, out.to_hex()); + } } From 1ffdf8a1ab75f49b95bae96d4dac31ab6cd3f526 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 21 Jan 2017 14:43:43 +0000 Subject: [PATCH 5/6] Fix test warnings --- openssl/src/ssl/tests/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs index 349c7a4d..536088ab 100644 --- a/openssl/src/ssl/tests/mod.rs +++ b/openssl/src/ssl/tests/mod.rs @@ -100,6 +100,7 @@ impl Server { Server::new_tcp(&["-www"]) } + #[allow(dead_code)] fn new_alpn() -> (Server, TcpStream) { Server::new_tcp(&["-www", "-nextprotoneg", @@ -1428,7 +1429,7 @@ fn status_callbacks() { let response = OcspResponse::from_der(ssl.ocsp_status().unwrap()).unwrap(); assert_eq!(response.status(), RESPONSE_STATUS_UNAUTHORIZED); Ok(true) - }); + }).unwrap(); let mut ssl = Ssl::new(&ctx.build()).unwrap(); ssl.set_status_type(STATUS_TYPE_OCSP).unwrap(); ssl.connect(stream).unwrap(); From 54900976bb76d3cef4a78df48a3645b0ac49bb46 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 22 Jan 2017 10:44:59 +0000 Subject: [PATCH 6/6] Support EC_GROUP_set_asn1_flag Closes #561 --- openssl-sys/src/lib.rs | 3 ++ openssl/src/ec.rs | 84 +++++++++++++++++++++++++++++++++------ openssl/src/x509/tests.rs | 23 +++++++++++ 3 files changed, 97 insertions(+), 13 deletions(-) diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 865061ee..970539d7 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -1109,6 +1109,8 @@ pub const OCSP_RESPONSE_STATUS_TRYLATER: c_int = 3; pub const OCSP_RESPONSE_STATUS_SIGREQUIRED: c_int = 5; pub const OCSP_RESPONSE_STATUS_UNAUTHORIZED: c_int = 6; +pub const OPENSSL_EC_NAMED_CURVE: c_int = 1; + pub const PKCS5_SALT_LEN: c_int = 8; pub const RSA_F4: c_long = 0x10001; @@ -1510,6 +1512,7 @@ extern { pub fn EC_GROUP_get_curve_GF2m(group: *const EC_GROUP, p: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int; pub fn EC_GROUP_get_degree(group: *const EC_GROUP) -> c_int; pub fn EC_GROUP_get_order(group: *const EC_GROUP, order: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int; + pub fn EC_GROUP_set_asn1_flag(key: *mut EC_GROUP, flag: c_int); pub fn EC_GROUP_free(group: *mut EC_GROUP); diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index 592c4026..ebb631e8 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -1,11 +1,13 @@ use ffi; use std::ptr; +use std::mem; +use libc::c_int; use {cvt, cvt_n, cvt_p, init}; use bn::{BigNumRef, BigNumContextRef}; use error::ErrorStack; use nid::Nid; -use types::OpenSslTypeRef; +use types::{OpenSslType, OpenSslTypeRef}; pub const POINT_CONVERSION_COMPRESSED: PointConversionForm = PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_COMPRESSED); @@ -16,9 +18,17 @@ pub const POINT_CONVERSION_UNCOMPRESSED: PointConversionForm = pub const POINT_CONVERSION_HYBRID: PointConversionForm = PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_HYBRID); +// OPENSSL_EC_EXPLICIT_CURVE, but that was only added in 1.1. +// Man page documents that 0 can be used in older versions. +pub const EXPLICIT_CURVE: Asn1Flag = Asn1Flag(0); +pub const NAMED_CURVE: Asn1Flag = Asn1Flag(ffi::OPENSSL_EC_NAMED_CURVE); + #[derive(Copy, Clone)] pub struct PointConversionForm(ffi::point_conversion_form_t); +#[derive(Copy, Clone)] +pub struct Asn1Flag(c_int); + type_!(EcGroup, EcGroupRef, ffi::EC_GROUP, ffi::EC_GROUP_free); impl EcGroup { @@ -80,6 +90,17 @@ impl EcGroupRef { cvt(ffi::EC_GROUP_get_order(self.as_ptr(), order.as_ptr(), ctx.as_ptr())).map(|_| ()) } } + + /// Sets the flag determining if the group corresponds to a named curve or must be explicitly + /// parameterized. + /// + /// This defaults to `EXPLICIT_CURVE` in OpenSSL 1.0.1 and 1.0.2, but `NAMED_CURVE` in OpenSSL + /// 1.1.0. + pub fn set_asn1_flag(&mut self, flag: Asn1Flag) { + unsafe { + ffi::EC_GROUP_set_asn1_flag(self.as_ptr(), flag.0); + } + } } type_!(EcPoint, EcPointRef, ffi::EC_POINT, ffi::EC_POINT_free); @@ -311,22 +332,18 @@ impl EcKey { /// let key = EcKey::from_public_key(&group, &point); /// ``` pub fn from_public_key(group: &EcGroupRef, public_key: &EcPointRef) -> Result { - unsafe { - let key = EcKey(try!(cvt_p(ffi::EC_KEY_new()))); - try!(cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr()))); - try!(cvt(ffi::EC_KEY_set_public_key(key.as_ptr(), public_key.as_ptr()))); - Ok(key) - } + let mut builder = try!(EcKeyBuilder::new()); + try!(builder.set_group(group)); + try!(builder.set_public_key(public_key)); + Ok(builder.build()) } /// Generates a new public/private key pair on the specified curve. pub fn generate(group: &EcGroupRef) -> Result { - unsafe { - let key = EcKey(try!(cvt_p(ffi::EC_KEY_new()))); - try!(cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr()))); - try!(cvt(ffi::EC_KEY_generate_key(key.as_ptr()))); - Ok(key) - } + let mut builder = try!(EcKeyBuilder::new()); + try!(builder.set_group(group)); + try!(builder.generate_key()); + Ok(builder.build()) } #[deprecated(since = "0.9.2", note = "use from_curve_name")] @@ -338,6 +355,47 @@ impl EcKey { private_key_from_der!(EcKey, ffi::d2i_ECPrivateKey); } +type_!(EcKeyBuilder, EcKeyBuilderRef, ffi::EC_KEY, ffi::EC_KEY_free); + +impl EcKeyBuilder { + pub fn new() -> Result { + unsafe { + init(); + cvt_p(ffi::EC_KEY_new()).map(EcKeyBuilder) + } + } + + pub fn build(self) -> EcKey { + unsafe { + let key = EcKey::from_ptr(self.as_ptr()); + mem::forget(self); + key + } + } +} + +impl EcKeyBuilderRef { + pub fn set_group(&mut self, group: &EcGroupRef) -> Result<&mut EcKeyBuilderRef, ErrorStack> { + unsafe { + cvt(ffi::EC_KEY_set_group(self.as_ptr(), group.as_ptr())).map(|_| self) + } + } + + pub fn set_public_key(&mut self, + public_key: &EcPointRef) + -> Result<&mut EcKeyBuilderRef, ErrorStack> { + unsafe { + cvt(ffi::EC_KEY_set_public_key(self.as_ptr(), public_key.as_ptr())).map(|_| self) + } + } + + pub fn generate_key(&mut self) -> Result<&mut EcKeyBuilderRef, ErrorStack> { + unsafe { + cvt(ffi::EC_KEY_generate_key(self.as_ptr())).map(|_| self) + } + } +} + #[cfg(test)] mod test { use bn::BigNumContext; diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index f89b7267..01cbf2ec 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -1,8 +1,11 @@ use hex::{FromHex, ToHex}; +use ec::{NAMED_CURVE, EcGroup, EcKey}; use hash::MessageDigest; +use nid::X9_62_PRIME256V1; use pkey::PKey; use rsa::Rsa; +use ssl::{SslMethod, SslContextBuilder}; use x509::{X509, X509Generator}; use x509::extension::Extension::{KeyUsage, ExtKeyUsage, SubjectAltName, OtherNid, OtherStr}; use x509::extension::AltNameOption as SAN; @@ -197,3 +200,23 @@ fn issued() { ca.issued(&cert).unwrap(); cert.issued(&cert).err().unwrap(); } + +#[test] +fn ecdsa_cert() { + let mut group = EcGroup::from_curve_name(X9_62_PRIME256V1).unwrap(); + group.set_asn1_flag(NAMED_CURVE); + let key = EcKey::generate(&group).unwrap(); + let key = PKey::from_ec_key(key).unwrap(); + + let cert = X509Generator::new() + .set_valid_period(365) + .add_name("CN".to_owned(), "TestServer".to_owned()) + .set_sign_hash(MessageDigest::sha256()) + .sign(&key) + .unwrap(); + + let mut ctx = SslContextBuilder::new(SslMethod::tls()).unwrap(); + ctx.set_certificate(&cert).unwrap(); + ctx.set_private_key(&key).unwrap(); + ctx.check_private_key().unwrap(); +}