diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..ff6a36a --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,21 @@ +# Configuration for probot-stale - https://github.com/probot/stale +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 14 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 14 +# Issues with these labels will never be considered stale +exemptLabels: + - bug + - enhancement + - security +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + Hello, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. + You may also label this issue as "bug", "enhancement", or "security" and I will leave it open. + Thank you for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: > + Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to reopen with up-to-date information. +only: issues diff --git a/.gitignore b/.gitignore index ce89848..61a7947 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ build local.properties .idea/** *.iml +*.aar +*.jar diff --git a/README.md b/README.md index 9748b1b..b59efa1 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,46 @@ -To run, clone this repo and make sure you have the Android Studio installed: -- SQLCipher for Android defines [compatibility support](https://github.com/sqlcipher/android-database-sqlcipher#compatibility), simply run on either an emulator or device. -- More information can be found at [SQLCipher for Android](https://zetetic.net/sqlcipher/sqlcipher-for-android/). +### Deprecated + +This project exists in support of the `android-database-sqlcipher` project which has been [officially deprecated](https://www.zetetic.net/blog/2023/08/31/sqlcipher-4.5.5-release#sqlcipher-android-455). The long-term replacement is [`sqlcipher-android`](https://github.com/sqlcipher/sqlcipher-android). Instructions for migrating from `android-database-sqlcipher` to `sqlcipher-android`may be found [here](https://www.zetetic.net/sqlcipher/sqlcipher-for-android-migration/). + + +To run: clone this repo and open with a recent version of Android Studio, or [build from the command line](https://developer.android.com/studio/build/building-cmdline). + +It is possible to run on an emulator or device, as documented in the [SQLCipher for Android compatibility section](https://github.com/sqlcipher/android-database-sqlcipher#compatibility). + +More information can be found in [SQLCipher for Android](https://zetetic.net/sqlcipher/sqlcipher-for-android/). ### Creating A New Test 1. Open this repository within Android Studio 2. Add a new class within `net.zetetic.tests` package that extends `SQLCipherTest`: -``` +```Java package net.zetetic.tests; import net.sqlcipher.database.SQLiteDatabase; -public class DemoTest extends SQLCipherTest { +public class AwesomeTest extends SQLCipherTest { @Override public boolean execute(SQLiteDatabase database) { try { // Add your scenario here return true; - }catch (Exception e){ + } catch (Exception e) { return false; } } @Override public String getName() { - return "Demo Test"; + return "Awesome Test"; } } ``` -3. Add `DemoTest` to the [`TestSuiteRunner`](https://github.com/sqlcipher/sqlcipher-android-tests/blob/master/src/main/java/net/zetetic/tests/TestSuiteRunner.java): +3. Add `AwesomeTest` to the [`TestSuiteRunner`](https://github.com/sqlcipher/sqlcipher-android-tests/blob/master/src/main/java/net/zetetic/tests/TestSuiteRunner.java): +```Java +tests.add(new AwesomeTest()); ``` -tests.add(new DemoTest()); -``` -4. Build and run the application on an Android device/emulator +4. Build and run the application on an Android device or emulator diff --git a/app/build.gradle b/app/build.gradle index 39abbba..76f05b3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 29 + compileSdkVersion 33 defaultConfig { applicationId "net.zetetic.sqlcipher.test" - minSdkVersion 26 - targetSdkVersion 29 + minSdkVersion 21 + targetSdkVersion 33 versionCode 1 versionName "1.0" } @@ -16,22 +16,26 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + namespace 'net.zetetic' } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - - // For testing zip-based distributions: - //implementation files('libs/sqlcipher.jar') + // For testing JAR-based distribution: + // implementation files('libs/sqlcipher.jar') // For testing local AAR packages: - // implementation (name: 'android-database-sqlcipher-4.0.1', ext: 'aar') - implementation files('libs/android-database-sqlcipher-4.2.0.aar') - implementation "androidx.sqlite:sqlite:2.0.1" - implementation "androidx.room:room-runtime:2.1.0" - annotationProcessor "androidx.room:room-compiler:2.1.0" - + //implementation (name: 'android-database-sqlcipher-4.5.4-release', ext: 'aar') // For testing on remote AAR references: - //implementation 'net.zetetic:android-database-sqlcipher:4.0.1@aar' + implementation 'net.zetetic:android-database-sqlcipher:4.5.4@aar' + + implementation "androidx.sqlite:sqlite:2.2.0" + + // For Room tests: + def room_version = "2.5.0" + implementation "androidx.room:room-runtime:$room_version" + annotationProcessor "androidx.room:room-compiler:$room_version" + + // For version comparison + implementation "org.apache.maven:maven-artifact:3.8.7" } diff --git a/app/libs/android-database-sqlcipher-4.2.0.aar b/app/libs/android-database-sqlcipher-4.2.0.aar deleted file mode 100644 index 35e38d6..0000000 Binary files a/app/libs/android-database-sqlcipher-4.2.0.aar and /dev/null differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1db8711..51b202b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,12 +1,13 @@ + android:versionCode="1" android:versionName="1.0-SNAPSHOT"> diff --git a/app/src/main/java/net/zetetic/NativeInitializer.java b/app/src/main/java/net/zetetic/NativeInitializer.java new file mode 100644 index 0000000..27a3345 --- /dev/null +++ b/app/src/main/java/net/zetetic/NativeInitializer.java @@ -0,0 +1,16 @@ +package net.zetetic; +import android.util.Log; + +import net.sqlcipher.database.SQLiteDatabase; + +public class NativeInitializer { + + static { + Log.i("NativeInitializer", "Before loadLibs"); + SQLiteDatabase.loadLibs(ZeteticApplication.getInstance()); + Log.i("NativeInitializer", "After loadLibs"); + } + + public static void initialize(){} + +} diff --git a/app/src/main/java/net/zetetic/QueryHelper.java b/app/src/main/java/net/zetetic/QueryHelper.java index 29cbdb5..01b4a5b 100644 --- a/app/src/main/java/net/zetetic/QueryHelper.java +++ b/app/src/main/java/net/zetetic/QueryHelper.java @@ -3,9 +3,24 @@ import android.database.Cursor; import androidx.sqlite.db.SupportSQLiteDatabase; +import java.util.ArrayList; +import java.util.List; + public class QueryHelper { + public static List getListFromQuery(SupportSQLiteDatabase database, String query){ + Cursor cursor = database.query(query, new String[]{}); + List results = new ArrayList<>(); + if(cursor != null){ + while(cursor.moveToNext()){ + results.add(cursor.getString(0)); + } + cursor.close(); + } + return results; + } + public static String singleValueFromQuery(SupportSQLiteDatabase database, String query){ Cursor cursor = database.query(query, new String[]{}); String value = ""; diff --git a/app/src/main/java/net/zetetic/ZeteticApplication.java b/app/src/main/java/net/zetetic/ZeteticApplication.java index f2bd015..d4ec90a 100644 --- a/app/src/main/java/net/zetetic/ZeteticApplication.java +++ b/app/src/main/java/net/zetetic/ZeteticApplication.java @@ -7,6 +7,8 @@ import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabaseHook; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; + import java.io.*; public class ZeteticApplication extends Application { @@ -19,6 +21,7 @@ public class ZeteticApplication extends Application { public static final String ONE_X_DATABASE = "1x.db"; public static final String ONE_X_USER_VERSION_DATABASE = "1x-user-version.db"; public static final String UNENCRYPTED_DATABASE = "unencrypted.db"; + public static final String licenseCode = ""; public ZeteticApplication() { instance = this; @@ -28,6 +31,12 @@ public static ZeteticApplication getInstance() { return instance; } + @Override + public void onCreate() { + super.onCreate(); + NativeInitializer.initialize(); + } + public void setCurrentActivity(Activity activity) { this.activity = activity; } @@ -51,6 +60,20 @@ public void prepareDatabaseEnvironment() { // databaseFile.delete(); } + public boolean includesLicenseCode(){ + return licenseCode != null && licenseCode.length() > 0; + } + + public boolean supportsMinLibraryVersionRequired(String requiredVersionString) { + try { + DefaultArtifactVersion requiredVersion = new DefaultArtifactVersion(requiredVersionString); + DefaultArtifactVersion actualVersion = new DefaultArtifactVersion(SQLiteDatabase.SQLCIPHER_ANDROID_VERSION); + return actualVersion.compareTo(requiredVersion) >= 0; + } catch (Exception ex){ + return false; + } + } + public SQLiteDatabase createDatabase(File databaseFile) { return createDatabase(databaseFile, null); } @@ -92,7 +115,7 @@ public void deleteDatabaseFileAndSiblings(String databaseName) { } } - private SQLiteDatabaseHook wrapHook(final SQLiteDatabaseHook hook) { + public SQLiteDatabaseHook wrapHook(final SQLiteDatabaseHook hook) { if (hook == null) { return keyHook; @@ -115,7 +138,7 @@ public void postKey(SQLiteDatabase database) { SQLiteDatabaseHook keyHook = new SQLiteDatabaseHook() { @Override public void preKey(SQLiteDatabase database) { - database.rawExecSQL("PRAGMA cipher_license = '';"); + database.rawExecSQL(String.format("PRAGMA cipher_license = '%s';", licenseCode)); } public void postKey(SQLiteDatabase database) { } diff --git a/app/src/main/java/net/zetetic/ZeteticContentProvider.java b/app/src/main/java/net/zetetic/ZeteticContentProvider.java index b96241a..9ec5c02 100644 --- a/app/src/main/java/net/zetetic/ZeteticContentProvider.java +++ b/app/src/main/java/net/zetetic/ZeteticContentProvider.java @@ -28,11 +28,8 @@ public boolean onCreate() { @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { - - SQLiteDatabase.loadLibs(ZeteticApplication.getInstance()); File databasePath = ZeteticApplication.getInstance().getDatabasePath(ZeteticApplication.DATABASE_NAME); database = ZeteticApplication.getInstance().createDatabase(databasePath, null); - createDatabaseWithData(database); SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); builder.setTables("t1"); diff --git a/app/src/main/java/net/zetetic/ZeteticContentProvider2.java b/app/src/main/java/net/zetetic/ZeteticContentProvider2.java index 48bd2a8..42826f6 100644 --- a/app/src/main/java/net/zetetic/ZeteticContentProvider2.java +++ b/app/src/main/java/net/zetetic/ZeteticContentProvider2.java @@ -29,7 +29,6 @@ public boolean onCreate() { @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { - SQLiteDatabase.loadLibs(ZeteticApplication.getInstance()); File databasePath = ZeteticApplication.getInstance().getDatabasePath(DATABASE_NAME); database = ZeteticApplication.getInstance().createDatabase(databasePath); diff --git a/app/src/main/java/net/zetetic/activities/TestScrollingCursorActivity.java b/app/src/main/java/net/zetetic/activities/TestScrollingCursorActivity.java index 6867627..03c4a13 100644 --- a/app/src/main/java/net/zetetic/activities/TestScrollingCursorActivity.java +++ b/app/src/main/java/net/zetetic/activities/TestScrollingCursorActivity.java @@ -93,7 +93,6 @@ public void run() { void initializeEnvironment(){ try{ - SQLiteDatabase.loadLibs(this); File databasePath = getDatabasePath(databaseFilename); ZeteticApplication.getInstance().extractAssetToDatabaseDirectory(databaseFilename); database = SQLiteDatabase.openDatabase(databasePath.getAbsolutePath(), diff --git a/app/src/main/java/net/zetetic/activities/TestSuiteBehaviorsActivity.java b/app/src/main/java/net/zetetic/activities/TestSuiteBehaviorsActivity.java index 336f2d2..4a6711d 100644 --- a/app/src/main/java/net/zetetic/activities/TestSuiteBehaviorsActivity.java +++ b/app/src/main/java/net/zetetic/activities/TestSuiteBehaviorsActivity.java @@ -1,9 +1,12 @@ package net.zetetic.activities; import android.app.Activity; +import android.media.AudioManager; +import android.media.ToneGenerator; import android.os.Bundle; import android.util.Log; import android.view.View; +import android.view.WindowManager; import android.widget.ArrayAdapter; import android.widget.HeaderViewListAdapter; import android.widget.ListView; @@ -21,6 +24,7 @@ import java.io.FileOutputStream; import java.util.ArrayList; import java.util.List; +import java.util.Locale; public class TestSuiteBehaviorsActivity extends Activity implements ResultNotifier { static final String EXTRA_IS_SUPPORT = "isSupport"; @@ -49,9 +53,9 @@ public void onButtonClick(View view) { results.clear(); hideStats(); findViewById(R.id.executeSuite).setEnabled(false); - resultsView = (ListView) findViewById(R.id.test_suite_results); + resultsView = findViewById(R.id.test_suite_results); ZeteticApplication.getInstance().setCurrentActivity(this); - + this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); if (getIntent().getBooleanExtra(EXTRA_IS_SUPPORT, false)) { new SupportSuiteRunner(this).execute(this); } @@ -80,7 +84,7 @@ public void send(TestResult result) { @Override public void complete() { - TextView stats = (TextView) statsView.findViewById(R.id.stats); + TextView stats = statsView.findViewById(R.id.stats); int successCount = 0; List failedTests = new ArrayList(); for(TestResult result : results){ @@ -90,15 +94,14 @@ public void complete() { failedTests.add(result.getName()); } } - String message = String.format("Passed: %d Failed: %d", successCount, results.size() - successCount); + String message = String.format(Locale.getDefault(), + "Passed: %d Failed: %d", successCount, results.size() - successCount); deleteTestResultsLog(); try { FileOutputStream resultStream = new FileOutputStream(testResults); resultStream.write(String.format("%s\n", message).getBytes()); - if(failedTests != null){ - for(String test : failedTests){ - resultStream.write(test.getBytes()); - } + for(String test : failedTests){ + resultStream.write(test.getBytes()); } resultStream.flush(); resultStream.close(); @@ -109,6 +112,9 @@ public void complete() { stats.setText(message); stats.setVisibility(View.VISIBLE); findViewById(R.id.executeSuite).setEnabled(true); + ToneGenerator toneG = new ToneGenerator(AudioManager.STREAM_ALARM, 100); + toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200); + this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } private void deleteTestResultsLog(){ diff --git a/app/src/main/java/net/zetetic/tests/CanThrowSQLiteExceptionTest.java b/app/src/main/java/net/zetetic/tests/CanThrowSQLiteExceptionTest.java index c7c54c9..684554f 100644 --- a/app/src/main/java/net/zetetic/tests/CanThrowSQLiteExceptionTest.java +++ b/app/src/main/java/net/zetetic/tests/CanThrowSQLiteExceptionTest.java @@ -1,7 +1,7 @@ package net.zetetic.tests; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; public class CanThrowSQLiteExceptionTest extends SQLCipherTest { diff --git a/app/src/main/java/net/zetetic/tests/CipherMigrateTest.java b/app/src/main/java/net/zetetic/tests/CipherMigrateTest.java index 745e70c..3dd425a 100644 --- a/app/src/main/java/net/zetetic/tests/CipherMigrateTest.java +++ b/app/src/main/java/net/zetetic/tests/CipherMigrateTest.java @@ -22,11 +22,13 @@ public boolean execute(SQLiteDatabase database) { public void preKey(SQLiteDatabase database) {} public void postKey(SQLiteDatabase database) { String value = QueryHelper.singleValueFromQuery(database, "PRAGMA cipher_migrate"); + setMessage(String.format("cipher_migrate result:%s", value)); status[0] = Integer.valueOf(value) == 0; } }; - database = SQLiteDatabase.openOrCreateDatabase(olderFormatDatabase, - ZeteticApplication.DATABASE_PASSWORD, null, hook); + database = SQLiteDatabase.openDatabase(olderFormatDatabase.getPath(), + ZeteticApplication.DATABASE_PASSWORD, null, + SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY, hook); if(database != null){ database.close(); } diff --git a/app/src/main/java/net/zetetic/tests/ClosedDatabaseTest.java b/app/src/main/java/net/zetetic/tests/ClosedDatabaseTest.java index c3c262a..6d7e074 100644 --- a/app/src/main/java/net/zetetic/tests/ClosedDatabaseTest.java +++ b/app/src/main/java/net/zetetic/tests/ClosedDatabaseTest.java @@ -3,7 +3,7 @@ import android.util.Log; import net.sqlcipher.DatabaseErrorHandler; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; import java.io.File; diff --git a/app/src/main/java/net/zetetic/tests/CompileStatementSyntaxErrorMessageTest.java b/app/src/main/java/net/zetetic/tests/CompileStatementSyntaxErrorMessageTest.java index 25e46e4..15501d6 100644 --- a/app/src/main/java/net/zetetic/tests/CompileStatementSyntaxErrorMessageTest.java +++ b/app/src/main/java/net/zetetic/tests/CompileStatementSyntaxErrorMessageTest.java @@ -4,7 +4,7 @@ import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteStatement; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; diff --git a/app/src/main/java/net/zetetic/tests/CorruptDatabaseTest.java b/app/src/main/java/net/zetetic/tests/CorruptDatabaseTest.java index 48f219e..6369aa0 100644 --- a/app/src/main/java/net/zetetic/tests/CorruptDatabaseTest.java +++ b/app/src/main/java/net/zetetic/tests/CorruptDatabaseTest.java @@ -3,7 +3,7 @@ import android.database.Cursor; import android.util.Log; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteDatabaseCorruptException; +import android.database.sqlite.SQLiteDatabaseCorruptException; import net.zetetic.ZeteticApplication; import java.io.File; diff --git a/app/src/main/java/net/zetetic/tests/CreateNonEncryptedDatabaseTest.java b/app/src/main/java/net/zetetic/tests/CreateNonEncryptedDatabaseTest.java index e228855..ae32e86 100644 --- a/app/src/main/java/net/zetetic/tests/CreateNonEncryptedDatabaseTest.java +++ b/app/src/main/java/net/zetetic/tests/CreateNonEncryptedDatabaseTest.java @@ -3,7 +3,7 @@ import android.database.Cursor; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; diff --git a/app/src/main/java/net/zetetic/tests/CustomCorruptionHandlerTest.java b/app/src/main/java/net/zetetic/tests/CustomCorruptionHandlerTest.java index 7178bd8..cb0a9a9 100644 --- a/app/src/main/java/net/zetetic/tests/CustomCorruptionHandlerTest.java +++ b/app/src/main/java/net/zetetic/tests/CustomCorruptionHandlerTest.java @@ -3,8 +3,8 @@ import android.util.Log; import net.sqlcipher.DatabaseErrorHandler; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteDatabaseCorruptException; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteDatabaseCorruptException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; import java.io.File; diff --git a/app/src/main/java/net/zetetic/tests/DefaultCursorWindowAllocationTest.java b/app/src/main/java/net/zetetic/tests/DefaultCursorWindowAllocationTest.java new file mode 100644 index 0000000..ee59c62 --- /dev/null +++ b/app/src/main/java/net/zetetic/tests/DefaultCursorWindowAllocationTest.java @@ -0,0 +1,47 @@ +package net.zetetic.tests; + +import net.sqlcipher.Cursor; +import net.sqlcipher.database.SQLiteDatabase; + +public class DefaultCursorWindowAllocationTest extends SQLCipherTest { + @Override + public boolean execute(SQLiteDatabase database) { + try { + int rowCount = 0; + int rows = 10000; + //final int dataSize = 2048; + final int dataSize = 16384; + buildDatabase(database, rows, 1, new RowColumnValueBuilder() { + @Override + public Object buildRowColumnValue(String[] columns, int row, int column) { + return generateRandomByteArray(dataSize); + } + }); + rows = 1; + Cursor cursor = database.rawQuery("SELECT count(length(a)) FROM t1;", new Object[]{}); + if(cursor == null) return false; + while(cursor.moveToNext()){ + //byte[] data = cursor.getBlob(0); + int size = cursor.getInt(0); + cursor.close(); +// if(data.length != dataSize) { +// cursor.close(); +// return false; +// } + rowCount++; + } + cursor.close(); + return rowCount == rows; + } catch (Exception e){ + String message = String.format("Error:%s", e.getMessage()); + log(message); + setMessage(message); + return false; + } + } + + @Override + public String getName() { + return "Default cursor window allocation test"; + } +} diff --git a/app/src/main/java/net/zetetic/tests/DeleteTableWithNullWhereArgsTest.java b/app/src/main/java/net/zetetic/tests/DeleteTableWithNullWhereArgsTest.java new file mode 100644 index 0000000..6419fcb --- /dev/null +++ b/app/src/main/java/net/zetetic/tests/DeleteTableWithNullWhereArgsTest.java @@ -0,0 +1,20 @@ +package net.zetetic.tests; + +import net.sqlcipher.database.SQLiteDatabase; + +public class DeleteTableWithNullWhereArgsTest extends SQLCipherTest { + @Override + public boolean execute(SQLiteDatabase database) { + String[] args = null; + int rowsDeleted = 0; + database.rawExecSQL("create table t1(a,b);"); + database.execSQL("insert into t1(a,b) values(?, ?);", new Object[]{1, 2}); + rowsDeleted = database.delete("t1", "a = 1", args); + return rowsDeleted == 1; + } + + @Override + public String getName() { + return "Delete Table Test"; + } +} diff --git a/app/src/main/java/net/zetetic/tests/ExecuteInsertConstraintErrorMessageTest.java b/app/src/main/java/net/zetetic/tests/ExecuteInsertConstraintErrorMessageTest.java index eedd500..a14bcdc 100644 --- a/app/src/main/java/net/zetetic/tests/ExecuteInsertConstraintErrorMessageTest.java +++ b/app/src/main/java/net/zetetic/tests/ExecuteInsertConstraintErrorMessageTest.java @@ -4,7 +4,7 @@ import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteStatement; -import net.sqlcipher.database.SQLiteConstraintException; +import android.database.sqlite.SQLiteConstraintException; import net.zetetic.ZeteticApplication; @@ -23,7 +23,7 @@ public boolean execute(SQLiteDatabase database) { Log.v(ZeteticApplication.TAG, "EXPECTED RESULT: DID throw SQLiteConstraintException", e); String message = e.getMessage(); setMessage(message); - if (!message.matches("error code 19: UNIQUE constraint failed: tt\\.a")) { + if (!message.matches("error code 19 \\(extended error code 2067\\): UNIQUE constraint failed: tt\\.a")) { Log.e(ZeteticApplication.TAG, "NOT EXPECTED: INCORRECT exception message: " + message); return false; } diff --git a/app/src/main/java/net/zetetic/tests/InvalidOpenArgumentTest.java b/app/src/main/java/net/zetetic/tests/InvalidOpenArgumentTest.java index 3cc0c4f..5123a01 100644 --- a/app/src/main/java/net/zetetic/tests/InvalidOpenArgumentTest.java +++ b/app/src/main/java/net/zetetic/tests/InvalidOpenArgumentTest.java @@ -3,7 +3,7 @@ import android.util.Log; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; diff --git a/app/src/main/java/net/zetetic/tests/InvalidPasswordTest.java b/app/src/main/java/net/zetetic/tests/InvalidPasswordTest.java index 48bc3f5..3061901 100644 --- a/app/src/main/java/net/zetetic/tests/InvalidPasswordTest.java +++ b/app/src/main/java/net/zetetic/tests/InvalidPasswordTest.java @@ -5,7 +5,7 @@ import net.sqlcipher.Cursor; import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabaseHook; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; diff --git a/app/src/main/java/net/zetetic/tests/JavaClientLibraryVersionTest.java b/app/src/main/java/net/zetetic/tests/JavaClientLibraryVersionTest.java index 0556de0..79ac869 100644 --- a/app/src/main/java/net/zetetic/tests/JavaClientLibraryVersionTest.java +++ b/app/src/main/java/net/zetetic/tests/JavaClientLibraryVersionTest.java @@ -2,18 +2,19 @@ import net.sqlcipher.database.SQLiteDatabase; -public class JavaClientLibraryVersionTest extends SQLCipherTest { +public class +JavaClientLibraryVersionTest extends SQLCipherTest { - String expectedClientLibraryVersion = "4.2.0"; + private final String EXPECTED_SQLCIPHER_ANDROID_VERSION = "4.5.4"; @Override public boolean execute(SQLiteDatabase database) { setMessage(String.format("Report:%s", SQLiteDatabase.SQLCIPHER_ANDROID_VERSION)); - return SQLiteDatabase.SQLCIPHER_ANDROID_VERSION.equals(expectedClientLibraryVersion); + return SQLiteDatabase.SQLCIPHER_ANDROID_VERSION.equals(EXPECTED_SQLCIPHER_ANDROID_VERSION); } @Override public String getName() { return "Java Client Library Version Test"; } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/zetetic/tests/JsonCastTest.java b/app/src/main/java/net/zetetic/tests/JsonCastTest.java new file mode 100644 index 0000000..e7ae516 --- /dev/null +++ b/app/src/main/java/net/zetetic/tests/JsonCastTest.java @@ -0,0 +1,23 @@ +package net.zetetic.tests; + +import net.sqlcipher.Cursor; +import net.sqlcipher.database.SQLiteDatabase; + +public class JsonCastTest extends SQLCipherTest { + @Override + public boolean execute(SQLiteDatabase database) { + String name = "Bob Smith", queryName = ""; + String query = String.format("select cast(json_extract('{\"user\":\"%s\"}','$.user') as TEXT);", name); + Cursor cursor = database.rawQuery(query, new Object[]{}); + if(cursor != null && cursor.moveToFirst()){ + queryName = cursor.getString(0); + cursor.close(); + } + return name.equals(queryName); + } + + @Override + public String getName() { + return "JSON cast test"; + } +} diff --git a/app/src/main/java/net/zetetic/tests/MultiThreadReadWriteTest.java b/app/src/main/java/net/zetetic/tests/MultiThreadReadWriteTest.java index a7e060d..c668a1d 100644 --- a/app/src/main/java/net/zetetic/tests/MultiThreadReadWriteTest.java +++ b/app/src/main/java/net/zetetic/tests/MultiThreadReadWriteTest.java @@ -5,7 +5,7 @@ import android.database.Cursor; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; diff --git a/app/src/main/java/net/zetetic/tests/PragmaCipherVersionTest.java b/app/src/main/java/net/zetetic/tests/PragmaCipherVersionTest.java index 519aab9..de47ce2 100644 --- a/app/src/main/java/net/zetetic/tests/PragmaCipherVersionTest.java +++ b/app/src/main/java/net/zetetic/tests/PragmaCipherVersionTest.java @@ -6,7 +6,7 @@ public class PragmaCipherVersionTest extends SQLCipherTest { - private final String CURRENT_CIPHER_VERSION = "4.2.0"; + private final String CURRENT_CIPHER_VERSION = "4.5.4"; @Override public boolean execute(SQLiteDatabase database) { @@ -31,4 +31,4 @@ public boolean execute(SQLiteDatabase database) { public String getName() { return "PRAGMA cipher_version Test"; } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/zetetic/tests/PreventUpdateWithNullWhereArgsFromThrowingExceptionTest.java b/app/src/main/java/net/zetetic/tests/PreventUpdateWithNullWhereArgsFromThrowingExceptionTest.java new file mode 100644 index 0000000..090282e --- /dev/null +++ b/app/src/main/java/net/zetetic/tests/PreventUpdateWithNullWhereArgsFromThrowingExceptionTest.java @@ -0,0 +1,34 @@ +package net.zetetic.tests; + +import android.content.ContentValues; +import android.database.Cursor; + +import net.sqlcipher.database.SQLiteDatabase; + +class PreventUpdateWithNullWhereArgsFromThrowingExceptionTest extends SQLCipherTest { + + @Override + public boolean execute(SQLiteDatabase database) { + try { + database.execSQL("create table t1(a,b);"); + database.execSQL("insert into t1(a,b) values(?, ?)", new Object[]{"foo", "bar"}); + ContentValues values = new ContentValues(); + values.put("b", "baz"); + database.update("t1", SQLiteDatabase.CONFLICT_ABORT, values, null, null); + long count = 0; + Cursor cursor = database.query("select count(*) from t1 where b = ?;", new Object[]{"baz"}); + if(cursor != null && cursor.moveToNext()){ + count = cursor.getLong(0); + } + return count == 1; + } + catch (Exception ex){ + return false; + } + } + + @Override + public String getName() { + return "Prevent update with null where args from throwing"; + } +} diff --git a/app/src/main/java/net/zetetic/tests/QueryNonEncryptedDatabaseTest.java b/app/src/main/java/net/zetetic/tests/QueryNonEncryptedDatabaseTest.java index 56ee92b..2bdb946 100644 --- a/app/src/main/java/net/zetetic/tests/QueryNonEncryptedDatabaseTest.java +++ b/app/src/main/java/net/zetetic/tests/QueryNonEncryptedDatabaseTest.java @@ -3,7 +3,7 @@ import android.database.Cursor; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; diff --git a/app/src/main/java/net/zetetic/tests/RawExecSQLExceptionTest.java b/app/src/main/java/net/zetetic/tests/RawExecSQLExceptionTest.java index 747f518..186335d 100644 --- a/app/src/main/java/net/zetetic/tests/RawExecSQLExceptionTest.java +++ b/app/src/main/java/net/zetetic/tests/RawExecSQLExceptionTest.java @@ -1,7 +1,7 @@ package net.zetetic.tests; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; diff --git a/app/src/main/java/net/zetetic/tests/RawQueryNoSuchFunctionErrorMessageTest.java b/app/src/main/java/net/zetetic/tests/RawQueryNoSuchFunctionErrorMessageTest.java index dbd91f1..cf0e736 100644 --- a/app/src/main/java/net/zetetic/tests/RawQueryNoSuchFunctionErrorMessageTest.java +++ b/app/src/main/java/net/zetetic/tests/RawQueryNoSuchFunctionErrorMessageTest.java @@ -3,7 +3,7 @@ import android.database.Cursor; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; diff --git a/app/src/main/java/net/zetetic/tests/RawQueryNonsenseStatementErrorMessageTest.java b/app/src/main/java/net/zetetic/tests/RawQueryNonsenseStatementErrorMessageTest.java index f9c9cd3..e8a8320 100644 --- a/app/src/main/java/net/zetetic/tests/RawQueryNonsenseStatementErrorMessageTest.java +++ b/app/src/main/java/net/zetetic/tests/RawQueryNonsenseStatementErrorMessageTest.java @@ -3,7 +3,7 @@ import android.database.Cursor; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; diff --git a/app/src/main/java/net/zetetic/tests/RawQuerySyntaxErrorMessageTest.java b/app/src/main/java/net/zetetic/tests/RawQuerySyntaxErrorMessageTest.java index 7b25706..af3455c 100644 --- a/app/src/main/java/net/zetetic/tests/RawQuerySyntaxErrorMessageTest.java +++ b/app/src/main/java/net/zetetic/tests/RawQuerySyntaxErrorMessageTest.java @@ -3,7 +3,7 @@ import android.database.Cursor; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; diff --git a/app/src/main/java/net/zetetic/tests/ReadableWritableInvalidPasswordTest.java b/app/src/main/java/net/zetetic/tests/ReadableWritableInvalidPasswordTest.java index dc96c2b..b2fe5be 100644 --- a/app/src/main/java/net/zetetic/tests/ReadableWritableInvalidPasswordTest.java +++ b/app/src/main/java/net/zetetic/tests/ReadableWritableInvalidPasswordTest.java @@ -3,7 +3,7 @@ import android.content.Context; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.sqlcipher.database.SQLiteOpenHelper; import net.zetetic.ZeteticApplication; diff --git a/app/src/main/java/net/zetetic/tests/ResetQueryCacheFinalizesSqlStatementAndReleasesReferenceTest.java b/app/src/main/java/net/zetetic/tests/ResetQueryCacheFinalizesSqlStatementAndReleasesReferenceTest.java new file mode 100644 index 0000000..8b111c9 --- /dev/null +++ b/app/src/main/java/net/zetetic/tests/ResetQueryCacheFinalizesSqlStatementAndReleasesReferenceTest.java @@ -0,0 +1,35 @@ +package net.zetetic.tests; + +import android.database.Cursor; +import net.sqlcipher.database.SQLiteDatabase; + +class ResetQueryCacheFinalizesSqlStatementAndReleasesReferenceTest extends SQLCipherTest { + @Override + public boolean execute(SQLiteDatabase database) { + int initialColumnCount = 0, updatedColumnCount = 0; + int initialExpectedColumnCount = 2; + int updatedColumnExpectedCount = 3; + database.execSQL("create table t1(a, b);"); + database.execSQL("insert into t1(a,b) values(?, ?);", + new Object[]{"foo", "bar"}); + Cursor cursor = database.query("t1", null,null,null,null,null,null); + if(cursor != null && cursor.moveToNext()){ + initialColumnCount = cursor.getColumnCount(); + cursor.close(); + } + database.resetCompiledSqlCache(); + database.execSQL("alter table t1 add c text null;"); + cursor = database.query("t1", null,null,null,null,null,null); + if(cursor != null && cursor.moveToNext()){ + updatedColumnCount = cursor.getColumnCount(); + cursor.close(); + } + return initialColumnCount == initialExpectedColumnCount + && updatedColumnCount == updatedColumnExpectedCount; + } + + @Override + public String getName() { + return "Reset Query cache"; + } +} diff --git a/app/src/main/java/net/zetetic/tests/SQLCipherTest.java b/app/src/main/java/net/zetetic/tests/SQLCipherTest.java index db32c12..a80e0f6 100644 --- a/app/src/main/java/net/zetetic/tests/SQLCipherTest.java +++ b/app/src/main/java/net/zetetic/tests/SQLCipherTest.java @@ -115,6 +115,7 @@ protected void buildDatabase(SQLiteDatabase database, int rows, int columns, Row String insert = String.format(insertTemplate, insertBuilder.toString()); database.execSQL("DROP TABLE IF EXISTS t1;"); database.execSQL(create); + database.execSQL("BEGIN;"); String[] names = columnNames.toArray(new String[0]); for (int row = 0; row < rows; row++) { Object[] insertArgs = new Object[columns]; @@ -123,6 +124,7 @@ protected void buildDatabase(SQLiteDatabase database, int rows, int columns, Row } database.execSQL(insert, insertArgs); } + database.execSQL("COMMIT;"); Log.i(TAG, String.format("Database built with %d columns, %d rows", columns, rows)); } diff --git a/app/src/main/java/net/zetetic/tests/SimpleQueryTest.java b/app/src/main/java/net/zetetic/tests/SimpleQueryTest.java new file mode 100644 index 0000000..703ceee --- /dev/null +++ b/app/src/main/java/net/zetetic/tests/SimpleQueryTest.java @@ -0,0 +1,40 @@ +package net.zetetic.tests; + +import net.sqlcipher.Cursor; +import net.sqlcipher.database.SQLiteDatabase; +import net.sqlcipher.database.SQLiteStatement; + +import java.util.UUID; + +public class SimpleQueryTest extends SQLCipherTest { + @Override + public boolean execute(SQLiteDatabase database) { + database.execSQL("create table t1(a,b);"); + database.execSQL("insert into t1(a, b) values(?, ?);", + new Object[]{UUID.randomUUID().toString(), "foo"}); + database.execSQL("insert into t1(a, b) values(?, ?);", + new Object[]{UUID.randomUUID().toString(), "bar"}); + int count = 0; + Cursor cursor = database.rawQuery("select * from t1 where a = ?1 or ?1 = -1;", + new Object[]{-1}); + if(cursor != null){ + while (cursor.moveToNext()){ + count++; + } + cursor.close(); + } + +// Works +// SQLiteStatement statement = database.compileStatement("select count(*) from t1 where a = ?1 or ?1 = -1;"); +// statement. +// statement.bindLong(1, -1); +// long count = statement.simpleQueryForLong(); +// statement.close(); + return count == 2; + } + + @Override + public String getName() { + return "Simple Query Test"; + } +} diff --git a/app/src/main/java/net/zetetic/tests/SummingStepTest.java b/app/src/main/java/net/zetetic/tests/SummingStepTest.java new file mode 100644 index 0000000..15ef3ae --- /dev/null +++ b/app/src/main/java/net/zetetic/tests/SummingStepTest.java @@ -0,0 +1,37 @@ +package net.zetetic.tests; + +import net.sqlcipher.Cursor; +import net.sqlcipher.database.SQLiteDatabase; + +import java.util.Locale; + +// Provided via GitHub Issue: https://github.com/sqlcipher/android-database-sqlcipher/issues/526 +public class SummingStepTest extends SQLCipherTest { + @Override + public boolean execute(SQLiteDatabase database) { + database.execSQL("create table sample_session(_id , start_time TEXT , client_id TEXT, type_id TEXT, value TEXT);"); + database.execSQL("create table sample_point(_id ,start_time TEXT , client_id TEXT, type_id TEXT, value TEXT);"); + database.execSQL("insert into sample_session(_id , start_time ,client_id, type_id, value) values(1, 1000,5 ,2, 0);"); + database.execSQL("insert into sample_point(_id , start_time ,client_id, type_id, value) values(1, 1000,5 ,2, 46);"); + database.execSQL("insert into sample_point(_id , start_time ,client_id, type_id, value) values(2, 1000,6 ,2, 46);"); + String query = "select sample_session._id,sample_session.start_time,sample_session.type_id, sample_session.client_id," + + "sum ( case sample_point.type_id when 2 then sample_point.value else 0 end) as step, sum ( case sample_point.type_id when 4 then sample_point.value else 0 end) as calorie, sum ( case sample_point.type_id when 3 then sample_point.value else 0 end) as distance, " + + "sum ( case sample_point.type_id when 5 then sample_point.value else 0 end) as altitude_offset from sample_session INNER JOIN sample_point ON sample_session.start_time = sample_point.start_time and sample_session.client_id = sample_point.client_id " + + "where sample_session.client_id =? and sample_point.client_id =? and sample_point.type_id in ( ?,?,?,? ) " + + "group by sample_session.start_time ORDER BY sample_session.start_time DESC limit 0,1000"; + Cursor cursor = database.rawQuery(query, new String[]{"5","5","2","4","3","5"}); + if(cursor != null){ + cursor.moveToFirst(); + int index = cursor.getColumnIndex("step"); + long value = cursor.getInt(index); + setMessage(String.format(Locale.getDefault(), "Expected:%d Returned:%d", 46, value)); + return value == 46; + } + return false; + } + + @Override + public String getName() { + return "SummingStepTest Test"; + } +} diff --git a/app/src/main/java/net/zetetic/tests/TestSuiteRunner.java b/app/src/main/java/net/zetetic/tests/TestSuiteRunner.java index 9b6e95b..58321d6 100644 --- a/app/src/main/java/net/zetetic/tests/TestSuiteRunner.java +++ b/app/src/main/java/net/zetetic/tests/TestSuiteRunner.java @@ -8,7 +8,6 @@ import net.sqlcipher.CursorWindow; import net.sqlcipher.CursorWindowAllocation; -import net.sqlcipher.database.SQLiteDatabase; import net.zetetic.ZeteticApplication; import java.util.ArrayList; @@ -49,8 +48,6 @@ protected void onPostExecute(Void aVoid) { } private void runSuite() { - - SQLiteDatabase.loadLibs(ZeteticApplication.getInstance()); CursorWindowAllocation defaultAllocation = CursorWindow.getCursorWindowAllocation(); for (SQLCipherTest test : getTestsToRun()) { try { @@ -71,6 +68,17 @@ private void runSuite() { private List getTestsToRun() { List tests = new ArrayList<>(); + if(ZeteticApplication.getInstance().supportsMinLibraryVersionRequired("4.5.4")) { + tests.add(new ResetQueryCacheFinalizesSqlStatementAndReleasesReferenceTest()); + } + if(ZeteticApplication.getInstance().supportsMinLibraryVersionRequired("4.5.4")){ + tests.add(new PreventUpdateWithNullWhereArgsFromThrowingExceptionTest()); + } + tests.add(new SummingStepTest()); + tests.add(new JsonCastTest()); + tests.add(new SimpleQueryTest()); + tests.add(new DefaultCursorWindowAllocationTest()); + tests.add(new DeleteTableWithNullWhereArgsTest()); tests.add(new LoopingInsertTest()); tests.add(new FIPSTest()); tests.add(new PragmaCipherVersionTest()); @@ -102,8 +110,8 @@ private List getTestsToRun() { tests.add(new TransactionNonExclusiveTest()); tests.add(new TransactionWithListenerTest()); tests.add(new LargeDatabaseCursorAccessTest()); - -//// tests.add(new TimeLargeByteArrayQueryTest()); + + tests.add(new TimeLargeByteArrayQueryTest()); tests.add(new QueryLimitTest()); tests.add(new RTreeTest()); @@ -187,7 +195,6 @@ private List getTestsToRun() { tests.add(new BindByteArrayRawQueryTest()); tests.add(new NullRawQueryTest()); tests.add(new ReadWriteDatabaseToExternalStorageTest()); - return tests; } } diff --git a/app/src/main/java/net/zetetic/tests/VerifyCipherProviderVersionTest.java b/app/src/main/java/net/zetetic/tests/VerifyCipherProviderVersionTest.java index 346d422..34d73cd 100644 --- a/app/src/main/java/net/zetetic/tests/VerifyCipherProviderVersionTest.java +++ b/app/src/main/java/net/zetetic/tests/VerifyCipherProviderVersionTest.java @@ -11,7 +11,7 @@ public boolean execute(SQLiteDatabase database) { "PRAGMA cipher_provider_version;"); setMessage(String.format("Reported:%s", provider)); return provider.contains("OpenSSL 1.1.1") || - provider.contains("OpenSSL 1.0.2p-fips"); + provider.contains("OpenSSL 1.0.2u-fips"); } @Override diff --git a/app/src/main/java/net/zetetic/tests/VerifyUTF8EncodingForKeyTest.java b/app/src/main/java/net/zetetic/tests/VerifyUTF8EncodingForKeyTest.java index 3efb886..53b2898 100644 --- a/app/src/main/java/net/zetetic/tests/VerifyUTF8EncodingForKeyTest.java +++ b/app/src/main/java/net/zetetic/tests/VerifyUTF8EncodingForKeyTest.java @@ -3,7 +3,8 @@ import android.database.Cursor; import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabaseHook; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; +import net.sqlcipher.database.SQLiteStatement; import net.zetetic.ZeteticApplication; import java.io.File; @@ -27,17 +28,26 @@ public boolean execute(SQLiteDatabase database) { setMessage(String.format("Database should not open with password:%s", invalidPassword)); return false; } - } catch (SQLiteException ex){} + } catch (SQLiteException ex){ + log(String.format("Error opening database:%s", ex.getMessage())); + } SQLiteDatabaseHook hook = new SQLiteDatabaseHook() { - public void preKey(SQLiteDatabase database) {} - public void postKey(SQLiteDatabase database) { - database.rawExecSQL("PRAGMA cipher_migrate;"); + public void preKey(SQLiteDatabase db) {} + public void postKey(SQLiteDatabase db) { + SQLiteStatement statement = db.compileStatement("PRAGMA cipher_migrate;"); + long result = statement.simpleQueryForLong(); + statement.close(); + String message = String.format("cipher_migrate result:%d", result); + setMessage(message); + log(message); } }; - sourceDatabase = SQLiteDatabase.openDatabase(sourceDatabaseFile.getPath(), - password, null, SQLiteDatabase.OPEN_READWRITE, hook); + sourceDatabase = SQLiteDatabase.openDatabase(sourceDatabaseFile.getAbsolutePath(), + password, null, + SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY, hook); return queryContent(sourceDatabase); } catch (Exception e) { + log(String.format("Error attempting to open database:%s", e.getMessage())); return false; } } diff --git a/app/src/main/java/net/zetetic/tests/support/CanThrowSQLiteExceptionTest.java b/app/src/main/java/net/zetetic/tests/support/CanThrowSQLiteExceptionTest.java index 052ea24..0e81e5b 100644 --- a/app/src/main/java/net/zetetic/tests/support/CanThrowSQLiteExceptionTest.java +++ b/app/src/main/java/net/zetetic/tests/support/CanThrowSQLiteExceptionTest.java @@ -1,7 +1,7 @@ package net.zetetic.tests.support; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.tests.SQLCipherTest; public class CanThrowSQLiteExceptionTest extends SupportTest { diff --git a/app/src/main/java/net/zetetic/tests/support/ClosedDatabaseTest.java b/app/src/main/java/net/zetetic/tests/support/ClosedDatabaseTest.java index ce76cef..f2ee33d 100644 --- a/app/src/main/java/net/zetetic/tests/support/ClosedDatabaseTest.java +++ b/app/src/main/java/net/zetetic/tests/support/ClosedDatabaseTest.java @@ -3,7 +3,7 @@ import android.util.Log; import net.sqlcipher.DatabaseErrorHandler; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.sqlcipher.database.SupportFactory; import net.zetetic.ZeteticApplication; import net.zetetic.tests.TestResult; diff --git a/app/src/main/java/net/zetetic/tests/support/CompileStatementSyntaxErrorMessageTest.java b/app/src/main/java/net/zetetic/tests/support/CompileStatementSyntaxErrorMessageTest.java index 2596d66..031cfb9 100644 --- a/app/src/main/java/net/zetetic/tests/support/CompileStatementSyntaxErrorMessageTest.java +++ b/app/src/main/java/net/zetetic/tests/support/CompileStatementSyntaxErrorMessageTest.java @@ -2,7 +2,7 @@ import android.util.Log; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.sqlcipher.database.SQLiteStatement; import net.zetetic.ZeteticApplication; import net.zetetic.tests.SQLCipherTest; diff --git a/app/src/main/java/net/zetetic/tests/support/DecryptedRoomTest.java b/app/src/main/java/net/zetetic/tests/support/DecryptedRoomTest.java new file mode 100644 index 0000000..832c372 --- /dev/null +++ b/app/src/main/java/net/zetetic/tests/support/DecryptedRoomTest.java @@ -0,0 +1,133 @@ +package net.zetetic.tests.support; + +import android.annotation.SuppressLint; +import android.app.Activity; + +import androidx.annotation.NonNull; +import androidx.room.Dao; +import androidx.room.Database; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.Insert; +import androidx.room.PrimaryKey; +import androidx.room.Query; +import androidx.room.Room; +import androidx.room.RoomDatabase; + +import net.zetetic.ZeteticApplication; +import net.zetetic.tests.TestResult; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class DecryptedRoomTest implements ISupportTest { + private static final String DB_NAME = "room.db"; + + private final Activity activity; + + @Entity() + public static class ParentEntity { + @PrimaryKey(autoGenerate = true) long id; + int intValue; + float floatValue; + double doubleValue; + char charValue; + boolean boolValue; + } + + @Entity(foreignKeys = @ForeignKey(entity = ParentEntity.class, + parentColumns = "id", childColumns = "parentId", onDelete = ForeignKey.CASCADE), + indices = @Index("parentId")) + public static class ChildEntity { + @PrimaryKey @NonNull String uuid; + String stringValue; + long parentId; + } + + @Dao + public static abstract class TestDao { + @Query("SELECT * FROM ChildEntity WHERE parentId = :parentId or :parentId = -1") + abstract List getAllChildrenForParent(long parentId); + + @Query("SELECT * FROM ChildEntity") + abstract List getAllChildren(); + + @Insert + abstract void insert(List entities); + + @Insert + abstract long insert(ParentEntity entity); + } + + @Database(entities = {ParentEntity.class, ChildEntity.class}, version = 1) + public static abstract class TestDatabase extends RoomDatabase { + abstract TestDao testDao(); + } + + public DecryptedRoomTest(Activity activity) { + this.activity = activity; + } + + @SuppressLint("DefaultLocale") + public TestResult run() { + File dbFile = ZeteticApplication.getInstance().getDatabasePath(DB_NAME); + + if (dbFile.exists()){ + dbFile.delete(); + } + + final TestResult result = new TestResult(getName(), false); + final TestDatabase room = Room.databaseBuilder(activity, TestDatabase.class, DB_NAME) + .build(); + ParentEntity parent = new ParentEntity(); + + parent.boolValue = true; + parent.charValue = 'x'; + parent.intValue = 1337; + parent.doubleValue = 3.14159; + parent.floatValue = 2.71828f; + parent.id = room.testDao().insert(parent); + + result.setResult(true); + + ChildEntity firstChild = new ChildEntity(); + + firstChild.uuid = UUID.randomUUID().toString(); + firstChild.stringValue = "Um, hi!"; + firstChild.parentId = parent.id; + + ChildEntity secondChild = new ChildEntity(); + + secondChild.uuid = UUID.randomUUID().toString(); + secondChild.stringValue = "And now for something completely different"; + secondChild.parentId = parent.id; + + List children = new ArrayList<>(); + + children.add(firstChild); + children.add(secondChild); + + room.testDao().insert(children); + + List allChildren = room.testDao().getAllChildren(); + List allChildrenNegativeOne = room.testDao().getAllChildrenForParent(-1); + List other = room.testDao().getAllChildrenForParent( parent.id); + + //the query should have returned both the list with same sizes here + if (allChildren.size() != allChildrenNegativeOne.size()) { + result.setResult(false); + result.setMessage(String.format("expected all children, found %d from entity and %d from main query", allChildrenNegativeOne.size(), allChildren.size())); + return result; + } + + return result; + } + + @Override + public String getName() { + return "Decrypted Room Test"; + } +} diff --git a/app/src/main/java/net/zetetic/tests/support/EncryptBytesTest.java b/app/src/main/java/net/zetetic/tests/support/EncryptBytesTest.java new file mode 100644 index 0000000..f06fbbf --- /dev/null +++ b/app/src/main/java/net/zetetic/tests/support/EncryptBytesTest.java @@ -0,0 +1,136 @@ +package net.zetetic.tests.support; + +import android.app.Activity; +import android.database.Cursor; + +import androidx.room.Dao; +import androidx.room.Database; +import androidx.room.Entity; +import androidx.room.PrimaryKey; +import androidx.room.RawQuery; +import androidx.room.Room; +import androidx.room.RoomDatabase; +import androidx.sqlite.db.SimpleSQLiteQuery; + +import net.sqlcipher.database.SQLiteDatabase; +import net.sqlcipher.database.SupportFactory; +import net.zetetic.ZeteticApplication; +import net.zetetic.tests.TestResult; + +import java.io.File; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Random; + +public class EncryptBytesTest implements ISupportTest { + + Activity activity; + + public EncryptBytesTest(Activity activity) { + this.activity = activity; + } + + @Dao + public interface BlobDao { + @RawQuery + byte[] blobRawGet(SimpleSQLiteQuery query); + } + + public static class Encryptor { + BlobDao dao; + byte[] encryptionKey; + + public Encryptor(BlobDao dao, byte[] encryptionKey) { + this.dao = dao; + this.encryptionKey = encryptionKey; + } + + public byte[] encryptBytes(byte[] material) { + return dao.blobRawGet(new SimpleSQLiteQuery("select sqlcipher_vle_encrypt(?, ?)", + new Object[]{material, encryptionKey})); + } + + public byte[] decryptBytes(byte[] material) { + return dao.blobRawGet(new SimpleSQLiteQuery("select sqlcipher_vle_decrypt(?, ?)", + new Object[]{material, encryptionKey})); + } + } + + public static class Encryptor2 { + RoomDatabase roomDatabase; + byte[] encryptionKey; + + public Encryptor2(RoomDatabase roomDatabase, byte[] encryptionKey) { + this.roomDatabase = roomDatabase; + this.encryptionKey = encryptionKey; + } + + public byte[] encryptBytes(byte[] material) { + byte[] result = null; + Cursor cursor = roomDatabase.getOpenHelper().getReadableDatabase().query("select sqlcipher_vle_encrypt(?, ?)", + new Object[]{material, encryptionKey}); + if(cursor != null && cursor.moveToNext()){ + result = cursor.getBlob(0); + cursor.close(); + } + return result; + } + + public byte[] decryptBytes(byte[] material) { + byte[] result = null; + Cursor cursor = roomDatabase.getOpenHelper().getReadableDatabase().query("select sqlcipher_vle_decrypt(?, ?)", + new Object[]{material, encryptionKey}); + if(cursor != null && cursor.moveToNext()){ + result = cursor.getBlob(0); + cursor.close(); + } + return result; + } + } + + @Entity + public static class BlobEntity { + @PrimaryKey(autoGenerate = true) + long id; + } + + @Database(entities = {BlobEntity.class}, version = 1) + public static abstract class BlobDatabase extends RoomDatabase { + abstract BlobDao blobDao(); + } + + + public TestResult run() { + TestResult result = new TestResult(getName(), false); + byte[] passphrase = SQLiteDatabase.getBytes(ZeteticApplication.DATABASE_PASSWORD.toCharArray()); + SupportFactory factory = new SupportFactory(passphrase, ZeteticApplication.getInstance().wrapHook(null)); + File databaseFile = ZeteticApplication.getInstance().getDatabasePath("test.db"); + if(databaseFile.exists()) { + databaseFile.delete(); + } + BlobDatabase room = Room.databaseBuilder(activity, BlobDatabase.class, databaseFile.getAbsolutePath()) + .openHelperFactory(factory) + .build(); + + byte[] key = generateRandomBytes(64); + //Encryptor encryptor = new Encryptor(room.blobDao(), key); + Encryptor2 encryptor = new Encryptor2(room, key); + + byte[] source = "hi".getBytes(); + byte[] encrypted = encryptor.encryptBytes(source); + byte[] decrypted = encryptor.decryptBytes(encrypted); + result.setResult(Arrays.equals(source, decrypted)); + return result; + } + + private byte[] generateRandomBytes(int length) { + Random random = new SecureRandom(); + byte[] value = new byte[length]; + random.nextBytes(value); + return value; + } + + public String getName() { + return "Encrypt Bytes Test"; + } +} \ No newline at end of file diff --git a/app/src/main/java/net/zetetic/tests/support/EncryptedRoomTest.java b/app/src/main/java/net/zetetic/tests/support/EncryptedRoomTest.java new file mode 100644 index 0000000..5894653 --- /dev/null +++ b/app/src/main/java/net/zetetic/tests/support/EncryptedRoomTest.java @@ -0,0 +1,151 @@ +package net.zetetic.tests.support; + +import android.annotation.SuppressLint; +import android.app.Activity; + +import androidx.annotation.NonNull; +import androidx.room.Dao; +import androidx.room.Database; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.Insert; +import androidx.room.PrimaryKey; +import androidx.room.Query; +import androidx.room.Room; +import androidx.room.RoomDatabase; + +import net.sqlcipher.Cursor; +import net.sqlcipher.database.SQLiteDatabase; +import net.sqlcipher.database.SupportFactory; +import net.zetetic.ZeteticApplication; +import net.zetetic.tests.TestResult; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class EncryptedRoomTest implements ISupportTest { + private static final String DB_NAME = "room.db"; + + private final Activity activity; + + @Entity() + public static class ParentEntity { + @PrimaryKey(autoGenerate = true) long id; + int intValue; + float floatValue; + double doubleValue; + char charValue; + boolean boolValue; + } + + @Entity(foreignKeys = @ForeignKey(entity = ParentEntity.class, + parentColumns = "id", childColumns = "parentId", onDelete = ForeignKey.CASCADE), + indices = @Index("parentId")) + public static class ChildEntity { + @PrimaryKey @NonNull String uuid; + String stringValue; + long parentId; + } + + @Dao + public static abstract class TestDao { + @Query("SELECT * FROM ChildEntity WHERE parentId = :parentId or :parentId = -1") + abstract List getAllChildrenForParent(long parentId); + + @Query("SELECT * FROM ChildEntity") + abstract List getAllChildren(); + + @Insert + abstract void insert(List entities); + + @Insert + abstract long insert(ParentEntity entity); + } + + @Database(entities = {ParentEntity.class, ChildEntity.class}, version = 1) + public static abstract class TestDatabase extends RoomDatabase { + abstract TestDao testDao(); + } + + public EncryptedRoomTest(Activity activity) { + this.activity = activity; + } + + @SuppressLint("DefaultLocale") + public TestResult run() { + File dbFile = ZeteticApplication.getInstance().getDatabasePath(DB_NAME); + + if (dbFile.exists()){ + dbFile.delete(); + } + + final TestResult result = new TestResult(getName(), false); + final byte[] passphrase = SQLiteDatabase.getBytes(ZeteticApplication.DATABASE_PASSWORD.toCharArray()); + final SupportFactory factory = new SupportFactory(passphrase, ZeteticApplication.getInstance().wrapHook(null)); + final TestDatabase room = Room.databaseBuilder(activity, TestDatabase.class, DB_NAME) + .openHelperFactory(factory) + .build(); + ParentEntity parent = new ParentEntity(); + + parent.boolValue = true; + parent.charValue = 'x'; + parent.intValue = 1337; + parent.doubleValue = 3.14159; + parent.floatValue = 2.71828f; + parent.id = room.testDao().insert(parent); + + result.setResult(true); + + ChildEntity firstChild = new ChildEntity(); + + firstChild.uuid = UUID.randomUUID().toString(); + firstChild.stringValue = "Um, hi!"; + firstChild.parentId = parent.id; + + ChildEntity secondChild = new ChildEntity(); + + secondChild.uuid = UUID.randomUUID().toString(); + secondChild.stringValue = "And now for something completely different"; + secondChild.parentId = parent.id; + + List children = new ArrayList<>(); + + children.add(firstChild); + children.add(secondChild); + + room.testDao().insert(children); + + List allChildren = room.testDao().getAllChildren(); + List allChildrenNegativeOne = room.testDao().getAllChildrenForParent( -1); + List other = room.testDao().getAllChildrenForParent( parent.id); + +// SQLiteDatabase db = (SQLiteDatabase)room.getOpenHelper().getReadableDatabase(); +// String[] projection = new String[] { "*" }; +// String[] args = new String[] { "-1" }; +// Cursor c = db.query("ChildEntity", projection, "parentId = ?1 or ?1 = -1", args, null, null, null); +// +// //the query should have returned both the list with same sizes here +// if (allChildren.size() != c.getCount()) { +// result.setResult(false); +// result.setMessage(String.format("expected all children, found %d from entity and %d from main query", c.getCount(), allChildren.size())); +// return result; +// } + + //the query should have returned both the list with same sizes here + if (allChildren.size() != allChildrenNegativeOne.size()) { + result.setResult(false); + result.setMessage(String.format("expected all children, found %d from entity and %d from main query", allChildrenNegativeOne.size(), allChildren.size())); + return result; + } + + return result; + } + + @Override + public String getName() { + return "Encrypted Room Test"; + } +} diff --git a/app/src/main/java/net/zetetic/tests/support/ExecuteInsertConstraintErrorMessageTest.java b/app/src/main/java/net/zetetic/tests/support/ExecuteInsertConstraintErrorMessageTest.java index 9f8c784..c4e5ce7 100644 --- a/app/src/main/java/net/zetetic/tests/support/ExecuteInsertConstraintErrorMessageTest.java +++ b/app/src/main/java/net/zetetic/tests/support/ExecuteInsertConstraintErrorMessageTest.java @@ -1,7 +1,7 @@ package net.zetetic.tests.support; import android.util.Log; -import net.sqlcipher.database.SQLiteConstraintException; +import android.database.sqlite.SQLiteConstraintException; import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteStatement; import net.zetetic.ZeteticApplication; @@ -20,7 +20,7 @@ public boolean execute(SQLiteDatabase database) { Log.v(ZeteticApplication.TAG, "EXPECTED RESULT: DID throw SQLiteConstraintException", e); String message = e.getMessage(); setMessage(message); - if (!message.matches("error code 19: UNIQUE constraint failed: tt\\.a")) { + if (!message.matches("error code 19 \\(extended error code 2067\\): UNIQUE constraint failed: tt\\.a")) { Log.e(ZeteticApplication.TAG, "NOT EXPECTED: INCORRECT exception message: " + message); return false; } diff --git a/app/src/main/java/net/zetetic/tests/support/InvalidPasswordTest.java b/app/src/main/java/net/zetetic/tests/support/InvalidPasswordTest.java index fa3e76d..042ba9a 100644 --- a/app/src/main/java/net/zetetic/tests/support/InvalidPasswordTest.java +++ b/app/src/main/java/net/zetetic/tests/support/InvalidPasswordTest.java @@ -2,7 +2,7 @@ import android.util.Log; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.sqlcipher.database.SupportFactory; import net.zetetic.ZeteticApplication; import net.zetetic.tests.TestResult; diff --git a/app/src/main/java/net/zetetic/tests/support/JavaClientLibraryVersionTest.java b/app/src/main/java/net/zetetic/tests/support/JavaClientLibraryVersionTest.java index 3f6d053..72ce5e8 100644 --- a/app/src/main/java/net/zetetic/tests/support/JavaClientLibraryVersionTest.java +++ b/app/src/main/java/net/zetetic/tests/support/JavaClientLibraryVersionTest.java @@ -5,16 +5,16 @@ public class JavaClientLibraryVersionTest extends SupportTest { - String expectedClientLibraryVersion = "4.2.0"; + private final String EXPECTED_SQLCIPHER_ANDROID_VERSION = "4.5.4"; @Override public boolean execute(SQLiteDatabase database) { setMessage(String.format("Report:%s", SQLiteDatabase.SQLCIPHER_ANDROID_VERSION)); - return SQLiteDatabase.SQLCIPHER_ANDROID_VERSION.equals(expectedClientLibraryVersion); + return SQLiteDatabase.SQLCIPHER_ANDROID_VERSION.equals(EXPECTED_SQLCIPHER_ANDROID_VERSION); } @Override public String getName() { return "Java Client Library Version Test"; } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/zetetic/tests/support/PragmaCipherVersionTest.java b/app/src/main/java/net/zetetic/tests/support/PragmaCipherVersionTest.java index 1dcad96..fb1cb6e 100644 --- a/app/src/main/java/net/zetetic/tests/support/PragmaCipherVersionTest.java +++ b/app/src/main/java/net/zetetic/tests/support/PragmaCipherVersionTest.java @@ -7,7 +7,7 @@ public class PragmaCipherVersionTest extends SupportTest { - private final String CURRENT_CIPHER_VERSION = "4.2.0"; + private final String CURRENT_CIPHER_VERSION = "4.5.4"; @Override public boolean execute(SQLiteDatabase database) { @@ -32,4 +32,4 @@ public boolean execute(SQLiteDatabase database) { public String getName() { return "PRAGMA cipher_version Test"; } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/zetetic/tests/support/RawExecSQLExceptionTest.java b/app/src/main/java/net/zetetic/tests/support/RawExecSQLExceptionTest.java index fc5554f..f06eb2a 100644 --- a/app/src/main/java/net/zetetic/tests/support/RawExecSQLExceptionTest.java +++ b/app/src/main/java/net/zetetic/tests/support/RawExecSQLExceptionTest.java @@ -2,7 +2,7 @@ import android.util.Log; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; import net.zetetic.tests.SQLCipherTest; diff --git a/app/src/main/java/net/zetetic/tests/support/RawQueryNoSuchFunctionErrorMessageTest.java b/app/src/main/java/net/zetetic/tests/support/RawQueryNoSuchFunctionErrorMessageTest.java index b3631af..f6445a6 100644 --- a/app/src/main/java/net/zetetic/tests/support/RawQueryNoSuchFunctionErrorMessageTest.java +++ b/app/src/main/java/net/zetetic/tests/support/RawQueryNoSuchFunctionErrorMessageTest.java @@ -3,7 +3,7 @@ import android.database.Cursor; import android.util.Log; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; import net.zetetic.tests.SQLCipherTest; diff --git a/app/src/main/java/net/zetetic/tests/support/RawQueryNonsenseStatementErrorMessageTest.java b/app/src/main/java/net/zetetic/tests/support/RawQueryNonsenseStatementErrorMessageTest.java index 06c066b..02a4cb7 100644 --- a/app/src/main/java/net/zetetic/tests/support/RawQueryNonsenseStatementErrorMessageTest.java +++ b/app/src/main/java/net/zetetic/tests/support/RawQueryNonsenseStatementErrorMessageTest.java @@ -3,7 +3,7 @@ import android.database.Cursor; import android.util.Log; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; import net.zetetic.tests.SQLCipherTest; diff --git a/app/src/main/java/net/zetetic/tests/support/RawQuerySyntaxErrorMessageTest.java b/app/src/main/java/net/zetetic/tests/support/RawQuerySyntaxErrorMessageTest.java index 24c1af1..feffcac 100644 --- a/app/src/main/java/net/zetetic/tests/support/RawQuerySyntaxErrorMessageTest.java +++ b/app/src/main/java/net/zetetic/tests/support/RawQuerySyntaxErrorMessageTest.java @@ -3,7 +3,7 @@ import android.database.Cursor; import android.util.Log; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.zetetic.ZeteticApplication; import net.zetetic.tests.SQLCipherTest; diff --git a/app/src/main/java/net/zetetic/tests/support/RawRekeyTest.java b/app/src/main/java/net/zetetic/tests/support/RawRekeyTest.java index 6b5351d..c4b04d0 100644 --- a/app/src/main/java/net/zetetic/tests/support/RawRekeyTest.java +++ b/app/src/main/java/net/zetetic/tests/support/RawRekeyTest.java @@ -19,7 +19,7 @@ public class RawRekeyTest implements ISupportTest { public TestResult run() { TestResult result = new TestResult(getName(), false); byte[] passphrase = SQLiteDatabase.getBytes(ZeteticApplication.DATABASE_PASSWORD.toCharArray()); - SupportFactory factory = new SupportFactory(passphrase); + SupportFactory factory = new SupportFactory(passphrase, ZeteticApplication.getInstance().wrapHook(null)); SupportSQLiteOpenHelper.Configuration cfg = SupportSQLiteOpenHelper.Configuration.builder(ZeteticApplication.getInstance()) .name(ZeteticApplication.DATABASE_NAME) @@ -41,11 +41,11 @@ public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, database.execSQL("create table t1(a,b);"); database.execSQL("insert into t1(a,b) values(?,?)", new Object[]{"one for the money", "two for the show"}); - database.execSQL(rekeyCommand); + database.query(rekeyCommand); helper.close(); passphrase = SQLiteDatabase.getBytes(password.toCharArray()); - factory = new SupportFactory(passphrase); + factory = new SupportFactory(passphrase, ZeteticApplication.getInstance().wrapHook(null)); cfg = SupportSQLiteOpenHelper.Configuration.builder(ZeteticApplication.getInstance()) .name(ZeteticApplication.DATABASE_NAME) diff --git a/app/src/main/java/net/zetetic/tests/support/ReadableWritableInvalidPasswordTest.java b/app/src/main/java/net/zetetic/tests/support/ReadableWritableInvalidPasswordTest.java index 1142356..e94d3c4 100644 --- a/app/src/main/java/net/zetetic/tests/support/ReadableWritableInvalidPasswordTest.java +++ b/app/src/main/java/net/zetetic/tests/support/ReadableWritableInvalidPasswordTest.java @@ -2,7 +2,7 @@ import android.util.Log; import net.sqlcipher.database.SQLiteDatabase; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.sqlcipher.database.SQLiteOpenHelper; import net.sqlcipher.database.SupportFactory; import net.zetetic.ZeteticApplication; diff --git a/app/src/main/java/net/zetetic/tests/support/RoomTest.java b/app/src/main/java/net/zetetic/tests/support/RoomTest.java index 0683c8e..ca38701 100644 --- a/app/src/main/java/net/zetetic/tests/support/RoomTest.java +++ b/app/src/main/java/net/zetetic/tests/support/RoomTest.java @@ -26,7 +26,6 @@ public class RoomTest implements ISupportTest { private static final String DB_NAME = "room.db"; - private final Activity activity; @Entity() diff --git a/app/src/main/java/net/zetetic/tests/support/RoomUpsertTest.java b/app/src/main/java/net/zetetic/tests/support/RoomUpsertTest.java new file mode 100644 index 0000000..0fed65e --- /dev/null +++ b/app/src/main/java/net/zetetic/tests/support/RoomUpsertTest.java @@ -0,0 +1,85 @@ +package net.zetetic.tests.support; + +import android.app.Activity; +import android.util.Log; + +import androidx.room.Dao; +import androidx.room.Database; +import androidx.room.Entity; +import androidx.room.PrimaryKey; +import androidx.room.Query; +import androidx.room.Room; +import androidx.room.RoomDatabase; +import androidx.room.Upsert; + +import net.sqlcipher.database.SQLiteDatabase; +import net.sqlcipher.database.SupportFactory; +import net.zetetic.ZeteticApplication; +import net.zetetic.tests.TestResult; + +import java.io.File; + +class RoomUpsertTest implements ISupportTest { + + private Activity activity; + private static final String DB_NAME = "room.db"; + private String TAG = getClass().getSimpleName(); + + public RoomUpsertTest(Activity activity){ + + this.activity = activity; + } + + @Override + public String getName() { + return "Room Upsert Test"; + } + + @Override + public TestResult run() { + TestResult result = new TestResult(getName(), false); + File databasePath = this.activity.getDatabasePath(DB_NAME); + if(databasePath.exists()){ + databasePath.delete(); + } + try { + final byte[] passphrase = SQLiteDatabase.getBytes(ZeteticApplication.DATABASE_PASSWORD.toCharArray()); + final SupportFactory factory = new SupportFactory(passphrase); + final UserDatabase room = Room.databaseBuilder(activity, UserDatabase.class, DB_NAME) + .openHelperFactory(factory) + .build(); + User user = new User(); + user.name = "Foo Bar"; + user.age = 41; + user.id = room.userDao().upsert(user); + user.age = 42; + room.userDao().upsert(user); + User[] searchUser = room.userDao().findById(user.id); + result.setResult(searchUser[0].age == 42); + } + catch (android.database.sqlite.SQLiteConstraintException ex){ + Log.i(TAG, "Error in Room Upsert test", ex); + } + return result; + } + + @Entity + public static class User { + @PrimaryKey(autoGenerate = true) long id; + String name; + int age; + } + + @Dao + public static abstract class UserDao { + @Upsert + abstract long upsert(User user); + @Query("SELECT * FROM user WHERE id=:id") + abstract User[] findById(long id); + } + + @Database(entities = {User.class}, version = 1) + public static abstract class UserDatabase extends RoomDatabase { + abstract UserDao userDao(); + } +} \ No newline at end of file diff --git a/app/src/main/java/net/zetetic/tests/support/SQLiteCompiledSqlExceptionTest.java b/app/src/main/java/net/zetetic/tests/support/SQLiteCompiledSqlExceptionTest.java new file mode 100644 index 0000000..37770de --- /dev/null +++ b/app/src/main/java/net/zetetic/tests/support/SQLiteCompiledSqlExceptionTest.java @@ -0,0 +1,97 @@ +package net.zetetic.tests.support; + +import android.app.Activity; +import android.content.ClipData; +import android.util.Log; + +import androidx.room.Dao; +import androidx.room.Database; +import androidx.room.Delete; +import androidx.room.Entity; +import androidx.room.Insert; +import androidx.room.OnConflictStrategy; +import androidx.room.PrimaryKey; +import androidx.room.Query; +import androidx.room.RawQuery; +import androidx.room.Room; +import androidx.room.RoomDatabase; +import androidx.sqlite.db.SimpleSQLiteQuery; + +import net.sqlcipher.database.SupportFactory; +import net.zetetic.ZeteticApplication; +import net.zetetic.tests.TestResult; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SQLiteCompiledSqlExceptionTest implements ISupportTest { + + private Activity activity; + private TestDatabase testDatabase; + + public SQLiteCompiledSqlExceptionTest(Activity activity) { + this.activity = activity; + } + + public TestResult run() { + boolean result = false; + try { + String key = "password"; + File dbFile = ZeteticApplication.getInstance().getDatabasePath("test.db"); + if (dbFile.exists()){ + dbFile.delete(); + } + SupportFactory factory = new SupportFactory(key.getBytes(), ZeteticApplication.getInstance().wrapHook(null)); + testDatabase = Room.databaseBuilder(ZeteticApplication.getInstance(), TestDatabase.class, dbFile.getName()) + .openHelperFactory(factory) // works without a problem if you comment out this line + .build(); + + for (int i = 0; i < 100; i++) { + testInsert(); + List args = new ArrayList<>(Arrays.asList(new Integer[]{111, 112})); + testDatabase.itemDao().deleteAll(args); + } + result = true; + } catch (Exception ex){ + Log.e(getClass().getSimpleName(), "Exception in test", ex); + } + return new TestResult(getName(), result); + } + + private void testInsert(){ + for(int i = 0; i < 100; i++){ + testDatabase.itemDao().insert(new Item(i)); + } + } + + public String getName() { + return "SQLiteCompiledSqlException Test"; + } + + @Dao + public interface ItemDao { + @Insert(onConflict = OnConflictStrategy.IGNORE) + void insert(Item item); + + @Query("DELETE FROM item WHERE id IN (:ids)") + abstract void deleteAll(List ids); + } + + @Entity + public static class Item { + @PrimaryKey(autoGenerate = true) + long id; + int value; + + public Item(int value){ + this.value = value; + } + } + + @Database(entities = {Item.class}, version = 1) + public static abstract class TestDatabase extends RoomDatabase { + abstract ItemDao itemDao(); + } +} diff --git a/app/src/main/java/net/zetetic/tests/support/SupportSuiteRunner.java b/app/src/main/java/net/zetetic/tests/support/SupportSuiteRunner.java index 36aae69..e0de72d 100644 --- a/app/src/main/java/net/zetetic/tests/support/SupportSuiteRunner.java +++ b/app/src/main/java/net/zetetic/tests/support/SupportSuiteRunner.java @@ -1,15 +1,12 @@ package net.zetetic.tests.support; import android.app.Activity; -import android.media.AudioManager; -import android.media.ToneGenerator; import android.os.AsyncTask; import android.os.Build; import android.util.Log; -import android.view.WindowManager; + import net.sqlcipher.CursorWindow; import net.sqlcipher.CursorWindowAllocation; -import net.sqlcipher.database.SQLiteDatabase; import net.zetetic.ZeteticApplication; import net.zetetic.tests.ResultNotifier; import net.zetetic.tests.TestResult; @@ -24,9 +21,6 @@ public class SupportSuiteRunner extends AsyncTask getTestsToRun() { List tests = new ArrayList<>(); +// tests.add(new SQLiteCompiledSqlExceptionTest(activity)); + if(ZeteticApplication.getInstance().supportsMinLibraryVersionRequired("4.5.4")) { + tests.add(new RoomUpsertTest(activity)); + } + if(ZeteticApplication.getInstance().includesLicenseCode()){ + tests.add(new EncryptBytesTest(activity)); + } + tests.add(new EncryptedRoomTest(activity)); + tests.add(new DecryptedRoomTest(activity)); tests.add(new LoopingInsertTest()); tests.add(new FIPSTest()); tests.add(new PragmaCipherVersionTest()); @@ -175,7 +170,6 @@ private List getTestsToRun() { tests.add(new BindByteArrayRawQueryTest()); tests.add(new NullRawQueryTest()); tests.add(new RoomTest(activity)); - return tests; } } diff --git a/app/src/main/java/net/zetetic/tests/support/SupportTest.java b/app/src/main/java/net/zetetic/tests/support/SupportTest.java index 74a2529..19e3495 100644 --- a/app/src/main/java/net/zetetic/tests/support/SupportTest.java +++ b/app/src/main/java/net/zetetic/tests/support/SupportTest.java @@ -69,7 +69,7 @@ private void internalTearDown(){ protected SQLiteDatabase createDatabase() { byte[] passphrase = SQLiteDatabase.getBytes(ZeteticApplication.DATABASE_PASSWORD.toCharArray()); - SupportFactory factory = new SupportFactory(passphrase); + SupportFactory factory = new SupportFactory(passphrase, ZeteticApplication.getInstance().wrapHook(null)); SupportSQLiteOpenHelper.Configuration cfg = SupportSQLiteOpenHelper.Configuration.builder(ZeteticApplication.getInstance()) .name(ZeteticApplication.DATABASE_NAME) diff --git a/app/src/main/java/net/zetetic/tests/support/VerifyCipherProviderVersionTest.java b/app/src/main/java/net/zetetic/tests/support/VerifyCipherProviderVersionTest.java index 9768b39..f32a56b 100644 --- a/app/src/main/java/net/zetetic/tests/support/VerifyCipherProviderVersionTest.java +++ b/app/src/main/java/net/zetetic/tests/support/VerifyCipherProviderVersionTest.java @@ -12,7 +12,7 @@ public boolean execute(SQLiteDatabase database) { "PRAGMA cipher_provider_version;"); setMessage(String.format("Reported:%s", provider)); return provider.contains("OpenSSL 1.1.1") || - provider.contains("OpenSSL 1.0.2p-fips"); + provider.contains("OpenSSL 1.0.2u-fips"); } @Override diff --git a/app/src/main/java/net/zetetic/tests/support/VerifyUTF8EncodingForKeyTest.java b/app/src/main/java/net/zetetic/tests/support/VerifyUTF8EncodingForKeyTest.java index cd12950..6479702 100644 --- a/app/src/main/java/net/zetetic/tests/support/VerifyUTF8EncodingForKeyTest.java +++ b/app/src/main/java/net/zetetic/tests/support/VerifyUTF8EncodingForKeyTest.java @@ -3,7 +3,7 @@ import android.database.Cursor; import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabaseHook; -import net.sqlcipher.database.SQLiteException; +import android.database.sqlite.SQLiteException; import net.sqlcipher.database.SupportFactory; import net.zetetic.ZeteticApplication; import net.zetetic.tests.SQLCipherTest; diff --git a/app/src/main/res/layout/test_result_row.xml b/app/src/main/res/layout/test_result_row.xml index 2397f87..0bb3e90 100644 --- a/app/src/main/res/layout/test_result_row.xml +++ b/app/src/main/res/layout/test_result_row.xml @@ -25,8 +25,7 @@ android:layout_width="fill_parent" android:gravity="left" android:textSize="14sp" - android:singleLine="true" - android:ellipsize="end" + android:singleLine="false" android:layout_below="@+id/test_name" android:layout_toLeftOf="@+id/test_status"/> \ No newline at end of file diff --git a/build.gradle b/build.gradle index da98637..c4cc1cb 100644 --- a/build.gradle +++ b/build.gradle @@ -2,11 +2,11 @@ buildscript { repositories { - jcenter() - google() + google() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.1' + classpath 'com.android.tools.build:gradle:8.5.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -16,7 +16,7 @@ buildscript { allprojects { repositories { google() - jcenter() + mavenCentral() flatDir { dirs 'libs' } diff --git a/gradle.properties b/gradle.properties index aac7c9b..7ce2e68 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,3 +15,8 @@ org.gradle.jvmargs=-Xmx1536m # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true + +android.useAndroidX=true +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1605817..c2537d9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Feb 11 11:42:45 CST 2019 +#Mon Sep 21 10:18:41 CDT 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip