Skip to content

Commit 5eb382e

Browse files
committed
Merge branch 'master' into zejn/rsa-privkey-no-verify
2 parents 40920a5 + 1390392 commit 5eb382e

19 files changed

+111
-143
lines changed

.travis.yml

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ after_success:
1010
- codecov
1111
matrix:
1212
include:
13+
# Linting
14+
- python: 3.6
15+
env: TOX_ENV=flake8
1316
# CPython 2.7
1417
- python: 2.7
1518
env: TOXENV=py27-base
@@ -21,17 +24,6 @@ matrix:
2124
env: TOXENV=py27-pycrypto-norsa
2225
- python: 2.7
2326
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-only
29-
- python: 3.4
30-
env: TOXENV=py34-pycryptodome-norsa
31-
- python: 3.4
32-
env: TOXENV=py34-pycrypto-norsa
33-
- python: 3.4
34-
env: TOXENV=py34-compatibility
3527
# CPython 3.5
3628
- python: 3.5
3729
env: TOXENV=py35-base
@@ -77,17 +69,6 @@ matrix:
7769
env: TOXENV=py37-compatibility
7870
dist: xenial
7971
sudo: true
80-
# PyPy 5.3.1
81-
- python: pypy-5.3.1
82-
env: TOXENV=pypy-base
83-
- python: pypy-5.3.1
84-
env: TOXENV=pypy-cryptography-only
85-
- python: pypy-5.3.1
86-
env: TOXENV=pypy-pycryptodome-norsa
87-
- python: pypy-5.3.1
88-
env: TOXENV=pypy-pycrypto-norsa
89-
- python: pypy-5.3.1
90-
env: TOXENV=pypy-compatibility
9172
# PyPy 3.5 (5.10.1?)
9273
- python: pypy3.5
9374
env: TOXENV=pypy-base
@@ -99,6 +80,3 @@ matrix:
9980
env: TOXENV=pypy-pycrypto-norsa
10081
- python: pypy3.5
10182
env: TOXENV=pypy-compatibility
102-
# Linting
103-
- python: 3.6
104-
env: TOX_ENV=flake8

CHANGELOG.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Changelog #
2+
3+
## Development ##
4+
5+
* Fix `to_dict` output, which should always be JSON encodeable. #139 (fixes #127 and #137)
6+
7+
## 3.1.0 -- 2019-12-10 ##
8+
9+
This is a greatly overdue release.
10+
11+
### Features ###
12+
13+
* Improve `JWT.decode()` #76 (fixes #75)
14+
* Sort headers when serializing to allow for headless JWT #136 (fixes #80)
15+
* Adjust dependency handling
16+
- Use PyCryptodome instead of PyCrypto #83
17+
- Update package dependencies #124 (fixes #158)
18+
* Avoid using deprecated methods #85
19+
* Support X509 certificates #107
20+
* Isolate and flesh out cryptographic backends to enable independent operation #129 (fixes #114)
21+
- Remove pyca/cryptography backend's dependency on python-ecdsa #117
22+
- Remove pycrypto/dome backends' dependency on python-rsa #121
23+
- Make pyca/cryptography backend the preferred backend if multiple backends are present #122
24+
25+
### Bugfixes/Improvements ###
26+
27+
* Enable flake8 check in tox/TravisCI #77
28+
* Fix `crytography` dependency typo #94
29+
* Trigger tests using `python setup.py test` #97
30+
* Properly raise an error if a claim is expected and not given #98
31+
* Typo fixes #110
32+
* Fix invalid RSA private key PKCS8 encoding by python-rsa backend #120 (fixes #119)
33+
* Remove `future` dependency #134 (fixes #112)
34+
* Fix incorrect use of `pytest.raises(message=...)` #141
35+
* Typo fix #143
36+
* Clarify sign docstring to allow for `dict` payload #150
37+
38+
### Housekeeping ###
39+
40+
* Streamline the code a bit and update classifiers #87
41+
* Fix typo and rephrase `access_token` documentation #89
42+
* Code linting now mostly honors flake8 #101
43+
* Document using a `dict` for `jwt.encode` and `jwt.decode` #103
44+
* Include docs and tests in source distributions #111
45+
* Updating README descriptions of crypto backends #130
46+
* Document versioning policy #131
47+
* Add `CHANGELOG.rst` #132 (fixes #99)
48+
* Simplify and extend `.travis.yml` #135
49+
* Move `CHANGELOG.rst` to `CHANGELOG.md` and update it #159

CHANGELOG.rst

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +0,0 @@
1-
---------
2-
Changelog
3-
---------
4-
5-
3.1.0 -- 2019-??-??
6-
^^^^^^^^^^^^^^^^^^^
7-
8-
Major
9-
"""""
10-
11-
* Require claim options added.
12-
`#98 <https://github.com/mpdavis/python-jose/pull/98>`_
13-
* Isolate and flesh out cryptographic backends to enable independent operation.
14-
`#114 <https://github.com/mpdavis/python-jose/issues/114>`_
15-
`#129 <https://github.com/mpdavis/python-jose/pull/129>`_
16-
* Remove pyca/cryptography backend's dependency on python-ecdsa.
17-
`#117 <https://github.com/mpdavis/python-jose/pull/117>`_
18-
* Remove pycrypto/dome backends' dependency on python-rsa.
19-
`#121 <https://github.com/mpdavis/python-jose/pull/121>`_
20-
* Make pyca/cryptography backend the preferred backend if multiple backends are present.
21-
`#122 <https://github.com/mpdavis/python-jose/pull/122>`_
22-
* Allow for headless JWT by sorting headers when serializing.
23-
`#136 <https://github.com/mpdavis/python-jose/pull/136>`_
24-
25-
Bugfixes
26-
""""""""
27-
28-
* Fix invalid RSA private key PKCS8 encoding by python-rsa backend.
29-
`#120 <https://github.com/mpdavis/python-jose/pull/120>`_
30-
31-
Housekeeping
32-
""""""""""""
33-
34-
* Test each cryptographic backend independently in CI.
35-
`#114 <https://github.com/mpdavis/python-jose/issues/114>`_
36-
`#129 <https://github.com/mpdavis/python-jose/pull/129>`_
37-
`#135 <https://github.com/mpdavis/python-jose/pull/135>`_
38-
* Add flake8 checks in CI.
39-
* Add CPython 3.7 and PyPy 3.5 testing in CI.
40-
* Remove package future as a dependency, not needed anymore.
41-
* Fix warnings from py.test.

jose/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
__version__ = "3.0.1"
2+
__version__ = "3.1.0"
33
__author__ = 'Michael Davis'
44
__license__ = 'MIT'
55
__copyright__ = 'Copyright 2016 Michael Davis'

jose/backends/cryptography_backend.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def __init__(self, key, algorithm, cryptography_backend=default_backend):
7676

7777
def _process_jwk(self, jwk_dict):
7878
if not jwk_dict.get('kty') == 'EC':
79-
raise JWKError("Incorrect key type. Expected: 'EC', Received: %s" % jwk_dict.get('kty'))
79+
raise JWKError("Incorrect key type. Expected: 'EC', Received: %s" % jwk_dict.get('kty'))
8080

8181
if not all(k in jwk_dict for k in ['x', 'y', 'crv']):
8282
raise JWKError('Mandatory parameters are missing')
@@ -183,15 +183,15 @@ def to_dict(self):
183183
'alg': self._algorithm,
184184
'kty': 'EC',
185185
'crv': crv,
186-
'x': long_to_base64(public_key.public_numbers().x, size=key_size),
187-
'y': long_to_base64(public_key.public_numbers().y, size=key_size),
186+
'x': long_to_base64(public_key.public_numbers().x, size=key_size).decode('ASCII'),
187+
'y': long_to_base64(public_key.public_numbers().y, size=key_size).decode('ASCII'),
188188
}
189189

190190
if not self.is_public():
191191
data['d'] = long_to_base64(
192192
self.prepared_key.private_numbers().private_value,
193193
size=key_size
194-
)
194+
).decode('ASCII')
195195

196196
return data
197197

@@ -244,7 +244,7 @@ def __init__(self, key, algorithm, cryptography_backend=default_backend):
244244

245245
def _process_jwk(self, jwk_dict):
246246
if not jwk_dict.get('kty') == 'RSA':
247-
raise JWKError("Incorrect key type. Expected: 'RSA', Received: %s" % jwk_dict.get('kty'))
247+
raise JWKError("Incorrect key type. Expected: 'RSA', Received: %s" % jwk_dict.get('kty'))
248248

249249
e = base64_to_long(jwk_dict.get('e', 256))
250250
n = base64_to_long(jwk_dict.get('n'))
@@ -354,18 +354,18 @@ def to_dict(self):
354354
data = {
355355
'alg': self._algorithm,
356356
'kty': 'RSA',
357-
'n': long_to_base64(public_key.public_numbers().n),
358-
'e': long_to_base64(public_key.public_numbers().e),
357+
'n': long_to_base64(public_key.public_numbers().n).decode('ASCII'),
358+
'e': long_to_base64(public_key.public_numbers().e).decode('ASCII'),
359359
}
360360

361361
if not self.is_public():
362362
data.update({
363-
'd': long_to_base64(self.prepared_key.private_numbers().d),
364-
'p': long_to_base64(self.prepared_key.private_numbers().p),
365-
'q': long_to_base64(self.prepared_key.private_numbers().q),
366-
'dp': long_to_base64(self.prepared_key.private_numbers().dmp1),
367-
'dq': long_to_base64(self.prepared_key.private_numbers().dmq1),
368-
'qi': long_to_base64(self.prepared_key.private_numbers().iqmp),
363+
'd': long_to_base64(self.prepared_key.private_numbers().d).decode('ASCII'),
364+
'p': long_to_base64(self.prepared_key.private_numbers().p).decode('ASCII'),
365+
'q': long_to_base64(self.prepared_key.private_numbers().q).decode('ASCII'),
366+
'dp': long_to_base64(self.prepared_key.private_numbers().dmp1).decode('ASCII'),
367+
'dq': long_to_base64(self.prepared_key.private_numbers().dmq1).decode('ASCII'),
368+
'qi': long_to_base64(self.prepared_key.private_numbers().iqmp).decode('ASCII'),
369369
})
370370

371371
return data

jose/backends/ecdsa_backend.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def __init__(self, key, algorithm):
7070

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

7575
if not all(k in jwk_dict for k in ['x', 'y', 'crv']):
7676
raise JWKError('Mandatory parameters are missing')
@@ -131,14 +131,14 @@ def to_dict(self):
131131
'alg': self._algorithm,
132132
'kty': 'EC',
133133
'crv': crv,
134-
'x': long_to_base64(public_key.pubkey.point.x(), size=key_size),
135-
'y': long_to_base64(public_key.pubkey.point.y(), size=key_size),
134+
'x': long_to_base64(public_key.pubkey.point.x(), size=key_size).decode('ASCII'),
135+
'y': long_to_base64(public_key.pubkey.point.y(), size=key_size).decode('ASCII'),
136136
}
137137

138138
if not self.is_public():
139139
data['d'] = long_to_base64(
140140
self.prepared_key.privkey.secret_multiplier,
141141
size=key_size
142-
)
142+
).decode('ASCII')
143143

144144
return data

jose/backends/pycrypto_backend.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def __init__(self, key, algorithm):
9595

9696
def _process_jwk(self, jwk_dict):
9797
if not jwk_dict.get('kty') == 'RSA':
98-
raise JWKError("Incorrect key type. Expected: 'RSA', Recieved: %s" % jwk_dict.get('kty'))
98+
raise JWKError("Incorrect key type. Expected: 'RSA', Received: %s" % jwk_dict.get('kty'))
9999

100100
e = base64_to_long(jwk_dict.get('e', 256))
101101
n = base64_to_long(jwk_dict.get('n'))
@@ -187,8 +187,8 @@ def to_dict(self):
187187
data = {
188188
'alg': self._algorithm,
189189
'kty': 'RSA',
190-
'n': long_to_base64(self.prepared_key.n),
191-
'e': long_to_base64(self.prepared_key.e),
190+
'n': long_to_base64(self.prepared_key.n).decode('ASCII'),
191+
'e': long_to_base64(self.prepared_key.e).decode('ASCII'),
192192
}
193193

194194
if not self.is_public():
@@ -203,12 +203,12 @@ def to_dict(self):
203203
dp = self.prepared_key.d % (self.prepared_key.p - 1)
204204
dq = self.prepared_key.d % (self.prepared_key.q - 1)
205205
data.update({
206-
'd': long_to_base64(self.prepared_key.d),
207-
'p': long_to_base64(self.prepared_key.q),
208-
'q': long_to_base64(self.prepared_key.p),
209-
'dp': long_to_base64(dq),
210-
'dq': long_to_base64(dp),
211-
'qi': long_to_base64(self.prepared_key.u),
206+
'd': long_to_base64(self.prepared_key.d).decode('ASCII'),
207+
'p': long_to_base64(self.prepared_key.q).decode('ASCII'),
208+
'q': long_to_base64(self.prepared_key.p).decode('ASCII'),
209+
'dp': long_to_base64(dq).decode('ASCII'),
210+
'dq': long_to_base64(dp).decode('ASCII'),
211+
'qi': long_to_base64(self.prepared_key.u).decode('ASCII'),
212212
})
213213

214214
return data

jose/backends/rsa_backend.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def __init__(self, key, algorithm):
170170

171171
def _process_jwk(self, jwk_dict):
172172
if not jwk_dict.get('kty') == 'RSA':
173-
raise JWKError("Incorrect key type. Expected: 'RSA', Recieved: %s" % jwk_dict.get('kty'))
173+
raise JWKError("Incorrect key type. Expected: 'RSA', Received: %s" % jwk_dict.get('kty'))
174174

175175
e = base64_to_long(jwk_dict.get('e'))
176176
n = base64_to_long(jwk_dict.get('n'))
@@ -248,18 +248,18 @@ def to_dict(self):
248248
data = {
249249
'alg': self._algorithm,
250250
'kty': 'RSA',
251-
'n': long_to_base64(public_key.n),
252-
'e': long_to_base64(public_key.e),
251+
'n': long_to_base64(public_key.n).decode('ASCII'),
252+
'e': long_to_base64(public_key.e).decode('ASCII'),
253253
}
254254

255255
if not self.is_public():
256256
data.update({
257-
'd': long_to_base64(self._prepared_key.d),
258-
'p': long_to_base64(self._prepared_key.p),
259-
'q': long_to_base64(self._prepared_key.q),
260-
'dp': long_to_base64(self._prepared_key.exp1),
261-
'dq': long_to_base64(self._prepared_key.exp2),
262-
'qi': long_to_base64(self._prepared_key.coef),
257+
'd': long_to_base64(self._prepared_key.d).decode('ASCII'),
258+
'p': long_to_base64(self._prepared_key.p).decode('ASCII'),
259+
'q': long_to_base64(self._prepared_key.q).decode('ASCII'),
260+
'dp': long_to_base64(self._prepared_key.exp1).decode('ASCII'),
261+
'dq': long_to_base64(self._prepared_key.exp2).decode('ASCII'),
262+
'qi': long_to_base64(self._prepared_key.coef).decode('ASCII'),
263263
})
264264

265265
return data

jose/exceptions.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ class JWTClaimsError(JWTError):
2424
pass
2525

2626

27-
class JWTSignatureError(JWTError):
28-
pass
29-
30-
3127
class ExpiredSignatureError(JWTError):
3228
pass
3329

jose/jwk.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from jose.constants import ALGORITHMS
77
from jose.exceptions import JWKError
88
from jose.utils import base64url_decode, base64url_encode
9-
from jose.utils import constant_time_string_compare
109
from jose.backends.base import Key
1110

1211
try:
@@ -36,7 +35,7 @@ def get_key(algorithm):
3635

3736
def register_key(algorithm, key_class):
3837
if not issubclass(key_class, Key):
39-
raise TypeError("Key class not a subclass of jwk.Key")
38+
raise TypeError("Key class is not a subclass of jwk.Key")
4039
ALGORITHMS.KEYS[algorithm] = key_class
4140
ALGORITHMS.SUPPORTED.add(algorithm)
4241
return True
@@ -53,11 +52,11 @@ def construct(key_data, algorithm=None):
5352
algorithm = key_data.get('alg', None)
5453

5554
if not algorithm:
56-
raise JWKError('Unable to find a algorithm for key: %s' % key_data)
55+
raise JWKError('Unable to find an algorithm for key: %s' % key_data)
5756

5857
key_class = get_key(algorithm)
5958
if not key_class:
60-
raise JWKError('Unable to find a algorithm for key: %s' % key_data)
59+
raise JWKError('Unable to find an algorithm for key: %s' % key_data)
6160
return key_class(key_data, algorithm)
6261

6362

@@ -119,7 +118,7 @@ def __init__(self, key, algorithm):
119118

120119
def _process_jwk(self, jwk_dict):
121120
if not jwk_dict.get('kty') == 'oct':
122-
raise JWKError("Incorrect key type. Expected: 'oct', Recieved: %s" % jwk_dict.get('kty'))
121+
raise JWKError("Incorrect key type. Expected: 'oct', Received: %s" % jwk_dict.get('kty'))
123122

124123
k = jwk_dict.get('k')
125124
k = k.encode('utf-8')
@@ -132,11 +131,11 @@ def sign(self, msg):
132131
return hmac.new(self.prepared_key, msg, self.hash_alg).digest()
133132

134133
def verify(self, msg, sig):
135-
return constant_time_string_compare(sig, self.sign(msg))
134+
return hmac.compare_digest(sig, self.sign(msg))
136135

137136
def to_dict(self):
138137
return {
139138
'alg': self._algorithm,
140139
'kty': 'oct',
141-
'k': base64url_encode(self.prepared_key),
140+
'k': base64url_encode(self.prepared_key).decode('ASCII'),
142141
}

0 commit comments

Comments
 (0)