From 0afc1b3a97c7606307ef9507e0001f299a1079b2 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Mon, 27 Nov 2017 19:22:10 +0100 Subject: [PATCH] Derive passwords on a separate thread in the intro Also, show a progress dialog while waiting --- .../main/java/me/impy/aegis/AuthActivity.java | 8 -- .../impy/aegis/CustomAuthenticatedSlide.java | 21 ++---- .../java/me/impy/aegis/DerivationTask.java | 57 +++++++++++++++ .../java/me/impy/aegis/IntroActivity.java | 73 ++++++++++++++----- 4 files changed, 116 insertions(+), 43 deletions(-) create mode 100644 app/src/main/java/me/impy/aegis/DerivationTask.java diff --git a/app/src/main/java/me/impy/aegis/AuthActivity.java b/app/src/main/java/me/impy/aegis/AuthActivity.java index eee62946..713325b1 100644 --- a/app/src/main/java/me/impy/aegis/AuthActivity.java +++ b/app/src/main/java/me/impy/aegis/AuthActivity.java @@ -101,14 +101,6 @@ public class AuthActivity extends AppCompatActivity implements FingerprintUiHelp builder.create().show(); } - /*DerivationTask task = new DerivationTask(this); - DerivationTask.Params params = new DerivationTask.Params() {{ - Slots = _slots; - Slot = (PasswordSlot) slot; - Password = AuthHelper.getPassword(_textPassword, true); - }}; - masterKey = task.execute(params).get();*/ - private void trySlots(Class type, Object obj) { new SlotCollectionTask(type, this, this).execute(new SlotCollectionTask.Params(){{ Slots = _slots; diff --git a/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java b/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java index f50b40d6..476b5eee 100644 --- a/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java +++ b/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java @@ -22,10 +22,7 @@ import java.lang.reflect.UndeclaredThrowableException; import javax.crypto.Cipher; import javax.crypto.SecretKey; -import me.impy.aegis.crypto.CryptoUtils; import me.impy.aegis.crypto.KeyStoreHandle; -import me.impy.aegis.crypto.slots.FingerprintSlot; -import me.impy.aegis.crypto.slots.PasswordSlot; import me.impy.aegis.crypto.slots.Slot; import me.impy.aegis.finger.FingerprintUiHelper; import me.impy.aegis.helpers.AuthHelper; @@ -60,18 +57,12 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH return _cryptType; } - public Cipher getCipher(Slot slot) throws Exception { - if (slot instanceof PasswordSlot) { - char[] password = AuthHelper.getPassword(_textPassword, true); - byte[] salt = CryptoUtils.generateSalt(); - SecretKey key = ((PasswordSlot)slot).deriveKey(password, salt, CryptoUtils.CRYPTO_SCRYPT_N, CryptoUtils.CRYPTO_SCRYPT_r, CryptoUtils.CRYPTO_SCRYPT_p); - CryptoUtils.zero(password); - return Slot.createCipher(key, Cipher.ENCRYPT_MODE); - } else if (slot instanceof FingerprintSlot) { - return _fingerCipher; - } else { - throw new RuntimeException(); - } + public char[] getPassword() { + return AuthHelper.getPassword(_textPassword, true); + } + + public Cipher getFingerCipher() { + return _fingerCipher; } public void setBgColor(int color) { diff --git a/app/src/main/java/me/impy/aegis/DerivationTask.java b/app/src/main/java/me/impy/aegis/DerivationTask.java new file mode 100644 index 00000000..9ddfe59b --- /dev/null +++ b/app/src/main/java/me/impy/aegis/DerivationTask.java @@ -0,0 +1,57 @@ +package me.impy.aegis; + +import android.app.ProgressDialog; +import android.content.Context; +import android.os.AsyncTask; + +import javax.crypto.SecretKey; + +import me.impy.aegis.crypto.CryptoUtils; +import me.impy.aegis.crypto.slots.PasswordSlot; + +public class DerivationTask extends AsyncTask { + private Callback _cb; + private ProgressDialog _dialog; + + public DerivationTask(Context context, Callback cb) { + _cb = cb; + _dialog = new ProgressDialog(context); + _dialog.setCancelable(false); + } + + @Override + protected SecretKey doInBackground(DerivationTask.Params... args) { + DerivationTask.Params params = args[0]; + try { + byte[] salt = CryptoUtils.generateSalt(); + SecretKey key = params.Slot.deriveKey(params.Password, salt, CryptoUtils.CRYPTO_SCRYPT_N, CryptoUtils.CRYPTO_SCRYPT_r, CryptoUtils.CRYPTO_SCRYPT_p); + CryptoUtils.zero(params.Password); + return key; + } catch (Exception e) { + return null; + } + } + + @Override + protected void onPreExecute() { + _dialog.setMessage("Deriving key from password"); + _dialog.show(); + } + + @Override + protected void onPostExecute(SecretKey key) { + if (_dialog.isShowing()) { + _dialog.dismiss(); + } + _cb.onTaskFinished(key); + } + + static class Params { + public PasswordSlot Slot; + public char[] Password; + } + + interface Callback { + void onTaskFinished(SecretKey key); + } +} diff --git a/app/src/main/java/me/impy/aegis/IntroActivity.java b/app/src/main/java/me/impy/aegis/IntroActivity.java index 3860d5ee..50b30aed 100644 --- a/app/src/main/java/me/impy/aegis/IntroActivity.java +++ b/app/src/main/java/me/impy/aegis/IntroActivity.java @@ -12,16 +12,19 @@ import com.github.paolorotolo.appintro.AppIntroFragment; import com.github.paolorotolo.appintro.model.SliderPage; import javax.crypto.Cipher; +import javax.crypto.SecretKey; import me.impy.aegis.crypto.CryptResult; +import me.impy.aegis.crypto.CryptoUtils; import me.impy.aegis.crypto.MasterKey; import me.impy.aegis.crypto.slots.FingerprintSlot; import me.impy.aegis.crypto.slots.PasswordSlot; +import me.impy.aegis.crypto.slots.Slot; import me.impy.aegis.crypto.slots.SlotCollection; import me.impy.aegis.db.Database; import me.impy.aegis.db.DatabaseFile; -public class IntroActivity extends AppIntro { +public class IntroActivity extends AppIntro implements DerivationTask.Callback { public static final int RESULT_OK = 0; public static final int RESULT_EXCEPTION = 1; @@ -29,6 +32,11 @@ public class IntroActivity extends AppIntro { private CustomAuthenticationSlide _authenticationSlide; private Fragment _endSlide; + private Database _database; + private DatabaseFile _databaseFile; + private PasswordSlot _passwordSlot; + private Cipher _passwordCipher; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -65,6 +73,10 @@ public class IntroActivity extends AppIntro { endSliderPage.setBgColor(getResources().getColor(R.color.colorPrimary)); _endSlide = AppIntroFragment.newInstance(endSliderPage); addSlide(_endSlide); + + // create the database and database file + _database = new Database(); + _databaseFile = new DatabaseFile(); } private void setException(Exception e) { @@ -76,10 +88,17 @@ public class IntroActivity extends AppIntro { @Override public void onSlideChanged(Fragment oldFragment, Fragment newFragment) { - // skip to the last slide if no encryption will be used - if (oldFragment == _authenticationSlide && newFragment != _endSlide) { - Intent intent = getIntent(); - int cryptType = intent.getIntExtra("cryptType", CustomAuthenticationSlide.CRYPT_TYPE_INVALID); + Intent intent = getIntent(); + int cryptType = intent.getIntExtra("cryptType", CustomAuthenticationSlide.CRYPT_TYPE_INVALID); + + if (newFragment == _endSlide && cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) { + _passwordSlot = new PasswordSlot(); + new DerivationTask(this, this).execute(new DerivationTask.Params() {{ + Slot = _passwordSlot; + Password = _authenticatedSlide.getPassword(); + }}); + } else if (oldFragment == _authenticationSlide && newFragment != _endSlide) { + // skip to the last slide if no encryption will be used if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_NONE) { // TODO: no magic indices getPager().setCurrentItem(5); @@ -91,10 +110,6 @@ public class IntroActivity extends AppIntro { public void onDonePressed(Fragment currentFragment) { super.onDonePressed(currentFragment); - // create the database and database file - Database database = new Database(); - DatabaseFile databaseFile = new DatabaseFile(); - int cryptType = _authenticatedSlide.getCryptType(); // generate the master key @@ -108,15 +123,20 @@ public class IntroActivity extends AppIntro { } } - SlotCollection slots = databaseFile.getSlots(); + SlotCollection slots = _databaseFile.getSlots(); if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) { try { // encrypt the master key with a key derived from the user's password // and add it to the list of slots - PasswordSlot slot = new PasswordSlot(); - Cipher cipher = _authenticatedSlide.getCipher(slot); - slots.encrypt(slot, masterKey, cipher); - slots.add(slot); + if (_passwordSlot == null || _passwordCipher == null) { + throw new RuntimeException(); + } + try { + slots.encrypt(_passwordSlot, masterKey, _passwordCipher); + slots.add(_passwordSlot); + } catch (Exception e) { + setException(e); + } } catch (Exception e) { setException(e); return; @@ -128,7 +148,7 @@ public class IntroActivity extends AppIntro { // encrypt the master key with the fingerprint key // and add it to the list of slots FingerprintSlot slot = new FingerprintSlot(); - Cipher cipher = _authenticatedSlide.getCipher(slot); + Cipher cipher = _authenticatedSlide.getFingerCipher(); slots.encrypt(slot, masterKey, cipher); slots.add(slot); } catch (Exception e) { @@ -139,15 +159,15 @@ public class IntroActivity extends AppIntro { // finally, save the database try { - byte[] bytes = database.serialize(); + byte[] bytes = _database.serialize(); if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_NONE) { - databaseFile.setContent(bytes); + _databaseFile.setContent(bytes); } else { CryptResult result = masterKey.encrypt(bytes); - databaseFile.setContent(result.Data); - databaseFile.setCryptParameters(result.Parameters); + _databaseFile.setContent(result.Data); + _databaseFile.setCryptParameters(result.Parameters); } - databaseFile.save(getApplicationContext()); + _databaseFile.save(getApplicationContext()); } catch (Exception e) { setException(e); return; @@ -163,4 +183,17 @@ public class IntroActivity extends AppIntro { prefs.edit().putBoolean("passedIntro", true).apply(); finish(); } + + @Override + public void onTaskFinished(SecretKey key) { + if (key != null) { + try { + _passwordCipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE); + } catch (Exception e) { + setException(e); + } + } else { + setException(new NullPointerException()); + } + } }