Add USB auto-read key transfer + UI redesign

USB auto-read:
- ESP responds to 'KEY?\n' over USB serial with 'HMAC_KEY:<hex>'
- Android UsbKeyReceiver (manifest BroadcastReceiver) catches USB_DEVICE_ATTACHED
- Filters on Espressif vendor 0x303a, requests USB permission
- Opens CDC-ACM serial (115200), sends KEY?, parses response, saves to SharedPreferences
- Works even when app not in foreground
- usb-serial-for-android v3.7.3 via JitPack

UI redesign:
- activity_main.xml: FrameLayout with settings icon top-right, relay button center, status bottom-left (14sp)
- Scan & Connect button moved to SettingsActivity
- SettingsActivity: hint text 'Voer de HMAC sleutel in of sluit de relaismodule aan op de telefoon'
- SettingsActivity: scan button saves key + returns RESULT_OK to trigger scanning
- MainActivity: removed scanButton, auto-scan on onResume when key is set
- Back button removed from settings (ActionBar up navigation only)

README updated with USB protocol docs, new UI description, and updated setup guide
This commit is contained in:
eddy 2026-07-26 08:39:28 +02:00
commit 4518aec34e
10 changed files with 270 additions and 74 deletions

View file

@ -53,7 +53,7 @@ De relay is beveiligd tegen ongeautoriseerde toegang via twee onafhankelijke lag
Omdat BLE Just Works geen MITM-bescherming biedt, is er een tweede laag toegevoegd:
- Both de ESP firmware en de Android app bevatten een **32-byte shared secret** (HMAC key)
- Zowel de ESP firmware als de Android app bevatten een **32-byte shared secret** (HMAC key)
- Elk commando wordt ondertekend met `HMAC-SHA256(key, command || sequence)`
- De ESP verifieert de handtekening voordat het relais schakelt
- Een **sequence counter** voorkomt replay-aanvallen: elk packet moet een strikt oplopend volgnummer hebben
@ -74,10 +74,20 @@ De sequence counter reset naar 0 bij elke (re)connectie op de ESP. De app start
- De HMAC key staat in `src/hmac_key.h` (niet in git, `.gitignore`)
- Een CLI-tool genereert een willekeurige key: `python3 tools/generate_hmac_key.py`
- De ESP print de key 5 seconden na opstarten naar serial
- De gebruiker voert de key eenmalig in via het instellingenscherm in de Android app
- **USB auto-read:** Sluit de ESP via USB-OTG aan op de telefoon → de app leest de key automatisch via USB serial (CDC-ACM). De ESP reageert op het commando `KEY?\n` met `HMAC_KEY:<hex>`. Geen encryptie nodig — fysieke connectie is de beveiliging.
- Handmatige invoer: via het instellingenscherm in de Android app (64-teken hex string)
- De app slaat de key op in SharedPreferences (overleeft app-herstarts)
- Zo kan dezelfde app gebruikt worden voor meerdere ESP modules — each met een eigen key
#### USB Serial Key Transfer Protocol
```
App → ESP: KEY?\n
ESP → App: HMAC_KEY:C3F17B612B93E82C56B055FA9B04E84664ACE1EC4E889F2763F271B65A5B54AF\n
```
De ESP luistert in `loop()` voor USB serial data. Bij `KEY?\n` antwoordt hij met de hex key. De Android `UsbKeyReceiver` (manifest-geregistreerde `BroadcastReceiver`) vangt `USB_DEVICE_ATTACHED` op, filtert op Espressif (vendor 0x303a), vraagt USB-permission, opent een CDC-ACM serial connectie, stuurt `KEY?\n`, en slaat de respons op in SharedPreferences.
### Beveiligingsanalyse
| Aanval | Hoe geweerd |
@ -169,29 +179,38 @@ Na het uploaden treedt er een harde reset op. Als het board in download-mode bli
- **BLE Pairing:** De app initieert bonding (`createBond()`) na connectie; Just Works pairing verloopt zonder gebruikersinteractie
- **MTU negotiatie:** Vraagt MTU 256 aan na service discovery (voor 37-byte HMAC packets)
- **HMAC authenticatie:** Elk commando wordt ondertekend met HMAC-SHA256 + sequence counter
- **Instellingenscherm:** Losse Activity voor het invoeren van de HMAC key (met oog-icoon om de key te tonen/verbergen)
- **USB auto-read:** Bij aansluiten van de ESP via USB-OTG wordt de HMAC key automatisch gelezen en opgeslagen (BroadcastReceiver)
- **Instellingenscherm:** Losse Activity voor het invoeren van de HMAC key (met oog-icoon om de key te tonen/verbergen), "Scan & Verbind" knop, en hint-tekst
- **Permissies:** Vraagt BLUETOOTH_SCAN, BLUETOOTH_CONNECT en ACCESS_FINE_LOCATION aan (Android 12+)
### Instellingenscherm
1. Open de app
2. Tik op het **tandwiel-icoon** rechtsboven
3. Voer de 64-teken hex key in (zelfde als de ESP op serial print)
4. Tik op het **oog-icoon** om de key te tonen/verbergen
5. Tik **Opslaan**
3. De app toont een hint: _"Voer de HMAC sleutel in of sluit de relaismodule aan op de telefoon."_
4. **Optie A — handmatig:** Voer de 64-teken hex key in (zelfde als de ESP op serial print). Tik op het **oog-icoon** om de key te tonen/verbergen. Tik **Opslaan**.
5. **Optie B — automatisch:** Sluit de ESP via USB-OTG aan op de telefoon. Android vraagt USB-toestemming (1x). De key wordt automatisch gelezen en opgeslagen. Toast: "HMAC sleutel gelezen van ESP!"
6. Tik **Scan & Verbind** om op te slaan en direct BLE scannen te starten.
De key wordt opgeslagen in SharedPreferences en overleeft app-herstarts. Bij geen key toont de app "Geen HMAC sleutel - open Instellingen" en wordt er niet gescant.
De key wordt opgeslagen in SharedPreferences en overleeft app-herstarts. Bij geen key toont de hoofdapp "Geen HMAC sleutel - open Instellingen" en wordt er niet gescant.
### Hoofdscherm UI
- **Tandwiel-icoon** rechtsboven → opent instellingenscherm
- **HOLD-knop** centrum → houd ingedrukt voor relais aan
- **Status tekst** linksonder (14sp) → toont verbindingsstatus
### Bouwen
Vereisten:
- Android SDK (API 34+)
- Java 17+ (getest met Java 17 en 21; Java 26 werkt niet met Kotlin 1.9 daemon — valt terug op non-incremental)
- Java 17+ (Java 26 werkt niet met Gradle 8.5 — gebruik Java 21)
- Gradle 8.2.2 (via wrapper)
- USB host support (usb-serial-for-android library via JitPack)
```bash
cd android
./gradlew assembleDebug
JAVA_HOME=/pad/naar/jdk21 ./gradlew assembleDebug
```
De APK staat na build in `android/app/build/outputs/apk/debug/app-debug.apk`.
@ -222,29 +241,23 @@ adb install -r android/app/build/outputs/apk/debug/app-debug.apk
pio run -t upload
```
3. **Lees de HMAC key uit serial** (5 seconden na opstarten):
```bash
# Via python:
python3 -c "import serial,time; s=serial.Serial('/dev/ttyACM0',115200); [print(l) for l in iter(lambda: s.readline().decode(errors='replace').strip() or None, None) if 'HMAC Key:' in l] if False else exec('''
import serial, time
s = serial.Serial(\"/dev/ttyACM0\", 115200, timeout=2)
start = time.time()
while time.time() - start < 15:
d = s.read(1024)
if d:
for line in d.decode(errors=\"replace\").splitlines():
if line.strip(): print(line.strip())
if b\"HMAC Key:\" in d: break
s.close()
''')"
```
4. **Installeer de app op de telefoon:**
3. **Installeer de app op de telefoon:**
```bash
adb install -r android/app/build/outputs/apk/debug/app-debug.apk
```
5. **Open de app → tandwiel-icoon → voer key in → Opslaan**
4. **Voer de HMAC key in (kies één):**
**Optie A — USB auto-read (aanbevolen):**
- Sluit de ESP via USB-OTG aan op de telefoon
- Android vraagt USB-toestemming (1x) — tik "Toestaan"
- De key wordt automatisch gelezen en opgeslagen (Toast bevestigt)
- Ontkoppel de ESP en zet hem op stroom
**Optie B — handmatig:**
- Open de app → tandwiel-icoon → voer de hex key in → Opslaan
5. **Scan & Verbind:** Tik in instellingen op "Scan & Verbind" (of de app scant automatisch bij terugkeren naar het hoofdscherm)
6. De app scant, paart (Just Works), en verbindt. Houd de HOLD-knop ingedrukt om het relais te schakelen.
@ -256,30 +269,31 @@ sylvia-touch-relay/
├── tools/
│ └── generate_hmac_key.py # CLI tool: genereert src/hmac_key.h
├── src/
│ ├── main.cpp # Firmware (BLE + HMAC + sensor + relais)
│ ├── main.cpp # Firmware (BLE + HMAC + USB serial + sensor + relais)
│ └── hmac_key.h # Gegenereerd, niet in git (.gitignore)
├── android/
│ ├── build.gradle # Root Gradle config
│ ├── settings.gradle
│ ├── settings.gradle # JitPack repos voor usb-serial-for-android
│ ├── gradle.properties
│ ├── gradlew # Gradle wrapper
│ ├── gradle/wrapper/
│ │ └── gradle-wrapper.properties
│ └── app/
│ ├── build.gradle # App module config
│ ├── build.gradle # App module config (+ usb-serial-for-android dep)
│ └── src/main/
│ ├── AndroidManifest.xml
│ ├── java/com/eddy/atomrelay/
│ │ ├── MainActivity.kt # BLE client + HMAC signing
│ │ └── SettingsActivity.kt # HMAC key invoer + oog toggle
│ │ ├── MainActivity.kt # BLE client + HMAC signing
│ │ ├── SettingsActivity.kt # HMAC key invoer + scan knop
│ │ └── UsbKeyReceiver.kt # USB auto-read BroadcastReceiver
│ └── res/
│ ├── drawable/
│ │ ├── ic_eye_open.xml # Oog open icoon
│ │ ├── ic_eye_closed.xml # Oog dicht icoon
│ │ └── ic_settings.xml # Tandwiel icoon
│ │ ├── ic_eye_open.xml # Oog open icoon
│ │ ├── ic_eye_closed.xml # Oog dicht icoon
│ │ └── ic_settings.xml # Tandwiel icoon
│ ├── layout/
│ │ ├── activity_main.xml # Hoofd UI
│ │ └── activity_settings.xml # Key invoer UI
│ │ ├── activity_main.xml # Hoofd UI (FrameLayout)
│ │ └── activity_settings.xml # Key invoer + scan UI
│ └── values/strings.xml
├── .gitignore
└── README.md
@ -294,4 +308,7 @@ sylvia-touch-relay/
- De HMAC key wordt via `#include "hmac_key.h"` in de firmware geincludeerd. Dit bestand is gegenereerd door `tools/generate_hmac_key.py` en staat in `.gitignore`.
- De app gebruikt `WRITE_TYPE_NO_RESPONSE` voor minimale latency — de encryptie en HMAC verificatie gebeuren op de link layer resp. application layer en zijn onafhankelijk van het write type.
- De sequence counter start bij 1 (niet 0) om te voorkomen dat het eerste packet na connectie als replay wordt geweerd (`0 <= 0` is true in de `seq <= lastSeq` check).
- Für meerdere ESP modules met verschillende keys: genereer per module een eigen key, flash de firmware, en voer de juiste key in de app in. De app ondersteunt momenteel slechtséén key (opgeslagen in SharedPreferences).
- Für meerdere ESP modules met verschillende keys: genereer per module een eigen key, flash de firmware, en voer de juiste key in de app in. De app ondersteunt momenteel slechtséén key (opgeslagen in SharedPreferences).
- De `UsbKeyReceiver` is een manifest-geregistreerde `BroadcastReceiver` voor `ACTION_USB_DEVICE_ATTACHED`. Hij filtert op Espressif (vendor 0x303a), vraagt USB-permission via `PendingIntent`, opent een CDC-ACM serial connectie (115200), stuurt `KEY?\n`, en slaat de respons op. Werkt ook als de app niet actief is.
- De USB serial library (`usb-serial-for-android` v3.7.3 via JitPack) ondersteunt CDC-ACM (ESP32-S3), FTDI, CP210x, CH340, PL2303, en meer.
- De `UsbKeyReceiver` doet serial I/O op een achtergrondthread (max 3s timeout) om ANR's te voorkomen.