Docs/API Reference/Octet.verify

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
)
  • expectedRegion turns on the region-claim, region-type, and contains checks. When nil/null, those report NOT-CHECKED.
  • maxAgeSeconds is the freshness window, judged against the signed timestamp.
  • requireAttestation adds the attestation-required gate: the result is FAIL unless hardware-attestation passed.
  • 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.
  • Verdictvalid / invalid / inconclusive (VALID / INVALID / INCONCLUSIVE on Android).
  • ProofCheckname: String, status: CheckStatus, detail: String (log-safe). name is a stable string from the check vocabulary, aligned to octet-verify.
  • CheckStatuspass / fail / warn / notChecked (PASS / FAIL / WARN / NOT_CHECKED on Android; the wire tags are PASS / FAIL / WARN / NOT-CHECKED).

Verdict rules

These mirror octet-verify verbatim:

  • isValid is true when no check is FAIL. NOT-CHECKED and WARN never reject.
  • isAuthentic is true when isValid and stage-signatures is PASS. NOT-CHECKED never makes a proof authentic.
  • verdict is invalid if any check is FAIL, else valid if isAuthentic, else inconclusive.
  • A skipped check is always surfaced as NOT-CHECKED, never silently a PASS.

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