Skip to content

Crashes immediately on startup on a Pixel running GrapheneOS after Keystore master key update in system #247

Description

@carstenraddatz

✍️ Describe the bug

The app crashes immediately on startup on a Pixel 9 Pro running GrapheneOS/Android 16. I gather this happens because EncryptedSharedPreferences fails to decrypt the stored data due to an invalidated or corrupted master key in the Android Keystore (likely triggered by a recent OS update).

Instead of handling the cryptographic failure gracefully, the app throws an unhandled RuntimeException during MainActivity initialization, locking the user into a permanent crash loop.

💣 Steps to reproduce

  1. Open the app after an Android system update or Keystore modification.
  2. The app crashes instantly during the launch/onCreate phase.

🔧 Expected behavior

The app should catch the cryptographic exception. Instead of crashing, it should restore from a previously auto-created backup file. This would allow to handle the failure gracefully (e.g., by wiping the corrupted encrypted preferences file and starting fresh Form the last known good file). This prevents the app from becoming completely unusable.

📷 Screenshots

Not applicable, as the app crashes instantly on launch.

📱 Tech info

  • Device: Pixel 9 Pro ("caiman")
  • OS: GrapheneOS Android 16 (Build: BP4A.260205.002 / 2026052401)
  • App Version: 1.4.0 (Package version code: 14, targetSdk 36, Installed via F-Droid)

📄 Additional context

Bug report created by a user with next to no Java/Kotlin knowledge.

An AI helped draft this patch

fun getEncryptedSharedPreferences(context: Context): SharedPreferences {
    return try {
        createEncryptedSharedPreferencesInstance(context)
    } catch (e: Exception) {
        if (e is GeneralSecurityException || e is IOException) {
            // Clear corrupted preferences file and master keys to recover
            context.deleteSharedPreferences(PREF_FILE_NAME)
            // Re-attempt creation
            createEncryptedSharedPreferencesInstance(context)
        } else {
            throw e
        }
    }
}

Crash Log / Stacktrace

type: crash
osVersion: google/caiman/caiman:16/BP4A.260205.002/2026052401:user/release-keys
flags: dev options enabled
package: rocks.poopjournal.fucksgiven:14, targetSdk 36
process: rocks.poopjournal.fucksgiven
processUptime: 66 + 44 ms
installer: org.fdroid.fdroid

java.lang.RuntimeException: Unable to start activity ComponentInfo{rocks.poopjournal.fucksgiven/rocks.poopjournal.fucksgiven.MainActivity}
	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4517)
	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4720)
	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:224)
	at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:133)
	at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:103)
	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:80)
	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2973)
	at android.os.Handler.dispatchMessage(Handler.java:132)
	at android.os.Looper.dispatchMessage(Looper.java:333)
	at android.os.Looper.loopOnce(Looper.java:263)
	at android.os.Looper.loop(Looper.java:367)
	at android.app.ActivityThread.main(ActivityThread.java:9332)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:566)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:955)
Caused by: javax.crypto.AEADBadTagException
	at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:632)
	at javax.crypto.Cipher.doFinal(Cipher.java:2132)
	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decryptInternal(AndroidKeystoreAesGcm.java:118)
	at com.google.crypto.tink.integration.android.AndroidKeystoreAesGcm.decrypt(AndroidKeystoreAesGcm.java:101)
	at com.google.crypto.tink.KeysetHandle.decrypt(KeysetHandle.java:919)
	at com.google.crypto.tink.KeysetHandle.readWithAssociatedData(KeysetHandle.java:804)
	at com.google.crypto.tink.KeysetHandle.read(KeysetHandle.java:785)
	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.readMasterkeyDecryptAndParseKeyset(AndroidKeysetManager.java:381)
	at com.google.crypto.tink.integration.android.AndroidKeysetManager$Builder.build(AndroidKeysetManager.java:297)
	at androidx.security.crypto.EncryptedSharedPreferences.create(EncryptedSharedPreferences.java:169)
	at rocks.poopjournal.fucksgiven.data.SecureStorage.getEncryptedSharedPreferences(SecureStorage.kt:30)
	at rocks.poopjournal.fucksgiven.data.SecureStorage.<init>(SecureStorage.kt:21)
	at rocks.poopjournal.fucksgiven.DaggerFucksGivenApplication_HiltComponents_SingletonC$SingletonCImpl$SwitchingProvider.get(DaggerFucksGivenApplication_HiltComponents_SingletonC.java:663)
	at dagger.internal.DoubleCheck.getSynchronized(DoubleCheck.java:54)
	at dagger.internal.DoubleCheck.get(DoubleCheck.java:45)
	at rocks.poopjournal.fucksgiven.DaggerFucksGivenApplication_HiltComponents_SingletonC$ActivityCImpl.injectMainActivity2(DaggerFucksGivenApplication_HiltComponents_SingletonC.java:418)
	at rocks.poopjournal.fucksgiven.DaggerFucksGivenApplication_HiltComponents_SingletonC$ActivityCImpl.injectMainActivity(DaggerFucksGivenApplication_HiltComponents_SingletonC.java:413)
	at rocks.poopjournal.fucksgiven.Hilt_MainActivity.inject(Hilt_MainActivity.java:100)
	at rocks.poopjournal.fucksgiven.Hilt_MainActivity$1.onContextAvailable(Hilt_MainActivity.java:47)
	at androidx.activity.contextaware.ContextAwareHelper.dispatchOnContextAvailable(ContextAwareHelper.kt:78)
	at androidx.activity.ComponentActivity.onCreate(ComponentActivity.kt:327)
	at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:217)
	at rocks.poopjournal.fucksgiven.Hilt_MainActivity.onCreate(Hilt_MainActivity.java:64)
	at rocks.poopjournal.fucksgiven.MainActivity.onCreate(MainActivity.kt:35)
	at android.app.Activity.performCreate(Activity.java:9364)
	at android.app.Activity.performCreate(Activity.java:9342)
	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1551)
	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4501)
	... 14 more
Caused by: android.security.KeyStoreException: Signature/MAC verification failed (internal Keystore code: -30 message: system/security/keystore2/src/operation.rs:853: KeystoreOperation::finish

Caused by:
    0: system/security/keystore2/src/operation.rs:434: Finish failed for AppUid(10312)
    1: Error::Km(r#VERIFICATION_FAILED)) (public error code: 10 internal Keystore code: -30)
	at android.security.KeyStore2.getKeyStoreException(KeyStore2.java:429)
	at android.security.KeyStoreOperation.handleExceptions(KeyStoreOperation.java:79)
	at android.security.KeyStoreOperation.finish(KeyStoreOperation.java:132)
	at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer$MainDataStream.finish(KeyStoreCryptoOperationChunkedStreamer.java:228)
	at android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:181)
	at android.security.keystore2.AndroidKeyStoreAuthenticatedAESCipherSpi$BufferAllOutputUntilDoFinalStreamer.doFinal(AndroidKeyStoreAuthenticatedAESCipherSpi.java:396)
	at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:624)
	... 41 more

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions