openaliro
Aliro reader: UWB/CCC core and ESP32-S3/C5/C6 port
Loading...
Searching...
No Matches
aliro_prov.h
1// Persistent reader provisioning storage: identity and credential trust anchors saved to and
2// loaded from NVS.
3// Declares aliro_prov_store for committing an identity/trust pair to NVS, and struct
4// aliro_trust_store, the set of trusted credential public keys against which a presented
5// credential is authenticated.
6/*
7 * Copyright (c) 2026 asxeem
8 * SPDX-License-Identifier: ISC
9 *
10 * aliro_prov — the reader provisioning seam (Phase 3.4): the reader's own
11 * identity (a stable reader identifier + P-256 signing key) and the trust store
12 * of credential public keys it authenticates presented credentials against.
13 * NVS-backed on target, with a clearly-marked dev fallback so the credential-auth
14 * transaction can be driven at bench before Phase-4 Matter provisioning writes a
15 * real identity.
16 *
17 * Split like aliro_crypto: the (de)serialisation + dev default + trust logic is
18 * portable and host-KAT'd (aliro_prov.c); the NVS load/store is target-only
19 * (aliro_prov_nvs.c, not compiled on host). Provenance: original.
20 */
21#pragma once
22
23#include <stdbool.h>
24#include <stddef.h>
25#include <stdint.h>
26
27#ifdef __cplusplus
28extern "C" {
29#endif
30
31#define ALIRO_READER_ID_LEN 32u
32#define ALIRO_READER_PRIV_LEN 32u
33#define ALIRO_CRED_PUB_LEN 65u /* uncompressed P-256 point: 0x04 | X | Y */
34/*
35 * 4 was too few and the cost of being wrong was total. An Apple home installs
36 * two endpoint keys per pairing, they accumulate across pairings, and nothing
37 * evicted them -- so a re-paired reader held 4 stale anchors, rejected the key
38 * the phone actually presented, and could not be recovered by pairing again.
39 * 6 at 97 B a slot: three pairings before anything is evicted, against a
40 * failure mode that used to be permanent. It was briefly 8 and came back down
41 * to buy RAM for the OpenThread stack, which the Interaction Model runs on and
42 * which was overflowing during commissioning -- this part has 128 KB and the
43 * image is at 96%. The number that matters is "more than one pairing's worth",
44 * and eviction (below) is what makes running out survivable rather than fatal.
45 */
46#define ALIRO_TRUST_MAX 6u /* trusted credential keys the store holds */
47#define ALIRO_GRK_LEN 16u /* group resolving key (Aliro BLE-UWB adv tag) */
48#define ALIRO_KPERSISTENT_LEN 32u /* per-credential expedited-fast key (§8.3.1.13) */
49
50/*
51 * The reader's provisioned identity. reader_id rides AUTH0 and both ECDSA
52 * transcripts (tag 0x4D); sign_priv signs the reader-usage transcript. is_dev
53 * marks the built-in bench identity, never a real deployment.
54 */
55struct aliro_reader_identity {
56 uint8_t reader_id[ALIRO_READER_ID_LEN];
57 uint8_t sign_priv[ALIRO_READER_PRIV_LEN];
58 uint8_t grk[ALIRO_GRK_LEN]; /* group resolving key; all-zero if none */
59 bool is_dev;
60};
61
62/*
63 * Trusted credential public keys. A presented credential authenticates only if
64 * its key is in here (or the store is empty and dev policy allows it). A raw-key
65 * allowlist is the interim seam; real issuer-chain validation is the Phase-4
66 * refinement that plugs in at aliro_prov_trust_check.
67 */
68struct aliro_trust_store {
69 uint8_t count;
70 uint8_t cred_pub[ALIRO_TRUST_MAX][ALIRO_CRED_PUB_LEN];
71 /* Expedited-fast state: bit i of kp_valid set = kpersistent[i] holds the
72 * Kpersistent agreed with cred_pub[i] in its last standard phase. */
73 uint8_t kp_valid;
74 uint8_t kpersistent[ALIRO_TRUST_MAX][ALIRO_KPERSISTENT_LEN];
75};
76
77/* Serialised blob v3: magic(4) ver(1) flags(1) reader_id(32) sign_priv(32)
78 * grk(16) count(1), count * cred_pub(65), kp_valid(1), count * kpersistent(32).
79 * (v2 ended at the cred_pub array; v1 also had no grk. Both still parsed.) */
80#define ALIRO_PROV_BLOB_HDR 6u
81#define ALIRO_PROV_BLOB_MAX \
82 (ALIRO_PROV_BLOB_HDR + ALIRO_READER_ID_LEN + ALIRO_READER_PRIV_LEN + ALIRO_GRK_LEN + 1u + \
83 (size_t)ALIRO_TRUST_MAX * ALIRO_CRED_PUB_LEN + 1u + \
84 (size_t)ALIRO_TRUST_MAX * ALIRO_KPERSISTENT_LEN)
85
86/* ---- portable core (aliro_prov.c) --------------------------------------- */
87
88/* Populate the built-in clearly-marked dev identity + an empty trust store. */
89void aliro_prov_dev_default(struct aliro_reader_identity *id, struct aliro_trust_store *ts);
90
91/* Serialise identity+trust to a self-describing blob. 0 + *out_len on success,
92 * -1 on overflow (cap < the assembled length). */
93int aliro_prov_serialize(const struct aliro_reader_identity *id, const struct aliro_trust_store *ts,
94 uint8_t *out, size_t cap, size_t *out_len);
95
96/* Parse a blob written by aliro_prov_serialize. 0 on success; -1 if malformed
97 * (bad magic/version/length/count). Outputs are untouched on failure. */
98int aliro_prov_deserialize(const uint8_t *buf, size_t len, struct aliro_reader_identity *id,
99 struct aliro_trust_store *ts);
100
101/* Trust decision for a presented credential public key:
102 * 0 trusted (cred_pub matches a stored key)
103 * 1 no-anchors (store empty; caller applies dev-open policy)
104 * -1 rejected (store non-empty and no match). */
105int aliro_prov_trust_check(const struct aliro_trust_store *ts,
106 const uint8_t cred_pub[ALIRO_CRED_PUB_LEN]);
107
108/* Add a credential key to the store. 0 added; 1 already present (dedup); -1 full
109 * or the point is not an uncompressed P-256 point (leading byte != 0x04). */
110int aliro_prov_trust_add(struct aliro_trust_store *ts, const uint8_t cred_pub[ALIRO_CRED_PUB_LEN]);
111
112/* Index of a credential key in the store, or -1 if not present. */
113int aliro_prov_trust_find(const struct aliro_trust_store *ts,
114 const uint8_t cred_pub[ALIRO_CRED_PUB_LEN]);
115
116/* Bind a Kpersistent (§8.3.1.13) to the credential at idx (from
117 * aliro_prov_trust_find), replacing any earlier one. 0 on success; -1 if idx is
118 * not a stored credential. */
119int aliro_prov_kpersistent_set(struct aliro_trust_store *ts, int idx,
120 const uint8_t kp[ALIRO_KPERSISTENT_LEN]);
121
122/* ---- target NVS backend (aliro_prov_nvs.c) ------------------------------ */
123
124/* Load identity+trust from NVS; on absence or a malformed blob fall back to the
125 * dev default (leaving NVS untouched). Always yields a usable identity.
126 * 0 a stored blob was loaded
127 * 1 the dev default was used (nothing stored)
128 * -1 an NVS error occurred; the dev default was used. */
129int aliro_prov_load(struct aliro_reader_identity *id, struct aliro_trust_store *ts);
130
131/* Persist identity+trust to NVS. 0 on success, negative on an NVS error. */
132int aliro_prov_store(const struct aliro_reader_identity *id, const struct aliro_trust_store *ts);
133
147int aliro_prov_erase(void);
148
149#ifdef __cplusplus
150}
151#endif