VYPR
High severityNVD Advisory· Published Feb 10, 2026· Updated Feb 11, 2026

cryptography Subgroup Attack Due to Missing Subgroup Validation for SECT Curves

CVE-2026-26007

Description

cryptography is a package designed to expose cryptographic primitives and recipes to Python developers. Prior to 46.0.5, the public_key_from_numbers (or EllipticCurvePublicNumbers.public_key()), EllipticCurvePublicNumbers.public_key(), load_der_public_key() and load_pem_public_key() functions do not verify that the point belongs to the expected prime-order subgroup of the curve. This missing validation allows an attacker to provide a public key point P from a small-order subgroup. This can lead to security issues in various situations, such as the most commonly used signature verification (ECDSA) and shared key negotiation (ECDH). When the victim computes the shared secret as S = [victim_private_key]P via ECDH, this leaks information about victim_private_key mod (small_subgroup_order). For curves with cofactor > 1, this reveals the least significant bits of the private key. When these weak public keys are used in ECDSA , it's easy to forge signatures on the small subgroup. Only SECT curves are impacted by this. This vulnerability is fixed in 46.0.5.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

PyCA cryptography before 46.0.5 fails to validate that EC public keys belong to the prime-order subgroup, enabling private key leakage and signature forgery on SECT curves.

Vulnerability

Overview

CVE-2026-26007 affects the PyCA cryptography before 46.0.5 does not verify that elliptic curve public key points belong to the expected prime-order subgroup of the curve. This missing validation allows an attacker to supply a point from a small-order subgroup, which can be exploited in ECDH and ECDSA operations. The issue only affects SECT binary curves (cofactor > 1) [1][2].

Attack

Vector and Prerequisites

An attacker can craft a malicious public key point P from a small-order subgroup and present it to a victim application. No authentication is required beyond the ability to inject a public key into the victim's cryptographic operation. The attack is feasible when the victim uses ECDH key agreement or ECDSA signature verification with the vulnerable functions: the victim's private key is used to compute a shared secret S = [victim_private_key]P, which leaks information about the private key modulo the small subgroup order. For curves with cofactor > 1, this reveals the least significant bits of the private key [2]. In ECDSA, weak public keys on small subgroups allow signature forgery [2].

Impact

Successful exploitation can lead to partial private key disclosure (least significant bits) via ECDH, or signature forgery via ECDSA. This undermines the security guarantees of cryptographic operations relying on these functions. The impact is limited to applications using SECT binary curves, which are uncommon in practice [1][2].

Mitigation

The vulnerability is fixed in cryptography version 46.0.5, released on 2026-02-10 [1][3]. Users should upgrade immediately. Additionally, the maintainers have deprecated all SECT curves and plan to remove them in the next release [1]. No workaround is available other than upgrading or avoiding SECT curves.

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
cryptographyPyPI
< 46.0.546.0.5

Affected products

2

Patches

1
0eebb9dbb634

EC check key on cofactor > 1 (#14287)

https://github.com/pyca/cryptographyPaul KehrerFeb 10, 2026via ghsa
5 files changed · +104 13
  • CHANGELOG.rst+15 0 modified
    @@ -1,6 +1,21 @@
     Changelog
     =========
     
    +.. _v46-0-5:
    +
    +46.0.5 - 2026-02-10
    +~~~~~~~~~~~~~~~~~~~
    +
    +* 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**
    +* Support for ``SECT*`` binary elliptic curves is deprecated and will be
    +  removed in the next release.
    +
     .. v46-0-4:
     
     46.0.4 - 2026-01-27
    
  • src/cryptography/hazmat/primitives/asymmetric/ec.py+23 0 modified
    @@ -445,3 +445,26 @@ def get_curve_for_oid(oid: ObjectIdentifier) -> type[EllipticCurve]:
                 "The provided object identifier has no matching elliptic "
                 "curve class"
             )
    +
    +
    +_SECT_CURVES: tuple[type[EllipticCurve], ...] = (
    +    SECT163K1,
    +    SECT163R2,
    +    SECT233K1,
    +    SECT233R1,
    +    SECT283K1,
    +    SECT283R1,
    +    SECT409K1,
    +    SECT409R1,
    +    SECT571K1,
    +    SECT571R1,
    +)
    +
    +for _curve_cls in _SECT_CURVES:
    +    utils.deprecated(
    +        _curve_cls,
    +        __name__,
    +        f"{_curve_cls.__name__} will be removed in the next release.",
    +        utils.DeprecatedIn46,
    +        name=_curve_cls.__name__,
    +    )
    
  • src/cryptography/utils.py+1 0 modified
    @@ -26,6 +26,7 @@ class CryptographyDeprecationWarning(UserWarning):
     DeprecatedIn41 = CryptographyDeprecationWarning
     DeprecatedIn42 = CryptographyDeprecationWarning
     DeprecatedIn43 = CryptographyDeprecationWarning
    +DeprecatedIn46 = CryptographyDeprecationWarning
     
     
     # If you're wondering why we don't use `Buffer`, it's because `Buffer` would
    
  • src/rust/src/backend/ec.rs+28 13 modified
    @@ -135,12 +135,10 @@ 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::pyfunction]
     #[pyo3(signature = (curve, backend=None))]
     fn generate_private_key(
    @@ -198,10 +196,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::pymethods]
    @@ -367,6 +362,29 @@ 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::pymethods]
     impl ECPublicKey {
         #[getter]
    @@ -606,10 +624,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__(
    
  • tests/hazmat/primitives/test_ec.py+37 0 modified
    @@ -1542,3 +1542,40 @@ def test_exchange_non_matching_curve(self, backend):
     
             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)
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.