encrypted-datastore
Extensions to store DataStore into EncryptedFile
.
[!WARNING] This library will continue to be maintained, but active development will cease when an official DataStore encryption solution is released by Google. Support the development of this feature by voting for it on the issue tracker: b/167697691.
Installation
Add the dependency:
repositories {
mavenCentral()
google()
}
dependencies {
implementation("io.github.osipxd:security-crypto-datastore:1.1.1-beta03")
// Or, if you want to use Preferences DataStore:
implementation("io.github.osipxd:security-crypto-datastore-preferences:1.1.1-beta03")
}
Dependencies:
[!NOTE] Ensure that the version of this library aligns with the DataStore library version used in your project.
Usage
To create an encrypted DataStore, simply replace the dataStore
method with encryptedDataStore
when setting up your delegate:
// At the top level of your Kotlin file:
val Context.settingsDataStore: DataStore<Settings> by encryptedDataStore(
fileName = "settings.pb",
serializer = SettingsSerializer
)
Or, if you want full control over EncryptedFile
creation
kotlin
val settingsDataStore: DataStore<Settings> = DataStoreFactory.createEncrypted(SettingsSerializer) {
EncryptedFile.Builder(
context.dataStoreFile("settings.pb"),
context,
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
}
Similarly, you can create Preferences DataStore:
// At the top level of your Kotlin file:
val Context.dataStore by encryptedPreferencesDataStore(name = "settings")
Or, if you want full control over EncryptedFile
creation
kotlin
val dataStore: DataStore<Preferences> = PreferenceDataStoreFactory.createEncrypted {
EncryptedFile.Builder(
context.preferencesDataStoreFile("settings"),
context,
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
}
Once your encrypted DataStore is configured, you can use it in the same manner as a regular DataStore. For usage guides and examples, refer to the DataStore documentation.
Migration
Migrating from DataStoreFactory to delegate usage
If you are starting with the following code:
val dataStore = DataStoreFactory.createEncrypted(serializer) {
EncryptedFile.Builder(
context.dataStoreFile("filename"),
context,
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
}
To simplify the creation of DataStore using a delegate, follow these steps:
- Move the field to the top level of your Kotlin file and convert it into an extension on
Context
. - Replace
DataStoreFactory.createEncrypted
withencryptedDataStore
.
val Context.dataStore by encryptedDataStore(
fileName = "filename", // Keep file the same
serializer = serializer,
)
[!NOTE] This only will be interchangeable if you have used
context.dataStoreFile(...)
to create the datastore file. If you have custom logic for master key creation, ensure to pass the created master key as themasterKey
parameter to the delegate.
Migrating from encrypted-datastore
to security-crypto-datastore
Change the dependency in build script:
dependencies {
- implementation("io.github.osipxd:encrypted-datastore:...")
+ implementation("io.github.osipxd:security-crypto-datastore:...")
}
New library uses StreamingAead
instead of Aead
under the hood, so to not lose the previously encrypted data you should specify fallbackAead
:
// This AEAD was used to encrypt DataStore previously, we will use it as fallback
val aead = AndroidKeysetManager.Builder()
.withSharedPref(context, "master_keyset", "master_key_preference")
.withKeyTemplate(KeyTemplates.get("AES256_GCM"))
.withMasterKeyUri("android-keystore://master_key")
.build()
.keysetHandle
.getPrimitive(Aead::class.java)
The old code to create DataStore was looking like this:
val dataStore = DataStoreFactory.create(serializer.encrypted(aead)) {
context.dataStoreFile("filename")
}
The new code will look like this:
// At the top level of your Kotlin file:
val Context.dataStore by encryptedDataStore(
fileName = "filename", // Keep file the same
serializer = serializer,
encryptionOptions = {
// Specify fallback Aead to make it possible to decrypt data encrypted with it
fallbackAead = aead
}
)
Or, if you want full control over EncryptedFile
creation
kotlin
val dataStore = DataStoreFactory.createEncrypted(
serializer = serializer,
encryptionOptions = { fallbackAead = aead }
) {
EncryptedFile.Builder(
context.dataStoreFile("filename"), // Keep file the same
context,
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
}
Thanks
- Artem Kulakov (Fi5t), for his example of DataStore encryption.
- Gods of Kotlin, for posibility to hack
internal
visibility modifier