From 1c2acbbeb7b252aee3b87e8f2d27d101943dea8c Mon Sep 17 00:00:00 2001 From: Matej Kriz Date: Wed, 27 Mar 2024 15:11:46 +0100 Subject: [PATCH] fix(suite-native): handle error on SecureStore.getItemAsync - For some users on some phones, sometimes... SecureStore is not wiped correctly on app uninstall so it failed to loaded when app is installed again. See https://github.com/expo/expo/issues/23426 - This is a workaround - deleting the key in that case and genereting new one. --- suite-native/storage/package.json | 1 + suite-native/storage/src/storage.ts | 33 +++++++++++++++++------------ yarn.lock | 1 + 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/suite-native/storage/package.json b/suite-native/storage/package.json index f19e1a9f649..b8ed54b935f 100644 --- a/suite-native/storage/package.json +++ b/suite-native/storage/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@reduxjs/toolkit": "1.9.5", + "@sentry/react-native": "5.19.1", "@suite-common/wallet-config": "workspace:*", "@suite-common/wallet-types": "workspace:*", "@trezor/utxo-lib": "workspace:*", diff --git a/suite-native/storage/src/storage.ts b/suite-native/storage/src/storage.ts index 0b98bfd8bcf..53a43aa05b9 100644 --- a/suite-native/storage/src/storage.ts +++ b/suite-native/storage/src/storage.ts @@ -2,6 +2,7 @@ import { Alert } from 'react-native'; import { MMKV } from 'react-native-mmkv'; import RNRestart from 'react-native-restart'; +import { captureException } from '@sentry/react-native'; import * as Random from 'expo-random'; import * as SecureStore from 'expo-secure-store'; import * as SplashScreen from 'expo-splash-screen'; @@ -12,19 +13,26 @@ import { unecryptedJotaiStorage } from './atomWithUnecryptedStorage'; export const ENCRYPTION_KEY = 'STORAGE_ENCRYPTION_KEY'; export const ENCRYPTED_STORAGE_ID = 'trezorSuite-app-storage'; -export const retrieveStorageEncryptionKey = async () => { - let secureKey = await SecureStore.getItemAsync(ENCRYPTION_KEY); +export let encryptedStorage: MMKV; + +const retrieveStorageEncryptionKey = async () => { + try { + const secureKey = await SecureStore.getItemAsync(ENCRYPTION_KEY); - if (secureKey == null) { - secureKey = Buffer.from(Random.getRandomBytes(16)).toString('hex'); - await SecureStore.setItemAsync(ENCRYPTION_KEY, secureKey); + if (secureKey) return secureKey; + } catch (error) { + // Some users are facing an error when they uninstall the app and then reinstall it, + // see https://github.com/expo/expo/issues/23426 + await SecureStore.deleteItemAsync(ENCRYPTION_KEY); + captureException(error); } + const secureKey = Buffer.from(Random.getRandomBytes(16)).toString('hex'); + await SecureStore.setItemAsync(ENCRYPTION_KEY, secureKey); + return secureKey; }; -export let encryptedStorage: MMKV; - export const clearStorage = () => { unecryptedJotaiStorage.clearAll(); encryptedStorage?.clearAll(); @@ -35,9 +43,6 @@ export const clearStorage = () => { // If someone will mess with encryptionKey it can corrupt storage and app will crash on startup. // Then app will hang on splashscreen indefinitely so we at least want to show some error message. const tryInitStorage = (encryptionKey: string) => { - // storage may be already initialized (for example in dev useEffect fire twice) - if (encryptedStorage) return encryptedStorage; - try { return new MMKV({ id: ENCRYPTED_STORAGE_ID, @@ -67,9 +72,11 @@ const tryInitStorage = (encryptionKey: string) => { }; export const initMmkvStorage = async (): Promise => { - const encryptionKey = await retrieveStorageEncryptionKey(); - - encryptedStorage = tryInitStorage(encryptionKey); + // storage may be already initialized (for example in dev useEffect fire twice) + if (!encryptedStorage) { + const encryptionKey = await retrieveStorageEncryptionKey(); + encryptedStorage = tryInitStorage(encryptionKey); + } return { setItem: (key, value) => { diff --git a/yarn.lock b/yarn.lock index a808838d18b..259b5d8af46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9652,6 +9652,7 @@ __metadata: resolution: "@suite-native/storage@workspace:suite-native/storage" dependencies: "@reduxjs/toolkit": "npm:1.9.5" + "@sentry/react-native": "npm:5.19.1" "@suite-common/wallet-config": "workspace:*" "@suite-common/wallet-types": "workspace:*" "@trezor/utxo-lib": "workspace:*"