"""VerIQ — result signing (Ed25519)."""
import base64
import json
import os
from pathlib import Path
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization
KEY_PATH = Path(os.environ.get("VERIQ_KEY_PATH", "/var/lib/veriq/ed25519.key"))
_cached_priv: Ed25519PrivateKey | None = None
def _load_or_create_private_key() -> Ed25519PrivateKey:
"""Load private key from disk; generate and persist on first run."""
global _cached_priv
if _cached_priv is not None:
return _cached_priv
if KEY_PATH.exists():
raw = KEY_PATH.read_bytes()
_cached_priv = Ed25519PrivateKey.from_private_bytes(raw)
else:
_cached_priv = Ed25519PrivateKey.generate()
KEY_PATH.parent.mkdir(parents=True, exist_ok=True)
KEY_PATH.write_bytes(_cached_priv.private_bytes(
serialization.Encoding.Raw,
serialization.PrivateFormat.Raw,
serialization.NoEncryption(),
))
KEY_PATH.chmod(0o600)
return _cached_priv
def public_key_b64() -> str:
"""Base64 of raw 32-byte public key — the value published for verification."""
pub = _load_or_create_private_key().public_key()
raw = pub.public_bytes(
serialization.Encoding.Raw,
serialization.PublicFormat.Raw,
)
return base64.b64encode(raw).decode()
def canonical_payload(result_id: str, score: int, percentile: float,
bank_version: str, identity: str | None,
completed_at: str) -> str:
"""Deterministic JSON string that gets signed."""
obj = {
"result_id": result_id,
"score": score,
"percentile": percentile,
"bank_version": bank_version,
"identity": identity,
"completed_at": completed_at,
}
return json.dumps(obj, sort_keys=True, separators=(",", ":"))
def sign_payload(payload: str) -> str:
sig = _load_or_create_private_key().sign(payload.encode("utf-8"))
return base64.b64encode(sig).decode()
def verify_payload(payload: str, signature_b64: str) -> bool:
"""Verify with our own public key (server-side re-check on verify page)."""
try:
pub = _load_or_create_private_key().public_key()
pub.verify(base64.b64decode(signature_b64), payload.encode("utf-8"))
return True
except Exception:
return False