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>
97 lines
3.2 KiB
Python
97 lines
3.2 KiB
Python
"""Claim 1: the certificate-chain authority behaves as stated."""
|
|
|
|
import dataclasses
|
|
from datetime import timedelta
|
|
|
|
import pytest
|
|
|
|
from capability_licensing import CertificateAuthority, verify_chain
|
|
from capability_licensing.certificates import parse_iso
|
|
|
|
|
|
def test_intact_chain_verifies_to_trusted_root(chain):
|
|
result = verify_chain(chain.capability, [chain.organisation], [chain.root])
|
|
assert result.valid
|
|
assert result.path == (
|
|
chain.capability.serial,
|
|
chain.organisation.serial,
|
|
chain.root.serial,
|
|
)
|
|
|
|
|
|
def test_root_is_self_signed_and_verifies_alone(chain):
|
|
result = verify_chain(chain.root, [], [chain.root])
|
|
assert result.valid
|
|
|
|
|
|
def test_tampered_certificate_fails(chain):
|
|
tampered = dataclasses.replace(
|
|
chain.capability,
|
|
claims={**dict(chain.capability.claims), "grant": "unlimited production use"},
|
|
)
|
|
result = verify_chain(tampered, [chain.organisation], [chain.root])
|
|
assert not result.valid
|
|
assert "does not verify" in result.reason
|
|
|
|
|
|
def test_tampered_subject_fails(chain):
|
|
tampered = dataclasses.replace(chain.capability, subject="renamed-capability")
|
|
result = verify_chain(tampered, [chain.organisation], [chain.root])
|
|
assert not result.valid
|
|
|
|
|
|
def test_tampered_intermediate_fails(chain):
|
|
tampered_org = dataclasses.replace(
|
|
chain.organisation, subject="Hostile Organisation"
|
|
)
|
|
result = verify_chain(chain.capability, [tampered_org], [chain.root])
|
|
assert not result.valid
|
|
|
|
|
|
def test_expired_certificate_fails(chain):
|
|
after_expiry = parse_iso(chain.capability.not_after) + timedelta(days=1)
|
|
result = verify_chain(
|
|
chain.capability, [chain.organisation], [chain.root], at=after_expiry
|
|
)
|
|
assert not result.valid
|
|
assert "expired" in result.reason
|
|
|
|
|
|
def test_not_yet_valid_certificate_fails(chain):
|
|
before_validity = parse_iso(chain.capability.not_before) - timedelta(days=1)
|
|
result = verify_chain(
|
|
chain.capability, [chain.organisation], [chain.root], at=before_validity
|
|
)
|
|
assert not result.valid
|
|
assert "not yet valid" in result.reason
|
|
|
|
|
|
def test_chain_to_untrusted_root_fails(chain):
|
|
other = CertificateAuthority()
|
|
other_root = other.create_root("Unaccredited Authority")
|
|
other_org = other.issue_organisation(other_root.serial, "Unaccredited Organisation")
|
|
other_leaf, _ = other.issue_capability(other_org.serial, "impostor", claims={})
|
|
result = verify_chain(other_leaf, [other_org, other_root], [chain.root])
|
|
assert not result.valid
|
|
assert "not in the trust store" in result.reason
|
|
|
|
|
|
def test_missing_intermediate_fails(chain):
|
|
result = verify_chain(chain.capability, [], [chain.root])
|
|
assert not result.valid
|
|
assert "issuer not found" in result.reason
|
|
|
|
|
|
def test_capability_may_not_issue_certificates(chain):
|
|
with pytest.raises(ValueError, match="role violation"):
|
|
chain.authority._issue(
|
|
issuer_serial=chain.capability.serial,
|
|
subject="illicit-sub-capability",
|
|
role="capability",
|
|
claims={},
|
|
lifetime_days=1,
|
|
)
|
|
|
|
|
|
def test_authority_does_not_retain_leaf_keys(chain):
|
|
assert chain.capability.serial not in chain.authority._signing_keys
|