Epoch Time vs Device Uptime for BLE Identity Rotation: How to Choose

Most firmware engineers spend weeks getting the cryptography right for BLE EID rotation, then pick the time source in an afternoon. That’s backwards. The crypto is a solved problem (AES-ECB, HKDF, pick your favorite). The time source is where your ephemeral ID scheme actually breaks in production: drift that silently grows until observers can’t resolve your device, counters that reset on a brownout, or a provisioning step you forgot to design into the supply chain.
You’ve already committed to rotating your BLE device identity. The question now is what drives the clock behind that rotation. You have two real options: wall-clock epoch time from an RTC, or a monotonic counter based on device uptime. No hybrid recommendation. By the end, you’ll know which fits your hardware, your power budget, and your observer architecture.
How the Rotation Function Consumes Time
The BLE EID rotation pipeline looks the same regardless of which time source you choose. A shared secret key and a time-varying input feed into a derivation function, and out comes an ephemeral identifier.
+-------------------+ +----------------+ +-------------+
| Shared Secret Key | ----> | EID Function | ----> | Ephemeral |
+-------------------+ | (AES/HKDF/...) | | Identifier |
+----------------+ +-------------+
^
|
+------------------+
| Time-Varying |
| Input |
| (THIS IS THE |
| DECISION) |
+------------------+The time-varying input increments at defined intervals, producing a new EID each rotation. Google’s Eddystone-EID spec is probably the best-known example, using epoch time as its input. But if you’re building a custom protocol (not bound to Eddystone or another SIG spec), you get to choose what that input actually is.
Everything else in the pipeline (the key, the crypto primitive, the output length) stays identical.
Option A: Epoch Time (RTC-Based Rotation)
How It Works
Your device keeps Unix epoch time via a hardware RTC. The rotation period is derived by integer division:
rotation_period = floor(epoch_seconds / rotation_interval)Both the device and any authorized observer can compute the current EID window independently, as long as they share the key and know the rotation interval. They don’t need to hear each other. They just need to agree on what time it is.
Apple’s Find My network uses a variant of this principle: epoch-synchronized key rotation that lets servers resolve identities without real-time contact with the device.
What You Need
A hardware RTC or a well-calibrated 32.768 kHz low-frequency oscillator. Most modern BLE SoCs (nRF52, EFR32, CC2640) have one on-die or support an external crystal.
Initial time provisioning. The device needs to learn the current time at least once. This could be a BLE GATT write during onboarding, an NFC tap on the factory floor, or a value flashed during manufacturing. However you do it, this is a supply chain step you have to design.
A drift budget. RTC crystals aren’t perfect:
Crystal Accuracy | 30 Days Drift | 90 Days Drift | 180 Days Drift
-----------------+---------------+---------------+----------------
±20 ppm | ±51.8 sec | ±155.5 sec | ±311.0 sec
±100 ppm | ±259.2 sec | ±777.6 sec | ±1555.2 secWith a ±20 ppm crystal (like an Epson FC-135), you’re off by about 5 minutes after 6 months. A cheap ±100 ppm oscillator drifts over 25 minutes in the same period. Your observer needs to widen its EID resolution window to account for this, or you need a re-sync policy.
When to Pick This
Choose epoch time when the observer must independently derive the current EID without hearing the device first. Server-side lookups, offline resolution, fleet-wide synchronized rotation windows: these all demand wall-clock time.
If your hardware already has an RTC and you have a provisioning moment (manufacturing, onboarding, first connection), epoch time is strictly more capable than an uptime counter.
Gotchas
The RTC draws continuous current, typically 0.5 to 2 µA. On a CR2032 coin cell (roughly 225 mAh), that’s a non-trivial baseline. Factor it into your power budget.
Time provisioning is a process you have to build and maintain. Every device that ships without a correct timestamp broadcasts wrong EIDs.
Drift is silent. Your device won’t throw an error when it’s 4 minutes off. Define a re-sync policy upfront, or widen your observer’s resolution window to cover worst-case drift at your maximum expected time between syncs.
Option B: Device Uptime (Monotonic Counter Rotation)
How It Works
The device maintains a tick counter from boot (or from the last known counter value read from flash). Rotation works the same way:
rotation_period = floor(ticks_since_boot / rotation_interval_ticks)No external time reference needed. The counter increments from whatever timer peripheral you have available: SysTick, a low-power timer, or an RTOS tick.
If you need the counter to survive reboots, you can periodically persist it to flash. You’re essentially stitching together multiple boot sessions into one continuous count, but the source is still uptime-based.
What You Need
A reliable system tick or low-power timer. Every BLE SoC has one. No additional hardware.
Flash persistence (if reboots happen). Write the counter to flash at intervals, but do the write-wear math first. A typical NOR flash page handles 10,000 to 100,000 erase cycles. Persisting every 10 minutes for 5 years means roughly 263,000 writes, so you’ll need wear leveling or a dedicated flash region.
An observer that hears the device directly. The observer can’t predict the EID offline. It has to receive the advertisement, scan response, or connected payload to learn the current identity.
When to Pick This
Your hardware has no RTC and adding one breaks the BOM target. Your device gets commissioned once, runs for months or years, and rarely reboots. Your observer always sees the advertisement directly; it never needs to compute “what should this device be broadcasting right now?” independently.
You want zero provisioning dependencies. The device boots, starts counting, and rotates. Nothing to sync.
Gotchas
Reboots without flash persistence reset the EID sequence to zero. A recently rebooted device could temporarily broadcast the same EID it used right after its previous boot, which is a privacy failure if reboots are frequent.
Flash persistence introduces its own complexity: write alignment, wear leveling, and the gap between the last persisted value and the actual counter at crash time. Budget time for getting this right.
If a device goes silent for a week and then reappears, the observer has no way to precompute what EID to expect.
The Decision Tree
Answer three questions. Get a definitive answer.
Q1: Must the observer derive the current EID
WITHOUT hearing the device first?
|
+-- YES --> Use Epoch Time (Option A)
|
+-- NO
|
Q2: Does your hardware have an RTC
(or budget for one)?
|
+-- YES --> Use Epoch Time (Option A)
| (it's strictly more capable)
|
+-- NO
|
Q3: Can you guarantee infrequent reboots
OR implement flash-based counter persistence?
|
+-- YES --> Use Uptime Counter (Option B)
|
+-- NO --> Add an RTC. Redesign the BOM.
Uptime without persistence on a
frequently-rebooting device is
a privacy failure.If Q1 is YES, the conversation is over. You need epoch time.
If you land on the bottom “NO” branch, neither option works cleanly with your current hardware. Something has to change: either add an RTC (a few cents for an external crystal, a few hundred µA for a module) or solve the reboot problem. Shipping a BLE device identity rotation scheme that resets on every power cycle is a bug, plain and simple.
Implementation Sketches
Here’s the minimal EID derivation for each approach. The only difference is the time source.
Epoch-based:
uint32_t rotation_period = rtc_get_epoch() / ROTATION_INTERVAL_SEC;
uint8_t input[16] = {0};
memcpy(input, &rotation_period, sizeof(rotation_period));
aes_ecb_encrypt(shared_key, input, eid_output);Uptime-based:
uint32_t rotation_period = ticks_since_boot / ROTATION_INTERVAL_TICKS;
uint8_t input[16] = {0};
memcpy(input, &rotation_period, sizeof(rotation_period));
aes_ecb_encrypt(shared_key, input, eid_output);Identical structure. Identical crypto. The time source is the only moving part.
If you’re building on a supported BLE SoC, the Hubble Device SDK handles much of the lower-level advertising configuration so you can focus on the rotation logic itself.
Mistakes That Will Cost You a Week
32-bit tick overflow. A millisecond-resolution uint32_t overflows at roughly 49.7 days. Your EID rotation period will wrap, and your device will rebroadcast old EIDs. Use a 64-bit counter or handle the overflow explicitly.
Rotation intervals shorter than observer scan windows. If your EID rotates every 5 seconds but your observer scans in 10-second windows, the observer may never see the same EID twice and can’t resolve the identity. Your rotation interval should be at least 2–3x your longest expected observer scan interval.
Confusing BLE random address rotation with EID rotation. BLE anonymous advertising uses rotating random addresses at the link layer (Bluetooth Core Spec v5.4, Vol 6, Part B, Section 1.3). Your application-layer EID rotation is a separate mechanism. They should be synchronized (rotate the BLE address when you rotate the EID) but they aren’t the same thing. Forgetting this lets an observer correlate your device across EID rotations by tracking the unchanged link-layer address.
Storing the shared key alongside the persisted counter. If both live in the same flash region and an attacker dumps the flash, they can reconstruct every past and future EID. Keep the key in a separate secure element, or at minimum a different flash partition with distinct access controls. The Hubble device security model covers key storage patterns worth reviewing.
Pick Your Time Source, Then Tune Your Interval
The cryptography doesn’t care. AES-ECB doesn’t know whether the input came from an RTC or a SysTick counter.
Go back to the decision tree. Answer Q1 honestly. If the observer needs offline EID resolution, you need epoch time. If your device has an RTC and a provisioning step, you should probably use epoch time anyway. If you’re truly cost-constrained with no RTC and stable uptime, the monotonic counter works, but you have to handle reboots.
Pick one. Implement it. The next problem is tuning the rotation interval itself: too fast and observers can’t resolve, too slow and you leak trackable windows. That deserves its own treatment.
Hubble Network enables BLE connectivity from any device, anywhere—no gateways or proximity constraints required. See how it works →