Extensions to encrypt DataStore using Tink

Overview

encrypted-datastore

Version License

Extensions to encrypt DataStore using Tink.

⚠️ This tiny library will be maintained until an official solution for DataStore encryption will be released by Google.


Installation

Add the dependency:

repositories {
    mavenCentral()
    google()
}

dependencies {
    implementation("io.github.osipxd:encrypted-datastore:1.0.0-alpha02")
}

Usage

First, you need Aead object to encrypt DataStore or you may use already created one:

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)

Then you can make any DataStore Serializer encrypted using extension-function Serializer .encrypted(Aead) :

object ProtoProfileSerializer : Serializer<Profile> {
    // serializer implementation here
}

val dataStore = DataStoreFactory.create(ProtoProfileSerializer.encrypted(aead)) {
    context.dataStoreFile("proto_profile")
}

If you need to create encrypted PreferenceDataStore, use function createEncrypted instead of create:

val prefsDataStore = PreferenceDataStoreFactory.createEncrypted(aead) {
    context.preferencesDataStoreFile("user_preferences")
}

Thanks

  • Artem Kulakov (Fi5t), for his example of DataStore encryption.
  • God, for posibility to hack Kotlin internal visibility modifier

License

MIT

Comments
  • Decrypting from stored protocol buffer file fails

    Decrypting from stored protocol buffer file fails

    After writing a protobuf file and reopening the app, reading the encrypted file fails with the following exception:

      Caused by: java.io.IOException: No matching key found for the ciphertext in the stream.
                     	at com.google.crypto.tink.streamingaead.InputStreamDecrypter.read(InputStreamDecrypter.java:176)
                     	at com.google.protobuf.CodedInputStream$StreamDecoder.read(CodedInputStream.java:2080)
                     	at com.google.protobuf.CodedInputStream$StreamDecoder.tryRefillBuffer(CodedInputStream.java:2831)
                     	at com.google.protobuf.CodedInputStream$StreamDecoder.isAtEnd(CodedInputStream.java:2754)
                     	at com.google.protobuf.CodedInputStream$StreamDecoder.readTag(CodedInputStream.java:2107)
                     	at com.google.protobuf.CodedInputStreamReader.getFieldNumber(CodedInputStreamReader.java:82)
                     	at com.google.protobuf.MessageSchema.mergeFromHelper(MessageSchema.java:3913)
                     	at com.google.protobuf.MessageSchema.mergeFrom(MessageSchema.java:3895)
                     	at com.google.protobuf.GeneratedMessageLite.parsePartialFrom(GeneratedMessageLite.java:1647)
    

    I am using v1.0.0-alpha03 of the library.

    Among other cases this type of error occurs when the outputstream the file is written to is not closed. This can be observed when using the regular EncryptedFile and not closing its outpur stream after write. Loading the file again fails with the same exception.

    However looking at the datastore implementation... they make use if the file.use { ... } function which would indicate a close operation. Feedback is appreciated.

    bug 
    opened by mpost 11
  • when i declare the preference datastore as it is on the documentation, it says

    when i declare the preference datastore as it is on the documentation, it says "no value passed for encryptionOptions"

     val dataStore = getMasterKey(context)?.let {
                    PreferenceDataStoreFactory.createEncrypted {
                        EncryptedFile(
                            context,
                            context.dataStoreFile(prefName!!),
                            masterKey = it
                        )
                    }
                }
    
    bug 
    opened by RhymezxCode 4
  • Preferences DataStore crashes if obfuscation enabled

    Preferences DataStore crashes if obfuscation enabled

    Stacktrace

    Fatal Exception: java.lang.RuntimeException: Field value_ for e4.c not found. Known fields are [public int e4.c.e, public java.lang.Object e4.c.f, public static final e4.c e4.c.g, public static volatile g4.x$b e4.c.h]
           at androidx.datastore.preferences.protobuf.MessageSchema.reflectField(MessageSchema.java:608)
           at androidx.datastore.preferences.protobuf.MessageSchema.newSchemaForRawMessageInfo(MessageSchema.java:479)
           at androidx.datastore.preferences.protobuf.MessageSchema.newSchema(MessageSchema.java:221)
           at androidx.datastore.preferences.protobuf.ManifestSchemaFactory.newSchema(ManifestSchemaFactory.java:77)
           at androidx.datastore.preferences.protobuf.ManifestSchemaFactory.createSchema(ManifestSchemaFactory.java:71)
           at androidx.datastore.preferences.protobuf.Protobuf.schemaFor(Protobuf.java:93)
           at androidx.datastore.preferences.protobuf.Protobuf.schemaFor(Protobuf.java:107)
           at androidx.datastore.preferences.protobuf.GeneratedMessageLite.makeImmutable(GeneratedMessageLite.java:170)
           at androidx.datastore.preferences.protobuf.GeneratedMessageLite$Builder.buildPartial(GeneratedMessageLite.java:386)
           at androidx.datastore.preferences.protobuf.GeneratedMessageLite$Builder.build(GeneratedMessageLite.java:394)
           at androidx.datastore.preferences.core.PreferencesSerializer.getValueProto(PreferencesSerializer.kt:76)
           at androidx.datastore.preferences.core.PreferencesSerializer.writeTo(PreferencesSerializer.kt:63)
           at androidx.datastore.preferences.core.PreferencesSerializer.writeTo(PreferencesSerializer.kt:36)
           at io.github.osipxd.datastore.encrypted.EncryptedSerializer.writeTo(EncryptedSerializer.kt:23)
           at androidx.datastore.core.SingleProcessDataStore.writeData$datastore_core(SingleProcessDataStore.kt:426)
           at androidx.datastore.core.SingleProcessDataStore.transformAndWrite(SingleProcessDataStore.kt:410)
           at androidx.datastore.core.SingleProcessDataStore$transformAndWrite$1.invokeSuspend(SingleProcessDataStore.kt:14)
           at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
           at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
           at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
           at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
           at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.java:570)
           at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
           at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
           at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
    

    Workarounds

    1. Add dependency androidx.datastore:datastore-preferences to build script
    2. Add proguard rules:
      -keepclassmembers class androidx.datastore.preferences.PreferencesProto$Value {
           private java.lang.Object value_;
           private int valueCase_;
      }
      
      -keepclassmembers class * extends androidx.datastore.preferences.protobuf.GeneratedMessageLite {
          <fields>;
      }
      
    opened by osipxd 1
  • GeneralSecurityException: decryption failed

    GeneralSecurityException: decryption failed

    Fatal Exception: java.security.GeneralSecurityException: decryption failed
           at com.google.crypto.tink.aead.AeadWrapper$WrappedAead.decrypt(AeadWrapper.java:83)
           at io.github.osipxd.datastore.encrypted.EncryptedSerializer.readFrom(EncryptedSerializer.kt:16)
           at androidx.datastore.core.SingleProcessDataStore.readData(SingleProcessDataStore.kt:381)
           at androidx.datastore.core.SingleProcessDataStore.readDataOrHandleCorruption(SingleProcessDataStore.kt:359)
           at androidx.datastore.core.SingleProcessDataStore.readAndInit(SingleProcessDataStore.kt:322)
           at androidx.datastore.core.SingleProcessDataStore.readAndInitOrPropagateFailure(SingleProcessDataStore.kt:311)
           at androidx.datastore.core.SingleProcessDataStore.handleRead(SingleProcessDataStore.kt:261)
           at androidx.datastore.core.SingleProcessDataStore.access$getFile(SingleProcessDataStore.kt:76)
           at androidx.datastore.core.SingleProcessDataStore$actor$3.invokeSuspend(SingleProcessDataStore.kt:239)
           at androidx.datastore.core.SingleProcessDataStore$actor$3.invoke(SingleProcessDataStore.kt)
           at androidx.datastore.core.SingleProcessDataStore$actor$3.invoke(SingleProcessDataStore.kt)
           at androidx.datastore.core.SimpleActor$offer$2.invokeSuspend(SimpleActor.kt:122)
           at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
           at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
           at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
           at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
           at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
           at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
           at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
           at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
    
    opened by osipxd 1
  • New library `security-crypto-datastore`

    New library `security-crypto-datastore`

    New library provides more simple and less error-prone API to create encrypted DataStores. All Tink-related stuff hidden from you in security-crypto library, and all you should do is wrap File with EncryptedFile:

    val dataStore = DataStoreFactory.createEncrypted(serializer) {
        EncryptedFile.Builder(
            context.dataStoreFile("filename"),
            context,
            MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
            EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
        ).build()
    }
    
    Or even simpler, if you use security-crypto-ktx:1.1.0
    val dataStore = DataStoreFactory.createEncrypted(serializer) {
        EncryptedFile(
            context = context,
            file = context.dataStoreFile("filename"),
            masterKey = MasterKey(context)
        )
    }
    

    Closes #7

    opened by osipxd 0
  • StreamingAead support

    StreamingAead support

    Introduced new extension-function Serializer.encrypted(StreamingAead) to encrypt DataStore in streaming manneer. Old extension-function with Aead is not planned to be removed yet, but for all new code it is recommended to use the new function. You can obtain StreamingAead similar to Aead:

    // Remember to initialize Tink
    //AeadConfig.register()
    StreamingAeadConfig.register()
    
    val handle = AndroidKeysetManager.Builder()
        .withSharedPref(context, "master_keyset", "master_key_preference")
        // Change key template AES256_GCM -> AES256_GCM_HKDF_4KB
        //.withKeyTemplate(KeyTemplates.get("AES256_GCM"))
        .withKeyTemplate(KeyTemplates.get("AES256_GCM_HKDF_4KB"))
        .withMasterKeyUri("android-keystore://master_key")
        .build()
        .keysetHandle
    
    // Get StreamingAead instead of Aead
    //val aead = handle.getPrimitive(Aead::class.java)
    val streamingAead = handle.getPrimitive(StreamingAead::class.java)
    

    ATTENTION! You can not use StreamingAead to decrypt data encrypted with Aead, so you can not just replace Aead with StreamingAead without migration. To not lose your previously encrypted data, you have three options:

    1. Migration - add fallback for StreamingAead using function StreamingAead.withDecryptionFallback(Aead)
    2. Do nothing - continue to use Aead
    3. Destructive migration - specify CorruptionHandler to replace old content with something else
    opened by osipxd 0
  • Move PreferencesDataStore extension to separated module

    Move PreferencesDataStore extension to separated module

    Breaking change: PreferenceDataStoreFactory.createEncrypted extension has been moved to separated module. To continue use it, change the dependency module in your build script:

    -implmentation("io.github.osipxd:encrypted-datastore:...")
    +implmentation("io.github.osipxd:encrypted-datastore-preferences:...")
    
    opened by osipxd 0
  • Version for datastore 1.1.0

    Version for datastore 1.1.0

    DataStore 1.1.0 contains breaking changes:

    • Exposed PreferencesSerializer, previously it was internal
    • Library is multiplatform now, so it may require changes on my side
    opened by osipxd 0
Releases(v1.0.0-alpha04)
  • v1.0.0-alpha04(Dec 19, 2022)

    Fixed

    • Fixed the case when data can not be read if output stream was not closed in serializer (#10) by @osipxd
    • Added default value for parameter encryptionOptions in PreferenceDataStoreFactory.createEncrypted (#12) by @osipxd

    Full Changelog: https://github.com/osipxd/encrypted-datastore/compare/v1.0.0-alpha03...v1.0.0-alpha04

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-alpha03(Nov 18, 2022)

    More high-level library security-crypto-datastore

    New library provides more simple and less error-prone API to create encrypted DataStores. All Tink-related stuff hidden from you in security-crypto library, and all you should do is wrap File with EncryptedFile:

    val dataStore = DataStoreFactory.createEncrypted(serializer) {
        EncryptedFile.Builder(
            context.dataStoreFile("filename"),
            context,
            MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
            EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
        ).build()
    }
    

    Or even simpler, if you use security-crypto-ktx:1.1.0:

    val dataStore = DataStoreFactory.createEncrypted(serializer) {
        EncryptedFile(
            context = context,
            file = context.dataStoreFile("filename"),
            masterKey = MasterKey(context)
        )
    }
    

    See the Migration guide.

    Streaming serializer

    Introduced new extension-function Serializer.encrypted(StreamingAead) to encrypt DataStore in streaming manner. Old extension-function with Aead is not planned to be removed yet, but for all new code it is recommended to use the new function or migrate to the security-crypto-datastore.

    ATTENTION! You can not use StreamingAead to decrypt data encrypted with Aead, so you can not just replace Aead with StreamingAead without migration. To not lose your previously encrypted data, you have three options:

    1. Migration - add fallback for StreamingAead using function StreamingAead.withDecryptionFallback(Aead)
    2. Do nothing - continue to use Aead
    3. Destructive migration - specify CorruptionHandler to replace old content with something else

    New module encrypted-datastore-preferences

    :warning: Breaking change:

    All stuff related to Preference DataStore was moved to io.github.osipxd:encrypted-datastore-preferences. To continue use it, change the dependency module in your build script:

    -implmentation("io.github.osipxd:encrypted-datastore:...")
    +implmentation("io.github.osipxd:encrypted-datastore-preferences:...")
    

    Fixed

    • Fixed crash when DataStore can not be decrypted (#1)

    Dependencies

    • Kotlin 1.5.301.7.21
    • Tink 1.6.11.7.0

    Housekeeping

    • Gradle 7.27.5.1
    • gradle-infrastructure 0.12.10.17
    • Migrate dependencies to version catalogs

    Full Changelog: https://github.com/osipxd/encrypted-datastore/compare/v1.0.0-alpha02...v1.0.0-alpha03

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-alpha02(Oct 12, 2021)

  • v1.0.0-alpha01(Oct 12, 2021)

Owner
Osip Fatkullin
Osip Fatkullin
Android library to easily serialize and cache your objects to disk using key/value pairs.

Deprecated This project is no longer maintained. No new issues or pull requests will be accepted. You can still use the source or fork the project to

Anup Cowkur 667 Dec 22, 2022
A simple library for validating user input in forms using annotations.

ValidationKomensky for Android A simple library for validating user input in forms using annotations. Features: Validate all views at once and show fe

Inmite s.r.o. 512 Nov 20, 2022
A set of helper classes for using dagger 1 with Android components such as Applications, Activities, Fragments, BroadcastReceivers, and Services.

##fb-android-dagger A set of helper classes for using dagger with Android components such as Applications, Activities, Fragments, BroadcastReceivers,

Andy Dennie 283 Nov 11, 2022
Android Secure SharedPreferences Using Facebook Conceal Encryption

SharedChamber Android Project : SharedChamber on top of SharedPreferences using Facebook Conceal Description Conceal provides a set of Java APIs to pe

Hafiq 95 Nov 25, 2022
A lightweight library for config and using SharedPreferences

preferences-helper SharePreferences is very popular with any project and all most all project has SharePreferences for saving data. This library will

Khang Tran 23 May 8, 2021
Trail is a simple logging system for Java and Android. Create logs using the same API and the library will detect automatically in which platform the code is running.

Trail Trail is a simple logging system for Java and Android. Create logs using the same API and the library will detect automatically in which platfor

Mauricio Togneri 13 Aug 29, 2022
recompose is a tool for converting Android layouts in XML to Kotlin code using Jetpack Compose.

recompose is a tool for converting Android layouts in XML to Kotlin code using Jetpack Compose.

Sebastian Kaspari 565 Jan 2, 2023
This project is an add-on for the excellent J2V8 Project. It allows users to debug JS running in V8 using Chrome DevTools. Uses Stetho for communication with Chrome DevTools.

J2V8-Debugger This project is an add-on for the excellent J2V8 Project. It allows users to debug JS running in V8 using Chrome DevTools. Uses Stetho f

Alex Trotsenko 76 Jan 3, 2023
Generate helper methods for compose navigation using KSP

Compose NavGen Generate helper methods for compose navigation using KSP. ?? You can try it now, but it's still under development. ?? TODO Support defa

Kenji Abe 6 Feb 5, 2022
Format numbers using a string pattern with this simple number formatted like ##-####-##

AndroidPattern Format numbers using a string pattern with this simple number formatted like ##-####-## Installation To get a Git project into your bui

Hussein Habibi Juybari 2 Oct 25, 2021
Android injection using the Anvil compiler plugin

Tangle creates Dagger bindings for Android classes using the Anvil Kotlin compiler plugin. This is meant to be an alternative to Hilt, for those who'd prefer to enjoy the faster compilation and better flexibility of Anvil.

Rick Busarow 67 Dec 29, 2022
DEMOMovieDB - Client App using movieDB with Kotlin

DEMOMovieDB DEMOMovieDB is a gorgeous client application for TMDb on Android, bu

null 0 Feb 15, 2022
λRPC allows using code with high-order functions as a service

λRPC Simple native RPC with high order functions support. Inspired by @altavir and Communicator. λRPC allows using code with high-order functions as a

Andrey Stoyan 5 May 18, 2022
A universal memory dumper using Frida

Fridump Fridump (v0.1) is an open source memory dumping tool, primarily aimed to penetration testers and developers. Fridump is using the Frida framew

null 547 Dec 22, 2022
Sample project displaying process of OTP validation using firebase

OTP-Validation-using-firebase Sample project displaying process of OTP validation using firebase Screenshots Concepts used Integrated Firebase sdk for

Ankita Gaba 2 Jun 18, 2022
Astha Nayak 4 Oct 10, 2022
Gits-android-extensions - A collection of Kotlin extensions to simplify Android development

gits-android-extensions A collection of Kotlin extensions to simplify Android de

GITS Indonesia 3 Feb 3, 2022
Wallpaper app made using Hilt, Retrofit, Room, Navigation Components, MVI, Coroutines, Flows, ViewModel, LiveData, Datastore Preference.

Android Picture Engine Wallpaper app made using Hilt, Retrofit, Room, Navigation Components, MVI, Coroutines, Flows, ViewModel, LiveData, Datastore Pr

Simone Conigliaro 59 Sep 27, 2022
A simple way to encrypt your secure data like passwords into a native .so library.

PLEASE NOTE, THIS PROJECT IS NO LONGER BEING MAINTAINED Cipher.so Providing a simple way to keep your secure info safe for android app development. Wi

lin 1.3k Dec 30, 2022
A simple and opinionated AES encrypt / decrypt Ruby gem that just works.

AESCrypt - Simple AES encryption / decryption for Ruby AESCrypt is a simple to use, opinionated AES encryption / decryption Ruby gem that just works.

Gurpartap Singh 158 Oct 18, 2022