From 54900976bb76d3cef4a78df48a3645b0ac49bb46 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 22 Jan 2017 10:44:59 +0000 Subject: [PATCH] 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(); +}