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 f2204451..73d06df3 100644 --- a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt +++ b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt @@ -41,6 +41,7 @@ class BluetoothMeshService(private val context: Context) { // Core components - each handling specific responsibilities private val encryptionService = EncryptionService(context) + private val noiseEncryptionService = com.bitchat.android.noise.NoiseEncryptionService(context) private val peerManager = PeerManager() private val fragmentManager = FragmentManager() private val securityManager = SecurityManager(encryptionService, myPeerID) @@ -123,11 +124,40 @@ class BluetoothMeshService(private val context: Context) { override fun processNoiseHandshake(peerID: String, payload: ByteArray, isInitiation: Boolean): Boolean { return try { - // For now, treat as legacy key exchange until full Noise implementation - encryptionService.addPeerPublicKey(peerID, payload) + Log.d(TAG, "Processing Noise handshake from $peerID, payload size: ${payload.size} bytes") + + // Use proper Noise protocol implementation + val response = noiseEncryptionService.processHandshakeMessage(payload, peerID) + + if (response != null) { + // Send response back + val responsePacket = BitchatPacket( + version = 1u, + type = MessageType.NOISE_HANDSHAKE_RESP.value, + senderID = hexStringToByteArray(myPeerID), + recipientID = hexStringToByteArray(peerID), + timestamp = System.currentTimeMillis().toULong(), + payload = response, + ttl = 1u + ) + connectionManager.broadcastPacket(RoutedPacket(responsePacket)) + Log.d(TAG, "Sent Noise handshake response to $peerID") + } + + // Check if session is now established + if (noiseEncryptionService.hasEstablishedSession(peerID)) { + Log.d(TAG, "Noise session established with $peerID") + // Get the peer's public key for fingerprinting + val peerPublicKey = noiseEncryptionService.getPeerPublicKeyData(peerID) + if (peerPublicKey != null) { + // Notify delegate about successful handshake + delegate?.registerPeerPublicKey(peerID, peerPublicKey) + } + } + true } catch (e: Exception) { - Log.e(TAG, "Failed to process Noise handshake: ${e.message}") + Log.e(TAG, "Failed to process Noise handshake from $peerID: ${e.message}") false } } @@ -203,16 +233,34 @@ class BluetoothMeshService(private val context: Context) { // Noise protocol operations override fun hasNoiseSession(peerID: String): Boolean { - // For now, return false since we don't have full Noise implemented - // This will be updated when NoiseEncryptionService is fully integrated - return false + return noiseEncryptionService.hasEstablishedSession(peerID) } override fun initiateNoiseHandshake(peerID: String) { - // For now, send a basic key exchange instead of full Noise handshake - // This will be updated when NoiseEncryptionService is fully integrated - Log.d(TAG, "TODO: Initiate full Noise handshake with $peerID") - sendKeyExchangeToDevice() + try { + // Initiate proper Noise handshake with specific peer + val handshakeData = noiseEncryptionService.initiateHandshake(peerID) + + if (handshakeData != null) { + val packet = BitchatPacket( + version = 1u, + type = MessageType.NOISE_HANDSHAKE_INIT.value, + senderID = hexStringToByteArray(myPeerID), + recipientID = hexStringToByteArray(peerID), + timestamp = System.currentTimeMillis().toULong(), + payload = handshakeData, + ttl = 1u + ) + + connectionManager.broadcastPacket(RoutedPacket(packet)) + Log.d(TAG, "Initiated Noise handshake with $peerID (${handshakeData.size} bytes)") + } else { + Log.w(TAG, "Failed to generate Noise handshake data for $peerID") + } + + } catch (e: Exception) { + Log.e(TAG, "Failed to initiate Noise handshake with $peerID: ${e.message}") + } } override fun updatePeerIDBinding(newPeerID: String, fingerprint: String, nickname: String, @@ -324,6 +372,11 @@ class BluetoothMeshService(private val context: Context) { } override fun onDeviceConnected(device: android.bluetooth.BluetoothDevice) { + // Send initial announcements after services are ready + serviceScope.launch { + delay(100) + sendBroadcastAnnounce() + } // Send key exchange to newly connected device serviceScope.launch { delay(100) // Ensure connection is stable @@ -354,11 +407,6 @@ class BluetoothMeshService(private val context: Context) { if (connectionManager.startServices()) { isActive = true - // Send initial announcements after services are ready - serviceScope.launch { - delay(1000) - sendBroadcastAnnounce() - } } else { Log.e(TAG, "Failed to start Bluetooth services") } @@ -540,19 +588,25 @@ class BluetoothMeshService(private val context: Context) { } /** - * Send key exchange + * Send identity announcement (broadcast our static public key) */ private fun sendKeyExchangeToDevice() { - val publicKeyData = securityManager.getCombinedPublicKeyData() + // For discovery, broadcast our static identity key (32 bytes) so peers can identify us + // This is not a handshake initiation, but identity announcement + val identityData = noiseEncryptionService.getStaticPublicKeyData() + val packet = BitchatPacket( - type = MessageType.NOISE_HANDSHAKE_INIT.value, - ttl = 1u, - senderID = myPeerID, - payload = publicKeyData + version = 1u, + type = MessageType.NOISE_IDENTITY_ANNOUNCE.value, // Changed to identity announce + senderID = hexStringToByteArray(myPeerID), + recipientID = SpecialRecipients.BROADCAST, + timestamp = System.currentTimeMillis().toULong(), + payload = identityData, + ttl = 1u ) connectionManager.broadcastPacket(RoutedPacket(packet)) - Log.d(TAG, "Sent key exchange") + Log.d(TAG, "Sent Noise identity announcement (${identityData.size} bytes)") } /** diff --git a/app/src/main/java/com/bitchat/android/noise/NoiseSession.kt b/app/src/main/java/com/bitchat/android/noise/NoiseSession.kt index b57b3eda..b43525a6 100644 --- a/app/src/main/java/com/bitchat/android/noise/NoiseSession.kt +++ b/app/src/main/java/com/bitchat/android/noise/NoiseSession.kt @@ -84,14 +84,14 @@ class NoiseSession( val role = if (isInitiator) HandshakeState.INITIATOR else HandshakeState.RESPONDER handshakeState = HandshakeState(PROTOCOL_NAME, role) - // Check if we need to set a local key pair + // Set up local static key pair properly if (handshakeState?.needsLocalKeyPair() == true) { - val staticKeyPair = Noise.createDH("25519") - staticKeyPair.setPrivateKey(localStaticPrivateKey, 0) - - // Set the local key pair on the handshake state val localKeyPair = handshakeState?.getLocalKeyPair() - localKeyPair?.setPrivateKey(localStaticPrivateKey, 0) + if (localKeyPair != null) { + // Set the static private key we loaded/generated + localKeyPair.setPrivateKey(localStaticPrivateKey, 0) + Log.d(TAG, "Set local static key for handshake state") + } } // Start the handshake @@ -121,8 +121,9 @@ class NoiseSession( try { state = NoiseSessionState.Handshaking - val messageBuffer = ByteArray(256) // Max handshake message size - val messageLength = handshakeState?.writeMessage(ByteArray(0), 0, messageBuffer, 0, 0) ?: 0 + val messageBuffer = ByteArray(512) // Increased buffer size for XX pattern + val handshakeStateLocal = handshakeState ?: throw IllegalStateException("Handshake state is null") + val messageLength = handshakeStateLocal.writeMessage(ByteArray(0), 0, messageBuffer, 0, 0) val firstMessage = messageBuffer.copyOf(messageLength) Log.d(TAG, "Sent real XX handshake message 1 to $peerID (${firstMessage.size} bytes)") @@ -154,19 +155,21 @@ class NoiseSession( } val payloadBuffer = ByteArray(256) // Buffer for any payload data + val handshakeStateLocal = handshakeState ?: throw IllegalStateException("Handshake state is null") // Read the incoming message - val payloadLength = handshakeState?.readMessage(message, 0, message.size, payloadBuffer, 0) ?: 0 + val payloadLength = handshakeStateLocal.readMessage(message, 0, message.size, payloadBuffer, 0) Log.d(TAG, "Read handshake message, payload length: $payloadLength") // Check the handshake action state - val action = handshakeState?.getAction() ?: HandshakeState.FAILED + val action = handshakeStateLocal.getAction() + Log.d(TAG, "Handshake action after read: $action") return when (action) { HandshakeState.WRITE_MESSAGE -> { // Need to send a response - val responseBuffer = ByteArray(256) - val responseLength = handshakeState?.writeMessage(ByteArray(0), 0, responseBuffer, 0, 0) ?: 0 + val responseBuffer = ByteArray(512) // Increased buffer size for XX pattern message 2 + val responseLength = handshakeStateLocal.writeMessage(ByteArray(0), 0, responseBuffer, 0, 0) responseBuffer.copyOf(responseLength).also { Log.d(TAG, "Generated handshake response: ${it.size} bytes") } @@ -180,18 +183,18 @@ class NoiseSession( } HandshakeState.FAILED -> { - throw Exception("Handshake failed") + throw Exception("Handshake failed - action state is FAILED") } else -> { - Log.d(TAG, "Handshake action: $action") + Log.d(TAG, "Handshake action: $action - no response needed") null } } } catch (e: Exception) { state = NoiseSessionState.Failed(e) - Log.e(TAG, "Real handshake failed with $peerID: ${e.message}") + Log.e(TAG, "Real handshake failed with $peerID: ${e.message}", e) throw e } }