Time synchronization#
How the reader obtains wall-clock time, how that meets Aliro's time-based credential checks, and the fixes this repo carries. The on-air protocol is protocol-research.md.
File references use paths inside the fetched workspace (workspace/); upstream line numbers are as of the pins in west.yml / bootstrap.sh.
Time synchronization and credential validity#
How the reader gets wall-clock time#
- The wall clock on the Zephyr CHIP port is RAM-only:
SetClock_RealTime()stores an offset against the monotonic clock (gBootRealTime,modules/lib/matter/src/platform/Zephyr/SystemTimeSupport.cpp). Any reset, warm or cold, erases it. - Only two things set it: the Matter Time Synchronization cluster's
SetUTCTimecommand and a configured trusted time source. Apple Home sendsSetUTCTimeexactly once, at commissioning (observed in a re-pair capture, 2026-07-17). It configures noTrustedTimeSourceand noDefaultNTP, so the clock is never set again. The device emitsTimeFailureat every boot. - Matter's Last Known Good Time (LKGT) is persisted in the fabric table, but written only at NOC install (certificate notBefore) and inside the cluster's
UpdateUTCTime(src/app/clusters/time-synchronization-server/time-synchronization-cluster.cpp:132). Nothing refreshes it, so it stays frozen at the commissioning timestamp, roughly 30 s before the first Access Document is minted. - The Nordic add-on's
GetCurrentTime()(subsys/aliro/time_utils/src/time_utils_chip.cpp) returns the wall clock when valid and otherwise silently substitutes LKGT, turning "time unknown" into a confidently wrong answer.GetCurrentUnixTime()has no fallback and fails.
Access Document validity#
Access Documents are minted on demand with validFrom set to the phone's current time and validUntil effectively unbounded (year 4001). So VerifyValidityPeriod (applications/matter-aliro-door-lock-app/src/aliro/interface_impl/access_document.cpp), which runs after the signature is verified, degenerates to: the reader clock must not be behind the phone clock.
Failure chain after any reboot, without the fixes below:
- RAM clock is gone;
GetCurrentTime()falls back to LKGT. - LKGT predates every document ever minted.
- Every fresh document is rejected as not-yet-valid (
Current time is outside the Access Document validity period). - Step-up fails closed, permanently, until the lock is re-commissioned.
Fixes carried by this repo's door-lock application#
- Document time ratchet (
CONFIG_DOOR_LOCK_TIME_CONCEPT_RATCHET, default y): when a signature-verified document'svalidFromlies ahead of the reader clock, ratchet the system clock forward tovalidFrom(never backward) instead of failing. Heals from issuer-signed material with no infrastructure. Trade-off: an issuer that signs a future-dated document can advance the clock early, weakening not-before enforcement. - Wall-clock persistence (
CONFIG_DOOR_LOCK_TIME_CONCEPT_PERSIST, default y): a 60 s poll persists the wall clock through the app'sKeyValueStorage(settings keydl/UT) once per hour, immediately when the clock first becomes valid, and after any backward correction; atAliroInitthe system clock is seeded from the stored value if (and only if) it is invalid. Restored time understates real time by at most one hour plus the power-off duration. It deliberately does not refresh Matter's fabric LKGT: LKGT feeds CASE certificate-validity checking, and a ratchet-poisoned time must not be persisted into that path. - Interplay: persist alone fixes the common case (reboot after long uptime); the ratchet alone covers documents minted while the lock was powered off. Because of persist, a ratchet advance survives reboot, as the ratchet Kconfig help states.
Interaction: the BLE advertisement's Dynamic Tag (found on bench, 2026-07-17)#
The Aliro BLE advertisement embeds an expiry timestamp of lock unix time + CONFIG_DOOR_LOCK_ULTRAWIDELOCK_BLE_SERVICE_DYNAMIC_TAG_EXPIRY_DURATION_S (default 900 s) when the wall clock is valid (ultrawidelock_service.cpp, PrepareAdvertisingDataLocked), and "expiry unavailable" when it is not. iPhones accept the unavailable form but silently ignore an advertisement whose expiry lies in their past. A clock that is valid but behind real time by more than the window kills unlock-on-approach for every phone, while NFC and Matter keep working. Both time fixes above create that state (the restored clock understates real time by the accumulated power-off duration; a ratcheted clock understates it by the presented document's age), so apps/nrf5340dk-lock/overlays/ultrawidelock-cred.conf disabled the Dynamic Tag while no real time source existed. With the Matter-timesync seeding merged (the clock steps up on sync) the overlay is back to =y, bench-gated on the seeded clock. Stock firmware never hits this: its clock is either fresh (before the first reboot) or invalid (after), never valid-but-stale. The ESP32 port derives the same tag from its live clock (SNTP-fed; ultrawidelock_ble.c + ultrawidelock_advtag.c), re-derives at half the 900 s window, immediately on a time step, and falls back to the "expiry unavailable" form when the clock is not yet valid.
Deferred: network time (SNTP / DefaultNTP)#
Real network time is the only fix that makes expiry and schedule enforcement accurate, but plain SNTP is unauthenticated: the only input to the trust chain an attacker on the home network or DNS path could control (backward spoof: unlock denial; forward spoof: weakened not-before without issuer keys). It needs the border router to offer a routable path (IPv6 NTP or NAT64/DNS64) and must be fail-open for offline homes. If built later: clamp to never-before-firmware-build-time, cap backward steps, run async with backoff, and rank it below SetUTCTime.
Bench validation#
- Commission, wait ~1 min after the clock becomes valid (first persist write), reboot: boot log shows
Wall clock seeded from persisted value, and the first step-up passes with no ratchet warning. - Power off for a while, mint a fresh document (unlock from a second same-account iPhone), power on: first step-up logs
system time ratcheted forwardand unlocks.
The temporary DBG overlay lines in apps/nrf5340dk-lock/overlays/ultrawidelock-cred.conf (DOOR_LOCK_APP_LOG_LEVEL_DBG, DOOR_LOCK_ULTRAWIDELOCK_TIME_UTILS_LOG_LEVEL_DBG) surface the Current time / validFrom / validUntil values during these tests and should be reverted afterwards.