| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- /*
- * Beacon payload wire format - shared by main.c's TX encode (send_beacon)
- * and beacon_rx.c's RX decode, so both sides agree on layout from one
- * place. A fixed timing prefix followed by zero or more 4 B packed
- * key/value entries - the extensible mechanism for everything added
- * after this milestone (table ids today, whatever flags/versions come
- * later) without a format change each time. A key this firmware doesn't
- * recognize is skipped, not an error - that's what makes it safe to add
- * new keys incrementally across a mixed-firmware deployment.
- *
- * Fixed prefix:
- * interval_ms 3 B ms until the next beacon (today: just the fixed
- * BEACON_CADENCE_MS; no schedule drift modeling
- * yet).
- * timestamp_ms 5 B ms since BEACON_EPOCH_UNIX_MS - a ~34-year span,
- * long enough that rollover is outside a device's
- * life. 0 (not "unsynced" per se - just the epoch
- * itself) until central's own wall clock has
- * synced (time_ctx.h).
- *
- * KV entries, 4 B each, BE: top 12 bits key, bottom 20 bits value
- * (BEACON_KV_KEY_BITS/BEACON_KV_VALUE_MASK). Key 0x000 is reserved - never
- * assigned, so a stray all-zero word (padding, a truncated read) can't be
- * mistaken for a real entry. Keys currently defined:
- * 0x001 current pubkey table (see pubkey_table.h) this node trusts.
- * Omitted entirely (not sent with a sentinel value) until table
- * load/provisioning exists - an absent key means "no claim",
- * which is the point of this being a KV scheme rather than
- * fixed fields.
- * 0x002 an older table_id still safe to validate against (no revoked
- * entries) - advisory, so a node running behind 0x001 still
- * trusts peers it already has rather than treating itself as
- * untrusted. May repeat (multiple 0x002 entries = multiple
- * still-safe fallbacks).
- *
- * After the KV region: BEACON_SIG_LEN bytes, central's signature (via
- * ident_sign() in main.c) over everything before it (the fixed prefix +
- * KV region, i.e. beacon_frame_encode()'s own output) - not over the
- * frame header or the group MAC trailer those ride outside this payload
- * entirely. beacon_frame_decode() never sees these bytes; the caller
- * strips them first (see beacon_rx.c).
- */
- #ifndef BEACON_FRAME_H_
- #define BEACON_FRAME_H_
- #include <stddef.h>
- #include <stdint.h>
- #include "frame/frame.h"
- #include "crypto/group_mac.h"
- #include "crypto/ident.h"
- /* Scheduling cadence - also the basis of the per-source uncertainty
- * beacon_rx.c offers time_ctx.h for beacon-derived time (half the
- * cadence; see project notes on why that's a reasonable starting bound
- * - beacon intervals are necessarily orders of magnitude longer than
- * whole-network propagation time). Moved here from main.c since it's
- * as much a protocol constant (every Beacon's own interval_ms field)
- * as a scheduling choice.
- */
- #define BEACON_CADENCE_MS 30000
- /* 2026-01-01T00:00:00Z, the epoch timestamp_ms counts from. */
- #define BEACON_EPOCH_UNIX_MS 1767225600000ULL
- #define BEACON_PAYLOAD_FIXED_LEN 8
- #define BEACON_KV_LEN 4
- #define BEACON_KV_KEY_BITS 12
- #define BEACON_KV_VALUE_BITS 20
- #define BEACON_KV_VALUE_MASK ((1UL << BEACON_KV_VALUE_BITS) - 1)
- #define BEACON_KEY_RESERVED 0x000
- #define BEACON_KEY_TABLE_CURRENT 0x001
- #define BEACON_KEY_TABLE_ALT 0x002
- /*
- * Signature trailer length - appended on the wire after the KV region,
- * same as pubkey_table.h's own trailing-signature layout. This file
- * only knows the length (so BEACON_MAX_KV_PAIRS/BEACON_PAYLOAD_MAX_LEN
- * below size correctly); it never touches key material or calls
- * ident_sign()/ident_verify() itself - that's main.c (TX, central only)
- * and beacon_rx.c (RX)'s job. beacon_frame_encode()/beacon_frame_decode()
- * only ever see the prefix+KV region, never the signature bytes - the
- * caller is responsible for appending (TX) or stripping (RX) exactly
- * BEACON_SIG_LEN bytes around those calls.
- */
- #define BEACON_SIG_LEN IDENT_SIG_LEN
- /* Largest number of KV pairs a Beacon can carry within FRAME_MAX_LEN,
- * alongside the signature trailer - derived, not hand-picked, so it
- * tracks FRAME_MAX_LEN/BEACON_SIG_LEN if either ever changes.
- */
- #define BEACON_MAX_KV_PAIRS \
- ((FRAME_MAX_LEN - FRAME_HDR_LEN - GROUP_MAC_LEN - BEACON_PAYLOAD_FIXED_LEN - BEACON_SIG_LEN) \
- / BEACON_KV_LEN)
- /* Largest full wire payload (prefix + KV + signature trailer) - what a
- * caller's buffer needs to hold end to end. beacon_frame_encode() itself
- * only fills/returns the prefix+KV prefix of this.
- */
- #define BEACON_PAYLOAD_MAX_LEN \
- (BEACON_PAYLOAD_FIXED_LEN + BEACON_MAX_KV_PAIRS * BEACON_KV_LEN + BEACON_SIG_LEN)
- struct beacon_frame {
- uint32_t interval_ms;
- uint64_t timestamp_ms;
- uint16_t kv_keys[BEACON_MAX_KV_PAIRS];
- uint32_t kv_values[BEACON_MAX_KV_PAIRS];
- size_t kv_count;
- };
- /*
- * Encodes into buf (caller-sized >= BEACON_PAYLOAD_MAX_LEN). kv_count must
- * not exceed BEACON_MAX_KV_PAIRS - callers building from a fixed-size
- * source array (e.g. main.c's alt_table_ids) already can't exceed it, so
- * this asserts rather than returning an error.
- * Returns the encoded payload length.
- */
- size_t beacon_frame_encode(uint8_t *buf, uint32_t interval_ms, uint64_t timestamp_ms,
- const uint16_t *kv_keys, const uint32_t *kv_values, size_t kv_count);
- /*
- * Decodes payload (the frame's payload region, as frame_decode() returns
- * it) into out. Returns 0 on success, -EINVAL if payload_len is too short
- * for the fixed prefix, if the trailing KV region isn't a whole number of
- * 4 B entries, or if it holds more entries than out->kv_keys/kv_values can
- * hold. Every KV entry is stored regardless of key - filtering by key
- * (e.g. "only care about table ids") is the caller's job, not this
- * function's.
- */
- int beacon_frame_decode(const uint8_t *payload, size_t payload_len, struct beacon_frame *out);
- #endif /* BEACON_FRAME_H_ */
|