2016-09-29 12:31:55 +02:00
|
|
|
package me.impy.aegis;
|
|
|
|
|
|
|
|
import android.Manifest;
|
|
|
|
import android.content.Context;
|
2017-08-06 16:03:36 +02:00
|
|
|
import android.content.Intent;
|
2016-09-29 12:31:55 +02:00
|
|
|
import android.content.SharedPreferences;
|
|
|
|
import android.os.Bundle;
|
|
|
|
import android.support.annotation.Nullable;
|
|
|
|
import android.view.View;
|
|
|
|
|
2017-08-06 16:03:36 +02:00
|
|
|
import java.lang.reflect.UndeclaredThrowableException;
|
|
|
|
import java.security.NoSuchAlgorithmException;
|
|
|
|
|
|
|
|
import javax.crypto.Cipher;
|
|
|
|
|
2016-09-29 12:31:55 +02:00
|
|
|
import agency.tango.materialintroscreen.MaterialIntroActivity;
|
|
|
|
import agency.tango.materialintroscreen.MessageButtonBehaviour;
|
|
|
|
import agency.tango.materialintroscreen.SlideFragmentBuilder;
|
2017-08-06 16:03:36 +02:00
|
|
|
import me.impy.aegis.crypto.CryptResult;
|
|
|
|
import me.impy.aegis.crypto.MasterKey;
|
|
|
|
import me.impy.aegis.crypto.slots.PasswordSlot;
|
|
|
|
import me.impy.aegis.crypto.slots.SlotCollection;
|
|
|
|
import me.impy.aegis.db.Database;
|
|
|
|
import me.impy.aegis.db.DatabaseFile;
|
2016-09-29 12:31:55 +02:00
|
|
|
|
|
|
|
public class IntroActivity extends MaterialIntroActivity {
|
2017-08-06 16:03:36 +02:00
|
|
|
public static final int RESULT_OK = 0;
|
|
|
|
public static final int RESULT_EXCEPTION = 1;
|
|
|
|
|
|
|
|
private CustomAuthenticatedSlide authenticatedSlide;
|
2016-09-29 12:31:55 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
2017-08-06 21:45:27 +02:00
|
|
|
hideBackButton();
|
2016-09-29 12:31:55 +02:00
|
|
|
|
|
|
|
addSlide(new SlideFragmentBuilder()
|
2017-08-02 21:29:27 +02:00
|
|
|
.backgroundColor(R.color.colorPrimary)
|
|
|
|
.buttonsColor(R.color.colorAccent)
|
|
|
|
.image(R.drawable.intro_shield)
|
|
|
|
.title("Welcome")
|
|
|
|
.description("Aegis is a brand new open source(!) authenticator app which generates tokens for your accounts.")
|
|
|
|
.build());
|
2016-09-29 12:31:55 +02:00
|
|
|
|
|
|
|
addSlide(new SlideFragmentBuilder()
|
2017-08-02 21:29:27 +02:00
|
|
|
.backgroundColor(R.color.colorAccent)
|
|
|
|
.buttonsColor(R.color.colorPrimary)
|
|
|
|
.neededPermissions(new String[]{Manifest.permission.CAMERA})
|
|
|
|
.image(R.drawable.intro_scanner)
|
|
|
|
.title("Permissions")
|
|
|
|
.description("Aegis needs permission to your camera in order to function properly. This is needed to scan QR codes.")
|
|
|
|
.build(),
|
2016-09-29 12:31:55 +02:00
|
|
|
new MessageButtonBehaviour(new View.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
|
|
|
|
}
|
|
|
|
}, "Permission granted"));
|
2017-08-05 15:15:31 +02:00
|
|
|
|
|
|
|
addSlide(new CustomAuthenticationSlide());
|
2017-08-06 16:03:36 +02:00
|
|
|
|
|
|
|
authenticatedSlide = new CustomAuthenticatedSlide();
|
|
|
|
addSlide(authenticatedSlide);
|
|
|
|
|
|
|
|
addSlide(new SlideFragmentBuilder()
|
|
|
|
.backgroundColor(R.color.colorPrimary)
|
|
|
|
.buttonsColor(R.color.colorAccent)
|
|
|
|
.image(R.drawable.intro_shield)
|
|
|
|
.title("All done!")
|
|
|
|
.description("Aegis has been set up and is ready to go.")
|
|
|
|
.build());
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setException(Exception e) {
|
|
|
|
Intent result = new Intent();
|
|
|
|
result.putExtra("exception", e);
|
|
|
|
setResult(RESULT_OK, result);
|
2016-09-29 12:31:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onFinish() {
|
|
|
|
super.onFinish();
|
2017-08-06 16:03:36 +02:00
|
|
|
|
|
|
|
// create the database and database file
|
|
|
|
Database database = new Database();
|
|
|
|
DatabaseFile databaseFile = new DatabaseFile();
|
|
|
|
|
2017-08-06 21:45:27 +02:00
|
|
|
int cryptType = authenticatedSlide.getCryptType();
|
|
|
|
|
|
|
|
// generate the master key
|
|
|
|
MasterKey masterKey = null;
|
|
|
|
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
|
|
|
|
try {
|
|
|
|
masterKey = MasterKey.generate();
|
|
|
|
} catch (Exception e) {
|
|
|
|
setException(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
SlotCollection slots = databaseFile.getSlots();
|
|
|
|
PasswordSlot slot = new PasswordSlot();
|
|
|
|
Cipher cipher = authenticatedSlide.getCipher(slot, Cipher.ENCRYPT_MODE);
|
|
|
|
masterKey.encryptSlot(slot, cipher);
|
|
|
|
slots.add(slot);
|
|
|
|
} catch (Exception e) {
|
|
|
|
setException(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_FINGER) {
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
// finally, save the database
|
2017-08-06 16:03:36 +02:00
|
|
|
try {
|
|
|
|
byte[] bytes = database.serialize();
|
2017-08-06 21:45:27 +02:00
|
|
|
if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
|
|
|
|
databaseFile.setContent(bytes);
|
|
|
|
} else {
|
|
|
|
CryptResult result = masterKey.encrypt(bytes);
|
|
|
|
databaseFile.setContent(result.Data);
|
|
|
|
databaseFile.setCryptParameters(result.Parameters);
|
|
|
|
}
|
2017-08-06 16:03:36 +02:00
|
|
|
databaseFile.save(getApplicationContext());
|
|
|
|
} 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
|
|
|
|
// TODO: show the intro if we can't find any database files
|
2016-09-29 12:31:55 +02:00
|
|
|
SharedPreferences prefs = this.getSharedPreferences("me.impy.aegis", Context.MODE_PRIVATE);
|
|
|
|
prefs.edit().putBoolean("passedIntro", true).apply();
|
|
|
|
}
|
|
|
|
}
|