Octet.verify
On-device proof verification. Static, synchronous, offline. Verifies a LocationProof (or its raw bytes) against the keys inside it, with no network call. See On-device Verification for what it can and cannot establish.
Signature
public enum Octet {
public static func verify(_ proof: LocationProof,
options: VerifyOptions = VerifyOptions()) -> ProofVerification
public static func verify(proofBytes: Data,
options: VerifyOptions = VerifyOptions()) -> ProofVerification
}
object Octet {
fun verify(proof: LocationProof,
options: VerifyOptions = VerifyOptions()): ProofVerification
fun verify(proofBytes: ByteArray,
options: VerifyOptions = VerifyOptions()): ProofVerification
}
The two overloads are equivalent: verify(proof:) reads proof.proofBytes. A structurally undecodable input returns verdict == invalid with a wire-format FAIL, never a throw.
VerifyOptions
public struct VerifyOptions: Sendable {
public var expectedRegion: ProofRegion? // enables region checks; nil ⇒ NOT-CHECKED
public var maxAgeSeconds: TimeInterval // freshness window; default 300
public var requireAttestation: Bool // fail-closed on hardware-attestation; default false
public var attestationBundle: Data? // iOS: enrolment bundle to anchor assertion-only proofs
public init(expectedRegion: ProofRegion? = nil, maxAgeSeconds: TimeInterval = 300,
requireAttestation: Bool = false, attestationBundle: Data? = nil)
}
data class VerifyOptions(
val expectedRegion: ProofRegion? = null, // enables region checks; null ⇒ NOT-CHECKED
val maxAgeSeconds: Long = 300, // freshness window; default 300
val requireAttestation: Boolean = false, // fail-closed on hardware-attestation; default false
val attestationBundle: ByteArray? = null, // iOS: enrolment bundle to anchor assertion-only proofs
)
expectedRegionturns on theregion-claim,region-type, andcontainschecks. Whennil/null, those reportNOT-CHECKED.maxAgeSecondsis the freshness window, judged against the signed timestamp.requireAttestationadds theattestation-requiredgate: the result isFAILunlesshardware-attestationpassed.attestationBundle(iOS) is a serialized enrolment bundle (attestationEnrolmentBundle().protoData()) that anchors an assertion-only proof to Apple's App Attest root. On Android it is ignored, because every Android proof carries its own key-attestation chain.
ProofVerification
The result. A flat, log-safe record: no coordinates, no proof bytes echoed, no PII.
| Field | Type | Meaning |
|---|---|---|
verdict |
Verdict |
Tri-state headline: valid / invalid / inconclusive. |
checks |
[ProofCheck] |
Every check that ran, in recipe order. |
isValid |
Bool |
No check is FAIL. |
isAuthentic |
Bool |
isValid and stage-signatures is PASS. |
Verdict—valid/invalid/inconclusive(VALID/INVALID/INCONCLUSIVEon Android).ProofCheck—name: String,status: CheckStatus,detail: String(log-safe).nameis a stable string from the check vocabulary, aligned tooctet-verify.CheckStatus—pass/fail/warn/notChecked(PASS/FAIL/WARN/NOT_CHECKEDon Android; the wire tags arePASS/FAIL/WARN/NOT-CHECKED).
Verdict rules
These mirror octet-verify verbatim:
isValidis true when no check isFAIL.NOT-CHECKEDandWARNnever reject.isAuthenticis true whenisValidandstage-signaturesisPASS.NOT-CHECKEDnever makes a proof authentic.verdictisinvalidif any check isFAIL, elsevalidifisAuthentic, elseinconclusive.- A skipped check is always surfaced as
NOT-CHECKED, never silently aPASS.
Check taxonomy
| Check | What it confirms | On-device |
|---|---|---|
wire-format |
The bytes decode to a well-formed proof with no smuggled duplicate fields. | PASS / FAIL |
stage-signatures |
Every stage is signed by the hardware-backed P-256 key the proof carries. | PASS / FAIL |
device-attestation-sig |
The device-key signature over the proof's committed fields. | PASS / FAIL |
binding / freshness |
Field bindings match their signed hashes; the proof falls within maxAgeSeconds. |
PASS / FAIL |
region-claim / region-type / contains |
The claimed region matches expectedRegion. |
PASS / FAIL when expectedRegion set, else NOT-CHECKED |
hardware-attestation |
The device-attestation chain anchors to a bundled Apple / Google root (Tier 3). | PASS / FAIL when anchorable, else NOT-CHECKED |
attestation-required |
Fail-closed gate on hardware-attestation. |
PASS / FAIL only when requireAttestation |
| replay-uniqueness, revocation | Backend-only state. | Always NOT-CHECKED on-device |
Example
let result = Octet.verify(verdict.proof!, options: VerifyOptions(maxAgeSeconds: 120))
if result.isAuthentic {
accept(verdict.proof!)
} else {
for check in result.checks where check.status == .fail {
log("verify failed: \(check.name) — \(check.detail)")
}
}
val result = Octet.verify(verdict.proof!!, VerifyOptions(maxAgeSeconds = 120))
if (result.isAuthentic) {
accept(verdict.proof!!)
} else {
result.checks.filter { it.status == CheckStatus.FAIL }
.forEach { log("verify failed: ${it.name} — ${it.detail}") }
}
See also
- On-device Verification. What the verdict means and its limits.
- Verifying Proofs. The
octet-verifyservice and the full trust model. OctetVerdict. The proof you pass in rides onverdict.proof.