Docs/Concepts/On-device Verification

On-device Verification

In short

Octet.verify checks a proof on the device, offline, with no network call. It runs the same checks as the octet-verify command-line tool and reports the same tri-state verdict.

A proof of location is verified by re-checking its signatures against the keys inside it. octet-verify does that as a standalone tool. Octet.verify (new in 2.0) does the same work in-process, so an app can check a proof it just produced, or one it received, without a round trip.

What it runs

Octet.verify runs the real cryptographic core, not a stub: ECDSA P-256 stage-signature verification, plus the structural, binding, freshness, region, and wire-format self-consistency checks. That is enough to catch a tampered field, an edited region, a stale proof, or a forged signature, all offline. With a hardware-attestation bundle it also anchors the device attestation to the bundled Apple and Google roots (Tier 3).

Two checks depend on state only the backend holds, so on-device they always report NOT-CHECKED, never a pass:

  • Cross-proof replay-uniqueness. Whether this proof's nullifier was seen before needs the backend's record of every proof.
  • Revocation. Whether the signing key was revoked needs the backend's revocation list.

For those, verify a proof against the octet-verify service as the authoritative check. Octet.verify is the fast local gate.

The verdict

Octet.verify returns a ProofVerification with a tri-state verdict and two booleans:

Field Meaning
verdict valid, invalid, or inconclusive.
isValid No check is FAIL. NOT-CHECKED and WARN never reject.
isAuthentic isValid and stage-signatures is PASS: the signatures actually verified, not merely went un-failed.

The rules mirror octet-verify exactly, including the anti-fail-open invariant: a check that could not run is reported as NOT-CHECKED, and a report full of NOT-CHECKED items can never read as authentic.

  • verdict is invalid if any check is FAIL.
  • Otherwise valid if the proof is authentic.
  • Otherwise inconclusive: the structure passed but the signatures were not verified (for example, an iOS proof with no embedded certificate and no attestation bundle supplied).

Read isAuthentic when you need the cryptographic guarantee. Reading verdict == valid alone accepts an inconclusive proof.

Relationship to octet-verify

Octet.verify and octet-verify share one check recipe and one verdict model, so a proof verdicts the same way in both. Use the on-device call for an immediate local gate on the phone. Use the octet-verify service or CLI off-device when you need the backend-only checks (replay-uniqueness, revocation) or an audit trail independent of the app.

Where to go next