openaliro
Aliro reader: UWB/CCC core and ESP32-S3/C5/C6 port
Loading...
Searching...
No Matches
aliro_reader.h
1/*
2 * Copyright (c) 2026 asxeem
3 * SPDX-License-Identifier: ISC
4 *
5 * aliro_reader — Aliro reader session/transaction layer. Owns the per-connection
6 * Aliro transaction on top of the aliro_ble transport: session lifecycle, the
7 * credential-auth exchange (AUTH0/AUTH1/EXCHANGE), the reader identity and
8 * credential trust gate, the M1-M4 ranging setup, and the handoff of the derived
9 * URSK plus negotiated ranging parameters to the UWB engine.
10 *
11 * Crypto lives in the aliro_crypto component; the wire codec in aliro_apdu.c and
12 * the ranging setup in aliro_ranging.c. See README.md for the flow and
13 * components/aliro_ble/SPEC.md for the transport contract.
14 */
15#pragma once
16
17#include <stdbool.h>
18#include <stdint.h>
19
20#ifdef __cplusplus
21extern "C" {
22#endif
23
26int aliro_reader_start(void);
27
28/* ---- Attach mode: coexist with a host another stack owns (esp-matter) ----- *
29 * Two phases so the reader shares one BLE controller with Matter:
30 * aliro_reader_ble_prepare() runs BEFORE the host stack starts its GATT server
31 * and returns the Aliro GATT service definition to register via that stack's
32 * hook; aliro_reader_start_attached() runs once the host is up + the device is
33 * operational (the owner has released the advertiser). */
34
37const void *aliro_reader_ble_prepare(void);
38
41int aliro_reader_start_attached(void);
42
48void aliro_reader_refresh_adv(void);
49
50/* Observe the lock state the reader has just ANNOUNCED to the phone: true when
51 * the grant that fires the Wallet animation goes out, false when the walk-away
52 * relock does.
53 *
54 * Distinct from the access listener above, which reports a credential verdict at
55 * authentication time. That fires on the unlock and never on the relock, so a
56 * Matter tile driven from it would show a lock that opens and never closes.
57 *
58 * Called from the BLE-host task the moment the notification is sent, so the
59 * listener must return immediately and must not block. NULL to unregister.
60 * Unconditional, unlike the access hook: this fires twice per unlock rather than
61 * on the transaction path, so a NULL check is not worth a Kconfig. */
62void aliro_reader_set_lock_state_listener(void (*cb)(bool unlocked));
63
64/* Feed one BLE connection-RSSI sample (dBm) into the session's ranging power gate
65 * (CONFIG_WOZ_RSSI_GATE; absent without it). The transport polls the controller
66 * every CONFIG_WOZ_RSSI_GATE_POLL_MS while its CoC is up and calls this from the
67 * BLE-host task. The gate holds AP-Completed — and with it the whole UWB radio —
68 * until the phone is inside the open threshold, and tears ranging down again on a
69 * sustained fade below the close threshold. */
70void aliro_reader_rssi_sample(uint16_t conn_handle, int8_t rssi_dbm);
71
72/* Send the phone a "Reader Status Changed" SDU (Aliro transaction step 23) over the
73 * active ranging session's BleSK channel: `unsecured` true on an approach grant (this
74 * is what fires the iPhone Wallet unlock animation), false on relock. Safe to call
75 * from any task -- it marshals the send onto the BLE-host task. No-op if no ranging
76 * session is established. */
77void aliro_reader_notify_unlock(bool unsecured);
78
79/* Drives the one deferred piece of the above: a Secured that could not be delivered
80 * because the peer had already gone is held for a few seconds after the next session
81 * establishes, so that a phone which merely woke on the doorstep is not shown a lock
82 * its own grant undoes a second later. Call from any periodic loop with a monotonic
83 * millisecond clock; cheap enough to call unconditionally, and a no-op unless a
84 * replay is pending. Nothing else needs it -- an ordinary walk-up never arms one. */
85void aliro_reader_status_tick(int64_t now_ms);
86
87/* True while some peer holds an established Aliro session (auth done, ranging
88 * channel up). This is the reader's presence signal, and it is the one an approach
89 * controller should relock on: ranging silence is not a departure, because iOS
90 * pauses ranging when the phone stops moving (bench: 3.07 s with the phone 26 cm
91 * from the reader). The link ending is a departure, and the RSSI gate's close path
92 * is what ends the link when the peer walks out of range. Safe to call from any
93 * task -- a plain read of the session table, no lock needed for a boolean. */
94bool aliro_reader_session_active(void);
95
96/* Register a listener for the per-transaction access verdict: true once a
97 * credential has authenticated and passed the trust gate (including the
98 * expedited-fast path and the dev-identity accept), false when one was presented
99 * and rejected. This is the same decision the nRF5340's vendor application prints
100 * as ACCESS GRANTED / ACCESS DENIED, and it is deliberately credential-independent
101 * — the listener gets a verdict and nothing else, so an observer of it can never
102 * leak a credential identifier.
103 *
104 * Called from the BLE-host task inside the transaction, alongside the software
105 * P-256 work, so the listener must return immediately and must not block. Pass NULL
106 * to unregister.
107 *
108 * Present only under CONFIG_WOZ_ALIRO_ACCESS_LISTENER; without it the hook and its
109 * three notify points compile away entirely. */
110#if defined(CONFIG_WOZ_ALIRO_ACCESS_LISTENER)
111void aliro_reader_set_access_listener(void (*cb)(bool granted));
112#endif
113
114/* Copy out the credential public key (uncompressed P-256, 65 bytes) of the most
115 * recent session that passed the trust check. The Matter door lock resolves it to
116 * the user that owns it, so the LockOperation event names who unlocked; without
117 * that the event is anonymous and Apple Home notifies every device in the home,
118 * including the one that just unlocked. Returns true if a credential has
119 * authenticated since boot (cred_pub written), false otherwise (left untouched).
120 * Safe to call from any task. */
121bool aliro_reader_authenticated_credential(uint8_t cred_pub[65]);
122
123/* ---- Demand-driven presence proof --------------------------------------- *
124 * A proof must not reuse the credential/range latches from a prior walk-up.
125 * restart() marshals a disconnect of every current Aliro link onto the BLE
126 * host task and returns a nonzero request ticket. checkpoint() becomes true
127 * only after those links are gone; its auth_generation is the floor a new
128 * transaction must advance past. */
129uint32_t aliro_reader_presence_restart(void);
130bool aliro_reader_presence_checkpoint(uint32_t request, uint32_t *auth_generation);
131
132/* Copy the credential accepted by an authentication newer than checkpoint.
133 * Returns false until a new trusted transaction has authenticated. */
134bool aliro_reader_presence_authenticated_after(uint32_t checkpoint, uint8_t cred_pub[65]);
135
136/* Presence is a named-human primitive, so ambiguity fails closed: returns one
137 * pinned credential only when the provisioned trust store has exactly one
138 * entry. Dev-open and multi-credential readers return false. */
139bool aliro_reader_presence_expected_credential(uint8_t cred_pub[65]);
140
141/* ---- Bench provisioning helpers (Phase 3.4) ---------------------------- *
142 * Back the `aliro-prov` / `aliro-trust` console commands. Kept as plain calls
143 * so the shell does not need the internal aliro_prov types. */
144
147void aliro_reader_prov_print(void);
148
152int aliro_reader_trust_last(void);
153
158int aliro_reader_trust_clear(void);
159
160/* ---- Matter provisioning bridge (Phase 4) ------------------------------ *
161 * Apple Home provisions the reader over Matter (Door Lock SetAliroReaderConfig +
162 * SetCredential). These let the Matter delegate persist that identity + trust
163 * into the same NVS store the reader loads at start(), so a handoff-started
164 * reader authenticates the Wallet credential Apple just installed. Kept as plain
165 * calls (no aliro_prov types) so the C++ delegate needs only this header. */
166
172int aliro_reader_provision_identity(const uint8_t reader_id[32], const uint8_t sign_priv[32],
173 const uint8_t grk[16]);
174
178int aliro_reader_provision_add_trust(const uint8_t cred_pub[65]);
179
182int aliro_reader_provision_clear(void);
183
184/* ---- Identity clone (bench, CONFIG_WOZ_ALIRO_CLONE) --------------------- *
185 * Replicate a reader's identity + trust store onto a second board so a phone's
186 * existing credential transacts with the clone (the "no pairing dance" path for
187 * the presence-second-factor experiment). export_blob emits the reader private
188 * key, so only the clone-gated console commands reach these. */
189
193int aliro_reader_export_blob(uint8_t *out, size_t cap, size_t *out_len);
194
197int aliro_reader_import_blob(const uint8_t *buf, size_t len);
198
199/* ---- Step-up (Access Document) bench control (CONFIG_WOZ_ALIRO_STEPUP) ---- *
200 * Back the `aliro-stepup` console command. Both are no-ops unless the reader was
201 * built with the step-up phase enabled. */
202
206void aliro_reader_stepup_arm(void);
207
209void aliro_reader_stepup_status(void);
210
211#ifdef __cplusplus
212}
213#endif