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:
parent
c707c21d7f
commit
9a84b05b8a
5 changed files with 155 additions and 116 deletions
|
|
@ -15,6 +15,7 @@ import android.os.Looper
|
|||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.SecureRandom
|
||||
|
||||
class UsbKeyReceiver : BroadcastReceiver() {
|
||||
|
||||
|
|
@ -26,6 +27,7 @@ class UsbKeyReceiver : BroadcastReceiver() {
|
|||
private const val MAX_RETRIES = 5
|
||||
private const val RETRY_DELAY_MS = 1000L
|
||||
private const val CDC_DATA_INTERFACE_CLASS = 0x0A
|
||||
private const val SET_KEY_TIMEOUT_MS = 3000
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
|
|
@ -70,6 +72,13 @@ class UsbKeyReceiver : BroadcastReceiver() {
|
|||
return usbManager.deviceList.values.firstOrNull { it.vendorId == ESP_VENDOR_ID }
|
||||
}
|
||||
|
||||
private fun generateRandomHexKey(): String {
|
||||
val random = SecureRandom()
|
||||
val bytes = ByteArray(32)
|
||||
random.nextBytes(bytes)
|
||||
return bytes.joinToString("") { "%02X".format(it) }
|
||||
}
|
||||
|
||||
private fun requestPermission(context: Context, usbManager: UsbManager, device: UsbDevice) {
|
||||
val flags = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
|
||||
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
|
|
@ -186,7 +195,7 @@ class UsbKeyReceiver : BroadcastReceiver() {
|
|||
val len = connection.bulkTransfer(epIn, buf, buf.size, 500)
|
||||
if (len > 0) {
|
||||
sb.append(String(buf, 0, len, StandardCharsets.US_ASCII))
|
||||
if (sb.contains("HMAC_KEY:") && sb.contains("\n")) {
|
||||
if ((sb.contains("HMAC_KEY:") || sb.contains("NO_KEY")) && sb.contains("\n")) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -196,6 +205,8 @@ class UsbKeyReceiver : BroadcastReceiver() {
|
|||
Log.d(TAG, "Serial response: ${response.take(200)}")
|
||||
|
||||
val keyLine = response.lines().firstOrNull { it.startsWith("HMAC_KEY:") }
|
||||
val noKey = response.lines().any { it.trim() == "NO_KEY" }
|
||||
|
||||
if (keyLine != null) {
|
||||
val hexKey = keyLine.removePrefix("HMAC_KEY:").trim()
|
||||
if (hexKey.length == 64 && hexKey.all { it in "0123456789ABCDEFabcdef" }) {
|
||||
|
|
@ -212,6 +223,45 @@ class UsbKeyReceiver : BroadcastReceiver() {
|
|||
handler.post { Toast.makeText(context, "Ongeldig sleutel formaat", Toast.LENGTH_SHORT).show() }
|
||||
return
|
||||
}
|
||||
} else if (noKey) {
|
||||
Log.d(TAG, "ESP has no key — generating and provisioning")
|
||||
val newKey = generateRandomHexKey()
|
||||
Log.d(TAG, "Generated key: $newKey")
|
||||
|
||||
val setKeyCmd = "SET_KEY:$newKey\n".toByteArray(StandardCharsets.US_ASCII)
|
||||
val written = connection.bulkTransfer(epOut, setKeyCmd, setKeyCmd.size, 1000)
|
||||
Log.d(TAG, "Sent SET_KEY (${written} bytes)")
|
||||
|
||||
Thread.sleep(300)
|
||||
|
||||
val okBuf = ByteArray(256)
|
||||
val okSb = StringBuilder()
|
||||
val okStart = System.currentTimeMillis()
|
||||
while (System.currentTimeMillis() - okStart < SET_KEY_TIMEOUT_MS) {
|
||||
val len = connection.bulkTransfer(epIn, okBuf, okBuf.size, 500)
|
||||
if (len > 0) {
|
||||
okSb.append(String(okBuf, 0, len, StandardCharsets.US_ASCII))
|
||||
if (okSb.contains("OK") && okSb.contains("\n")) break
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "SET_KEY response: ${okSb.toString().take(100)}")
|
||||
|
||||
if (okSb.contains("OK")) {
|
||||
Log.d(TAG, "Key provisioned successfully, saving to prefs")
|
||||
val prefs = context.getSharedPreferences(SettingsActivity.PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.edit().putString(SettingsActivity.PREF_KEY, newKey).apply()
|
||||
handler.post {
|
||||
Toast.makeText(context, "Nieuwe HMAC sleutel gegenereerd en naar ESP geschreven!", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
return
|
||||
} else {
|
||||
Log.w(TAG, "SET_KEY failed, no OK response")
|
||||
connection.close()
|
||||
connection = null
|
||||
if (attempt < MAX_RETRIES) {
|
||||
Thread.sleep(RETRY_DELAY_MS)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Attempt $attempt: no HMAC_KEY in response, will retry")
|
||||
connection.close()
|
||||
|
|
|
|||
Loading…
Reference in a new issue