From be1fdcef56e90cb7eec8e072388667bf32756751 Mon Sep 17 00:00:00 2001 From: Igor Kharakhordin Date: Wed, 5 Mar 2025 12:41:25 +0100 Subject: [PATCH 01/22] Enable StrongBox by default on Android with a fallback --- flutter_secure_storage/CHANGELOG.md | 5 +++ .../FlutterSecureStorage.java | 43 +++++++++++++------ .../ciphers/RSACipher18Implementation.java | 38 +++++++++++----- .../ciphers/RSACipherOAEPImplementation.java | 5 ++- .../crypto/MasterKey.java | 9 ++-- flutter_secure_storage/pubspec.yaml | 2 +- 6 files changed, 73 insertions(+), 29 deletions(-) diff --git a/flutter_secure_storage/CHANGELOG.md b/flutter_secure_storage/CHANGELOG.md index 6f057845..9e55841f 100644 --- a/flutter_secure_storage/CHANGELOG.md +++ b/flutter_secure_storage/CHANGELOG.md @@ -1,3 +1,8 @@ +## 10.0.1 +* Enabled StrongBox by default, use fallback if it's not available. + +# Before fork + ## 10.0.0-beta.4 * [Apple] Merged ios and macos implementation into a new package flutter_secure_storage_darwin * [Apple] Refactored code and added missing options diff --git a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java index da7183a6..7134e1e6 100644 --- a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java +++ b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java @@ -2,8 +2,10 @@ import android.content.Context; import android.content.SharedPreferences; +import android.os.Build; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; +import android.security.keystore.StrongBoxUnavailableException; import android.util.Base64; import android.util.Log; @@ -61,7 +63,7 @@ public FlutterSecureStorage(Context context, Map options) throws } } - encryptedPreferences = getEncryptedSharedPreferences(deleteOnFailure, options, context.getApplicationContext(), sharedPreferencesName); + encryptedPreferences = getEncryptedSharedPreferences(deleteOnFailure, options, context.getApplicationContext(), sharedPreferencesName, true); } public boolean containsKey(String key) { @@ -102,15 +104,25 @@ private String addPrefixToKey(String key) { return preferencesKeyPrefix + "_" + key; } - private SharedPreferences getEncryptedSharedPreferences(boolean deleteOnFailure, Map options, Context context, String sharedPreferencesName) throws GeneralSecurityException, IOException { + private SharedPreferences getEncryptedSharedPreferences(boolean deleteOnFailure, Map options, Context context, String sharedPreferencesName, boolean isStrongBoxBacked) throws GeneralSecurityException, IOException { try { - final SharedPreferences encryptedPreferences = initializeEncryptedSharedPreferencesManager(context, sharedPreferencesName); + final SharedPreferences encryptedPreferences = initializeEncryptedSharedPreferencesManager(context, sharedPreferencesName, isStrongBoxBacked); boolean migrated = encryptedPreferences.getBoolean(PREF_KEY_MIGRATED, false); if (!migrated) { migrateToEncryptedPreferences(context, sharedPreferencesName, encryptedPreferences, deleteOnFailure, options); } return encryptedPreferences; } catch (GeneralSecurityException | IOException e) { + if (e instanceof GeneralSecurityException) { + Throwable cause = e.getCause(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + if (cause instanceof StrongBoxUnavailableException) { + // Fallback to not using Strongbox + return getEncryptedSharedPreferences(deleteOnFailure, options, context, sharedPreferencesName, false); + } + } + } if (!deleteOnFailure) { Log.w(TAG, "initialization failed, resetOnError false, so throwing exception.", e); @@ -121,7 +133,7 @@ private SharedPreferences getEncryptedSharedPreferences(boolean deleteOnFailure, context.getSharedPreferences(sharedPreferencesName, Context.MODE_PRIVATE).edit().clear().apply(); try { - return initializeEncryptedSharedPreferencesManager(context, sharedPreferencesName); + return initializeEncryptedSharedPreferencesManager(context, sharedPreferencesName, isStrongBoxBacked); } catch (Exception f) { Log.e(TAG, "initialization after reset failed", f); throw f; @@ -129,16 +141,21 @@ private SharedPreferences getEncryptedSharedPreferences(boolean deleteOnFailure, } } - private SharedPreferences initializeEncryptedSharedPreferencesManager(Context context, String sharedPreferencesName) throws GeneralSecurityException, IOException { + private SharedPreferences initializeEncryptedSharedPreferencesManager(Context context, String sharedPreferencesName, boolean isStrongBoxBacked) throws GeneralSecurityException, IOException { + KeyGenParameterSpec.Builder keyGenBuilder = new KeyGenParameterSpec.Builder( + MasterKey.DEFAULT_MASTER_KEY_ALIAS, + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .setKeySize(256); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && isStrongBoxBacked) { + keyGenBuilder.setIsStrongBoxBacked(true); + } + MasterKey masterKey = new MasterKey.Builder(context) - .setKeyGenParameterSpec(new KeyGenParameterSpec.Builder( - MasterKey.DEFAULT_MASTER_KEY_ALIAS, - KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_GCM) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) - .setKeySize(256) - .build()) - .build(); + .setKeyGenParameterSpec(keyGenBuilder.build()) + .build(isStrongBoxBacked); return EncryptedSharedPreferences.create( context, diff --git a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/ciphers/RSACipher18Implementation.java b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/ciphers/RSACipher18Implementation.java index c36fd543..46fd7b40 100644 --- a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/ciphers/RSACipher18Implementation.java +++ b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/ciphers/RSACipher18Implementation.java @@ -5,6 +5,7 @@ import android.os.Build; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; +import android.security.keystore.StrongBoxUnavailableException; import androidx.annotation.RequiresApi; @@ -123,26 +124,38 @@ private void setLocale(Locale locale) { context.createConfigurationContext(config); } + private AlgorithmParameterSpec getSpec(boolean isStrongBoxBacked) { + Calendar start = Calendar.getInstance(); + Calendar end = Calendar.getInstance(); + end.add(Calendar.YEAR, 25); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return makeAlgorithmParameterSpecLegacy(context, start, end); + } + + return makeAlgorithmParameterSpec(context, start, end, isStrongBoxBacked); + } + + @RequiresApi(api = Build.VERSION_CODES.P) private void createKeys(Context context) throws Exception { final Locale localeBeforeFakingEnglishLocale = Locale.getDefault(); try { setLocale(Locale.ENGLISH); - Calendar start = Calendar.getInstance(); - Calendar end = Calendar.getInstance(); - end.add(Calendar.YEAR, 25); KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance(TYPE_RSA, KEYSTORE_PROVIDER_ANDROID); AlgorithmParameterSpec spec; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - spec = makeAlgorithmParameterSpecLegacy(context, start, end); - } else { - spec = makeAlgorithmParameterSpec(context, start, end); - } + try { + spec = getSpec(true); + + kpGenerator.initialize(spec); + kpGenerator.generateKeyPair(); + } catch (StrongBoxUnavailableException e) { + spec = getSpec(false); - kpGenerator.initialize(spec); - kpGenerator.generateKeyPair(); + kpGenerator.initialize(spec); + kpGenerator.generateKeyPair(); + } } finally { setLocale(localeBeforeFakingEnglishLocale); } @@ -161,7 +174,7 @@ private AlgorithmParameterSpec makeAlgorithmParameterSpecLegacy(Context context, } @RequiresApi(api = Build.VERSION_CODES.M) - protected AlgorithmParameterSpec makeAlgorithmParameterSpec(Context context, Calendar start, Calendar end) { + protected AlgorithmParameterSpec makeAlgorithmParameterSpec(Context context, Calendar start, Calendar end, boolean isStrongBoxBacked) { final KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT) .setCertificateSubject(new X500Principal("CN=" + keyAlias)) .setDigests(KeyProperties.DIGEST_SHA256) @@ -170,6 +183,9 @@ protected AlgorithmParameterSpec makeAlgorithmParameterSpec(Context context, Cal .setCertificateSerialNumber(BigInteger.valueOf(1)) .setCertificateNotBefore(start.getTime()) .setCertificateNotAfter(end.getTime()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && isStrongBoxBacked) { + builder.setIsStrongBoxBacked(true); + } return builder.build(); } } diff --git a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/ciphers/RSACipherOAEPImplementation.java b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/ciphers/RSACipherOAEPImplementation.java index e05e4c88..fce941a8 100644 --- a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/ciphers/RSACipherOAEPImplementation.java +++ b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/ciphers/RSACipherOAEPImplementation.java @@ -30,7 +30,7 @@ protected String createKeyAlias() { @RequiresApi(api = Build.VERSION_CODES.M) @Override - protected AlgorithmParameterSpec makeAlgorithmParameterSpec(Context context, Calendar start, Calendar end) { + protected AlgorithmParameterSpec makeAlgorithmParameterSpec(Context context, Calendar start, Calendar end, boolean isStrongBoxBacked) { final KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT) .setCertificateSubject(new X500Principal("CN=" + keyAlias)) .setDigests(KeyProperties.DIGEST_SHA256) @@ -39,6 +39,9 @@ protected AlgorithmParameterSpec makeAlgorithmParameterSpec(Context context, Cal .setCertificateSerialNumber(BigInteger.valueOf(1)) .setCertificateNotBefore(start.getTime()) .setCertificateNotAfter(end.getTime()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && isStrongBoxBacked) { + builder.setIsStrongBoxBacked(true); + } return builder.build(); } diff --git a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/crypto/MasterKey.java b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/crypto/MasterKey.java index e67b7942..2dd61849 100644 --- a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/crypto/MasterKey.java +++ b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/crypto/MasterKey.java @@ -264,8 +264,8 @@ public Builder setKeyGenParameterSpec(@NonNull KeyGenParameterSpec keyGenParamet * @return The master key. */ @NonNull - public MasterKey build() throws GeneralSecurityException, IOException { - return Api23Impl.build(this); + public MasterKey build(boolean isStrongBoxBacked) throws GeneralSecurityException, IOException { + return Api23Impl.build(this, isStrongBoxBacked); } static class Api23Impl { @@ -277,7 +277,7 @@ static String getKeystoreAlias(KeyGenParameterSpec keyGenParameterSpec) { return keyGenParameterSpec.getKeystoreAlias(); } @SuppressWarnings("deprecation") - static MasterKey build(Builder builder) throws GeneralSecurityException, IOException { + static MasterKey build(Builder builder, boolean isStrongBoxBacked) throws GeneralSecurityException, IOException { if (builder.mKeyScheme == null && builder.mKeyGenParameterSpec == null) { throw new IllegalArgumentException("build() called before " + "setKeyGenParameterSpec or setKeyScheme."); @@ -289,6 +289,9 @@ static MasterKey build(Builder builder) throws GeneralSecurityException, IOExcep .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setKeySize(DEFAULT_AES_GCM_MASTER_KEY_SIZE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && isStrongBoxBacked) { + keyGenBuilder.setIsStrongBoxBacked(true); + } if (builder.mAuthenticationRequired) { keyGenBuilder.setUserAuthenticationRequired(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { diff --git a/flutter_secure_storage/pubspec.yaml b/flutter_secure_storage/pubspec.yaml index c5d1ba6e..580d2eaa 100644 --- a/flutter_secure_storage/pubspec.yaml +++ b/flutter_secure_storage/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_secure_storage description: A Flutter plugin for securely storing sensitive data using encrypted storage. -version: 10.0.0-beta.4 +version: 10.0.1 repository: https://github.com/mogol/flutter_secure_storage/tree/develop/flutter_secure_storage environment: From 733b438460963cddc1d633c53d9d4acaaae7ce20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 13:51:15 +0100 Subject: [PATCH 02/22] fix(app): flag to force strongbox by removing fallback --- .../FlutterSecureStorage.java | 15 ++++++++++++--- .../lib/options/android_options.dart | 13 ++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java index 7134e1e6..440fe3b8 100644 --- a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java +++ b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java @@ -33,6 +33,7 @@ public class FlutterSecureStorage { private static final String PREF_OPTION_PREFIX = "preferencesKeyPrefix"; private static final String PREF_OPTION_DELETE_ON_FAILURE = "resetOnError"; private static final String PREF_KEY_MIGRATED = "preferencesMigrated"; + private static final String PREF_OPTION_ONLY_ALLOW_STRONGBOX = "onlyAllowStrongBox"; @NonNull private final SharedPreferences encryptedPreferences; @NonNull @@ -63,7 +64,15 @@ public FlutterSecureStorage(Context context, Map options) throws } } - encryptedPreferences = getEncryptedSharedPreferences(deleteOnFailure, options, context.getApplicationContext(), sharedPreferencesName, true); + boolean onlyAllowStrongbox = false; + if (options.containsKey(PREF_OPTION_ONLY_ALLOW_STRONGBOX)) { + var value = options.get(PREF_OPTION_ONLY_ALLOW_STRONGBOX); + if (value instanceof String) { + onlyAllowStrongbox = Boolean.parseBoolean((String) value); + } + } + + encryptedPreferences = getEncryptedSharedPreferences(deleteOnFailure, options, context.getApplicationContext(), sharedPreferencesName, true, onlyAllowStrongbox); } public boolean containsKey(String key) { @@ -104,7 +113,7 @@ private String addPrefixToKey(String key) { return preferencesKeyPrefix + "_" + key; } - private SharedPreferences getEncryptedSharedPreferences(boolean deleteOnFailure, Map options, Context context, String sharedPreferencesName, boolean isStrongBoxBacked) throws GeneralSecurityException, IOException { + private SharedPreferences getEncryptedSharedPreferences(boolean deleteOnFailure, Map options, Context context, String sharedPreferencesName, boolean isStrongBoxBacked, boolean isOnlyStrongBoxAllowed) throws GeneralSecurityException, IOException { try { final SharedPreferences encryptedPreferences = initializeEncryptedSharedPreferencesManager(context, sharedPreferencesName, isStrongBoxBacked); boolean migrated = encryptedPreferences.getBoolean(PREF_KEY_MIGRATED, false); @@ -117,7 +126,7 @@ private SharedPreferences getEncryptedSharedPreferences(boolean deleteOnFailure, Throwable cause = e.getCause(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - if (cause instanceof StrongBoxUnavailableException) { + if (cause instanceof StrongBoxUnavailableException && !isOnlyStrongBoxAllowed) { // Fallback to not using Strongbox return getEncryptedSharedPreferences(deleteOnFailure, options, context, sharedPreferencesName, false); } diff --git a/flutter_secure_storage/lib/options/android_options.dart b/flutter_secure_storage/lib/options/android_options.dart index b6609e52..76bdd99d 100644 --- a/flutter_secure_storage/lib/options/android_options.dart +++ b/flutter_secure_storage/lib/options/android_options.dart @@ -27,10 +27,12 @@ class AndroidOptions extends Options { StorageCipherAlgorithm.AES_CBC_PKCS7Padding, this.sharedPreferencesName, this.preferencesKeyPrefix, + bool onlyAllowStrongBox = false, }) : _encryptedSharedPreferences = encryptedSharedPreferences, _resetOnError = resetOnError, _keyCipherAlgorithm = keyCipherAlgorithm, - _storageCipherAlgorithm = storageCipherAlgorithm; + _storageCipherAlgorithm = storageCipherAlgorithm, + _onlyAllowStrongBox = onlyAllowStrongBox; /// EncryptedSharedPrefences are only available on API 23 and greater final bool _encryptedSharedPreferences; @@ -70,6 +72,12 @@ class AndroidOptions extends Options { /// WARNING: If you change this you can't retrieve already saved preferences. final String? preferencesKeyPrefix; + /// If true, only allow keys to be stored in StrongBox backed keymaster. + /// This option is only available on API 28 and greater. If set to true some phones might now work + /// Defaults to false. + /// https://developer.android.com/training/articles/keystore#HardwareSecurityModule + final bool _onlyAllowStrongBox; + static const AndroidOptions defaultOptions = AndroidOptions(); @override @@ -80,6 +88,7 @@ class AndroidOptions extends Options { 'storageCipherAlgorithm': _storageCipherAlgorithm.name, 'sharedPreferencesName': sharedPreferencesName ?? '', 'preferencesKeyPrefix': preferencesKeyPrefix ?? '', + 'onlyAllowStrongBox': '$_onlyAllowStrongBox', }; AndroidOptions copyWith({ @@ -89,6 +98,7 @@ class AndroidOptions extends Options { StorageCipherAlgorithm? storageCipherAlgorithm, String? preferencesKeyPrefix, String? sharedPreferencesName, + bool? onlyAllowStrongBox, }) => AndroidOptions( encryptedSharedPreferences: @@ -99,5 +109,6 @@ class AndroidOptions extends Options { storageCipherAlgorithm ?? _storageCipherAlgorithm, sharedPreferencesName: sharedPreferencesName, preferencesKeyPrefix: preferencesKeyPrefix, + onlyAllowStrongBox: onlyAllowStrongBox ?? _onlyAllowStrongBox, ); } From ea794b8d20c7586fab8fec44a46750a44ae1db9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 13:53:35 +0100 Subject: [PATCH 03/22] fix(app): update changelog --- flutter_secure_storage/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter_secure_storage/CHANGELOG.md b/flutter_secure_storage/CHANGELOG.md index 9e55841f..f9a99e57 100644 --- a/flutter_secure_storage/CHANGELOG.md +++ b/flutter_secure_storage/CHANGELOG.md @@ -1,5 +1,6 @@ ## 10.0.1 * Enabled StrongBox by default, use fallback if it's not available. +* [Android] Allow to force StrongBox with a flag (onlyAllowStrongBox) # Before fork From 4aa2c13ec87dc503ab6865cc38370b62675115da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 14:18:07 +0100 Subject: [PATCH 04/22] fix(app): add function to check if the device supports strongbox --- .../FlutterSecureStorage.java | 35 +++++++++++++++++++ .../FlutterSecureStoragePlugin.java | 7 ++++ ...ter_secure_storage_platform_interface.dart | 7 ++++ ...method_channel_flutter_secure_storage.dart | 8 +++++ 4 files changed, 57 insertions(+) diff --git a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java index 440fe3b8..718da5ff 100644 --- a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java +++ b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java @@ -95,6 +95,41 @@ public void deleteAll() { encryptedPreferences.edit().clear().apply(); } + public boolean isStrongBoxAvailable() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + try { + // Check if the device supports StrongBox + KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + + String keyAlias = "strongboxCheck"; + KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(keyAlias, + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .setKeySize(256) + .build(); + + KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); + keyGenerator.init(keyGenParameterSpec); + keyGenerator.generateKey(); + + // Check if the key was generated in StrongBox + KeyInfo keyInfo = (KeyInfo) KeyFactory.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore") + .getKeySpec(keyGenerator.generateKey(), KeyInfo.class); + + return keyInfo.isInsideStrongBox(); + + } catch (Exception e) { + Log.e(TAG, "StrongBox check failed", e); + return false; + } + } + + return false; + } + + public Map readAll() { Map result = new HashMap<>(); Map allEntries = encryptedPreferences.getAll(); diff --git a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java index bf28a2ee..9717bad3 100644 --- a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java +++ b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java @@ -123,6 +123,9 @@ private void handleMethodCall(MethodCall call, Result result) { case "deleteAll": handleDeleteAll(result); break; + case "isStrongBoxSupported": + handleStrongBoxAvailable(result); + break; default: result.notImplemented(); } @@ -164,6 +167,10 @@ private void handleDeleteAll(Result result) { result.success(null); } + private void handleStrongBoxAvailable(Result result) { + result.success(secureStorage.isStrongBoxAvailable()); + } + @SuppressWarnings("unchecked") private Map extractMapFromObject(Object object) { if (!(object instanceof Map)) { diff --git a/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart b/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart index 89575675..79694c9c 100644 --- a/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart +++ b/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart @@ -120,4 +120,11 @@ abstract class FlutterSecureStoragePlatform extends PlatformInterface { Future deleteAll({ required Map options, }); + + + /// Checks if the android device supports secure hardware-backed storage. + /// + /// Returns: + /// - A [Future] that resolves to `true` if the device supports secure hardware-backed storage, or `false` otherwise. + Future isStrongBoxSupported(); } diff --git a/flutter_secure_storage_platform_interface/lib/src/method_channel_flutter_secure_storage.dart b/flutter_secure_storage_platform_interface/lib/src/method_channel_flutter_secure_storage.dart index 1cf5365b..41107d71 100644 --- a/flutter_secure_storage_platform_interface/lib/src/method_channel_flutter_secure_storage.dart +++ b/flutter_secure_storage_platform_interface/lib/src/method_channel_flutter_secure_storage.dart @@ -114,4 +114,12 @@ class MethodChannelFlutterSecureStorage extends FlutterSecureStoragePlatform { 'value': value, 'options': options, }); + + @override + Future isStrongBoxSupported() async { + if (defaultTargetPlatform != TargetPlatform.android) { + throw UnsupportedError('StrongBox is only supported on Android.'); + } + return (await _channel.invokeMethod('isStrongBoxSupported')) ?? false; + } } From 12d92c93b4c6351c21a551eb9aeee7e0d4d7a72b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 14:23:54 +0100 Subject: [PATCH 05/22] fix(app): add method to check if strongbox is supported --- flutter_secure_storage/lib/flutter_secure_storage.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/flutter_secure_storage/lib/flutter_secure_storage.dart b/flutter_secure_storage/lib/flutter_secure_storage.dart index 92291cd5..54884b42 100644 --- a/flutter_secure_storage/lib/flutter_secure_storage.dart +++ b/flutter_secure_storage/lib/flutter_secure_storage.dart @@ -344,6 +344,14 @@ class FlutterSecureStorage { }); } + Future isStrongBoxSupported() async { + if (defaultTargetPlatform == TargetPlatform.android) { + return await _platform.isStrongBoxSupported(); + } else { + throw UnsupportedError(_unsupportedPlatform); + } + } + /// Select correct options based on current platform Map _selectOptions( AppleOptions? iOptions, From ccab95c5febf376eb8542795db35fce6fa4e7cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 14:58:09 +0100 Subject: [PATCH 06/22] fix(app): use own interface definition --- flutter_secure_storage/pubspec.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flutter_secure_storage/pubspec.yaml b/flutter_secure_storage/pubspec.yaml index 580d2eaa..d7a4def9 100644 --- a/flutter_secure_storage/pubspec.yaml +++ b/flutter_secure_storage/pubspec.yaml @@ -33,7 +33,11 @@ dependencies: # https://github.com/flutter/flutter/issues/46264 flutter_secure_storage_darwin: ^0.1.0 flutter_secure_storage_linux: ^2.0.0 - flutter_secure_storage_platform_interface: ^2.0.1 + flutter_secure_storage_platform_interface: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_platform_interface + ref: strongbox-enabled flutter_secure_storage_web: ^2.0.0 flutter_secure_storage_windows: ^4.0.0 meta: ^1.3.0 From c024bc7468612bfc2350a32d924840c1ea8ce0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 15:00:31 +0100 Subject: [PATCH 07/22] fix(app): use git platform interface --- flutter_secure_storage_linux/pubspec.yaml | 6 +++++- flutter_secure_storage_web/pubspec.yaml | 6 +++++- flutter_secure_storage_windows/pubspec.yaml | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/flutter_secure_storage_linux/pubspec.yaml b/flutter_secure_storage_linux/pubspec.yaml index ead9dc39..60896a30 100644 --- a/flutter_secure_storage_linux/pubspec.yaml +++ b/flutter_secure_storage_linux/pubspec.yaml @@ -10,7 +10,11 @@ environment: dependencies: flutter: sdk: flutter - flutter_secure_storage_platform_interface: ^2.0.0 + flutter_secure_storage_platform_interface: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_platform_interface + ref: strongbox-enabled flutter: plugin: diff --git a/flutter_secure_storage_web/pubspec.yaml b/flutter_secure_storage_web/pubspec.yaml index 2664a72a..38ec4811 100644 --- a/flutter_secure_storage_web/pubspec.yaml +++ b/flutter_secure_storage_web/pubspec.yaml @@ -10,7 +10,11 @@ environment: dependencies: flutter: sdk: flutter - flutter_secure_storage_platform_interface: ^2.0.0 + flutter_secure_storage_platform_interface: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_platform_interface + ref: strongbox-enabled flutter_web_plugins: sdk: flutter web: ">=0.5.0 <2.0.0" diff --git a/flutter_secure_storage_windows/pubspec.yaml b/flutter_secure_storage_windows/pubspec.yaml index eb277149..8070859f 100644 --- a/flutter_secure_storage_windows/pubspec.yaml +++ b/flutter_secure_storage_windows/pubspec.yaml @@ -11,7 +11,11 @@ dependencies: ffi: ^2.0.0 flutter: sdk: flutter - flutter_secure_storage_platform_interface: ^2.0.0 + flutter_secure_storage_platform_interface: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_platform_interface + ref: strongbox-enabled path: ^1.8.0 path_provider: ^2.0.0 win32: ^5.5.4 From c6475d62095a96a0f644cafffeb5d75acf7d3097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 15:03:58 +0100 Subject: [PATCH 08/22] fix(app): fix test --- .../lib/test/test_flutter_secure_storage_platform.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flutter_secure_storage/lib/test/test_flutter_secure_storage_platform.dart b/flutter_secure_storage/lib/test/test_flutter_secure_storage_platform.dart index 74a4f7ae..de3ef13d 100644 --- a/flutter_secure_storage/lib/test/test_flutter_secure_storage_platform.dart +++ b/flutter_secure_storage/lib/test/test_flutter_secure_storage_platform.dart @@ -52,4 +52,7 @@ class TestFlutterSecureStoragePlatform extends FlutterSecureStoragePlatform { required Map options, }) async => data[key] = value; + + @override + Future isStrongBoxSupported() async => true; } From 7b738c0400eb811065bb89e88bc3438dff523eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 15:07:45 +0100 Subject: [PATCH 09/22] fix(app): add unsported error to windows --- .../lib/src/flutter_secure_storage_windows_ffi.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart b/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart index 1499e1e8..1ce13dd5 100644 --- a/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart +++ b/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart @@ -178,6 +178,13 @@ class FlutterSecureStorageWindows extends FlutterSecureStoragePlatform { await _backwardCompatible.delete(key: key, options: options); } } + + @override + Future isStrongBoxSupported() async { + throw UnimplementedError( + 'isStrongBoxSupported is not supported on Windows.', + ); + } } /// Creates a custom instance of `FlutterSecureStorageWindows` for testing. From 750e0e29ffada50c3e55c36b2522c96abe60b83f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 15:09:23 +0100 Subject: [PATCH 10/22] fix(app): default impl for strong box --- .../lib/flutter_secure_storage_platform_interface.dart | 4 +++- .../lib/src/flutter_secure_storage_windows_ffi.dart | 7 ------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart b/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart index 79694c9c..a337ca21 100644 --- a/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart +++ b/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart @@ -126,5 +126,7 @@ abstract class FlutterSecureStoragePlatform extends PlatformInterface { /// /// Returns: /// - A [Future] that resolves to `true` if the device supports secure hardware-backed storage, or `false` otherwise. - Future isStrongBoxSupported(); + Future isStrongBoxSupported() { + throw UnsupportedError('isStrongBoxSupported() is not available on this platform'); + } } diff --git a/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart b/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart index 1ce13dd5..1499e1e8 100644 --- a/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart +++ b/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart @@ -178,13 +178,6 @@ class FlutterSecureStorageWindows extends FlutterSecureStoragePlatform { await _backwardCompatible.delete(key: key, options: options); } } - - @override - Future isStrongBoxSupported() async { - throw UnimplementedError( - 'isStrongBoxSupported is not supported on Windows.', - ); - } } /// Creates a custom instance of `FlutterSecureStorageWindows` for testing. From 3c20c9d0d6de8d6583327413db761511457b079a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 15:17:54 +0100 Subject: [PATCH 11/22] fix(app): update dependencies --- flutter_secure_storage/pubspec.yaml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/flutter_secure_storage/pubspec.yaml b/flutter_secure_storage/pubspec.yaml index d7a4def9..b3867916 100644 --- a/flutter_secure_storage/pubspec.yaml +++ b/flutter_secure_storage/pubspec.yaml @@ -31,15 +31,31 @@ dependencies: # implementation constraints as "any". We cannot do it right now as it fails pub publish # validation, so we set a ^ constraint. # https://github.com/flutter/flutter/issues/46264 - flutter_secure_storage_darwin: ^0.1.0 - flutter_secure_storage_linux: ^2.0.0 + flutter_secure_storage_darwin: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_darwin + ref: strongbox-enabled + flutter_secure_storage_linux: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_linux + ref: strongbox-enabled flutter_secure_storage_platform_interface: git: url: git@github.com:QuickBirdEng/flutter_secure_storage.git path: flutter_secure_storage_platform_interface ref: strongbox-enabled - flutter_secure_storage_web: ^2.0.0 - flutter_secure_storage_windows: ^4.0.0 + flutter_secure_storage_web: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_web + ref: strongbox-enabled + flutter_secure_storage_windows: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_windows + ref: strongbox-enabled meta: ^1.3.0 dev_dependencies: From 0f80b5f91cddec760c51e5288958be8a9a2038d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 15:23:33 +0100 Subject: [PATCH 12/22] fix(app): fix formatting --- .../lib/flutter_secure_storage_platform_interface.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart b/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart index a337ca21..e6d8d8d8 100644 --- a/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart +++ b/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart @@ -121,12 +121,14 @@ abstract class FlutterSecureStoragePlatform extends PlatformInterface { required Map options, }); - /// Checks if the android device supports secure hardware-backed storage. - /// + /// /// Returns: - /// - A [Future] that resolves to `true` if the device supports secure hardware-backed storage, or `false` otherwise. + /// - A [Future] that resolves to `true` if the device supports secure + /// hardware-backed storage, or `false` otherwise. Future isStrongBoxSupported() { - throw UnsupportedError('isStrongBoxSupported() is not available on this platform'); + throw UnsupportedError( + 'isStrongBoxSupported() is not available on this platform', + ); } } From 05a98d58fb82d2b822ba64775eb670e98feef209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 15:31:18 +0100 Subject: [PATCH 13/22] fix(app): emtpy impl strongbox windows --- .../lib/src/flutter_secure_storage_windows_ffi.dart | 7 +++++++ .../lib/src/flutter_secure_storage_windows_stub.dart | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart b/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart index 1499e1e8..4dad4f2e 100644 --- a/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart +++ b/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart @@ -178,6 +178,13 @@ class FlutterSecureStorageWindows extends FlutterSecureStoragePlatform { await _backwardCompatible.delete(key: key, options: options); } } + + @override + Future isStrongBoxSupported() async { + throw UnsupportedError( + 'isStrongBoxSupported() is not available on this platform', + ); + } } /// Creates a custom instance of `FlutterSecureStorageWindows` for testing. diff --git a/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart b/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart index b6647f0a..5aee20df 100644 --- a/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart +++ b/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart @@ -49,6 +49,13 @@ class FlutterSecureStorageWindows extends FlutterSecureStoragePlatform { }) => Future.value(); + @override + Future isStrongBoxSupported() async { + throw UnsupportedError( + 'isStrongBoxSupported() is not available on this platform', + ); + } + // @override // Future isCupertinoProtectedDataAvailable() => Future.value(true); // From c24f4e0420aafd7af806a0b90506cd416fab1bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 16:24:06 +0100 Subject: [PATCH 14/22] fix(app): add check for strongbox --- .../FlutterSecureStorage.java | 34 ------------------- .../FlutterSecureStoragePlugin.java | 5 ++- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java index 718da5ff..6d347620 100644 --- a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java +++ b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java @@ -94,40 +94,6 @@ public void delete(String key) { public void deleteAll() { encryptedPreferences.edit().clear().apply(); } - - public boolean isStrongBoxAvailable() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - try { - // Check if the device supports StrongBox - KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); - keyStore.load(null); - - String keyAlias = "strongboxCheck"; - KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(keyAlias, - KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_GCM) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) - .setKeySize(256) - .build(); - - KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); - keyGenerator.init(keyGenParameterSpec); - keyGenerator.generateKey(); - - // Check if the key was generated in StrongBox - KeyInfo keyInfo = (KeyInfo) KeyFactory.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore") - .getKeySpec(keyGenerator.generateKey(), KeyInfo.class); - - return keyInfo.isInsideStrongBox(); - - } catch (Exception e) { - Log.e(TAG, "StrongBox check failed", e); - return false; - } - } - - return false; - } public Map readAll() { diff --git a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java index 9717bad3..2f824c72 100644 --- a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java +++ b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java @@ -1,5 +1,6 @@ package com.it_nomads.fluttersecurestorage; +import android.content.pm.PackageManager; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -24,6 +25,7 @@ public class FlutterSecureStoragePlugin implements MethodCallHandler, FlutterPlu private HandlerThread workerThread; private Handler workerThreadHandler; private FlutterPluginBinding binding; + private boolean isStrongBoxAvailable; @Override public void onAttachedToEngine(FlutterPluginBinding binding) { @@ -52,6 +54,7 @@ private boolean initSecureStorage(Result result, Map options) { if (secureStorage != null) return true; try { + isStrongBoxAvailable = getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE); secureStorage = new FlutterSecureStorage(binding.getApplicationContext(), options); return true; } catch (Exception e) { @@ -168,7 +171,7 @@ private void handleDeleteAll(Result result) { } private void handleStrongBoxAvailable(Result result) { - result.success(secureStorage.isStrongBoxAvailable()); + result.success(isStrongBoxAvailable); } @SuppressWarnings("unchecked") From 05b9a6fcde93df340c853a500bb3193873f956eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 16:25:55 +0100 Subject: [PATCH 15/22] fix(app): use correct application context --- .../fluttersecurestorage/FlutterSecureStoragePlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java index 2f824c72..6ec1d4c4 100644 --- a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java +++ b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java @@ -54,7 +54,7 @@ private boolean initSecureStorage(Result result, Map options) { if (secureStorage != null) return true; try { - isStrongBoxAvailable = getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE); + isStrongBoxAvailable = binding.getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE); secureStorage = new FlutterSecureStorage(binding.getApplicationContext(), options); return true; } catch (Exception e) { From 849c36b6429e64075966706c2930994a74366253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 16:28:39 +0100 Subject: [PATCH 16/22] fix(app): fix method signature --- .../it_nomads/fluttersecurestorage/FlutterSecureStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java index 6d347620..93340f35 100644 --- a/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java +++ b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java @@ -129,7 +129,7 @@ private SharedPreferences getEncryptedSharedPreferences(boolean deleteOnFailure, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (cause instanceof StrongBoxUnavailableException && !isOnlyStrongBoxAllowed) { // Fallback to not using Strongbox - return getEncryptedSharedPreferences(deleteOnFailure, options, context, sharedPreferencesName, false); + return getEncryptedSharedPreferences(deleteOnFailure, options, context, sharedPreferencesName, false, isOnlyStrongBoxAllowed); } } } From 768e2827915bcbf2f988d9873144730aa8f34863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 16:46:10 +0100 Subject: [PATCH 17/22] fix(app): add argument --- .../lib/src/method_channel_flutter_secure_storage.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flutter_secure_storage_platform_interface/lib/src/method_channel_flutter_secure_storage.dart b/flutter_secure_storage_platform_interface/lib/src/method_channel_flutter_secure_storage.dart index 41107d71..27ffa19d 100644 --- a/flutter_secure_storage_platform_interface/lib/src/method_channel_flutter_secure_storage.dart +++ b/flutter_secure_storage_platform_interface/lib/src/method_channel_flutter_secure_storage.dart @@ -120,6 +120,8 @@ class MethodChannelFlutterSecureStorage extends FlutterSecureStoragePlatform { if (defaultTargetPlatform != TargetPlatform.android) { throw UnsupportedError('StrongBox is only supported on Android.'); } - return (await _channel.invokeMethod('isStrongBoxSupported')) ?? false; + return (await _channel + .invokeMethod('isStrongBoxSupported', {'key': ''})) ?? + false; } } From 575fb7438229126e02dfa170caf8cdbb021ca894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 16:53:58 +0100 Subject: [PATCH 18/22] fix(app): update isStrongBoxSupported --- .../lib/flutter_secure_storage.dart | 8 ++++++-- .../test/test_flutter_secure_storage_platform.dart | 5 ++++- .../flutter_secure_storage_platform_interface.dart | 4 +++- .../src/method_channel_flutter_secure_storage.dart | 12 +++++++++--- .../lib/src/flutter_secure_storage_windows_ffi.dart | 4 +++- .../lib/src/flutter_secure_storage_windows_stub.dart | 4 +++- 6 files changed, 28 insertions(+), 9 deletions(-) diff --git a/flutter_secure_storage/lib/flutter_secure_storage.dart b/flutter_secure_storage/lib/flutter_secure_storage.dart index 54884b42..3be811d7 100644 --- a/flutter_secure_storage/lib/flutter_secure_storage.dart +++ b/flutter_secure_storage/lib/flutter_secure_storage.dart @@ -344,9 +344,13 @@ class FlutterSecureStorage { }); } - Future isStrongBoxSupported() async { + Future isStrongBoxSupported({ + AndroidOptions? aOptions, + }) async { if (defaultTargetPlatform == TargetPlatform.android) { - return await _platform.isStrongBoxSupported(); + return _platform.isStrongBoxSupported( + options: aOptions?.params ?? this.aOptions.params, + ); } else { throw UnsupportedError(_unsupportedPlatform); } diff --git a/flutter_secure_storage/lib/test/test_flutter_secure_storage_platform.dart b/flutter_secure_storage/lib/test/test_flutter_secure_storage_platform.dart index de3ef13d..0b63d2bc 100644 --- a/flutter_secure_storage/lib/test/test_flutter_secure_storage_platform.dart +++ b/flutter_secure_storage/lib/test/test_flutter_secure_storage_platform.dart @@ -54,5 +54,8 @@ class TestFlutterSecureStoragePlatform extends FlutterSecureStoragePlatform { data[key] = value; @override - Future isStrongBoxSupported() async => true; + Future isStrongBoxSupported({ + required Map options, + }) async => + true; } diff --git a/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart b/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart index e6d8d8d8..bb38c3b4 100644 --- a/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart +++ b/flutter_secure_storage_platform_interface/lib/flutter_secure_storage_platform_interface.dart @@ -126,7 +126,9 @@ abstract class FlutterSecureStoragePlatform extends PlatformInterface { /// Returns: /// - A [Future] that resolves to `true` if the device supports secure /// hardware-backed storage, or `false` otherwise. - Future isStrongBoxSupported() { + Future isStrongBoxSupported({ + required Map options, + }) { throw UnsupportedError( 'isStrongBoxSupported() is not available on this platform', ); diff --git a/flutter_secure_storage_platform_interface/lib/src/method_channel_flutter_secure_storage.dart b/flutter_secure_storage_platform_interface/lib/src/method_channel_flutter_secure_storage.dart index 27ffa19d..3c864f67 100644 --- a/flutter_secure_storage_platform_interface/lib/src/method_channel_flutter_secure_storage.dart +++ b/flutter_secure_storage_platform_interface/lib/src/method_channel_flutter_secure_storage.dart @@ -116,12 +116,18 @@ class MethodChannelFlutterSecureStorage extends FlutterSecureStoragePlatform { }); @override - Future isStrongBoxSupported() async { + Future isStrongBoxSupported({ + required Map options, + }) async { if (defaultTargetPlatform != TargetPlatform.android) { throw UnsupportedError('StrongBox is only supported on Android.'); } - return (await _channel - .invokeMethod('isStrongBoxSupported', {'key': ''})) ?? + return (await _channel.invokeMethod( + 'isStrongBoxSupported', + { + 'options': options, + }, + )) ?? false; } } diff --git a/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart b/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart index 4dad4f2e..7f5051b6 100644 --- a/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart +++ b/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart @@ -180,7 +180,9 @@ class FlutterSecureStorageWindows extends FlutterSecureStoragePlatform { } @override - Future isStrongBoxSupported() async { + Future isStrongBoxSupported({ + required Map options, + }) async { throw UnsupportedError( 'isStrongBoxSupported() is not available on this platform', ); diff --git a/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart b/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart index 5aee20df..becb68c6 100644 --- a/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart +++ b/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart @@ -50,7 +50,9 @@ class FlutterSecureStorageWindows extends FlutterSecureStoragePlatform { Future.value(); @override - Future isStrongBoxSupported() async { + Future isStrongBoxSupported({ + required Map options, + }) async { throw UnsupportedError( 'isStrongBoxSupported() is not available on this platform', ); From d9ee174fcb2ddd5f4a351b149721586a93ca707e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 Mar 2025 17:04:55 +0100 Subject: [PATCH 19/22] fix(app): add options to method interface --- flutter_secure_storage/lib/flutter_secure_storage.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter_secure_storage/lib/flutter_secure_storage.dart b/flutter_secure_storage/lib/flutter_secure_storage.dart index 3be811d7..e1cdf5f0 100644 --- a/flutter_secure_storage/lib/flutter_secure_storage.dart +++ b/flutter_secure_storage/lib/flutter_secure_storage.dart @@ -344,6 +344,7 @@ class FlutterSecureStorage { }); } + /// [aOptions] optional Android options Future isStrongBoxSupported({ AndroidOptions? aOptions, }) async { From f3db17c0999d275295c39cccc6eee24069ac5cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Fri, 7 Mar 2025 09:11:55 +0100 Subject: [PATCH 20/22] fix(app): update changelog --- flutter_secure_storage/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter_secure_storage/CHANGELOG.md b/flutter_secure_storage/CHANGELOG.md index f9a99e57..b5ae045e 100644 --- a/flutter_secure_storage/CHANGELOG.md +++ b/flutter_secure_storage/CHANGELOG.md @@ -1,6 +1,7 @@ ## 10.0.1 * Enabled StrongBox by default, use fallback if it's not available. * [Android] Allow to force StrongBox with a flag (onlyAllowStrongBox) +* [Android] Method to check if an Android device supports Strongbox # Before fork From bc8dfa341ee5fb276260a6487d1f810eaa797ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Fri, 7 Mar 2025 11:00:19 +0100 Subject: [PATCH 21/22] fix(app): update refs --- flutter_secure_storage/pubspec.yaml | 10 +++++----- flutter_secure_storage_linux/pubspec.yaml | 2 +- flutter_secure_storage_web/pubspec.yaml | 2 +- flutter_secure_storage_windows/pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/flutter_secure_storage/pubspec.yaml b/flutter_secure_storage/pubspec.yaml index b3867916..3ebb00ee 100644 --- a/flutter_secure_storage/pubspec.yaml +++ b/flutter_secure_storage/pubspec.yaml @@ -35,27 +35,27 @@ dependencies: git: url: git@github.com:QuickBirdEng/flutter_secure_storage.git path: flutter_secure_storage_darwin - ref: strongbox-enabled + ref: develop flutter_secure_storage_linux: git: url: git@github.com:QuickBirdEng/flutter_secure_storage.git path: flutter_secure_storage_linux - ref: strongbox-enabled + ref: develop flutter_secure_storage_platform_interface: git: url: git@github.com:QuickBirdEng/flutter_secure_storage.git path: flutter_secure_storage_platform_interface - ref: strongbox-enabled + ref: develop flutter_secure_storage_web: git: url: git@github.com:QuickBirdEng/flutter_secure_storage.git path: flutter_secure_storage_web - ref: strongbox-enabled + ref: develop flutter_secure_storage_windows: git: url: git@github.com:QuickBirdEng/flutter_secure_storage.git path: flutter_secure_storage_windows - ref: strongbox-enabled + ref: develop meta: ^1.3.0 dev_dependencies: diff --git a/flutter_secure_storage_linux/pubspec.yaml b/flutter_secure_storage_linux/pubspec.yaml index 60896a30..017b49d4 100644 --- a/flutter_secure_storage_linux/pubspec.yaml +++ b/flutter_secure_storage_linux/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: git: url: git@github.com:QuickBirdEng/flutter_secure_storage.git path: flutter_secure_storage_platform_interface - ref: strongbox-enabled + ref: develop flutter: plugin: diff --git a/flutter_secure_storage_web/pubspec.yaml b/flutter_secure_storage_web/pubspec.yaml index 38ec4811..780adf1d 100644 --- a/flutter_secure_storage_web/pubspec.yaml +++ b/flutter_secure_storage_web/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: git: url: git@github.com:QuickBirdEng/flutter_secure_storage.git path: flutter_secure_storage_platform_interface - ref: strongbox-enabled + ref: develop flutter_web_plugins: sdk: flutter web: ">=0.5.0 <2.0.0" diff --git a/flutter_secure_storage_windows/pubspec.yaml b/flutter_secure_storage_windows/pubspec.yaml index 8070859f..d939d353 100644 --- a/flutter_secure_storage_windows/pubspec.yaml +++ b/flutter_secure_storage_windows/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: git: url: git@github.com:QuickBirdEng/flutter_secure_storage.git path: flutter_secure_storage_platform_interface - ref: strongbox-enabled + ref: develop path: ^1.8.0 path_provider: ^2.0.0 win32: ^5.5.4 From 3f2e44ed431900874e0b46d22f5dd1e94a655b5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 01:55:30 +0000 Subject: [PATCH 22/22] chore(deps): bump org.jetbrains.kotlin.android Bumps [org.jetbrains.kotlin.android](https://github.com/JetBrains/kotlin) from 1.9.25 to 2.2.0. - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.9.25...v2.2.0) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.android dependency-version: 2.2.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- flutter_secure_storage/example/android/settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_secure_storage/example/android/settings.gradle.kts b/flutter_secure_storage/example/android/settings.gradle.kts index f6d45351..77bf4159 100644 --- a/flutter_secure_storage/example/android/settings.gradle.kts +++ b/flutter_secure_storage/example/android/settings.gradle.kts @@ -20,7 +20,7 @@ pluginManagement { plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("com.android.application") version "8.8.0" apply false - id("org.jetbrains.kotlin.android") version "1.9.25" apply false + id("org.jetbrains.kotlin.android") version "2.2.0" apply false } include(":app")