From cfb4ea31d51c274a31f500cd1a5e8bdac571607c Mon Sep 17 00:00:00 2001 From: Bradley Beddoes Date: Wed, 9 Aug 2017 12:02:58 +1000 Subject: [PATCH 1/4] Support for EcKey creation from affine coordinates Sets the public key for an EcKey based on its affine co-ordinates, i.e. it constructs an EC_POINT object based on the supplied x and y values and sets the public key to be this EC_POINT. The initial usecase here is creating EcKey instances from JWK representations as defined within RFC 7517. --- openssl-sys/src/lib.rs | 1 + openssl/Cargo.toml | 1 + openssl/src/ec.rs | 36 +++++++++++++++++++++++++++++++++++- openssl/src/lib.rs | 2 ++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 353f619f..69c95395 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -1674,6 +1674,7 @@ extern "C" { pub fn EC_KEY_generate_key(key: *mut EC_KEY) -> c_int; pub fn EC_KEY_check_key(key: *const EC_KEY) -> c_int; pub fn EC_KEY_free(key: *mut EC_KEY); + pub fn EC_KEY_set_public_key_affine_coordinates(key: *mut EC_KEY, x: *const BIGNUM, y: *const BIGNUM) -> c_int; #[cfg(not(osslconf = "OPENSSL_NO_EC2M"))] pub fn EC_GF2m_simple_method() -> *const EC_METHOD; diff --git a/openssl/Cargo.toml b/openssl/Cargo.toml index baa903a3..e6c89289 100644 --- a/openssl/Cargo.toml +++ b/openssl/Cargo.toml @@ -31,3 +31,4 @@ tempdir = "0.3" winapi = "0.2" ws2_32-sys = "0.2" hex = "0.2" +data-encoding = "1.2" diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index 95baa833..5ff9fd5b 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -459,12 +459,26 @@ impl EcKeyBuilderRef { pub fn generate_key(&mut self) -> Result<&mut EcKeyBuilderRef, ErrorStack> { unsafe { cvt(ffi::EC_KEY_generate_key(self.as_ptr())).map(|_| self) } } + + /// Sets the public key based on affine coordinates. + pub fn set_public_key_affine_coordinates(&mut self, + x: &BigNumRef, + y: &BigNumRef) + -> Result<&mut EcKeyBuilderRef, ErrorStack> { + unsafe { + cvt(ffi::EC_KEY_set_public_key_affine_coordinates(self.as_ptr(), + x.as_ptr(), + y.as_ptr()) + ).map(|_| self) + } + } } #[cfg(test)] mod test { - use bn::BigNumContext; + use bn::{BigNum, BigNumContext}; use nid; + use data_encoding; use super::*; #[test] @@ -539,4 +553,24 @@ mod test { assert!(ec_key.public_key().is_some()); assert!(ec_key.private_key().is_none()); } + + #[test] + fn key_from_affine_coordinates() { + let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); + let x = data_encoding::base64url::decode_nopad("MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4".as_bytes()) + .unwrap(); + let y = data_encoding::base64url::decode_nopad("4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM".as_bytes()) + .unwrap(); + + let xbn = BigNum::from_slice(&x).unwrap(); + let ybn = BigNum::from_slice(&y).unwrap(); + + let mut builder = EcKeyBuilder::new().unwrap(); + builder.set_group(&group).unwrap(); + builder.set_public_key_affine_coordinates(&xbn, &ybn).unwrap(); + + let ec_key = builder.build(); + assert!(ec_key.check_key().is_ok()); + assert!(ec_key.public_key().is_some()); + } } diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index 44752dcc..a6d5e6a0 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -13,6 +13,8 @@ extern crate openssl_sys as ffi; extern crate hex; #[cfg(test)] extern crate tempdir; +#[cfg(test)] +extern crate data_encoding; #[doc(inline)] pub use ffi::init; From d9e0321851ce0b3b9d5f10b6b0dab93478e265e5 Mon Sep 17 00:00:00 2001 From: Bradley Beddoes Date: Wed, 9 Aug 2017 12:44:54 +1000 Subject: [PATCH 2/4] Set the private key within EcKeyBuilder The initial usecase here is creating EcKey instances from JWK representations, that hold private keys, as defined within RFC 7517. --- openssl/src/ec.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index 5ff9fd5b..e221b411 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -472,6 +472,15 @@ impl EcKeyBuilderRef { ).map(|_| self) } } + + /// Sets the private key. + pub fn set_private_key(&mut self, + key: &BigNumRef) + -> Result<&mut EcKeyBuilderRef, ErrorStack> { + unsafe { + cvt(ffi::EC_KEY_set_private_key(self.as_ptr(), key.as_ptr())).map(|_| self) + } + } } #[cfg(test)] @@ -573,4 +582,20 @@ mod test { assert!(ec_key.check_key().is_ok()); assert!(ec_key.public_key().is_some()); } + + #[test] + fn set_private_key() { + let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); + let d = data_encoding::base64url::decode_nopad("870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE".as_bytes()) + .unwrap(); + + let dbn = BigNum::from_slice(&d).unwrap(); + + let mut builder = EcKeyBuilder::new().unwrap(); + builder.set_group(&group).unwrap(); + builder.set_private_key(&dbn).unwrap(); + + let ec_key = builder.build(); + assert!(ec_key.private_key().is_some()); + } } From 16e8fbc31ee2f144b43a0a8353d6f85514081bf3 Mon Sep 17 00:00:00 2001 From: Bradley Beddoes Date: Wed, 9 Aug 2017 13:21:57 +1000 Subject: [PATCH 3/4] Fix EC_KEY_set_public_key_affine_coordinates Previous definition incorrectly used `const` pointers but the underpinning library definition (unfortunately) does not. --- openssl-sys/src/lib.rs | 2 +- openssl/src/ec.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 69c95395..f0e3e18a 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -1674,7 +1674,7 @@ extern "C" { pub fn EC_KEY_generate_key(key: *mut EC_KEY) -> c_int; pub fn EC_KEY_check_key(key: *const EC_KEY) -> c_int; pub fn EC_KEY_free(key: *mut EC_KEY); - pub fn EC_KEY_set_public_key_affine_coordinates(key: *mut EC_KEY, x: *const BIGNUM, y: *const BIGNUM) -> c_int; + pub fn EC_KEY_set_public_key_affine_coordinates(key: *mut EC_KEY, x: *mut BIGNUM, y: *mut BIGNUM) -> c_int; #[cfg(not(osslconf = "OPENSSL_NO_EC2M"))] pub fn EC_GF2m_simple_method() -> *const EC_METHOD; diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index e221b411..0a3cfcb6 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -462,8 +462,8 @@ impl EcKeyBuilderRef { /// Sets the public key based on affine coordinates. pub fn set_public_key_affine_coordinates(&mut self, - x: &BigNumRef, - y: &BigNumRef) + x: &mut BigNumRef, + y: &mut BigNumRef) -> Result<&mut EcKeyBuilderRef, ErrorStack> { unsafe { cvt(ffi::EC_KEY_set_public_key_affine_coordinates(self.as_ptr(), @@ -571,12 +571,12 @@ mod test { let y = data_encoding::base64url::decode_nopad("4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM".as_bytes()) .unwrap(); - let xbn = BigNum::from_slice(&x).unwrap(); - let ybn = BigNum::from_slice(&y).unwrap(); + let mut xbn = BigNum::from_slice(&x).unwrap(); + let mut ybn = BigNum::from_slice(&y).unwrap(); let mut builder = EcKeyBuilder::new().unwrap(); builder.set_group(&group).unwrap(); - builder.set_public_key_affine_coordinates(&xbn, &ybn).unwrap(); + builder.set_public_key_affine_coordinates(&mut xbn, &mut ybn).unwrap(); let ec_key = builder.build(); assert!(ec_key.check_key().is_ok()); From c96658387740983610e91d3dc37bf98701f4ce94 Mon Sep 17 00:00:00 2001 From: Bradley Beddoes Date: Wed, 9 Aug 2017 14:20:22 +1000 Subject: [PATCH 4/4] Refine sig for set_public_key_affine_coordinates This functions signature was originally defined to require mutable references for `x` / `y` as the underpinning OpenSSL C API was not `const`. However the actual OpenSSL implementation makes no changes. This being the case we've chosen to reflect non mutability at the Rust level. --- openssl/src/ec.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index 0a3cfcb6..e221b411 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -462,8 +462,8 @@ impl EcKeyBuilderRef { /// Sets the public key based on affine coordinates. pub fn set_public_key_affine_coordinates(&mut self, - x: &mut BigNumRef, - y: &mut BigNumRef) + x: &BigNumRef, + y: &BigNumRef) -> Result<&mut EcKeyBuilderRef, ErrorStack> { unsafe { cvt(ffi::EC_KEY_set_public_key_affine_coordinates(self.as_ptr(), @@ -571,12 +571,12 @@ mod test { let y = data_encoding::base64url::decode_nopad("4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM".as_bytes()) .unwrap(); - let mut xbn = BigNum::from_slice(&x).unwrap(); - let mut ybn = BigNum::from_slice(&y).unwrap(); + let xbn = BigNum::from_slice(&x).unwrap(); + let ybn = BigNum::from_slice(&y).unwrap(); let mut builder = EcKeyBuilder::new().unwrap(); builder.set_group(&group).unwrap(); - builder.set_public_key_affine_coordinates(&mut xbn, &mut ybn).unwrap(); + builder.set_public_key_affine_coordinates(&xbn, &ybn).unwrap(); let ec_key = builder.build(); assert!(ec_key.check_key().is_ok());