Skip to content

Commit

Permalink
Support compresssed octets format in agreement::agree_ephemeral() #1255
Browse files Browse the repository at this point in the history
+ Add EphemeralPrivateKey.compute_public_key_compressed()
+ Fix test cases for public keys in compressed format
  • Loading branch information
Hanson Char committed Nov 25, 2023
1 parent d8ad218 commit 3116d68
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 26 deletions.
85 changes: 69 additions & 16 deletions aws-lc-rs/src/agreement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand All @@ -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<EVP_PKEY>,
) -> Result<PublicKey, Unspecified> {
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<PublicKey, Unspecified> {
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),
}
}

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);

Expand Down
32 changes: 25 additions & 7 deletions aws-lc-rs/src/data/agreement_tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 29 additions & 3 deletions aws-lc-rs/src/ec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<EVP_PKEY>,
) -> Result<usize, Unspecified> {
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)
}

Expand Down Expand Up @@ -444,9 +471,8 @@ unsafe fn ec_point_to_bytes(
ec_group: &ConstPointer<EC_GROUP>,
ec_point: &ConstPointer<EC_POINT>,
buf: &mut [u8; PUBLIC_KEY_MAX_LEN],
pt_conv_form: point_conversion_form_t,
) -> Result<usize, Unspecified> {
let pt_conv_form = point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED;

let out_len = EC_POINT_point2oct(
**ec_group,
**ec_point,
Expand Down

0 comments on commit 3116d68

Please sign in to comment.