11from __future__ import annotations
22
3- from hashlib import md5 , sha256
3+ from hashlib import md5 , new , sha256
44from hmac import compare_digest
5+ from time import time
56from typing import Callable
7+ from uuid import uuid4
68
79from httoop .exceptions import InvalidHeader
810from httoop .header .element import HeaderElement
911from httoop .util import ByteUnicodeDict , _
1012
1113
1214class DigestAuthScheme :
13-
1415 algorithms = {
15- 'MD5' : lambda val : md5 (val ). hexdigest (). encode ( 'ASCII' ) , # nosec
16- 'MD5-sess ' : lambda val : md5 ( val ). hexdigest (). encode ( 'ASCII' ), # nosec
17- 'SHA-256' : lambda val : sha256 ( val ). hexdigest (). encode ( 'ASCII ' ),
18- 'SHA-256-sess' : lambda val : sha256 ( val ). hexdigest (). encode ( 'ASCII' ),
19- # 'SHA-512-256': lambda val: sha256(val).hexdigest().encode('ASCII'), TODO: ??
20- # 'SHA-512- 256-sess': lambda val: sha256(val).hexdigest().encode('ASCII'), TODO: ??
21- }
16+ 'MD5' : lambda : md5 () , # noqa: S324
17+ 'SHA-256 ' : lambda : sha256 (),
18+ 'SHA-512- 256' : lambda : new ( 'sha512_256 ' ),
19+ } # not case insensitive per RFC
20+ algorithms [ 'MD5-sess' ] = algorithms [ 'MD5' ]
21+ algorithms [ 'SHA-256-sess' ] = algorithms [ 'SHA-256' ]
22+ algorithms [ 'SHA-512-256-sess' ] = algorithms [ 'SHA-512-256' ]
2223 qops = (b'auth' , b'auth-int' ) # quality of protection
2324
2425 @classmethod
25- def get_algorithm (cls , algorithm : bytes | str ) -> Callable :
26+ def get_algorithm (cls , algorithm : bytes | str ) -> Callable [ bytes , bytes ] :
2627 try :
27- return cls .algorithms [algorithm .decode ('ASCII' , 'ignore' ) if isinstance (algorithm , bytes ) else algorithm ]
28+ H = cls .algorithms [algorithm .decode ('ASCII' , 'ignore' ) if isinstance (algorithm , bytes ) else algorithm ]
2829 except KeyError :
29- raise InvalidHeader (_ ('Unknown digest authentication algorithm: %r' ), algorithm )
30+ raise InvalidHeader (_ ('Unknown digest authentication algorithm: %r' ), algorithm ) from None
31+
32+ def _algo (value ) -> bytes :
33+ h = H ()
34+ h .update (value )
35+ return h .hexdigest ().encode ('ASCII' )
36+
37+ return _algo
3038
3139 @classmethod
3240 def generate_nonce (cls , authinfo : ByteUnicodeDict ) -> bytes :
33- from time import time
34- from uuid import uuid4
35-
3641 nonce = b'%d:%s:%s' % (
3742 time (),
3843 authinfo .get ('etag' , authinfo .get ('realm' , b'' )),
@@ -176,7 +181,7 @@ def calculate_request_digest(cls, authinfo: ByteUnicodeDict) -> bytes:
176181 algorithm = authinfo .get ('algorithm' , b'MD5' ).decode ('ASCII' , 'replace' )
177182 H = cls .get_algorithm (algorithm )
178183
179- if algorithm == 'MD5 -sess' and authinfo .get ('A1' ): # noqa: SIM108
184+ if algorithm . endswith ( ' -sess') and authinfo .get ('A1' ): # noqa: SIM108
180185 secret = H (authinfo ['A1' ])
181186 else :
182187 secret = H (cls .A1 (authinfo ))
@@ -198,18 +203,17 @@ def A2(cls, params: ByteUnicodeDict) -> bytes:
198203 if not qop or qop == b'auth' :
199204 return b'%s:%s' % (params ['method' ], params ['uri' ])
200205 if qop == b'auth-int' :
201- H = cls .get_algorithm (params [ 'algorithm' ] )
206+ H = cls .get_algorithm (params . get ( 'algorithm' , b'MD5' ) )
202207 return b'%s:%s:%s' % (params ['method' ], params ['uri' ], H (params ['entity_body' ]))
203208 raise NotImplementedError (f'Unknown quality of protection: { qop !r} ' ) # pragma: no cover
204209
205210 @classmethod
206211 def A1 (cls , params : ByteUnicodeDict ) -> bytes :
207212 algorithm = params .get ('algorithm' , b'' )
208213
209- if not algorithm or algorithm == b'MD5' :
214+ if not algorithm or not algorithm . endswith ( b'-sess' ) :
210215 return b'%s:%s:%s' % (params ['username' ], params ['realm' ], params ['password' ])
211- if algorithm == b'MD5-sess' :
212- H = cls .get_algorithm (algorithm )
213- s = b'%s:%s:%s' % (params ['username' ], params ['realm' ], params ['password' ])
214- return b'%s:%s:%s' % (H (s ), params ['nonce' ], params ['cnonce' ])
215- raise NotImplementedError (f'Unknown algorithm: { algorithm } ' ) # pragma: no cover
216+
217+ H = cls .get_algorithm (algorithm )
218+ s = b'%s:%s:%s' % (params ['username' ], params ['realm' ], params ['password' ])
219+ return b'%s:%s:%s' % (H (s ), params ['nonce' ], params ['cnonce' ])
0 commit comments