11package com .google .android .apps .forscience .whistlepunk .gdrivesync ;
22
3+ import android .app .Activity ;
34import android .content .Context ;
5+ import android .content .Intent ;
46import android .content .SharedPreferences ;
57import android .util .Log ;
68
79import androidx .security .crypto .EncryptedSharedPreferences ;
810import androidx .security .crypto .MasterKey ;
911
12+ import com .google .android .apps .forscience .whistlepunk .MainActivity ;
13+
1014import java .io .File ;
1115import java .io .IOException ;
1216import java .security .GeneralSecurityException ;
1317import java .security .KeyStore ;
18+ import java .security .KeyStoreException ;
19+ import java .security .NoSuchAlgorithmException ;
20+ import java .security .cert .CertificateException ;
1421
1522public 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