Leave breadcrumbs wherever you go
This document provides a technical overview of the end-to-end encrypted live location sharing system used in Loch More. It is intended for users who want to understand how their data is protected.
When you share your live location through Loch More, your GPS coordinates are encrypted on your device before they leave it. They are decrypted only on the viewer's device (or browser). The relay server that sits in between transports encrypted blobs and has no ability to read them.
When you create a sharing session, the app generates three independent cryptographic secrets on your device:
The encryption key and the session secret are completely independent. Viewers receive only the encryption key, which lets them decrypt location data but does not let them upload fake points or modify the session in any way. All write operations (uploading points, deleting the session, managing viewer access) require the session secret, which only the sharer possesses.
The encryption key is embedded in the sharing link as a URL fragment (the part after the #). URL fragments are never sent to the server by browsers -- they are processed entirely on the client side. This means the relay server never receives the encryption key, even when a viewer opens the link.
If you prefer, you can disable embedding the key in the URL and send it to viewers through a separate channel (e.g. a different messaging app), for an additional layer of protection.
Every location point uploaded to the server is encrypted using AES-256-GCM, a widely used authenticated encryption standard. The encrypted payload contains:
The server receives only the encrypted ciphertext. It cannot extract coordinates, speed, altitude, or any other location information from it.
| Information | Server can see? | Notes |
|---|---|---|
| Your GPS coordinates | No | Encrypted end-to-end; server only stores ciphertext |
| Speed, altitude | No | Encrypted inside the same payload |
| Real timestamps | No | Shifted by a large random offset (see below) |
| That a session exists | Yes | The server stores the session ID to route data |
| How many points were uploaded | Yes | Needed for storage management and enforcing limits |
| Approximate upload timing | Yes | The server sees when requests arrive, but not the real location timestamps |
| Encryption key | No | Transmitted only in the URL fragment, which the server never receives |
| Session secret | No | The server stores only a SHA-256 hash, not the raw secret |
Real timestamps are never sent to the server. Instead, each timestamp is shifted by the session's random time offset before upload. The offset itself is encrypted with the encryption key and stored on the server as an opaque blob. Only someone with the encryption key can recover real times.
This means the server cannot correlate your location uploads with real-world events based on timing, beyond the approximate moment each upload request arrives.
Strict-Transport-Security, X-Content-Type-Options: nosniff, and X-Frame-Options: DENY.Since location data is encrypted end-to-end, TLS serves as a second layer of protection. Even if TLS were somehow compromised, an attacker would still only see encrypted ciphertext.
The session owner (the person sharing their location) authenticates to the server using the session secret. The server never stores this secret in plaintext. Instead, it stores a SHA-256 hash and validates requests by hashing the provided secret and comparing. For ongoing API calls, the app uses short-lived HMAC tokens derived from the secret, so the raw secret is not transmitted repeatedly over the network.
Session owners can choose to enable or disable viewer approval, which requires them to explicitly approve each viewer before that viewer can access the encrypted location data. When a new viewer requests access:
Denied viewers receive no location data. Pending viewers receive no data until approved. By default, viewer approval is enabled.
On Android, the encryption key and session secret are encrypted at rest using the Android Keystore, a hardware-backed (where available) secure storage system. The keys stored in the Keystore never leave the secure environment and cannot be extracted, even on a rooted device (on hardware-backed implementations). This protects your sharing credentials if your device is lost or stolen.
The server enforces rate limits on all API endpoints to prevent abuse, including:
These limits protect both the infrastructure and individual sessions from excessive automated access.
The following table lists attack scenarios, whether they are mitigated, and how.
| Attack vector | Mitigated? | Explanation |
|---|---|---|
| Server database breach | Yes | The database contains only encrypted ciphertext, SHA-256-hashed secrets, and shifted timestamps. An attacker who dumps the entire database cannot recover any plaintext locations, real timestamps, or raw session secrets. |
| Rogue server operator | Yes | A malicious operator has the same access as a database breach. The encryption key never reaches the server, so even full server access cannot decrypt location data. |
| TLS interception (man-in-the-middle) | Yes | Even if TLS were compromised, the attacker only sees AES-256-GCM ciphertext. E2E encryption provides a second, independent layer of protection. |
| Replay attacks (re-sending captured ciphertext) | Yes | Each ciphertext is bound to its session via Additional Authenticated Data (AAD). Replaying a ciphertext in a different context fails authentication. |
| Cross-session ciphertext transplant | Yes | AAD includes the session ID, preventing an attacker from moving encrypted points between sessions. |
| Session ID enumeration | Yes | Session IDs are random UUIDs (128-bit). Guessing a valid ID is computationally infeasible. Unauthenticated session lookups are rate-limited. |
| Brute-force session secret | Yes | The secret is 256-bit random. Brute-forcing the SHA-256 hash stored on the server is computationally infeasible. |
| Brute-force encryption key | Yes | The encryption key is 256-bit random. Brute-forcing AES-256 is computationally infeasible. |
| API abuse / denial of service | Partially | Rate limits are enforced on all endpoints (session creation, point uploads, point retrieval, viewer actions). This mitigates automated abuse, but cannot fully prevent a sustained distributed attack. |
| Traffic analysis (upload frequency) | Partially | The server can observe when upload requests arrive and their approximate frequency, which could reveal patterns such as "the user is stationary" or "the user started moving". The sharing interval is configurable. Real timestamps are not exposed. |
| Request size analysis | Yes | Payloads are padded to a fixed block size before encryption, preventing inference about which optional fields (speed, altitude) are present. |
| Fake data injection by a viewer | Yes | Uploading points requires the session secret, which is never shared with viewers. A viewer who has the encryption key can decrypt data but cannot write to the session. |
| Timing correlation between sessions | Partially | If the same device runs multiple sharing sessions, the server could observe that uploads to different sessions arrive at similar times. This does not reveal location data. |
| Push notification metadata | Partially | When viewer approval is enabled, push notifications are sent via a third-party push service. These notifications contain a session identifier and viewer fingerprint but no location data. The push provider can observe that a session is active and that a viewer requested access. |
| IP address correlation | No | The server necessarily sees the IP addresses of both the sharer (uploading points) and viewers (fetching points). An attacker with server access could use IP geolocation for rough location inference or correlate sharer and viewer identities. Using a VPN mitigates this. |
| Attack vector | Mitigated? | Explanation |
|---|---|---|
| Malicious server serving modified JavaScript | No | If an attacker gains control of the web server (or DNS), they could serve a modified viewer page that exfiltrates the decryption key or plaintext data. This is an inherent limitation of any web-based E2E system. The Android app is not affected by this vector. |
| Cross-origin data theft | Yes | The viewer page enforces a strict Content Security Policy that restricts script sources and connections. |
| Clickjacking | Yes | The server sends X-Frame-Options: DENY, preventing the viewer from being embedded in an attacker's page. |
| Referrer leakage | Yes | The viewer sets a no-referrer policy, preventing the sharing URL (including the fragment) from leaking to third parties via HTTP headers. |
| CDN compromise (third-party JavaScript) | Mostly | External scripts (map library) are loaded with Subresource Integrity (SRI) hashes. A compromised CDN cannot serve modified scripts unless the hash also matches, which is computationally infeasible. |
| Malicious browser extension | No | A browser extension with sufficient permissions can read the decrypted location data from the page DOM or intercept JavaScript variables. This is outside the app's control. |
| Browser localStorage theft | Partially | The web viewer caches decrypted trail data in localStorage for performance. This cache expires automatically after a short period. An attacker with access to the browser's storage (e.g. via XSS on another page on the same origin) could read cached trail data. |
| Attack vector | Mitigated? | Explanation |
|---|---|---|
| Device theft (locked device) | Yes | The encryption key and session secret are encrypted at rest using the Android Keystore (hardware-backed where available). Extracting them from a locked device is infeasible on supported hardware. |
| Device theft (unlocked device) | No | An attacker with access to the unlocked device can open the app and view or modify sharing sessions. |
| Malware on the sharer's device | No | Malware with sufficient permissions could read encryption keys from memory, intercept location data before encryption, or access the Android Keystore if the device is unlocked. This is outside the app's control. |
| Attack vector | Mitigated? | Explanation |
|---|---|---|
| Sharing link intercepted (key in URL) | Yes, with correct settings | If the link is intercepted (e.g. over an unencrypted messaging channel), the attacker gains the decryption key. Mitigation: Keep viewer approval enabled so the sharer must explicitly approve each viewer. Alternatively, disable embedding the key in the URL and send it separately. |
| Sharing link forwarded to unintended recipients | Yes, with correct settings | Anyone with the full link can decrypt the location data. Mitigation: Keep viewer approval enabled. Each viewer's 6-digit fingerprint can be verified out-of-band. Unrecognized viewers can be denied. |
| Key sent separately but intercepted | Yes, with correct settings | If the key is sent over a compromised channel, the attacker can decrypt. This is inherent to symmetric key distribution. Mitigation: Keep viewer approval enabled. Even if they key and session id are intercepted, a viewer must still be approved before they recieve location data. |
| Messaging app URL previews processing the fragment | Yes, with correct settings | URL fragments are not sent to servers by browsers. Some messaging apps may process links in their own preview generators, but the fragment is not included in HTTP requests per the URL specification. Apps that follow standards will not leak the key. Mitigation: Keep viewer approval enabled for added security. |
| Attack vector | Mitigated? | Explanation |
|---|---|---|
| User approves a malicious viewer | Partially | Each viewer is shown with a 6-digit fingerprint that the sharer can verify out-of-band. However, if the user does not verify the fingerprint, a social engineering attack could succeed. |
| Viewer fingerprint collision | Partially | The 6-digit viewer fingerprint has a limited space of one million possible values. Two different viewers could have the same fingerprint, making out-of-band verification ambiguous. This is unlikely at small viewer counts but becomes more probable as the number of viewers grows. |
| User shares link publicly | No | If a user posts their sharing link publicly (e.g. on social media), anyone can view their location. Viewer approval helps, but the user must actively deny unknown viewers. |
| User forgets to stop sharing | Partially | Points have a configurable expiry (1 hour to 30 days) and the session has a point cap. Old data is automatically purged. However, new points continue to be uploaded until the user stops sharing. |
The core security property of Loch More's location sharing is straightforward: your location data is encrypted on your device and can only be decrypted by someone who has the encryption key. The relay server is structurally unable to access your plaintext location, timestamps, or movement patterns. This is enforced by the mathematics of AES-256-GCM, not by a policy or a promise.