Proof of Location
A proof of location is a signed, time-stamped record that a device was inside a region at a given time, and anyone holding it can verify the signatures offline.
For example, before your backend lets a user trade, it can require a proof that the phone is in the US. Your app asks the SDK, and a YES or NO comes back with a proof attached:
let v = await sdk.loc.isWithin(region: .country(isoCode: "US"))
if v.result == .yes {
ship(v.proof!.proofBytes)
}
val v = sdk.loc.isWithin(OctetRegion.country("US"))
if (v.result == OctetVerdict.Result.YES) {
ship(v.proof!!.proofBytes)
}
ship stands for your own code that sends the proof bytes to your backend. Your backend saves them to a file and checks them with the open-source verifier, with no call to Octet:
octet-verify proof.bin
Exit code 0 means the proof is authentic. See Verifier Quick Start.
Why a claim needs a proof
A claim like "the device was in the US at 3 pm Tuesday" can come from the OS, the GPS chip, a browser API, or a mock-location app. All of them are easy to forge. The claim is only worth acting on if a third party (your backend, an auditor, a regulator) can check it without trusting whoever made it.
The Octet SDK produces those proofs. Every YES or NO it returns carries one. An INDETERMINATE carries none. You can:
- Forward a proof to your backend and verify it offline with the open-source verifier.
- Hand it to a compliance auditor to re-check months later.
- Store it as a durable record of where the device was at that time.
How it works
The SDK combines several independent on-device signals (GNSS, cellular network identity, motion sensors) with platform attestation (Apple App Attest on iOS, hardware key attestation on Android) to produce a verdict and a cryptographic proof. The SDK creates the signing key in the device's hardware where it can (the Secure Enclave on iOS, StrongBox or the Trusted Execution Environment on Android). Each proof records where its key lives as the device-key security tier: HARDWARE_STRONGBOX, HARDWARE_TEE, or SOFTWARE. On a device without hardware-backed key storage, the SDK falls back to a software key and still signs proofs, so a verifier decides which tiers to accept. The proof is a signed protobuf that names:
- The claimed region (for example country
US, or a disc of radius 250 m). - The time of the fix and the validity interval the proof covers.
- The assurance tier (
spoofingVerdict:VERIFIEDorPLAUSIBLE) and the device-integrity status. - An attestation chain from platform hardware (App Attest on iOS, key attestation on Android), which the SDK checks when it builds the proof.
You call isWithin(region) and get back a Verdict. On YES or NO the verdict carries a proof. If the SDK cannot get a trustworthy fix (rooted device, a mock-location app, or no usable GPS signal), it issues no proof and returns INDETERMINATE with a reason code.
flowchart LR
A[Your app] -->|isWithin region| B[OctetSDK]
B --> C{Can the SDK<br/>answer?}
C -->|Yes| D[Verdict YES or NO<br/>+ signed proof]
C -->|No| E[Verdict INDETERMINATE<br/>+ reason code]
D -->|forward| F[Your backend]
F -->|verify offline| G[octet-verify]
What the SDK does not do
- Judge the user. It reports whether it can issue a proof and, when it cannot, a reason code. An adversarial reason (
SPOOFING_DETECTED,MOCK_LOCATION_DETECTED,TAMPERING,ATTESTATION_FAILED) describes the location fix, not the person. What to do about the user is your policy. - Guarantee a
YES. On an emulator, indoors with no cellular, on a rooted device, or with a mock-location app running, the result isINDETERMINATE. - Predict the future. A proof states where the device was at time T, not where it will be later. See Time Semantics.
Where to go next
- Device Attestation. How App Attest and key attestation tie a proof to genuine hardware.
- Verdicts. The
YES/NO/INDETERMINATEvalues and how to read the reason code. - Regions. What kinds of areas you can ask about.
- Time Semantics. How
atTimeand proof validity intervals interact.