Skip to content

Commit f9c0fec

Browse files
author
Roland Hedberg
committed
Merge pull request #42 from rebeckag/unicode_cleanup
Unicode with base64
2 parents c4cdd44 + 576a164 commit f9c0fec

File tree

4 files changed

+59
-62
lines changed

4 files changed

+59
-62
lines changed

src/jwkest/__init__.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import logging
44
import re
55
import struct
6+
import six
67

78
try:
89
from builtins import zip
@@ -103,12 +104,13 @@ def long_to_base64(n):
103104
if not len(data):
104105
data = '\x00'
105106
s = base64.urlsafe_b64encode(data).rstrip(b'=')
106-
return s
107+
return s.decode("ascii")
107108

108109

109110
def base64_to_long(data):
110-
# if isinstance(data, str):
111-
# data = bytes(data)
111+
if isinstance(data, six.text_type):
112+
data = data.encode("ascii")
113+
112114
# urlsafe_b64decode will happily convert b64encoded data
113115
_d = base64.urlsafe_b64decode(bytes(data) + b'==')
114116
return intarr2long(struct.unpack('%sB' % len(_d), _d))
@@ -164,10 +166,7 @@ def b64d(b):
164166
:param b: bytes
165167
"""
166168

167-
if b.endswith(b'='): # shouldn't but there you are
168-
cb = b.split(b'=')[0]
169-
else:
170-
cb = b
169+
cb = b.rstrip(b"=") # shouldn't but there you are
171170

172171
# Python's base64 functions ignore invalid characters, so we need to
173172
# check for them explicitly.

tests/test_0_jwkest.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import base64
12
import os
23
import struct
3-
from jwkest import long2intarr
4+
from jwkest import long2intarr, b64d, b64e
45
from jwkest import intarr2long
56
from jwkest import base64_to_long
67
from jwkest import long_to_base64
@@ -14,11 +15,13 @@
1415
def full_path(local_file):
1516
return os.path.join(BASEDIR, local_file)
1617

18+
1719
CERT = full_path("cert.pem")
1820
KEY = full_path("server.key")
1921

2022
_CKEY = pem_cert2rsa(CERT)
2123

24+
2225
def test_long_intarr_long():
2326
ia = long2intarr(_CKEY.n)
2427
_n = intarr2long(ia)
@@ -39,5 +42,16 @@ def test_long_base64_long():
3942
assert _CKEY.n == l
4043

4144

45+
def test_b64d_with_padded_data():
46+
data = "abcd".encode("utf-8")
47+
encoded = base64.urlsafe_b64encode(data)
48+
assert b64d(encoded) == data
49+
50+
51+
def test_b64_encode_decode():
52+
data = "abcd".encode("utf-8")
53+
assert b64d(b64e(data)) == data
54+
55+
4256
if __name__ == "__main__":
43-
test_long_base64_long()
57+
test_long_base64_long()

tests/test_2_jwk.py

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from jwkest.jwk import RSAKey
2020
from jwkest.jwk import base64_to_long
2121
import os.path
22+
import pytest
2223

2324
__author__ = 'rohe0002'
2425
BASEDIR = os.path.abspath(os.path.dirname(__file__))
@@ -31,12 +32,12 @@ def full_path(local_file):
3132
CERT = full_path("cert.pem")
3233
KEY = full_path("server.key")
3334

34-
N = b'wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8'
35-
E = b'AQAB'
35+
N = 'wf-wiusGhA-gleZYQAOPQlNUIucPiqXdPVyieDqQbXXOPBe3nuggtVzeq7pVFH1dZz4dY2Q2LA5DaegvP8kRvoSB_87ds3dy3Rfym_GUSc5B0l1TgEobcyaep8jguRoHto6GWHfCfKqoUYZq4N8vh4LLMQwLR6zi6Jtu82nB5k8'
36+
E = 'AQAB'
3637

3738
JWK = {"keys": [
38-
{'kty': 'RSA', 'use': 'foo', 'e': E.decode("utf-8"), 'kid': "abc",
39-
'n': N.decode("utf8")}
39+
{'kty': 'RSA', 'use': 'foo', 'e': E, 'kid': "abc",
40+
'n': N}
4041
]}
4142

4243

@@ -54,14 +55,12 @@ def test_urlsafe_base64decode():
5455
s0 = base64.b64encode(data)
5556
# try to convert it back to long, should throw an exception if the strict
5657
# function is used
57-
try:
58-
l = base64url_to_long(s0)
59-
except ValueError:
60-
pass
61-
else:
62-
assert False
63-
# Not else
58+
with pytest.raises(ValueError):
59+
base64url_to_long(s0)
60+
61+
# Not else, should not raise exception
6462
l = base64_to_long(s0)
63+
assert l
6564

6665

6766
def test_pem_cert2rsa():
@@ -87,8 +86,8 @@ def test_kspec():
8786
print(_key)
8887
jwk = _key.serialize()
8988
assert jwk["kty"] == "RSA"
90-
assert jwk["e"] == JWK["keys"][0]["e"].encode("utf-8")
91-
assert jwk["n"] == JWK["keys"][0]["n"].encode("utf-8")
89+
assert jwk["e"] == JWK["keys"][0]["e"]
90+
assert jwk["n"] == JWK["keys"][0]["n"]
9291

9392

9493
def test_loads_0():
@@ -170,9 +169,8 @@ def test_import_rsa_key():
170169
djwk = jwk_wrap(_ckey).to_dict()
171170
print(djwk)
172171
assert _eq(djwk.keys(), ["kty", "e", "n", "p", "q", "d"])
173-
assert djwk[
174-
"n"] == b'5zbNbHIYIkGGJ3RGdRKkYmF4gOorv5eDuUKTVtuu3VvxrpOWvwnFV-NY0LgqkQSMMyVzodJE3SUuwQTUHPXXY5784vnkFqzPRx6bHgPxKz7XfwQjEBTafQTMmOeYI8wFIOIHY5i0RWR-gxDbh_D5TXuUqScOOqR47vSpIbUH-nc'
175-
assert djwk['e'] == b'AQAB'
172+
assert djwk["n"] == '5zbNbHIYIkGGJ3RGdRKkYmF4gOorv5eDuUKTVtuu3VvxrpOWvwnFV-NY0LgqkQSMMyVzodJE3SUuwQTUHPXXY5784vnkFqzPRx6bHgPxKz7XfwQjEBTafQTMmOeYI8wFIOIHY5i0RWR-gxDbh_D5TXuUqScOOqR47vSpIbUH-nc'
173+
assert djwk['e'] == 'AQAB'
176174

177175

178176
def test_serialize_rsa_pub_key():
@@ -238,25 +236,15 @@ def test_cmp_rsa_ec():
238236

239237
_key2 = ECKey(**ECKEY)
240238

241-
try:
242-
assert _key1 == _key2
243-
except AssertionError:
244-
pass
245-
else:
246-
assert False
239+
assert _key1 != _key2
247240

248241

249242
def test_cmp_neq_ec():
250243
priv, pub = P256.key_pair()
251244
_key1 = ECKey(x=pub[0], y=pub[1], d=priv, crv="P-256")
252245
_key2 = ECKey(**ECKEY)
253246

254-
try:
255-
assert _key1 == _key2
256-
except AssertionError:
257-
pass
258-
else:
259-
assert False
247+
assert _key1 != _key2
260248

261249

262250
JWKS = {"keys": [

tests/test_3_jws.py

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
def full_path(local_file):
2727
return os.path.join(BASEDIR, local_file)
2828

29+
2930
KEY = full_path("server.key")
3031

3132
JWK = {"keys": [{'alg': 'RSA',
@@ -44,7 +45,6 @@ def full_path(local_file):
4445
119, 98, 61, 34, 61, 46, 33, 114, 5, 46, 79, 8, 192, 205, 154, 245,
4546
103, 208, 128, 163]
4647

47-
4848
JWKS = {"keys": [
4949
{
5050
"n": b"zkpUgEgXICI54blf6iWiD2RbMDCOO1jV0VSff1MFFnujM4othfMsad7H1kRo50YM5S_X9TdvrpdOfpz5aBaKFhT6Ziv0nhtcekq1eRl8mjBlvGKCE5XGk-0LFSDwvqgkJoFYInq7bu0a4JEzKs5AyJY75YlGh879k1Uu2Sv3ZZOunfV1O1Orta-NvS-aG_jN5cstVbCGWE20H0vFVrJKNx0Zf-u-aA-syM4uX7wdWgQ-owoEMHge0GmGgzso2lwOYf_4znanLwEuO3p5aabEaFoKNR4K6GjQcjBcYmDEE4CtfRU9AEmhcD1kleiTB9TjPWkgDmT9MXsGxBHf3AKT5w",
@@ -132,6 +132,7 @@ def full_path(local_file):
132132
SIGKEYS = KEYS()
133133
SIGKEYS.load_dict(JWKS)
134134

135+
135136
def test_1():
136137
claimset = {"iss": "joe",
137138
"exp": 1300819380,
@@ -148,14 +149,14 @@ def test_1():
148149

149150

150151
def test_hmac_256():
151-
payload = b'Please take a moment to register today'
152+
payload = 'Please take a moment to register today'
152153
keys = [SYMKey(key=jwkest.intarr2bin(HMAC_KEY))]
153154
_jws = JWS(payload, alg="HS256")
154155
_jwt = _jws.sign_compact(keys)
155156

156157
info = JWS().verify_compact(_jwt, keys)
157158

158-
assert info == payload.decode("utf-8")
159+
assert info == payload
159160

160161

161162
def test_hmac_384():
@@ -205,7 +206,7 @@ def test_left_hash_hs512():
205206
def test_rs256():
206207
payload = "Please take a moment to register today"
207208
keys = [RSAKey(key=import_rsa_key_from_file(KEY))]
208-
#keys[0]._keytype = "private"
209+
# keys[0]._keytype = "private"
209210
_jws = JWS(payload, alg="RS256")
210211
_jwt = _jws.sign_compact(keys)
211212

@@ -218,7 +219,7 @@ def test_rs256():
218219
def test_rs384():
219220
payload = "Please take a moment to register today"
220221
keys = [RSAKey(key=import_rsa_key_from_file(KEY))]
221-
#keys[0]._keytype = "private"
222+
# keys[0]._keytype = "private"
222223
_jws = JWS(payload, alg="RS384")
223224
_jwt = _jws.sign_compact(keys)
224225

@@ -230,7 +231,7 @@ def test_rs384():
230231
def test_rs512():
231232
payload = "Please take a moment to register today"
232233
keys = [RSAKey(key=import_rsa_key_from_file(KEY))]
233-
#keys[0]._keytype = "private"
234+
# keys[0]._keytype = "private"
234235
_jws = JWS(payload, alg="RS512")
235236
_jwt = _jws.sign_compact(keys)
236237

@@ -323,7 +324,7 @@ def test_signer_es512():
323324
payload = "Please take a moment to register today"
324325
_key = ECKey().load_key(P521)
325326
keys = [_key]
326-
#keys[0]._keytype = "private"
327+
# keys[0]._keytype = "private"
327328
_jws = JWS(payload, alg="ES512")
328329
_jwt = _jws.sign_compact(keys)
329330

@@ -335,7 +336,7 @@ def test_signer_es512():
335336
def test_signer_ps256():
336337
payload = "Please take a moment to register today"
337338
keys = [RSAKey(key=import_rsa_key_from_file(KEY))]
338-
#keys[0]._keytype = "private"
339+
# keys[0]._keytype = "private"
339340
_jws = JWS(payload, alg="PS256")
340341
_jwt = _jws.sign_compact(keys)
341342

@@ -347,7 +348,7 @@ def test_signer_ps256():
347348
def test_signer_ps256_fail():
348349
payload = "Please take a moment to register today"
349350
keys = [RSAKey(key=import_rsa_key_from_file(KEY))]
350-
#keys[0]._keytype = "private"
351+
# keys[0]._keytype = "private"
351352
_jws = JWS(payload, alg="PS256")
352353
_jwt = _jws.sign_compact(keys)[:-5] + 'abcde'
353354

@@ -363,7 +364,7 @@ def test_signer_ps256_fail():
363364
def test_signer_ps384():
364365
payload = "Please take a moment to register today"
365366
keys = [RSAKey(key=import_rsa_key_from_file(KEY))]
366-
#keys[0]._keytype = "private"
367+
# keys[0]._keytype = "private"
367368
_jws = JWS(payload, alg="PS384")
368369
_jwt = _jws.sign_compact(keys)
369370

@@ -376,7 +377,7 @@ def test_signer_ps512():
376377
payload = "Please take a moment to register today"
377378
# Key has to be big enough > 512+512+2
378379
keys = [RSAKey(key=import_rsa_key_from_file(full_path("./size2048.key")))]
379-
#keys[0]._keytype = "private"
380+
# keys[0]._keytype = "private"
380381
_jws = JWS(payload, alg="PS512")
381382
_jwt = _jws.sign_compact(keys)
382383

@@ -410,20 +411,21 @@ def test_sign_2():
410411
"use": "sig",
411412
"kid": "af22448d-4c7b-464d-b63a-f5bd90f6d7d1",
412413
"n": "o9g8DpUwBW6B1qmcm-TfEh4rNX7n1t38jdo4Gkl_cI3q--7n0Blg0kN88LHZvyZjUB2NhBdFYNxMP8ucy0dOXvWGWzaPmGnq3DM__lN8P4WjD1cCTAVEYKawNBAmGKqrFj1SgpPNsSqiqK-ALM1w6mZ-QGimjOgwCyJy3l9lzZh5D8tKnS2t1pZgE0X5P7lZQWHYpHPqp4jKhETzrCpPGfv0Rl6nmmjp7NlRYBkWKf_HEKE333J6M039m2FbKgxrBg3zmYYpmHuMzVgxxb8LSiv5aqyeyJjxM-YDUAgNQBfKNhONqXyu9DqtSprNkw6sqmuxK0QUVrNYl3b03PgS5Q"
413-
}]}
414+
}]}
414415

415416
keys = KEYS()
416417
keys.load_dict(keyset)
417418
jws = JWS("payload", alg="RS512")
418419
jws.sign_compact(keys=keys)
419420

421+
420422
def test_signer_protected_headers():
421423
payload = "Please take a moment to register today"
422424
_key = ECKey().load_key(P256)
423425
keys = [_key]
424426
_jws = JWS(payload, alg="ES256")
425427
protected = dict(header1=u"header1 is protected",
426-
header2="header2 is protected too", a=1)
428+
header2="header2 is protected too", a=1)
427429
_jwt = _jws.sign_compact(keys, protected=protected)
428430

429431
exp_protected = protected.copy()
@@ -436,32 +438,26 @@ def test_signer_protected_headers():
436438
info = _rj.verify_compact(_jwt, keys)
437439
assert info == payload
438440

441+
439442
def test_verify_protected_headers():
440443
payload = "Please take a moment to register today"
441444
_key = ECKey().load_key(P256)
442445
keys = [_key]
443446
_jws = JWS(payload, alg="ES256")
444447
protected = dict(header1=u"header1 is protected",
445-
header2="header2 is protected too", a=1)
448+
header2="header2 is protected too", a=1)
446449
_jwt = _jws.sign_compact(keys, protected=protected)
447450
protectedHeader, enc_payload, sig = _jwt.split(".")
448451
data = dict(payload=enc_payload, signatures=[
449452
dict(
450453
header=dict(alg=u"ES256", jwk=_key.serialize()),
451454
protected=protectedHeader,
452455
signature=sig,
453-
)
454-
])
455-
fobj = io.BytesIO(JSONEncoder().encode(data).encode("utf-8"))
456+
)
457+
])
458+
stream = io.StringIO(json.dumps(data, ensure_ascii=False))
456459
_jws = JWS()
457-
reader = codecs.getreader("utf-8")
458-
assert _jws.verify_json(reader(fobj)) == payload
459-
460-
class JSONEncoder(json.JSONEncoder):
461-
def default(self, o):
462-
if isinstance(o, bytes):
463-
return o.decode('utf-8')
464-
return json.JSONEncoder.default(self, o)
460+
assert _jws.verify_json(stream) == payload
465461

466462

467463
def test_pick():

0 commit comments

Comments
 (0)