From 42c914929b52eb16421a4ef1f7e09c8f9fdab7db Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 18 Mar 2026 16:01:03 +0900 Subject: [PATCH] EC check key on cofactor > 1 An attacker could create a malicious public key that reveals portions of your private key when using certain uncommon elliptic curves (binary curves). This version now includes additional security checks to prevent this attack. This issue only affects binary elliptic curves, which are rarely used in real-world applications. Credit to **XlabAI Team of Tencent Xuanwu Lab and Atuin Automated Vulnerability Discovery Engine** for reporting the issue. **CVE-2026-26007** This is a partial backport of upstream commit 0eebb9dbb6343d9bc1d91e5a2482ed4e054a6d8c, to only include what's relevant for CVE-2026-26007. CVE: CVE-2026-26007 Origin: backport, https://github.com/pyca/cryptography/commit/0eebb9dbb6343d9bc1d91e5a2482ed4e054a6d8c Reference: https://salsa.debian.org/python-team/packages/python-cryptography/-/commit/464e7ca3b0b4493d5906d0c3685de71fda770c59 Signed-off-by: Nguyen Dat Tho Signed-off-by: Paul Kehrer Co-authored-by: Alex Gaynor Upstream-Status: Backport [Backport from https://github.com/pyca/cryptography/commit/0eebb9dbb6343d9bc1d91e5a2482ed4e054a6d8c] --- src/rust/src/backend/ec.rs | 39 ++++++++++++++++++++---------- tests/hazmat/primitives/test_ec.py | 37 ++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/rust/src/backend/ec.rs b/src/rust/src/backend/ec.rs index 6a224b49f..27fced086 100644 --- a/src/rust/src/backend/ec.rs +++ b/src/rust/src/backend/ec.rs @@ -155,12 +155,9 @@ pub(crate) fn public_key_from_pkey( ) -> CryptographyResult { let ec = pkey.ec_key()?; let curve = py_curve_from_curve(py, ec.group())?; - check_key_infinity(&ec)?; - Ok(ECPublicKey { - pkey: pkey.to_owned(), - curve: curve.into(), - }) + ECPublicKey::new(pkey.to_owned(), curve.into()) } + #[pyo3::prelude::pyfunction] fn generate_private_key( py: pyo3::Python<'_>, @@ -215,10 +212,7 @@ fn from_public_bytes( let ec = openssl::ec::EcKey::from_public_key(&curve, &point)?; let pkey = openssl::pkey::PKey::from_ec_key(ec)?; - Ok(ECPublicKey { - pkey, - curve: py_curve.into(), - }) + ECPublicKey::new(pkey, py_curve.into()) } #[pyo3::prelude::pymethods] @@ -357,6 +351,28 @@ impl ECPrivateKey { } } +impl ECPublicKey { + fn new( + pkey: openssl::pkey::PKey, + curve: pyo3::Py, + ) -> CryptographyResult { + let ec = pkey.ec_key()?; + check_key_infinity(&ec)?; + let mut bn_ctx = openssl::bn::BigNumContext::new()?; + let mut cofactor = openssl::bn::BigNum::new()?; + ec.group().cofactor(&mut cofactor, &mut bn_ctx)?; + let one = openssl::bn::BigNum::from_u32(1)?; + if cofactor != one { + ec.check_key().map_err(|_| { + pyo3::exceptions::PyValueError::new_err( + "Invalid EC key (key out of range, infinity, etc.)", + ) + })?; + } + + Ok(ECPublicKey { pkey, curve }) + } +} #[pyo3::prelude::pymethods] impl ECPublicKey { #[getter] @@ -591,10 +607,7 @@ impl EllipticCurvePublicNumbers { let pkey = openssl::pkey::PKey::from_ec_key(public_key)?; - Ok(ECPublicKey { - pkey, - curve: self.curve.clone_ref(py), - }) + ECPublicKey::new(pkey, self.curve.clone_ref(py)) } fn __eq__( diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py index 334e76dcc..f7f2242f6 100644 --- a/tests/hazmat/primitives/test_ec.py +++ b/tests/hazmat/primitives/test_ec.py @@ -1340,3 +1340,40 @@ class TestECDH: with pytest.raises(ValueError): key.exchange(ec.ECDH(), public_key) + + +def test_invalid_sect_public_keys(backend): + _skip_curve_unsupported(backend, ec.SECT571K1()) + public_numbers = ec.EllipticCurvePublicNumbers(1, 1, ec.SECT571K1()) + with pytest.raises(ValueError): + public_numbers.public_key() + + point = binascii.unhexlify( + b"0400000000000000000000000000000000000000000000000000000000000000000" + b"0000000000000000000000000000000000000000000000000000000000000000000" + b"0000000000010000000000000000000000000000000000000000000000000000000" + b"0000000000000000000000000000000000000000000000000000000000000000000" + b"0000000000000000000001" + ) + with pytest.raises(ValueError): + ec.EllipticCurvePublicKey.from_encoded_point(ec.SECT571K1(), point) + + der = binascii.unhexlify( + b"3081a7301006072a8648ce3d020106052b810400260381920004000000000000000" + b"0000000000000000000000000000000000000000000000000000000000000000000" + b"0000000000000000000000000000000000000000000000000000000000000100000" + b"0000000000000000000000000000000000000000000000000000000000000000000" + b"0000000000000000000000000000000000000000000000000000000000000000000" + b"00001" + ) + with pytest.raises(ValueError): + serialization.load_der_public_key(der) + + pem = textwrap.dedent("""-----BEGIN PUBLIC KEY----- + MIGnMBAGByqGSM49AgEGBSuBBAAmA4GSAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE= + -----END PUBLIC KEY-----""").encode() + with pytest.raises(ValueError): + serialization.load_pem_public_key(pem)