Add technical paper + link from README

paper/vigil.md: per-ecosystem detection methodology (Apple/FMDN/Samsung/Tile/DULT) and the rotation-clone presence engine (identity-agnostic detection, the rotation-rate squeeze, FP analysis, honest floor). Draft working paper accompanying the reference implementation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0187UiEtasyowhEBYs6s9iF5
This commit is contained in:
Kara Zajac
2026-07-15 00:24:18 -04:00
parent 6a3c39d16d
commit 0fa6a2dd2a
2 changed files with 556 additions and 0 deletions
+550
View File
@@ -0,0 +1,550 @@
# VIGIL: On-Device Temporal Detection of Personal Location Trackers, Including Identity-Rotating Clones
**Kara Zajac** · Black Flag Intel
Draft v0.1 · 2026-07-15 · *working paper, accompanies the VIGIL reference implementation*
---
## Abstract
Consumer item-trackers — Apple AirTag, Tile, Samsung Galaxy SmartTag, and the
Google Find My Device network — have become instruments of stalking: in a survey
of intimate-partner-violence survivors, 44% of those stalked reported physical
location tracking, with vehicles the most common hiding place. Every deployed
countermeasure (Apple *Tracker Detect*, the iOS *Find My* and Android *Unknown
Tracker* alerts, and the open-source *AirGuard*) shares a structural assumption:
it **keys on a device identity** and alarms when the *same* identifier is seen
repeatedly while moving with the victim. This assumption fails in two directions.
It is *spatial* where the true signal is *temporal*; and it is defeated outright
by identity-rotating clones — a commodity ESP32 firmware that cycles ~2,000 Find
My keys (a fresh one every ~30 s) tracked a victim for five days while raising
**zero** alerts on every tool tested.
We present **VIGIL**, an Android application that reframes the problem as
*temporal co-movement*: not "is a tracker near me" but "has *the same* tracker
been at enough of *my* distinct places, over enough time, while close enough to
be on me." VIGIL contributes (1) a cross-ecosystem, fully on-device detector that
parses five BLE wire formats and gates alerts on an RSSI proximity test that
prior tools omit, together with a learned, on-device *baseline* that auto-trusts
the trackers that live where the user lives; and (2) an identity-agnostic
**presence engine** that detects rotating-key clones by the one thing identity
churn cannot hide — an unbroken, coherent, co-moving RF *presence* — and a
**rotation-rate "squeeze"** argument showing that running an identity detector and
a churn detector together leaves no rotation rate that evades both. VIGIL holds no
network permission: the tracker database, the learned baseline, and all inference
run and remain on the device. We describe the per-ecosystem detection methodology,
the temporal model, the presence engine, the implementation, and a proposed
evaluation, and we are explicit about the adaptive-adversary floor below which
single-session on-device detection is information-theoretically impossible.
---
## 1. Introduction
The same crowd-sourced location networks that help people find lost keys also let
a motivated abuser find a person. Apple's Find My network turns a US$29 coin-sized
beacon into a globe-spanning tracker that piggybacks on hundreds of millions of
other people's iPhones; Google's Find My Device network does the same across the
Android install base. Placed in a target's bag, coat, or car, such a beacon
reports the target's location without any infrastructure of the attacker's own.
Platform vendors responded with **unwanted-tracking alerts**, and the security
community produced **AirGuard** (TU Darmstadt), the best open-source detector.
These tools work, in their measured evaluations, *slowly and narrowly*: an
in-the-wild study found an iPhone took 5 h 41 m to alert on an AirTag (and only on
reaching home Wi-Fi), never detected a Chipolo, and that Samsung's own app never
found its own SmartTag. All of them share the same core algorithm — **count
repeat sightings of one BLE identifier; alarm past a threshold** — and all of
them inherit its two structural weaknesses:
1. **Spatial, not temporal.** They reason about *presence*, so they need
hand-tuned thresholds to avoid alarming on the many trackers legitimately
around us. The discriminating fact — that a *stalking* tracker recurs across
the victim's *changing* locations — is used only weakly.
2. **Identity-bound.** Because they track an identifier, any tracker that
*rotates* its identifier faster than the accumulation window is invisible.
Positive Security's *Find You* clone exploits exactly this: ~2,000 pre-loaded
Find My public keys, one broadcast every ~30 s, so every sighting looks like a
brand-new device. In a five-day carry test it defeated iOS, Apple's Tracker
Detect, and AirGuard's background detection.
**VIGIL** is built on the opposite premise. Where OVERWATCH (its sibling project)
asks *what is watching this place*, VIGIL asks *what has been with me* — it is a
temporal detector. Its contributions:
- **A cross-ecosystem, on-device temporal detector.** VIGIL parses Apple Find My,
Google FMDN, Samsung SmartTag, Tile, and the unified DULT format, filters to the
separated-from-owner state where signalled, and escalates a device only when it
clears a **co-movement test** across the user's distinct geographic cells — gated
by an **RSSI proximity check** that AirGuard omits, which rejects the "tracker in
a passing car" false positive.
- **A learned, on-device baseline.** VIGIL learns the places a user dwells and
auto-trusts trackers that recur there across days — the user's own AirTag, a
partner's Tile — collapsing the dominant benign false-positive class without a
server and without the user tagging anything.
- **A presence engine for rotating clones.** VIGIL detects the attack class that
defeats every deployed tool, by abandoning identity tracking in favour of
identity-agnostic *presence continuity*, and we give a squeeze argument
(§5.4) that closes the evasion gap left by either detector alone.
- **A privacy architecture with no network.** VIGIL requests no `INTERNET`
permission. There is no server, no account, and no telemetry.
We are equally explicit about what VIGIL *cannot* do (§5.6, §10): a patient,
low-duty-cycle adaptive adversary can push detection from minutes-latency
single-session to days-latency multi-session, and no on-device passive detector
escapes that floor.
---
## 2. Background and threat model
### 2.1 BLE trackers and the separated state
An item-tracker is a Bluetooth-Low-Energy beacon. When **near its owner** it is
connected or advertises a non-locatable payload; when **separated** from its owner
it broadcasts a payload that any nearby "finder" phone can encrypt-and-upload to
the vendor's network, letting the owner retrieve its location. Two facts make
passive third-party detection possible at all:
1. Only the **separated** state matters for stalking — a tracker with its owner is
not following *you* — and separated trackers advertise loudly (every ~2 s).
2. To be findable, separated trackers **rotate their identifier slowly.** The
Apple/Google **DULT** specification, and each vendor's implementation, rotate
the advertised address/key roughly **once per 24 hours** in the separated state
(versus ~15 minutes near-owner). A separated AirTag holds one identity for a
full day, re-rolling at 04:00 local. This slow rotation is *deliberate* — it is
what lets a victim's phone recognise the same tracker over time — and it is the
invariant the clone attack violates and that VIGIL's presence engine exploits.
### 2.2 Threat model
**Adversary.** Plants one or more trackers on the victim (bag, vehicle, clothing).
May use (a) a genuine, unmodified tracker; (b) a genuine tracker with a physically
disabled speaker; or (c) a modified/cloned emitter (custom firmware, rotating
keys, silent). The adversary controls the tracker but not the victim's phone.
**Defender.** The victim's Android phone, running VIGIL, **passively** scanning
BLE. It never transmits to or connects to the tracker for detection (an optional,
user-initiated GATT "make it ring" is out of the passive path). It has no server.
**Goal.** Alert the victim, with useful latency and a tolerable false-positive
rate, that a tracker is travelling with them — *including* when the tracker rotates
its identity or is silent.
**Non-goals.** VIGIL does not track individuals, does not deanonymise tracker
owners beyond the vendor-provided reactive flows, and does not claim to defeat a
patient adaptive adversary in a single session (§5.6).
---
## 3. Per-ecosystem detection
VIGIL's scanner (`scan/BleTrackerScanner.kt`) runs a hardware-filtered BLE scan —
the controller wakes the app only on a signature match, which makes screen-off
scanning deliverable — and hands each match to a stateless parser
(`scan/TrackerParser.kt`) that recognises five wire formats. For each ecosystem we
describe (a) the on-air **signature** VIGIL matches, (b) how it reads the
**separated** state, and (c) the **identity** it keys on for temporal correlation.
### 3.1 Apple Find My / AirTag
**Signature.** BLE manufacturer-specific data under Apple company id `0x004C`,
message type `0x12` (offline finding), payload length `0x19` (25 bytes). After
Android strips the two company-id bytes, the payload is
`[type=0x12, length=0x19, status, key(22), keyTopBits, hint]`.
**Separated state.** The **status byte** carries a "maintained" bit (bit 2): set
when the owner was in range within the current rotation period, cleared when
separated. VIGIL treats a cleared/absent maintained bit as separated. (Setting the
status byte to `0x00` is a known first-party-alert-suppression trick, so VIGIL
does not treat the bit as ground truth for *benign-ness* — only for *relevance*.)
**Identity.** The advertised P-224 public key **is** the identity, and — uniquely
— the BLE MAC is *derived from the key*, so both rotate together. VIGIL keys on a
hash of the advertised key bytes, which is stable across the ~24 h separated
epoch. The 04:00 re-roll intentionally severs continuity; §5 addresses bridging it.
### 3.2 Google Find My Device network (FMDN / "Find Hub")
**Signature.** BLE service data (AD type `0x16`) under 16-bit UUID `0xFEAA` (the
Eddystone UUID; pairing uses a different service, `0xFE2C`). The first
service-data byte is the frame type.
**Separated state.** VIGIL reads it **in cleartext**: frame `0x40` = normal, frame
`0x41` = unwanted-tracking/separated. A subtlety with defensive consequences: the
`0x41` state is *server-triggered* — Google decides a tag has been separated
(~≥30 min) and a nearby Android device flips it. A freshly-separated stock tag is
therefore still rotating its EID (~every 1024 s) and MAC together and is **not
passively re-linkable** until flipped to `0x41`, at which point the MAC freezes for
24 h. VIGIL keys FMDN identity on the MAC, valid within the separated epoch.
### 3.3 Samsung Galaxy SmartTag / SmartTag2
**Signature.** BLE service data under UUID `0xFD5A` (the Samsung company id
`0x0075` is emitted by all Samsung gear and is not tag-unique; the service UUID is
the anchor). A connectable/setup tag also exposes a `smarttag` device name.
**Separated state.** The first service-data byte encodes a 3-bit state (bits 57):
`2` = lost (after 15 min), `3` = "overmature-lost" (after 24 h; triggers Samsung's
own anti-stalking), `4``6` = paired/connected. VIGIL treats states 23 as
separated. In overmature state the rotating privacy id becomes daily-static,
giving a ~24 h re-link window; in the first 24 h everything rotates every 15 min
and is not passively re-linkable.
### 3.4 Tile
**Signature.** BLE service data under `0xFEED` (activated), `0xFEEC`
(pre-activation), or the legacy `0xFE84`.
**Separated state.** *None.* A Tile emits no separated/lost-state beacon — it is
"always findable," advertising identically whether with its owner or lost.
**Identity.** Tile's decisive property: the **BLE MAC is static and never
rotates**, in any state. This makes Tile the easiest ecosystem to correlate (an
indefinite persistent key) but the one with no separated flag, so VIGIL treats
every Tile as a live candidate and relies entirely on persistence + co-movement.
Notably, Tile's "Anti-Theft Mode" — which hides a Tile from Tile's *own*
Scan-and-Secure and from platform alerts — changes nothing at the BLE layer, so a
third-party scanner such as VIGIL detects an anti-theft Tile identically to a
normal one, closing a documented stalking gap.
### 3.5 DULT (unified, emerging)
**Signature.** BLE service data under UUID `0xFCB2`.
**Separated state.** The near-owner bit is the least-significant bit of byte 14
(`1` = near owner, `0` = separated); a Network-ID byte identifies the vendor
network. VIGIL parses this for forward compatibility as trackers migrate to the
unified format.
### 3.6 Partner tags and summary
Chipolo, Pebblebee, eufy, Motorola, Jio, and Rolling Square do **not** carry a
brand-specific signature: each SKU advertises as the *network* it was provisioned
to (Apple `0x004C`/`0x12` or Google FMDN `0xFEAA`). VIGIL detects the network, not
the brand; legacy proprietary SKUs (classic Chipolo, original JioTag) join no
crowd network and are out of scope.
| Ecosystem | Signature | Separated signal | MAC rotation (separated) | Re-link window |
|---|---|---|---|---|
| Apple Find My | mfg `0x004C`, type `0x12` | status "maintained" bit cleared | ~24 h (re-roll 04:00), MAC = key | ~24 h |
| Google FMDN | svc data `0xFEAA`, frame `0x41` | cleartext frame `0x41` (server-gated) | 24 h once `0x41`; else ~1024 s | ~24 h once separated |
| Samsung SmartTag | svc data `0xFD5A` | state byte 2/3 (lost/overmature) | 24 h once overmature; else 15 min | ~24 h once overmature |
| Tile | svc data `0xFEED`/`0xFEEC` | none (always findable) | **none — static MAC** | indefinite |
| DULT (unified) | svc data `0xFCB2` | byte-14 LSB = 0 | ~24 h separated | ~24 h |
---
## 4. The temporal co-movement model
### 4.1 The reframing
A tracker being *near* a user is uninformative — modern environments are saturated
with them. The stalking signal is **persistence across the user's own changing
locations**: because the phone is (almost) always on the user, counting a
tracker's presence across the user's *distinct places* over time *is* the
co-movement measure, and it needs no second trajectory.
### 4.2 The test
Each parsed sighting is geotagged with a coarse fused-location fix and written to
an on-device store (`data/db/VigilDatabase.kt`) as `(trackerId, timestamp, rssi,
separated, lat, lon, geohash7)`, retained 14 days. Over the trailing 24 h a
tracker is scored on:
- **Effective sightings** — raw sightings debounced to at most one per 15 min, so a
2 s-interval beacon cannot trivially inflate the count. Require ≥ 3.
- **Distinct places** — the number of distinct **geohash-7 cells** (~153 m) among
its sightings. Require ≥ N, with N = 2/3/4 by sensitivity (high/medium/low).
- **Span** — first-to-last minutes. Require ≥ T, with T = 30/45/90 min.
- **RSSI proximity gate** — the smoothed peak RSSI must at least once cross an
on-body/in-bag floor (90/85/80 dBm by sensitivity). **This is the piece
AirGuard omits**, and it is what distinguishes a tracker *on* the user from one
that merely shared a road with them at two lights.
A device escalates `OBSERVED → SUSPICIOUS → ALERTING` (`detect/CoMovementEvaluator.kt`).
ALERTING requires all four conditions; SUSPICIOUS requires repeated close sightings
without yet enough spatial/temporal spread. Non-separated (near-owner) sightings
never escalate. These thresholds are adapted from AirGuard's field-tuned model
(≥3 sightings, N distinct locations ≥150 m apart, T minutes) with the added RSSI
gate.
### 4.3 The learned baseline and the allowlist
Two trust signals suppress the dominant benign class — the trackers that *belong*
in the user's life.
- **Allowlist.** The user marks a tracker "This is mine"; it never alerts again.
- **Learned baseline** (`detect/BaselineManager.kt`). VIGIL counts location-tagged
sightings per geohash-6 cell; a well-visited cell becomes an **anchor** (home,
work). A tracker seen at an anchor on ≥ 3 distinct calendar days is auto-marked
*baseline-safe* and excluded from alerting. This learns "the AirTag that is
always in my house" without a server and without the user labelling anything.
(The current visit-count anchor heuristic is a v1; the roadmap replaces it with
stay-point/dwell clustering.)
---
## 5. The rotation-clone presence engine
This section addresses the attack class that defeats every deployed detector, and
is the paper's core modeling contribution. The full design is in
`docs/detection-rotation-clone.md`; we summarise the argument here.
### 5.1 The attack and why identity detectors fail
The *Find You* clone is one physical ESP32 radio that broadcasts a pool of ~2,000
Apple Find My public keys, cycling to a new one every ~30 s. To any identity-keyed
detector each key is a new device seen once, so the "same identifier ≥ 3 times"
counter never accumulates and the detector stays silent — measured at zero alerts
over five days.
### 5.2 The exploitable invariant
The defence begins with a standards fact: DULT, Apple, and Google **mandate ~24 h
rotation in the separated state**. A legitimate separated tracker therefore holds
one identity for a full day; a clone rotating every 30 s runs **~2,880× faster
than any specification-compliant device**. This is not a heuristic "that seems
suspicious" — it is a *categorical* violation of a standardised invariant, giving
a clean anomaly baseline.
### 5.3 Presence, not identity
VIGIL stops tracking *who* is present and starts tracking *whether one radio is
continuously present*. A rotating clone, to function as a tracker, must emit an
**unbroken stream of separated-state beacons at roughly stable close-range RSSI
that moves with the victim across locations where the ambient tracker population
fully turns over.** We formalise three identity-agnostic quantities:
- **Novel-identifier churn rate** `λ_novel` — the rate of never-before-seen
separated identities (over a 5-min window against a 30-min novelty set). A real
separated tracker ≈ 0.0007/min; the clone ≈ 2/min.
- **Presence continuity** — the duty cycle with which *some* separated beacon
occupies the close band (RSSI ≥ ~65 dBm), and the longest unbroken run.
- **Seamless-novel-handover chain** — the number of times the close-band identity
*changes* to a **novel** id with **no RSSI discontinuity** (`|Δrssi| ≤ 8 dB`)
and **no time gap** (`Δt ≤ 6 s`). One physical radio produces a long chain of
such seamless handovers; a crowd of many radios at many distances produces
gappy, RSSI-discontinuous handovers. This chain is the tell no amount of identity
churn can fake while remaining a functioning tracker.
The physics that make co-movement a hard discriminator: a static tag or an
independently-moving passer can remain in the close band (radius R ≈ 10 m) for at
most `2R / v_rel` — ≈ 1.5 s at driving speed. Only a co-moving emitter (relative
velocity ≈ 0) has unbounded dwell. So a close-band presence sustained across ≥ 3
distinct cells while the user is moving *cannot* be explained by any single
ambient source.
### 5.4 The pipeline and the squeeze
A three-stage streaming pipeline (all O(1)-ish per sighting, on-device): a **CUSUM
change-point** on `λ_novel` versus a rolling personal baseline *triggers* an
identity-agnostic **presence-track confirmer** (counting seamless novel handovers
and RSSI coherence), which alarms only behind a hard **co-movement gate** (net
displacement ≥ 500 m across ≥ 3 cells while moving).
The robustness argument is the **rotation-rate squeeze.** VIGIL runs the classic
identity detector (which catches any identity persisting > ~35 min) *and* the
churn detector (which catches sustained novel-churn above ~0.51/min)
simultaneously:
```
rotation period: 30 s ─── 2 min ─── 5 min ──────────── 24 h
└ churn path catches ┘
└ overlap ┘
└────── identity path catches ──────┘
```
**No rotation period evades both.** Fast rotation is caught by churn; slow rotation
by identity; the middle by both. Moreover the presence-continuity path is
rotation-*rate*-agnostic — slowing rotation lowers `λ_novel` but does nothing to
the unbroken coherent co-moving track. The adversary's only real lever is
*continuity*, not rate.
### 5.5 False positives
The engine's viability rests on dense-urban and transit false positives. Four
discriminators, strongest first: (1) the **seamless-handover chain** — a crowd is
many radios and cannot fake one continuous `(t, rssi)` track across 500 m; (2)
**co-movement across turnover** — bounded ambient dwell means a presence spanning
≥ 3 non-adjacent cells is not any single source; (3) **RSSI coherence** — one
radio yields a smooth RSSI random-walk, a crowd a high-variance cloud; (4)
**stationary suppression** — no motion, no alarm, which removes the entire
apartment/at-home class. The genuinely hard case is **live rush-hour transit**,
where boarding churn, vehicular co-movement, and continuous occupancy align. The
resolution is a **mode-change gate**: a transit crowd cannot follow the victim
*off the train and down the street*, so escalation to a hard alert is withheld
until the coherent co-moving presence survives a detected context transition. This
trades **latency** for transit-FP suppression — a deliberate, disclosed choice,
not a false alarm.
### 5.6 The honest floor
Every evasion degrades the adversary's own tracker (slower/duty-cycled rotation
and reduced TX power mean fewer, coarser location fixes). Below some duty cycle — a
few randomly-timed, random-power beacons per 15 min — the emitter becomes
information-theoretically indistinguishable, *within a single session*, from sparse
ambient pass-bys, and detection must move to **cross-session recurrence** (the same
anomalous weakly-co-moving presence recurring day after day along the victim's
routes), which costs **days of latency** and requires opt-in persistent storage.
This is the honest floor: a patient, low-duty-cycle adaptive adversary forces
VIGIL from minutes-latency single-session detection to days-latency multi-session
detection. VIGIL states this rather than claiming an impossible guarantee.
---
## 6. Implementation
VIGIL is a native Android/Kotlin app (min SDK 26), reusing the scanning and
service architecture of OVERWATCH.
```
scan/TrackerSignatures.kt BLE hex signatures for every ecosystem
scan/TrackerParser.kt ScanResult -> TrackerObservation (five wire formats)
scan/BleTrackerScanner.kt hardware-filtered scan (screen-off capable)
service/ScanService.kt foreground service; geotags, persists, alerts, vibrates
data/db/VigilDatabase.kt Room: trackers, 14-day sightings, baseline places
data/TrackerRepository.kt ingest + baseline + evaluate on each sighting
detect/CoMovementEvaluator the co-movement test + RSSI proximity gate
detect/BaselineManager.kt anchor-place learning -> auto-trust household tags
detect/PresenceEngine.kt (next) the rotation-clone engine of §5
```
**Privacy properties.** The manifest declares **no `INTERNET` permission**; VIGIL
cannot open a socket. All state — trackers, sightings, baseline, inference — lives
in an on-device SQLite database and never leaves the phone. `BLUETOOTH_SCAN` is
declared *without* `neverForLocation`, because detection is intentionally
location-correlated (and because that flag would filter out the very beacon-shaped
advertisements VIGIL parses). VIGIL only listens: it never advertises, probes, or
connects to a tracker on the detection path.
---
## 7. Proposed evaluation
VIGIL is a prototype; the following methodology is specified but **not yet
executed**, and no detection-rate claims are made here.
- **Red-team emitter.** A parameterised OpenHaystack/*Find You* ESP32 clone with
sweepable rotation period (30 s … 24 h), TX-power variance, and duty cycle. Carried,
it is a real co-moving emitter — ground-truth positive without endangering anyone.
- **Ambient baselines.** Raw sighting logs across environment classes (rural drive,
suburban, dense-urban walk, bus, rush-hour subway, highway, apartment block,
crowd), each with paired emitter-on/off runs.
- **Record/replay harness.** The presence engine is a pure
`List<Sighting> → List<ScoredEvent>` function; ambient logs are recorded once and
replayed to tune thresholds deterministically and offline in Kotlin, and
attack-in-context scenarios are synthesised by merging clean and emitter-only
traces.
- **Metrics.** True-positive rate at a fixed false-positive budget (headline:
TPR at ≤ 1 FP per active-carry-day, per class); detection latency
(attach → first alert); ROC/AUC; robustness curves versus rotation period, TX
power, and duty cycle; and ablations isolating each discriminator.
---
## 8. Comparison to prior art
| Capability | Apple Tracker Detect | iOS / Android built-in | AirGuard | **VIGIL** |
|---|---|---|---|---|
| Cross-ecosystem | Find My only | AirTag + FMDN + DULT partners (not Tile) | yes | yes |
| Background / automatic | no (manual scan) | yes | yes | yes |
| Fully on-device / no account | yes | yes | yes | **yes (no network permission)** |
| RSSI proximity gate on alerts | no | undisclosed | **no** | **yes** |
| Learned baseline (auto-trust) | no | no | manual ignore only | **yes** |
| Detects rotating-key clones | no | no | no | **designed (§5)** |
| Covers Tile anti-theft | no | no | yes (static MAC) | yes |
| Survivor-grade evidence export | no | no | limited | roadmap |
Cross-ecosystem, offline operation, and temporal co-movement are *not* novel —
AirGuard has them. VIGIL differentiates on the pieces AirGuard's architecture
forecloses: the RSSI gate, the learned baseline, and above all the identity-agnostic
presence engine that targets the clone attack.
---
## 9. Privacy and ethics
VIGIL is a defensive, survivor-oriented tool. It maps and reasons about
*trackers*, never about people, and holds no network to which anything could be
exfiltrated. The learned baseline is a privacy *feature* — it exists so the app
does not need to phone home to know which tags are "normal." The dual-use surface
(BLE parsing that could in principle be repurposed for tracking) is mitigated by
the passive-only, no-transmit, no-network design. As with all anti-stalking tools,
VIGIL should be paired with survivor-safety guidance: a detected tracker can
indicate an abuser with physical access, and "make it ring / remove the battery"
advice must be given with situational-safety caveats.
---
## 10. Limitations
- **Single-session floor (§5.6).** A patient low-duty-cycle adaptive adversary is
only detectable across sessions.
- **Near-owner and freshly-separated tags** are not passively re-linkable (Apple
near-owner; default FMDN before the `0x41` flip; Samsung in the first 24 h).
- **Wire-format offsets** are drawn from published reverse-engineering and, for
SmartTag2 and 2024+ Tile, inferred from older captures; they require empirical
validation before production trust.
- **Background scan latency** is bounded by Android's ~15-min background floor
absent a persistent foreground service; motion-triggered scanning (roadmap)
reduces it.
- **Not yet evaluated.** §7 is a plan, not a result.
---
## 11. Related work
Heinrich et al. reverse-engineered Apple Find My ("Who Can Find My Devices?", PETS
2021) and built AirGuard ("Protecting Android Users from Stalking Attacks by Apple
Find My Devices", WiSec 2022), later measuring real-world stalking prevalence and
detector efficacy ("Please Unstalk Me", PETS 2024). Mayberry/Shafqat et al.
("Who Tracks the Trackers?" / "Track You", PETS 2023) analysed AirTag anti-stalking
timing. Yu et al. (USENIX Security 2024) reverse-engineered Samsung SmartTag, and
Georgia Tech (2025) analysed Tile's static-MAC linkability. Bräunlein/Positive
Security demonstrated the rotating-key clone ("Find You"). Naturalistic evaluations
("Stop Following Me!", EuroUSEC 2024) found deployed detectors rarely useful. The
Apple/Google **DULT** IETF effort standardises the separated-state signalling and a
baseline detection algorithm. VIGIL builds directly on this line and extends it to
the identity-rotating adversary that all of it currently misses. Change-point
detection (Page 1954; Basseville & Nikiforov 1993) and bounded-memory distinct
counting (Datar et al.; Flajolet et al.) underpin the presence engine; the
identity-free "sustained close presence" reasoning is anticipated by
contact-tracing designs (DP-3T; Google/Apple Exposure Notification).
---
## 12. Conclusion
The anti-stalking detectors people rely on are spatial where the threat is
temporal, and identity-bound where the adversary can rotate identities. VIGIL
reframes detection as temporal co-movement, adds the proximity gate and learned
baseline that make that reframing practical on-device and private, and introduces
a presence engine that detects the rotating-key clone by the one property it cannot
hide. The rotation-rate squeeze shows the combination leaves no safe rotation rate;
the honest floor shows where even this stops. We release VIGIL as a working
prototype and an open design, and invite the empirical evaluation §7 lays out.
---
## References
1. A. Heinrich, M. Stute, T. Kornhuber, M. Hollick. "Who Can Find My Devices? Security and Privacy of Apple's Crowd-Sourced Bluetooth Location Tracking System." *PoPETs* 2021. https://petsymposium.org/popets/2021/popets-2021-0045.pdf
2. A. Heinrich, N. Bittner, M. Hollick. "AirGuard — Protecting Android Users from Stalking Attacks by Apple Find My Devices." *ACM WiSec* 2022. https://arxiv.org/pdf/2202.11813 · code: https://github.com/seemoo-lab/AirGuard
3. A. Heinrich, L. Würsching, M. Hollick. "Please Unstalk Me: Understanding Stalking with Bluetooth Trackers and Democratizing Anti-Stalking Protection." *PoPETs* 2024. https://petsymposium.org/popets/2024/popets-2024-0082.pdf
4. N. Shafqat et al. "Track You: A Deep Dive into Safety Alerts for Apple AirTags." *PoPETs* 2023. https://petsymposium.org/popets/2023/popets-2023-0102.pdf
5. T. Yu et al. "Analyzing the Security of Samsung SmartTag." *USENIX Security* 2024. https://arxiv.org/pdf/2210.14702
6. Kumar, Raymaker, Specter. Tile protocol analysis. 2025. https://arxiv.org/html/2510.00350v1
7. F. Bräunlein / Positive Security. "Find You: Building a stealth AirTag clone." https://positive.security/blog/find-you · https://github.com/positive-security/find-you
8. A. Catley. "AirTag reverse engineering." https://adamcatley.com/AirTag.html
9. IETF DULT WG. "Detecting Unwanted Location Trackers" (accessory-protocol; threat-model). https://datatracker.ietf.org/wg/dult/
10. Google. "Find My Device network (Fast Pair FMDN) specification." https://developers.google.com/nearby/fast-pair/specifications/extensions/fmdn
11. SEEMOO-Lab. "OpenHaystack." https://github.com/seemoo-lab/openhaystack
12. "Stop Following Me! Evaluating the Effectiveness of Anti-Stalking Features." *EuroUSEC* 2024. https://arxiv.org/abs/2312.07157
13. E. S. Page. "Continuous Inspection Schemes." *Biometrika* 41, 1954. (CUSUM.)
14. M. Basseville, I. Nikiforov. *Detection of Abrupt Changes: Theory and Application.* 1993.
15. M. Datar, A. Gionis, P. Indyk, R. Motwani. "Maintaining Stream Statistics over Sliding Windows." *SODA* 2002.
*This working paper accompanies the VIGIL reference implementation. The rotation-clone
engine (§5) is fully specified in `docs/detection-rotation-clone.md` and is designed
but not yet wired into the shipping build.*