26#include "esp_random.h"
27#include "mbedtls/md.h"
44#ifndef ESPNOW_PAIRING_SECRET_HEX
47 #error "ESPNOW_PAIRING_SECRET not configured for RELEASE build. " \
48 "Copy secrets.template.yml to secrets.local.yml and add your secret. " \
49 "Generate with: openssl rand -hex 16"
52 #warning "Using auto-generated pairing secret for DEBUG build. NOT SECURE for production!"
53 #define ESPNOW_PAIRING_SECRET_HEX "00000000deadbeefcafebabedeadbeef"
59 "ESPNOW_PAIRING_SECRET_HEX must be exactly 32 hex characters");
69 return (c >=
'0' && c <=
'9') ?
static_cast<uint8_t
>(c -
'0') :
70 (c >=
'a' && c <= 'f') ? static_cast<uint8_t>(c -
'a' + 10) :
71 (c >=
'A' && c <= 'F') ? static_cast<uint8_t>(c -
'A' + 10) : 0;
75 constexpr uint8_t
HexByte(
const char* s,
size_t i)
noexcept {
76 return static_cast<uint8_t
>(
129static constexpr uint8_t
BROADCAST_MAC[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
269 uint8_t full_hmac[32];
271 mbedtls_md_context_t ctx;
272 mbedtls_md_init(&ctx);
273 mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1);
275 mbedtls_md_hmac_update(&ctx, challenge, challenge_len);
276 mbedtls_md_hmac_finish(&ctx, full_hmac);
277 mbedtls_md_free(&ctx);
292 const uint8_t received_hmac[
HMAC_SIZE])
noexcept
300 diff |= (expected[i] ^ received_hmac[i]);
324 for (
int i = 0; i < 6; ++i) {
325 if (mac[i] != 0)
return false;
341inline bool MacEquals(
const uint8_t a[6],
const uint8_t b[6])
noexcept
343 return std::memcmp(a, b, 6) == 0;
349inline void FormatMac(
const uint8_t mac[6],
char* out,
size_t out_size)
noexcept
351 if (out_size >= 18) {
352 snprintf(out, out_size,
"%02X:%02X:%02X:%02X:%02X:%02X",
353 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
static constexpr size_t HMAC_SIZE
HMAC output size (truncated SHA-256)
Definition espnow_security.hpp:114
static constexpr size_t CHALLENGE_SIZE
Challenge nonce size in bytes.
Definition espnow_security.hpp:111
static constexpr uint8_t BROADCAST_MAC[6]
Broadcast MAC address for discovery.
Definition espnow_security.hpp:129
static constexpr uint32_t PAIRING_MODE_TIMEOUT_SEC
Pairing mode timeout in seconds (default)
Definition espnow_security.hpp:123
bool IsBroadcastMac(const uint8_t mac[6]) noexcept
Check if a MAC address is the broadcast address.
Definition espnow_security.hpp:333
void FormatMac(const uint8_t mac[6], char *out, size_t out_size) noexcept
Format MAC address to string buffer (needs 18 bytes: XX:XX:XX:XX:XX:XX\0)
Definition espnow_security.hpp:349
bool IsZeroMac(const uint8_t mac[6]) noexcept
Check if a MAC address is all zeros.
Definition espnow_security.hpp:322
PairingRejectReason
Definition espnow_security.hpp:169
@ ProtocolMismatch
Protocol version mismatch.
@ WrongDeviceType
Requester looking for wrong device type.
@ NotInPairingMode
Device not in pairing mode.
@ AlreadyPaired
Already paired (peer list full)
@ HmacFailed
HMAC verification failed.
bool VerifyPairingHmac(const uint8_t *challenge, size_t challenge_len, const uint8_t received_hmac[HMAC_SIZE]) noexcept
Verify an HMAC response using constant-time comparison.
Definition espnow_security.hpp:291
static constexpr uint8_t PAIRING_SECRET[16]
Pre-shared pairing secret (16 bytes).
Definition espnow_security.hpp:91
static constexpr size_t MAX_DEVICE_NAME_LEN
Maximum device name length.
Definition espnow_security.hpp:120
void GenerateChallenge(uint8_t out[CHALLENGE_SIZE]) noexcept
Generate a random challenge nonce.
Definition espnow_security.hpp:314
static constexpr size_t MAX_APPROVED_PEERS
Maximum number of approved peers to store in NVS.
Definition espnow_security.hpp:117
DeviceType
Device type identifiers for mutual verification.
Definition espnow_security.hpp:140
@ FatigueTester
Fatigue test unit.
@ RemoteController
UI board / M5Dial remote controller.
void ComputePairingHmac(const uint8_t *challenge, size_t challenge_len, uint8_t out[HMAC_SIZE]) noexcept
Compute HMAC-SHA256, truncated to HMAC_SIZE bytes.
Definition espnow_security.hpp:266
#define ESPNOW_PAIRING_SECRET_HEX
Definition espnow_security.hpp:53
static constexpr uint32_t PAIRING_RESPONSE_TIMEOUT_MS
Pairing response timeout in milliseconds.
Definition espnow_security.hpp:126
bool MacEquals(const uint8_t a[6], const uint8_t b[6]) noexcept
Compare two MAC addresses.
Definition espnow_security.hpp:341
Security/Pairing message type values.
Definition espnow_security.hpp:157
static constexpr uint8_t Unpair
Remove a paired device.
Definition espnow_security.hpp:162
static constexpr uint8_t PairingConfirm
Final confirmation.
Definition espnow_security.hpp:160
static constexpr uint8_t PairingRequest
Initiate pairing (broadcast)
Definition espnow_security.hpp:158
static constexpr uint8_t PairingReject
Explicit rejection.
Definition espnow_security.hpp:161
static constexpr uint8_t PairingResponse
Response with HMAC proof.
Definition espnow_security.hpp:159
Compile-time hex string to byte array parser.
Definition espnow_security.hpp:66
constexpr uint8_t HexCharToNibble(char c) noexcept
Convert a single hex character to its numeric value (0-15)
Definition espnow_security.hpp:68
constexpr uint8_t HexByte(const char *s, size_t i) noexcept
Convert two hex characters at position i*2 to a single byte.
Definition espnow_security.hpp:75
Information about an approved (paired) peer.
Definition espnow_security.hpp:240
uint8_t mac[6]
Peer's MAC address.
Definition espnow_security.hpp:241
uint8_t device_type
DeviceType enum value.
Definition espnow_security.hpp:242
uint32_t paired_timestamp
Unix timestamp when paired (or 0)
Definition espnow_security.hpp:244
bool valid
true if this slot is in use
Definition espnow_security.hpp:245
char name[MAX_DEVICE_NAME_LEN]
Friendly name.
Definition espnow_security.hpp:243
Pairing confirmation payload - sent by initiator to complete.
Definition espnow_security.hpp:217
uint8_t success
1 = success, 0 = verification failed
Definition espnow_security.hpp:220
uint8_t hmac_response[HMAC_SIZE]
HMAC(secret, responder_challenge)
Definition espnow_security.hpp:219
uint8_t confirmer_mac[6]
Confirmer's MAC address.
Definition espnow_security.hpp:218
Pairing rejection payload.
Definition espnow_security.hpp:226
uint8_t reason
PairingRejectReason.
Definition espnow_security.hpp:228
uint8_t rejecter_mac[6]
Rejecter's MAC address.
Definition espnow_security.hpp:227
Pairing request payload - sent by initiator (broadcast).
Definition espnow_security.hpp:189
uint8_t requester_mac[6]
Requester's MAC address.
Definition espnow_security.hpp:190
uint8_t challenge[CHALLENGE_SIZE]
Random nonce for verification.
Definition espnow_security.hpp:193
uint8_t device_type
DeviceType - what I am.
Definition espnow_security.hpp:191
uint8_t protocol_version
Protocol version for compatibility.
Definition espnow_security.hpp:194
uint8_t expected_peer_type
DeviceType - what I'm looking for.
Definition espnow_security.hpp:192
Pairing response payload - sent by responder (unicast).
Definition espnow_security.hpp:203
uint8_t device_type
DeviceType - what I am.
Definition espnow_security.hpp:205
char device_name[MAX_DEVICE_NAME_LEN]
Human-readable name.
Definition espnow_security.hpp:208
uint8_t hmac_response[HMAC_SIZE]
HMAC(secret, requester_challenge)
Definition espnow_security.hpp:207
uint8_t responder_mac[6]
Responder's MAC address.
Definition espnow_security.hpp:204
uint8_t challenge[CHALLENGE_SIZE]
My challenge for mutual auth.
Definition espnow_security.hpp:206
Security settings stored in NVS.
Definition espnow_security.hpp:251
ApprovedPeer approved_peers[MAX_APPROVED_PEERS]
Definition espnow_security.hpp:252