Skip to content

Commit d1537b9

Browse files
authored
Merge pull request #40 from arduino/fix/invalid-protocol-buffer-exception-on-init
Fix/invalid protocol buffer exception on init
2 parents dfd909d + 8b8ffff commit d1537b9

File tree

4 files changed

+174
-21
lines changed

4 files changed

+174
-21
lines changed

OpenScienceJournal/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ android {
99
applicationId "cc.arduino.sciencejournal"
1010
minSdkVersion 19
1111
targetSdkVersion 29
12-
versionCode 9
13-
versionName "1.3.3"
12+
versionCode 10
13+
versionName "1.3.4"
1414
multiDexEnabled true
1515
vectorDrawables.useSupportLibrary = true
1616
}

OpenScienceJournal/whistlepunk_library/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ android {
2121
defaultConfig {
2222
minSdkVersion 19
2323
targetSdkVersion 29
24-
versionCode 9
25-
versionName "1.3.3"
24+
versionCode 10
25+
versionName "1.3.4"
2626
vectorDrawables.useSupportLibrary = true
2727
}
2828

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

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,31 @@
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;
1819

20+
import java.io.File;
1921
import java.io.IOException;
2022
import java.security.GeneralSecurityException;
23+
import java.security.KeyStore;
24+
import java.security.KeyStoreException;
25+
import java.security.NoSuchAlgorithmException;
26+
import java.security.cert.CertificateException;
2127

2228
public class ArduinoAccountProvider extends AbstractAccountsProvider {
2329

2430
private static final String LOG_TAG = "ArduinoAccountProvider";
31+
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";
2534

2635
private ArduinoAccount arduinoAccount;
2736

2837
public ArduinoAccountProvider(final Context context) {
2938
super(context);
39+
3040
final SharedPreferences prefs = getSharedPreferences();
3141
final String jsonToken = prefs.getString("token", null);
3242
if (!StringUtils.isEmpty(jsonToken)) {
@@ -100,27 +110,86 @@ public void showAddAccountDialog(Activity activity) {
100110
public void showAccountSwitcherDialog(Fragment fragment, int requestCode) {
101111
}
102112

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+
}
121+
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+
}
131+
132+
// delete master key
133+
try {
134+
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
135+
keyStore.load(null);
136+
keyStore.deleteEntry(MasterKey.DEFAULT_MASTER_KEY_ALIAS);
137+
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+
}
143+
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();
155+
}
156+
157+
Runtime.getRuntime().exit(0);
158+
}
159+
103160
private SharedPreferences getSharedPreferences() {
104161
MasterKey masterKey = null;
105162
try {
163+
// build MasterKey
106164
masterKey = new MasterKey.Builder(applicationContext, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
107165
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
108166
.build();
109167

110-
return EncryptedSharedPreferences.create(
168+
// get or create shared preferences
169+
SharedPreferences prefs = EncryptedSharedPreferences.create(
111170
applicationContext,
112-
"ArduinoSharedPreferences",
171+
SHARED_PREFS_FILENAME,
113172
masterKey,
114173
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
115174
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
116175
);
117-
} catch (GeneralSecurityException e) {
118-
Log.e(LOG_TAG, "Unable to retrieve encrypted shared preferences", e);
119-
throw new RuntimeException("Unable to retrieve encrypted shared preferences", e);
120-
} catch (IOException e) {
121-
Log.e(LOG_TAG, "Unable to retrieve encrypted shared preferences", e);
122-
throw new RuntimeException("Unable to retrieve encrypted shared preferences", e);
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;
182+
} catch (GeneralSecurityException | IOException e) {
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;
123193
}
124194
}
125-
126195
}

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

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
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;
7+
import android.util.Log;
58

69
import androidx.security.crypto.EncryptedSharedPreferences;
7-
import androidx.security.crypto.MasterKeys;
10+
import androidx.security.crypto.MasterKey;
11+
12+
import com.google.android.apps.forscience.whistlepunk.MainActivity;
13+
14+
import java.io.File;
15+
import java.io.IOException;
16+
import java.security.GeneralSecurityException;
17+
import java.security.KeyStore;
18+
import java.security.KeyStoreException;
19+
import java.security.NoSuchAlgorithmException;
20+
import java.security.cert.CertificateException;
821

922
public class GDriveShared {
23+
private static final String LOG_TAG = "GDriveShared";
1024

1125
private static boolean mAccountLoaded = false;
1226

@@ -54,23 +68,93 @@ public static GDriveAccount getCredentials(final Context context) {
5468
return mAccount;
5569
}
5670

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+
}
79+
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+
}
89+
90+
// delete master key
91+
try {
92+
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
93+
keyStore.load(null);
94+
keyStore.deleteEntry(MasterKey.DEFAULT_MASTER_KEY_ALIAS);
95+
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+
}
101+
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();
113+
}
114+
115+
Runtime.getRuntime().exit(0);
116+
}
117+
57118
private static SharedPreferences getSharedPreferences(final Context context) {
119+
MasterKey masterKey = null;
58120
try {
59-
final String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
60-
return EncryptedSharedPreferences.create(
61-
PREFS_NAME,
62-
masterKeyAlias,
121+
// build MasterKey
122+
masterKey = new MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
123+
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
124+
.build();
125+
126+
// get or create shared preferences
127+
SharedPreferences prefs = EncryptedSharedPreferences.create(
63128
context,
129+
SHARED_PREFS_FILENAME,
130+
masterKey,
64131
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
65132
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
66133
);
67-
} catch (Exception e) {
68-
throw new RuntimeException(e);
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;
140+
} catch (GeneralSecurityException | IOException e) {
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;
69151
}
70152
}
71153

72-
private static final String PREFS_NAME = "GoogleDriveSync";
73154

155+
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";
74158
private static final String KEY_ACCOUNT_ID = "account_id";
75159
private static final String KEY_EMAIL = "email";
76160
private static final String KEY_TOKEN = "token";

0 commit comments

Comments
 (0)