diff options
author | Joe Birr-Pixton <jpixton@gmail.com> | 2024-09-07 13:56:15 +0100 |
---|---|---|
committer | Joe Birr-Pixton <jpixton@gmail.com> | 2024-09-10 10:19:58 +0000 |
commit | bc892cd5db6eaa737e68feffcec18fc8ccf94f6a (patch) | |
tree | 1977aa6cde428e0f34c160d1064af1f0118312cb | |
parent | 97301811997dbbd2208124d6cdf4f3587c4eb028 (diff) |
kx: work around aws-lc-rs accepting more point formats
-rw-r--r-- | rustls/src/crypto/ring/kx.rs | 30 |
1 files changed, 30 insertions, 0 deletions
diff --git a/rustls/src/crypto/ring/kx.rs b/rustls/src/crypto/ring/kx.rs index 8c267e9e..8c019d2e 100644 --- a/rustls/src/crypto/ring/kx.rs +++ b/rustls/src/crypto/ring/kx.rs @@ -26,6 +26,20 @@ struct KxGroup { /// `SupportedKxGroup::fips()` is true if and only if the algorithm is allowed, /// _and_ the implementation is FIPS-validated. fips_allowed: bool, + + /// aws-lc-rs 1.9 and later accepts more formats of public keys than + /// just uncompressed. + /// + /// That is not compatible with TLS: + /// - TLS1.3 outlaws other encodings, + /// - TLS1.2 negotiates other encodings (we only offer uncompressed), and + /// defaults to uncompressed if negotiation is not done. + /// + /// This function should return `true` if the basic shape of its argument + /// is consistent with an uncompressed point encoding. It does not need + /// to verify that the point is on the curve (if the curve requires that + /// for security); aws-lc-rs/ring must do that. + pub_key_validator: fn(&[u8]) -> bool, } impl SupportedKxGroup for KxGroup { @@ -43,6 +57,7 @@ impl SupportedKxGroup for KxGroup { agreement_algorithm: self.agreement_algorithm, priv_key, pub_key, + pub_key_validator: self.pub_key_validator, })) } @@ -76,6 +91,8 @@ pub static X25519: &dyn SupportedKxGroup = &KxGroup { // are not compliant to SP 800-56Arev3." // -- <https://csrc.nist.gov/csrc/media/Projects/cryptographic-module-validation-program/documents/fips%20140-3/FIPS%20140-3%20IG.pdf> fips_allowed: false, + + pub_key_validator: |point: &[u8]| point.len() == 32, }; /// Ephemeral ECDH on secp256r1 (aka NIST-P256) @@ -83,6 +100,7 @@ pub static SECP256R1: &dyn SupportedKxGroup = &KxGroup { name: NamedGroup::secp256r1, agreement_algorithm: &agreement::ECDH_P256, fips_allowed: true, + pub_key_validator: uncompressed_point, }; /// Ephemeral ECDH on secp384r1 (aka NIST-P384) @@ -90,8 +108,16 @@ pub static SECP384R1: &dyn SupportedKxGroup = &KxGroup { name: NamedGroup::secp384r1, agreement_algorithm: &agreement::ECDH_P384, fips_allowed: true, + pub_key_validator: uncompressed_point, }; +fn uncompressed_point(point: &[u8]) -> bool { + // See `UncompressedPointRepresentation`, which is a retelling of + // SEC1 section 2.3.3 "Elliptic-Curve-Point-to-Octet-String Conversion" + // <https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.8.2> + matches!(point.first(), Some(0x04)) +} + /// A list of all the key exchange groups supported by rustls. pub static ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[X25519, SECP256R1, SECP384R1]; @@ -102,11 +128,15 @@ struct KeyExchange { agreement_algorithm: &'static agreement::Algorithm, priv_key: agreement::EphemeralPrivateKey, pub_key: agreement::PublicKey, + pub_key_validator: fn(&[u8]) -> bool, } impl ActiveKeyExchange for KeyExchange { /// Completes the key exchange, given the peer's public key. fn complete(self: Box<Self>, peer: &[u8]) -> Result<SharedSecret, Error> { + if !(self.pub_key_validator)(peer) { + return Err(PeerMisbehaved::InvalidKeyShare.into()); + } let peer_key = agreement::UnparsedPublicKey::new(self.agreement_algorithm, peer); super::ring_shim::agree_ephemeral(self.priv_key, &peer_key) .map_err(|_| PeerMisbehaved::InvalidKeyShare.into()) |