mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 19:25:20 +00:00
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:
@@ -29,15 +29,15 @@ class NoiseEncryptionService(private val context: Context) {
|
||||
}
|
||||
|
||||
// Static identity key (persistent across app restarts) - loaded from secure storage
|
||||
private val staticIdentityPrivateKey: ByteArray
|
||||
private val staticIdentityPublicKey: ByteArray
|
||||
private var staticIdentityPrivateKey: ByteArray
|
||||
private var staticIdentityPublicKey: ByteArray
|
||||
|
||||
// Ed25519 signing key (persistent across app restarts) - loaded from secure storage
|
||||
private val signingPrivateKey: ByteArray
|
||||
private val signingPublicKey: ByteArray
|
||||
private var signingPrivateKey: ByteArray
|
||||
private var signingPublicKey: ByteArray
|
||||
|
||||
// Session management
|
||||
private val sessionManager: NoiseSessionManager
|
||||
private lateinit var sessionManager: NoiseSessionManager
|
||||
|
||||
// Channel encryption for password-protected channels
|
||||
private val channelEncryption = NoiseChannelEncryption()
|
||||
@@ -56,6 +56,32 @@ class NoiseEncryptionService(private val context: Context) {
|
||||
// Initialize identity state manager for persistent storage
|
||||
identityStateManager = SecureIdentityStateManager(context)
|
||||
|
||||
// Load or create keys - temporary placeholders
|
||||
staticIdentityPrivateKey = ByteArray(32)
|
||||
staticIdentityPublicKey = ByteArray(32)
|
||||
signingPrivateKey = ByteArray(32)
|
||||
signingPublicKey = ByteArray(32)
|
||||
|
||||
loadOrGenerateKeys()
|
||||
|
||||
// Initialize session manager
|
||||
initializeSessionManager()
|
||||
}
|
||||
|
||||
private fun initializeSessionManager() {
|
||||
// Create new session manager with current keys
|
||||
sessionManager = NoiseSessionManager(staticIdentityPrivateKey, staticIdentityPublicKey)
|
||||
|
||||
// Set up session callbacks
|
||||
sessionManager.onSessionEstablished = { peerID, remoteStaticKey ->
|
||||
handleSessionEstablished(peerID, remoteStaticKey)
|
||||
}
|
||||
|
||||
// Ensure any other callbacks are wired if needed
|
||||
// sessionManager.onSessionFailed could be wired if we exposed it
|
||||
}
|
||||
|
||||
private fun loadOrGenerateKeys() {
|
||||
// Load or create static identity key (persistent across sessions)
|
||||
val loadedKeyPair = identityStateManager.loadStaticKey()
|
||||
if (loadedKeyPair != null) {
|
||||
@@ -89,16 +115,8 @@ class NoiseEncryptionService(private val context: Context) {
|
||||
identityStateManager.saveSigningKey(signingPrivateKey, signingPublicKey)
|
||||
Log.d(TAG, "Generated and saved new Ed25519 signing key")
|
||||
}
|
||||
|
||||
// Initialize session manager
|
||||
sessionManager = NoiseSessionManager(staticIdentityPrivateKey, staticIdentityPublicKey)
|
||||
|
||||
// Set up session callbacks
|
||||
sessionManager.onSessionEstablished = { peerID, remoteStaticKey ->
|
||||
handleSessionEstablished(peerID, remoteStaticKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Public Interface
|
||||
|
||||
/**
|
||||
@@ -135,7 +153,23 @@ class NoiseEncryptionService(private val context: Context) {
|
||||
* Clear persistent identity (for panic mode)
|
||||
*/
|
||||
fun clearPersistentIdentity() {
|
||||
Log.w(TAG, "🚨 Panic Mode: Clearing persistent identity and rotating in-memory keys")
|
||||
|
||||
// 1. Clear storage
|
||||
identityStateManager.clearIdentityData()
|
||||
|
||||
// 2. Clear all sessions immediately
|
||||
if (::sessionManager.isInitialized) {
|
||||
sessionManager.shutdown()
|
||||
}
|
||||
|
||||
// 3. Regenerate keys immediately (in-memory rotation)
|
||||
loadOrGenerateKeys()
|
||||
|
||||
// 4. Re-initialize SessionManager with new keys
|
||||
initializeSessionManager()
|
||||
|
||||
Log.d(TAG, "✅ Identity cleared and keys rotated")
|
||||
}
|
||||
|
||||
// MARK: - Handshake Management
|
||||
@@ -478,7 +512,9 @@ class NoiseEncryptionService(private val context: Context) {
|
||||
* Clean shutdown
|
||||
*/
|
||||
fun shutdown() {
|
||||
sessionManager.shutdown()
|
||||
if (::sessionManager.isInitialized) {
|
||||
sessionManager.shutdown()
|
||||
}
|
||||
channelEncryption.clear()
|
||||
// No need to clear fingerprints here - they are managed centrally
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user