Clean-room reference implementation (Ed25519 certificate chain, AES-256-GCM envelope, three-rung revocation ladder, crypto-erasure) with runnable examples and 36 tests that reproduce each demonstrated claim on stand-in payloads. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
178 lines
6.2 KiB
Python
178 lines
6.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Claim 3: the three-rung revocation ladder on an encrypted capability unit.
|
|
|
|
The payload is a STAND-IN blob, not a real model. The three rungs, weakest to
|
|
strongest:
|
|
|
|
(a) SOFT / advisory: a revocation-list entry; verifiers report revoked; the
|
|
ciphertext and its key still exist. Reversible.
|
|
(b) ACCESS-GATED: the authority withholds the wrapping key; decryption is
|
|
denied; recoverable if the authority re-releases.
|
|
(c) CRYPTO-ERASURE: the wrapping key is destroyed; decryption is
|
|
permanently impossible. Irreversible.
|
|
"""
|
|
|
|
import hashlib
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
|
|
from capability_licensing import (
|
|
CertificateAuthority,
|
|
KeyAuthority,
|
|
KeyDestroyedError,
|
|
KeyState,
|
|
KeyWithheldError,
|
|
LicenceInvalidError,
|
|
RevocationList,
|
|
RUNG_CRYPTO_ERASURE,
|
|
verify_chain,
|
|
)
|
|
|
|
failures = []
|
|
|
|
|
|
def check(label: str, condition: bool, evidence: str) -> None:
|
|
marker = "ok " if condition else "FAIL"
|
|
print(f" [{marker}] {label}")
|
|
print(f" {evidence}")
|
|
if not condition:
|
|
failures.append(label)
|
|
|
|
|
|
def main() -> int:
|
|
print("=== Claim 3: three-rung revocation on an encrypted capability unit ===\n")
|
|
|
|
authority = CertificateAuthority()
|
|
root = authority.create_root("Reference Root Authority")
|
|
organisation = authority.issue_organisation(root.serial, "Reference Research Organisation")
|
|
licence, _licence_key = authority.issue_capability(
|
|
organisation.serial,
|
|
"demonstration-capability",
|
|
claims={"grant": "research demonstration only"},
|
|
)
|
|
|
|
revocations = RevocationList()
|
|
keys = KeyAuthority(
|
|
trusted_roots=[root],
|
|
intermediates=[organisation],
|
|
revocation_list=revocations,
|
|
)
|
|
|
|
payload = b"STAND-IN CAPABILITY UNIT (not a real model)\n" + os.urandom(64 * 1024)
|
|
payload_digest = hashlib.sha256(payload).hexdigest()
|
|
sealed = keys.seal_unit("unit-demo-001", payload, licence.serial)
|
|
print(f" stand-in payload: {len(payload):,} bytes, sha256 {payload_digest[:16]}...")
|
|
print(f" sealed unit: {sealed.ciphertext_size():,} bytes AES-256-GCM ciphertext, "
|
|
f"licensed to {licence.serial}\n")
|
|
|
|
# Baseline: a valid licence decrypts.
|
|
plaintext = keys.request_decrypt(sealed, licence)
|
|
check(
|
|
"baseline: a valid licence decrypts the unit",
|
|
hashlib.sha256(plaintext).hexdigest() == payload_digest,
|
|
"decrypted plaintext digest matches the original payload",
|
|
)
|
|
|
|
# ---- Rung (a): SOFT / advisory -------------------------------------
|
|
print("\n --- rung (a): SOFT (advisory revocation-list entry) ---")
|
|
revocations.revoke(licence.serial, "licence terms breached")
|
|
chain_check = verify_chain(
|
|
licence, [organisation], [root], revocation_list=revocations
|
|
)
|
|
check(
|
|
"verification reports the certificate revoked",
|
|
not chain_check.valid and "revoked" in chain_check.reason,
|
|
f"verify_chain: INVALID - {chain_check.reason}",
|
|
)
|
|
denied_soft = None
|
|
try:
|
|
keys.request_decrypt(sealed, licence)
|
|
except LicenceInvalidError as exc:
|
|
denied_soft = exc
|
|
check(
|
|
"a compliant key authority honours the list and refuses decryption",
|
|
denied_soft is not None,
|
|
f"refused with LicenceInvalidError: {denied_soft}",
|
|
)
|
|
check(
|
|
"advisory honesty: the ciphertext and wrapping key still exist",
|
|
keys.key_state(sealed.unit_id) is KeyState.RELEASED,
|
|
"key state is still 'released'; enforcement rests on verifiers honouring "
|
|
"the list, not on the cryptography",
|
|
)
|
|
revocations.reinstate(licence.serial)
|
|
recovered_soft = keys.request_decrypt(sealed, licence)
|
|
check(
|
|
"soft revocation is reversible: reinstating restores decryption",
|
|
hashlib.sha256(recovered_soft).hexdigest() == payload_digest,
|
|
"after reinstatement the same licence decrypts again",
|
|
)
|
|
|
|
# ---- Rung (b): ACCESS-GATED ----------------------------------------
|
|
print("\n --- rung (b): ACCESS-GATED (wrapping key withheld) ---")
|
|
keys.withhold(sealed.unit_id)
|
|
denied_gated = None
|
|
try:
|
|
keys.request_decrypt(sealed, licence)
|
|
except KeyWithheldError as exc:
|
|
denied_gated = exc
|
|
check(
|
|
"with the key withheld, decryption is denied even for a valid licence",
|
|
denied_gated is not None,
|
|
f"refused with KeyWithheldError: {denied_gated}",
|
|
)
|
|
keys.re_release(sealed.unit_id)
|
|
recovered_gated = keys.request_decrypt(sealed, licence)
|
|
check(
|
|
"access-gating is recoverable: re-release restores decryption",
|
|
hashlib.sha256(recovered_gated).hexdigest() == payload_digest,
|
|
"after re-release the same licence decrypts again",
|
|
)
|
|
|
|
# ---- Rung (c): CRYPTO-ERASURE --------------------------------------
|
|
print("\n --- rung (c): CRYPTO-ERASURE (wrapping key destroyed) ---")
|
|
keys.destroy(sealed.unit_id)
|
|
revocations.revoke(
|
|
licence.serial, "capability unit crypto-erased", rung=RUNG_CRYPTO_ERASURE
|
|
)
|
|
denied_erased = None
|
|
try:
|
|
keys.request_decrypt(sealed, licence)
|
|
except KeyDestroyedError as exc:
|
|
denied_erased = exc
|
|
check(
|
|
"after erasure, every decrypt request fails permanently",
|
|
denied_erased is not None,
|
|
f"refused with KeyDestroyedError: {denied_erased}",
|
|
)
|
|
unrecoverable = None
|
|
try:
|
|
keys.re_release(sealed.unit_id)
|
|
except KeyDestroyedError as exc:
|
|
unrecoverable = exc
|
|
check(
|
|
"erasure is irreversible: the authority cannot re-release a destroyed key",
|
|
unrecoverable is not None,
|
|
f"re-release refused with KeyDestroyedError: {unrecoverable}",
|
|
)
|
|
|
|
print()
|
|
print(" ladder asymmetry, stated honestly: rungs (a) and (b) are reversible")
|
|
print(" administrative states; only rung (c) is final at the level of the")
|
|
print(" cryptography itself.")
|
|
print()
|
|
if failures:
|
|
print(f"RESULT: FAIL - {len(failures)} check(s) did not behave as claimed")
|
|
return 1
|
|
print(
|
|
"RESULT: PASS - soft flags and is honoured, access-gating denies and "
|
|
"recovers, crypto-erasure denies permanently"
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|