Skip to content

Verify permit co-signatures offline (permit.co_signature.v1)#47

Merged
sftimeless merged 3 commits into
mainfrom
codex/permit-cosignature-phase1c
Jul 15, 2026
Merged

Verify permit co-signatures offline (permit.co_signature.v1)#47
sftimeless merged 3 commits into
mainfrom
codex/permit-cosignature-phase1c

Conversation

@sftimeless

Copy link
Copy Markdown
Member

Adds an adjudicator for permit.co_signature.v1, so an auditor can verify a human's passkey co-signature on a permit with Keel's servers switched off.

Verdicts

verdict when
supported pack integrity verified and the co-signer key resolved from a Keel-pinned, signed manifest and the WebAuthn assertion passes every check. Reports custody_tier=human_passkey.
insufficient_evidence pack integrity unverifiable, or the key manifest is absent / unsigned / self-signed / signed by a non-pinned key, or the co-signer key is revoked or outside its validity window.
disproved the assertion is invalid: tampered, replayed across permits, wrong key, wrong challenge, wrong origin, bad rpIdHash, missing user verification, malformed signature.
unverifiable_scope unsupported COSE algorithm or unknown claim version.

The security properties that matter

  • Trust is pinned, never taken from the pack. The co-signer key manifest's signer must resolve to a Keel-pinned trust entry with purpose permit_binding_signing, valid at signing time. If no pinned trust root is available, verification fails closed.
  • Unsigned and legacy trust manifests are refused outright for co-signatures. The verifier's older tolerance for an unsigned local manifest does not apply here.
  • The target permit is re-derived from the signed pack's own records and compared against the self-declared context; a mismatch is disproved. A caller cannot assert what the co-signature was over.
  • --allow-unsigned cannot launder a co-signature. That compatibility path still yields insufficient_evidence for this claim.

Verification

25 tests, including a cross-check of every keel-permit golden vector against this runtime (so the implementation cannot drift from the normative contract) and an end-to-end signed-pack verification with no network.

Adversarial testing was run against the trust seam with pack integrity deliberately valid, so the outer layers could not do the rejecting. All forgeries failed closed, with a passing control:

attack result
control — untampered Keel-signed pack supported
key manifest signed with an attacker's key insufficient_evidence
key_id spoof — Keel's pinned key_id, attacker's private key insufficient_evidence
co-signer public key swapped, Keel's signature left intact insufficient_evidence
full forgery — swap the key and re-sign the manifest insufficient_evidence
revoked co-signer key (validly Keel-signed) insufficient_evidence

Honest boundary

Offline verification chains to Keel's signed key manifest, and the auditor must hold Keel's pinned trust root out of band. This is not an independent or notarized co-signature — an auditor who distrusts Keel's manifest signature has no fallback. That would require an external identity root, which is out of scope.

Sequencing

🤖 Generated with Claude Code

sftimeless and others added 2 commits July 14, 2026 13:40
The conformance vectors fell back to an empty list when keel-permit was not
checked out alongside this repo. Parametrizing over an empty list collects zero
cases and passes green, so the tests proving this verifier still matches the
normative permit.co_signature.v1 contract would have silently vanished rather
than failed. A missing or empty corpus now raises.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ure-phase1c

# Conflicts:
#	keel_verifier/verifier.py
@sftimeless
sftimeless merged commit f2b730b into main Jul 15, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant