Skip to content

Commit b4d6ca2

Browse files
authored
Merge pull request #109 from mattsb42-aws/explicit-dependencies
Cryptographic backend isolation tests
2 parents b3c04ff + 4d7b598 commit b4d6ca2

10 files changed

+322
-192
lines changed

.gitignore

+9
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,12 @@ docs/_build/
5555

5656
# PyBuilder
5757
target/
58+
59+
# PyCharm
60+
.idea/
61+
62+
# PyEnv
63+
.python-version
64+
65+
# PyTest
66+
.pytest_cache/

.travis.yml

+58-9
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,70 @@
22
# detail: https://blog.travis-ci.com/2017-06-21-trusty-updates-2017-Q2-launch
33
dist: precise
44
language: python
5-
python:
6-
- "2.7"
7-
- "3.4"
8-
- "3.5"
9-
- "3.6"
10-
- "pypy-5.3.1"
115
install:
126
- pip install -U setuptools && pip install -U tox codecov tox-travis
137
script:
148
- tox
159
after_success:
1610
- codecov
11+
matrix:
12+
include:
13+
# CPython 2.7
14+
- python: 2.7
15+
env: TOXENV=py27-base
16+
- python: 2.7
17+
env: TOXENV=py27-cryptography
18+
- python: 2.7
19+
env: TOXENV=py27-pycryptodome
20+
- python: 2.7
21+
env: TOXENV=py27-pycrypto
22+
- python: 2.7
23+
env: TOXENV=py27-compatibility
24+
# CPython 3.4
25+
- python: 3.4
26+
env: TOXENV=py34-base
27+
- python: 3.4
28+
env: TOXENV=py34-cryptography
29+
- python: 3.4
30+
env: TOXENV=py34-pycryptodome
31+
- python: 3.4
32+
env: TOXENV=py34-pycrypto
33+
- python: 3.4
34+
env: TOXENV=py34-compatibility
35+
# CPython 3.5
36+
- python: 3.5
37+
env: TOXENV=py35-base
38+
- python: 3.5
39+
env: TOXENV=py35-cryptography
40+
- python: 3.5
41+
env: TOXENV=py35-pycryptodome
42+
- python: 3.5
43+
env: TOXENV=py35-pycrypto
44+
- python: 3.5
45+
env: TOXENV=py35-compatibility
46+
# CPython 3.5
47+
- python: 3.5
48+
env: TOXENV=py35-base
49+
- python: 3.5
50+
env: TOXENV=py35-cryptography
51+
- python: 3.5
52+
env: TOXENV=py35-pycryptodome
53+
- python: 3.5
54+
env: TOXENV=py35-pycrypto
55+
- python: 3.5
56+
env: TOXENV=py35-compatibility
57+
# PyPy 5.3.1
58+
- python: pypy-5.3.1
59+
env: TOXENV=pypy-base
60+
- python: pypy-5.3.1
61+
env: TOXENV=pypy-cryptography
62+
- python: pypy-5.3.1
63+
env: TOXENV=pypy-pycryptodome
64+
- python: pypy-5.3.1
65+
env: TOXENV=pypy-pycrypto
66+
- python: pypy-5.3.1
67+
env: TOXENV=pypy-compatibility
1768
# matrix:
1869
# include:
1970
# - python: 3.6
20-
# env:
21-
# - TOX_ENV=flake8
22-
# script: tox -e $TOX_ENV
71+
# env: TOX_ENV=flake8

jose/backends/cryptography_backend.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def __init__(self, key, algorithm, cryptography_backend=default_backend):
6767

6868
def _process_jwk(self, jwk_dict):
6969
if not jwk_dict.get('kty') == 'EC':
70-
raise JWKError("Incorrect key type. Expected: 'EC', Recieved: %s" % jwk_dict.get('kty'))
70+
raise JWKError("Incorrect key type. Expected: 'EC', Received: %s" % jwk_dict.get('kty'))
7171

7272
if not all(k in jwk_dict for k in ['x', 'y', 'crv']):
7373
raise JWKError('Mandatory parameters are missing')
@@ -212,7 +212,7 @@ def __init__(self, key, algorithm, cryptography_backend=default_backend):
212212

213213
def _process_jwk(self, jwk_dict):
214214
if not jwk_dict.get('kty') == 'RSA':
215-
raise JWKError("Incorrect key type. Expected: 'RSA', Recieved: %s" % jwk_dict.get('kty'))
215+
raise JWKError("Incorrect key type. Expected: 'RSA', Received: %s" % jwk_dict.get('kty'))
216216

217217
e = base64_to_long(jwk_dict.get('e', 256))
218218
n = base64_to_long(jwk_dict.get('n'))

setup.py

-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ def get_packages(package):
6363
'pytest',
6464
'pytest-cov',
6565
'pytest-runner',
66-
'cryptography',
6766
],
6867
install_requires=['six <2.0', 'ecdsa <1.0', 'rsa', 'future <1.0']
6968
)

tests/algorithms/test_EC.py

+64-64
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@
22
from jose.constants import ALGORITHMS
33
from jose.exceptions import JOSEError, JWKError
44

5-
from jose.backends.ecdsa_backend import ECDSAECKey
6-
from jose.backends.cryptography_backend import CryptographyECKey
5+
from jose.backends import ECKey
6+
try:
7+
from jose.backends.ecdsa_backend import ECDSAECKey
8+
import ecdsa
9+
except ImportError:
10+
ECDSAECKey = ecdsa = None
11+
12+
try:
13+
from jose.backends.cryptography_backend import CryptographyECKey
14+
except ImportError:
15+
CryptographyECKey = None
716

8-
import ecdsa
917
import pytest
1018

1119
private_key = """-----BEGIN EC PRIVATE KEY-----
@@ -14,69 +22,78 @@
1422
WkG0HJWIORlPbvXME+DRh6G/yVOKnTm88Q==
1523
-----END EC PRIVATE KEY-----"""
1624

25+
# Private key generated using NIST256p curve
26+
TOO_SHORT_PRIVATE_KEY = b"""\
27+
-----BEGIN EC PRIVATE KEY-----
28+
MHcCAQEEIMlUyYGOpjV4bbW0C9FKS2zkspD0L/5vJLnr6sJoLdc+oAoGCCqGSM49
29+
AwEHoUQDQgAE6TDUNj5QXl+RKdZvBV+cg7Td6cJRB+Ta8XAhIuCAzonq0Ix//1+C
30+
pNSsy11sIKmMl61YJzxvZ6WkNluBmkDPCQ==
31+
-----END EC PRIVATE KEY-----
32+
"""
33+
34+
35+
def _backend_exception_types():
36+
"""Build the backend exception types based on available backends."""
37+
if None not in (ECDSAECKey, ecdsa):
38+
yield ECDSAECKey, ecdsa.BadDigestError
39+
40+
if CryptographyECKey is not None:
41+
yield CryptographyECKey, TypeError
1742

18-
class TestECAlgorithm:
1943

20-
@pytest.mark.parametrize("Backend", [ECDSAECKey, CryptographyECKey])
21-
def test_key_from_pem(self, Backend):
22-
assert not Backend(private_key, ALGORITHMS.ES256).is_public()
44+
@pytest.mark.ecdsa
45+
@pytest.mark.skipif(
46+
None in (ECDSAECKey, ecdsa),
47+
reason="python-ecdsa backend not available"
48+
)
49+
def test_key_from_ecdsa():
50+
key = ecdsa.SigningKey.from_pem(private_key)
51+
assert not ECKey(key, ALGORITHMS.ES256).is_public()
52+
53+
54+
class TestECAlgorithm:
2355

24-
@pytest.mark.parametrize("Backend", [ECDSAECKey, CryptographyECKey])
25-
def test_key_from_ecdsa(self, Backend):
26-
key = ecdsa.SigningKey.from_pem(private_key)
27-
assert not Backend(key, ALGORITHMS.ES256).is_public()
56+
def test_key_from_pem(self):
57+
assert not ECKey(private_key, ALGORITHMS.ES256).is_public()
2858

29-
@pytest.mark.parametrize("Backend", [ECDSAECKey, CryptographyECKey])
30-
def test_to_pem(self, Backend):
31-
key = Backend(private_key, ALGORITHMS.ES256)
59+
def test_to_pem(self):
60+
key = ECKey(private_key, ALGORITHMS.ES256)
3261
assert not key.is_public()
3362
assert key.to_pem().strip() == private_key.strip().encode('utf-8')
3463

3564
public_pem = key.public_key().to_pem()
36-
assert Backend(public_pem, ALGORITHMS.ES256).is_public()
37-
38-
@pytest.mark.parametrize(
39-
"Backend,ExceptionType",
40-
[
41-
(ECDSAECKey, ecdsa.BadDigestError),
42-
(CryptographyECKey, TypeError)
43-
]
44-
)
65+
assert ECKey(public_pem, ALGORITHMS.ES256).is_public()
66+
67+
@pytest.mark.parametrize("Backend,ExceptionType", _backend_exception_types())
4568
def test_key_too_short(self, Backend, ExceptionType):
46-
priv_key = ecdsa.SigningKey.generate(curve=ecdsa.NIST256p).to_pem()
47-
key = Backend(priv_key, ALGORITHMS.ES512)
69+
key = Backend(TOO_SHORT_PRIVATE_KEY, ALGORITHMS.ES512)
4870
with pytest.raises(ExceptionType):
4971
key.sign(b'foo')
5072

51-
@pytest.mark.parametrize("Backend", [ECDSAECKey, CryptographyECKey])
52-
def test_get_public_key(self, Backend):
53-
key = Backend(private_key, ALGORITHMS.ES256)
73+
def test_get_public_key(self):
74+
key = ECKey(private_key, ALGORITHMS.ES256)
5475
pubkey = key.public_key()
5576
pubkey2 = pubkey.public_key()
5677
assert pubkey == pubkey2
5778

58-
@pytest.mark.parametrize("Backend", [ECDSAECKey, CryptographyECKey])
59-
def test_string_secret(self, Backend):
79+
def test_string_secret(self):
6080
key = 'secret'
6181
with pytest.raises(JOSEError):
62-
Backend(key, ALGORITHMS.ES256)
82+
ECKey(key, ALGORITHMS.ES256)
6383

64-
@pytest.mark.parametrize("Backend", [ECDSAECKey, CryptographyECKey])
65-
def test_object(self, Backend):
84+
def test_object(self):
6685
key = object()
6786
with pytest.raises(JOSEError):
68-
Backend(key, ALGORITHMS.ES256)
87+
ECKey(key, ALGORITHMS.ES256)
6988

70-
@pytest.mark.parametrize("Backend", [ECDSAECKey, CryptographyECKey])
71-
def test_invalid_algorithm(self, Backend):
89+
def test_invalid_algorithm(self):
7290
with pytest.raises(JWKError):
73-
Backend(private_key, 'nonexistent')
91+
ECKey(private_key, 'nonexistent')
7492

7593
with pytest.raises(JWKError):
76-
Backend({'kty': 'bla'}, ALGORITHMS.ES256)
94+
ECKey({'kty': 'bla'}, ALGORITHMS.ES256)
7795

78-
@pytest.mark.parametrize("Backend", [ECDSAECKey, CryptographyECKey])
79-
def test_EC_jwk(self, Backend):
96+
def test_EC_jwk(self):
8097
key = {
8198
"kty": "EC",
8299
@@ -87,22 +104,21 @@ def test_EC_jwk(self, Backend):
87104
"d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt",
88105
}
89106

90-
assert not Backend(key, ALGORITHMS.ES512).is_public()
107+
assert not ECKey(key, ALGORITHMS.ES512).is_public()
91108

92109
del key['d']
93110

94111
# We are now dealing with a public key.
95-
assert Backend(key, ALGORITHMS.ES512).is_public()
112+
assert ECKey(key, ALGORITHMS.ES512).is_public()
96113

97114
del key['x']
98115

99116
# This key is missing a required parameter.
100117
with pytest.raises(JWKError):
101-
Backend(key, ALGORITHMS.ES512)
118+
ECKey(key, ALGORITHMS.ES512)
102119

103-
@pytest.mark.parametrize("Backend", [ECDSAECKey])
104-
def test_verify(self, Backend):
105-
key = Backend(private_key, ALGORITHMS.ES256)
120+
def test_verify(self):
121+
key = ECKey(private_key, ALGORITHMS.ES256)
106122
msg = b'test'
107123
signature = key.sign(msg)
108124
public_key = key.public_key()
@@ -129,23 +145,7 @@ def assert_parameters(self, as_dict, private):
129145
# Private parameters should be absent
130146
assert 'd' not in as_dict
131147

132-
@pytest.mark.parametrize("Backend", [ECDSAECKey, CryptographyECKey])
133-
def test_to_dict(self, Backend):
134-
key = Backend(private_key, ALGORITHMS.ES256)
148+
def test_to_dict(self):
149+
key = ECKey(private_key, ALGORITHMS.ES256)
135150
self.assert_parameters(key.to_dict(), private=True)
136151
self.assert_parameters(key.public_key().to_dict(), private=False)
137-
138-
@pytest.mark.parametrize("BackendSign", [ECDSAECKey, CryptographyECKey])
139-
@pytest.mark.parametrize("BackendVerify", [ECDSAECKey, CryptographyECKey])
140-
def test_signing_parity(self, BackendSign, BackendVerify):
141-
key_sign = BackendSign(private_key, ALGORITHMS.ES256)
142-
key_verify = BackendVerify(private_key, ALGORITHMS.ES256).public_key()
143-
144-
msg = b'test'
145-
sig = key_sign.sign(msg)
146-
147-
# valid signature
148-
assert key_verify.verify(msg, sig)
149-
150-
# invalid signature
151-
assert not key_verify.verify(msg, b'n' * 64)

tests/algorithms/test_EC_compat.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import pytest
2+
3+
try:
4+
from jose.backends.ecdsa_backend import ECDSAECKey
5+
from jose.backends.cryptography_backend import CryptographyECKey
6+
except ImportError:
7+
ECDSAECKey = CryptographyECKey = None
8+
from jose.constants import ALGORITHMS
9+
10+
from .test_EC import private_key
11+
12+
13+
@pytest.mark.backend_compatibility
14+
@pytest.mark.skipif(
15+
None in (ECDSAECKey, CryptographyECKey),
16+
reason="Multiple crypto backends not available for backend compatibility tests"
17+
)
18+
class TestBackendRsaCompatibility(object):
19+
20+
@pytest.mark.parametrize("BackendSign", [ECDSAECKey, CryptographyECKey])
21+
@pytest.mark.parametrize("BackendVerify", [ECDSAECKey, CryptographyECKey])
22+
def test_signing_parity(self, BackendSign, BackendVerify):
23+
key_sign = BackendSign(private_key, ALGORITHMS.ES256)
24+
key_verify = BackendVerify(private_key, ALGORITHMS.ES256).public_key()
25+
26+
msg = b'test'
27+
sig = key_sign.sign(msg)
28+
29+
# valid signature
30+
assert key_verify.verify(msg, sig)
31+
32+
# invalid signature
33+
assert not key_verify.verify(msg, b'n' * 64)

0 commit comments

Comments
 (0)