android, desktop: adapted code for self destruct for ios logic (#3643)

* android, desktop: adapted code for self destruct for ios logic

* init db in case of periodic && self destruct enabled
This commit is contained in:
Stanislav Dmitrenko 2024-01-09 16:59:21 +07:00 committed by GitHub
parent 61c507e7da
commit a853ba3a15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 39 additions and 23 deletions

View File

@ -3,11 +3,12 @@ package chat.simplex.app
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import androidx.work.* import androidx.work.*
import chat.simplex.app.*
import chat.simplex.app.SimplexService.Companion.showPassphraseNotification import chat.simplex.app.SimplexService.Companion.showPassphraseNotification
import chat.simplex.common.model.ChatController import chat.simplex.common.model.ChatController
import chat.simplex.common.views.helpers.DBMigrationResult import chat.simplex.common.views.helpers.DBMigrationResult
import chat.simplex.app.BuildConfig import chat.simplex.common.platform.chatModel
import chat.simplex.common.platform.initChatControllerAndRunMigrations
import chat.simplex.common.views.helpers.DatabaseUtils
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.util.Date import java.util.Date
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -57,6 +58,10 @@ class MessagesFetcherWork(
val durationSeconds = inputData.getInt(INPUT_DATA_DURATION, 60) val durationSeconds = inputData.getInt(INPUT_DATA_DURATION, 60)
var shouldReschedule = true var shouldReschedule = true
try { try {
// In case of self-destruct is enabled the initialization process will not start in SimplexApp, Let's start it here
if (DatabaseUtils.ksSelfDestructPassword.get() != null && chatModel.chatDbStatus.value == null) {
initChatControllerAndRunMigrations()
}
withTimeout(durationSeconds * 1000L) { withTimeout(durationSeconds * 1000L) {
val chatController = ChatController val chatController = ChatController
SimplexService.waitDbMigrationEnds(chatController) SimplexService.waitDbMigrationEnds(chatController)

View File

@ -26,6 +26,7 @@ import kotlinx.coroutines.sync.withLock
import java.io.* import java.io.*
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.system.exitProcess
const val TAG = "SIMPLEX" const val TAG = "SIMPLEX"
@ -46,8 +47,8 @@ class SimplexApp: Application(), LifecycleEventObserver {
try { try {
Looper.loop() Looper.loop()
} catch (e: Throwable) { } catch (e: Throwable) {
if (e.message != null && e.message!!.startsWith("Unable to start activity")) { if (e is UnsatisfiedLinkError || e.message?.startsWith("Unable to start activity") == true) {
android.os.Process.killProcess(android.os.Process.myPid()) Process.killProcess(Process.myPid())
break break
} else { } else {
// Send it to our exception handled because it will not get the exception otherwise // Send it to our exception handled because it will not get the exception otherwise
@ -63,7 +64,9 @@ class SimplexApp: Application(), LifecycleEventObserver {
tmpDir.deleteRecursively() tmpDir.deleteRecursively()
tmpDir.mkdir() tmpDir.mkdir()
initChatControllerAndRunMigrations(false) if (DatabaseUtils.ksSelfDestructPassword.get() == null) {
initChatControllerAndRunMigrations()
}
ProcessLifecycleOwner.get().lifecycle.addObserver(this@SimplexApp) ProcessLifecycleOwner.get().lifecycle.addObserver(this@SimplexApp)
} }

View File

@ -72,6 +72,10 @@ class SimplexService: Service() {
stopSelf() stopSelf()
} else { } else {
isServiceStarted = true isServiceStarted = true
// In case of self-destruct is enabled the initialization process will not start in SimplexApp, Let's start it here
if (DatabaseUtils.ksSelfDestructPassword.get() != null && chatModel.chatDbStatus.value == null) {
initChatControllerAndRunMigrations()
}
} }
} }

View File

@ -41,21 +41,20 @@ val appPreferences: AppPreferences
val chatController: ChatController = ChatController val chatController: ChatController = ChatController
fun initChatControllerAndRunMigrations(ignoreSelfDestruct: Boolean) { fun initChatControllerAndRunMigrations() {
if (ignoreSelfDestruct || DatabaseUtils.ksSelfDestructPassword.get() == null) { withBGApi {
withBGApi { if (appPreferences.chatStopped.get() && appPreferences.storeDBPassphrase.get() && ksDatabasePassword.get() != null) {
if (appPreferences.chatStopped.get() && appPreferences.storeDBPassphrase.get() && ksDatabasePassword.get() != null) { initChatController(startChat = ::showStartChatAfterRestartAlert)
initChatController(startChat = ::showStartChatAfterRestartAlert) } else {
} else { initChatController()
initChatController()
}
runMigrations()
} }
runMigrations()
} }
} }
suspend fun initChatController(useKey: String? = null, confirmMigrations: MigrationConfirmation? = null, startChat: () -> CompletableDeferred<Boolean> = { CompletableDeferred(true) }) { suspend fun initChatController(useKey: String? = null, confirmMigrations: MigrationConfirmation? = null, startChat: () -> CompletableDeferred<Boolean> = { CompletableDeferred(true) }) {
try { try {
if (chatModel.ctrlInitInProgress.value) return
chatModel.ctrlInitInProgress.value = true chatModel.ctrlInitInProgress.value = true
val dbKey = useKey ?: DatabaseUtils.useDatabaseKey() val dbKey = useKey ?: DatabaseUtils.useDatabaseKey()
val confirm = confirmMigrations ?: if (appPreferences.confirmDBUpgrades.get()) MigrationConfirmation.Error else MigrationConfirmation.YesUp val confirm = confirmMigrations ?: if (appPreferences.confirmDBUpgrades.get()) MigrationConfirmation.Error else MigrationConfirmation.YesUp

View File

@ -460,10 +460,10 @@ suspend fun deleteChatAsync(m: ChatModel) {
m.controller.apiDeleteStorage() m.controller.apiDeleteStorage()
DatabaseUtils.ksDatabasePassword.remove() DatabaseUtils.ksDatabasePassword.remove()
m.controller.appPrefs.storeDBPassphrase.set(true) m.controller.appPrefs.storeDBPassphrase.set(true)
deleteChatDatabaseFiles() deleteAppDatabaseAndFiles()
} }
fun deleteChatDatabaseFiles() { fun deleteAppDatabaseAndFiles() {
val chat = File(dataDir, chatDatabaseFileName) val chat = File(dataDir, chatDatabaseFileName)
val chatBak = File(dataDir, "$chatDatabaseFileName.bak") val chatBak = File(dataDir, "$chatDatabaseFileName.bak")
val agent = File(dataDir, agentDatabaseFileName) val agent = File(dataDir, agentDatabaseFileName)
@ -473,6 +473,7 @@ fun deleteChatDatabaseFiles() {
agent.delete() agent.delete()
agentBak.delete() agentBak.delete()
filesDir.deleteRecursively() filesDir.deleteRecursively()
filesDir.mkdir()
remoteHostsDir.deleteRecursively() remoteHostsDir.deleteRecursively()
tmpDir.deleteRecursively() tmpDir.deleteRecursively()
tmpDir.mkdir() tmpDir.mkdir()

View File

@ -17,7 +17,7 @@ object DatabaseUtils {
val ksAppPassword = KeyStoreItem(APP_PASSWORD_ALIAS, appPreferences.encryptedAppPassphrase, appPreferences.initializationVectorAppPassphrase) val ksAppPassword = KeyStoreItem(APP_PASSWORD_ALIAS, appPreferences.encryptedAppPassphrase, appPreferences.initializationVectorAppPassphrase)
val ksSelfDestructPassword = KeyStoreItem(SELF_DESTRUCT_PASSWORD_ALIAS, appPreferences.encryptedSelfDestructPassphrase, appPreferences.initializationVectorSelfDestructPassphrase) val ksSelfDestructPassword = KeyStoreItem(SELF_DESTRUCT_PASSWORD_ALIAS, appPreferences.encryptedSelfDestructPassphrase, appPreferences.initializationVectorSelfDestructPassphrase)
class KeyStoreItem(val alias: String, val passphrase: SharedPreference<String?>, val initVector: SharedPreference<String?>) { class KeyStoreItem(private val alias: String, val passphrase: SharedPreference<String?>, val initVector: SharedPreference<String?>) {
fun get(): String? { fun get(): String? {
return cryptor.decryptData( return cryptor.decryptData(
passphrase.get()?.toByteArrayFromBase64ForPassphrase() ?: return null, passphrase.get()?.toByteArrayFromBase64ForPassphrase() ?: return null,

View File

@ -34,7 +34,7 @@ fun LocalAuthView(m: ChatModel, authRequest: LocalAuthRequest) {
} else { } else {
val r: LAResult = if (passcode.value == authRequest.password) { val r: LAResult = if (passcode.value == authRequest.password) {
if (authRequest.selfDestruct && sdPassword != null && controller.ctrl == -1L) { if (authRequest.selfDestruct && sdPassword != null && controller.ctrl == -1L) {
initChatControllerAndRunMigrations(true) initChatControllerAndRunMigrations()
} }
LAResult.Success LAResult.Success
} else { } else {
@ -67,8 +67,8 @@ private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: (
* */ * */
chatCloseStore(ctrl) chatCloseStore(ctrl)
} }
deleteChatDatabaseFiles() deleteAppDatabaseAndFiles()
// Clear sensitive data on screen just in case ModalManager will fail to prevent hiding its modals while database encrypts itself // Clear sensitive data on screen just in case ModalManager fails to hide its modals while new database is created
m.chatId.value = null m.chatId.value = null
m.chatItems.clear() m.chatItems.clear()
m.chats.clear() m.chats.clear()

View File

@ -12,6 +12,7 @@ import chat.simplex.res.MR
@Composable @Composable
fun SetAppPasscodeView( fun SetAppPasscodeView(
passcodeKeychain: DatabaseUtils.KeyStoreItem = ksAppPassword, passcodeKeychain: DatabaseUtils.KeyStoreItem = ksAppPassword,
prohibitedPasscodeKeychain: DatabaseUtils.KeyStoreItem = ksSelfDestructPassword,
title: String = generalGetString(MR.strings.new_passcode), title: String = generalGetString(MR.strings.new_passcode),
reason: String? = null, reason: String? = null,
submit: () -> Unit, submit: () -> Unit,
@ -51,7 +52,7 @@ fun SetAppPasscodeView(
} else { } else {
SetPasswordView(title, generalGetString(MR.strings.save_verb), SetPasswordView(title, generalGetString(MR.strings.save_verb),
// Do not allow to set app passcode == selfDestruct passcode // Do not allow to set app passcode == selfDestruct passcode
submitEnabled = { pwd -> pwd != (if (passcodeKeychain.alias == ksSelfDestructPassword.alias) ksAppPassword else ksSelfDestructPassword).get() }) { submitEnabled = { pwd -> pwd != prohibitedPasscodeKeychain.get() }) {
enteredPassword = passcode.value enteredPassword = passcode.value
passcode.value = "" passcode.value = ""
confirming = true confirming = true

View File

@ -454,6 +454,7 @@ fun SimplexLockView(
Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background, contentColor = LocalContentColor.current) { Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background, contentColor = LocalContentColor.current) {
SetAppPasscodeView( SetAppPasscodeView(
passcodeKeychain = ksSelfDestructPassword, passcodeKeychain = ksSelfDestructPassword,
prohibitedPasscodeKeychain = ksAppPassword,
reason = generalGetString(MR.strings.self_destruct), reason = generalGetString(MR.strings.self_destruct),
submit = { submit = {
selfDestructPasscodeAlert(generalGetString(MR.strings.self_destruct_passcode_changed)) selfDestructPasscodeAlert(generalGetString(MR.strings.self_destruct_passcode_changed))
@ -600,7 +601,7 @@ private fun EnableSelfDestruct(
) { ) {
Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background, contentColor = LocalContentColor.current) { Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background, contentColor = LocalContentColor.current) {
SetAppPasscodeView( SetAppPasscodeView(
passcodeKeychain = ksSelfDestructPassword, title = generalGetString(MR.strings.set_passcode), reason = generalGetString(MR.strings.enabled_self_destruct_passcode), passcodeKeychain = ksSelfDestructPassword, prohibitedPasscodeKeychain = ksAppPassword, title = generalGetString(MR.strings.set_passcode), reason = generalGetString(MR.strings.enabled_self_destruct_passcode),
submit = { submit = {
selfDestruct.set(true) selfDestruct.set(true)
selfDestructPasscodeAlert(generalGetString(MR.strings.self_destruct_passcode_enabled)) selfDestructPasscodeAlert(generalGetString(MR.strings.self_destruct_passcode_enabled))

View File

@ -24,7 +24,9 @@ fun initApp() {
override fun cancelAllNotifications() = chat.simplex.common.model.NtfManager.cancelAllNotifications() override fun cancelAllNotifications() = chat.simplex.common.model.NtfManager.cancelAllNotifications()
} }
applyAppLocale() applyAppLocale()
initChatControllerAndRunMigrations(false) if (DatabaseUtils.ksSelfDestructPassword.get() == null) {
initChatControllerAndRunMigrations()
}
// LALAL // LALAL
//testCrypto() //testCrypto()
} }