trading-system-docs/tolaria/c5-sync-service/approval_signature.py
Red Queen 9c8d239ae5 AUTH.3D: Human DELETE Approval Authenticity (Ed25519, verify-only)
- approval_payload: kanonischer, deterministischer Approval-Payload
- approval_signature: Ed25519 sign/verify (Christian=Private Key, Executor=Public Key only)
- approval_verifier: verify-only, alle Bindings fail-closed
- approval_state: Lifecycle CREATED->CONSUMED, Single-Use, Reservation, OUTCOME_UNKNOWN
- test_approval_auth3d: T1-T30 + adversarial (50 Tests)
- test_approval_helpers: synthetische Test-Keypairs
- sensitivity_proof: Mutationen A-J machen Tests ROT

Nur synthetische Test-Keypairs. Kein produktives Deployment.
DELETE_OPERATION_ACTIVATION bleibt BLOCKED bis AUTH.3D geprueft.
2026-08-27 09:36:21 +00:00

124 lines
3.9 KiB
Python

"""
AUTH.3D — approval_signature.py
Ed25519 Signatur (RFC 8032) für Human DELETE Approvals.
- Christian besitzt den PRIVATE KEY (NIE im Stack).
- DELETE Executor kennt nur den PUBLIC KEY (verify-only).
- RQ/Hermes/SAVE/DELETE-Executor besitzen KEINEN Private Key.
NUR isolierter Code + Tests mit synthetischen Test-Keypairs.
KEIN echter Key wird erzeugt. KEIN produktives Deployment.
"""
from __future__ import annotations
import base64
import hashlib
from typing import Optional, Tuple
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import (
Ed25519PrivateKey,
Ed25519PublicKey,
)
from approval_payload import canonicalize
# Signaturformat: Ed25519 (64 Bytes), Base64-URL-encoded.
SIGNATURE_ALGORITHM = "Ed25519"
class ApprovalSignatureError(ValueError):
"""Fehler bei Signatur-Erzeugung/Verifikation."""
def generate_test_keypair() -> Tuple[Ed25519PrivateKey, Ed25519PublicKey]:
"""
Erzeugt ein SYNTHETISCHES Test-Keypair (NUR für Tests).
NIEMALS für produktive Approvals verwenden. Kein echter Key.
"""
private = Ed25519PrivateKey.generate()
return private, private.public_key()
def key_id(public_key: Ed25519PublicKey) -> str:
"""KEY_ID = SHA-256 über die Public-Key-Bytes (hex, kurz)."""
raw = public_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)
return hashlib.sha256(raw).hexdigest()
def sign(private_key: Ed25519PrivateKey, payload: dict) -> str:
"""
Signiert den kanonisierten Payload mit dem Private Key.
Gibt die Signatur als Base64-URL-String zurück.
"""
canonical = canonicalize(payload)
sig = private_key.sign(canonical)
return base64.urlsafe_b64encode(sig).decode("ascii")
def verify(public_key: Ed25519PublicKey, payload: dict, signature: str) -> bool:
"""
Verifiziert die Signatur über den kanonisierten Payload.
Gibt True bei gültiger Signatur, False bei ungültiger/malformed.
FAIL CLOSED: jede Abweichung -> False.
"""
if not isinstance(signature, str) or not signature:
return False
try:
sig_bytes = base64.urlsafe_b64decode(signature.encode("ascii"))
except Exception:
return False
if len(sig_bytes) != 64:
return False
try:
canonical = canonicalize(payload)
except Exception:
return False
try:
public_key.verify(sig_bytes, canonical)
return True
except InvalidSignature:
return False
except Exception:
return False
def public_key_to_pem(public_key: Ed25519PublicKey) -> bytes:
"""Serialisiert den Public Key zu PEM (für Verifier-Konfiguration)."""
return public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
def public_key_from_pem(pem: bytes) -> Ed25519PublicKey:
"""Lädt einen Public Key aus PEM."""
try:
return serialization.load_pem_public_key(pem) # type: ignore[return-value]
except Exception as e:
raise ApprovalSignatureError(f"Ungültiger Public Key PEM: {e}") from e
def private_key_to_pem(private_key: Ed25519PrivateKey) -> bytes:
"""Serialisiert den Private Key zu PEM (NUR für Christians Gerät / Tests)."""
return private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
)
def private_key_from_pem(pem: bytes) -> Ed25519PrivateKey:
"""Lädt einen Private Key aus PEM (NUR Christians Gerät / Tests)."""
try:
return serialization.load_pem_private_key(pem, password=None) # type: ignore[return-value]
except Exception as e:
raise ApprovalSignatureError(f"Ungültiger Private Key PEM: {e}") from e