Proof of Location Scenario

This guide demonstrates how to implement proof of location credentials using TrustWeave, inspired by decentralized geospatial web concepts. You’ll learn how to create location-based verifiable credentials, anchor them to blockchains for tamper-proof verification, and build a system for geospatial data integrity.

What You’ll Build

By the end of this tutorial, you’ll have:

  • ✅ Created DIDs for location providers and verifiers
  • ✅ Issued Verifiable Credentials proving location claims
  • ✅ Anchored location proofs to blockchain for immutability
  • ✅ Built a location verification system
  • ✅ Created location-based presentations with selective disclosure
  • ✅ Implemented geospatial data integrity verification

Big Picture & Significance

The Location Verification Challenge

Location data is critical for many applications, but verifying location claims and protecting privacy are fundamental challenges. Traditional systems rely on central authorities and don’t protect user privacy.

Industry Context:

  • Market Size: Location-based services market projected to reach $157 billion by 2026
  • Privacy Concerns: Location data is highly sensitive
  • Trust Requirements: Need to verify location claims for critical applications
  • Regulatory Pressure: GDPR and other regulations require location data protection
  • Decentralization: Growing demand for decentralized location verification

Why This Matters:

  1. Privacy: Protect sensitive location data
  2. Trust: Verify location claims cryptographically
  3. Decentralization: Avoid reliance on central authorities
  4. Integrity: Ensure location data hasn’t been tampered with
  5. Selective Disclosure: Share location without revealing exact coordinates
  6. Compliance: Meet regulatory requirements for location data

The Location Verification Problem

Traditional location systems face critical issues:

  • Centralized Trust: Reliance on single authorities creates bottlenecks
  • Privacy Violations: Location data often shared without consent
  • No Verification: Can’t verify location claims
  • No Integrity: Location data can be tampered with
  • No Interoperability: Different systems can’t verify each other’s claims

Value Proposition

Problems Solved

  1. Cryptographic Proof: Location claims are cryptographically signed
  2. Privacy Protection: Selective disclosure protects sensitive data
  3. Blockchain Anchoring: Immutable proof of when/where data was created
  4. Verifiable Credentials: Standard format for location claims
  5. Self-Sovereign: Users control their location data
  6. Interoperability: Standard format works across systems
  7. Decentralization: No reliance on central authorities

Business Benefits

For Users:

  • Privacy: Control what location information is shared
  • Trust: Verify location claims cryptographically
  • Control: Own and control location data
  • Portability: Location credentials work across platforms

For Service Providers:

  • Trust: Verify location claims without intermediaries
  • Compliance: Meet regulatory requirements
  • Efficiency: Automated location verification
  • Innovation: Enable new location-based services

For Regulators:

  • Audit Trails: Complete location data records
  • Privacy: Enhanced privacy protection
  • Compliance: Meet regulatory requirements

ROI Considerations

  • Privacy: Enhanced privacy protection reduces liability
  • Trust: Increased trust enables new use cases
  • Compliance: Automated compliance reduces costs
  • Innovation: Enable new location-based services

Understanding the Problem

Geospatial data and location claims face several challenges:

  1. Trust: How do you verify someone was actually at a location?
  2. Privacy: Location data is highly sensitive and needs protection
  3. Integrity: Geospatial data can be tampered with
  4. Provenance: Need to track where location data came from
  5. Decentralization: Avoid reliance on central authorities

TrustWeave solves this by enabling:

  • Cryptographic proof: Location claims are cryptographically signed
  • Selective disclosure: Share location without revealing exact coordinates
  • Blockchain anchoring: Immutable proof of when/where data was created
  • Verifiable credentials: Standard format for location claims
  • Self-sovereign: Users control their location data

How It Works: Location Proof Flow

flowchart TD
    A["Location Provider<br/>GPS Device<br/>IoT Sensor<br/>Mobile App"] -->|captures location| B["Location Data<br/>Coordinates lat/lon<br/>Timestamp<br/>Accuracy<br/>Device ID"]
    B -->|issues credential| C["Verifiable Credential<br/>Location Claim<br/>Proof cryptographic<br/>Metadata"]
    C -->|anchors to blockchain| D["Blockchain Anchor<br/>Immutable timestamp<br/>Location digest<br/>Integrity proof"]
    D -->|stores in wallet| E["Location Wallet<br/>Stores location credentials<br/>Creates presentations<br/>Selective disclosure"]

    style A fill:#1976d2,stroke:#0d47a1,stroke-width:2px,color:#fff
    style B fill:#f57c00,stroke:#e65100,stroke-width:2px,color:#fff
    style C fill:#388e3c,stroke:#1b5e20,stroke-width:2px,color:#fff
    style D fill:#c2185b,stroke:#880e4f,stroke-width:2px,color:#fff
    style E fill:#7b1fa2,stroke:#4a148c,stroke-width:2px,color:#fff

Prerequisites

  • Java 21+
  • Kotlin 2.2.0+
  • Gradle 8.5+
  • Basic understanding of Kotlin and coroutines
  • Basic understanding of geospatial concepts (coordinates, bounding boxes)

Step 1: Add Dependencies

Add TrustWeave dependencies to your build.gradle.kts. These modules provide DID support, credential issuance, wallet storage, and the in-memory services used for location proofs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
dependencies {
    // Core TrustWeave modules
    implementation("com.trustweave:trustweave-core:1.0.0-SNAPSHOT")
    implementation("com.trustweave:trustweave-json:1.0.0-SNAPSHOT")
    implementation("com.trustweave:trustweave-kms:1.0.0-SNAPSHOT")
    implementation("com.trustweave:trustweave-did:1.0.0-SNAPSHOT")
    implementation("com.trustweave:trustweave-anchor:1.0.0-SNAPSHOT")

    // Test kit for in-memory implementations
    implementation("com.trustweave:trustweave-testkit:1.0.0-SNAPSHOT")

    // Kotlinx Serialization
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")

    // Coroutines
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
}

Result: With these dependencies synced, you can run the location proof samples without adding more modules or adapters.

Step 2: Complete Example

Here’s the complete proof-of-location workflow. Execute it once to see the happy path (capture → issue → store → present → verify → anchor) before you inspect each step in detail.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
import com.trustweave.credential.models.VerifiableCredential
import com.trustweave.credential.models.VerifiablePresentation
import com.trustweave.credential.CredentialIssuanceOptions
import com.trustweave.credential.CredentialVerificationOptions
import com.trustweave.credential.PresentationOptions
import com.trustweave.credential.issuer.CredentialIssuer
import com.trustweave.credential.verifier.CredentialVerifier
import com.trustweave.credential.proof.Ed25519ProofGenerator
import com.trustweave.credential.proof.ProofGeneratorRegistry
import com.trustweave.testkit.credential.InMemoryWallet
import com.trustweave.testkit.did.DidKeyMockMethod
import com.trustweave.testkit.kms.InMemoryKeyManagementService
import com.trustweave.testkit.anchor.InMemoryBlockchainAnchorClient
import com.trustweave.did.DidMethodRegistry
import com.trustweave.anchor.BlockchainAnchorRegistry
import com.trustweave.anchor.anchorTyped
import com.trustweave.anchor.AnchorResult
import kotlinx.serialization.json.Json

@Serializable
data class LocationClaim(
    val latitude: Double,
    val longitude: Double,
    val accuracy: Double, // meters
    val altitude: Double? = null,
    val timestamp: String,
    val deviceId: String,
    val locationMethod: String = "GPS" // GPS, WiFi, Cell Tower, etc.
)

@Serializable
data class LocationProof(
    val locationClaim: LocationClaim,
    val credentialDigest: String,
    val anchorRef: String? = null
)

fun main() = runBlocking {
    println("=== Proof of Location Scenario ===\n")

    // Step 1: Setup services
    println("Step 1: Setting up services...")
    val locationProviderKms = InMemoryKeyManagementService()
    val verifierKms = InMemoryKeyManagementService()

    val didMethod = DidKeyMockMethod(locationProviderKms)
    val didRegistry = DidMethodRegistry().apply { register(didMethod) }

    // Setup blockchain for anchoring
    val anchorClient = InMemoryBlockchainAnchorClient("eip155:1", emptyMap())
    val blockchainRegistry = BlockchainAnchorRegistry().apply {
        register("eip155:1", anchorClient)
    }

    // Step 2: Create DIDs
    println("\nStep 2: Creating DIDs...")
    val locationProviderDid = didMethod.createDid()
    println("Location Provider DID: ${locationProviderDid.id}")

    val deviceDid = didMethod.createDid()
    println("Device DID: ${deviceDid.id}")

    // Step 3: Create location wallet
    println("\nStep 3: Creating location wallet...")
    val locationWallet = InMemoryWallet(
        walletDid = deviceDid.id,
        holderDid = deviceDid.id
    )
    println("Location wallet created: ${locationWallet.walletId}")

    // Step 4: Capture location data
    println("\nStep 4: Capturing location data...")
    val locationClaim = LocationClaim(
        latitude = 37.7749,
        longitude = -122.4194,
        accuracy = 10.5, // 10.5 meters accuracy
        altitude = 52.0,
        timestamp = Instant.now().toString(),
        deviceId = deviceDid.id,
        locationMethod = "GPS"
    )
    println("Location captured:")
    println("  - Coordinates: (${locationClaim.latitude}, ${locationClaim.longitude})")
    println("  - Accuracy: ${locationClaim.accuracy} meters")
    println("  - Timestamp: ${locationClaim.timestamp}")

    // Step 5: Create location credential
    println("\nStep 5: Creating location credential...")
    val locationCredential = VerifiableCredential(
        id = "https://example.com/location/${deviceDid.id.substringAfterLast(":")}-${Instant.now().toEpochMilli()}",
        type = listOf("VerifiableCredential", "LocationCredential", "ProofOfLocation"),
        issuer = locationProviderDid.id,
        credentialSubject = buildJsonObject {
            put("id", deviceDid.id)
            put("location", buildJsonObject {
                put("latitude", locationClaim.latitude)
                put("longitude", locationClaim.longitude)
                put("accuracy", locationClaim.accuracy)
                locationClaim.altitude?.let { put("altitude", it) }
                put("timestamp", locationClaim.timestamp)
                put("deviceId", locationClaim.deviceId)
                put("locationMethod", locationClaim.locationMethod)
            })
        },
        issuanceDate = Instant.now().toString(),
        expirationDate = null, // Location proofs typically don't expire
        credentialSchema = com.trustweave.credential.models.CredentialSchema(
            id = "https://example.com/schemas/location.json",
            type = "JsonSchemaValidator2018",
            schemaFormat = com.trustweave.spi.SchemaFormat.JSON_SCHEMA
        )
    )

    // Step 6: Issue credential with proof
    println("\nStep 6: Issuing credential with proof...")
    val issuerKey = locationProviderKms.generateKey("Ed25519")
    val proofGenerator = Ed25519ProofGenerator(
        signer = { data, keyId -> locationProviderKms.sign(keyId, data) },
        getPublicKeyId = { keyId -> issuerKey.id }
    )

    val proofRegistry = ProofGeneratorRegistry().apply { register(proofGenerator) }

    val credentialIssuer = CredentialIssuer(
        proofGenerator = proofGenerator,
        resolveDid = { did -> didRegistry.resolve(did) != null },
        proofRegistry = proofRegistry
    )

    val issuedCredential = credentialIssuer.issue(
        credential = locationCredential,
        issuerDid = locationProviderDid.id,
        keyId = issuerKey.id,
        options = CredentialIssuanceOptions(proofType = "Ed25519Signature2020")
    )

    println("Credential issued:")
    println("  - Type: ${issuedCredential.type}")
    println("  - Has proof: ${issuedCredential.proof != null}")

    // Step 7: Compute digest and anchor to blockchain
    println("\nStep 7: Anchoring location proof to blockchain...")
    val credentialDigest = DigestUtils.sha256DigestMultibase(
        kotlinx.serialization.json.Json.encodeToJsonElement(
            com.trustweave.credential.models.VerifiableCredential.serializer(),
            issuedCredential
        )
    )

    val locationProof = LocationProof(
        locationClaim = locationClaim,
        credentialDigest = credentialDigest
    )

    val anchorResult = blockchainRegistry.anchorTyped(
        value = locationProof,
        serializer = LocationProof.serializer(),
        targetChainId = "eip155:1"
    )

    println("Location proof anchored:")
    println("  - Transaction hash: ${anchorResult.ref.txHash}")
    println("  - Chain ID: ${anchorResult.ref.chainId}")
    println("  - Digest: $credentialDigest")

    // Step 8: Store credential in wallet
    println("\nStep 8: Storing credential in wallet...")
    val credentialId = locationWallet.store(issuedCredential)
    println("Credential stored with ID: $credentialId")

    // Step 9: Organize location credentials
    println("\nStep 9: Organizing location credentials...")
    val locationCollection = locationWallet.createCollection(
        name = "Location History",
        description = "Historical location proofs"
    )
    locationWallet.addToCollection(credentialId, locationCollection)

    // Tag by location type
    val locationTags = when {
        locationClaim.latitude > 37.7 && locationClaim.latitude < 37.8 ->
            setOf("san-francisco", "california", "usa", "gps")
        else -> setOf("location", "gps", "verified")
    }
    locationWallet.tagCredential(credentialId, locationTags)

    // Add metadata
    locationWallet.addMetadata(credentialId, mapOf(
        "city" to "San Francisco",
        "country" to "USA",
        "anchorTxHash" to anchorResult.ref.txHash,
        "accuracy" to locationClaim.accuracy
    ))

    println("Created collection: $locationCollection")
    println("Added tags: ${locationTags.joinToString()}")

    // Step 10: Query location credentials
    println("\nStep 10: Querying location credentials...")
    val recentLocations = locationWallet.query {
        byType("LocationCredential")
        valid()
    }
    println("Found ${recentLocations.size} valid location credentials")

    // Find locations in specific area (using tags)
    val sfLocations = locationWallet.findByTag("san-francisco")
    println("San Francisco locations: ${sfLocations.size}")

    // Step 11: Create location presentation with selective disclosure
    println("\nStep 11: Creating location presentation...")

    // Selective disclosure: reveal approximate location but not exact coordinates
    val approximatePresentation = locationWallet.createSelectiveDisclosure(
        credentialIds = listOf(credentialId),
        disclosedFields = listOf(
            "location.timestamp",
            "location.locationMethod"
            // Exact coordinates NOT disclosed for privacy
        ),
        holderDid = deviceDid.id,
        options = PresentationOptions(
            holderDid = deviceDid.id,
            proofType = "Ed25519Signature2020",
            challenge = "location-verification-${Instant.now().toEpochMilli()}"
        )
    )
    println("Approximate location presentation created (coordinates hidden)")

    // Full location presentation (for trusted verifiers)
    val fullPresentation = locationWallet.createPresentation(
        credentialIds = listOf(credentialId),
        holderDid = deviceDid.id,
        options = PresentationOptions(
            holderDid = deviceDid.id,
            proofType = "Ed25519Signature2020",
            challenge = "full-location-verification"
        )
    )
    println("Full location presentation created")

    // Step 12: Verify location credential
    println("\nStep 12: Verifying location credential...")
    val verifier = CredentialVerifier(didRegistry.resolve(deviceDid.id) ?: throw IllegalArgumentException("Device DID not found"))

    val verificationResult = verifier.verify(
        credential = issuedCredential,
        options = CredentialVerificationOptions(
            checkRevocation = false,
            checkExpiration = false, // Location proofs don't expire
            validateSchema = false,
            didResolver = { did -> didRegistry.resolve(did) != null }
        )
    )

    if (verificationResult.valid) {
        println("✅ Location credential is valid!")
        println("  - Proof valid: ${verificationResult.proofValid}")
        println("  - Issuer valid: ${verificationResult.issuerValid}")
        println("  - Schema valid: ${verificationResult.schemaValid}")
    } else {
        println("❌ Location credential verification failed:")
        verificationResult.errors.forEach { println("  - $it") }
    }

    // Step 13: Verify blockchain anchor
    println("\nStep 13: Verifying blockchain anchor...")
    val retrievedProof = blockchainRegistry.readTyped<LocationProof>(
        ref = anchorResult.ref,
        serializer = LocationProof.serializer()
    )

    val retrievedDigest = retrievedProof.credentialDigest
    if (retrievedDigest == credentialDigest) {
        println("✅ Blockchain anchor verified!")
        println("  - Digest matches: $retrievedDigest")
        println("  - Location proof is immutable")
    } else {
        println("❌ Digest mismatch - location proof may have been tampered with")
    }

    // Step 14: Location-based queries
    println("\nStep 14: Location-based queries...")
    val stats = locationWallet.getStatistics()
    println("""
        Location Wallet Statistics:
        - Total location credentials: ${stats.totalCredentials}
        - Valid credentials: ${stats.validCredentials}
        - Collections: ${stats.collectionsCount}
        - Tags: ${stats.tagsCount}
    """.trimIndent())

    println("\n=== Scenario Complete ===")
}

**Result:** Console output walks through each milestonelocation capture, credential issuance, wallet storage, presentation, verification, anchoring. Use it as your regression baseline.

## Step-by-Step Breakdown

### Step 1: Setup Services

Captures a raw location claim and sets up DID registries, wallets, and proof generators for both provider and verifier.
- **Outcome:** The environment mirrors a decentralised location system without external infrastructure.

```kotlin
val locationProviderKms = InMemoryKeyManagementService()
val anchorClient = InMemoryBlockchainAnchorClient("eip155:1", emptyMap())
val blockchainRegistry = BlockchainAnchorRegistry().apply {
    register("eip155:1", anchorClient)
}

Step 2: Create Location Claim

Capture location data from device:

1
2
3
4
5
6
7
8
val locationClaim = LocationClaim(
    latitude = 37.7749,
    longitude = -122.4194,
    accuracy = 10.5, // meters
    timestamp = Instant.now().toString(),
    deviceId = deviceDid.id,
    locationMethod = "GPS"
)

Step 3: Create Location Credential

Create a verifiable credential for the location:

1
2
3
4
5
6
7
8
9
10
11
12
13
val locationCredential = VerifiableCredential(
    type = listOf("VerifiableCredential", "LocationCredential"),
    issuer = locationProviderDid.id,
    credentialSubject = buildJsonObject {
        put("location", buildJsonObject {
            put("latitude", locationClaim.latitude)
            put("longitude", locationClaim.longitude)
            put("accuracy", locationClaim.accuracy)
            put("timestamp", locationClaim.timestamp)
        })
    },
    issuanceDate = Instant.now().toString()
)

Step 4: Issue and Anchor

Issues a location credential with typed geospatial claims plus metadata (accuracy, method, timestamp).

  • Outcome: locationCredential is ready to store, present, and anchor.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
val issuedCredential = credentialIssuer.issue(...)

val credentialDigest = DigestUtils.sha256DigestMultibase(
    Json.encodeToJsonElement(issuedCredential)
)

val locationProof = LocationProof(
    locationClaim = locationClaim,
    credentialDigest = credentialDigest
)

val anchorResult = blockchainRegistry.anchorTyped(
    value = locationProof,
    serializer = LocationProof.serializer(),
    targetChainId = "eip155:1"
)

Step 5: Store and Organize

Stores the credential in the user’s wallet so it can be queried and presented later.

  • Outcome: The user gains custody of the proof for subsequent sharing.
1
2
3
4
5
val credentialId = locationWallet.store(issuedCredential)

val locationCollection = locationWallet.createCollection("Location History")
locationWallet.addToCollection(credentialId, locationCollection)
locationWallet.tagCredential(credentialId, setOf("san-francisco", "gps"))

Step 6: Selective Disclosure

Share location with privacy:

1
2
3
4
5
6
7
8
9
10
11
// Approximate location (privacy-preserving)
val approximatePresentation = locationWallet.createSelectiveDisclosure(
    credentialIds = listOf(credentialId),
    disclosedFields = listOf(
        "location.timestamp",
        "location.locationMethod"
        // Coordinates hidden
    ),
    holderDid = deviceDid.id,
    options = PresentationOptions(...)
)

Advanced Geospatial Features

Geofencing Verification

Verify location within a geographic boundary:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fun verifyLocationInGeofence(
    credential: VerifiableCredential,
    minLat: Double,
    maxLat: Double,
    minLon: Double,
    maxLon: Double
): Boolean {
    val location = credential.credentialSubject.jsonObject["location"]?.jsonObject
        ?: return false

    val lat = location["latitude"]?.jsonPrimitive?.content?.toDouble() ?: return false
    val lon = location["longitude"]?.jsonPrimitive?.content?.toDouble() ?: return false

    return lat in minLat..maxLat && lon in minLon..maxLon
}

// Usage: Verify device is in San Francisco
val inSF = verifyLocationInGeofence(
    credential = issuedCredential,
    minLat = 37.7,
    maxLat = 37.8,
    minLon = -122.5,
    maxLon = -122.3
)

Distance-Based Verification

Verify location within a certain distance:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
fun verifyLocationWithinDistance(
    credential: VerifiableCredential,
    targetLat: Double,
    targetLon: Double,
    maxDistance: Double // meters
): Boolean {
    val location = credential.credentialSubject.jsonObject["location"]?.jsonObject
        ?: return false

    val lat = location["latitude"]?.jsonPrimitive?.content?.toDouble() ?: return false
    val lon = location["longitude"]?.jsonPrimitive?.content?.toDouble() ?: return false
    val accuracy = location["accuracy"]?.jsonPrimitive?.content?.toDouble() ?: Double.MAX_VALUE

    val distance = calculateHaversineDistance(lat, lon, targetLat, targetLon)

    // Account for location accuracy
    return distance <= (maxDistance + accuracy)
}

// Haversine formula for great-circle distance
fun calculateHaversineDistance(
    lat1: Double,
    lon1: Double,
    lat2: Double,
    lon2: Double
): Double {
    val earthRadius = 6371000.0 // meters
    val dLat = Math.toRadians(lat2 - lat1)
    val dLon = Math.toRadians(lon2 - lon1)
    val a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
            Math.sin(dLon / 2) * Math.sin(dLon / 2)
    val c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
    return earthRadius * c
}

### Privacy-Preserving Location Sharing

Share location without revealing exact coordinates:

```kotlin
fun createPrivacyPreservingLocationPresentation(
    wallet: Wallet,
    credentialId: String,
    privacyLevel: PrivacyLevel
): VerifiablePresentation {
    return when (privacyLevel) {
        PrivacyLevel.EXACT -> {
            // Share exact coordinates
            wallet.createPresentation(
                credentialIds = listOf(credentialId),
                holderDid = wallet.holderDid,
                options = PresentationOptions(...)
            )
        }
        PrivacyLevel.APPROXIMATE -> {
            // Share approximate location (rounded coordinates)
            wallet.createSelectiveDisclosure(
                credentialIds = listOf(credentialId),
                disclosedFields = listOf(
                    "location.timestamp",
                    "location.locationMethod"
                    // Coordinates rounded/obfuscated
                ),
                holderDid = wallet.holderDid,
                options = PresentationOptions(...)
            )
        }
        PrivacyLevel.REGION -> {
            // Share only region/city, not coordinates
            wallet.createSelectiveDisclosure(
                credentialIds = listOf(credentialId),
                disclosedFields = listOf(
                    "location.timestamp"
                    // Only timestamp, no coordinates
                ),
                holderDid = wallet.holderDid,
                options = PresentationOptions(...)
            )
        }
    }
}

enum class PrivacyLevel {
    EXACT,      // Exact coordinates
    APPROXIMATE, // Approximate location
    REGION      // Region only
}

data class BoundingBox( val minLat: Double, val maxLat: Double, val minLon: Double, val maxLon: Double )

Real-World Use Cases

1. Emergency Response (NG9-1-1)

Verify caller location for emergency services:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun createEmergencyLocationCredential(
    callerDid: String,
    location: LocationClaim,
    emergencyType: String
): VerifiableCredential {
    return VerifiableCredential(
        type = listOf("VerifiableCredential", "EmergencyLocationCredential"),
        issuer = emergencyServiceDid.id,
        credentialSubject = buildJsonObject {
            put("id", callerDid)
            put("emergencyType", emergencyType)
            put("location", buildJsonObject {
                put("latitude", location.latitude)
                put("longitude", location.longitude)
                put("accuracy", location.accuracy)
                put("timestamp", location.timestamp)
            })
        },
        issuanceDate = Instant.now().toString()
    )
}

2. Vehicular Network Security

Prevent Sybil attacks in VANETs using location proofs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
fun createVehicleLocationProof(
    vehicleDid: String,
    location: LocationClaim,
    roadsideUnitDid: String
): VerifiableCredential {
    return VerifiableCredential(
        type = listOf("VerifiableCredential", "VehicleLocationProof"),
        issuer = roadsideUnitDid,
        credentialSubject = buildJsonObject {
            put("id", vehicleDid)
            put("location", buildJsonObject {
                put("latitude", location.latitude)
                put("longitude", location.longitude)
                put("timestamp", location.timestamp)
            })
            put("roadsideUnit", roadsideUnitDid)
        },
        issuanceDate = location.timestamp
    )
}

// Verify vehicle trajectory using multiple proofs
fun verifyVehicleTrajectory(
    locationProofs: List<VerifiableCredential>
): Boolean {
    // Check proofs are from different roadside units
    val issuers = locationProofs.map { it.issuer }.toSet()
    if (issuers.size < 2) return false // Need multiple sources

    // Verify temporal sequence
    val timestamps = locationProofs.mapNotNull { credential ->
        credential.credentialSubject.jsonObject["location"]?.jsonObject
            ?.get("timestamp")?.jsonPrimitive?.content
    }.sorted()

    // Check timestamps are sequential
    return timestamps.zipWithNext().all { (t1, t2) ->
        Instant.parse(t1).isBefore(Instant.parse(t2))
    }
}

3. Cloud Data Location Verification

Verify data is stored in authorized geographic locations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
fun createDataLocationCredential(
    dataHash: String,
    storageLocation: LocationClaim,
    dataCenterDid: String
): VerifiableCredential {
    return VerifiableCredential(
        type = listOf("VerifiableCredential", "DataLocationCredential"),
        issuer = dataCenterDid,
        credentialSubject = buildJsonObject {
            put("dataHash", dataHash)
            put("storageLocation", buildJsonObject {
                put("latitude", storageLocation.latitude)
                put("longitude", storageLocation.longitude)
                put("dataCenter", dataCenterDid)
                put("timestamp", storageLocation.timestamp)
            })
        },
        issuanceDate = storageLocation.timestamp
    )
}

// Verify data is in authorized region
fun verifyDataLocationCompliance(
    credential: VerifiableCredential,
    allowedRegions: List<BoundingBox>
): Boolean {
    val location = credential.credentialSubject.jsonObject["storageLocation"]?.jsonObject
        ?: return false

    val lat = location["latitude"]?.jsonPrimitive?.content?.toDouble() ?: return false
    val lon = location["longitude"]?.jsonPrimitive?.content?.toDouble() ?: return false

    return allowedRegions.any { region ->
        lat in region.minLat..region.maxLat && lon in region.minLon..region.maxLon
    }
}

4. Multimedia Content Authentication

Verify location where media was captured (like ProofMode):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
fun createMediaLocationCredential(
    mediaHash: String,
    captureLocation: LocationClaim,
    deviceDid: String,
    mediaType: String
): VerifiableCredential {
    return VerifiableCredential(
        type = listOf("VerifiableCredential", "MediaLocationCredential"),
        issuer = deviceDid,
        credentialSubject = buildJsonObject {
            put("mediaHash", mediaHash)
            put("mediaType", mediaType)
            put("captureLocation", buildJsonObject {
                put("latitude", captureLocation.latitude)
                put("longitude", captureLocation.longitude)
                put("accuracy", captureLocation.accuracy)
                put("timestamp", captureLocation.timestamp)
            })
            put("deviceId", deviceDid)
        },
        issuanceDate = captureLocation.timestamp
    )
}

// Verify media authenticity
fun verifyMediaLocation(
    mediaHash: String,
    claimedLocation: Pair<Double, Double>,
    credential: VerifiableCredential
): Boolean {
    val credentialHash = credential.credentialSubject.jsonObject["mediaHash"]?.jsonPrimitive?.content
    if (credentialHash != mediaHash) return false

    val location = credential.credentialSubject.jsonObject["captureLocation"]?.jsonObject
        ?: return false

    val lat = location["latitude"]?.jsonPrimitive?.content?.toDouble() ?: return false
    val lon = location["longitude"]?.jsonPrimitive?.content?.toDouble() ?: return false

    val distance = calculateHaversineDistance(
        lat, lon,
        claimedLocation.first,
        claimedLocation.second
    )

    return distance < 100.0 // Within 100 meters
}

5. Supply Chain Tracking

Track products through supply chain with location proofs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun createSupplyChainLocationProof(
    productId: String,
    location: LocationClaim,
    facility: String,
    timestamp: String
): VerifiableCredential {
    return VerifiableCredential(
        type = listOf("VerifiableCredential", "SupplyChainLocationCredential"),
        issuer = facilityDid.id,
        credentialSubject = buildJsonObject {
            put("productId", productId)
            put("location", buildJsonObject {
                put("latitude", location.latitude)
                put("longitude", location.longitude)
                put("facility", facility)
                put("timestamp", timestamp)
            })
        },
        issuanceDate = timestamp
    )
}

6. IoT Device Location

Track IoT device locations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun createIoTLocationCredential(
    deviceId: String,
    location: LocationClaim,
    sensorData: Map<String, Any>
): VerifiableCredential {
    return VerifiableCredential(
        type = listOf("VerifiableCredential", "IoTLocationCredential"),
        issuer = iotGatewayDid.id,
        credentialSubject = buildJsonObject {
            put("deviceId", deviceId)
            put("location", buildJsonObject {
                put("latitude", location.latitude)
                put("longitude", location.longitude)
                put("accuracy", location.accuracy)
                put("timestamp", location.timestamp)
            })
            put("sensorData", sensorData)
        },
        issuanceDate = location.timestamp
    )
}

Decentralized Geospatial Data Sharing

Share geospatial datasets with verifiable provenance:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Serializable
data class GeospatialDataset(
    val datasetId: String,
    val name: String,
    val description: String,
    val boundingBox: BoundingBox,
    val dataHash: String,
    val format: String = "GeoJSON",
    val crs: String = "EPSG:4326",
    val providerDid: String,
    val timestamp: String
)

fun createGeospatialDatasetCredential(
    dataset: GeospatialDataset,
    issuerDid: String
): VerifiableCredential {
    return VerifiableCredential(
        type = listOf("VerifiableCredential", "GeospatialDatasetCredential"),
        issuer = issuerDid,
        credentialSubject = buildJsonObject {
            put("datasetId", dataset.datasetId)
            put("name", dataset.name)
            put("description", dataset.description)
            put("boundingBox", buildJsonObject {
                put("minLat", dataset.boundingBox.minLat)
                put("maxLat", dataset.boundingBox.maxLat)
                put("minLon", dataset.boundingBox.minLon)
                put("maxLon", dataset.boundingBox.maxLon)
            })
            put("dataHash", dataset.dataHash)
            put("format", dataset.format)
            put("crs", dataset.crs)
            put("providerDid", dataset.providerDid)
            put("timestamp", dataset.timestamp)
        },
        issuanceDate = dataset.timestamp
    )
}

// Anchor dataset metadata to blockchain
fun anchorGeospatialDataset(
    dataset: GeospatialDataset,
    blockchainRegistry: BlockchainAnchorRegistry,
    chainId: String
): AnchorResult {
    val credential = createGeospatialDatasetCredential(dataset, dataset.providerDid)
    val digest = DigestUtils.sha256DigestMultibase(
        Json.encodeToJsonElement(VerifiableCredential.serializer(), credential)
    )

    return blockchainRegistry.anchorTyped(
        value = LocationProof(dataset, digest),
        serializer = LocationProof.serializer(),
        targetChainId = chainId
    )
}

Multi-Source Location Verification

Verify location using multiple sources (GPS, WiFi, Cell Tower):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
fun createMultiSourceLocationCredential(
    deviceDid: String,
    gpsLocation: LocationClaim?,
    wifiLocation: LocationClaim?,
    cellLocation: LocationClaim?,
    issuerDid: String
): VerifiableCredential {
    val locations = buildJsonObject {
        gpsLocation?.let {
            put("gps", buildJsonObject {
                put("latitude", it.latitude)
                put("longitude", it.longitude)
                put("accuracy", it.accuracy)
                put("timestamp", it.timestamp)
            })
        }
        wifiLocation?.let {
            put("wifi", buildJsonObject {
                put("latitude", it.latitude)
                put("longitude", it.longitude)
                put("accuracy", it.accuracy)
                put("timestamp", it.timestamp)
            })
        }
        cellLocation?.let {
            put("cell", buildJsonObject {
                put("latitude", it.latitude)
                put("longitude", it.longitude)
                put("accuracy", it.accuracy)
                put("timestamp", it.timestamp)
            })
        }
    }

    // Calculate consensus location
    val consensusLocation = calculateConsensusLocation(
        listOfNotNull(gpsLocation, wifiLocation, cellLocation)
    )

    return VerifiableCredential(
        type = listOf("VerifiableCredential", "MultiSourceLocationCredential"),
        issuer = issuerDid,
        credentialSubject = buildJsonObject {
            put("id", deviceDid)
            put("locations", locations)
            put("consensus", buildJsonObject {
                put("latitude", consensusLocation.first)
                put("longitude", consensusLocation.second)
                put("confidence", consensusLocation.third)
            })
        },
        issuanceDate = Instant.now().toString()
    )
}

fun calculateConsensusLocation(
    locations: List<LocationClaim>
): Triple<Double, Double, Double> { // lat, lon, confidence
    if (locations.isEmpty()) {
        throw IllegalArgumentException("At least one location required")
    }

    // Weighted average based on accuracy (lower accuracy = higher weight)
    val weights = locations.map { 1.0 / it.accuracy }
    val totalWeight = weights.sum()

    val avgLat = locations.zip(weights).sumOf { (loc, weight) ->
        loc.latitude * weight
    } / totalWeight

    val avgLon = locations.zip(weights).sumOf { (loc, weight) ->
        loc.longitude * weight
    } / totalWeight

    // Confidence based on number of sources and agreement
    val confidence = when {
        locations.size >= 3 -> 0.95
        locations.size == 2 -> 0.85
        else -> 0.70
    }

    return Triple(avgLat, avgLon, confidence)
}

Benefits

  1. Cryptographic Proof: Location claims are cryptographically verifiable
  2. Privacy Control: Selective disclosure protects sensitive location data
  3. Immutability: Blockchain anchoring provides tamper-proof records
  4. Decentralized: No reliance on central location services
  5. Standard Format: W3C VC standard for interoperability
  6. Self-Sovereign: Users control their location data

Best Practices

  1. Privacy First: Use selective disclosure to minimize location exposure
  2. Anchor Important Locations: Anchor critical location proofs to blockchain
  3. Organize by Context: Use collections and tags for location types
  4. Verify Before Trust: Always verify location credentials
  5. Consider Accuracy: Include accuracy information in claims
  6. Timestamp Everything: Always include precise timestamps

Next Steps