beacon_frame.h 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. * Beacon payload wire format - shared by main.c's TX encode (send_beacon)
  3. * and beacon_rx.c's RX decode, so both sides agree on layout from one
  4. * place. A fixed timing prefix followed by zero or more 4 B packed
  5. * key/value entries - the extensible mechanism for everything added
  6. * after this milestone (table ids today, whatever flags/versions come
  7. * later) without a format change each time. A key this firmware doesn't
  8. * recognize is skipped, not an error - that's what makes it safe to add
  9. * new keys incrementally across a mixed-firmware deployment.
  10. *
  11. * Fixed prefix:
  12. * interval_ms 3 B ms until the next beacon (today: just the fixed
  13. * BEACON_CADENCE_MS; no schedule drift modeling
  14. * yet).
  15. * timestamp_ms 5 B ms since BEACON_EPOCH_UNIX_MS - a ~34-year span,
  16. * long enough that rollover is outside a device's
  17. * life. 0 (not "unsynced" per se - just the epoch
  18. * itself) until central's own wall clock has
  19. * synced (time_ctx.h).
  20. *
  21. * KV entries, 4 B each, BE: top 12 bits key, bottom 20 bits value
  22. * (BEACON_KV_KEY_BITS/BEACON_KV_VALUE_MASK). Key 0x000 is reserved - never
  23. * assigned, so a stray all-zero word (padding, a truncated read) can't be
  24. * mistaken for a real entry. Keys currently defined:
  25. * 0x001 current pubkey table (see pubkey_table.h) this node trusts.
  26. * Omitted entirely (not sent with a sentinel value) until table
  27. * load/provisioning exists - an absent key means "no claim",
  28. * which is the point of this being a KV scheme rather than
  29. * fixed fields.
  30. * 0x002 an older table_id still safe to validate against (no revoked
  31. * entries) - advisory, so a node running behind 0x001 still
  32. * trusts peers it already has rather than treating itself as
  33. * untrusted. May repeat (multiple 0x002 entries = multiple
  34. * still-safe fallbacks).
  35. *
  36. * After the KV region: BEACON_SIG_LEN bytes, central's signature (via
  37. * ident_sign() in main.c) over everything before it (the fixed prefix +
  38. * KV region, i.e. beacon_frame_encode()'s own output) - not over the
  39. * frame header or the group MAC trailer those ride outside this payload
  40. * entirely. beacon_frame_decode() never sees these bytes; the caller
  41. * strips them first (see beacon_rx.c).
  42. */
  43. #ifndef BEACON_FRAME_H_
  44. #define BEACON_FRAME_H_
  45. #include <stddef.h>
  46. #include <stdint.h>
  47. #include "frame/frame.h"
  48. #include "crypto/group_mac.h"
  49. #include "crypto/ident.h"
  50. /* Scheduling cadence - also the basis of the per-source uncertainty
  51. * beacon_rx.c offers time_ctx.h for beacon-derived time (half the
  52. * cadence; see project notes on why that's a reasonable starting bound
  53. * - beacon intervals are necessarily orders of magnitude longer than
  54. * whole-network propagation time). Moved here from main.c since it's
  55. * as much a protocol constant (every Beacon's own interval_ms field)
  56. * as a scheduling choice.
  57. */
  58. #define BEACON_CADENCE_MS 30000
  59. /* 2026-01-01T00:00:00Z, the epoch timestamp_ms counts from. */
  60. #define BEACON_EPOCH_UNIX_MS 1767225600000ULL
  61. #define BEACON_PAYLOAD_FIXED_LEN 8
  62. #define BEACON_KV_LEN 4
  63. #define BEACON_KV_KEY_BITS 12
  64. #define BEACON_KV_VALUE_BITS 20
  65. #define BEACON_KV_VALUE_MASK ((1UL << BEACON_KV_VALUE_BITS) - 1)
  66. #define BEACON_KEY_RESERVED 0x000
  67. #define BEACON_KEY_TABLE_CURRENT 0x001
  68. #define BEACON_KEY_TABLE_ALT 0x002
  69. /*
  70. * Signature trailer length - appended on the wire after the KV region,
  71. * same as pubkey_table.h's own trailing-signature layout. This file
  72. * only knows the length (so BEACON_MAX_KV_PAIRS/BEACON_PAYLOAD_MAX_LEN
  73. * below size correctly); it never touches key material or calls
  74. * ident_sign()/ident_verify() itself - that's main.c (TX, central only)
  75. * and beacon_rx.c (RX)'s job. beacon_frame_encode()/beacon_frame_decode()
  76. * only ever see the prefix+KV region, never the signature bytes - the
  77. * caller is responsible for appending (TX) or stripping (RX) exactly
  78. * BEACON_SIG_LEN bytes around those calls.
  79. */
  80. #define BEACON_SIG_LEN IDENT_SIG_LEN
  81. /* Largest number of KV pairs a Beacon can carry within FRAME_MAX_LEN,
  82. * alongside the signature trailer - derived, not hand-picked, so it
  83. * tracks FRAME_MAX_LEN/BEACON_SIG_LEN if either ever changes.
  84. */
  85. #define BEACON_MAX_KV_PAIRS \
  86. ((FRAME_MAX_LEN - FRAME_HDR_LEN - GROUP_MAC_LEN - BEACON_PAYLOAD_FIXED_LEN - BEACON_SIG_LEN) \
  87. / BEACON_KV_LEN)
  88. /* Largest full wire payload (prefix + KV + signature trailer) - what a
  89. * caller's buffer needs to hold end to end. beacon_frame_encode() itself
  90. * only fills/returns the prefix+KV prefix of this.
  91. */
  92. #define BEACON_PAYLOAD_MAX_LEN \
  93. (BEACON_PAYLOAD_FIXED_LEN + BEACON_MAX_KV_PAIRS * BEACON_KV_LEN + BEACON_SIG_LEN)
  94. struct beacon_frame {
  95. uint32_t interval_ms;
  96. uint64_t timestamp_ms;
  97. uint16_t kv_keys[BEACON_MAX_KV_PAIRS];
  98. uint32_t kv_values[BEACON_MAX_KV_PAIRS];
  99. size_t kv_count;
  100. };
  101. /*
  102. * Encodes into buf (caller-sized >= BEACON_PAYLOAD_MAX_LEN). kv_count must
  103. * not exceed BEACON_MAX_KV_PAIRS - callers building from a fixed-size
  104. * source array (e.g. main.c's alt_table_ids) already can't exceed it, so
  105. * this asserts rather than returning an error.
  106. * Returns the encoded payload length.
  107. */
  108. size_t beacon_frame_encode(uint8_t *buf, uint32_t interval_ms, uint64_t timestamp_ms,
  109. const uint16_t *kv_keys, const uint32_t *kv_values, size_t kv_count);
  110. /*
  111. * Decodes payload (the frame's payload region, as frame_decode() returns
  112. * it) into out. Returns 0 on success, -EINVAL if payload_len is too short
  113. * for the fixed prefix, if the trailing KV region isn't a whole number of
  114. * 4 B entries, or if it holds more entries than out->kv_keys/kv_values can
  115. * hold. Every KV entry is stored regardless of key - filtering by key
  116. * (e.g. "only care about table ids") is the caller's job, not this
  117. * function's.
  118. */
  119. int beacon_frame_decode(const uint8_t *payload, size_t payload_len, struct beacon_frame *out);
  120. #endif /* BEACON_FRAME_H_ */