1919except ImportError :
2020 import simplejson as json
2121
22- __all__ = ['encode' , 'decode' , 'DecodeError' ]
23-
2422
2523if sys .version_info >= (3 , 0 , 0 ):
2624 unicode = str
2725 basestring = str
2826
2927
28+ __all__ = ['encode' , 'decode' , 'DecodeError' ]
29+
30+
3031class DecodeError (Exception ):
3132 pass
3233
@@ -47,11 +48,14 @@ class ExpiredSignature(Exception):
4748 'HS512' : lambda msg , key : hmac .new (key , msg , hashlib .sha512 ).digest ()
4849}
4950
51+
5052def prepare_HS_key (key ):
5153 if not isinstance (key , basestring ) and not isinstance (key , bytes ):
52- raise TypeError ("Expecting a string- or bytes-formatted key." )
54+ raise TypeError ('Expecting a string- or bytes-formatted key.' )
55+
5356 if isinstance (key , unicode ):
5457 key = key .encode ('utf-8' )
58+
5559 return key
5660
5761prepare_key_methods = {
@@ -83,11 +87,13 @@ def prepare_RS_key(key):
8387 if isinstance (key , basestring ):
8488 if isinstance (key , unicode ):
8589 key = key .encode ('utf-8' )
90+
8691 key = RSA .importKey (key )
8792 elif isinstance (key , RSA ._RSAobj ):
8893 pass
8994 else :
90- raise TypeError ("Expecting a PEM- or RSA-formatted key." )
95+ raise TypeError ('Expecting a PEM- or RSA-formatted key.' )
96+
9197 return key
9298
9399 prepare_key_methods .update ({
@@ -108,20 +114,26 @@ def constant_time_compare(val1, val2):
108114 """
109115 if len (val1 ) != len (val2 ):
110116 return False
117+
111118 result = 0
112- if sys .version_info >= (3 , 0 , 0 ): # bytes are numbers
119+
120+ if sys .version_info >= (3 , 0 , 0 ):
121+ # Bytes are numbers
113122 for x , y in zip (val1 , val2 ):
114123 result |= x ^ y
115124 else :
116125 for x , y in zip (val1 , val2 ):
117126 result |= ord (x ) ^ ord (y )
127+
118128 return result == 0
119129
120130
121131def base64url_decode (input ):
122132 rem = len (input ) % 4
133+
123134 if rem > 0 :
124135 input += b'=' * (4 - rem )
136+
125137 return base64 .urlsafe_b64decode (input )
126138
127139
@@ -135,28 +147,31 @@ def header(jwt):
135147 header_data = base64url_decode (header_segment ).decode ('utf-8' )
136148 return json .loads (header_data )
137149 except (ValueError , TypeError ):
138- raise DecodeError (" Invalid header encoding" )
150+ raise DecodeError (' Invalid header encoding' )
139151
140152
141153def encode (payload , key , algorithm = 'HS256' , headers = None ):
142154 segments = []
143155
144156 # Check that we get a mapping
145157 if not isinstance (payload , Mapping ):
146- raise TypeError (" Expecting a mapping object, as json web token only"
147- " support json objects." )
158+ raise TypeError (' Expecting a mapping object, as json web token only'
159+ ' support json objects.' )
148160
149161 # Header
150- header = {" typ" : " JWT" , " alg" : algorithm }
162+ header = {' typ' : ' JWT' , ' alg' : algorithm }
151163 if headers :
152164 header .update (headers )
165+
153166 json_header = json .dumps (header , separators = (',' , ':' )).encode ('utf-8' )
154167 segments .append (base64url_encode (json_header ))
155168
156169 # Payload
157- for time_claim in ['exp' , 'iat' , 'nbf' ]: # convert datetime to a intDate value in known time-format claims
170+ for time_claim in ['exp' , 'iat' , 'nbf' ]:
171+ # Convert datetime to a intDate value in known time-format claims
158172 if isinstance (payload .get (time_claim ), datetime ):
159173 payload [time_claim ] = timegm (payload [time_claim ].utctimetuple ())
174+
160175 json_payload = json .dumps (payload , separators = (',' , ':' )).encode ('utf-8' )
161176 segments .append (base64url_encode (json_payload ))
162177
@@ -166,8 +181,10 @@ def encode(payload, key, algorithm='HS256', headers=None):
166181 key = prepare_key_methods [algorithm ](key )
167182 signature = signing_methods [algorithm ](signing_input , key )
168183 except KeyError :
169- raise NotImplementedError ("Algorithm not supported" )
184+ raise NotImplementedError ('Algorithm not supported' )
185+
170186 segments .append (base64url_encode (signature ))
187+
171188 return b'.' .join (segments )
172189
173190
@@ -176,7 +193,7 @@ def decode(jwt, key='', verify=True, verify_expiration=True, leeway=0):
176193
177194 if verify :
178195 verify_signature (payload , signing_input , header , signature , key ,
179- verify_expiration , leeway )
196+ verify_expiration , leeway )
180197
181198 return payload
182199
@@ -188,50 +205,53 @@ def load(jwt):
188205 signing_input , crypto_segment = jwt .rsplit (b'.' , 1 )
189206 header_segment , payload_segment = signing_input .split (b'.' , 1 )
190207 except ValueError :
191- raise DecodeError (" Not enough segments" )
208+ raise DecodeError (' Not enough segments' )
192209
193210 try :
194211 header_data = base64url_decode (header_segment )
195212 except (TypeError , binascii .Error ):
196- raise DecodeError (" Invalid header padding" )
213+ raise DecodeError (' Invalid header padding' )
197214 try :
198215 header = json .loads (header_data .decode ('utf-8' ))
199216 except ValueError as e :
200- raise DecodeError (" Invalid header string: %s" % e )
217+ raise DecodeError (' Invalid header string: %s' % e )
201218
202219 try :
203220 payload_data = base64url_decode (payload_segment )
204221 except (TypeError , binascii .Error ):
205- raise DecodeError (" Invalid payload padding" )
222+ raise DecodeError (' Invalid payload padding' )
206223 try :
207224 payload = json .loads (payload_data .decode ('utf-8' ))
208225 except ValueError as e :
209- raise DecodeError (" Invalid payload string: %s" % e )
226+ raise DecodeError (' Invalid payload string: %s' % e )
210227
211228 try :
212229 signature = base64url_decode (crypto_segment )
213230 except (TypeError , binascii .Error ):
214- raise DecodeError (" Invalid crypto padding" )
231+ raise DecodeError (' Invalid crypto padding' )
215232
216233 return (payload , signing_input , header , signature )
217234
218235
219236def verify_signature (payload , signing_input , header , signature , key = '' ,
220- verify_expiration = True , leeway = 0 ):
237+ verify_expiration = True , leeway = 0 ):
221238 try :
222239 algorithm = header ['alg' ].upper ()
223240 key = prepare_key_methods [algorithm ](key )
241+
224242 if algorithm .startswith ('HS' ):
225243 expected = verify_methods [algorithm ](signing_input , key )
244+
226245 if not constant_time_compare (signature , expected ):
227- raise DecodeError (" Signature verification failed" )
246+ raise DecodeError (' Signature verification failed' )
228247 else :
229248 if not verify_methods [algorithm ](signing_input , key , signature ):
230- raise DecodeError (" Signature verification failed" )
249+ raise DecodeError (' Signature verification failed' )
231250 except KeyError :
232- raise DecodeError (" Algorithm not supported" )
251+ raise DecodeError (' Algorithm not supported' )
233252
234253 if 'exp' in payload and verify_expiration :
235254 utc_timestamp = timegm (datetime .utcnow ().utctimetuple ())
255+
236256 if payload ['exp' ] < (utc_timestamp - leeway ):
237- raise ExpiredSignature (" Signature has expired" )
257+ raise ExpiredSignature (' Signature has expired' )
0 commit comments