Skip to content

Commit 3bade27

Browse files
committed
Merge pull request jpadilla#23 from jpadilla/patch-1
Make PyCrypto optional if you're not using any of the RSASSA-PKCS1-v1_5 algorithms
2 parents 3ca7667 + bfae3e9 commit 3bade27

File tree

3 files changed

+86
-38
lines changed

3 files changed

+86
-38
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Installing
66
----------
77

88
sudo easy_install PyJWT
9+
10+
**Note**: The RSASSA-PKCS1-v1_5 algorithms depend on PyCrypto. If you plan on using any of those algorithms you'll need to install it as well.
11+
12+
sudo easy_install PyCrypto
913

1014
Usage
1115
-----
@@ -38,8 +42,7 @@ Change the algorithm with by setting it in encode:
3842
jwt.encode({"some": "payload"}, "secret", "HS512")
3943

4044
For the RSASSA-PKCS1-v1_5 algorithms, the "secret" argument in jwt.encode is supposed to be a private RSA key as
41-
imported with Crypto.PublicKey.RSA.importKey. Likewise, the "secret" argument in jwt.decode is supposed to be the
42-
public RSA key imported with the same method.
45+
imported with Crypto.PublicKey.RSA.importKey. Likewise, the "secret" argument in jwt.decode is supposed to be the public RSA key imported with the same method.
4346

4447
Tests
4548
-----

jwt/__init__.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@
1414
from calendar import timegm
1515
from collections import Mapping
1616

17-
from Crypto.Signature import PKCS1_v1_5
18-
from Crypto.Hash import SHA256
19-
from Crypto.Hash import SHA384
20-
from Crypto.Hash import SHA512
21-
2217
try:
2318
import json
2419
except ImportError:
@@ -42,20 +37,34 @@ class ExpiredSignature(Exception):
4237
signing_methods = {
4338
'HS256': lambda msg, key: hmac.new(key, msg, hashlib.sha256).digest(),
4439
'HS384': lambda msg, key: hmac.new(key, msg, hashlib.sha384).digest(),
45-
'HS512': lambda msg, key: hmac.new(key, msg, hashlib.sha512).digest(),
46-
'RS256': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA256.new(msg)),
47-
'RS384': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA384.new(msg)),
48-
'RS512': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA512.new(msg)),
49-
}
40+
'HS512': lambda msg, key: hmac.new(key, msg, hashlib.sha512).digest()
41+
}
5042

5143
verify_methods = {
5244
'HS256': lambda msg, key: hmac.new(key, msg, hashlib.sha256).digest(),
5345
'HS384': lambda msg, key: hmac.new(key, msg, hashlib.sha384).digest(),
54-
'HS512': lambda msg, key: hmac.new(key, msg, hashlib.sha512).digest(),
55-
'RS256': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA256.new(msg), sig),
56-
'RS384': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA384.new(msg), sig),
57-
'RS512': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA512.new(msg), sig),
58-
}
46+
'HS512': lambda msg, key: hmac.new(key, msg, hashlib.sha512).digest()
47+
}
48+
49+
try:
50+
from Crypto.Signature import PKCS1_v1_5
51+
from Crypto.Hash import SHA256
52+
from Crypto.Hash import SHA384
53+
from Crypto.Hash import SHA512
54+
55+
signing_methods.update({
56+
'RS256': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA256.new(msg)),
57+
'RS384': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA384.new(msg)),
58+
'RS512': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA512.new(msg))
59+
})
60+
61+
verify_methods.update({
62+
'RS256': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA256.new(msg), sig),
63+
'RS384': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA384.new(msg), sig),
64+
'RS512': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA512.new(msg), sig)
65+
})
66+
except ImportError:
67+
pass
5968

6069

6170
def constant_time_compare(val1, val2):

tests/test_jwt.py

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
if sys.version_info >= (3, 0, 0):
1111
unicode = str
1212

13-
from Crypto.PublicKey import RSA
14-
1513

1614
def utc_timestamp():
1715
return timegm(datetime.utcnow().utctimetuple())
@@ -175,30 +173,68 @@ def test_decode_with_expiration_with_leeway(self):
175173
jwt.decode(jwt_message, secret, leeway=1)
176174

177175
def test_encode_decode_with_rsa_sha256(self):
178-
with open('tests/testkey','r') as rsa_priv_file:
179-
priv_rsakey = RSA.importKey(rsa_priv_file.read())
180-
jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS256')
181-
with open('tests/testkey.pub','r') as rsa_pub_file:
182-
pub_rsakey = RSA.importKey(rsa_pub_file.read())
183-
assert jwt.decode(jwt_message, pub_rsakey)
176+
try:
177+
from Crypto.PublicKey import RSA
178+
179+
with open('tests/testkey','r') as rsa_priv_file:
180+
priv_rsakey = RSA.importKey(rsa_priv_file.read())
181+
jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS256')
182+
183+
with open('tests/testkey.pub','r') as rsa_pub_file:
184+
pub_rsakey = RSA.importKey(rsa_pub_file.read())
185+
assert jwt.decode(jwt_message, pub_rsakey)
186+
except ImportError:
187+
pass
184188

185189
def test_encode_decode_with_rsa_sha384(self):
186-
with open('tests/testkey','r') as rsa_priv_file:
187-
priv_rsakey = RSA.importKey(rsa_priv_file.read())
188-
jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS384')
189-
with open('tests/testkey.pub','r') as rsa_pub_file:
190-
pub_rsakey = RSA.importKey(rsa_pub_file.read())
191-
assert jwt.decode(jwt_message, pub_rsakey)
190+
try:
191+
from Crypto.PublicKey import RSA
192192

193-
def test_encode_decode_with_rsa_sha512(self):
194-
with open('tests/testkey','r') as rsa_priv_file:
195-
priv_rsakey = RSA.importKey(rsa_priv_file.read())
196-
jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS512')
197-
with open('tests/testkey.pub','r') as rsa_pub_file:
198-
pub_rsakey = RSA.importKey(rsa_pub_file.read())
199-
assert jwt.decode(jwt_message, pub_rsakey)
193+
with open('tests/testkey','r') as rsa_priv_file:
194+
priv_rsakey = RSA.importKey(rsa_priv_file.read())
195+
jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS384')
200196

197+
with open('tests/testkey.pub','r') as rsa_pub_file:
198+
pub_rsakey = RSA.importKey(rsa_pub_file.read())
199+
assert jwt.decode(jwt_message, pub_rsakey)
200+
except ImportError:
201+
pass
201202

203+
def test_encode_decode_with_rsa_sha512(self):
204+
try:
205+
from Crypto.PublicKey import RSA
206+
207+
with open('tests/testkey','r') as rsa_priv_file:
208+
priv_rsakey = RSA.importKey(rsa_priv_file.read())
209+
jwt_message = jwt.encode(self.payload, priv_rsakey, algorithm='RS512')
210+
211+
with open('tests/testkey.pub','r') as rsa_pub_file:
212+
pub_rsakey = RSA.importKey(rsa_pub_file.read())
213+
assert jwt.decode(jwt_message, pub_rsakey)
214+
except ImportError:
215+
pass
216+
217+
def test_crypto_related_signing_methods(self):
218+
try:
219+
import Crypto
220+
self.assertTrue('RS256' in jwt.signing_methods)
221+
self.assertTrue('RS384' in jwt.signing_methods)
222+
self.assertTrue('RS512' in jwt.signing_methods)
223+
except ImportError:
224+
self.assertFalse('RS256' in jwt.signing_methods)
225+
self.assertFalse('RS384' in jwt.signing_methods)
226+
self.assertFalse('RS512' in jwt.signing_methods)
227+
228+
def test_crypto_related_verify_methods(self):
229+
try:
230+
import Crypto
231+
self.assertTrue('RS256' in jwt.verify_methods)
232+
self.assertTrue('RS384' in jwt.verify_methods)
233+
self.assertTrue('RS512' in jwt.verify_methods)
234+
except ImportError:
235+
self.assertFalse('RS256' in jwt.verify_methods)
236+
self.assertFalse('RS384' in jwt.verify_methods)
237+
self.assertFalse('RS512' in jwt.verify_methods)
202238

203239

204240
if __name__ == '__main__':

0 commit comments

Comments
 (0)