Upgrading from android-database-sqlcipher to sqlcipher-android

@developernotes how to get current db cipher_page_size, kdf_iter, cipher_hmac_algorithm, cipher_kdf_algorithm to verify that is actual (to get/verify non-default SQLCipher 3 settings) and check that database.rawExecSQL("PRAGMA cipher_compatibility = 3;"); applied successfully?

Hi @FrozenPyrozen

You can use PRAGMA cipher_settings; [1] once you have keyed the database to report the current library configuration:

$ ./sqlcipher ~/Desktop/foo.db
SQLite version 3.46.1 2024-08-13 09:16:08 (SQLCipher 4.6.1 community)
Enter ".help" for usage hints.
sqlite> pragma key = 'foo';
ok
sqlite> pragma cipher_settings;
PRAGMA kdf_iter = 256000;
PRAGMA cipher_page_size = 4096;
PRAGMA cipher_use_hmac = 1;
PRAGMA cipher_plaintext_header_size = 0;
PRAGMA cipher_hmac_algorithm = HMAC_SHA512;
PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA512;
sqlite> pragma cipher_compatibility = 3;
sqlite> pragma cipher_settings;
PRAGMA kdf_iter = 64000;
PRAGMA cipher_page_size = 1024;
PRAGMA cipher_use_hmac = 1;
PRAGMA cipher_plaintext_header_size = 0;
PRAGMA cipher_hmac_algorithm = HMAC_SHA1;
PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;

  1. SQLCipher API - Full Database Encryption PRAGMAs, Functions, and Settings | Zetetic ↩︎

It’s possible to connect to existing db from Android java code and check all of these?

@developernotes also I’ve noticed that in my case error file is encrypted or is not a database: , while compiling: select count(*) from sqlite_master; E Database: net.sqlcipher.database.SQLiteException: file is encrypted or is not a database: , while compiling: select count(*) from sqlite_master; coming after calling getWritableDatabase. It’s possible to place migration hook before calling getWritableDatabase or make sure that kdf_iter and other cipher... configs are right from Android java code?

Hi @FrozenPyrozen,

You can provide a SQLiteDatabaseHook instance to a SQLiteOpenHelper derivative.

Hi, @developernotes I’m not sure how to run ./sqlcipher ~/Desktop/foo.db in my case im working with Android device from Java code, maybe you know how to check these cipher config from Java code?

Enter ".help" for usage hints. sqlite> pragma key = 'foo'; ok sqlite> pragma cipher_settings; PRAGMA kdf_iter = 256000; PRAGMA cipher_page_size = 4096; PRAGMA cipher_use_hmac = 1; PRAGMA cipher_plaintext_header_size = 0; PRAGMA cipher_hmac_algorithm = HMAC_SHA512; PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA512; sqlite> pragma cipher_compatibility = 3; sqlite> pragma cipher_settings; PRAGMA kdf_iter = 64000; PRAGMA cipher_page_size = 1024; PRAGMA cipher_use_hmac = 1; PRAGMA cipher_plaintext_header_size = 0; PRAGMA cipher_hmac_algorithm = HMAC_SHA1; PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;

Hi @FrozenPyrozen,

I was just showing your the PRAGMA commands that you can issue. If you are operating on the Android platform you would generally need to exercise the SQLCipher for Android API to invoke those commands.

@developernotes are there are some methods to get all cipher settings from current db? For example I could call stmh similar to db.getCipherEncryptionSettings() from pre lib update 3.5.7 sql cipher build and then get the db.getCipherEncryptionSettings() from build with cipher 4.5.4?

@developernotes in my case I’ve got a class DatabaseHelper which extends from SQLiteOpenHelper so I need to put SQLiteDatabaseHook into constructor or where?
image

@FrozenPyrozen,

No, you will need to execute the PRAGMA commands and iterate over the results. This is an example using the older android-database-sqlcipher API, this is an example using the sqlcipher-android library. You will note the API is quite similar for this scenario.

1 Like

Hi @FrozenPyrozen,

The older android-database-sqlcipher exposes several constructors to the SQLiteOpenHelper which accept a SQLiteDatabaseHook. Additionally, you will find several constructor options with the SQLiteOpenHelper in the newer sqlcipher-android library that does the same.

1 Like

Hi, @developernotes it really helps me out I’ve added, but do you have examples how to get other pragma cipher settings like kdf_iter, cipher_plaintext_header_size, cipher_hmac_algorithm, cipher_kdf_algorithm, kdf_iter, cipher_page_size, cipher_use_hmac, cipher_plaintext_header_size, cipher_hmac_algorithm, cipher_kdf_algorithm like this for older android-database-sqlcipher API?

Hi @FrozenPyrozen,

Sorry, the PRAGMA cipher_settings command wasn’t added until SQLCipher core 4.1.0 release [1]. That said, if you are using the newer library, you can execute that command against a database that was created with SQLCipher 3.x.


  1. sqlcipher/CHANGELOG.md at master · sqlcipher/sqlcipher · GitHub ↩︎

Hello @FrozenPyrozen,

Just to clarify, the PRAGMA cipher_settings command will only return the runtime configured options from the library. It will not report what a given database file was configured with as that is not stored within the database file itself.

@developernotes Is it possible to get kdf_iter, cipher_plaintext_header_size, cipher_hmac_algorithm, cipher_kdf_algorithm, kdf_iter, cipher_page_size, cipher_use_hmac, cipher_plaintext_header_size, cipher_hmac_algorithm, cipher_kdf_algorithm that were used to create/configure db initially?

Hi, @developernotes

Curently I’ve added checkCipherSettings to check before database.execSQL("PRAGMA cipher_compatibility = 3;"); configs and compare with after it

Code:

private void checkCipherSettings(SQLiteDatabase database) {
                String[] pragmas = {
                        "PRAGMA cipher_compatibility;",
                        "PRAGMA kdf_iter;",
                        "PRAGMA cipher_plaintext_header_size;",
                        "PRAGMA cipher_hmac_algorithm;",
                        "PRAGMA cipher_kdf_algorithm;",
                        "PRAGMA cipher_page_size;",
                        "PRAGMA cipher_use_hmac;",
                        "PRAGMA cipher_settings;"
                };

                try {
                    for (String pragma : pragmas) {
                        Cursor cursor = database.rawQuery(pragma, null);
                        if (cursor != null && cursor.moveToFirst()) {
                            String result = cursor.getString(0);
                            Log.i("CipherSettings", "[KLOG]: " + pragma + " " + result);
                            cursor.close();
                        } else {
                            Log.i("CipherSettings", "[KLOG] Failed to retrieve value for " + pragma);
                        }
                    }
                } catch (Exception e) {
                    Log.e("CipherSettings", "[KLOG] Error while fetching cipher settings: " + e.getMessage(), e);
                }
            }

            @Override
            public void postKey(SQLiteDatabase database) {
                Log.i("DatabaseHelper", "[KLOG] postKey triggered for db: " + database);
                Log.i("DatabaseHelper", "[KLOG] _________ before this.checkCipherSettings for existing db __________: ");
                this.checkCipherSettings(database);
                Log.i("DatabaseHelper", "[KLOG] _________ after this.checkCipherSettings for existing db __________: ");
                Log.i("DatabaseHelper", "[KLOG] postKey before database cipher_compatibility to 3");
                // Apply PRAGMA to maintain SQLCipher 3 compatibility for net.zetetic:android-database-sqlcipher after 4x version
                database.execSQL("PRAGMA cipher_compatibility = 3;");
              

                Log.i("DatabaseHelper", "[KLOG] postKey after database cipher_compatibility to 3");
                Log.i("DatabaseHelper", "[KLOG] _________ before this.checkCipherSettings for cipher_compatibility = 3 db __________: ");
                this.checkCipherSettings(database);
                Log.i("DatabaseHelper", "[KLOG] _________ after this.checkCipherSettings for cipher_compatibility = 3 db __________: ");
}

And I’ve got some differences:

After this I’ve changed postKey to set exact same kdf_iter, cipher_hmac_algorithm, cipher_kdf_algorithm, cipher_page_size settings as it was before database.execSQL("PRAGMA cipher_compatibility = 3;");

public void postKey(SQLiteDatabase database) {
                Log.i("DatabaseHelper", "[KLOG] postKey triggered for db: " + database);
                Log.i("DatabaseHelper", "[KLOG] _________ before this.checkCipherSettings for existing db __________: ");
                this.checkCipherSettings(database);
                Log.i("DatabaseHelper", "[KLOG] _________ after this.checkCipherSettings for existing db __________: ");
                Log.i("DatabaseHelper", "[KLOG] postKey before database cipher_compatibility to 3");
                // Apply PRAGMA to maintain SQLCipher 3 compatibility for net.zetetic:android-database-sqlcipher after 4x version
                database.execSQL("PRAGMA cipher_compatibility = 3;");
                database.execSQL("PRAGMA kdf_iter = 256000;");
                database.execSQL("PRAGMA cipher_hmac_algorithm = HMAC_SHA512;");
                database.execSQL("PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA512;");
                database.execSQL("PRAGMA cipher_page_size = 4096;");

                Log.i("DatabaseHelper", "[KLOG] postKey after database cipher_compatibility to 3");
                Log.i("DatabaseHelper", "[KLOG] _________ before this.checkCipherSettings for cipher_compatibility = 3 db __________: ");
                this.checkCipherSettings(database);
                Log.i("DatabaseHelper", "[KLOG] _________ after this.checkCipherSettings for cipher_compatibility = 3 db __________: ");
}

Now they are equal, but I still have an error android.database.sqlite.SQLiteException: file is not a database: , while compiling: select count(*) from sqlite_master; after this:

Maybe I could check existing db setup with sql cipher old library v3? Or maybe I missed some pragma that needs for opening db file

Hi @FrozenPyrozen,

No, that is not stored in the database file. Unless the database was created with non-default configuration options with regard to those settings, you could cycle through the values supported by PRAGMA cipher_compatibility [1] and attempt to open the database to identify the SQLCipher version needed for that specific database. The cipher_compatibility pragma will set the appropriate values on your behalf. Please review the documentation for usage.


  1. SQLCipher API - Full Database Encryption PRAGMAs, Functions, and Settings | Zetetic ↩︎

@developernotes could I get somehow these values and other creation config from place where I created a db with old v3 library?

Hi @FrozenPyrozen,

Can you share what you’re trying to accomplish? This thread is getting rather long and I don’t think we understand exactly what your situation is in detail. Could you provide those details? Thanks!

Hi, @developernotes I’m trying to update net.zetetic:android-database-sqlcipher:3.5.7 to net.zetetic:android-database-sqlcipher:4.5.4. So basically I’ve got prev app build with net.zetetic:android-database-sqlcipher:3.5.7 and I’ve created new app build with net.zetetic:android-database-sqlcipher:4.5.4. I’ve decided to go with backwards compatibility step with database.execSQL("PRAGMA cipher_compatibility = 3;");. When I’m installing new app on prev with already created db I’ve got an error on this.getWritableDatabase of based on SQLiteOpenHelper class`:

file is encrypted or is not a database: , while compiling: select count(*) from sqlite_master; 11-14 15:15:27.619 18529 18529 E Database: net.sqlcipher.database.SQLiteException: file is encrypted or is not a database: , while compiling: select count(*) from sqlite_master;

So I’m passing compatibility hook to based on SQLiteOpenHelper class constructor super(context, NetworkConstant.DATABASE_NAME, null, NetworkConstant.DATABASE_VERSION, DatabaseHelper.migrateDbFrom3xFormatToCurrentSupportHook()); but it didn’t help and I trying to understand why

Currently I’m trying to get all possible db info to understand what is the db settings for prev app and what’s changed in new app with net.zetetic:android-database-sqlcipher:4.5.4

Here I’ve tried to compare cipher settings for prev and new build, but maybe I missed to check smth else, because after setting the same pragmas when I’ve got before launching database.execSQL("PRAGMA cipher_compatibility = 3;"); didn’t work