Skip to content

Commit f67a141

Browse files
committed
A JWT might just be encrypted not signed and encrypted.
1 parent dd2de67 commit f67a141

File tree

2 files changed

+72
-24
lines changed

2 files changed

+72
-24
lines changed

src/cryptojwt/jwt.py

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import json
33
import uuid
44
from datetime import datetime
5+
from json import JSONDecodeError
56

67
from cryptojwt import as_unicode
78
from cryptojwt import jwe
@@ -83,12 +84,14 @@ def get_jwt_keys(jwt, keys, use):
8384
class JWT(object):
8485

8586
def __init__(self, own_keys=None, iss='', rec_keys=None, lifetime=0,
86-
sign_alg='RS256', encrypt=False, enc_enc="A128CBC-HS256",
87-
enc_alg="RSA1_5", msg_cls=None, iss2msg_cls=None):
87+
sign=True, sign_alg='RS256', encrypt=False,
88+
enc_enc="A128CBC-HS256", enc_alg="RSA1_5", msg_cls=None,
89+
iss2msg_cls=None):
8890
self.own_keys = own_keys
8991
self.rec_keys = rec_keys or {}
9092
self.iss = iss
9193
self.lifetime = lifetime
94+
self.sign = sign
9295
self.sign_alg = sign_alg
9396
self.encrypt = encrypt
9497
self.enc_alg = enc_alg
@@ -154,12 +157,6 @@ def pack(self, payload=None, kid='', owner='', recv='', **kwargs):
154157
"""
155158
_args = self.pack_init()
156159

157-
if self.sign_alg != 'none':
158-
_key = self.pack_key(owner, kid)
159-
_args['kid'] = _key.kid
160-
else:
161-
_key = None
162-
163160
try:
164161
_encrypt = kwargs['encrypt']
165162
except KeyError:
@@ -178,11 +175,23 @@ def pack(self, payload=None, kid='', owner='', recv='', **kwargs):
178175
if payload is not None:
179176
_args.update(payload)
180177

181-
_jws = JWS(json.dumps(_args), alg=self.sign_alg)
182-
_sjwt = _jws.sign_compact([_key])
183-
# _jws = _jwt.to_jwt([_key], self.sign_alg)
178+
if self.sign:
179+
if self.sign_alg != 'none':
180+
_key = self.pack_key(owner, kid)
181+
_args['kid'] = _key.kid
182+
else:
183+
_key = None
184+
185+
_jws = JWS(json.dumps(_args), alg=self.sign_alg)
186+
_sjwt = _jws.sign_compact([_key])
187+
else:
188+
_sjwt = json.dumps(_args)
189+
184190
if _encrypt:
185-
return self._encrypt(_sjwt, recv)
191+
if not self.sign:
192+
return self._encrypt(_sjwt, recv, cty='json')
193+
else:
194+
return self._encrypt(_sjwt, recv)
186195
else:
187196
return _sjwt
188197

@@ -213,27 +222,53 @@ def unpack(self, token):
213222
214223
:param token: The Json Web Token
215224
:return: If decryption and signature verification work the payload
216-
will be returned as a Message instance.
225+
will be returned as a Message instance if possible.
217226
"""
218227
if not token:
219228
raise KeyError
220229

221-
_rj = jwe.factory(token)
222-
if _rj:
223-
token = self._decrypt(_rj, token)
230+
_content_type = 'jwt'
224231

225-
_rj = jws.factory(token)
232+
# Check if it's an encrypted JWT
233+
_rj = jwe.factory(token)
226234
if _rj:
227-
info = self._verify(_rj, token)
235+
# Yes, try to decode
236+
_info = self._decrypt(_rj, token)
237+
# Try to find out if the information encrypted was a signed JWT
238+
try:
239+
_content_type = _rj.jwt.headers['cty']
240+
except KeyError:
241+
pass
242+
else:
243+
_info = token
244+
245+
# If I have reason to believe the information I have is a signed JWT
246+
if _content_type.lower() == 'jwt':
247+
# Check that is a signed JWT
248+
_rj = jws.factory(_info)
249+
if _rj:
250+
_info = self._verify(_rj, _info)
251+
else:
252+
raise Exception()
228253
else:
229-
raise Exception()
254+
# So, not a signed JWT
255+
try:
256+
# A JSON document ?
257+
_info = json.loads(_info)
258+
except JSONDecodeError: # Oh, no ! Not JSON
259+
return _info
230260

261+
# If I know what message class the info should be mapped into
231262
if self.msg_cls:
232-
return self.verify_profile(self.msg_cls, **info)
263+
_msg_cls = self.msg_cls
233264
else:
234265
try:
235-
_msg_cls = self.iss2msg_cls[info['iss']]
266+
# try to find a issuer specific message class
267+
_msg_cls = self.iss2msg_cls[_info['iss']]
236268
except KeyError:
237-
return info
238-
else:
239-
return self.verify_profile(_msg_cls, **info)
269+
_msg_cls = None
270+
271+
if _msg_cls:
272+
return self.verify_profile(_msg_cls, **_info)
273+
else:
274+
return _info

tests/test_5_jwt.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,16 @@ def test_jwt_pack_unpack_sym():
8282
bob = JWT(own_keys=None, iss=BOB, rec_keys={ALICE: [_sym_key]})
8383
info = bob.unpack(_jwt)
8484
assert info
85+
86+
87+
def test_jwt_pack_encrypt_no_sign():
88+
alice = JWT(sign=False, own_keys=ALICE_KEYS, iss=ALICE,
89+
rec_keys={BOB: BOB_PUB_KEYS})
90+
91+
payload = {'sub': 'sub', 'aud': BOB}
92+
_jwt = alice.pack(payload=payload, encrypt=True, recv=BOB)
93+
94+
bob = JWT(own_keys=BOB_KEYS, iss=BOB, rec_keys={ALICE: ALICE_PUB_KEYS})
95+
info = bob.unpack(_jwt)
96+
97+
assert set(info.keys()) == {'iat', 'iss', 'sub', 'aud'}

0 commit comments

Comments
 (0)