diff --git a/Beacon.h b/Beacon.h index 13821b8..834da22 100644 --- a/Beacon.h +++ b/Beacon.h @@ -35,6 +35,10 @@ const uint32_t beacon_interval_options[] = { 10000, 30000, 60000, 300000, 600000 #define BEACON_CR 5 #define BEACON_TXP 17 +// Beacon IFAC network — must match rnsd's RNodeInterface config +#define BEACON_NETWORK_NAME "helv4net" +#define BEACON_PASSPHRASE "R3ticulum-priv8-m3sh" + // Pre-computed RNS destination hash for PLAIN destination "rnlog.beacon" // Computed as: SHA256(SHA256("rnlog.beacon")[:10])[:16] const uint8_t RNS_DEST_HASH[16] = { diff --git a/IfacAuth.h b/IfacAuth.h index 1d74afc..c4289cc 100644 --- a/IfacAuth.h +++ b/IfacAuth.h @@ -21,6 +21,7 @@ #include "sodium/crypto_sign_ed25519.h" #include "nvs_flash.h" #include "nvs.h" +#include "mbedtls/sha256.h" // NVS namespace for IFAC key storage #define IFAC_NVS_NAMESPACE "ifac" @@ -112,14 +113,49 @@ static int rns_hkdf_var(const uint8_t *ikm, size_t ikm_len, return 0; } +// ---- Reticulum IFAC_SALT (from Reticulum.py IFAC_SALT constant) ---- +static const uint8_t RNS_IFAC_SALT[32] = { + 0xad, 0xf5, 0x4d, 0x88, 0x2c, 0x9a, 0x9b, 0x80, 0x77, 0x1e, 0xb4, 0x99, 0x5d, 0x70, 0x2d, 0x4a, + 0x3e, 0x73, 0x33, 0x91, 0xb2, 0xa0, 0xf5, 0x3f, 0x41, 0x6d, 0x9f, 0x90, 0x7e, 0x55, 0xcf, 0xf8, +}; + +// ---- Self-provision IFAC key from network_name + passphrase ---- +// Replicates Reticulum.py:821-826 IFAC key derivation: +// ifac_origin = SHA256(network_name) || SHA256(passphrase) +// ifac_origin_hash = SHA256(ifac_origin) +// ifac_key = HKDF(ikm=ifac_origin_hash, salt=IFAC_SALT, length=64) +static bool ifac_self_provision(const char *network_name, const char *passphrase) { + uint8_t name_hash[32], key_hash[32]; + mbedtls_sha256((const uint8_t *)network_name, strlen(network_name), name_hash, 0); + mbedtls_sha256((const uint8_t *)passphrase, strlen(passphrase), key_hash, 0); + + uint8_t ifac_origin[64]; + memcpy(ifac_origin, name_hash, 32); + memcpy(ifac_origin + 32, key_hash, 32); + + uint8_t origin_hash[32]; + mbedtls_sha256(ifac_origin, 64, origin_hash, 0); + + int ret = rns_hkdf_var(origin_hash, 32, RNS_IFAC_SALT, 32, ifac_key, 64); + if (ret != 0) return false; + + ifac_nvs_save(); + ifac_derive_keypair(); + ifac_configured = true; + return true; +} + // ---- IFAC Initialization ---- // Call from setup() after lxmf_init_identity(). +// Loads from NVS if available, otherwise self-provisions from beacon config. static void ifac_init() { if (ifac_nvs_load()) { ifac_derive_keypair(); ifac_configured = true; } + // If no key loaded, caller should call ifac_self_provision() + // with network_name/passphrase from beacon config. } // ---- Apply IFAC to Outgoing Packet ---- diff --git a/RNode_Firmware.ino b/RNode_Firmware.ino index 8df27fd..13aac8b 100644 --- a/RNode_Firmware.ino +++ b/RNode_Firmware.ino @@ -460,8 +460,11 @@ void setup() { } // Initialize LXMF identity (load from NVS or generate new) lxmf_init_identity(); - // Initialize IFAC authentication (load from NVS if provisioned) + // Initialize IFAC authentication (load from NVS, or self-provision) ifac_init(); + if (!ifac_configured) { + ifac_self_provision(BEACON_NETWORK_NAME, BEACON_PASSPHRASE); + } // Load user settings from config EEPROM uint8_t s_disp = EEPROM.read(config_addr(ADDR_CONF_DISP_TIMEOUT)); if (s_disp != 0xFF && s_disp >= 5 && s_disp <= 60)