From 689ec8d9035a4aea9f174e1f12063f309e8cfc2d Mon Sep 17 00:00:00 2001 From: ekalaga Date: Fri, 13 Mar 2026 12:33:58 +0000 Subject: [PATCH] fix: replace ecdsa with cryptography to mitigate Minerva timing attack The python-ecdsa library is vulnerable to the Minerva timing side-channel attack, which can leak nonce information during ECDSA signing and potentially lead to private key recovery. The upstream maintainers have no plans to fix it. - Make `cryptography` a required dependency (was optional) - Remove `ecdsa` from install_requires and requirements.txt - Remove ecdsa fallback in backends/__init__.py and utils.py - Fix pre-existing test bug: SECP256R1 must be instantiated (SECP256R1()) --- jose/backends/__init__.py | 5 +---- jose/utils.py | 24 +++++------------------- requirements.txt | 2 +- setup.cfg | 4 +--- tests/algorithms/test_EC.py | 2 +- 5 files changed, 9 insertions(+), 28 deletions(-) diff --git a/jose/backends/__init__.py b/jose/backends/__init__.py index 99189691..15a8f545 100644 --- a/jose/backends/__init__.py +++ b/jose/backends/__init__.py @@ -8,10 +8,7 @@ except ImportError: RSAKey = None -try: - from jose.backends.cryptography_backend import CryptographyECKey as ECKey # noqa: F401 -except ImportError: - from jose.backends.ecdsa_backend import ECDSAECKey as ECKey # noqa: F401 +from jose.backends.cryptography_backend import CryptographyECKey as ECKey # noqa: F401 try: from jose.backends.cryptography_backend import CryptographyAESKey as AESKey # noqa: F401 diff --git a/jose/utils.py b/jose/utils.py index d62cafb0..9e336f0f 100644 --- a/jose/utils.py +++ b/jose/utils.py @@ -2,25 +2,11 @@ import re import struct -# Piggyback of the backends implementation of the function that converts a long -# to a bytes stream. Some plumbing is necessary to have the signatures match. -try: - from cryptography.utils import int_to_bytes as _long_to_bytes - - def long_to_bytes(n, blocksize=0): - return _long_to_bytes(n, blocksize or None) - -except ImportError: - from ecdsa.ecdsa import int_to_string as _long_to_bytes - - def long_to_bytes(n, blocksize=0): - ret = _long_to_bytes(n) - if blocksize == 0: - return ret - else: - assert len(ret) <= blocksize - padding = blocksize - len(ret) - return b"\x00" * padding + ret +from cryptography.utils import int_to_bytes as _long_to_bytes + + +def long_to_bytes(n, blocksize=0): + return _long_to_bytes(n, blocksize or None) def long_to_base64(data, size=0): diff --git a/requirements.txt b/requirements.txt index 7bc375f5..9d16935d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ pycryptodome rsa -ecdsa != 0.15 +cryptography >=3.4.0 pyasn1 diff --git a/setup.cfg b/setup.cfg index e4e3d192..fd7addad 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,7 +34,7 @@ classifiers = packages = find: python_requires = >=3.9 install_requires = - ecdsa != 0.15 + cryptography >=3.4.0 rsa >=4.0, <5.0, !=4.4, !=4.1.1 pyasn1 >=0.5.0 @@ -42,8 +42,6 @@ install_requires = test = pytest pytest-cov -cryptography = - cryptography >=3.4.0 pycrypto = pycrypto >=2.6.0, <2.7.0 pycryptodome = diff --git a/tests/algorithms/test_EC.py b/tests/algorithms/test_EC.py index d8602a2b..100006b2 100644 --- a/tests/algorithms/test_EC.py +++ b/tests/algorithms/test_EC.py @@ -235,7 +235,7 @@ def test_incorrect_public_key_hmac_signing(): def b64(x): return base64.urlsafe_b64encode(x).replace(b"=", b"") - KEY = CryptographyEc.generate_private_key(CryptographyEc.SECP256R1) + KEY = CryptographyEc.generate_private_key(CryptographyEc.SECP256R1()) PUBKEY = KEY.public_key().public_bytes( encoding=serialization.Encoding.OpenSSH, format=serialization.PublicFormat.OpenSSH,