Derive passwords on a separate thread in the intro

Also, show a progress dialog while waiting
This commit is contained in:
Alexander Bakker 2017-11-27 19:22:10 +01:00
parent 5df20ef525
commit 0afc1b3a97
4 changed files with 116 additions and 43 deletions

View file

@ -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 <T extends Slot> void trySlots(Class<T> type, Object obj) {
new SlotCollectionTask<T>(type, this, this).execute(new SlotCollectionTask.Params(){{
Slots = _slots;

View file

@ -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) {

View file

@ -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<DerivationTask.Params, Void, SecretKey> {
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);
}
}

View file

@ -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);
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());
}
}
}