From 42c914929b52eb16421a4ef1f7e09c8f9fdab7db Mon Sep 17 00:00:00 2001
From: Paul Kehrer <paul.l.kehrer@gmail.com>
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 <tho3.nguyen@lge.com>
Signed-off-by: Paul Kehrer <paul.l.kehrer@gmail.com>
Co-authored-by: Alex Gaynor <alex.gaynor@gmail.com>

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<ECPublicKey> {
     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<openssl::pkey::Public>,
+        curve: pyo3::Py<pyo3::PyAny>,
+    ) -> CryptographyResult<ECPublicKey> {
+        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)
