Aegis/app/src/main/java/me/impy/aegis/IntroActivity.java
Alexander Bakker a140ba8506 Fix various issues by moving most global state logic out of MainActivity
* Move global state to a new class that overrides Application
* Make sure all preferences are stored in the same place and follow the same naming convention
2017-12-24 18:23:11 +01:00

210 lines
7.9 KiB
Java

package me.impy.aegis;
import android.Manifest;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import com.github.paolorotolo.appintro.AppIntro;
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.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;
import me.impy.aegis.db.DatabaseManager;
public class IntroActivity extends AppIntro implements DerivationTask.Callback {
public static final int RESULT_OK = 0;
public static final int RESULT_EXCEPTION = 1;
private CustomAuthenticatedSlide _authenticatedSlide;
private CustomAuthenticationSlide _authenticationSlide;
private Fragment _endSlide;
private Database _database;
private DatabaseFile _databaseFile;
private PasswordSlot _passwordSlot;
private Cipher _passwordCipher;
private AegisApplication _app;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_app = (AegisApplication) getApplication();
showSkipButton(false);
//showPagerIndicator(false);
setGoBackLock(true);
SliderPage homeSliderPage = new SliderPage();
homeSliderPage.setTitle("Welcome");
homeSliderPage.setDescription("Aegis is a secure, free and open source 2FA app");
homeSliderPage.setImageDrawable(R.drawable.intro_shield);
homeSliderPage.setBgColor(getResources().getColor(R.color.colorPrimary));
addSlide(AppIntroFragment.newInstance(homeSliderPage));
SliderPage permSliderPage = new SliderPage();
permSliderPage.setTitle("Permissions");
permSliderPage.setDescription("Aegis needs permission to use your camera in order to scan QR codes. " +
"It also needs access to external storage to able to export the database.");
permSliderPage.setImageDrawable(R.drawable.intro_scanner);
permSliderPage.setBgColor(getResources().getColor(R.color.colorAccent));
addSlide(AppIntroFragment.newInstance(permSliderPage));
askForPermissions(new String[]{
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
}, 2);
_authenticationSlide = new CustomAuthenticationSlide();
_authenticationSlide.setBgColor(getResources().getColor(R.color.colorHeaderSuccess));
addSlide(_authenticationSlide);
_authenticatedSlide = new CustomAuthenticatedSlide();
_authenticatedSlide.setBgColor(getResources().getColor(R.color.colorPrimary));
addSlide(_authenticatedSlide);
SliderPage endSliderPage = new SliderPage();
endSliderPage.setTitle("All done!");
endSliderPage.setDescription("Aegis has been set up and is ready to go.");
endSliderPage.setImageDrawable(R.drawable.intro_shield);
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) {
Intent result = new Intent();
result.putExtra("exception", e);
setResult(RESULT_EXCEPTION, result);
finish();
}
@Override
public void onSlideChanged(Fragment oldFragment, Fragment newFragment) {
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);
}
}
}
@Override
public void onDonePressed(Fragment currentFragment) {
super.onDonePressed(currentFragment);
int cryptType = _authenticatedSlide.getCryptType();
// wait for the key derivation background task
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE &&
(_passwordSlot == null || _passwordCipher == null)) {
return;
}
// generate the master key
MasterKey masterKey = null;
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
try {
masterKey = MasterKey.generate();
} catch (Exception e) {
setException(e);
return;
}
}
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
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;
}
}
if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_FINGER) {
try {
// encrypt the master key with the fingerprint key
// and add it to the list of slots
FingerprintSlot slot = new FingerprintSlot();
Cipher cipher = _authenticatedSlide.getFingerCipher();
slots.encrypt(slot, masterKey, cipher);
slots.add(slot);
} catch (Exception e) {
setException(e);
return;
}
}
// finally, save the database
try {
byte[] bytes = _database.serialize();
if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
_databaseFile.setContent(bytes);
} else {
CryptResult result = masterKey.encrypt(bytes);
_databaseFile.setContent(result.Data);
_databaseFile.setCryptParameters(result.Parameters);
}
DatabaseManager.save(getApplicationContext(), _databaseFile);
} catch (Exception e) {
setException(e);
return;
}
// send the master key back to the main activity
Intent result = new Intent();
result.putExtra("key", masterKey);
setResult(RESULT_OK, result);
// skip the intro from now on
_app.getPreferences().edit().putBoolean("pref_intro", 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());
}
}
}