Skip to content

ADR-0010: SQLCipher + Android Keystore encryption

Context

The app stores sensitive notification text (amounts, merchants, account references). The data must be encrypted at rest on-device, with the key unavailable to other apps and unrecoverable after app-data wipe.

Decision

  • Room runs on SQLCipher (net.zetetic:sqlcipher-android) via SupportOpenHelperFactory (the app calls System.loadLibrary("sqlcipher") before opening).
  • The SQLCipher passphrase is a random 32-byte value generated on first launch, encrypted with an Android Keystore AES-GCM key (autobudget_sqlcipher_key, 256-bit, AES/GCM/NoPadding), stored base64 in autobudget_crypto SharedPreferences.
  • Backups are disabled: android:allowBackup="false" plus data_extraction_rules.xml / backup_rules.xml.

Consequences

  • The key material never leaves the device; the ciphertext and the key are destroyed together on app-data wipe or uninstall — history is permanently unrecoverable.
  • The DB cannot be read by other apps, and cannot be restored onto another device via backup.
  • sqlcipher-android ships 16 KB-aligned native libraries, so no build-time ELF patching is needed (see ADR-0011).

Alternatives considered

  • Plain Room — rejected: notification text at rest unencrypted.
  • Keystore-bound symmetric key as the passphrase directly — rejected: an attacker with the ciphertext + a way to invoke the Keystore could decrypt; wrapping a random passphrase adds a layer and survives key rotation.