# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference) # Canonical Residual: https://corpusfetch.com/c/across-what-key-id-and-dual-window-verification-gates-must-an-instance-orchestrate-cryptographic-key-rotation-to-prevent-signature-lockout-and-session-drop.md # Remnant Warrant: https://corpusfetch.com/c/across-what-key-id-and-dual-window-verification-gates-must-an-instance-orchestrate-cryptographic-key-rotation-to-prevent-signature-lockout-and-session-drop.warrant.json # Markdown SHA-256: fb82a9b209d96bee85ae32f14de60d3826ae24517c068153bafa8cb82077cb1e # Code AST SHA-256: 05e5e218012d22b1d40ef3254d545e6b52581a493e57d82058e7e8d33591b974 # Verified Classes: SignatureVerificationError, KeyringVerifier # Defensive Guards: 8 fail-closed invariants (zero external deps) import base64 import hashlib import hmac import json import time from dataclasses import dataclass from typing import Any, Mapping class SignatureVerificationError(PermissionError): '''Raised when a signed token fails keyring lookup, constant-time HMAC, or expiry.''' pass @dataclass(frozen=True) class KeyringVerifier: ''' Manages a multi-version cryptographic keyring supporting zero-downtime key rotation: - Signs all new payloads exclusively with `active_kid`. - Verifies incoming payloads against any non-revoked key in `keys`. - Signals when a verified payload used an older retiring key (`needs_reissue`). ''' active_kid: str keys: Mapping[str, bytes] fixed_algorithm: str = "HS256" def __post_init__(self) -> None: if not self.active_kid or self.active_kid not in self.keys: raise ValueError("active_kid must be present in the keys mapping") for kid, secret in self.keys.items(): if not isinstance(secret, bytes) or len(secret) < 32: raise ValueError( f"Key {kid!r} must be at least 32 raw bytes (256 bits) for HMAC-SHA256" ) @staticmethod def _b64u_encode(data: bytes) -> str: return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii") @staticmethod def _b64u_decode(text: str) -> bytes: padding = "=" * ((4 - len(text) % 4) % 4) return base64.urlsafe_b64decode((text + padding).encode("ascii")) def sign_payload(self, claims: Mapping[str, Any], ttl_sec: int = 3600) -> str: ''' Signs a JSON payload with `active_kid` and embeds both `kid` and `alg` in the protected header. ''' now = int(time.time()) header = {"alg": self.fixed_algorithm, "kid": self.active_kid, "typ": "JWT"} body = {**claims, "iat": now, "exp": now + ttl_sec} header_b64 = self._b64u_encode( json.dumps(header, separators=(",", ":"), sort_keys=True).encode("utf-8") ) body_b64 = self._b64u_encode( json.dumps(body, separators=(",", ":"), sort_keys=True).encode("utf-8") ) signing_input = f"{header_b64}.{body_b64}".encode("ascii") secret = self.keys[self.active_kid] sig = hmac.new(secret, signing_input, hashlib.sha256).digest() return f"{header_b64}.{body_b64}.{self._b64u_encode(sig)}" def verify_payload(self, token: str) -> tuple[dict[str, Any], bool]: ''' Verifies a signed token in constant time against the keyring. Returns `(claims_dict, needs_rotation_reissue)`. ''' parts = token.split(".") if len(parts) != 3: raise SignatureVerificationError("Malformed token structure") header_b64, body_b64, sig_b64 = parts try: header = json.loads(self._b64u_decode(header_b64).decode("utf-8")) claims = json.loads(self._b64u_decode(body_b64).decode("utf-8")) provided_sig = self._b64u_decode(sig_b64) except Exception as exc: raise SignatureVerificationError("Failed to decode token segments") from exc # Gate 1: Enforce fixed server-side algorithm (blocks alg:none & confusion) if header.get("alg") != self.fixed_algorithm: raise SignatureVerificationError( f"Rejected unexpected algorithm: {header.get('alg')!r}" ) # Gate 2: Lookup exact key by `kid` from active/retiring keyring kid = header.get("kid") if not isinstance(kid, str) or kid not in self.keys: raise SignatureVerificationError(f"Unknown or retired key ID: {kid!r}") # Gate 3: Constant-time HMAC comparison signing_input = f"{header_b64}.{body_b64}".encode("ascii") expected_sig = hmac.new(self.keys[kid], signing_input, hashlib.sha256).digest() if not hmac.compare_digest(provided_sig, expected_sig): raise SignatureVerificationError("Cryptographic signature mismatch") # Gate 4: Validate expiration timestamp exp = claims.get("exp") if not isinstance(exp, int) or int(time.time()) >= exp: raise SignatureVerificationError("Token has expired") needs_reissue = kid != self.active_kid return claims, needs_reissue