diff --git a/CHANGELOG.md b/CHANGELOG.md index 79fd0d6a..71aa19af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog # +## Development ## + +* Fix `to_dict` output, which should always be JSON encodeable. #139 (fixes #127 and #137) + ## 3.1.0 -- 2019-12-10 ## This is a greatly overdue release. diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 00000000..e69de29b diff --git a/jose/backends/cryptography_backend.py b/jose/backends/cryptography_backend.py index fb6205f3..4e8ff7da 100644 --- a/jose/backends/cryptography_backend.py +++ b/jose/backends/cryptography_backend.py @@ -183,15 +183,15 @@ def to_dict(self): 'alg': self._algorithm, 'kty': 'EC', 'crv': crv, - 'x': long_to_base64(public_key.public_numbers().x, size=key_size), - 'y': long_to_base64(public_key.public_numbers().y, size=key_size), + 'x': long_to_base64(public_key.public_numbers().x, size=key_size).decode('ASCII'), + 'y': long_to_base64(public_key.public_numbers().y, size=key_size).decode('ASCII'), } if not self.is_public(): data['d'] = long_to_base64( self.prepared_key.private_numbers().private_value, size=key_size - ) + ).decode('ASCII') return data @@ -354,18 +354,18 @@ def to_dict(self): data = { 'alg': self._algorithm, 'kty': 'RSA', - 'n': long_to_base64(public_key.public_numbers().n), - 'e': long_to_base64(public_key.public_numbers().e), + 'n': long_to_base64(public_key.public_numbers().n).decode('ASCII'), + 'e': long_to_base64(public_key.public_numbers().e).decode('ASCII'), } if not self.is_public(): data.update({ - 'd': long_to_base64(self.prepared_key.private_numbers().d), - 'p': long_to_base64(self.prepared_key.private_numbers().p), - 'q': long_to_base64(self.prepared_key.private_numbers().q), - 'dp': long_to_base64(self.prepared_key.private_numbers().dmp1), - 'dq': long_to_base64(self.prepared_key.private_numbers().dmq1), - 'qi': long_to_base64(self.prepared_key.private_numbers().iqmp), + 'd': long_to_base64(self.prepared_key.private_numbers().d).decode('ASCII'), + 'p': long_to_base64(self.prepared_key.private_numbers().p).decode('ASCII'), + 'q': long_to_base64(self.prepared_key.private_numbers().q).decode('ASCII'), + 'dp': long_to_base64(self.prepared_key.private_numbers().dmp1).decode('ASCII'), + 'dq': long_to_base64(self.prepared_key.private_numbers().dmq1).decode('ASCII'), + 'qi': long_to_base64(self.prepared_key.private_numbers().iqmp).decode('ASCII'), }) return data diff --git a/jose/backends/ecdsa_backend.py b/jose/backends/ecdsa_backend.py index dc9ebe75..73b3eab0 100644 --- a/jose/backends/ecdsa_backend.py +++ b/jose/backends/ecdsa_backend.py @@ -131,14 +131,14 @@ def to_dict(self): 'alg': self._algorithm, 'kty': 'EC', 'crv': crv, - 'x': long_to_base64(public_key.pubkey.point.x(), size=key_size), - 'y': long_to_base64(public_key.pubkey.point.y(), size=key_size), + 'x': long_to_base64(public_key.pubkey.point.x(), size=key_size).decode('ASCII'), + 'y': long_to_base64(public_key.pubkey.point.y(), size=key_size).decode('ASCII'), } if not self.is_public(): data['d'] = long_to_base64( self.prepared_key.privkey.secret_multiplier, size=key_size - ) + ).decode('ASCII') return data diff --git a/jose/backends/pycrypto_backend.py b/jose/backends/pycrypto_backend.py index c002102f..cf270a88 100644 --- a/jose/backends/pycrypto_backend.py +++ b/jose/backends/pycrypto_backend.py @@ -185,8 +185,8 @@ def to_dict(self): data = { 'alg': self._algorithm, 'kty': 'RSA', - 'n': long_to_base64(self.prepared_key.n), - 'e': long_to_base64(self.prepared_key.e), + 'n': long_to_base64(self.prepared_key.n).decode('ASCII'), + 'e': long_to_base64(self.prepared_key.e).decode('ASCII'), } if not self.is_public(): @@ -201,12 +201,12 @@ def to_dict(self): dp = self.prepared_key.d % (self.prepared_key.p - 1) dq = self.prepared_key.d % (self.prepared_key.q - 1) data.update({ - 'd': long_to_base64(self.prepared_key.d), - 'p': long_to_base64(self.prepared_key.q), - 'q': long_to_base64(self.prepared_key.p), - 'dp': long_to_base64(dq), - 'dq': long_to_base64(dp), - 'qi': long_to_base64(self.prepared_key.u), + 'd': long_to_base64(self.prepared_key.d).decode('ASCII'), + 'p': long_to_base64(self.prepared_key.q).decode('ASCII'), + 'q': long_to_base64(self.prepared_key.p).decode('ASCII'), + 'dp': long_to_base64(dq).decode('ASCII'), + 'dq': long_to_base64(dp).decode('ASCII'), + 'qi': long_to_base64(self.prepared_key.u).decode('ASCII'), }) return data diff --git a/jose/backends/rsa_backend.py b/jose/backends/rsa_backend.py index ca9e9566..38e42bb6 100644 --- a/jose/backends/rsa_backend.py +++ b/jose/backends/rsa_backend.py @@ -246,18 +246,18 @@ def to_dict(self): data = { 'alg': self._algorithm, 'kty': 'RSA', - 'n': long_to_base64(public_key.n), - 'e': long_to_base64(public_key.e), + 'n': long_to_base64(public_key.n).decode('ASCII'), + 'e': long_to_base64(public_key.e).decode('ASCII'), } if not self.is_public(): data.update({ - 'd': long_to_base64(self._prepared_key.d), - 'p': long_to_base64(self._prepared_key.p), - 'q': long_to_base64(self._prepared_key.q), - 'dp': long_to_base64(self._prepared_key.exp1), - 'dq': long_to_base64(self._prepared_key.exp2), - 'qi': long_to_base64(self._prepared_key.coef), + 'd': long_to_base64(self._prepared_key.d).decode('ASCII'), + 'p': long_to_base64(self._prepared_key.p).decode('ASCII'), + 'q': long_to_base64(self._prepared_key.q).decode('ASCII'), + 'dp': long_to_base64(self._prepared_key.exp1).decode('ASCII'), + 'dq': long_to_base64(self._prepared_key.exp2).decode('ASCII'), + 'qi': long_to_base64(self._prepared_key.coef).decode('ASCII'), }) return data diff --git a/jose/jwk.py b/jose/jwk.py index ad89e6d5..b2c1113a 100644 --- a/jose/jwk.py +++ b/jose/jwk.py @@ -137,5 +137,5 @@ def to_dict(self): return { 'alg': self._algorithm, 'kty': 'oct', - 'k': base64url_encode(self.prepared_key), + 'k': base64url_encode(self.prepared_key).decode('ASCII'), } diff --git a/tests/algorithms/test_EC.py b/tests/algorithms/test_EC.py index 7f012afb..62b952c5 100644 --- a/tests/algorithms/test_EC.py +++ b/tests/algorithms/test_EC.py @@ -1,3 +1,4 @@ +import json from jose.constants import ALGORITHMS from jose.exceptions import JOSEError, JWKError @@ -194,6 +195,9 @@ def assert_parameters(self, as_dict, private): # Private parameters should be absent assert 'd' not in as_dict + # as_dict should be serializable to JSON + json.dumps(as_dict) + def test_to_dict(self): key = ECKey(private_key, ALGORITHMS.ES256) self.assert_parameters(key.to_dict(), private=True) diff --git a/tests/algorithms/test_HMAC.py b/tests/algorithms/test_HMAC.py index e84c2c02..843d3a28 100644 --- a/tests/algorithms/test_HMAC.py +++ b/tests/algorithms/test_HMAC.py @@ -1,3 +1,4 @@ +import json from jose.constants import ALGORITHMS from jose.exceptions import JOSEError @@ -31,7 +32,7 @@ def test_RSA_key(self): def test_to_dict(self): passphrase = 'The quick brown fox jumps over the lazy dog' - encoded = b'VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw' + encoded = 'VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw' key = HMACKey(passphrase, ALGORITHMS.HS256) as_dict = key.to_dict() @@ -43,3 +44,6 @@ def test_to_dict(self): assert 'k' in as_dict assert as_dict['k'] == encoded + + # as_dict should be serializable to JSON + json.dumps(as_dict) diff --git a/tests/algorithms/test_RSA.py b/tests/algorithms/test_RSA.py index 97aeb20e..cdcb1dae 100644 --- a/tests/algorithms/test_RSA.py +++ b/tests/algorithms/test_RSA.py @@ -1,4 +1,5 @@ import base64 +import json import sys try: @@ -370,6 +371,9 @@ def assert_parameters(self, as_dict, private): assert 'dq' not in as_dict assert 'qi' not in as_dict + # as_dict should be serializable to JSON + json.dumps(as_dict) + def assert_roundtrip(self, key): assert RSAKey( key.to_dict(),