com.abubusoft.kripton.android.sqlcipher.KriptonSQLCipherHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kripton-orm Show documentation
Show all versions of kripton-orm Show documentation
Kripton Persistence Library - ORM module
The newest version!
/*
* Copyright (C) 2016 The Android Open Source Project
* Modifications Copyright (c) 2017 CommonsWare, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.abubusoft.kripton.android.sqlcipher;
import com.abubusoft.kripton.android.Logger;
import android.content.Context;
import android.os.Build;
import net.sqlcipher.DatabaseErrorHandler;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;
import net.sqlcipher.database.SQLiteException;
import net.sqlcipher.database.SQLiteOpenHelper;
import androidx.annotation.RequiresApi;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
/**
* SupportSQLiteOpenHelper implementation that works with SQLCipher for Android
*/
class KriptonSQLCipherHelper implements SupportSQLiteOpenHelper {
private final OpenHelper delegate;
private final byte[] passphrase;
private final boolean clearPassphrase;
private final boolean requiredPassphrase;
KriptonSQLCipherHelper(Context context, String name, Callback callback, byte[] passphrase,
KriptonSQLCipherHelperFactory.Options options) {
SQLiteDatabase.loadLibs(context);
clearPassphrase = options.clearPassphrase;
delegate = createDelegate(context, name, callback, options);
this.passphrase = passphrase;
this.requiredPassphrase = options.requiredPassphrase;
}
private OpenHelper createDelegate(Context context, String name, final Callback callback,
KriptonSQLCipherHelperFactory.Options options) {
final Database[] dbRef = new Database[1];
return (new OpenHelper(context, name, dbRef, callback, options));
}
/**
* {@inheritDoc}
*/
@Override
synchronized public String getDatabaseName() {
return delegate.getDatabaseName();
}
/**
* {@inheritDoc}
*/
@Override
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
synchronized public void setWriteAheadLoggingEnabled(boolean enabled) {
delegate.setWriteAheadLoggingEnabled(enabled);
}
/**
* {@inheritDoc}
*
* NOTE: by default, this implementation zeros out the passphrase after
* opening the database
*/
@Override
synchronized public SupportSQLiteDatabase getWritableDatabase() {
SupportSQLiteDatabase result;
// if we don't have a passphrase, an exception will be thrown
if (requiredPassphrase && passphrase == null) {
Logger.fatal("Try to open ciphered database %s without any passphrase", getDatabaseName());
throw new SQLCipherPassphraseRequiredException();
}
try {
result = delegate.getWritableSupportDatabase(passphrase);
} catch (SQLiteException e) {
if (passphrase != null) {
boolean isCleared = true;
for (byte b : passphrase) {
isCleared = isCleared && (b == (byte) 0);
}
if (isCleared) {
throw new IllegalStateException("The passphrase appears to be cleared. This happens by"
+ "default the first time you use the factory to open a database, so we can remove the"
+ "cleartext passphrase from memory. If you close the database yourself, please use a"
+ "fresh SafeHelperFactory to reopen it. If something else (e.g., Room) closed the"
+ "database, and you cannot control that, use SafeHelperFactory.Options to opt out of"
+ "the automatic password clearing step. See the project README for more information.");
}
}
throw e;
}
if (clearPassphrase && passphrase != null) {
for (int i = 0; i < passphrase.length; i++) {
passphrase[i] = (byte) 0;
}
}
return (result);
}
/**
* {@inheritDoc}
*
* NOTE: this implementation delegates to getWritableDatabase(), to ensure
* that we only need the passphrase once
*/
@Override
public SupportSQLiteDatabase getReadableDatabase() {
return (getWritableDatabase());
}
/**
* {@inheritDoc}
*/
@Override
synchronized public void close() {
delegate.close();
}
static class OpenHelper extends SQLiteOpenHelper {
private final Database[] dbRef;
private volatile Callback callback;
private volatile boolean migrated;
OpenHelper(Context context, String name, Database[] dbRef, Callback callback,
KriptonSQLCipherHelperFactory.Options options) {
super(context, name, null, callback.version, new SQLiteDatabaseHook() {
@Override
public void preKey(SQLiteDatabase database) {
if (options != null && options.preKeySql != null) {
database.rawExecSQL(options.preKeySql);
}
}
@Override
public void postKey(SQLiteDatabase database) {
if (options != null && options.postKeySql != null) {
database.rawExecSQL(options.postKeySql);
}
}
}, new DatabaseErrorHandler() {
@Override
public void onCorruption(SQLiteDatabase dbObj) {
Database db = dbRef[0];
if (db != null) {
callback.onCorruption(db);
}
}
});
this.dbRef = dbRef;
this.callback = callback;
}
synchronized SupportSQLiteDatabase getWritableSupportDatabase(byte[] passphrase) {
migrated = false;
SQLiteDatabase db = super.getWritableDatabase(passphrase);
if (migrated) {
close();
return getWritableSupportDatabase(passphrase);
}
return getWrappedDb(db);
}
synchronized Database getWrappedDb(SQLiteDatabase db) {
Database wrappedDb = dbRef[0];
if (wrappedDb == null) {
wrappedDb = new Database(db);
dbRef[0] = wrappedDb;
}
return (dbRef[0]);
}
/**
* {@inheritDoc}
*/
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
callback.onCreate(getWrappedDb(sqLiteDatabase));
}
/**
* {@inheritDoc}
*/
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
migrated = true;
callback.onUpgrade(getWrappedDb(sqLiteDatabase), oldVersion, newVersion);
}
/**
* {@inheritDoc}
*/
@Override
public void onConfigure(SQLiteDatabase db) {
callback.onConfigure(getWrappedDb(db));
}
/**
* {@inheritDoc}
*/
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
migrated = true;
callback.onDowngrade(getWrappedDb(db), oldVersion, newVersion);
}
/**
* {@inheritDoc}
*/
@Override
public void onOpen(SQLiteDatabase db) {
if (!migrated) {
// from Google: "if we've migrated, we'll re-open the db so we
// should not call the callback."
callback.onOpen(getWrappedDb(db));
}
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void close() {
super.close();
dbRef[0] = null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy