Skip to content

Commit 1d5a0f5

Browse files
committed
feat(authenitcation.digest): add support for SHA-512-256 algorithm
1 parent 25d35e2 commit 1d5a0f5

1 file changed

Lines changed: 22 additions & 17 deletions

File tree

httoop/authentication/digest.py

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,43 @@
11
from __future__ import annotations
22

3-
from hashlib import md5, sha256
3+
from hashlib import md5, new, sha256
44
from hmac import compare_digest
5+
from time import time
56
from typing import Callable
7+
from uuid import uuid4
68

79
from httoop.exceptions import InvalidHeader
810
from httoop.header.element import HeaderElement
911
from httoop.util import ByteUnicodeDict, _
1012

1113

1214
class 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))
@@ -208,7 +213,7 @@ def A1(cls, params: ByteUnicodeDict) -> bytes:
208213

209214
if not algorithm or algorithm == b'MD5':
210215
return b'%s:%s:%s' % (params['username'], params['realm'], params['password'])
211-
if algorithm == b'MD5-sess':
216+
if algorithm.endswith(b'-sess'):
212217
H = cls.get_algorithm(algorithm)
213218
s = b'%s:%s:%s' % (params['username'], params['realm'], params['password'])
214219
return b'%s:%s:%s' % (H(s), params['nonce'], params['cnonce'])

0 commit comments

Comments
 (0)