mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-09-19 04:59:59 +00:00
fix(ble): require a verified ANNOUNCE before granting broadcast notifications
A peer could subscribe to the full mesh feed by writing the CCCD alone. Gate subscription on a signature-valid announce from that address and fail closed with GATT_INSUFFICIENT_AUTHORIZATION until one arrives.
This commit is contained in:
parent
eab894ce78
commit
6e53bef71e
@ -101,6 +101,10 @@ class BluetoothConnectionManager(
|
||||
// Public property for address-peer mapping
|
||||
val addressPeerMap get() = connectionTracker.addressPeerMap
|
||||
|
||||
fun noteVerifiedAnnounceReceived(deviceAddress: String) {
|
||||
connectionTracker.noteAnnounceReceived(deviceAddress)
|
||||
}
|
||||
|
||||
fun observePeerIfCurrent(deviceAddress: String, linkID: String, peerID: String): Boolean =
|
||||
connectionTracker.observePeerIfCurrent(deviceAddress, linkID, peerID)
|
||||
|
||||
|
||||
@ -283,6 +283,26 @@ class BluetoothGattServerManager(
|
||||
}
|
||||
|
||||
if (BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE.contentEquals(value)) {
|
||||
if (!GattNotificationEligibility.maySubscribe(
|
||||
connectionTracker.hasSeenFirstAnnounce(device.address)
|
||||
)
|
||||
) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"Rejecting broadcast subscription from ${device.address} before verified ANNOUNCE"
|
||||
)
|
||||
if (responseNeeded) {
|
||||
gattServer?.sendResponse(
|
||||
device,
|
||||
requestId,
|
||||
BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION,
|
||||
0,
|
||||
null
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
connectionTracker.addSubscribedDevice(device)
|
||||
|
||||
connectionScope.launch {
|
||||
|
||||
@ -579,6 +579,10 @@ class BluetoothMeshService(private val context: Context) : TransportBridgeServic
|
||||
val result = messageHandler.handleAnnounceWithResult(routed)
|
||||
if (result !is AnnounceHandlingResult.Accepted) return false
|
||||
|
||||
routed.relayAddress?.let { deviceAddress ->
|
||||
connectionManager.noteVerifiedAnnounceReceived(deviceAddress)
|
||||
}
|
||||
|
||||
DirectLinkAnnouncementPolicy.observationFor(routed, MAX_TTL)?.let { observation ->
|
||||
if (connectionManager.observePeerIfCurrent(
|
||||
observation.relayAddress,
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
package com.bitchat.android.mesh
|
||||
|
||||
/**
|
||||
* Whether a BLE central may subscribe to the mesh broadcast notification feed.
|
||||
*
|
||||
* Granting notifications before the peer has sent a verified ANNOUNCE would let
|
||||
* a passive listener harvest the full mesh feed with no participation (#901).
|
||||
*/
|
||||
internal object GattNotificationEligibility {
|
||||
fun maySubscribe(hasVerifiedAnnounce: Boolean): Boolean = hasVerifiedAnnounce
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package com.bitchat.android.mesh
|
||||
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class GattNotificationEligibilityTest {
|
||||
@Test
|
||||
fun `subscription denied before a verified announce`() {
|
||||
assertFalse(GattNotificationEligibility.maySubscribe(hasVerifiedAnnounce = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `subscription allowed after a verified announce`() {
|
||||
assertTrue(GattNotificationEligibility.maySubscribe(hasVerifiedAnnounce = true))
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user