Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions app/src/main/java/com/bitchat/android/crypto/EncryptionService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,36 @@ import java.util.concurrent.ConcurrentHashMap
* This is the main interface for all encryption/decryption operations in bitchat.
* It now uses the Noise protocol for secure transport encryption with proper session management.
*/
class EncryptionService(private val context: Context) {
open class EncryptionService(private val context: Context) {

companion object {
private const val TAG = "EncryptionService"
private const val ED25519_PRIVATE_KEY_PREF = "ed25519_signing_private_key"
}

// Core Noise encryption service
private val noiseService: NoiseEncryptionService = NoiseEncryptionService(context)
private val noiseService: NoiseEncryptionService by lazy { NoiseEncryptionService(context) }

// Session tracking for established connections
private val establishedSessions = ConcurrentHashMap<String, String>() // peerID -> fingerprint

// Ed25519 signing keys (separate from Noise static keys)
private val ed25519PrivateKey: Ed25519PrivateKeyParameters
private val ed25519PublicKey: Ed25519PublicKeyParameters
private lateinit var ed25519PrivateKey: Ed25519PrivateKeyParameters
private lateinit var ed25519PublicKey: Ed25519PublicKeyParameters

// Callbacks for UI state updates
var onSessionEstablished: ((String) -> Unit)? = null // peerID
var onSessionLost: ((String) -> Unit)? = null // peerID
var onHandshakeRequired: ((String) -> Unit)? = null // peerID

init {
initialize()
}

/**
* Initialization logic moved to method to allow overriding in tests
*/
protected open fun initialize() {
// Initialize or load Ed25519 signing keys
val keyPair = loadOrCreateEd25519KeyPair()
ed25519PrivateKey = keyPair.private as Ed25519PrivateKeyParameters
Expand Down Expand Up @@ -356,7 +363,7 @@ class EncryptionService(private val context: Context) {
/**
* Verify Ed25519 signature against data using a public key
*/
fun verifyEd25519Signature(signature: ByteArray, data: ByteArray, publicKeyBytes: ByteArray): Boolean {
open fun verifyEd25519Signature(signature: ByteArray, data: ByteArray, publicKeyBytes: ByteArray): Boolean {
return try {
val publicKey = Ed25519PublicKeyParameters(publicKeyBytes, 0)
val verifier = Ed25519Signer()
Expand Down
62 changes: 42 additions & 20 deletions app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,11 @@ class SecurityManager(private val encryptionService: EncryptionService, private
// They are signed/idempotent and we need to ensure first-announce per-connection can bind.
}

// NEW: Signature verification logging (not rejecting yet)
verifyPacketSignatureWithLogging(packet, peerID)
// Enforce mandatory signature verification
if (!verifyPacketSignature(packet, peerID)) {
Log.w(TAG, "Dropping packet from $peerID due to signature verification failure")
return false
}

Log.d(TAG, "Packet validation passed for $peerID, messageID: $messageID")
return true
Expand Down Expand Up @@ -223,48 +226,67 @@ class SecurityManager(private val encryptionService: EncryptionService, private
}

/**
* Verify packet signature using peer's signing public key and log the result
* Verify packet signature using peer's signing public key
* Returns true only if signature is present and valid
*/
private fun verifyPacketSignatureWithLogging(packet: BitchatPacket, peerID: String) {
private fun verifyPacketSignature(packet: BitchatPacket, peerID: String): Boolean {
try {
// Check if packet has a signature
// 1. Mandatory Signature Check
if (packet.signature == null) {
Log.d(TAG, "📝 Signature check for $peerID: NO_SIGNATURE (packet type ${packet.type})")
return
Log.w(TAG, " Signature check for $peerID: NO_SIGNATURE (packet type ${packet.type})")
return false
}

// Try to get peer's signing public key from peer info
val peerInfo = delegate?.getPeerInfo(peerID)
val signingPublicKey = peerInfo?.signingPublicKey
// 2. Get Signing Public Key
var signingPublicKey: ByteArray? = null

if (MessageType.fromValue(packet.type) == MessageType.ANNOUNCE) {
// Special Case: ANNOUNCE packets carry their own signing key
try {
val announcement = com.bitchat.android.model.IdentityAnnouncement.decode(packet.payload)
signingPublicKey = announcement?.signingPublicKey
} catch (e: Exception) {
Log.w(TAG, "Failed to decode announcement for key extraction: ${e.message}")
}
} else {
// Standard Case: Get key from known peer info
val peerInfo = delegate?.getPeerInfo(peerID)
signingPublicKey = peerInfo?.signingPublicKey
}

if (signingPublicKey == null) {
Log.d(TAG, "📝 Signature check for $peerID: NO_SIGNING_KEY (packet type ${packet.type})")
return
// If we don't have a key (and it's not an announce), we can't verify.
// For security, we must reject packets from unknown peers unless it's an announce.
Log.w(TAG, "❌ Signature check for $peerID: NO_SIGNING_KEY_AVAILABLE (packet type ${packet.type})")
return false
}

// Get the canonical packet data for signature verification (without signature)
// 3. Get Canonical Data
val packetDataForSigning = packet.toBinaryDataForSigning()
if (packetDataForSigning == null) {
Log.w(TAG, "📝 Signature check for $peerID: ENCODING_ERROR (packet type ${packet.type})")
return
Log.w(TAG, " Signature check for $peerID: ENCODING_ERROR (packet type ${packet.type})")
return false
}

// Verify the signature using the peer's signing public key
val signature = packet.signature!! // We already checked for null above
// 4. Verify Signature
val signature = packet.signature!!
val isSignatureValid = encryptionService.verifyEd25519Signature(
signature,
packetDataForSigning,
signingPublicKey
)

if (isSignatureValid) {
Log.d(TAG, "📝 Signature check for $peerID: ✅ VALID (packet type ${packet.type})")
// Log.v(TAG, "✅ Signature verified for $peerID (type ${packet.type})")
return true
} else {
Log.w(TAG, "📝 Signature check for $peerID: ❌ INVALID (packet type ${packet.type})")
Log.w(TAG, "❌ Signature INVALID for $peerID (type ${packet.type})")
return false
}

} catch (e: Exception) {
Log.w(TAG, "📝 Signature check for $peerID: ERROR - ${e.message} (packet type ${packet.type})")
Log.e(TAG, "❌ Signature verification error for $peerID: ${e.message}")
return false
}
}

Expand Down
Loading
Loading