Skip to content

Commit eb086ba

Browse files
committed
Restart application when keystore reset occurs
1 parent a7f0903 commit eb086ba

File tree

2 files changed

+124
-53
lines changed

2 files changed

+124
-53
lines changed

OpenScienceJournal/whistlepunk_library/src/main/java/com/google/android/apps/forscience/whistlepunk/accounts/arduino/ArduinoAccountProvider.java

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import com.google.android.apps.forscience.auth0.Auth0Token;
1414
import com.google.android.apps.forscience.whistlepunk.ActivityWithNavigationView;
15+
import com.google.android.apps.forscience.whistlepunk.MainActivity;
1516
import com.google.android.apps.forscience.whistlepunk.accounts.AbstractAccountsProvider;
1617
import com.google.android.apps.forscience.whistlepunk.accounts.AppAccount;
1718
import com.google.android.apps.forscience.whistlepunk.remote.StringUtils;
@@ -20,11 +21,16 @@
2021
import java.io.IOException;
2122
import java.security.GeneralSecurityException;
2223
import java.security.KeyStore;
24+
import java.security.KeyStoreException;
25+
import java.security.NoSuchAlgorithmException;
26+
import java.security.cert.CertificateException;
2327

2428
public class ArduinoAccountProvider extends AbstractAccountsProvider {
2529

2630
private static final String LOG_TAG = "ArduinoAccountProvider";
2731
private static final String SHARED_PREFS_FILENAME = "ArduinoSharedPreferences";
32+
private static final String UNENCRYPTED_SHARED_PREFS_FILENAME = "ArduinoSharedPreferencesSafe";
33+
private static final String KEY_KEYSTORE_RESET = "keystore_reset";
2834

2935
private ArduinoAccount arduinoAccount;
3036

@@ -104,37 +110,51 @@ public void showAddAccountDialog(Activity activity) {
104110
public void showAccountSwitcherDialog(Fragment fragment, int requestCode) {
105111
}
106112

107-
private SharedPreferences getBrandNewSharedPreferences() {
108-
MasterKey masterKey = null;
109-
110-
try {
111-
File sharedPrefsFile = new File(applicationContext.getFilesDir().getParent() + "/shared_prefs/" + SHARED_PREFS_FILENAME + ".xml");
112-
boolean deleted = sharedPrefsFile.delete();
113+
private Boolean getKeystoreResetState() {
114+
SharedPreferences prefs = applicationContext.getSharedPreferences(UNENCRYPTED_SHARED_PREFS_FILENAME, Context.MODE_PRIVATE);
115+
return prefs.getBoolean(KEY_KEYSTORE_RESET, false);
116+
}
117+
private void setKeystoreResetState(Boolean state) {
118+
SharedPreferences prefs = applicationContext.getSharedPreferences(UNENCRYPTED_SHARED_PREFS_FILENAME, Context.MODE_PRIVATE);
119+
prefs.edit().putBoolean(KEY_KEYSTORE_RESET, state).apply();
120+
}
113121

114-
Log.d(LOG_TAG, String.format("Shared prefs file deleted: %s", deleted));
122+
private void resetKeystoreAndRestart() {
123+
// delete shared preferences file
124+
File sharedPrefsFile = new File(applicationContext.getFilesDir().getParent() + "/shared_prefs/" + SHARED_PREFS_FILENAME + ".xml");
125+
if (sharedPrefsFile.exists()) {
126+
Boolean deleted = sharedPrefsFile.delete();
127+
Log.i(LOG_TAG, String.format("Shared prefs file \"%s\" deleted: %s", sharedPrefsFile.getAbsolutePath(), deleted));
128+
} else {
129+
Log.i(LOG_TAG, String.format("Shared prefs file \"%s\" non-existent", sharedPrefsFile.getAbsolutePath()));
130+
}
115131

116-
// delete MasterKey
132+
// delete master key
133+
try {
117134
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
118135
keyStore.load(null);
119136
keyStore.deleteEntry(MasterKey.DEFAULT_MASTER_KEY_ALIAS);
120137

121-
// build MasterKey
122-
masterKey = new MasterKey.Builder(applicationContext, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
123-
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
124-
.build();
138+
Log.i(LOG_TAG, "Master key deleted");
139+
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
140+
Log.i(LOG_TAG, "Unable to delete master key");
141+
throw new RuntimeException("Unable to delete master key", e);
142+
}
125143

126-
// create shared preferences
127-
return EncryptedSharedPreferences.create(
128-
applicationContext,
129-
SHARED_PREFS_FILENAME,
130-
masterKey,
131-
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
132-
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
133-
);
134-
} catch (GeneralSecurityException | IOException e) {
135-
Log.e(LOG_TAG, "Unable to retrieve encrypted shared preferences", e);
136-
throw new RuntimeException("Unable to retrieve encrypted shared preferences", e);
144+
// save on (non-encrypted) shared preferences the reset state to avoid loops
145+
setKeystoreResetState(true);
146+
Log.i(LOG_TAG, "Set keystore reset state to TRUE.");
147+
148+
// restart app
149+
Intent intent = new Intent(applicationContext, MainActivity.class);
150+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
151+
Log.i(LOG_TAG, "Restarting application...");
152+
applicationContext.startActivity(intent);
153+
if (applicationContext instanceof Activity) {
154+
((Activity) applicationContext).finish();
137155
}
156+
157+
Runtime.getRuntime().exit(0);
138158
}
139159

140160
private SharedPreferences getSharedPreferences() {
@@ -146,17 +166,30 @@ private SharedPreferences getSharedPreferences() {
146166
.build();
147167

148168
// get or create shared preferences
149-
return EncryptedSharedPreferences.create(
169+
SharedPreferences prefs = EncryptedSharedPreferences.create(
150170
applicationContext,
151171
SHARED_PREFS_FILENAME,
152172
masterKey,
153173
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
154174
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
155175
);
176+
177+
// set keystore reset to false to enable future resets
178+
setKeystoreResetState(false);
179+
Log.i(LOG_TAG, "Set keystore reset state to FALSE.");
180+
181+
return prefs;
156182
} catch (GeneralSecurityException | IOException e) {
157-
Log.e(LOG_TAG, "Unable to retrieve encrypted shared preferences, regenerating master key.", e);
158-
return this.getBrandNewSharedPreferences();
183+
Boolean keystoreReset = getKeystoreResetState();
184+
if (keystoreReset) {
185+
// a keystore reset just occurred, interrupt execution.
186+
throw new RuntimeException("Unable to retrieve encrypted shared preferences", e);
187+
} else {
188+
Log.i(LOG_TAG, "Unable to retrieve encrypted shared preferences, regenerating master key.", e);
189+
resetKeystoreAndRestart();
190+
}
191+
192+
return null;
159193
}
160194
}
161-
162195
}

OpenScienceJournal/whistlepunk_library/src/main/java/com/google/android/apps/forscience/whistlepunk/gdrivesync/GDriveShared.java

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
package com.google.android.apps.forscience.whistlepunk.gdrivesync;
22

3+
import android.app.Activity;
34
import android.content.Context;
5+
import android.content.Intent;
46
import android.content.SharedPreferences;
57
import android.util.Log;
68

79
import androidx.security.crypto.EncryptedSharedPreferences;
810
import androidx.security.crypto.MasterKey;
911

12+
import com.google.android.apps.forscience.whistlepunk.MainActivity;
13+
1014
import java.io.File;
1115
import java.io.IOException;
1216
import java.security.GeneralSecurityException;
1317
import java.security.KeyStore;
18+
import java.security.KeyStoreException;
19+
import java.security.NoSuchAlgorithmException;
20+
import java.security.cert.CertificateException;
1421

1522
public class GDriveShared {
1623
private static final String LOG_TAG = "GDriveShared";
@@ -61,37 +68,51 @@ public static GDriveAccount getCredentials(final Context context) {
6168
return mAccount;
6269
}
6370

64-
private static SharedPreferences getBrandNewSharedPreferences(Context context) {
65-
MasterKey masterKey = null;
66-
67-
try {
68-
File sharedPrefsFile = new File(context.getFilesDir().getParent() + "/shared_prefs/" + SHARED_PREFS_FILENAME + ".xml");
69-
boolean deleted = sharedPrefsFile.delete();
71+
private static Boolean getKeystoreResetState(final Context context) {
72+
SharedPreferences prefs = context.getSharedPreferences(UNENCRYPTED_SHARED_PREFS_FILENAME, Context.MODE_PRIVATE);
73+
return prefs.getBoolean(KEY_KEYSTORE_RESET, false);
74+
}
75+
private static void setKeystoreResetState(final Context context, Boolean state) {
76+
SharedPreferences prefs = context.getSharedPreferences(UNENCRYPTED_SHARED_PREFS_FILENAME, Context.MODE_PRIVATE);
77+
prefs.edit().putBoolean(KEY_KEYSTORE_RESET, state).apply();
78+
}
7079

71-
Log.d(LOG_TAG, String.format("Shared prefs file deleted: %s", deleted));
80+
private static void resetKeystoreAndRestart(final Context context) {
81+
// delete shared preferences file
82+
File sharedPrefsFile = new File(context.getFilesDir().getParent() + "/shared_prefs/" + SHARED_PREFS_FILENAME + ".xml");
83+
if (sharedPrefsFile.exists()) {
84+
Boolean deleted = sharedPrefsFile.delete();
85+
Log.i(LOG_TAG, String.format("Shared prefs file \"%s\" deleted: %s", sharedPrefsFile.getAbsolutePath(), deleted));
86+
} else {
87+
Log.i(LOG_TAG, String.format("Shared prefs file \"%s\" non-existent", sharedPrefsFile.getAbsolutePath()));
88+
}
7289

73-
// delete MasterKey
90+
// delete master key
91+
try {
7492
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
7593
keyStore.load(null);
7694
keyStore.deleteEntry(MasterKey.DEFAULT_MASTER_KEY_ALIAS);
7795

78-
// build MasterKey
79-
masterKey = new MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
80-
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
81-
.build();
96+
Log.i(LOG_TAG, "Master key deleted");
97+
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
98+
Log.i(LOG_TAG, "Unable to delete master key");
99+
throw new RuntimeException("Unable to delete master key", e);
100+
}
82101

83-
// create shared preferences
84-
return EncryptedSharedPreferences.create(
85-
context,
86-
SHARED_PREFS_FILENAME,
87-
masterKey,
88-
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
89-
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
90-
);
91-
} catch (GeneralSecurityException | IOException e) {
92-
Log.e(LOG_TAG, "Unable to retrieve encrypted shared preferences", e);
93-
throw new RuntimeException("Unable to retrieve encrypted shared preferences", e);
102+
// save on (non-encrypted) shared preferences the reset state to avoid loops
103+
setKeystoreResetState(context, true);
104+
Log.i(LOG_TAG, "Set keystore reset state to TRUE.");
105+
106+
// restart app
107+
Intent intent = new Intent(context, MainActivity.class);
108+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
109+
Log.i(LOG_TAG, "Restarting application...");
110+
context.startActivity(intent);
111+
if (context instanceof Activity) {
112+
((Activity) context).finish();
94113
}
114+
115+
Runtime.getRuntime().exit(0);
95116
}
96117

97118
private static SharedPreferences getSharedPreferences(final Context context) {
@@ -103,20 +124,37 @@ private static SharedPreferences getSharedPreferences(final Context context) {
103124
.build();
104125

105126
// get or create shared preferences
106-
return EncryptedSharedPreferences.create(
127+
SharedPreferences prefs = EncryptedSharedPreferences.create(
107128
context,
108129
SHARED_PREFS_FILENAME,
109130
masterKey,
110131
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
111132
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
112133
);
134+
135+
// set keystore reset to false to enable future resets
136+
setKeystoreResetState(context, false);
137+
Log.i(LOG_TAG, "Set keystore reset state to FALSE.");
138+
139+
return prefs;
113140
} catch (GeneralSecurityException | IOException e) {
114-
Log.e(LOG_TAG, "Unable to retrieve encrypted shared preferences, regenerating master key.", e);
115-
return getBrandNewSharedPreferences(context);
141+
Boolean keystoreReset = getKeystoreResetState(context);
142+
if (keystoreReset) {
143+
// a keystore reset just occurred, interrupt execution to avoid loops
144+
throw new RuntimeException("Unable to retrieve encrypted shared preferences", e);
145+
} else {
146+
Log.i(LOG_TAG, "Unable to retrieve encrypted shared preferences, regenerating master key.", e);
147+
resetKeystoreAndRestart(context);
148+
}
149+
150+
return null;
116151
}
117152
}
118153

154+
119155
private static final String SHARED_PREFS_FILENAME = "GoogleDriveSync";
156+
private static final String UNENCRYPTED_SHARED_PREFS_FILENAME = "ArduinoSharedPreferencesSafe";
157+
private static final String KEY_KEYSTORE_RESET = "keystore_reset";
120158
private static final String KEY_ACCOUNT_ID = "account_id";
121159
private static final String KEY_EMAIL = "email";
122160
private static final String KEY_TOKEN = "token";

0 commit comments

Comments
 (0)