Coverage for astrocyte/ingest/hmac_auth.py: 100%
17 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-04 05:24 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-04 05:24 +0000
1"""HMAC signature helpers for webhook ingest (M4)."""
3from __future__ import annotations
5import hashlib
6import hmac
9def compute_hmac_sha256_hex(secret: str, body: bytes) -> str:
10 """Return lowercase hex digest of HMAC-SHA256(secret, body)."""
11 return hmac.new(secret.encode("utf-8"), body, hashlib.sha256).hexdigest()
14def normalize_signature_header(value: str) -> str:
15 """Strip ``sha256=`` / ``sha1=`` prefix if present (GitHub-style)."""
16 v = value.strip()
17 for prefix in ("sha256=", "sha1="):
18 if v.lower().startswith(prefix):
19 return v.split("=", 1)[1].strip()
20 return v
23def verify_hmac_sha256(secret: str, body: bytes, signature_header: str) -> bool:
24 """Constant-time compare of computed HMAC to header value (hex)."""
25 expected = compute_hmac_sha256_hex(secret, body)
26 got = normalize_signature_header(signature_header)
27 if len(got) != len(expected):
28 return False
29 return hmac.compare_digest(expected.lower(), got.lower())