secrets-engine/src/secrets_engine/application_time.py
tegwick f4b4dd6b17
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
Use bounded Railiance time for protected validity checks
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
2026-09-15 22:16:31 +02:00

47 lines
2.2 KiB
Python

"""Consumer-owned bounded time adapter; explicit opt-in, no configured fallback."""
from datetime import datetime, timezone, timedelta
from functools import lru_cache
from secrets_engine.errors import DecisionError
@lru_cache(maxsize=8)
def _clock(path):
try:
from railiance_clock.client import Clock, FileTrust
return Clock(FileTrust(path))
except (ImportError, OSError, ValueError, TypeError, KeyError) as exc:
raise DecisionError("Railiance clock unavailable or untrusted") from exc
def read_window(cfg):
path = getattr(cfg, "clock_trust_file", None)
if not path:
return None
try:
return _clock(str(path)).read()
except (OSError, ValueError) as exc:
raise DecisionError("Railiance clock unavailable or untrusted") from exc
def validity_bounds(now=None, window=None):
if window is None:
current = (now or datetime.now(timezone.utc)).astimezone(timezone.utc)
return current, current
if now is not None:
raise DecisionError("choose interval evidence or explicit test time, not both")
try:
lower, upper = window.lower_ns, window.upper_ns
if type(lower) is not int or type(upper) is not int or not 0 <= lower <= upper:
raise ValueError("invalid interval")
epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
return epoch + timedelta(microseconds=lower // 1000), epoch + timedelta(microseconds=(upper + 999) // 1000)
except (ValueError, TypeError, AttributeError, OverflowError) as exc:
raise DecisionError("invalid Railiance time interval") from exc
def recheck_binding(cfg, binding):
if not getattr(cfg, "clock_trust_file", None):
return
if not binding.decision_expires_at:
raise DecisionError("bounded-time consume requires decision validity")
lower, upper = validity_bounds(window=read_window(cfg))
expires = datetime.fromisoformat(binding.decision_expires_at.replace("Z", "+00:00"))
start = datetime.fromisoformat(binding.decision_not_before.replace("Z", "+00:00")) if binding.decision_not_before else None
if upper >= expires or (start is not None and lower < start):
raise DecisionError("decision validity does not contain the Railiance interval")