summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Birr-Pixton <jpixton@gmail.com>2024-09-07 13:56:15 +0100
committerJoe Birr-Pixton <jpixton@gmail.com>2024-09-10 10:19:58 +0000
commitbc892cd5db6eaa737e68feffcec18fc8ccf94f6a (patch)
tree1977aa6cde428e0f34c160d1064af1f0118312cb
parent97301811997dbbd2208124d6cdf4f3587c4eb028 (diff)
kx: work around aws-lc-rs accepting more point formats
-rw-r--r--rustls/src/crypto/ring/kx.rs30
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())