fix(security): Clear in-memory keys during panic mode (#596)

* fix(security): Clear in-memory keys during panic mode #588

* feat: Recreate mesh service after panic clear (#602)

* feat: Recreate mesh service after panic clear

This commit refactors the panic clear process to ensure a new mesh identity is immediately created and applied.

Previously, the `ChatViewModel` would clear sensitive data, but the recreation of the `BluetoothMeshService` was handled externally. This could lead to a delay or failure in adopting the new identity.

Key changes:
- Introduces `MeshServiceHolder` to manage the lifecycle of the `BluetoothMeshService` instance.
- Adds `recreateMeshServiceAfterPanic()` to `ChatViewModel`, which now explicitly clears the old service instance and creates a new one with a regenerated identity.
- The `meshService` property in `ChatViewModel` is now a `var` to allow it to be replaced with the fresh instance post-panic.
- The new service is started, and a broadcast announcement is sent immediately, ensuring the new peer ID is used on the network.

* fix: Ensure mesh service is properly managed in foreground service

* refactor: Decouple handlers from direct service reference

This commit updates the `VerificationHandler` and `MediaSendingManager` to receive the `meshService` via a lambda function (`getMeshService`) instead of a direct reference.

This change decouples the handlers from the service instance, preventing them from holding a stale reference if the service reconnects or changes. By invoking the lambda to get the current service instance when needed, it ensures they always interact with the active `meshService`.

* fix: restart bluetooth

---------

Co-authored-by: Moe Hamade <69801237+moehamade@users.noreply.github.com>
This commit is contained in:
callebtc
2026-01-15 15:11:10 +07:00
committed by GitHub
co-authored by Moe Hamade
parent 9dfebd73db
commit ae67fa4344
9 changed files with 183 additions and 31 deletions
@@ -0,0 +1,55 @@
package com.bitchat.android.crypto
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.bitchat.android.noise.NoiseEncryptionService
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import java.util.Arrays
@RunWith(RobolectricTestRunner::class)
class EncryptionServiceTest {
private lateinit var context: Context
private lateinit var encryptionService: EncryptionService
@Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
encryptionService = EncryptionService(context)
}
@Test
fun `test clearPersistentIdentity changes keys`() {
// 1. Get initial keys
val initialStaticKey = encryptionService.getStaticPublicKey()
val initialSigningKey = encryptionService.getSigningPublicKey()
val initialFingerprint = encryptionService.getIdentityFingerprint()
assertNotNull("Initial static key should not be null", initialStaticKey)
assertNotNull("Initial signing key should not be null", initialSigningKey)
// 2. Call clearPersistentIdentity (Panic Mode)
encryptionService.clearPersistentIdentity()
// 3. Get keys again.
val afterStaticKey = encryptionService.getStaticPublicKey()
val afterSigningKey = encryptionService.getSigningPublicKey()
val afterFingerprint = encryptionService.getIdentityFingerprint()
// 4. Verify keys are different (Panic Mode should clear/rotate in-memory keys)
// Note: We use string comparison for byte arrays to be safe in assertion messages
assertNotEquals("Static key should change after panic",
Arrays.toString(initialStaticKey), Arrays.toString(afterStaticKey))
assertNotEquals("Signing key should change after panic",
Arrays.toString(initialSigningKey), Arrays.toString(afterSigningKey))
assertNotEquals("Fingerprint should change after panic",
initialFingerprint, afterFingerprint)
}
}