Replace compile-time HMAC key with USB provisioning

ESP firmware:
- Store HMAC key in NVS via Preferences library instead of compile-time
  hmac_key.h
- KEY? responds with HMAC_KEY:<hex> (if provisioned) or NO_KEY
- New SET_KEY:<64-hex> command stores key in NVS and responds OK
- Boot messages updated to reflect provisioning state

Android app:
- UsbKeyReceiver detects NO_KEY response and auto-provisions:
  generates random 32-byte key via SecureRandom, sends SET_KEY,
  waits for OK, saves key to SharedPreferences
- Read loop now breaks on both HMAC_KEY: and NO_KEY responses

Cleanup:
- Removed tools/generate_hmac_key.py and src/hmac_key.h
- Removed hmac_key.h from .gitignore
- README updated for new provisioning flow
This commit is contained in:
eddy 2026-07-26 09:30:21 +02:00
commit 9a84b05b8a
5 changed files with 155 additions and 116 deletions

View file

@ -5,7 +5,7 @@
#include <BLE2902.h>
#include <BLESecurity.h>
#include <mbedtls/md.h>
#include "hmac_key.h"
#include <Preferences.h>
#define SENSOR_PIN 38
#define RELAY_PIN 1
@ -15,6 +15,9 @@
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361f26d8"
#define HMAC_PKT_SIZE 37 // 1 cmd + 4 seq + 32 hmac
#define HMAC_KEY_LEN 32
#define NVS_NAMESPACE "hmac"
#define NVS_KEY_NAME "key"
BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
@ -22,6 +25,10 @@ bool bleCommand = false;
bool relayState = false;
uint32_t lastSeq = 0;
uint8_t hmacKey[HMAC_KEY_LEN];
bool keyProvisioned = false;
Preferences preferences;
class MyServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer *pServer) override {
deviceConnected = true;
@ -58,7 +65,7 @@ static bool verifyHmac(const uint8_t *packet) {
uint8_t computed[32];
const mbedtls_md_info_t *mdInfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
mbedtls_md_hmac(mdInfo, HMAC_KEY, 32, hmacData, 5, computed);
mbedtls_md_hmac(mdInfo, hmacKey, HMAC_KEY_LEN, hmacData, 5, computed);
if (memcmp(computed, packet + 5, 32) != 0) {
Serial.println("BLE: HMAC mismatch");
@ -69,6 +76,20 @@ static bool verifyHmac(const uint8_t *packet) {
return true;
}
static bool isHexKey(const String &hex) {
for (int i = 0; i < hex.length(); i++) {
char c = hex.charAt(i);
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))) return false;
}
return true;
}
static void parseHexKey(const String &hex, uint8_t *out) {
for (int i = 0; i < HMAC_KEY_LEN; i++) {
out[i] = (uint8_t)strtoul(hex.substring(i * 2, i * 2 + 2).c_str(), nullptr, 16);
}
}
class MyCharacteristicCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pChar) override {
std::string value = pChar->getValue();
@ -103,6 +124,18 @@ void setup() {
delay(500);
Serial.println("=== Atom VoiceS3R BLE Relay ===");
preferences.begin(NVS_NAMESPACE, true);
size_t len = preferences.getBytes(NVS_KEY_NAME, hmacKey, HMAC_KEY_LEN);
preferences.end();
if (len == HMAC_KEY_LEN) {
keyProvisioned = true;
Serial.println("HMAC key loaded from NVS");
} else {
keyProvisioned = false;
Serial.println("No HMAC key provisioned — connect USB and run SET_KEY");
}
pinMode(SENSOR_PIN, INPUT_PULLUP);
pinMode(RELAY_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
@ -145,14 +178,15 @@ void setup() {
Serial.println("BLE: advertising started as 'AtomS3R-Relay'");
Serial.println("Printing HMAC key in 5 seconds...");
delay(5000);
Serial.print("HMAC Key: ");
for (int i = 0; i < 32; i++) {
Serial.printf("%02X", HMAC_KEY[i]);
if (keyProvisioned) {
Serial.print("HMAC Key: ");
for (int i = 0; i < HMAC_KEY_LEN; i++) {
Serial.printf("%02X", hmacKey[i]);
}
Serial.println();
} else {
Serial.println("No HMAC key — use USB SET_KEY to provision");
}
Serial.println();
Serial.println("Enter this key in the Android app > Settings.");
Serial.println("Setup done.");
}
@ -165,11 +199,29 @@ void loop() {
if (c == '\n') {
serialBuf.trim();
if (serialBuf == "KEY?") {
Serial.print("HMAC_KEY:");
for (int i = 0; i < 32; i++) {
Serial.printf("%02X", HMAC_KEY[i]);
if (keyProvisioned) {
Serial.print("HMAC_KEY:");
for (int i = 0; i < HMAC_KEY_LEN; i++) {
Serial.printf("%02X", hmacKey[i]);
}
Serial.println();
} else {
Serial.println("NO_KEY");
}
} else if (serialBuf.startsWith("SET_KEY:")) {
String hex = serialBuf.substring(8);
hex.toUpperCase();
if (hex.length() == 64 && isHexKey(hex)) {
parseHexKey(hex, hmacKey);
preferences.begin(NVS_NAMESPACE, false);
preferences.putBytes(NVS_KEY_NAME, hmacKey, HMAC_KEY_LEN);
preferences.end();
keyProvisioned = true;
Serial.println("OK");
Serial.println("HMAC key stored in NVS");
} else {
Serial.println("ERROR: invalid key format");
}
Serial.println();
}
serialBuf = "";
}