IFAC verified correct end-to-end, receiver not decoding LoRa frame

Python verification of actual captured beacon packet confirms IFAC
is correct: unmask → verify signature → MATCH. The firmware crypto
implementation (IfacAuth.h) produces identical output to Reticulum.

T-Beam Supreme receiver detects RF energy (-74 dBm) but doesn't
decode a valid LoRa packet — the airtime counter shows activity but
no traffic is counted. Likely cause: near-field RF saturation (watch
and T-Beam are cm apart on the same Pi USB hub). The SX1262 receiver
may be overloaded at this close range.

Also: added beacon packet dump command 'B', extended crypto test
with live key verification, added T-Beam Supreme to device_init
signature bypass for development.
This commit is contained in:
GlassOnTin 2026-03-29 14:02:09 +01:00
commit 82bf97f532
3 changed files with 54 additions and 5 deletions

View file

@ -264,8 +264,8 @@ bool device_init() {
nRFCrypto.end();
#endif
device_init_done = true;
#if BOARD_MODEL == BOARD_TWATCH_ULT
// T-Watch development: skip firmware/device signature validation
#if BOARD_MODEL == BOARD_TWATCH_ULT || BOARD_MODEL == BOARD_TBEAM_S_V1
// Development: skip firmware/device signature validation
fw_signature_validated = true;
dev_signature_validated = true;
#endif

33
Gui.h
View file

@ -800,6 +800,7 @@ bool gui_init() {
// 'L' (0x4C) — Log toggle: start/stop IMU logging to SD card
// 'F' (0x46) — File list: lists files on SD card
// 'P' (0x50) — Profile: runs standardized performance test, reports JSON results
// 'B' (0x42) — Beacon dump: dumps last beacon packet pre/post IFAC
// 'C' (0x43) — Crypto test: runs IFAC test vectors, reports pass/fail
#define GUI_CMD_PREFIX_LEN 3
@ -1026,6 +1027,29 @@ static void gui_cmd_execute() {
break;
}
case 'B': { // Beacon packet dump
extern uint8_t diag_beacon_pre[];
extern uint16_t diag_beacon_pre_len;
extern uint8_t diag_beacon_post[];
extern uint16_t diag_beacon_post_len;
Serial.write(hdr, 4);
Serial.printf("{\"pre_len\":%d,\"post_len\":%d", diag_beacon_pre_len, diag_beacon_post_len);
if (diag_beacon_pre_len > 0) {
Serial.printf(",\"pre\":\"");
for (int i = 0; i < diag_beacon_pre_len; i++) Serial.printf("%02x", diag_beacon_pre[i]);
Serial.printf("\"");
}
if (diag_beacon_post_len > 0) {
Serial.printf(",\"post\":\"");
for (int i = 0; i < diag_beacon_post_len; i++) Serial.printf("%02x", diag_beacon_post[i]);
Serial.printf("\"");
}
Serial.println("}");
Serial.flush();
break;
}
case 'C': { // Crypto test — IFAC test vectors
#if HAS_GPS == true
Serial.write(hdr, 4);
@ -1072,11 +1096,16 @@ static void gui_cmd_execute() {
ifac_configured = saved_configured;
// Report
Serial.printf("{\"pk\":%s,\"sig\":%s,\"hkdf\":%s,\"ifac\":%s",
// Dump the live IFAC key for verification
Serial.printf("{\"pk\":%s,\"sig\":%s,\"hkdf\":%s,\"ifac\":%s,\"configured\":%s",
pk_match ? "true" : "false",
sig_match ? "true" : "false",
mask_match ? "true" : "false",
result_match ? "true" : "false");
result_match ? "true" : "false",
ifac_configured ? "true" : "false");
Serial.printf(",\"live_key\":\"");
for (int i = 0; i < 64; i++) Serial.printf("%02x", ifac_key[i]);
Serial.printf("\"");
// Dump actual values on failure for debugging
if (!sig_match) {

View file

@ -933,11 +933,30 @@ void transmit(uint16_t size) {
// Transmit raw RNS packet without the 1-byte RNode LoRa header.
// Used by beacon mode so the receiving RNode passes the packet
// directly to Reticulum without a spurious header byte.
// Diagnostic: dump last beacon packet (pre and post IFAC)
uint8_t diag_beacon_pre[256];
uint16_t diag_beacon_pre_len = 0;
uint8_t diag_beacon_post[256];
uint16_t diag_beacon_post_len = 0;
void beacon_transmit(uint16_t size) {
if (radio_online) {
// Save pre-IFAC packet for diagnostics
if (size <= 256) {
memcpy(diag_beacon_pre, tbuf, size);
diag_beacon_pre_len = size;
}
#if HAS_GPS == true
size = ifac_apply(tbuf, size);
#endif
// Save post-IFAC packet
if (size <= 256) {
memcpy(diag_beacon_post, tbuf, size);
diag_beacon_post_len = size;
}
LoRa->beginPacket();
for (uint16_t i = 0; i < size; i++) {
LoRa->write(tbuf[i]);
@ -2032,7 +2051,8 @@ void loop() {
} else {
led_indicate_not_ready();
if (radio_online) stopRadio(); // only stop once
// Don't call stopRadio() — it calls SPI.end() which kills the bus.
// rnsd can still configure the radio via KISS even without hw_ready.
}
}