"""Token minting + round-trip verification for all three PoC token types.""" from __future__ import annotations import pytest from poc.keys import build_ect_key_resolver, build_key_registry from poc.tokens import mint_ect, mint_exec_record, mint_mandate from act.crypto import ACTKeyResolver from act.errors import ACTError from act.verify import ACTVerifier from ect.verify import verify as ect_verify, VerifyOptions SERVER = "mcp-server" def _act_verifier(identities) -> ACTVerifier: reg = build_key_registry(identities) return ACTVerifier( ACTKeyResolver(registry=reg), verifier_id=SERVER, trusted_issuers={i.name for i in identities.values()}, ) def test_mandate_round_trips(identities): m = mint_mandate( user=identities["user"], agent=identities["agent"], audience=SERVER, purpose="research task", ) v = _act_verifier(identities).verify_mandate(m.compact, check_sub=False) assert v.jti == m.mandate.jti assert v.iss == "user" assert v.sub == "agent" assert {c.action for c in v.cap} >= {"mcp.search", "mcp.summarize"} def test_record_preserves_mandate_jti(identities): """ACT §3.2: Phase 2 record carries the mandate's jti.""" m = mint_mandate( user=identities["user"], agent=identities["agent"], audience=SERVER, purpose="research task", ) rec = mint_exec_record( agent=identities["agent"], mandate=m.mandate, exec_act="mcp.search", pred_jtis=[], inp_body=b"input", out_body=b"output", ) assert rec.record.jti == m.mandate.jti vr = _act_verifier(identities).verify_record(rec.compact) assert vr.jti == m.mandate.jti assert vr.exec_act == "mcp.search" assert vr.status == "completed" def test_record_rejects_unauthorised_exec_act(identities): """Verifier must raise ACTCapabilityError when exec_act ∉ cap.""" from act.errors import ACTCapabilityError from act.token import Capability m = mint_mandate( user=identities["user"], agent=identities["agent"], audience=SERVER, purpose="p", ) # Narrow the mandate to only mcp.search so mcp.summarize is unauthorised. m.mandate.cap = [Capability(action="mcp.search")] # Build the record locally so we can bypass the local validate() guard # and produce a compact that only the verifier can spot as malformed. rec = mint_exec_record( agent=identities["agent"], mandate=m.mandate, exec_act="mcp.search", pred_jtis=[], inp_body=b"i", out_body=b"o", ) # Swap exec_act *after* signing to simulate a forged record. The # verifier should reject it on capability-consistency grounds (ACT §7.1). import act.crypto as _crypto from act.token import encode_jws rec.record.exec_act = "mcp.summarize" rec.record.cap = [Capability(action="mcp.search")] tampered = encode_jws( rec.record, _crypto.sign(identities["agent"].private_key, rec.record.signing_input()), ) with pytest.raises(ACTCapabilityError): _act_verifier(identities).verify_record(tampered) def test_ect_round_trips(identities): et = mint_ect( agent=identities["agent"], audience=SERVER, exec_act="mcp.search", pred_jtis=["some-prior-jti"], inp_body=b'{"query":"x"}', ) parsed = ect_verify( et.compact, VerifyOptions( verifier_id=SERVER, resolve_key=build_ect_key_resolver(identities), ), ) assert parsed.payload.iss == "agent" assert parsed.payload.exec_act == "mcp.search" assert parsed.payload.pred == ["some-prior-jti"] assert parsed.payload.inp_hash # present def test_wrong_audience_rejected_by_act_verifier(identities): m = mint_mandate( user=identities["user"], agent=identities["agent"], audience="some-other-workload", purpose="p", ) # mcp-server is not the mandate's aud → verifier MUST refuse. verifier = _act_verifier(identities) with pytest.raises(ACTError): verifier.verify_mandate(m.compact, check_sub=False)