net-kingdom/tests/test_keycape_attempt_receipt_verifier.py
tegwick 4c4c7c4569
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
NK-WP-0049-T33: independent attempt receipt verifier, retained location and ACME reissue step
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 696317@bnt-lap001
Assistant-Session: bab9e511-0915-45b3-926a-2fab552edff6
2026-10-23 00:22:50 +02:00

112 lines
5.2 KiB
Python

"""Synthetic tests for the independent KeyCape attempt receipt verifier; no cluster, no values."""
import copy
from datetime import datetime, timezone
import hashlib
import importlib.util
import json
from pathlib import Path
import tempfile
import unittest
SOURCE = Path(__file__).parents[1] / 'tools/bootstrap-recovery/verify-keycape-attempt-receipt.py'
spec = importlib.util.spec_from_file_location('verify_receipt', SOURCE)
v = importlib.util.module_from_spec(spec)
spec.loader.exec_module(v)
CIPHERTEXT = 'c' * 64
class Verifier(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.root = Path(self.tmp.name)
hashes = {}
for name in v.SOURCE_FILES:
(self.root / name).write_bytes(f'synthetic {name}\n'.encode())
hashes[name] = hashlib.sha256((self.root / name).read_bytes()).hexdigest()
done = {'status': 'absence_verified'}
self.receipt = {
'schema': v.SCHEMA, 'status': v.PASSED, 'provision_phase': 'complete',
**{k: True for k in v.REQUIRED_TRUE}, **{k: False for k in v.REQUIRED_FALSE},
**{k: [dict(done)] for k in v.OBLIGATIONS}, 'plaintext_cleanup_obligation': dict(done),
'ciphertext_sha256': CIPHERTEXT, 'public_signing_spki_sha256': 'a' * 64,
'source_files_sha256': hashes, 'session_source_sha256': hashes['isolated-keycape-session.py'],
'finished_at': '2026-10-24T10:00:00+00:00'}
self.transfer = {'ciphertext_sha256': CIPHERTEXT}
def tearDown(self):
self.tmp.cleanup()
def check(self, receipt=None, transfer=None, not_after=None):
raw = json.dumps(self.receipt if receipt is None else receipt).encode()
return v.verify(raw, self.transfer if transfer is None else transfer, self.root, not_after=not_after)
def mutated(self, **changes):
r = copy.deepcopy(self.receipt)
r.update(changes)
return r
def test_complete_receipt_verifies(self):
self.assertEqual(self.check(), [])
def test_refuses_failed_or_partial_outcomes(self):
cases = [self.mutated(status='failed'), self.mutated(status='bounded_cleanup_uncertain'),
self.mutated(provision_phase='issuer_launch'), self.mutated(schema='other'),
self.mutated(error='isolated_keycape_failed_private_diagnostics_suppressed')]
cases += [self.mutated(**{k: False}) for k in v.REQUIRED_TRUE]
missing = copy.deepcopy(self.receipt)
del missing['denied_scopes_refused']
cases.append(missing)
for receipt in cases:
with self.subTest(receipt={k: receipt.get(k) for k in ('status', 'provision_phase')}):
self.assertTrue(self.check(receipt))
def test_refuses_overclaims_and_uncertainty(self):
for key in v.REQUIRED_FALSE:
with self.subTest(key=key):
self.assertIn(f'{key} is not false', self.check(self.mutated(**{key: True})))
def test_refuses_open_cleanup_obligations(self):
open_row = [{'status': 'registered_before_plaintext'}]
for key in v.OBLIGATIONS:
with self.subTest(key=key):
self.assertTrue(self.check(self.mutated(**{key: open_row})))
self.assertTrue(self.check(self.mutated(plaintext_cleanup_obligation={'status': 'pending'})))
def test_binds_ciphertext_to_transfer_receipt(self):
self.assertIn('ciphertext_sha256 differs from the transfer receipt',
self.check(transfer={'ciphertext_sha256': 'd' * 64}))
self.assertIn('transfer receipt lacks ciphertext_sha256', self.check(transfer={}))
def test_recomputes_source_from_checkout(self):
(self.root / 'isolated-keycape.py').write_bytes(b'changed\n')
self.assertIn('source differs from checkout: isolated-keycape.py', self.check())
def test_session_source_must_match(self):
self.assertIn('session_source_sha256 differs', self.check(self.mutated(session_source_sha256='0' * 64)))
def test_admission_window(self):
early = datetime(2026, 10, 24, 9, 0, tzinfo=timezone.utc)
late = datetime(2026, 10, 24, 11, 0, tzinfo=timezone.utc)
self.assertIn('attempt finished after the admission window', self.check(not_after=early))
self.assertEqual(self.check(not_after=late), [])
def test_secret_shaped_content_is_refused(self):
for value in ('-----BEGIN PRIVATE KEY-----', 'AGE-SECRET-KEY-1QQQQ',
'eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzZXJ2aWNlIn0.sig', 'A' * 200):
with self.subTest(value=value[:12]):
findings = self.check(self.mutated(note=value))
self.assertIn('secret-shaped content present; do not retain or publish this receipt', findings)
def test_cli_exit_codes(self):
receipt, transfer = self.root / 'recovery.json', self.root / 'transfer.json'
receipt.write_text(json.dumps(self.receipt))
transfer.write_text(json.dumps(self.transfer))
args = ['--receipt', str(receipt), '--transfer-receipt', str(transfer), '--source-root', str(self.root)]
self.assertEqual(v.main(args), 0)
receipt.write_text(json.dumps(self.mutated(status='failed')))
self.assertEqual(v.main(args), 1)
if __name__ == '__main__':
unittest.main()