diff --git a/flutter_secure_storage/CHANGELOG.md b/flutter_secure_storage/CHANGELOG.md index 6f057845..b5ae045e 100644 --- a/flutter_secure_storage/CHANGELOG.md +++ b/flutter_secure_storage/CHANGELOG.md @@ -1,3 +1,10 @@ +## 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 + ## 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..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 @@ -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; @@ -31,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 @@ -61,7 +64,15 @@ public FlutterSecureStorage(Context context, Map options) throws } } - encryptedPreferences = getEncryptedSharedPreferences(deleteOnFailure, options, context.getApplicationContext(), sharedPreferencesName); + 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) { @@ -83,6 +94,7 @@ public void delete(String key) { public void deleteAll() { encryptedPreferences.edit().clear().apply(); } + public Map readAll() { Map result = new HashMap<>(); @@ -102,15 +114,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, boolean isOnlyStrongBoxAllowed) 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 && !isOnlyStrongBoxAllowed) { + // Fallback to not using Strongbox + return getEncryptedSharedPreferences(deleteOnFailure, options, context, sharedPreferencesName, false, isOnlyStrongBoxAllowed); + } + } + } if (!deleteOnFailure) { Log.w(TAG, "initialization failed, resetOnError false, so throwing exception.", e); @@ -121,7 +143,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 +151,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/FlutterSecureStoragePlugin.java b/flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStoragePlugin.java index bf28a2ee..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 @@ -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 = binding.getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE); secureStorage = new FlutterSecureStorage(binding.getApplicationContext(), options); return true; } catch (Exception e) { @@ -123,6 +126,9 @@ private void handleMethodCall(MethodCall call, Result result) { case "deleteAll": handleDeleteAll(result); break; + case "isStrongBoxSupported": + handleStrongBoxAvailable(result); + break; default: result.notImplemented(); } @@ -164,6 +170,10 @@ private void handleDeleteAll(Result result) { result.success(null); } + private void handleStrongBoxAvailable(Result result) { + result.success(isStrongBoxAvailable); + } + @SuppressWarnings("unchecked") private Map extractMapFromObject(Object object) { if (!(object instanceof Map)) { 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/example/android/settings.gradle.kts b/flutter_secure_storage/example/android/settings.gradle.kts index f6d45351..e1500d4d 100644 --- a/flutter_secure_storage/example/android/settings.gradle.kts +++ b/flutter_secure_storage/example/android/settings.gradle.kts @@ -19,7 +19,7 @@ pluginManagement { plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" - id("com.android.application") version "8.8.0" apply false + id("com.android.application") version "8.12.0" apply false id("org.jetbrains.kotlin.android") version "1.9.25" apply false } diff --git a/flutter_secure_storage/lib/flutter_secure_storage.dart b/flutter_secure_storage/lib/flutter_secure_storage.dart index 92291cd5..e1cdf5f0 100644 --- a/flutter_secure_storage/lib/flutter_secure_storage.dart +++ b/flutter_secure_storage/lib/flutter_secure_storage.dart @@ -344,6 +344,19 @@ class FlutterSecureStorage { }); } + /// [aOptions] optional Android options + Future isStrongBoxSupported({ + AndroidOptions? aOptions, + }) async { + if (defaultTargetPlatform == TargetPlatform.android) { + return _platform.isStrongBoxSupported( + options: aOptions?.params ?? this.aOptions.params, + ); + } else { + throw UnsupportedError(_unsupportedPlatform); + } + } + /// Select correct options based on current platform Map _selectOptions( AppleOptions? iOptions, 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, ); } 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..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 @@ -52,4 +52,10 @@ class TestFlutterSecureStoragePlatform extends FlutterSecureStoragePlatform { required Map options, }) async => data[key] = value; + + @override + Future isStrongBoxSupported({ + required Map options, + }) async => + true; } diff --git a/flutter_secure_storage/pubspec.yaml b/flutter_secure_storage/pubspec.yaml index c5d1ba6e..3ebb00ee 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: @@ -31,11 +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_platform_interface: ^2.0.1 - flutter_secure_storage_web: ^2.0.0 - flutter_secure_storage_windows: ^4.0.0 + flutter_secure_storage_darwin: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_darwin + ref: develop + flutter_secure_storage_linux: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_linux + ref: develop + flutter_secure_storage_platform_interface: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_platform_interface + ref: develop + flutter_secure_storage_web: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_web + ref: develop + flutter_secure_storage_windows: + git: + url: git@github.com:QuickBirdEng/flutter_secure_storage.git + path: flutter_secure_storage_windows + 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 ead9dc39..017b49d4 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: develop flutter: plugin: 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..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 @@ -120,4 +120,17 @@ 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({ + 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 1cf5365b..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 @@ -114,4 +114,20 @@ class MethodChannelFlutterSecureStorage extends FlutterSecureStoragePlatform { 'value': value, 'options': options, }); + + @override + Future isStrongBoxSupported({ + required Map options, + }) async { + if (defaultTargetPlatform != TargetPlatform.android) { + throw UnsupportedError('StrongBox is only supported on Android.'); + } + return (await _channel.invokeMethod( + 'isStrongBoxSupported', + { + 'options': options, + }, + )) ?? + false; + } } diff --git a/flutter_secure_storage_web/pubspec.yaml b/flutter_secure_storage_web/pubspec.yaml index 2664a72a..780adf1d 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: develop flutter_web_plugins: sdk: flutter web: ">=0.5.0 <2.0.0" 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..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 @@ -178,6 +178,15 @@ class FlutterSecureStorageWindows extends FlutterSecureStoragePlatform { await _backwardCompatible.delete(key: key, options: options); } } + + @override + Future isStrongBoxSupported({ + required Map options, + }) 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..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 @@ -49,6 +49,15 @@ class FlutterSecureStorageWindows extends FlutterSecureStoragePlatform { }) => Future.value(); + @override + Future isStrongBoxSupported({ + required Map options, + }) async { + throw UnsupportedError( + 'isStrongBoxSupported() is not available on this platform', + ); + } + // @override // Future isCupertinoProtectedDataAvailable() => Future.value(true); // diff --git a/flutter_secure_storage_windows/pubspec.yaml b/flutter_secure_storage_windows/pubspec.yaml index eb277149..d939d353 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: develop path: ^1.8.0 path_provider: ^2.0.0 win32: ^5.5.4