Skip to content

Commit e084933

Browse files
committed
incorporated the feedback from the 1st review
1 parent 44d63e3 commit e084933

2 files changed

Lines changed: 242 additions & 333 deletions

File tree

apimatic_core/security/signature_verification/hmac_signature_verifier.py

Lines changed: 17 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# hmac_signature_verifier.py
21
import hmac
32
import hashlib
43
from typing import Callable, Optional, Union
@@ -41,7 +40,7 @@ class HmacSignatureVerifier(SignatureVerifier):
4140
4241
Parameters
4342
----------
44-
key : str
43+
secret_key : str
4544
Shared secret used for HMAC.
4645
signature_header : str
4746
Header name containing the provided signature (case-insensitive lookup).
@@ -60,41 +59,41 @@ class HmacSignatureVerifier(SignatureVerifier):
6059
def __init__(
6160
self,
6261
*,
63-
key: str,
62+
secret_key: str,
6463
signature_header: str,
6564
canonical_message_builder: Optional[Callable[[Request], Union[bytes, str, None]]] = None,
6665
hash_alg=hashlib.sha256,
67-
encoder: Optional[DigestEncoder] = None,
68-
signature_value_template: Optional[str] = None,
66+
encoder: Optional[DigestEncoder] = HexEncoder(),
67+
signature_value_template: Optional[str] = '{digest}',
6968
) -> None:
70-
if not isinstance(key, str) or not key:
71-
raise ValueError("key must be a non-empty string.")
69+
if not isinstance(secret_key, str) or not secret_key:
70+
raise ValueError("key must be a non-empty string")
7271
if not isinstance(signature_header, str) or not signature_header.strip():
73-
raise ValueError("signature_header must be a non-empty string.")
72+
raise ValueError("signature_header must be a non-empty string")
7473

75-
self._key_bytes = key.encode("utf-8")
74+
self._secret_key = secret_key
7675
self._signature_header_lc = signature_header.lower().strip()
7776
self._message_resolver = canonical_message_builder
7877
self._hash_alg = hash_alg
79-
self._encoder = encoder or HexEncoder()
78+
self._encoder = encoder
8079
self._signature_value_template = signature_value_template
8180

8281
def verify(self, request: Request) -> SignatureVerificationResult:
8382
try:
8483
provided = self._read_signature_header(request)
8584
if provided is None:
8685
return SignatureVerificationResult.failed(
87-
ValueError(f"Signature header '{self._signature_header_lc}' is missing.")
86+
ValueError(f"Signature header '{self._signature_header_lc}' is missing")
8887
)
8988

9089
message_bytes = self._resolve_message_bytes(request)
91-
digest = hmac.new(self._key_bytes, message_bytes, self._hash_alg).digest()
90+
digest = hmac.new(self._secret_key.encode("utf-8"), message_bytes, self._hash_alg).digest()
9291
encoded_digest = self._encoder.encode(digest)
93-
expected = self._wrap_expected_signature(encoded_digest)
92+
expected = self._signature_value_template.replace("{digest}", encoded_digest)
9493

9594
is_match = hmac.compare_digest(provided, expected)
9695
return SignatureVerificationResult.passed() if is_match else SignatureVerificationResult.failed(
97-
SignatureVerificationError("Signature mismatch.")
96+
SignatureVerificationError("Signature mismatch")
9897
)
9998
except Exception as exc:
10099
return SignatureVerificationResult.failed(
@@ -109,28 +108,7 @@ def _read_signature_header(self, request: Request) -> Optional[str]:
109108
return None if value is None or value.strip() == "" else value
110109

111110
def _resolve_message_bytes(self, request: Request) -> bytes:
112-
"""
113-
Resolve the message to be signed:
114-
- If message_resolver is provided and returns bytes/str -> use it.
115-
If it returns None -> fall back to raw/text body.
116-
- If no resolver provided -> fall back to raw/text body.
117-
"""
118-
if callable(self._message_resolver):
119-
resolved = self._message_resolver(request)
120-
if isinstance(resolved, bytes):
121-
return resolved
122-
if isinstance(resolved, str):
123-
return resolved.encode("utf-8")
124-
# resolved is None -> fall through to default
125-
# default fallback: prefer raw_body, else textual body
126-
raw = getattr(request, "raw_body", None)
127-
if isinstance(raw, (bytes, bytearray)):
128-
return bytes(raw)
129-
body = getattr(request, "body", None)
130-
return (body or "").encode("utf-8")
131-
132-
def _wrap_expected_signature(self, encoded_digest: str) -> str:
133-
template = self._signature_value_template
134-
if not template:
135-
return encoded_digest
136-
return template.replace("{digest}", encoded_digest) if "{digest}" in template else template
111+
if self._message_resolver is None:
112+
return request.raw_body
113+
114+
return self._message_resolver(request)

0 commit comments

Comments
 (0)