feat(admission): founder-signed trust and recovery-attempt admission (WP14 T02, WP15 T05)
Reuse the gated-executor approval pattern: ssh-keygen -Y signatures over exact bytes, verified against a runtime-unwritable allowed_signers anchor with a fixed namespace per purpose. load_trust now refuses responder trust without a founder admission; the KeyCape selector preflight gains --admission for one exact, bounded recovery attempt. WP14 re-pointed to CUST-WP-0082-T06 and split founder key admission into human-gated T04; WP15 T04 now requires the signed admission. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 809600@bnt-lap001 Assistant-Session: 40ba228f-c8a0-4ec9-bb29-3129951afdc4
This commit is contained in:
parent
497040c58c
commit
ebd8e49aff
15 changed files with 469 additions and 29 deletions
|
|
@ -17,7 +17,7 @@ from tests.test_decision_transport import server, credential, clear_proxies
|
|||
FIXTURE = Path(__file__).parent / 'fixtures/pdp-signature/decision_rotate_signed.json'
|
||||
PUBLIC = 'IVL40Zt5HSRFMkLhXy6rbLfP-ntqXtMAl5YOBpiB2xI'
|
||||
|
||||
from tests.signature_support import signature_tools
|
||||
from tests.signature_support import signature_tools, admit
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def verifier(signature_tools):
|
||||
|
|
@ -29,7 +29,7 @@ def trust_file(tmp_path):
|
|||
provenance = envelope['provenance']
|
||||
trust = {k:provenance[k] for k in ('evaluator','mode','policy_package','policy_version','policy_package_digest')}
|
||||
trust.update(schema='secrets-engine.pdp-trust.v1', keys={'testdata-ed25519':PUBLIC})
|
||||
path = tmp_path/'trust.json'; path.write_text(json.dumps(trust)); return path
|
||||
path = tmp_path/'trust.json'; path.write_text(json.dumps(trust)); return admit(path)
|
||||
|
||||
|
||||
def checked(body, credential, trust_file, verifier):
|
||||
|
|
@ -63,17 +63,17 @@ def test_response_authentication_refuses(credential, trust_file, verifier, chang
|
|||
|
||||
@pytest.mark.parametrize('field',['evaluator','mode','policy_package','policy_version','policy_package_digest'])
|
||||
def test_independent_issuer_and_pin_refusal(credential, trust_file, verifier, field):
|
||||
trust=json.loads(trust_file.read_text()); trust[field]='wrong'; trust_file.write_text(json.dumps(trust))
|
||||
trust=json.loads(trust_file.read_text()); trust[field]='wrong'; trust_file.write_text(json.dumps(trust)); admit(trust_file)
|
||||
with pytest.raises(DecisionError): checked(FIXTURE.read_bytes(),credential,trust_file,verifier)
|
||||
|
||||
|
||||
def test_rotation_overlap_then_removal(credential, trust_file, verifier, signature_tools):
|
||||
trust=json.loads(trust_file.read_text()); trust['keys']['next-admitted']='Ivwpd5Lwtv_Av8_bftsMCqFOAlo2XsDjQuhuOCnLdLY'
|
||||
trust_file.write_text(json.dumps(trust)); checked(FIXTURE.read_bytes(),credential,trust_file,verifier)
|
||||
trust_file.write_text(json.dumps(trust)); admit(trust_file); checked(FIXTURE.read_bytes(),credential,trust_file,verifier)
|
||||
from tests.signature_support import sign_envelope
|
||||
next_body=json.dumps(sign_envelope(json.loads(FIXTURE.read_bytes()),signature_tools,next_key=True)).encode()
|
||||
checked(next_body,credential,trust_file,verifier)
|
||||
del trust['keys']['testdata-ed25519']; trust_file.write_text(json.dumps(trust))
|
||||
del trust['keys']['testdata-ed25519']; trust_file.write_text(json.dumps(trust)); admit(trust_file)
|
||||
with pytest.raises(DecisionError): checked(FIXTURE.read_bytes(),credential,trust_file,verifier)
|
||||
checked(next_body,credential,trust_file,verifier)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue