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.
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.
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
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
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.
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 the masterKey
parameter to the delegate.
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:https://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
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()
}