diff --git a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt index 432dbc807..2a09f74d8 100644 --- a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt +++ b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt @@ -1,6 +1,5 @@ /* - * - * Nextcloud Talk application + * Nextcloud Talk application * * @author Marcel Hibbe * @author Andy Scherzinger @@ -9,18 +8,18 @@ * Copyright (C) 2022 Andy Scherzinger * Copyright (C) 2017 Mario Danic * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ package com.nextcloud.talk.application @@ -51,6 +50,7 @@ import com.facebook.cache.disk.DiskCacheConfig import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.imagepipeline.core.ImagePipelineConfig import com.nextcloud.talk.BuildConfig +import com.nextcloud.talk.R import com.nextcloud.talk.components.filebrowser.webdav.DavUtils import com.nextcloud.talk.dagger.modules.BusModule import com.nextcloud.talk.dagger.modules.ContextModule @@ -61,6 +61,7 @@ import com.nextcloud.talk.dagger.modules.ViewModelModule import com.nextcloud.talk.jobs.AccountRemovalWorker import com.nextcloud.talk.jobs.CapabilitiesWorker import com.nextcloud.talk.jobs.SignalingSettingsWorker +import com.nextcloud.talk.models.database.Models import com.nextcloud.talk.utils.ClosedInterfaceImpl import com.nextcloud.talk.utils.DeviceUtils import com.nextcloud.talk.utils.DisplayUtils @@ -74,12 +75,17 @@ import com.vanniktech.emoji.EmojiManager import com.vanniktech.emoji.google.GoogleEmojiProvider import de.cotech.hw.SecurityKeyManager import de.cotech.hw.SecurityKeyManagerConfig +import io.requery.android.sqlcipher.SqlCipherDatabaseSource +import net.sqlcipher.database.SQLiteDatabase +import net.sqlcipher.database.SQLiteDatabaseHook +import net.sqlcipher.database.SQLiteOpenHelper import okhttp3.OkHttpClient import org.conscrypt.Conscrypt import org.webrtc.PeerConnectionFactory import org.webrtc.voiceengine.WebRtcAudioManager import org.webrtc.voiceengine.WebRtcAudioUtils import java.security.Security +import java.util.Locale import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton @@ -113,6 +119,17 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver { lateinit var okHttpClient: OkHttpClient //endregion + val hook: SQLiteDatabaseHook = object : SQLiteDatabaseHook { + override fun preKey(database: SQLiteDatabase) { + // unused atm + } + override fun postKey(database: SQLiteDatabase) { + Log.i("TalkApplication", "DB cipher_migrate START") + database.rawExecSQL("PRAGMA cipher_migrate;") + Log.i("TalkApplication", "DB cipher_migrate END") + } + } + //region private methods private fun initializeWebRtc() { try { @@ -153,6 +170,8 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver { componentApplication.inject(this) + checkAndUpgradeDbCypher() + Coil.setImageLoader(buildDefaultImageLoader()) setAppTheme(appPreferences.theme) super.onCreate() @@ -241,10 +260,48 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver { .build() } + private fun checkAndUpgradeDbCypher() { + if (appPreferences.isDbCypherToUpgrade) { + val database = object : SqlCipherDatabaseSource( + this, + Models.DEFAULT, + this.resources.getString(R.string.nc_app_product_name).lowercase(Locale.getDefault()) + .replace(" ", "_").trim { it <= ' ' } + ".sqlite", + this.getString(R.string.nc_talk_database_encryption_key), + DatabaseModule.DB_VERSION + ) { + + override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + checkAndUpdateCipherMigrationStatus(newVersion, appPreferences) + super.onUpgrade(db, oldVersion, newVersion) + } + } + + try { + val field = SQLiteOpenHelper::class.java.getDeclaredField("mHook") + field.isAccessible = true + field.set(database, hook) + } catch (e: NoSuchFieldException) { + Log.e("SqlCipherDatabaseSource", "Error accessing mHook field") + } catch (e: IllegalAccessException) { + Log.e("SqlCipherDatabaseSource", "Error setting mHook field") + } + + checkAndUpdateCipherMigrationStatus(database.writableDatabase.version, appPreferences) + } + } + + private fun checkAndUpdateCipherMigrationStatus(version: Int, appPreferences: AppPreferences) { + if (version >= CIPHER_V4_MIGRATION && appPreferences.isDbCypherToUpgrade) { + appPreferences.isDbCypherToUpgrade = false + } + } + companion object { private val TAG = NextcloudTalkApplication::class.java.simpleName const val FIFTY_PERCENT = 0.5 const val HALF_DAY: Long = 12 + const val CIPHER_V4_MIGRATION: Int = 7 //region Singleton //endregion diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/DatabaseModule.java b/app/src/main/java/com/nextcloud/talk/dagger/modules/DatabaseModule.java index c30b36a34..148c574cf 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/DatabaseModule.java +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/DatabaseModule.java @@ -39,6 +39,7 @@ import javax.inject.Singleton; @Module public class DatabaseModule { + public static final int DB_VERSION = 7; @Provides @Singleton @@ -46,7 +47,7 @@ public class DatabaseModule { return new SqlCipherDatabaseSource(context, Models.DEFAULT, context.getResources().getString(R.string.nc_app_product_name).toLowerCase() .replace(" ", "_").trim() + ".sqlite", - context.getString(R.string.nc_talk_database_encryption_key), 6); + context.getString(R.string.nc_talk_database_encryption_key), DB_VERSION); } @Provides diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java index fd141a4d1..b86d8a583 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java @@ -281,6 +281,13 @@ public interface AppPreferences { @KeyByResource(R.string.nc_settings_theme_key) @UnregisterChangeListenerMethod void unregisterThemeChangeListener(OnPreferenceValueChangedListener listener); + + @KeyByResource(R.string.nc_settings_db_cypher_v4_upgrade_key) + @DefaultValue(R.bool.value_true) + boolean getIsDbCypherToUpgrade(); + + @KeyByResource(R.string.nc_settings_db_cypher_v4_upgrade_key) + void setIsDbCypherToUpgrade(boolean value); @KeyByResource(R.string.nc_settings_phone_book_integration_key) @RegisterChangeListenerMethod diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fc3942e1a..fc15041b5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -533,4 +533,6 @@ Send without notification Call without notification + db_cypher_v4_upgrade +