Fix BLE ECDH handshake: wait for CCCD write before sending pubkey
All checks were successful
CI / build (push) Successful in 1m5s
All checks were successful
CI / build (push) Successful in 1m5s
BleRelayManager.kt: Android's gatt.writeCharacteristic() returns false when a previous GATT operation (CCCD writeDescriptor) is still pending. Split startEcdhHandshake() into two phases: CCCD setup (with ecdhCccdPending flag) and sendEcdhHandshake() triggered from onDescriptorWrite callback. main.cpp: After SET_ID over USB, update BLE advertising scan response manufacturer data so Android can find the server by its new UUID prefix.
This commit is contained in:
parent
a242bfa58a
commit
3be06b7469
2 changed files with 43 additions and 7 deletions
|
|
@ -81,6 +81,7 @@ class BleRelayManager(
|
|||
private var ecdhDone = false
|
||||
@Volatile private var ecdhDeferred: CompletableDeferred<Boolean>? = null
|
||||
private var ecdhNotifyBuf: ByteArray = ByteArray(0)
|
||||
private var ecdhCccdPending = false
|
||||
|
||||
private class CompletableDeferred<T> {
|
||||
private var result: T? = null
|
||||
|
|
@ -314,6 +315,7 @@ class BleRelayManager(
|
|||
ecdhCharacteristic = null
|
||||
aesKey = null
|
||||
ecdhDone = false
|
||||
ecdhCccdPending = false
|
||||
seq = 1
|
||||
triedAddresses.clear()
|
||||
handler.postDelayed(scanRunnable, RECONNECT_DELAY_MS)
|
||||
|
|
@ -413,6 +415,19 @@ class BleRelayManager(
|
|||
Log.d(TAG, "ECDH notify received (${value.size} bytes)")
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
|
||||
if (ecdhCccdPending && descriptor.characteristic.uuid == ECDH_UUID) {
|
||||
ecdhCccdPending = false
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
sendEcdhHandshake(gatt)
|
||||
} else {
|
||||
Log.w(TAG, "ECDH: CCCD write failed (status=$status)")
|
||||
gatt.disconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
|
|
@ -425,18 +440,30 @@ class BleRelayManager(
|
|||
return
|
||||
}
|
||||
|
||||
val cccd = char.getDescriptor(OTA_CCCD_UUID)
|
||||
if (cccd == null) {
|
||||
Log.w(TAG, "ECDH: no CCCD descriptor")
|
||||
gatt.disconnect()
|
||||
return
|
||||
}
|
||||
|
||||
if (!gatt.setCharacteristicNotification(char, true)) {
|
||||
Log.w(TAG, "ECDH: setCharacteristicNotification failed")
|
||||
gatt.disconnect()
|
||||
return
|
||||
}
|
||||
|
||||
val cccd = char.getDescriptor(OTA_CCCD_UUID)
|
||||
if (cccd != null) {
|
||||
ecdhCccdPending = true
|
||||
cccd.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
|
||||
gatt.writeDescriptor(cccd)
|
||||
if (!gatt.writeDescriptor(cccd)) {
|
||||
ecdhCccdPending = false
|
||||
Log.w(TAG, "ECDH: writeDescriptor for CCCD returned false")
|
||||
gatt.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun sendEcdhHandshake(gatt: BluetoothGatt) {
|
||||
val myPsk = psk ?: run {
|
||||
Log.w(TAG, "ECDH: no PSK")
|
||||
gatt.disconnect()
|
||||
|
|
@ -462,9 +489,9 @@ class BleRelayManager(
|
|||
System.arraycopy(sig, 0, handshake, ECDH_PUBKEY_LEN, 32)
|
||||
|
||||
ecdhDeferred = CompletableDeferred()
|
||||
char.value = handshake
|
||||
char.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
|
||||
if (!gatt.writeCharacteristic(char)) {
|
||||
ecdhCharacteristic!!.value = handshake
|
||||
ecdhCharacteristic!!.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
|
||||
if (!gatt.writeCharacteristic(ecdhCharacteristic!!)) {
|
||||
Log.w(TAG, "ECDH: write pubkey failed")
|
||||
gatt.disconnect()
|
||||
return
|
||||
|
|
|
|||
|
|
@ -617,6 +617,15 @@ void loop() {
|
|||
preferences.end();
|
||||
uuidProvisioned = true;
|
||||
pUuidChar->setValue(deviceUuid, ESP_UUID_LEN);
|
||||
|
||||
uint8_t mfgPayload[6];
|
||||
mfgPayload[0] = 0xFF;
|
||||
mfgPayload[1] = 0xFF;
|
||||
memcpy(mfgPayload + 2, deviceUuid, 4);
|
||||
NimBLEAdvertisementData scanResp;
|
||||
scanResp.setCompleteServices(NimBLEUUID(SERVICE_UUID));
|
||||
scanResp.setManufacturerData(std::string((char*)mfgPayload, 6));
|
||||
NimBLEDevice::getAdvertising()->setScanResponseData(scanResp);
|
||||
Serial.println("OK");
|
||||
Serial.println("UUID stored in NVS");
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue