diff --git a/README.rst b/README.rst index 0af5a6b..662333b 100644 --- a/README.rst +++ b/README.rst @@ -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 ----- diff --git a/requests_http_signature/__init__.py b/requests_http_signature/__init__.py index 27ab8b7..338ce1c 100644 --- a/requests_http_signature/__init__.py +++ b/requests_http_signature/__init__.py @@ -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") diff --git a/test/test.py b/test/test.py index 03fd779..bc38058 100755 --- a/test/test.py +++ b/test/test.py @@ -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,