sqlcipher / sqlcipher-android

SQLCipher for Android provides an interface to SQLCipher databases on the Android platform.
Other
92 stars 18 forks source link

android.database.sqlite.SQLiteException - file is not a database (code 26) #32

Open UKMIITB opened 4 months ago

UKMIITB commented 4 months ago
Fatal Exception: android.database.sqlite.SQLiteException: file is not a database (code 26): , while compiling: SELECT COUNT(*) FROM sqlite_schema;
#################################################################
Error Code : 26 (SQLITE_NOTADB)
Caused By : File opened that is not a database file or encrypted.
    (file is not a database (code 26): , while compiling: SELECT COUNT(*) FROM sqlite_schema;)
#################################################################
       at net.zetetic.database.sqlcipher.SQLiteConnection.nativePrepareStatement(SQLiteConnection.java)
       at net.zetetic.database.sqlcipher.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:973)
       at net.zetetic.database.sqlcipher.SQLiteConnection.executeForLong(SQLiteConnection.java:628)
       at net.zetetic.database.sqlcipher.SQLiteConnection.open(SQLiteConnection.java:240)
       at net.zetetic.database.sqlcipher.SQLiteConnection.open(SQLiteConnection.java:202)
       at net.zetetic.database.sqlcipher.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:474)
       at net.zetetic.database.sqlcipher.SQLiteConnectionPool.open(SQLiteConnectionPool.java:189)
       at net.zetetic.database.sqlcipher.SQLiteConnectionPool.open(SQLiteConnectionPool.java:181)
       at net.zetetic.database.sqlcipher.SQLiteDatabase.openInner(SQLiteDatabase.java:1028)
       at net.zetetic.database.sqlcipher.SQLiteDatabase.open(SQLiteDatabase.java:1013)
       at net.zetetic.database.sqlcipher.SQLiteDatabase.openDatabase(SQLiteDatabase.java:840)
       at net.zetetic.database.sqlcipher.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:359)
       at net.zetetic.database.sqlcipher.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:278)
       at net.zetetic.database.sqlcipher.SupportHelper.getWritableDatabase(SupportHelper.java:60)
       at androidx.room.RoomDatabase.inTransaction(RoomDatabase.kt:638)
       at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.kt:457)
       at androidx.room.RoomDatabase.query(RoomDatabase.kt:486)
       at androidx.room.util.DBUtil.query(DBUtil.kt:75)

I am getting this crash in production. Its not happening for all users & not reproducible. But in production this is happening.

This is the code for initialisation.

    @Singleton
    @Provides
    fun providesAppEncryptedDatabase(
        @ApplicationContext context: Context
    ): AppEncryptedDatabase {
        var passphrase =
            PreferenceManager.getSecureStringApp(KEY_DB_ENCRYPTION)
                ?.toByteArray(Charsets.ISO_8859_1)
        if (passphrase == null) {
            passphrase =CommonUtils.generatePassphrase()
            PreferenceManager.saveStringSecurelyApp(
                key = KEY_DB_ENCRYPTION,
                value = passphrase.toString(Charsets.ISO_8859_1)
            )
        }
        try {
            SplitInstallHelper.loadLibrary(context, "sqlcipher")
        } catch (e: Exception) {
            e.log()
            try {
                System.loadLibrary("sqlcipher")
            } catch (e: Exception) {
                e.log()
            }
        }

        val sqlCipherOpenerFactory = SupportOpenHelperFactory(passphrase, null, true)
        return Room.databaseBuilder(
                context,
                AppEncryptedDatabase::class.java,
                ENCRYPTED_DATABASE_NAME
            )
            .openHelperFactory(sqlCipherOpenerFactory)
            .build()
    }

So here once passphrase is generated same passphrase is getting used. And we donot clear that passphrase from shared preference, so the case of trying to open from another passphrase would not happen.

sjlombardo commented 4 months ago

Hello @UKMIITB In this case something is causing the key mismatch. There is not enough information to determine what, but that error is thrown when the incorrect key is used. You should check all the various edge cases, e.g. the implementations for your secured preferences, and any ways that a database could be used without a corresponding preference (e.g. restore from backup, transfer onto a different device, generation of passphrase, etc).

GrenderG commented 1 month ago

I was able to reproduce this issue with a 100% "success" rate, @sjlombardo. I'm testing this on a Pixel 7A with Android API 34 and version 4.5.7 of the library. I'm using Room with a hardcoded password, so the issue isn't related to password mismatches.

Essentially, in my case, the application always crashes if I try to insert an item and I have never performed any SELECT statement before.

developernotes commented 1 month ago

Hi @GrenderG,

Can you post a public repo with a small test reproduction of the behavior you are seeing? We would be happy to investigate this further with a test case. Thanks!

GrenderG commented 1 month ago

Hello, I can try to do that this week. However, it should be very simple to test; basically you just need to open the app for the first time, then try to insert data without having performed any SELECT query before.

For the record, this is how I get the instance:

public static synchronized DummyDatabase getInstance(Context context) {
    System.loadLibrary("sqlcipher");
    return Room.databaseBuilder(context.getApplicationContext(),
                    DummyDatabase.class, Constants.DATABASE_NAME)
            .fallbackToDestructiveMigration()
            .setAutoCloseTimeout(60, TimeUnit.SECONDS)
            .openHelperFactory(new SupportOpenHelperFactory(
                    Constants.DB_DEFAULT_PASSWORD.getBytes(StandardCharsets.UTF_8)))
            .build();
}

To note:

sjlombardo commented 1 month ago

@GrenderG - in the scenario you are describing, does does the database already exist, or is the application creating a new database?

GrenderG commented 1 month ago

@GrenderG - in the scenario you are describing, does does the database already exist, or is the application creating a new database?

The application is creating a new database.

developernotes commented 1 month ago

Hi @GrenderG,

Can you take a look at this small demo application and compare with what you are seeing locally?

GrenderG commented 3 weeks ago

Hello, I tried it and the crash wasn't happening (my app is made with Java, not Kotlin, probably not relevant though). However, I found a way to avoid the crash in my scenario: if I use ExecutorService instead of a normal AsyncTask, it will work fine.

developernotes commented 3 weeks ago

Hi @GrenderG,

Thanks for the update, glad to hear you were able to resolve the issue.