mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-26 11:05:22 +00:00
temporary
This commit is contained in:
@@ -99,6 +99,20 @@ class BluetoothConnectionManager(
|
|||||||
try {
|
try {
|
||||||
isActive = true
|
isActive = true
|
||||||
|
|
||||||
|
|
||||||
|
// CRITICAL FIX: To be discoverable by iOS, we MUST set the adapter's name
|
||||||
|
// to our 8-character peerID. iOS uses the `localName` field for discovery.
|
||||||
|
try {
|
||||||
|
if (bluetoothAdapter?.name != myPeerID) {
|
||||||
|
bluetoothAdapter?.name = myPeerID
|
||||||
|
Log.d(TAG, "Set Bluetooth adapter name to peerID: $myPeerID for iOS compatibility.")
|
||||||
|
}
|
||||||
|
} catch (se: SecurityException) {
|
||||||
|
Log.e(TAG, "Missing BLUETOOTH_CONNECT permission to set adapter name.", se)
|
||||||
|
// This is not fatal, but will prevent discovery by iOS clients.
|
||||||
|
// We can still be discovered if the iOS client initiates the connection.
|
||||||
|
}
|
||||||
|
|
||||||
// Start all component managers
|
// Start all component managers
|
||||||
connectionScope.launch {
|
connectionScope.launch {
|
||||||
// Start connection tracker first
|
// Start connection tracker first
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.bitchat.android.model.ReadReceipt
|
|||||||
import com.bitchat.android.protocol.BitchatPacket
|
import com.bitchat.android.protocol.BitchatPacket
|
||||||
import com.bitchat.android.protocol.MessageType
|
import com.bitchat.android.protocol.MessageType
|
||||||
import com.bitchat.android.protocol.SpecialRecipients
|
import com.bitchat.android.protocol.SpecialRecipients
|
||||||
|
import com.bitchat.android.util.toHexString
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
@@ -36,17 +37,17 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// My peer identification - same format as iOS
|
// My peer identification - same format as iOS
|
||||||
val myPeerID: String = generateCompatiblePeerID()
|
val myPeerID: ByteArray = generateCompatiblePeerID()
|
||||||
|
|
||||||
// Core components - each handling specific responsibilities
|
// Core components - each handling specific responsibilities
|
||||||
private val encryptionService = EncryptionService(context)
|
private val encryptionService = EncryptionService(context)
|
||||||
private val peerManager = PeerManager()
|
private val peerManager = PeerManager()
|
||||||
private val fragmentManager = FragmentManager()
|
private val fragmentManager = FragmentManager()
|
||||||
private val securityManager = SecurityManager(encryptionService, myPeerID)
|
private val securityManager = SecurityManager(encryptionService, myPeerID.toHexString())
|
||||||
private val storeForwardManager = StoreForwardManager()
|
private val storeForwardManager = StoreForwardManager()
|
||||||
private val messageHandler = MessageHandler(myPeerID)
|
private val messageHandler = MessageHandler(myPeerID.toHexString())
|
||||||
internal val connectionManager = BluetoothConnectionManager(context, myPeerID, fragmentManager) // Made internal for access
|
internal val connectionManager = BluetoothConnectionManager(context, myPeerID.toHexString(), fragmentManager) // Made internal for access
|
||||||
private val packetProcessor = PacketProcessor(myPeerID)
|
private val packetProcessor = PacketProcessor(myPeerID.toHexString())
|
||||||
|
|
||||||
// Service state management
|
// Service state management
|
||||||
private var isActive = false
|
private var isActive = false
|
||||||
@@ -119,6 +120,17 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
storeForwardManager.sendCachedMessages(peerID)
|
storeForwardManager.sendCachedMessages(peerID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to process Noise handshake: ${e.message}")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoreForwardManager delegates
|
// StoreForwardManager delegates
|
||||||
@@ -189,6 +201,36 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
return securityManager.decryptFromPeer(encryptedData, senderPeerID)
|
return securityManager.decryptFromPeer(encryptedData, senderPeerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updatePeerIDBinding(newPeerID: String, fingerprint: String, nickname: String,
|
||||||
|
publicKey: ByteArray, previousPeerID: String?) {
|
||||||
|
// Update peer mapping in the PeerManager for peer ID rotation support
|
||||||
|
peerManager.addOrUpdatePeer(newPeerID, nickname)
|
||||||
|
|
||||||
|
// If there was a previous peer ID, remove it to avoid duplicates
|
||||||
|
previousPeerID?.let { oldPeerID ->
|
||||||
|
peerManager.removePeer(oldPeerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the public key with the delegate (ChatViewModel)
|
||||||
|
delegate?.registerPeerPublicKey(newPeerID, publicKey)
|
||||||
|
|
||||||
|
Log.d(TAG, "Updated peer ID binding: $newPeerID (was: $previousPeerID), fingerprint: ${fingerprint.take(16)}...")
|
||||||
|
}
|
||||||
|
|
||||||
// Message operations
|
// Message operations
|
||||||
override fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String? {
|
override fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String? {
|
||||||
return delegate?.decryptChannelMessage(encryptedContent, channel)
|
return delegate?.decryptChannelMessage(encryptedContent, channel)
|
||||||
@@ -584,10 +626,10 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
/**
|
/**
|
||||||
* Generate peer ID compatible with iOS
|
* Generate peer ID compatible with iOS
|
||||||
*/
|
*/
|
||||||
private fun generateCompatiblePeerID(): String {
|
private fun generateCompatiblePeerID(): ByteArray {
|
||||||
val randomBytes = ByteArray(4)
|
val randomBytes = ByteArray(4)
|
||||||
Random.nextBytes(randomBytes)
|
Random.nextBytes(randomBytes)
|
||||||
return randomBytes.joinToString("") { "%02x".format(it) }
|
return randomBytes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import com.bitchat.android.model.RoutedPacket
|
|||||||
import com.bitchat.android.protocol.BitchatPacket
|
import com.bitchat.android.protocol.BitchatPacket
|
||||||
import com.bitchat.android.protocol.MessageType
|
import com.bitchat.android.protocol.MessageType
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.security.MessageDigest
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@@ -29,27 +31,136 @@ class MessageHandler(private val myPeerID: String) {
|
|||||||
private val handlerScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
private val handlerScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle Noise encrypted transport message (temporarily stubbed)
|
* Handle Noise encrypted transport message
|
||||||
*/
|
*/
|
||||||
suspend fun handleNoiseEncrypted(routed: RoutedPacket) {
|
suspend fun handleNoiseEncrypted(routed: RoutedPacket) {
|
||||||
val packet = routed.packet
|
val packet = routed.packet
|
||||||
val peerID = routed.peerID ?: "unknown"
|
val peerID = routed.peerID ?: "unknown"
|
||||||
|
|
||||||
Log.d(TAG, "TODO: Handle Noise encrypted message from $peerID (${packet.payload.size} bytes)")
|
Log.d(TAG, "Processing Noise encrypted message from $peerID (${packet.payload.size} bytes)")
|
||||||
|
|
||||||
// For now, just log it - this will be implemented with actual Noise protocol handling
|
// Skip our own messages
|
||||||
|
if (peerID == myPeerID) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Decrypt the message using the Noise service
|
||||||
|
val decryptedData = delegate?.decryptFromPeer(packet.payload, peerID)
|
||||||
|
if (decryptedData == null) {
|
||||||
|
Log.w(TAG, "Failed to decrypt Noise message from $peerID - may need handshake")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's a special format message (type marker + payload)
|
||||||
|
if (decryptedData.size > 1) {
|
||||||
|
val typeMarker = decryptedData[0].toUByte()
|
||||||
|
|
||||||
|
// Check if this is a delivery ACK with the new format
|
||||||
|
if (typeMarker == MessageType.DELIVERY_ACK.value) {
|
||||||
|
// Extract the ACK JSON data (skip the type marker)
|
||||||
|
val ackData = decryptedData.sliceArray(1 until decryptedData.size)
|
||||||
|
|
||||||
|
// Decode the delivery ACK
|
||||||
|
val ack = DeliveryAck.decode(ackData)
|
||||||
|
if (ack != null) {
|
||||||
|
delegate?.onDeliveryAckReceived(ack)
|
||||||
|
Log.d(TAG, "Processed delivery ACK from $peerID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for read receipt with type marker
|
||||||
|
if (typeMarker == MessageType.READ_RECEIPT.value) {
|
||||||
|
val receiptData = decryptedData.sliceArray(1 until decryptedData.size)
|
||||||
|
val receipt = ReadReceipt.decode(receiptData)
|
||||||
|
if (receipt != null) {
|
||||||
|
delegate?.onReadReceiptReceived(receipt)
|
||||||
|
Log.d(TAG, "Processed read receipt from $peerID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse as a full inner packet (for compatibility with other message types)
|
||||||
|
val innerPacket = BitchatPacket.fromBinaryData(decryptedData)
|
||||||
|
if (innerPacket != null) {
|
||||||
|
Log.d(TAG, "Decrypted inner packet type ${innerPacket.type} from $peerID")
|
||||||
|
|
||||||
|
// Create a new routed packet with the decrypted inner packet
|
||||||
|
val innerRouted = RoutedPacket(innerPacket, peerID, routed.relayAddress)
|
||||||
|
|
||||||
|
// Process the decrypted inner packet recursively
|
||||||
|
when (MessageType.fromValue(innerPacket.type)) {
|
||||||
|
MessageType.MESSAGE -> handleMessage(innerRouted)
|
||||||
|
MessageType.DELIVERY_ACK -> handleDeliveryAck(innerRouted)
|
||||||
|
MessageType.READ_RECEIPT -> handleReadReceipt(innerRouted)
|
||||||
|
else -> {
|
||||||
|
Log.w(TAG, "Unexpected inner packet type: ${innerPacket.type}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Failed to parse decrypted data as packet from $peerID")
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error processing Noise encrypted message from $peerID: ${e.message}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle Noise identity announcement (temporarily stubbed)
|
* Handle Noise identity announcement - supports peer ID rotation
|
||||||
*/
|
*/
|
||||||
suspend fun handleNoiseIdentityAnnouncement(routed: RoutedPacket) {
|
suspend fun handleNoiseIdentityAnnouncement(routed: RoutedPacket) {
|
||||||
val packet = routed.packet
|
val packet = routed.packet
|
||||||
val peerID = routed.peerID ?: "unknown"
|
val peerID = routed.peerID ?: "unknown"
|
||||||
|
|
||||||
Log.d(TAG, "TODO: Handle Noise identity announcement from $peerID (${packet.payload.size} bytes)")
|
Log.d(TAG, "Processing Noise identity announcement from $peerID (${packet.payload.size} bytes)")
|
||||||
|
|
||||||
// For now, just log it - this will be implemented with peer ID rotation handling
|
// Skip our own announcements
|
||||||
|
if (peerID == myPeerID) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Parse the identity announcement
|
||||||
|
val announcement = parseNoiseIdentityAnnouncement(packet.payload)
|
||||||
|
if (announcement == null) {
|
||||||
|
Log.w(TAG, "Failed to parse Noise identity announcement from $peerID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Parsed identity announcement: peerID=${announcement.peerID}, " +
|
||||||
|
"nickname=${announcement.nickname}, fingerprint=${announcement.fingerprint?.take(16)}...")
|
||||||
|
|
||||||
|
// Verify the announcement signature (basic validation)
|
||||||
|
// In a full implementation, this would use cryptographic verification
|
||||||
|
if (announcement.signature.isEmpty()) {
|
||||||
|
Log.w(TAG, "Identity announcement from $peerID has no signature")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update peer binding in the delegate (ChatViewModel/BluetoothMeshService)
|
||||||
|
delegate?.updatePeerIDBinding(
|
||||||
|
newPeerID = announcement.peerID,
|
||||||
|
fingerprint = announcement.fingerprint ?: "",
|
||||||
|
nickname = announcement.nickname,
|
||||||
|
publicKey = announcement.publicKey,
|
||||||
|
previousPeerID = announcement.previousPeerID
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check if we need to initiate a handshake with this peer
|
||||||
|
val hasSession = delegate?.hasNoiseSession(announcement.peerID) ?: false
|
||||||
|
if (!hasSession) {
|
||||||
|
Log.d(TAG, "No session with ${announcement.peerID}, may need handshake")
|
||||||
|
|
||||||
|
// Use lexicographic comparison to decide who initiates (prevents both sides from initiating)
|
||||||
|
if (myPeerID < announcement.peerID) {
|
||||||
|
delegate?.initiateNoiseHandshake(announcement.peerID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Successfully processed identity announcement from $peerID")
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error processing Noise identity announcement from $peerID: ${e.message}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -343,6 +454,45 @@ class MessageHandler(private val myPeerID: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse Noise identity announcement from payload
|
||||||
|
*/
|
||||||
|
private fun parseNoiseIdentityAnnouncement(payload: ByteArray): NoiseIdentityAnnouncement? {
|
||||||
|
return try {
|
||||||
|
val jsonString = String(payload, Charsets.UTF_8)
|
||||||
|
val json = JSONObject(jsonString)
|
||||||
|
|
||||||
|
val peerID = json.getString("peerID")
|
||||||
|
val nickname = json.getString("nickname")
|
||||||
|
val publicKeyBase64 = json.getString("publicKey")
|
||||||
|
val timestampMs = json.getLong("timestamp")
|
||||||
|
val signatureBase64 = json.getString("signature")
|
||||||
|
val previousPeerID = if (json.has("previousPeerID")) json.getString("previousPeerID") else null
|
||||||
|
|
||||||
|
// Decode base64 fields
|
||||||
|
val publicKey = android.util.Base64.decode(publicKeyBase64, android.util.Base64.DEFAULT)
|
||||||
|
val signature = android.util.Base64.decode(signatureBase64, android.util.Base64.DEFAULT)
|
||||||
|
|
||||||
|
// Calculate fingerprint from public key
|
||||||
|
val digest = MessageDigest.getInstance("SHA-256")
|
||||||
|
val hash = digest.digest(publicKey)
|
||||||
|
val fingerprint = hash.joinToString("") { "%02x".format(it) }
|
||||||
|
|
||||||
|
NoiseIdentityAnnouncement(
|
||||||
|
peerID = peerID,
|
||||||
|
nickname = nickname,
|
||||||
|
publicKey = publicKey,
|
||||||
|
timestamp = Date(timestampMs),
|
||||||
|
signature = signature,
|
||||||
|
fingerprint = fingerprint,
|
||||||
|
previousPeerID = previousPeerID
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to parse Noise identity announcement: ${e.message}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shutdown the handler
|
* Shutdown the handler
|
||||||
*/
|
*/
|
||||||
@@ -351,6 +501,47 @@ class MessageHandler(private val myPeerID: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Noise Identity Announcement data class (compatible with iOS version)
|
||||||
|
*/
|
||||||
|
data class NoiseIdentityAnnouncement(
|
||||||
|
val peerID: String,
|
||||||
|
val nickname: String,
|
||||||
|
val publicKey: ByteArray,
|
||||||
|
val timestamp: Date,
|
||||||
|
val signature: ByteArray,
|
||||||
|
val fingerprint: String?,
|
||||||
|
val previousPeerID: String? = null
|
||||||
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as NoiseIdentityAnnouncement
|
||||||
|
|
||||||
|
if (peerID != other.peerID) return false
|
||||||
|
if (nickname != other.nickname) return false
|
||||||
|
if (!publicKey.contentEquals(other.publicKey)) return false
|
||||||
|
if (timestamp != other.timestamp) return false
|
||||||
|
if (!signature.contentEquals(other.signature)) return false
|
||||||
|
if (fingerprint != other.fingerprint) return false
|
||||||
|
if (previousPeerID != other.previousPeerID) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = peerID.hashCode()
|
||||||
|
result = 31 * result + nickname.hashCode()
|
||||||
|
result = 31 * result + publicKey.contentHashCode()
|
||||||
|
result = 31 * result + timestamp.hashCode()
|
||||||
|
result = 31 * result + signature.contentHashCode()
|
||||||
|
result = 31 * result + (fingerprint?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (previousPeerID?.hashCode() ?: 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate interface for message handler callbacks
|
* Delegate interface for message handler callbacks
|
||||||
*/
|
*/
|
||||||
@@ -373,6 +564,12 @@ interface MessageHandlerDelegate {
|
|||||||
fun encryptForPeer(data: ByteArray, recipientPeerID: String): ByteArray?
|
fun encryptForPeer(data: ByteArray, recipientPeerID: String): ByteArray?
|
||||||
fun decryptFromPeer(encryptedData: ByteArray, senderPeerID: String): ByteArray?
|
fun decryptFromPeer(encryptedData: ByteArray, senderPeerID: String): ByteArray?
|
||||||
|
|
||||||
|
// Noise protocol operations
|
||||||
|
fun hasNoiseSession(peerID: String): Boolean
|
||||||
|
fun initiateNoiseHandshake(peerID: String)
|
||||||
|
fun updatePeerIDBinding(newPeerID: String, fingerprint: String, nickname: String,
|
||||||
|
publicKey: ByteArray, previousPeerID: String?)
|
||||||
|
|
||||||
// Message operations
|
// Message operations
|
||||||
fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String?
|
fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String?
|
||||||
|
|
||||||
|
|||||||
@@ -87,16 +87,52 @@ class SecurityManager(private val encryptionService: EncryptionService, private
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle Noise handshake packet (temporarily stubbed for basic build)
|
* Handle Noise handshake packet
|
||||||
*/
|
*/
|
||||||
suspend fun handleNoiseHandshake(routed: RoutedPacket, step: Int): Boolean {
|
suspend fun handleNoiseHandshake(routed: RoutedPacket, step: Int): Boolean {
|
||||||
val packet = routed.packet
|
val packet = routed.packet
|
||||||
val peerID = routed.peerID ?: "unknown"
|
val peerID = routed.peerID ?: "unknown"
|
||||||
|
|
||||||
Log.d(TAG, "TODO: Handle Noise handshake step $step from $peerID (${packet.payload.size} bytes)")
|
Log.d(TAG, "Processing Noise handshake step $step from $peerID (${packet.payload.size} bytes)")
|
||||||
|
|
||||||
// For now, just treat it as a key exchange for compatibility
|
// Skip our own handshake messages
|
||||||
return handleKeyExchange(routed)
|
if (peerID == myPeerID) return false
|
||||||
|
|
||||||
|
if (packet.payload.isEmpty()) {
|
||||||
|
Log.w(TAG, "Noise handshake packet has empty payload")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent duplicate handshake processing
|
||||||
|
val exchangeKey = "$peerID-${packet.payload.sliceArray(0 until minOf(16, packet.payload.size)).contentHashCode()}"
|
||||||
|
|
||||||
|
if (processedKeyExchanges.contains(exchangeKey)) {
|
||||||
|
Log.d(TAG, "Already processed handshake: $exchangeKey")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
processedKeyExchanges.add(exchangeKey)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Process the Noise handshake through the delegate (NoiseEncryptionService)
|
||||||
|
val success = delegate?.processNoiseHandshake(peerID, packet.payload, step == 1) ?: false
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
Log.d(TAG, "Successfully processed Noise handshake step $step from $peerID")
|
||||||
|
|
||||||
|
// Notify delegate
|
||||||
|
delegate?.onKeyExchangeCompleted(peerID, packet.payload, routed.relayAddress)
|
||||||
|
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Failed to process Noise handshake from $peerID")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to process Noise handshake from $peerID: ${e.message}")
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -333,4 +369,5 @@ class SecurityManager(private val encryptionService: EncryptionService, private
|
|||||||
*/
|
*/
|
||||||
interface SecurityManagerDelegate {
|
interface SecurityManagerDelegate {
|
||||||
fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray, receivedAddress: String?)
|
fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray, receivedAddress: String?)
|
||||||
|
fun processNoiseHandshake(peerID: String, payload: ByteArray, isInitiation: Boolean): Boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ data class BitchatPacket(
|
|||||||
var ttl: UByte
|
var ttl: UByte
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
|
// Secondary constructor removed to enforce ByteArray usage for peerIDs
|
||||||
|
/*
|
||||||
constructor(
|
constructor(
|
||||||
type: UByte,
|
type: UByte,
|
||||||
ttl: UByte,
|
ttl: UByte,
|
||||||
@@ -95,6 +97,7 @@ data class BitchatPacket(
|
|||||||
signature = null,
|
signature = null,
|
||||||
ttl = ttl
|
ttl = ttl
|
||||||
)
|
)
|
||||||
|
*/
|
||||||
|
|
||||||
fun toBinaryData(): ByteArray? {
|
fun toBinaryData(): ByteArray? {
|
||||||
return BinaryProtocol.encode(this)
|
return BinaryProtocol.encode(this)
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.bitchat.android.util
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension function to convert a ByteArray to a hexadecimal string.
|
||||||
|
*/
|
||||||
|
fun ByteArray.toHexString(): String {
|
||||||
|
return this.joinToString("") { "%02x".format(it) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.bitchat.android.util
|
||||||
|
|
||||||
|
import java.util.Arrays
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper class for ByteArray to allow it to be used as a key in HashMaps.
|
||||||
|
* The default ByteArray does not override equals() and hashCode() based on content.
|
||||||
|
*
|
||||||
|
* @param bytes The byte array to wrap.
|
||||||
|
*/
|
||||||
|
data class ByteArrayWrapper(val bytes: ByteArray) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
other as ByteArrayWrapper
|
||||||
|
return Arrays.equals(bytes, other.bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return Arrays.hashCode(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toHexString(): String {
|
||||||
|
return bytes.joinToString("") { "%02x".format(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user