Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions OpenScienceJournal/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ android {
applicationId "cc.arduino.sciencejournal"
minSdkVersion 19
targetSdkVersion 29
versionCode 9
versionName "1.3.3"
versionCode 10
versionName "1.3.4"
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
}
Expand Down
4 changes: 2 additions & 2 deletions OpenScienceJournal/whistlepunk_library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ android {
defaultConfig {
minSdkVersion 19
targetSdkVersion 29
versionCode 9
versionName "1.3.3"
versionCode 10
versionName "1.3.4"
vectorDrawables.useSupportLibrary = true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,31 @@

import com.google.android.apps.forscience.auth0.Auth0Token;
import com.google.android.apps.forscience.whistlepunk.ActivityWithNavigationView;
import com.google.android.apps.forscience.whistlepunk.MainActivity;
import com.google.android.apps.forscience.whistlepunk.accounts.AbstractAccountsProvider;
import com.google.android.apps.forscience.whistlepunk.accounts.AppAccount;
import com.google.android.apps.forscience.whistlepunk.remote.StringUtils;

import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

public class ArduinoAccountProvider extends AbstractAccountsProvider {

private static final String LOG_TAG = "ArduinoAccountProvider";
private static final String SHARED_PREFS_FILENAME = "ArduinoSharedPreferences";
private static final String UNENCRYPTED_SHARED_PREFS_FILENAME = "ArduinoSharedPreferencesSafe";
private static final String KEY_KEYSTORE_RESET = "keystore_reset";

private ArduinoAccount arduinoAccount;

public ArduinoAccountProvider(final Context context) {
super(context);

final SharedPreferences prefs = getSharedPreferences();
final String jsonToken = prefs.getString("token", null);
if (!StringUtils.isEmpty(jsonToken)) {
Expand Down Expand Up @@ -100,27 +110,86 @@ public void showAddAccountDialog(Activity activity) {
public void showAccountSwitcherDialog(Fragment fragment, int requestCode) {
}

private Boolean getKeystoreResetState() {
SharedPreferences prefs = applicationContext.getSharedPreferences(UNENCRYPTED_SHARED_PREFS_FILENAME, Context.MODE_PRIVATE);
return prefs.getBoolean(KEY_KEYSTORE_RESET, false);
}
private void setKeystoreResetState(Boolean state) {
SharedPreferences prefs = applicationContext.getSharedPreferences(UNENCRYPTED_SHARED_PREFS_FILENAME, Context.MODE_PRIVATE);
prefs.edit().putBoolean(KEY_KEYSTORE_RESET, state).apply();
}

private void resetKeystoreAndRestart() {
// delete shared preferences file
File sharedPrefsFile = new File(applicationContext.getFilesDir().getParent() + "/shared_prefs/" + SHARED_PREFS_FILENAME + ".xml");
if (sharedPrefsFile.exists()) {
Boolean deleted = sharedPrefsFile.delete();
Log.i(LOG_TAG, String.format("Shared prefs file \"%s\" deleted: %s", sharedPrefsFile.getAbsolutePath(), deleted));
} else {
Log.i(LOG_TAG, String.format("Shared prefs file \"%s\" non-existent", sharedPrefsFile.getAbsolutePath()));
}

// delete master key
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
keyStore.deleteEntry(MasterKey.DEFAULT_MASTER_KEY_ALIAS);

Log.i(LOG_TAG, "Master key deleted");
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
Log.i(LOG_TAG, "Unable to delete master key");
throw new RuntimeException("Unable to delete master key", e);
}

// save on (non-encrypted) shared preferences the reset state to avoid loops
setKeystoreResetState(true);
Log.i(LOG_TAG, "Set keystore reset state to TRUE.");

// restart app
Intent intent = new Intent(applicationContext, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.i(LOG_TAG, "Restarting application...");
applicationContext.startActivity(intent);
if (applicationContext instanceof Activity) {
((Activity) applicationContext).finish();
}

Runtime.getRuntime().exit(0);
}

private SharedPreferences getSharedPreferences() {
MasterKey masterKey = null;
try {
// build MasterKey
masterKey = new MasterKey.Builder(applicationContext, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();

return EncryptedSharedPreferences.create(
// get or create shared preferences
SharedPreferences prefs = EncryptedSharedPreferences.create(
applicationContext,
"ArduinoSharedPreferences",
SHARED_PREFS_FILENAME,
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
} catch (GeneralSecurityException e) {
Log.e(LOG_TAG, "Unable to retrieve encrypted shared preferences", e);
throw new RuntimeException("Unable to retrieve encrypted shared preferences", e);
} catch (IOException e) {
Log.e(LOG_TAG, "Unable to retrieve encrypted shared preferences", e);
throw new RuntimeException("Unable to retrieve encrypted shared preferences", e);

// set keystore reset to false to enable future resets
setKeystoreResetState(false);
Log.i(LOG_TAG, "Set keystore reset state to FALSE.");

return prefs;
} catch (GeneralSecurityException | IOException e) {
Boolean keystoreReset = getKeystoreResetState();
if (keystoreReset) {
// a keystore reset just occurred, interrupt execution.
throw new RuntimeException("Unable to retrieve encrypted shared preferences", e);
} else {
Log.i(LOG_TAG, "Unable to retrieve encrypted shared preferences, regenerating master key.", e);
resetKeystoreAndRestart();
}

return null;
}
}

}
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
package com.google.android.apps.forscience.whistlepunk.gdrivesync;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;

import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKeys;
import androidx.security.crypto.MasterKey;

import com.google.android.apps.forscience.whistlepunk.MainActivity;

import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

public class GDriveShared {
private static final String LOG_TAG = "GDriveShared";

private static boolean mAccountLoaded = false;

Expand Down Expand Up @@ -54,23 +68,93 @@ public static GDriveAccount getCredentials(final Context context) {
return mAccount;
}

private static Boolean getKeystoreResetState(final Context context) {
SharedPreferences prefs = context.getSharedPreferences(UNENCRYPTED_SHARED_PREFS_FILENAME, Context.MODE_PRIVATE);
return prefs.getBoolean(KEY_KEYSTORE_RESET, false);
}
private static void setKeystoreResetState(final Context context, Boolean state) {
SharedPreferences prefs = context.getSharedPreferences(UNENCRYPTED_SHARED_PREFS_FILENAME, Context.MODE_PRIVATE);
prefs.edit().putBoolean(KEY_KEYSTORE_RESET, state).apply();
}

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

// delete master key
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
keyStore.deleteEntry(MasterKey.DEFAULT_MASTER_KEY_ALIAS);

Log.i(LOG_TAG, "Master key deleted");
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
Log.i(LOG_TAG, "Unable to delete master key");
throw new RuntimeException("Unable to delete master key", e);
}

// save on (non-encrypted) shared preferences the reset state to avoid loops
setKeystoreResetState(context, true);
Log.i(LOG_TAG, "Set keystore reset state to TRUE.");

// restart app
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.i(LOG_TAG, "Restarting application...");
context.startActivity(intent);
if (context instanceof Activity) {
((Activity) context).finish();
}

Runtime.getRuntime().exit(0);
}

private static SharedPreferences getSharedPreferences(final Context context) {
MasterKey masterKey = null;
try {
final String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
return EncryptedSharedPreferences.create(
PREFS_NAME,
masterKeyAlias,
// build MasterKey
masterKey = new MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();

// get or create shared preferences
SharedPreferences prefs = EncryptedSharedPreferences.create(
context,
SHARED_PREFS_FILENAME,
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
} catch (Exception e) {
throw new RuntimeException(e);

// set keystore reset to false to enable future resets
setKeystoreResetState(context, false);
Log.i(LOG_TAG, "Set keystore reset state to FALSE.");

return prefs;
} catch (GeneralSecurityException | IOException e) {
Boolean keystoreReset = getKeystoreResetState(context);
if (keystoreReset) {
// a keystore reset just occurred, interrupt execution to avoid loops
throw new RuntimeException("Unable to retrieve encrypted shared preferences", e);
} else {
Log.i(LOG_TAG, "Unable to retrieve encrypted shared preferences, regenerating master key.", e);
resetKeystoreAndRestart(context);
}

return null;
}
}

private static final String PREFS_NAME = "GoogleDriveSync";

private static final String SHARED_PREFS_FILENAME = "GoogleDriveSync";
private static final String UNENCRYPTED_SHARED_PREFS_FILENAME = "ArduinoSharedPreferencesSafe";
private static final String KEY_KEYSTORE_RESET = "keystore_reset";
private static final String KEY_ACCOUNT_ID = "account_id";
private static final String KEY_EMAIL = "email";
private static final String KEY_TOKEN = "token";
Expand Down