diff --git a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt index 20cd8902..64e01084 100644 --- a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt +++ b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt @@ -134,9 +134,15 @@ class BluetoothMeshService(private val context: Context) { payload = response, 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)") } + + override fun getPeerInfo(peerID: String): PeerInfo? { + return peerManager.getPeerInfo(peerID) + } } // StoreForwardManager delegates @@ -191,7 +197,9 @@ class BluetoothMeshService(private val context: Context) { // Packet operations 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) { @@ -240,7 +248,9 @@ class BluetoothMeshService(private val context: Context) { 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)") } else { Log.w(TAG, "Failed to generate Noise handshake data for $peerID") @@ -456,7 +466,9 @@ class BluetoothMeshService(private val context: Context) { 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 ) - 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)") // 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 ) - 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") } catch (e: Exception) { @@ -699,7 +715,9 @@ class BluetoothMeshService(private val context: Context) { 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 } + /** + * 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 /** diff --git a/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt b/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt index 59090c52..89f0197c 100644 --- a/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt +++ b/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt @@ -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 { // Skip validation for our own packets @@ -83,6 +83,9 @@ class SecurityManager(private val encryptionService: EncryptionService, private processedMessages.add(messageID) messageTimestamps[messageID] = currentTime + // NEW: Signature verification logging (not rejecting yet) + verifyPacketSignatureWithLogging(packet, peerID) + Log.d(TAG, "Packet validation passed for $peerID, messageID: $messageID") 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 */ @@ -339,4 +388,5 @@ class SecurityManager(private val encryptionService: EncryptionService, private interface SecurityManagerDelegate { fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray) fun sendHandshakeResponse(peerID: String, response: ByteArray) + fun getPeerInfo(peerID: String): PeerInfo? // NEW: For signature verification }