mirror of
https://github.com/greenart7c3/Amber.git
synced 2026-10-05 10:58:23 +00:00
The per-account Room database (amber_db_<npub>) stored two NIP-46 secret values as cleartext TEXT columns: the bunker connection `secret` and the `localKey` — the latter being a full Nostr private key. Anyone with access to the app's internal storage (rooted device, privilege-escalating malware, or a future bug exporting the DB) could recover these values and bypass the Keystore protection the main account nsec enjoys (CWE-312). Fix: envelope-encrypt both columns with the existing Keystore-backed AES-256-GCM key (SecureCryptoHelper) before Room persistence, and decrypt on read, so every existing consumer continues to see the plaintext values it already expects. - SecureCryptoHelper: add non-suspend encryptBlocking/decryptBlocking so Migration.migrate() and getByKeySync() can call them without a runBlocking bridge; suspend variants now delegate to the blocking implementations. - ApplicationEntityCrypto.kt (new): encryptForStorage/decryptFromStorage mappers + DecryptingPagingSource. Sentinel rule: empty values stay "" at rest (matches the WebDAV password idiom in LocalPreferences.kt:661-681), preserving `WHERE localKey != ''` enumeration in NotificationSubscription and the `localPubKey` derivation on empty localKey. - ApplicationDao: split methods touching `secret`/`localKey` into Room- generated `*Raw` (encrypted columns) and default-method wrappers that apply the mappers. `getBySecret` rewritten to decrypt and filter in Kotlin (random GCM IV breaks `WHERE secret = :secret`). - CachingApplicationDao: add delegating `*Raw` overrides so the decorator still instantiates; cache logic unchanged. - AppDatabase: add MIGRATION_18_19 (in-place envelope-encrypt of existing plaintext rows via compiled statement + transaction; empty values stay empty). Bump @Database version to 19. - Backup/restore: no changes — ApplicationBackup.buildPayload reads via the wrapped DAO (plaintext) and the JSON is already NIP-44 encrypted by the account key; restore goes through the wrapped insert (auto-encrypts). - Tests: new androidTest ApplicationEntityCryptoTest covers round-trip, raw-column-ciphertext assertion, empty sentinel, localPubKey derivation, getAllWithLocalKey filter, getBySecret (hit/miss/empty), insertApplicationWithPermissions, getAll, and the MIGRATION_18_19 row re-encryption. Requires a device/emulator (AndroidKeyStore unavailable under JVM test). - New room-testing androidTestImplementation dependency. Verified: ktlintCheck, lint (no issues), testFreeDebugUnitTest (0 failures), compileFreeDebugAndroidTestKotlin, assembleFreeDebug, assembleOfflineDebug, and the offline merged manifest check (no INTERNET/ACCESS_NETWORK_STATE/ CHANGE_NETWORK_STATE permissions leaked).