feat: add secure-storage module (Tink + Android Keystore)#33
Merged
Conversation
Adds io.github.maniramezan.compose:secure-storage, an encrypted key-value store backed by Google Tink AEAD with a master key sealed in the Android Keystore — the Android equivalent of iOS Keychain for storing small secrets (tokens, session metadata, flags) that must survive process death. - Public SecureStorage interface + AeadSecureStorage (AES256-GCM, key bound as associated data) + Keystore-backed factory via SecureStorage.create(). - KeyValueStore seam keeps crypto unit-testable without the framework/Keystore; AeadSecureStorageTest covers round-trip, encrypted-at-rest, tamper, and key-binding using a software AEAD. - Wired into settings, version catalog (Tink 1.21.0), vanniktech POM, and the binary-compatibility ignore list (matches the other published modules).
Make the library testable by consumers without the Android Keystore: - Add InMemorySecureStorage, a public, dependency-free SecureStorage that consumers can use as a test double (or in previews) on a plain JVM. Document it from the SecureStorage interface KDoc. - Test the double's contract (round-trip, overwrite, remove/clear, defensive byte-array copies). - Add a Robolectric test for SharedPreferencesKeyValueStore (the persistence seam), pinned to @config(sdk = 35) like the other Robolectric tests.
- Expand KDoc on the SecureStorage public surface: security model (what it protects and what it doesn't), threading/lifetime, per-method @param/@return semantics, null-on-undecryptable read contract, defensive byte copies, and @throws/@return on create(). Verified link-clean via Dokka. - Add a Secure Storage docs-site page (install, quick start, how it works, multiple stores, error handling, testing with InMemorySecureStorage, API summary) and wire it into the mkdocs nav. - List the secure-storage artifact in Getting Started.
Record the decision to ship secure-storage as a standalone, non-Compose published module backed by Tink + Android Keystore (over deprecated EncryptedSharedPreferences), with a narrow SecureStorage interface and an InMemorySecureStorage test double. Links to ADR 0002 (module boundaries) and ADR 0003 (testing strategy); registered in the ADR index and mkdocs nav.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a new published module
io.github.maniramezan.compose:secure-storage— an encrypted key-value store backed by Google Tink AEAD with a master key sealed in the Android Keystore. It's the Android equivalent of iOS Keychain for small secrets (tokens, session metadata, feature flags) that must survive process death.Replaces the need for the deprecated
androidx.security:security-crypto(EncryptedSharedPreferences, sunset April 2024).API
SecureStorageinterface +AeadSecureStorage(AES256-GCM; the entry key is bound as associated data, so ciphertext can't be replayed under another key).KeyValueStoreseam keeps the crypto unit-testable without the Android framework or Keystore.Consumer testing
Consumers depend on the
SecureStorageinterface (notcreate()) and injectInMemorySecureStoragein tests — public, dependency-free, pure-JVM, no Keystore/Robolectric required:Tests (16, all green)
AeadSecureStorageTest(8) — round-trip, encrypted-at-rest, overwrite, remove, clear, tamper→null, key-binding (software AEAD + in-memory store).InMemorySecureStorageTest(5) — test-double contract + defensive byte-array copies.SharedPreferencesKeyValueStoreTest(3) — persistence seam via Robolectric (@Config(sdk = 35)).SecureStorage.create()Keystore wiring — belongs in an instrumented test, not a fake.Wiring
settings.gradle.ktsinclude, version catalog (Tink 1.21.0), vanniktech POM metadata (secure-storage/gradle.properties).Release
First consumer is the Novalingo Android app (Firebase auth session-hint persistence). Landing this
feat:lets release-please cut 0.7.0 and publish to Maven Central.Verification (local, mirrors CI)
./gradlew check✅:catalog:assembleDebug :sample:assembleDebug :baselineprofile:assembleDebug✅mkdocs build --strict✅