fix: code review fixes — SharedPreferences docs, Log.e, dead notify code
This commit is contained in:
parent
36a20899be
commit
0833327cea
4 changed files with 9 additions and 16 deletions
12
README.md
12
README.md
|
|
@ -61,13 +61,13 @@ Offset Grootte Veld Beschrijving
|
||||||
5 32 bytes hmac HMAC-SHA256(key, command || sequence)
|
5 32 bytes hmac HMAC-SHA256(key, command || sequence)
|
||||||
```
|
```
|
||||||
|
|
||||||
De sequence counter reset naar 0 bij elke (re)connectie op de module. De app start bij 1 om te zorgen dat het eerste packet altijd geaccepteerd wordt (`1 > 0`). Replay van een eerder packet wordt geweerd omdat `seq <= lastSeq`. De sequence counter wordt gepersisteerd in EncryptedSharedPreferences en overleeft app-herstarts — bij een Activity-restart met actieve BLE-verbinding blijft de counter oplopen in plaats van terug te vallen naar 1.
|
De sequence counter reset naar 0 bij elke (re)connectie op de module. De app start bij 1 om te zorgen dat het eerste packet altijd geaccepteerd wordt (`1 > 0`). Replay van een eerder packet wordt geweerd omdat `seq <= lastSeq`. De sequence counter wordt gepersisteerd in SharedPreferences en overleeft app-herstarts — bij een Activity-restart met actieve BLE-verbinding blijft de counter oplopen in plaats van terug te vallen naar 1.
|
||||||
|
|
||||||
#### Key management
|
#### Key management
|
||||||
|
|
||||||
- De HMAC key wordt **niet meegecompileerd** — de module start zonder key en wordt geprovisioneerd via USB
|
- De HMAC key wordt **niet meegecompileerd** — de module start zonder key en wordt geprovisioneerd via USB
|
||||||
- De key wordt opgeslagen in **NVS** (Non-Volatile Storage) op de module via de Arduino `Preferences` library (namespace `hmac`, key `key`, partitie op 0x9000-0xDFFF)
|
- De key wordt opgeslagen in **NVS** (Non-Volatile Storage) op de module via de Arduino `Preferences` library (namespace `hmac`, key `key`, partitie op 0x9000-0xDFFF)
|
||||||
- De app slaat de key op in **EncryptedSharedPreferences** (AES256-GCM versleuteld, overleeft app-herstarts)
|
- De app slaat de key op in **SharedPreferences** (overleeft app-herstarts)
|
||||||
- **USB provisioning:** Open Instellingen → Firmware bijwerken. De app checkt eerst de firmware versie (`VERSION?`) en flasht indien nodig. Na het flashen (of als versie al actueel is) volgt automatisch de key exchange:
|
- **USB provisioning:** Open Instellingen → Firmware bijwerken. De app checkt eerst de firmware versie (`VERSION?`) en flasht indien nodig. Na het flashen (of als versie al actueel is) volgt automatisch de key exchange:
|
||||||
- Als de module al een key heeft → module antwoordt `HMAC_KEY:<hex>` → app slaat op lokaal op
|
- Als de module al een key heeft → module antwoordt `HMAC_KEY:<hex>` → app slaat op lokaal op
|
||||||
- Als de module nog geen key heeft → module antwoordt `NO_KEY` → app genereert willekeurige 32-byte key via `SecureRandom` → stuurt `SET_KEY:<hex>\n` en `SET_ID:<hex>\n` → module slaat op in NVS → app slaat op lokaal op
|
- Als de module nog geen key heeft → module antwoordt `NO_KEY` → app genereert willekeurige 32-byte key via `SecureRandom` → stuurt `SET_KEY:<hex>\n` en `SET_ID:<hex>\n` → module slaat op in NVS → app slaat op lokaal op
|
||||||
|
|
@ -115,7 +115,7 @@ module → App: OK\n (bevestiging)
|
||||||
| **Commando vervalsing** | HMAC dekt command byte + sequence — wijzigen maakt handtekening ongeldig |
|
| **Commando vervalsing** | HMAC dekt command byte + sequence — wijzigen maakt handtekening ongeldig |
|
||||||
| **Brute-force key** | 32-byte key = 2^256 mogelijkheden — onhaalbaar |
|
| **Brute-force key** | 32-byte key = 2^256 mogelijkheden — onhaalbaar |
|
||||||
| **Sniffen** | Pakketten zijn onversleuteld; geen confidentiality. Alleen commando's (aan/uit) zijn zichtbaar, niet de key. |
|
| **Sniffen** | Pakketten zijn onversleuteld; geen confidentiality. Alleen commando's (aan/uit) zijn zichtbaar, niet de key. |
|
||||||
| **Reverse engineering van key** | Sleutel staat niet in de APK — wordt pas bij USB provisioning gegenereerd en in EncryptedSharedPreferences (AES256-GCM) opgeslagen. Extractie vereist fysieke toegang tot de telefoon én root. |
|
| **Reverse engineering van key** | Sleutel staat niet in de APK — wordt pas bij USB provisioning gegenereerd en in SharedPreferences opgeslagen. Extractie vereist fysieke toegang tot de telefoon. |
|
||||||
| **Key extractie via USB** | Fysieke USB-toegang tot de module geeft `KEY?\n` de huidige key terug. Bescherming: fysieke toegang tot het board beperken. |
|
| **Key extractie via USB** | Fysieke USB-toegang tot de module geeft `KEY?\n` de huidige key terug. Bescherming: fysieke toegang tot het board beperken. |
|
||||||
| **NVS wiping** | `pio run -t erase` wist alle flash waaronder NVS — module is daarna leeg en kan opnieuw geprovisioneerd worden. |
|
| **NVS wiping** | `pio run -t erase` wist alle flash waaronder NVS — module is daarna leeg en kan opnieuw geprovisioneerd worden. |
|
||||||
|
|
||||||
|
|
@ -149,7 +149,7 @@ De touch sensoren en BLE werken onafhankelijk. Als de sensor losgelaten wordt en
|
||||||
| Relay Characteristic | `beb5483e-36e1-4688-b7f5-ea07361f26d8` |
|
| Relay Characteristic | `beb5483e-36e1-4688-b7f5-ea07361f26d8` |
|
||||||
| UUID Characteristic | `beb5483e-36e1-4688-b7f5-ea07361f26d9` |
|
| UUID Characteristic | `beb5483e-36e1-4688-b7f5-ea07361f26d9` |
|
||||||
|
|
||||||
De relay characteristic ondersteunt `WRITE_NR` (Write Without Response) en `NOTIFY`. De UUID characteristic is read-only en bevat het unieke device-ID van de module.
|
De relay characteristic ondersteunt `WRITE_NR` (Write Without Response). De UUID characteristic is read-only en bevat het unieke device-ID van de module.
|
||||||
|
|
||||||
### Key provisioning
|
### Key provisioning
|
||||||
|
|
||||||
|
|
@ -251,7 +251,7 @@ De firmware binaries worden via `tools/copy_firmware.sh` naar de app assets geko
|
||||||
3. **Weergave:** Kies Systeem (volgt telefoon), Licht of Donker
|
3. **Weergave:** Kies Systeem (volgt telefoon), Licht of Donker
|
||||||
4. **Relais labels:** Stel eigen namen in voor de twee relais knoppen
|
4. **Relais labels:** Stel eigen namen in voor de twee relais knoppen
|
||||||
|
|
||||||
De key wordt opgeslagen in EncryptedSharedPreferences (AES256-GCM versleuteld) en overleeft app-herstarts. Bij geen key toont de hoofdapp "Geen 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 sleutel — open Instellingen" en wordt er niet gescant.
|
||||||
|
|
||||||
### Hoofdscherm UI
|
### Hoofdscherm UI
|
||||||
|
|
||||||
|
|
@ -385,7 +385,7 @@ De app gebruikt `Theme.AppCompat.DayNight` in plaats van het eerder geforceerde
|
||||||
- De app gebruikt raw Android USB API (`UsbDeviceConnection.bulkTransfer`) in plaats van een externe serial library.
|
- De app gebruikt raw Android USB API (`UsbDeviceConnection.bulkTransfer`) in plaats van een externe serial library.
|
||||||
- `minSdk` is 26 (Android 8.0).
|
- `minSdk` is 26 (Android 8.0).
|
||||||
- ProgressDialog is vervangen door eigen `AlertDialog` layouts (`dialog_waiting.xml`, `dialog_progress.xml`).
|
- ProgressDialog is vervangen door eigen `AlertDialog` layouts (`dialog_waiting.xml`, `dialog_progress.xml`).
|
||||||
- De HMAC key in de app is opgeslagen via `EncryptedSharedPreferences` (AES256-GCM).
|
- De HMAC key in de app is opgeslagen via `SharedPreferences`.
|
||||||
- De app gebruikt `Theme.AppCompat.DayNight` met Systeem/Licht/Donker thema-selector.
|
- De app gebruikt `Theme.AppCompat.DayNight` met Systeem/Licht/Donker thema-selector.
|
||||||
- `compileSdk`/`targetSdk` blijft op 34 (Android 14) voor maximale compatibiliteit met de doelgroep.
|
- `compileSdk`/`targetSdk` blijft op 34 (Android 14) voor maximale compatibiliteit met de doelgroep.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import android.annotation.SuppressLint
|
||||||
import android.bluetooth.BluetoothAdapter
|
import android.bluetooth.BluetoothAdapter
|
||||||
import android.bluetooth.BluetoothGatt
|
import android.bluetooth.BluetoothGatt
|
||||||
import android.bluetooth.BluetoothGattCharacteristic
|
import android.bluetooth.BluetoothGattCharacteristic
|
||||||
import android.bluetooth.BluetoothGattDescriptor
|
|
||||||
import android.bluetooth.BluetoothProfile
|
import android.bluetooth.BluetoothProfile
|
||||||
import android.bluetooth.le.ScanCallback
|
import android.bluetooth.le.ScanCallback
|
||||||
import android.bluetooth.le.ScanFilter
|
import android.bluetooth.le.ScanFilter
|
||||||
|
|
@ -34,7 +33,7 @@ class BleRelayManager(
|
||||||
private val SERVICE_UUID: UUID = UUID.fromString("4fafc201-1fb5-459e-8fcc-c5c9c331914b")
|
private val SERVICE_UUID: UUID = UUID.fromString("4fafc201-1fb5-459e-8fcc-c5c9c331914b")
|
||||||
private val CHARACTERISTIC_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26d8")
|
private val CHARACTERISTIC_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26d8")
|
||||||
private val UUID_CHAR_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26d9")
|
private val UUID_CHAR_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26d9")
|
||||||
private val CCCD_UUID: UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
|
|
||||||
private const val SCAN_TIMEOUT_MS = 30000L
|
private const val SCAN_TIMEOUT_MS = 30000L
|
||||||
private const val RECONNECT_DELAY_MS = 2000L
|
private const val RECONNECT_DELAY_MS = 2000L
|
||||||
private const val MAX_SCAN_RETRIES = 3
|
private const val MAX_SCAN_RETRIES = 3
|
||||||
|
|
@ -251,12 +250,6 @@ class BleRelayManager(
|
||||||
val storedUuid = deviceUuid
|
val storedUuid = deviceUuid
|
||||||
if (storedUuid != null && value.contentEquals(storedUuid)) {
|
if (storedUuid != null && value.contentEquals(storedUuid)) {
|
||||||
Log.d(TAG, "UUID verified, setting up relay")
|
Log.d(TAG, "UUID verified, setting up relay")
|
||||||
relayCharacteristic?.let { rc ->
|
|
||||||
gatt.setCharacteristicNotification(rc, true)
|
|
||||||
val descriptor = rc.getDescriptor(CCCD_UUID)
|
|
||||||
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
|
|
||||||
gatt.writeDescriptor(descriptor)
|
|
||||||
}
|
|
||||||
handler.post { callbacks.onStatusChanged(BleStatus.Connected) }
|
handler.post { callbacks.onStatusChanged(BleStatus.Connected) }
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "UUID mismatch -- disconnecting")
|
Log.w(TAG, "UUID mismatch -- disconnecting")
|
||||||
|
|
|
||||||
|
|
@ -376,6 +376,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
cleanupAction()
|
cleanupAction()
|
||||||
throw e
|
throw e
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Flash inner error", e)
|
||||||
progressDialog?.dismiss()
|
progressDialog?.dismiss()
|
||||||
progressDialog = null
|
progressDialog = null
|
||||||
Toast.makeText(this@SettingsActivity, getString(R.string.flash_failed, e.message), Toast.LENGTH_LONG).show()
|
Toast.makeText(this@SettingsActivity, getString(R.string.flash_failed, e.message), Toast.LENGTH_LONG).show()
|
||||||
|
|
|
||||||
|
|
@ -192,10 +192,9 @@ void setup() {
|
||||||
NimBLEService *pService = pServer->createService(SERVICE_UUID);
|
NimBLEService *pService = pServer->createService(SERVICE_UUID);
|
||||||
pCharacteristic = pService->createCharacteristic(
|
pCharacteristic = pService->createCharacteristic(
|
||||||
CHARACTERISTIC_UUID,
|
CHARACTERISTIC_UUID,
|
||||||
NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::NOTIFY
|
NIMBLE_PROPERTY::WRITE_NR
|
||||||
);
|
);
|
||||||
pCharacteristic->setCallbacks(new MyCharacteristicCallbacks());
|
pCharacteristic->setCallbacks(new MyCharacteristicCallbacks());
|
||||||
pCharacteristic->setValue("0");
|
|
||||||
|
|
||||||
NimBLECharacteristic *pUuidChar = pService->createCharacteristic(
|
NimBLECharacteristic *pUuidChar = pService->createCharacteristic(
|
||||||
UUID_CHAR_UUID,
|
UUID_CHAR_UUID,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue