TP-53465 | Migration of encryption library from android-database-sqlcipher to sqlcipher-android (#9246)

This commit is contained in:
Ujjwal Kumar
2024-01-22 19:27:38 +05:30
committed by GitHub
parent 079dc9f62b
commit dee6739c54
6 changed files with 18 additions and 172 deletions

View File

@@ -269,7 +269,7 @@
}
# SQL Cipher
-keep,includedescriptorclasses class net.sqlcipher.** { *; }
-keep,includedescriptorclasses interface net.sqlcipher.** { *; }
-keep,includedescriptorclasses class net.zetetic.database.** { *; }
-keep,includedescriptorclasses interface net.zetetic.database.** { *; }
-keep class androidx.lifecycle.SavedStateHandle { *; }

View File

@@ -35,6 +35,7 @@ androidx-pagingRuntimeKtx = "3.1.1"
androidx-preferenceKtx = "1.2.0"
androidx-securityCryptoKtx = "1.1.0-alpha03"
androidx-test-espresso = "3.5.1"
androidx-sqlite = "2.2.0"
androidx-test-junit = "1.1.5"
androidx-test-monitor = "1.6.0"
androidx-test-rules = "1.4.0"
@@ -109,7 +110,7 @@ spotless = "6.24.0"
truecaller = "3.0.0"
visit = "1.24"
wasabeef-recyclerviewAnimators = "4.0.1"
zetetic-androidDatabaseSqlcipher = "4.5.4"
zetetic-sqlcipherAndroid = "4.5.5"
zxing = "3.3.3"
[libraries]
@@ -198,6 +199,7 @@ androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "
androidx-room-testing = { module = "androidx.room:room-testing", version.ref = "room" }
androidx-security-crypto-ktx = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-securityCryptoKtx" }
androidx-sqlite = { module = "androidx.sqlite:sqlite", version.ref = "androidx-sqlite" }
androidx-test-espresso-contrib = { module = "androidx.test.espresso:espresso-contrib", version.ref = "androidx-test-espresso" }
androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-test-espresso" }
@@ -351,7 +353,7 @@ visit = { module = "com.github.VisitApp:VisitSDK", version.ref = "visit" }
wasabeef-recyclerview-animators = { module = "jp.wasabeef:recyclerview-animators", version.ref = "wasabeef-recyclerviewAnimators" }
zetetic-android-database-sqlcipher = { module = "net.zetetic:android-database-sqlcipher", version.ref = "zetetic-androidDatabaseSqlcipher" }
zetetic-sqlcipher-android = { module = "net.zetetic:sqlcipher-android", version.ref = "zetetic-sqlcipherAndroid" }
zxing = { module = "com.google.zxing:core", version.ref = "zxing" }

View File

@@ -87,6 +87,7 @@ dependencies {
implementation libs.androidx.lifecycle.viewmodel.ktx
implementation libs.androidx.paging.compose
implementation libs.androidx.room.paging
implementation libs.androidx.sqlite
implementation libs.android.material
implementation libs.android.play.core.ktx
@@ -99,7 +100,7 @@ dependencies {
implementation libs.raamcosta.composeDestinations.animation.core
implementation libs.zetetic.android.database.sqlcipher
implementation libs.zetetic.sqlcipher.android
implementation libs.zxing

View File

@@ -1,153 +0,0 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.pay.db
import android.content.Context
import android.database.sqlite.SQLiteException
import androidx.sqlite.db.SupportSQLiteDatabase
import androidx.sqlite.db.SupportSQLiteOpenHelper
import com.google.android.play.core.splitinstall.SplitInstallHelper
import java.io.File
import net.sqlcipher.database.SQLiteDatabase
import net.sqlcipher.database.SQLiteDatabaseHook
import net.sqlcipher.database.SQLiteOpenHelper
/**
* A factory to create [SupportSQLiteOpenHelper] instances that uses SQLCipher to encrypt the
* database. It is created to load the SQLCipher native libraries before opening the database using
* SplitInstallHelper Keep this updated with new SupportSQLiteOpenHelper in-case of Cipher Lin
* Update
*
* @param passphrase The passphrase to use to encrypt the database.
* @param hook The hook to use to configure the database.
* @param clearPassphrase Whether to clear the passphrase after opening the database.
*/
class SqlCipherOpenerFactory
@JvmOverloads
constructor(
private val passphrase: ByteArray,
private val hook: SQLiteDatabaseHook? = null,
private val clearPassphrase: Boolean = true
) : SupportSQLiteOpenHelper.Factory {
override fun create(
configuration: SupportSQLiteOpenHelper.Configuration
): SupportSQLiteOpenHelper {
return SqlCipherOpenerHelper(configuration, passphrase, hook, clearPassphrase)
}
}
class SqlCipherOpenerHelper
internal constructor(
configuration: SupportSQLiteOpenHelper.Configuration,
passphrase: ByteArray?,
hook: SQLiteDatabaseHook?,
clearPassphrase: Boolean
) : SupportSQLiteOpenHelper {
private val standardHelper: SQLiteOpenHelper
private val passphrase: ByteArray?
private val clearPassphrase: Boolean
init {
loadLibs(configuration.context, configuration.context.filesDir)
this.passphrase = passphrase
this.clearPassphrase = clearPassphrase
standardHelper =
object :
SQLiteOpenHelper(
configuration.context,
configuration.name,
null as SQLiteDatabase.CursorFactory?,
configuration.callback.version,
hook
) {
override fun onCreate(db: SQLiteDatabase) {
configuration.callback.onCreate(db)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
configuration.callback.onUpgrade(db, oldVersion, newVersion)
}
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
configuration.callback.onDowngrade(db, oldVersion, newVersion)
}
override fun onOpen(db: SQLiteDatabase) {
configuration.callback.onOpen(db)
}
override fun onConfigure(db: SQLiteDatabase) {
configuration.callback.onConfigure(db)
}
}
}
override val databaseName: String
get() = standardHelper.databaseName
override val readableDatabase: SupportSQLiteDatabase
get() = writableDatabase
override fun setWriteAheadLoggingEnabled(enabled: Boolean) {
standardHelper.setWriteAheadLoggingEnabled(enabled)
}
override val writableDatabase: SupportSQLiteDatabase
get() {
val result: SQLiteDatabase
try {
result = standardHelper.getWritableDatabase(passphrase)
} catch (var8: SQLiteException) {
if (passphrase != null) {
var isCleared = true
val var4 = passphrase
val var5 = var4.size
var var6 = 0
while (var6 < var5) {
val b = var4[var6]
isCleared = isCleared && b.toInt() == 0
++var6
}
if (isCleared) {
throw IllegalStateException(
"The passphrase appears to be cleared. This happens by default the first time you use the factory to open a database, so we can remove the cleartext passphrase from memory. If you close the database yourself, please use a fresh SupportFactory to reopen it. If something else (e.g., Room) closed the database, and you cannot control that, use SupportFactory boolean constructor option to opt out of the automatic password clearing step. See the project README for more information.",
var8
)
}
}
throw var8
}
if (clearPassphrase && passphrase != null) {
for (i in passphrase.indices) {
passphrase[i] = 0
}
}
return result
}
override fun close() {
standardHelper.close()
}
}
/**
* Loads the SQLCipher native libraries using SplitInstallHelper
*
* @param context The context to use to load the libraries.
* @param workingDir The working directory to use to load the libraries.
*/
@Synchronized
fun loadLibs(context: Context, workingDir: File?) {
SQLiteDatabase.loadLibs(context, workingDir) { libNames ->
val var3 = libNames.size
for (var4 in 0 until var3) {
val libName = libNames[var4]
SplitInstallHelper.loadLibrary(context, libName)
}
}
}

View File

@@ -9,9 +9,9 @@ package com.navi.pay.network.di
import android.content.Context
import androidx.room.Room
import com.google.android.play.core.splitinstall.SplitInstallHelper
import com.google.gson.Gson
import com.navi.base.sharedpref.PreferenceManager
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
import com.navi.common.model.ModuleName
import com.navi.common.model.NetworkInfo
import com.navi.common.utils.log
@@ -35,7 +35,6 @@ import com.navi.pay.db.NAVI_PAY_APP_ENCRYPTED_DATABASE_MIGRATION_2_3
import com.navi.pay.db.NAVI_PAY_APP_ENCRYPTED_DATABASE_MIGRATION_3_4
import com.navi.pay.db.NaviPayAppDatabase
import com.navi.pay.db.NaviPayAppEncryptedDatabase
import com.navi.pay.db.SqlCipherOpenerFactory
import com.navi.pay.management.paytocontacts.PhoneContactManager
import com.navi.pay.management.paytocontacts.PhoneContactManagerImpl
import com.navi.pay.network.NaviPayHttpClient
@@ -48,7 +47,6 @@ import com.navi.pay.onboarding.home.VpaQRCodeManager
import com.navi.pay.onboarding.home.VpaQRCodeManagerImpl
import com.navi.pay.permission.utils.PermissionStateProvider
import com.navi.pay.permission.utils.PermissionStateProviderImpl
import com.navi.pay.utils.ENABLE_DEFAULT_SUPPORT_FACTORY
import com.navi.pay.utils.KEY_DB_ENCRYPTION
import com.navi.pay.utils.NAVIPAY_NETWORK_INFO_TIMEOUT
import com.navi.pay.utils.NAVI_PAY_DATABASE_NAME
@@ -61,7 +59,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Qualifier
import javax.inject.Singleton
import net.sqlcipher.database.SupportFactory
import net.zetetic.database.sqlcipher.SupportOpenHelperFactory
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
@@ -138,17 +136,18 @@ object NaviPayNetworkModule {
value = passphrase.toString(Charsets.ISO_8859_1)
)
}
val sqlCipherOpenerFactory =
try {
SplitInstallHelper.loadLibrary(context, "sqlcipher")
} catch (e: Exception) {
e.log()
try {
if (FirebaseRemoteConfigHelper.getBoolean(ENABLE_DEFAULT_SUPPORT_FACTORY)) {
SupportFactory(passphrase)
} else {
SqlCipherOpenerFactory(passphrase)
}
System.loadLibrary("sqlcipher")
} catch (e: Exception) {
e.log()
SqlCipherOpenerFactory(passphrase)
}
}
val sqlCipherOpenerFactory = SupportOpenHelperFactory(passphrase, null, true)
return Room.databaseBuilder(
context,
NaviPayAppEncryptedDatabase::class.java,

View File

@@ -264,9 +264,6 @@ const val VPA_QR_CODE_IMAGES = "vpa_qr_code_images"
const val QR_CODE_IMAGE_EXTENSION = ".jpeg"
const val NAVI_PAY_DEBUG_LOG = "NAVI_PAY_DEBUG_LOG"
// Firebase Config Keys
const val ENABLE_DEFAULT_SUPPORT_FACTORY = "ENABLE_DEFAULT_SUPPORT_FACTORY"
// Image Quality Constant
const val IMAGE_QUALITY = 20