Skip to content

Commit be1fdce

Browse files
committed
Enable StrongBox by default on Android with a fallback
1 parent 56d75d5 commit be1fdce

File tree

6 files changed

+73
-29
lines changed

6 files changed

+73
-29
lines changed

flutter_secure_storage/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 10.0.1
2+
* Enabled StrongBox by default, use fallback if it's not available.
3+
4+
# Before fork
5+
16
## 10.0.0-beta.4
27
* [Apple] Merged ios and macos implementation into a new package flutter_secure_storage_darwin
38
* [Apple] Refactored code and added missing options

flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/FlutterSecureStorage.java

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import android.content.Context;
44
import android.content.SharedPreferences;
5+
import android.os.Build;
56
import android.security.keystore.KeyGenParameterSpec;
67
import android.security.keystore.KeyProperties;
8+
import android.security.keystore.StrongBoxUnavailableException;
79
import android.util.Base64;
810
import android.util.Log;
911

@@ -61,7 +63,7 @@ public FlutterSecureStorage(Context context, Map<String, Object> options) throws
6163
}
6264
}
6365

64-
encryptedPreferences = getEncryptedSharedPreferences(deleteOnFailure, options, context.getApplicationContext(), sharedPreferencesName);
66+
encryptedPreferences = getEncryptedSharedPreferences(deleteOnFailure, options, context.getApplicationContext(), sharedPreferencesName, true);
6567
}
6668

6769
public boolean containsKey(String key) {
@@ -102,15 +104,25 @@ private String addPrefixToKey(String key) {
102104
return preferencesKeyPrefix + "_" + key;
103105
}
104106

105-
private SharedPreferences getEncryptedSharedPreferences(boolean deleteOnFailure, Map<String, Object> options, Context context, String sharedPreferencesName) throws GeneralSecurityException, IOException {
107+
private SharedPreferences getEncryptedSharedPreferences(boolean deleteOnFailure, Map<String, Object> options, Context context, String sharedPreferencesName, boolean isStrongBoxBacked) throws GeneralSecurityException, IOException {
106108
try {
107-
final SharedPreferences encryptedPreferences = initializeEncryptedSharedPreferencesManager(context, sharedPreferencesName);
109+
final SharedPreferences encryptedPreferences = initializeEncryptedSharedPreferencesManager(context, sharedPreferencesName, isStrongBoxBacked);
108110
boolean migrated = encryptedPreferences.getBoolean(PREF_KEY_MIGRATED, false);
109111
if (!migrated) {
110112
migrateToEncryptedPreferences(context, sharedPreferencesName, encryptedPreferences, deleteOnFailure, options);
111113
}
112114
return encryptedPreferences;
113115
} catch (GeneralSecurityException | IOException e) {
116+
if (e instanceof GeneralSecurityException) {
117+
Throwable cause = e.getCause();
118+
119+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
120+
if (cause instanceof StrongBoxUnavailableException) {
121+
// Fallback to not using Strongbox
122+
return getEncryptedSharedPreferences(deleteOnFailure, options, context, sharedPreferencesName, false);
123+
}
124+
}
125+
}
114126

115127
if (!deleteOnFailure) {
116128
Log.w(TAG, "initialization failed, resetOnError false, so throwing exception.", e);
@@ -121,24 +133,29 @@ private SharedPreferences getEncryptedSharedPreferences(boolean deleteOnFailure,
121133
context.getSharedPreferences(sharedPreferencesName, Context.MODE_PRIVATE).edit().clear().apply();
122134

123135
try {
124-
return initializeEncryptedSharedPreferencesManager(context, sharedPreferencesName);
136+
return initializeEncryptedSharedPreferencesManager(context, sharedPreferencesName, isStrongBoxBacked);
125137
} catch (Exception f) {
126138
Log.e(TAG, "initialization after reset failed", f);
127139
throw f;
128140
}
129141
}
130142
}
131143

132-
private SharedPreferences initializeEncryptedSharedPreferencesManager(Context context, String sharedPreferencesName) throws GeneralSecurityException, IOException {
144+
private SharedPreferences initializeEncryptedSharedPreferencesManager(Context context, String sharedPreferencesName, boolean isStrongBoxBacked) throws GeneralSecurityException, IOException {
145+
KeyGenParameterSpec.Builder keyGenBuilder = new KeyGenParameterSpec.Builder(
146+
MasterKey.DEFAULT_MASTER_KEY_ALIAS,
147+
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
148+
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
149+
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
150+
.setKeySize(256);
151+
152+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && isStrongBoxBacked) {
153+
keyGenBuilder.setIsStrongBoxBacked(true);
154+
}
155+
133156
MasterKey masterKey = new MasterKey.Builder(context)
134-
.setKeyGenParameterSpec(new KeyGenParameterSpec.Builder(
135-
MasterKey.DEFAULT_MASTER_KEY_ALIAS,
136-
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
137-
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
138-
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
139-
.setKeySize(256)
140-
.build())
141-
.build();
157+
.setKeyGenParameterSpec(keyGenBuilder.build())
158+
.build(isStrongBoxBacked);
142159

143160
return EncryptedSharedPreferences.create(
144161
context,

flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/ciphers/RSACipher18Implementation.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import android.os.Build;
66
import android.security.keystore.KeyGenParameterSpec;
77
import android.security.keystore.KeyProperties;
8+
import android.security.keystore.StrongBoxUnavailableException;
89

910
import androidx.annotation.RequiresApi;
1011

@@ -123,26 +124,38 @@ private void setLocale(Locale locale) {
123124
context.createConfigurationContext(config);
124125
}
125126

127+
private AlgorithmParameterSpec getSpec(boolean isStrongBoxBacked) {
128+
Calendar start = Calendar.getInstance();
129+
Calendar end = Calendar.getInstance();
130+
end.add(Calendar.YEAR, 25);
131+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
132+
return makeAlgorithmParameterSpecLegacy(context, start, end);
133+
}
134+
135+
return makeAlgorithmParameterSpec(context, start, end, isStrongBoxBacked);
136+
}
137+
138+
@RequiresApi(api = Build.VERSION_CODES.P)
126139
private void createKeys(Context context) throws Exception {
127140
final Locale localeBeforeFakingEnglishLocale = Locale.getDefault();
128141
try {
129142
setLocale(Locale.ENGLISH);
130-
Calendar start = Calendar.getInstance();
131-
Calendar end = Calendar.getInstance();
132-
end.add(Calendar.YEAR, 25);
133143

134144
KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance(TYPE_RSA, KEYSTORE_PROVIDER_ANDROID);
135145

136146
AlgorithmParameterSpec spec;
137147

138-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
139-
spec = makeAlgorithmParameterSpecLegacy(context, start, end);
140-
} else {
141-
spec = makeAlgorithmParameterSpec(context, start, end);
142-
}
148+
try {
149+
spec = getSpec(true);
150+
151+
kpGenerator.initialize(spec);
152+
kpGenerator.generateKeyPair();
153+
} catch (StrongBoxUnavailableException e) {
154+
spec = getSpec(false);
143155

144-
kpGenerator.initialize(spec);
145-
kpGenerator.generateKeyPair();
156+
kpGenerator.initialize(spec);
157+
kpGenerator.generateKeyPair();
158+
}
146159
} finally {
147160
setLocale(localeBeforeFakingEnglishLocale);
148161
}
@@ -161,7 +174,7 @@ private AlgorithmParameterSpec makeAlgorithmParameterSpecLegacy(Context context,
161174
}
162175

163176
@RequiresApi(api = Build.VERSION_CODES.M)
164-
protected AlgorithmParameterSpec makeAlgorithmParameterSpec(Context context, Calendar start, Calendar end) {
177+
protected AlgorithmParameterSpec makeAlgorithmParameterSpec(Context context, Calendar start, Calendar end, boolean isStrongBoxBacked) {
165178
final KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT)
166179
.setCertificateSubject(new X500Principal("CN=" + keyAlias))
167180
.setDigests(KeyProperties.DIGEST_SHA256)
@@ -170,6 +183,9 @@ protected AlgorithmParameterSpec makeAlgorithmParameterSpec(Context context, Cal
170183
.setCertificateSerialNumber(BigInteger.valueOf(1))
171184
.setCertificateNotBefore(start.getTime())
172185
.setCertificateNotAfter(end.getTime());
186+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && isStrongBoxBacked) {
187+
builder.setIsStrongBoxBacked(true);
188+
}
173189
return builder.build();
174190
}
175191
}

flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/ciphers/RSACipherOAEPImplementation.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ protected String createKeyAlias() {
3030

3131
@RequiresApi(api = Build.VERSION_CODES.M)
3232
@Override
33-
protected AlgorithmParameterSpec makeAlgorithmParameterSpec(Context context, Calendar start, Calendar end) {
33+
protected AlgorithmParameterSpec makeAlgorithmParameterSpec(Context context, Calendar start, Calendar end, boolean isStrongBoxBacked) {
3434
final KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT)
3535
.setCertificateSubject(new X500Principal("CN=" + keyAlias))
3636
.setDigests(KeyProperties.DIGEST_SHA256)
@@ -39,6 +39,9 @@ protected AlgorithmParameterSpec makeAlgorithmParameterSpec(Context context, Cal
3939
.setCertificateSerialNumber(BigInteger.valueOf(1))
4040
.setCertificateNotBefore(start.getTime())
4141
.setCertificateNotAfter(end.getTime());
42+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && isStrongBoxBacked) {
43+
builder.setIsStrongBoxBacked(true);
44+
}
4245
return builder.build();
4346
}
4447

flutter_secure_storage/android/src/main/java/com/it_nomads/fluttersecurestorage/crypto/MasterKey.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,8 @@ public Builder setKeyGenParameterSpec(@NonNull KeyGenParameterSpec keyGenParamet
264264
* @return The master key.
265265
*/
266266
@NonNull
267-
public MasterKey build() throws GeneralSecurityException, IOException {
268-
return Api23Impl.build(this);
267+
public MasterKey build(boolean isStrongBoxBacked) throws GeneralSecurityException, IOException {
268+
return Api23Impl.build(this, isStrongBoxBacked);
269269
}
270270

271271
static class Api23Impl {
@@ -277,7 +277,7 @@ static String getKeystoreAlias(KeyGenParameterSpec keyGenParameterSpec) {
277277
return keyGenParameterSpec.getKeystoreAlias();
278278
}
279279
@SuppressWarnings("deprecation")
280-
static MasterKey build(Builder builder) throws GeneralSecurityException, IOException {
280+
static MasterKey build(Builder builder, boolean isStrongBoxBacked) throws GeneralSecurityException, IOException {
281281
if (builder.mKeyScheme == null && builder.mKeyGenParameterSpec == null) {
282282
throw new IllegalArgumentException("build() called before "
283283
+ "setKeyGenParameterSpec or setKeyScheme.");
@@ -289,6 +289,9 @@ static MasterKey build(Builder builder) throws GeneralSecurityException, IOExcep
289289
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
290290
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
291291
.setKeySize(DEFAULT_AES_GCM_MASTER_KEY_SIZE);
292+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && isStrongBoxBacked) {
293+
keyGenBuilder.setIsStrongBoxBacked(true);
294+
}
292295
if (builder.mAuthenticationRequired) {
293296
keyGenBuilder.setUserAuthenticationRequired(true);
294297
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {

flutter_secure_storage/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: flutter_secure_storage
22
description: A Flutter plugin for securely storing sensitive data using encrypted storage.
3-
version: 10.0.0-beta.4
3+
version: 10.0.1
44
repository: https://github.com/mogol/flutter_secure_storage/tree/develop/flutter_secure_storage
55

66
environment:

0 commit comments

Comments
 (0)