Skip to content

Commit

Permalink
remove dependency of pyca/cryptography backend on python-ecdsa
Browse files Browse the repository at this point in the history
  • Loading branch information
mattsb42-aws committed Dec 25, 2018
1 parent cea6ae8 commit e786a0e
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 7 deletions.
32 changes: 26 additions & 6 deletions jose/backends/cryptography_backend.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from __future__ import division

import math

import six

try:
from ecdsa import SigningKey as EcdsaSigningKey, VerifyingKey as EcdsaVerifyingKey
except ImportError:
SigningKey = VerifyingKey = None
from ecdsa.util import sigdecode_string, sigencode_string, sigdecode_der, sigencode_der
EcdsaSigningKey = EcdsaVerifyingKey = None

from jose.backends.base import Key
from jose.utils import base64_to_long, long_to_base64
Expand All @@ -15,7 +18,9 @@
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec, rsa, padding
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature
from cryptography.hazmat.primitives.serialization import load_pem_private_key, load_pem_public_key
from cryptography.utils import int_from_bytes, int_to_bytes
from cryptography.x509 import load_pem_x509_certificate


Expand Down Expand Up @@ -94,15 +99,30 @@ def _process_jwk(self, jwk_dict):
else:
return public.public_key(self.cryptography_backend())

def _sig_component_length(self):
"""Determine the correct serialization length for an encoded signature component.
This is the number of bytes required to encode the maximum key value.
"""
return math.ceil(self.prepared_key.key_size / 8.0)

def _der_to_asn1(self, der_signature):
"""Convert signature from DER encoding to ASN1 encoding."""
order = (2 ** self.prepared_key.curve.key_size) - 1
return sigencode_string(*sigdecode_der(der_signature, order), order=order)
r, s = decode_dss_signature(der_signature)
component_length = self._sig_component_length()
return int_to_bytes(r, component_length) + int_to_bytes(s, component_length)

def _asn1_to_der(self, asn1_signature):
"""Convert signature from ASN1 encoding to DER encoding."""
order = (2 ** self.prepared_key.curve.key_size) - 1
return sigencode_der(*sigdecode_string(asn1_signature, order), order=order)
component_length = self._sig_component_length()
if len(asn1_signature) != int(2 * component_length):
raise ValueError("Invalid signature")

r_bytes = asn1_signature[:component_length]
s_bytes = asn1_signature[component_length:]
r = int_from_bytes(r_bytes, "big")
s = int_from_bytes(s_bytes, "big")
return encode_dss_signature(r, s)

def sign(self, msg):
if self.hash_alg.digest_size * 8 > self.prepared_key.curve.key_size:
Expand Down
25 changes: 24 additions & 1 deletion tests/algorithms/test_EC.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@

try:
from jose.backends.cryptography_backend import CryptographyECKey
from cryptography.hazmat.primitives.asymmetric import ec as CryptographyEc
from cryptography.hazmat.backends import default_backend as CryptographyBackend
except ImportError:
CryptographyECKey = None
CryptographyECKey = CryptographyEc = CryptographyBackend = None

import pytest

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


@pytest.mark.cryptography
@pytest.mark.skipif(CryptographyECKey is None, reason="pyca/cryptography backend not available")
@pytest.mark.parametrize("algorithm, expected_length", (
(ALGORITHMS.ES256, 32),
(ALGORITHMS.ES384, 48),
(ALGORITHMS.ES512, 66)
))
def test_cryptography_sig_component_length(algorithm, expected_length):
# Put mapping inside here to avoid more complex handling for test runs that do not have pyca/cryptography
mapping = {
ALGORITHMS.ES256: CryptographyEc.SECP256R1,
ALGORITHMS.ES384: CryptographyEc.SECP384R1,
ALGORITHMS.ES512: CryptographyEc.SECP521R1,
}
key = CryptographyECKey(
CryptographyEc.generate_private_key(mapping[algorithm](), backend=CryptographyBackend()),
algorithm
)
assert key._sig_component_length() == expected_length


@pytest.mark.cryptography
@pytest.mark.skipif(CryptographyECKey is None, reason="pyca/cryptography backend not available")
def test_cryptograhy_der_to_asn1():
Expand Down

0 comments on commit e786a0e

Please sign in to comment.