Skip to content

Commit e786a0e

Browse files
committed
remove dependency of pyca/cryptography backend on python-ecdsa
1 parent cea6ae8 commit e786a0e

File tree

2 files changed

+50
-7
lines changed

2 files changed

+50
-7
lines changed

jose/backends/cryptography_backend.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
from __future__ import division
2+
3+
import math
4+
15
import six
26

37
try:
48
from ecdsa import SigningKey as EcdsaSigningKey, VerifyingKey as EcdsaVerifyingKey
59
except ImportError:
6-
SigningKey = VerifyingKey = None
7-
from ecdsa.util import sigdecode_string, sigencode_string, sigdecode_der, sigencode_der
10+
EcdsaSigningKey = EcdsaVerifyingKey = None
811

912
from jose.backends.base import Key
1013
from jose.utils import base64_to_long, long_to_base64
@@ -15,7 +18,9 @@
1518
from cryptography.hazmat.backends import default_backend
1619
from cryptography.hazmat.primitives import hashes, serialization
1720
from cryptography.hazmat.primitives.asymmetric import ec, rsa, padding
21+
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature
1822
from cryptography.hazmat.primitives.serialization import load_pem_private_key, load_pem_public_key
23+
from cryptography.utils import int_from_bytes, int_to_bytes
1924
from cryptography.x509 import load_pem_x509_certificate
2025

2126

@@ -94,15 +99,30 @@ def _process_jwk(self, jwk_dict):
9499
else:
95100
return public.public_key(self.cryptography_backend())
96101

102+
def _sig_component_length(self):
103+
"""Determine the correct serialization length for an encoded signature component.
104+
105+
This is the number of bytes required to encode the maximum key value.
106+
"""
107+
return math.ceil(self.prepared_key.key_size / 8.0)
108+
97109
def _der_to_asn1(self, der_signature):
98110
"""Convert signature from DER encoding to ASN1 encoding."""
99-
order = (2 ** self.prepared_key.curve.key_size) - 1
100-
return sigencode_string(*sigdecode_der(der_signature, order), order=order)
111+
r, s = decode_dss_signature(der_signature)
112+
component_length = self._sig_component_length()
113+
return int_to_bytes(r, component_length) + int_to_bytes(s, component_length)
101114

102115
def _asn1_to_der(self, asn1_signature):
103116
"""Convert signature from ASN1 encoding to DER encoding."""
104-
order = (2 ** self.prepared_key.curve.key_size) - 1
105-
return sigencode_der(*sigdecode_string(asn1_signature, order), order=order)
117+
component_length = self._sig_component_length()
118+
if len(asn1_signature) != int(2 * component_length):
119+
raise ValueError("Invalid signature")
120+
121+
r_bytes = asn1_signature[:component_length]
122+
s_bytes = asn1_signature[component_length:]
123+
r = int_from_bytes(r_bytes, "big")
124+
s = int_from_bytes(s_bytes, "big")
125+
return encode_dss_signature(r, s)
106126

107127
def sign(self, msg):
108128
if self.hash_alg.digest_size * 8 > self.prepared_key.curve.key_size:

tests/algorithms/test_EC.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111

1212
try:
1313
from jose.backends.cryptography_backend import CryptographyECKey
14+
from cryptography.hazmat.primitives.asymmetric import ec as CryptographyEc
15+
from cryptography.hazmat.backends import default_backend as CryptographyBackend
1416
except ImportError:
15-
CryptographyECKey = None
17+
CryptographyECKey = CryptographyEc = CryptographyBackend = None
1618

1719
import pytest
1820

@@ -63,6 +65,27 @@ def test_key_from_ecdsa():
6365
assert not ECKey(key, ALGORITHMS.ES256).is_public()
6466

6567

68+
@pytest.mark.cryptography
69+
@pytest.mark.skipif(CryptographyECKey is None, reason="pyca/cryptography backend not available")
70+
@pytest.mark.parametrize("algorithm, expected_length", (
71+
(ALGORITHMS.ES256, 32),
72+
(ALGORITHMS.ES384, 48),
73+
(ALGORITHMS.ES512, 66)
74+
))
75+
def test_cryptography_sig_component_length(algorithm, expected_length):
76+
# Put mapping inside here to avoid more complex handling for test runs that do not have pyca/cryptography
77+
mapping = {
78+
ALGORITHMS.ES256: CryptographyEc.SECP256R1,
79+
ALGORITHMS.ES384: CryptographyEc.SECP384R1,
80+
ALGORITHMS.ES512: CryptographyEc.SECP521R1,
81+
}
82+
key = CryptographyECKey(
83+
CryptographyEc.generate_private_key(mapping[algorithm](), backend=CryptographyBackend()),
84+
algorithm
85+
)
86+
assert key._sig_component_length() == expected_length
87+
88+
6689
@pytest.mark.cryptography
6790
@pytest.mark.skipif(CryptographyECKey is None, reason="pyca/cryptography backend not available")
6891
def test_cryptograhy_der_to_asn1():

0 commit comments

Comments
 (0)