This project implements the Support...
series of classes and interfaces
that Room
can use for working with a particular edition of SQLite. Specficially, this
project's classes connect Room with SQLCipher for Android,
a version of SQLite that offers transparent encryption of its contents.
Right now,
this project is for experimentation purposes and for helping to prove the
practicality of the Support...
class setup. Do not use this in production
applications just yet.
There are two versions of this library, for AndroidX and for the older Android Support Library.
If you cannot use SSL, use http://repo.commonsware.com
for the repository URL.
repositories {
maven {
url "https://s3.amazonaws.com/repo.commonsware.com"
}
}
dependencies {
implementation "com.commonsware.cwac:saferoom.x:0.5.0"
}
repositories {
maven {
url "https://s3.amazonaws.com/repo.commonsware.com"
}
}
dependencies {
implementation "com.commonsware.cwac:saferoom:0.4.4"
}
When you use Room, you use Room.databaseBuilder()
or Room.inMemoryDatabaseBuilder()
to get a RoomDatabase.Builder
. After configuring that object, you call
build()
to get an instance of your custom subclass of RoomDatabase
, whichever
one that you supplied as a Java class object to the
Room.databaseBuilder()
or Room.inMemoryDatabaseBuilder()
method.
To use SafeRoom, on the RoomDatabase.Builder
, before calling build()
:
-
Create an instance of
com.commonsware.cwac.saferoom.SafeHelperFactory
, passing in the passphrase to use -
Pass that
SafeHelperFactory
to theRoomDatabase.Builder
via theopenHelperFactory()
method
// EditText passphraseField;
SafeHelperFactory factory=SafeHelperFactory.fromUser(passphraseField.getText());
StuffDatabase db=Room.databaseBuilder(ctxt, StuffDatabase.class, DB_NAME)
.openHelperFactory(factory)
.build();
A cardinal rule of passphrases in Java is: do not hold them in String
objects. You have no means of clearing those from memory, as a String
is an immutable value.
The SafeHelperFactory
constructor takes a char[]
for the passphrase. If
you are getting the passphrase from the user via an EditText
widget,
use the fromUser()
factory method instead, supplying the Editable
that you get from getText()
on the EditText
.
SafeRoom will zero out the char[]
once the database is opened. If you use
fromUser()
, SafeRoom will also clear the contents of the Editable
.
If you have an existing SQLite database — created with Room or
otherwise — the SQLCipherUtils
class has getDatabaseState()
and encrypt()
methods for you.
getDatabaseState()
returns a State
object indicating whether a database
is ENCRYPTED
, UNENCRYPTED
, or DOES_NOT_EXIST
. The determination
of whether the database is unencrypted is based on whether we can open it
without a passphrase. There are two versions of getDatabaseState()
:
-
getDatabaseState(Context, String)
for aContext
and database name -
getDatabaseState(File)
, where theFile
points to the database
encrypt()
will take an unencrypted database as input and encrypt it
using the supplied passphrase. Technically, it will encrypt a copy
of the database, then delete the unencrypted one and rename the copy to
the original name. There are three versions of encrypt()
:
-
encrypt(Context, String, Editable)
where theString
is the database name and theEditable
is the passphrase (e.g., fromgetText()
on anEditText
) -
encrypt(Context, String, char[])
where theString
is the database name and thechar[]
is the passphrase -
encrypt(Context, File, char[])
where theFile
points to the database and thechar[]
is the passphrase
The passphrase is left untouched by encrypt()
, so you can turn around and
use it with SafeHelperFactory
. If you are not planning on opening the database,
please clear out the passphrase after encrypt()
returns.
Only call encrypt()
when the database is closed. Ideally, call encrypt()
before opening the database in Room. At minimum, call close()
on your
RoomDatabase
before calling encrypt()
.
If you want to change the passphrase for an existing database:
-
Open it in writeable mode
-
Call
SafeHelperFactory.rekey()
, supplying that database plus either achar[]
or anEditable
reflecting the new passphrase to use
Note that this does not encrypt an unencrypted database. Use the encrypt()
option listed above for that.
The Editable
will be cleared as part of this work, but the char[]
will
not be zero'd out. Please clear that array as soon as you are done with it.
You can call decrypt()
on SQLCipherUtils
to decrypt an existing
SQLCipher-encrypted database. Supply the Context
, the File
pointing
to the database, and a char[]
with the passphrase. decrypt()
will
replace the encrypted database with a decrypted one, so that database can
be opened using ordinary SQLite.
As one might expect, this project depends on SQLCipher for Android.
This project also depends upon android.arch.persistence:db
(Android Support Library edition)
or androidx.sqlite:sqlite-framework
(AndroidX edition), which is
the support database API that Room uses.
The Android Support Library edition of CWAC-SafeRoom is frozen at supporting 1.1.1
of android.arch.persistence:db
.
The AndroidX edition of CWAC-SafeRoom supports 2.0.0
of androidx.sqlite:sqlite-framework
and should be updated to support newer versions of AndroidX over time.
This project has two sources of tests. Some are local to the project. The
rest come from the support-db-tests
project. That project contains tests that exercise any support database API
implementation.
TL;DR: to run the full set of CWAC-SafeRoom tests, use SafeRoomSuite
.
Either run that directly from your IDE, or set up a run configuration pointing
to it, etc.
This is version v0.5.0 of this module
Right now, there is no demo project.
JavaDocs are available,
though most of the library is not public
, as it does not need to be.
Android's Architecture Components contains a chapter dedicated to SafeRoom.
The code in this project is licensed under the Apache
Software License 2.0, per the terms of the included LICENSE
file. The copyrights are owned by CommonsWare for things unique to this
library and a combination of CommonsWare and the Android Open Source
Project for code modified from the Architecture Components' Framework*
set of classes.
If you have questions regarding the use of this code, please post a question
on Stack Overflow tagged with
commonsware-cwac
and android
after searching to see if there already is an answer. Be sure to indicate
what CWAC module you are having issues with, and be sure to include source code
and stack traces if you are encountering crashes.
If you have encountered what is clearly a bug, or if you have a feature request, please post an issue. Be certain to include complete steps for reproducing the issue. The contribution guidelines provide some suggestions for how to create a bug report that will get the problem fixed the fastest.
You are also welcome to join the CommonsWare Community and post questions and ideas to the CWAC category.
Do not ask for help via social media.
Also, if you plan on hacking on the code with an eye for contributing something back, please open an issue that we can use for discussing implementation details. Just lobbing a pull request over the fence may work, but it may not. Again, the contribution guidelines provide a bit of guidance here.
- v0.5.0: released AndroidX edition
- v0.4.4: addressed thread-safety issue
- v0.4.3: bumped
android.arch.persistence:db
dependency to1.1.1
- v0.4.2: fixed edge case WAL issue
- v0.4.1: added Room-specific tests, fixed WAL issue
- v0.4.0: updated to
1.1.0
of the support database API - v0.3.4: changed non-WAL journal mode to TRUNCATE
- v0.3.3: added WAL support, with an assist from plackemacher
- v0.3.2: added
decrypt()
utility method - v0.3.1: changed
rekey()
to use the existingchangePassword()
- v0.3.0: added
rekey()
, upgraded to SQLCipher for Android 3.5.9, replaced tests - v0.2.1: added temporary implementation of
getDatabaseName()
toHelper
- v0.2.0: added
SQLCipherUtils
to help encrypt existing databases - v0.1.3: upgraded to Android Gradle Plugin 3.0.0, set transitive dependencies to
api
- v0.1.2: fixed issue #3, related to closing statements
- v0.1.1: updated support database dependency to
1.0.0
- v0.1.0: eliminated Room dependency
- v0.0.4: raised Room dependencies to
1.0.0-beta1
and SQLCipher for Android to3.5.7
- v0.0.3: raised Room dependencies to
1.0.0-alpha8
- v0.0.2: raised Room dependencies to
1.0.0-alpha5
- v0.0.1: initial release