From 3116d68815d89d785ba172f2c70b676237af3d49 Mon Sep 17 00:00:00 2001 From: Hanson Char Date: Tue, 17 Oct 2023 16:56:32 -0700 Subject: [PATCH] Support compresssed octets format in agreement::agree_ephemeral() #1255 + Add EphemeralPrivateKey.compute_public_key_compressed() + Fix test cases for public keys in compressed format --- aws-lc-rs/src/agreement.rs | 85 +++++++++++++++++++++----- aws-lc-rs/src/data/agreement_tests.txt | 32 +++++++--- aws-lc-rs/src/ec.rs | 32 +++++++++- 3 files changed, 123 insertions(+), 26 deletions(-) diff --git a/aws-lc-rs/src/agreement.rs b/aws-lc-rs/src/agreement.rs index 65fb4ff7ac1..6d9927ee93c 100644 --- a/aws-lc-rs/src/agreement.rs +++ b/aws-lc-rs/src/agreement.rs @@ -97,6 +97,16 @@ impl AlgorithmID { AlgorithmID::X25519 => 32, } } + + #[inline] + fn pub_key_len_compressed(&self) -> usize { + match self { + AlgorithmID::ECDH_P256 => 33, + AlgorithmID::ECDH_P384 => 49, + AlgorithmID::ECDH_P521 => 67, + AlgorithmID::X25519 => 32, + } + } } impl Debug for AlgorithmID { @@ -321,7 +331,7 @@ impl EphemeralPrivateKey { }) } - /// Computes the public key from the private key. + /// Computes the public key from the private key in uncompressed format. /// /// # Errors /// `error::Unspecified` when operation fails due to internal error. @@ -341,22 +351,54 @@ impl EphemeralPrivateKey { }) } } - KeyInner::X25519(priv_key) => { - let mut buffer = [0u8; MAX_PUBLIC_KEY_LEN]; - let mut out_len = buffer.len(); + KeyInner::X25519(priv_key) => self.compute_x25519_public_key(priv_key), + } + } - if 1 != unsafe { - EVP_PKEY_get_raw_public_key(**priv_key, buffer.as_mut_ptr(), &mut out_len) - } { - return Err(Unspecified); - } + fn compute_x25519_public_key( + &self, + priv_key: &LcPtr, + ) -> Result { + let mut buffer = [0u8; MAX_PUBLIC_KEY_LEN]; + let mut out_len = buffer.len(); + + if 1 != unsafe { + EVP_PKEY_get_raw_public_key(**priv_key, buffer.as_mut_ptr(), &mut out_len) + } { + return Err(Unspecified); + } - Ok(PublicKey { - alg: self.algorithm(), - public_key: buffer, - len: out_len, - }) + Ok(PublicKey { + alg: self.algorithm(), + public_key: buffer, + len: out_len, + }) + } + + /// Computes the public key from the private key in compressed format. + /// + /// # Errors + /// `error::Unspecified` when operation fails due to internal error. + /// + pub fn compute_public_key_compressed(&self) -> Result { + match &self.inner_key { + KeyInner::ECDH_P256(ec_key) + | KeyInner::ECDH_P384(ec_key) + | KeyInner::ECDH_P521(ec_key) => { + let mut buffer = [0u8; MAX_PUBLIC_KEY_LEN]; + unsafe { + let key_len = ec::marshal_public_key_to_buffer_compressed( + &mut buffer, + &ec_key.as_const(), + )?; + Ok(PublicKey { + alg: self.algorithm(), + public_key: buffer, + len: key_len, + }) + } } + KeyInner::X25519(priv_key) => self.compute_x25519_public_key(priv_key), } } @@ -529,8 +571,13 @@ where if peer_public_key.alg != expected_alg { return Err(error_value); } + let peer_pub_bytes = peer_public_key.bytes.as_ref(); - if peer_pub_bytes.len() != expected_pub_key_len { + let peer_pub_bytes_len = peer_pub_bytes.len(); + + if peer_pub_bytes_len != expected_pub_key_len + && peer_pub_bytes_len != expected_alg.id.pub_key_len_compressed() + { return Err(error_value); } @@ -955,7 +1002,13 @@ mod tests { assert_eq!(my_private.algorithm(), alg); let computed_public = my_private.compute_public_key().unwrap(); - assert_eq!(computed_public.as_ref(), &my_public[..]); + if computed_public.as_ref().len() == my_public.len() { + assert_eq!(computed_public.as_ref(), &my_public[..]); + } else { + let computed_public = + my_private.compute_public_key_compressed().unwrap(); + assert_eq!(computed_public.as_ref(), &my_public[..]); + } assert_eq!(my_private.algorithm(), alg); diff --git a/aws-lc-rs/src/data/agreement_tests.txt b/aws-lc-rs/src/data/agreement_tests.txt index 589c46a69fc..aad01337c47 100644 --- a/aws-lc-rs/src/data/agreement_tests.txt +++ b/aws-lc-rs/src/data/agreement_tests.txt @@ -250,30 +250,48 @@ Curve = P-521 PeerQ = 0400D0B3975AC4B799F5BEA16D5E13E9AF971D5E9B984C9F39728B5E5739735A219B97C356436ADC6E95BB0352F6BE64A6C2912D4EF2D0433CED2B6171640012D9460F Error = Peer public key is missing the Y coordinate completely. +# RFC 5903 (IKE and IKEv2 ECDH) Test Vectors +# +# PeerQ is (grx, gry) in compressed encoding. +# D is i. +# MyQ is (gix, giy) in compressed encoding. +# Output is girx. + Curve = P-256 PeerQ = 02D12DFB5289C8D4F81208B70270398C342296970A0BCCB74C736FC7554494BF63 -Error = Peer public key is in compressed form (0x02). +D = C88F01F510D9AC3F70A292DAA2316DE544E9AAB8AFE84049C62A9C57862D1433 +MyQ = 03dad0b65394221cf9b051e1feca5787d098dfe637fc90b9ef945d0c3772581180 +Output = D6840F6B42F6EDAFD13116E0E12565202FEF8E9ECE7DCE03812464D04B9442DE Curve = P-384 PeerQ = 02E558DBEF53EECDE3D3FCCFC1AEA08A89A987475D12FD950D83CFA41732BC509D0D1AC43A0336DEF96FDA41D0774A3571 -Error = Peer public key is in compressed form (0x02). +D = 099F3C7034D4A2C699884D73A375A67F7624EF7C6B3C0F160647B67414DCE655E35B538041E649EE3FAEF896783AB194 +MyQ = 02667842d7d180ac2cde6f74f37551f55755c7645c20ef73e31634fe72b4c55ee6de3ac808acb4bdb4c88732aee95f41aa +Output = 11187331C279962D93D604243FD592CB9D0A926F422E47187521287E7156C5C4D603135569B9E9D09CF5D4A270F59746 Curve = P-521 PeerQ = 0200D0B3975AC4B799F5BEA16D5E13E9AF971D5E9B984C9F39728B5E5739735A219B97C356436ADC6E95BB0352F6BE64A6C2912D4EF2D0433CED2B6171640012D9460F -Error = Peer public key is in compressed form (0x02). +D = 0037ADE9319A89F4DABDB3EF411AACCCA5123C61ACAB57B5393DCE47608172A095AA85A30FE1C2952C6771D937BA9777F5957B2639BAB072462F68C27A57382D4A52 +MyQ = 020015417e84dbf28c0ad3c278713349dc7df153c897a1891bd98bab4357c9ecbee1e3bf42e00b8e380aeae57c2d107564941885942af5a7f4601723c4195d176ced3e +Output = 01144C7D79AE6956BC8EDB8E7C787C4521CB086FA64407F97894E5E6B2D79B04D1427E73CA4BAA240A34786859810C06B3C715A3A8CC3151F2BEE417996D19F3DDEA Curve = P-256 PeerQ = 03D12DFB5289C8D4F81208B70270398C342296970A0BCCB74C736FC7554494BF63 -Error = Peer public key is in compressed form (0x03). +D = C88F01F510D9AC3F70A292DAA2316DE544E9AAB8AFE84049C62A9C57862D1433 +MyQ = 03dad0b65394221cf9b051e1feca5787d098dfe637fc90b9ef945d0c3772581180 +Output = D6840F6B42F6EDAFD13116E0E12565202FEF8E9ECE7DCE03812464D04B9442DE Curve = P-384 PeerQ = 03E558DBEF53EECDE3D3FCCFC1AEA08A89A987475D12FD950D83CFA41732BC509D0D1AC43A0336DEF96FDA41D0774A3571 -Error = Peer public key is in compressed form (0x03). +D = 099F3C7034D4A2C699884D73A375A67F7624EF7C6B3C0F160647B67414DCE655E35B538041E649EE3FAEF896783AB194 +MyQ = 02667842d7d180ac2cde6f74f37551f55755c7645c20ef73e31634fe72b4c55ee6de3ac808acb4bdb4c88732aee95f41aa +Output = 11187331C279962D93D604243FD592CB9D0A926F422E47187521287E7156C5C4D603135569B9E9D09CF5D4A270F59746 Curve = P-521 PeerQ = 0300D0B3975AC4B799F5BEA16D5E13E9AF971D5E9B984C9F39728B5E5739735A219B97C356436ADC6E95BB0352F6BE64A6C2912D4EF2D0433CED2B6171640012D9460F -Error = Peer public key is in compressed form (0x03). - +D = 0037ADE9319A89F4DABDB3EF411AACCCA5123C61ACAB57B5393DCE47608172A095AA85A30FE1C2952C6771D937BA9777F5957B2639BAB072462F68C27A57382D4A52 +MyQ = 020015417e84dbf28c0ad3c278713349dc7df153c897a1891bd98bab4357c9ecbee1e3bf42e00b8e380aeae57c2d107564941885942af5a7f4601723c4195d176ced3e +Output = 01144C7D79AE6956BC8EDB8E7C787C4521CB086FA64407F97894E5E6B2D79B04D1427E73CA4BAA240A34786859810C06B3C715A3A8CC3151F2BEE417996D19F3DDEA # NIST vectors from # http://csrc.nist.gov/groups/STM/cavp/documents/components/ecccdhtestvectors.zip diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index 97f8ea9b2ca..da7af5769b8 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -278,7 +278,34 @@ pub(crate) unsafe fn marshal_public_key_to_buffer( let ec_point = ConstPointer::new(EC_KEY_get0_public_key(ec_key))?; - let out_len = ec_point_to_bytes(&ec_group, &ec_point, buffer)?; + let out_len = ec_point_to_bytes( + &ec_group, + &ec_point, + buffer, + point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED, + )?; + Ok(out_len) +} + +pub(crate) unsafe fn marshal_public_key_to_buffer_compressed( + buffer: &mut [u8; PUBLIC_KEY_MAX_LEN], + evp_pkey: &ConstPointer, +) -> Result { + let ec_key = EVP_PKEY_get0_EC_KEY(**evp_pkey); + if ec_key.is_null() { + return Err(Unspecified); + } + + let ec_group = ConstPointer::new(EC_KEY_get0_group(ec_key))?; + + let ec_point = ConstPointer::new(EC_KEY_get0_public_key(ec_key))?; + + let out_len = ec_point_to_bytes( + &ec_group, + &ec_point, + buffer, + point_conversion_form_t::POINT_CONVERSION_COMPRESSED, + )?; Ok(out_len) } @@ -444,9 +471,8 @@ unsafe fn ec_point_to_bytes( ec_group: &ConstPointer, ec_point: &ConstPointer, buf: &mut [u8; PUBLIC_KEY_MAX_LEN], + pt_conv_form: point_conversion_form_t, ) -> Result { - let pt_conv_form = point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED; - let out_len = EC_POINT_point2oct( **ec_group, **ec_point,