Use a class variable to set content digest alg

pull/34/head
Andrey Kislyuk 2022-04-14 23:45:53 -07:00
parent b68a6e7db4
commit e3e7c0f4c7
No known key found for this signature in database
GPG Key ID: 8AFAFCD242818A52
3 changed files with 17 additions and 14 deletions

View File

@ -112,11 +112,11 @@ constructor as bytes in the PEM format, or configure the key resolver as follows
Digest algorithms
~~~~~~~~~~~~~~~~~
If you need to generate a Content-Digest header using SHA-512, you can do so via subclassing::
To generate a Content-Digest header using SHA-512 instead of the default SHA-256, subclass ``HTTPSignatureAuth`` as
follows::
class MySigner(HTTPSignatureAuth):
def add_digest(self, request):
super().add_digest(request, algorithm="sha-512")
default_content_digest_hasher = "sha-512"
Links
-----

View File

@ -77,7 +77,10 @@ class HTTPSignatureAuth(requests.auth.AuthBase):
to customize the retrieval of header and derived component values if needed.
"""
_digest_hashers = {"sha-256": hashlib.sha256, "sha-512": hashlib.sha512}
_content_digest_hashers = {"sha-256": hashlib.sha256, "sha-512": hashlib.sha512}
default_content_digest_hasher = "sha-256"
"The hash algorithm to use to generate the Content-Digest header field (either ``sha-256`` or ``sha-512``)."
_auto_cover_header_fields = {"authorization", "content-digest", "date"}
def __init__(self, *,
@ -111,14 +114,14 @@ class HTTPSignatureAuth(requests.auth.AuthBase):
if "Date" not in request.headers:
request.headers["Date"] = email.utils.formatdate(timestamp, usegmt=True)
def add_digest(self, request, algorithm="sha-256"):
def add_digest(self, request):
if request.body is None and "content-digest" in self.covered_component_ids:
raise RequestsHttpSignatureException("Could not compute digest header for request without a body")
if request.body is not None:
if "Content-Digest" not in request.headers:
hasher = self._digest_hashers[algorithm]
hasher = self._content_digest_hashers[self.default_content_digest_hasher]
digest = hasher(request.body).digest()
digest_node = http_sfv.Dictionary({algorithm: digest})
digest_node = http_sfv.Dictionary({self.default_content_digest_hasher: digest})
request.headers["Content-Digest"] = str(digest_node)
def get_nonce(self, request):
@ -196,11 +199,11 @@ class HTTPSignatureAuth(requests.auth.AuthBase):
A list of lowercased header names or derived component IDs (``@method``, ``@target-uri``, ``@authority``,
``@scheme``, ``@request-target``, ``@path``, ``@query``, ``@query-params``, ``@status``, or
``@request-response``, as specified in the standard) to require to be covered by the signature. If the
"content-digest" header field is specified here (recommended for messages that have a body), it will be
``content-digest`` header field is specified here (recommended for messages that have a body), it will be
verified by matching it against the digest hash computed on the body of the message (expected to be bytes).
If this parameter is not specified, ``verify()`` will set it to ("@method", "@authority", "@target-uri")
for messages without a body, and ("@method", "@authority", "@target-uri", "content-digest") for messages
If this parameter is not specified, ``verify()`` will set it to ``("@method", "@authority", "@target-uri")``
for messages without a body, and ``("@method", "@authority", "@target-uri", "content-digest")`` for messages
with a body.
:param signature_algorithm:
The algorithm expected to be used by the signature. Any signature not using the expected algorithm will
@ -257,10 +260,10 @@ class HTTPSignatureAuth(requests.auth.AuthBase):
if len(digest) < 1:
raise InvalidSignature("Found a content-digest header with no digests")
for k, v in digest.items():
if k not in cls._digest_hashers:
raise InvalidSignature(f'Unsupported digest algorithm "{k}"')
if k not in cls._content_digest_hashers:
raise InvalidSignature(f'Unsupported content digest algorithm "{k}"')
raw_digest = v.value
hasher = cls._digest_hashers[k]
hasher = cls._content_digest_hashers[k]
expect_digest = hasher(body).digest()
if raw_digest != expect_digest:
raise InvalidSignature("The content-digest header does not match the message body")

View File

@ -43,7 +43,7 @@ class TestAdapter(HTTPAdapter):
response.raw = io.BytesIO(json.dumps({}).encode())
signer = HTTPMessageSigner(signature_algorithm=self.client_auth.signer.signature_algorithm,
key_resolver=self.client_auth.signer.key_resolver)
hasher = HTTPSignatureAuth._digest_hashers["sha-256"]
hasher = HTTPSignatureAuth._content_digest_hashers["sha-256"]
digest = hasher(response.raw.getvalue()).digest()
response.headers["Content-Digest"] = str(http_sfv.Dictionary({"sha-256": digest}))
signer.sign(response,