Sign mesh message (#353)

* sign BLE packets

* fix x
This commit is contained in:
callebtc
2025-08-30 12:45:36 +02:00
committed by GitHub
parent cedc6552ce
commit 33b5814b7a
2 changed files with 103 additions and 8 deletions
@@ -134,9 +134,15 @@ class BluetoothMeshService(private val context: Context) {
payload = response, payload = response,
ttl = MAX_TTL ttl = MAX_TTL
) )
connectionManager.broadcastPacket(RoutedPacket(responsePacket)) // Sign the handshake response
val signedPacket = signPacketBeforeBroadcast(responsePacket)
connectionManager.broadcastPacket(RoutedPacket(signedPacket))
Log.d(TAG, "Sent Noise handshake response to $peerID (${response.size} bytes)") Log.d(TAG, "Sent Noise handshake response to $peerID (${response.size} bytes)")
} }
override fun getPeerInfo(peerID: String): PeerInfo? {
return peerManager.getPeerInfo(peerID)
}
} }
// StoreForwardManager delegates // StoreForwardManager delegates
@@ -191,7 +197,9 @@ class BluetoothMeshService(private val context: Context) {
// Packet operations // Packet operations
override fun sendPacket(packet: BitchatPacket) { override fun sendPacket(packet: BitchatPacket) {
connectionManager.broadcastPacket(RoutedPacket(packet)) // Sign the packet before broadcasting
val signedPacket = signPacketBeforeBroadcast(packet)
connectionManager.broadcastPacket(RoutedPacket(signedPacket))
} }
override fun relayPacket(routed: RoutedPacket) { override fun relayPacket(routed: RoutedPacket) {
@@ -240,7 +248,9 @@ class BluetoothMeshService(private val context: Context) {
ttl = MAX_TTL ttl = MAX_TTL
) )
connectionManager.broadcastPacket(RoutedPacket(packet)) // Sign the handshake packet before broadcasting
val signedPacket = signPacketBeforeBroadcast(packet)
connectionManager.broadcastPacket(RoutedPacket(signedPacket))
Log.d(TAG, "Initiated Noise handshake with $peerID (${handshakeData.size} bytes)") Log.d(TAG, "Initiated Noise handshake with $peerID (${handshakeData.size} bytes)")
} else { } else {
Log.w(TAG, "Failed to generate Noise handshake data for $peerID") Log.w(TAG, "Failed to generate Noise handshake data for $peerID")
@@ -456,7 +466,9 @@ class BluetoothMeshService(private val context: Context) {
ttl = MAX_TTL ttl = MAX_TTL
) )
connectionManager.broadcastPacket(RoutedPacket(packet)) // Sign the packet before broadcasting
val signedPacket = signPacketBeforeBroadcast(packet)
connectionManager.broadcastPacket(RoutedPacket(signedPacket))
} }
} }
@@ -521,7 +533,9 @@ class BluetoothMeshService(private val context: Context) {
ttl = MAX_TTL ttl = MAX_TTL
) )
connectionManager.broadcastPacket(RoutedPacket(packet)) // Sign the packet before broadcasting
val signedPacket = signPacketBeforeBroadcast(packet)
connectionManager.broadcastPacket(RoutedPacket(signedPacket))
Log.d(TAG, "📤 Sent encrypted private message to $recipientPeerID (${encrypted.size} bytes)") Log.d(TAG, "📤 Sent encrypted private message to $recipientPeerID (${encrypted.size} bytes)")
// FIXED: Don't send didReceiveMessage for our own sent messages // FIXED: Don't send didReceiveMessage for our own sent messages
@@ -584,7 +598,9 @@ class BluetoothMeshService(private val context: Context) {
ttl = 7u // Same TTL as iOS messageTTL ttl = 7u // Same TTL as iOS messageTTL
) )
connectionManager.broadcastPacket(RoutedPacket(packet)) // Sign the packet before broadcasting
val signedPacket = signPacketBeforeBroadcast(packet)
connectionManager.broadcastPacket(RoutedPacket(signedPacket))
Log.d(TAG, "📤 Sent read receipt to $recipientPeerID for message $messageID") Log.d(TAG, "📤 Sent read receipt to $recipientPeerID for message $messageID")
} catch (e: Exception) { } catch (e: Exception) {
@@ -699,7 +715,9 @@ class BluetoothMeshService(private val context: Context) {
payload = nickname.toByteArray() payload = nickname.toByteArray()
) )
connectionManager.broadcastPacket(RoutedPacket(packet)) // Sign the packet before broadcasting
val signedPacket = signPacketBeforeBroadcast(packet)
connectionManager.broadcastPacket(RoutedPacket(signedPacket))
} }
/** /**
@@ -861,6 +879,33 @@ class BluetoothMeshService(private val context: Context) {
return result return result
} }
/**
* Sign packet before broadcasting using our signing private key
*/
private fun signPacketBeforeBroadcast(packet: BitchatPacket): BitchatPacket {
return try {
// Get the canonical packet data for signing (without signature)
val packetDataForSigning = packet.toBinaryDataForSigning()
if (packetDataForSigning == null) {
Log.w(TAG, "Failed to encode packet type ${packet.type} for signing, sending unsigned")
return packet
}
// Sign the packet data using our signing key
val signature = encryptionService.signData(packetDataForSigning)
if (signature != null) {
Log.d(TAG, "✅ Signed packet type ${packet.type} (signature ${signature.size} bytes)")
packet.copy(signature = signature)
} else {
Log.w(TAG, "Failed to sign packet type ${packet.type}, sending unsigned")
packet
}
} catch (e: Exception) {
Log.w(TAG, "Error signing packet type ${packet.type}: ${e.message}, sending unsigned")
packet
}
}
// MARK: - Panic Mode Support // MARK: - Panic Mode Support
/** /**
@@ -41,7 +41,7 @@ class SecurityManager(private val encryptionService: EncryptionService, private
} }
/** /**
* Validate packet security (timestamp, replay attacks, duplicates) * Validate packet security (timestamp, replay attacks, duplicates, signatures)
*/ */
fun validatePacket(packet: BitchatPacket, peerID: String): Boolean { fun validatePacket(packet: BitchatPacket, peerID: String): Boolean {
// Skip validation for our own packets // Skip validation for our own packets
@@ -83,6 +83,9 @@ class SecurityManager(private val encryptionService: EncryptionService, private
processedMessages.add(messageID) processedMessages.add(messageID)
messageTimestamps[messageID] = currentTime messageTimestamps[messageID] = currentTime
// NEW: Signature verification logging (not rejecting yet)
verifyPacketSignatureWithLogging(packet, peerID)
Log.d(TAG, "Packet validation passed for $peerID, messageID: $messageID") Log.d(TAG, "Packet validation passed for $peerID, messageID: $messageID")
return true return true
} }
@@ -225,6 +228,52 @@ class SecurityManager(private val encryptionService: EncryptionService, private
} }
} }
/**
* Verify packet signature using peer's signing public key and log the result
*/
private fun verifyPacketSignatureWithLogging(packet: BitchatPacket, peerID: String) {
try {
// Check if packet has a signature
if (packet.signature == null) {
Log.d(TAG, "📝 Signature check for $peerID: NO_SIGNATURE (packet type ${packet.type})")
return
}
// Try to get peer's signing public key from peer info
val peerInfo = delegate?.getPeerInfo(peerID)
val signingPublicKey = peerInfo?.signingPublicKey
if (signingPublicKey == null) {
Log.d(TAG, "📝 Signature check for $peerID: NO_SIGNING_KEY (packet type ${packet.type})")
return
}
// Get the canonical packet data for signature verification (without signature)
val packetDataForSigning = packet.toBinaryDataForSigning()
if (packetDataForSigning == null) {
Log.w(TAG, "📝 Signature check for $peerID: ENCODING_ERROR (packet type ${packet.type})")
return
}
// Verify the signature using the peer's signing public key
val signature = packet.signature!! // We already checked for null above
val isSignatureValid = encryptionService.verifyEd25519Signature(
signature,
packetDataForSigning,
signingPublicKey
)
if (isSignatureValid) {
Log.d(TAG, "📝 Signature check for $peerID: ✅ VALID (packet type ${packet.type})")
} else {
Log.w(TAG, "📝 Signature check for $peerID: ❌ INVALID (packet type ${packet.type})")
}
} catch (e: Exception) {
Log.w(TAG, "📝 Signature check for $peerID: ERROR - ${e.message} (packet type ${packet.type})")
}
}
/** /**
* Check if we have encryption keys for a peer * Check if we have encryption keys for a peer
*/ */
@@ -339,4 +388,5 @@ class SecurityManager(private val encryptionService: EncryptionService, private
interface SecurityManagerDelegate { interface SecurityManagerDelegate {
fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray) fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray)
fun sendHandshakeResponse(peerID: String, response: ByteArray) fun sendHandshakeResponse(peerID: String, response: ByteArray)
fun getPeerInfo(peerID: String): PeerInfo? // NEW: For signature verification
} }