Add an activity to decrypt the database

This commit is contained in:
Alexander Bakker 2017-08-06 18:15:47 +02:00
parent 53e86db187
commit 722ea50b68
12 changed files with 192 additions and 69 deletions

View file

@ -10,7 +10,7 @@ android {
targetSdkVersion 25
versionCode 1
versionName "1.0"
jackOptions{
jackOptions {
enabled true
}
}
@ -29,6 +29,7 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:recyclerview-v7:25.0.0'
compile 'com.android.support:appcompat-v7:25.0.0'
compile 'com.android.support:design:25.0.0'
compile 'agency.tango.android:material-intro-screen:0.0.3'
@ -37,7 +38,6 @@ dependencies {
compile 'com.android.support:cardview-v7:25.0.0'
compile 'com.android.support:recyclerview-v7:25.0.0'
compile 'com.android.support:support-v4:25.0.0'
compile 'com.android.support:recyclerview-v7:25.0.0'
compile 'com.yarolegovich:lovely-dialog:1.0.4'
compile 'com.mattprecious.swirl:swirl:1.0.0'
testCompile 'junit:junit:4.12'

View file

@ -33,6 +33,8 @@
android:label="@string/title_activity_intro"
android:theme="@style/Theme.Intro">
</activity>
<activity android:name=".AuthActivity">
</activity>
<activity android:name=".PreferencesActivity">
</activity>
<activity android:name=".SetPasswordActivity">

View file

@ -0,0 +1,79 @@
package me.impy.aegis;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import java.lang.reflect.UndeclaredThrowableException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.crypto.slots.PasswordSlot;
import me.impy.aegis.crypto.slots.Slot;
import me.impy.aegis.crypto.slots.SlotCollection;
public class AuthActivity extends AppCompatActivity {
public static final int RESULT_OK = 0;
public static final int RESULT_EXCEPTION = 1;
private EditText textPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_auth);
textPassword = (EditText) findViewById(R.id.text_password);
Intent intent = getIntent();
final SlotCollection slots = (SlotCollection) intent.getSerializableExtra("slots");
Button button = (Button) findViewById(R.id.button_decrypt);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MasterKey masterKey = null;
try {
if (slots.has(PasswordSlot.class)) {
PasswordSlot slot = slots.find(PasswordSlot.class);
char[] password = getPassword(true);
SecretKey key = slot.deriveKey(password);
CryptoUtils.zero(password);
Cipher cipher = Slot.createCipher(key, Cipher.DECRYPT_MODE);
masterKey = MasterKey.decryptSlot(slot, cipher);
}
} catch (Exception e) {
// TODO: feedback
throw new UndeclaredThrowableException(e);
}
// send the master key back to the main activity
Intent result = new Intent();
result.putExtra("key", masterKey);
setResult(RESULT_OK, result);
finish();
}
});
}
private char[] getPassword(boolean clear) {
char[] password = getEditTextChars(textPassword);
if (clear) {
textPassword.getText().clear();
}
return password;
}
private static char[] getEditTextChars(EditText text) {
Editable editable = text.getText();
char[] chars = new char[editable.length()];
editable.getChars(0, editable.length(), chars, 0);
return chars;
}
}

View file

@ -1,5 +1,6 @@
package me.impy.aegis;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -19,10 +20,13 @@ public class CustomAuthenticationSlide extends SlideFragment {
buttonGroup = (RadioGroup) view.findViewById(R.id.rg_authenticationMethod);
RadioButton button = (RadioButton) view.findViewById(R.id.rb_fingerprint);
button.setOnClickListener(v -> {
if (canMoveFurther()) {
buttonGroup.clearCheck();
Toast.makeText(getActivity().getBaseContext(), "Fingerprint is not supported yet", Toast.LENGTH_SHORT).show();
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (canMoveFurther()) {
buttonGroup.clearCheck();
Toast.makeText(CustomAuthenticationSlide.this.getActivity(), "Fingerprint is not supported yet", Toast.LENGTH_SHORT).show();
}
}
});

View file

@ -54,6 +54,7 @@ public class MainActivity extends AppCompatActivity {
private static final int CODE_GET_KEYINFO = 0;
private static final int CODE_ADD_KEYINFO = 1;
private static final int CODE_DO_INTRO = 2;
private static final int CODE_DECRYPT = 3;
RecyclerView rvKeyProfiles;
KeyProfileAdapter mKeyProfileAdapter;
@ -72,6 +73,18 @@ public class MainActivity extends AppCompatActivity {
if (!prefs.getBoolean("passedIntro", false)) {
Intent intro = new Intent(this, IntroActivity.class);
startActivityForResult(intro, CODE_DO_INTRO);
} else {
try {
db.load();
} catch (Exception e) {
// TODO: feedback
throw new UndeclaredThrowableException(e);
}
if (!db.isDecrypted()) {
Intent intent = new Intent(this, AuthActivity.class);
intent.putExtra("slots", db.getFile().getSlots());
startActivityForResult(intent, CODE_DECRYPT);
}
}
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
@ -132,6 +145,9 @@ public class MainActivity extends AppCompatActivity {
case CODE_DO_INTRO:
onDoIntroResult(resultCode, data);
break;
case CODE_DECRYPT:
onDecryptResult(resultCode, data);
break;
}
}
@ -194,6 +210,20 @@ public class MainActivity extends AppCompatActivity {
// TODO: feedback
throw new UndeclaredThrowableException(e);
}
loadKeyProfiles();
}
private void onDecryptResult(int resultCode, Intent data) {
MasterKey key = (MasterKey) data.getSerializableExtra("key");
try {
db.setMasterKey(key);
} catch (Exception e) {
// TODO: feedback
throw new UndeclaredThrowableException(e);
}
loadKeyProfiles();
}
@Override
@ -304,7 +334,7 @@ public class MainActivity extends AppCompatActivity {
{
Log.println(Log.DEBUG, "OKK ", "OKKK");
Intent scannerActivity = new Intent(getApplicationContext(), ScannerActivity.class);
startActivityForResult(scannerActivity, GET_KEYINFO);
startActivityForResult(scannerActivity, CODE_GET_KEYINFO);
}
}
@ -356,65 +386,11 @@ public class MainActivity extends AppCompatActivity {
}
}
/*private void loadDatabase() {
try {
databaseFile = DatabaseFile.load(getApplicationContext());
} catch (IOException e) {
// the database file doesn't exist yet
//createDatabase();
saveDatabase();
return;
} catch (Exception e) {
// something else went wrong
throw new UndeclaredThrowableException(e);
}
byte[] content = databaseFile.getContent();
if (databaseFile.isEncrypted()) {
try {
SlotCollection slots = databaseFile.getSlots();
// look up slots in order of preference
if (slots.has(FingerprintSlot.class)) {
FingerprintSlot slot = slots.find(FingerprintSlot.class);
} else if (slots.has(PasswordSlot.class)) {
PasswordSlot slot = slots.find(PasswordSlot.class);
SecretKey derivedKey = slot.deriveKey("testpassword".toCharArray());
Cipher cipher = Slot.createCipher(derivedKey, Cipher.DECRYPT_MODE);
masterKey = MasterKey.decryptSlot(slot, cipher);
//} else if (slots.has(RawSlot.class)) {
} else {
throw new Exception("the slot collection doesn't contain any supported slot types");
}
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}
CryptResult result;
try {
result = masterKey.decrypt(content, databaseFile.getCryptParameters());
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}
content = result.Data;
}
database = new Database();
try {
database.deserialize(content);
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}
try {
mKeyProfiles.addAll(database.getKeys());
mKeyProfileAdapter.notifyDataSetChanged();
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}
}*/
private void saveDatabase() {
if (!db.isDecrypted()) {
return;
}
try {
db.save();
} catch (Exception e) {
@ -423,6 +399,15 @@ public class MainActivity extends AppCompatActivity {
}
}
private void loadKeyProfiles() {
try {
mKeyProfiles.addAll(db.getKeys());
mKeyProfileAdapter.notifyDataSetChanged();
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean causeIsKeyUserNotAuthenticated(Exception e) {
// TODO: is there a way to catch "Key user not authenticated" specifically aside from checking the exception message?
return e.getCause().getMessage().equals("Key user not authenticated");

View file

@ -3,4 +3,4 @@ package me.impy.aegis.crypto;
public class CryptParameters {
public byte[] Nonce;
public byte[] Tag;
}
}

View file

@ -26,7 +26,7 @@ public class CryptoUtils {
public static final byte CRYPTO_NONCE_SIZE = 12;
public static final byte CRYPTO_SALT_SIZE = 32;
// TODO: decide on a 'secure-enough' iteration count
public static final short CRYPTO_ITERATION_COUNT = 2000;
public static final short CRYPTO_ITERATION_COUNT = 10000;
public static final String CRYPTO_CIPHER_RAW = "AES/ECB/NoPadding";
public static final String CRYPTO_CIPHER_AEAD = "AES/GCM/NoPadding";
// TODO: use a separate library for an HMAC-SHA256 implementation

View file

@ -2,6 +2,7 @@ package me.impy.aegis.crypto.slots;
import android.annotation.SuppressLint;
import java.io.Serializable;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@ -14,7 +15,7 @@ import javax.crypto.spec.SecretKeySpec;
import me.impy.aegis.crypto.CryptoUtils;
public abstract class Slot {
public abstract class Slot implements Serializable {
public final static byte TYPE_RAW = 0x00;
public final static byte TYPE_DERIVED = 0x01;
public final static byte TYPE_FINGERPRINT = 0x02;

View file

@ -1,12 +1,13 @@
package me.impy.aegis.crypto.slots;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import me.impy.aegis.util.LittleByteBuffer;
public class SlotCollection implements Iterable<Slot> {
public class SlotCollection implements Iterable<Slot>, Serializable {
private List<Slot> _slots = new ArrayList<>();
public static byte[] serialize(SlotCollection slots) {

View file

@ -66,6 +66,10 @@ public class DatabaseManager {
return _db.getKeys();
}
public DatabaseFile getFile() {
return _file;
}
public boolean isLoaded() {
return _file != null;
}

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="me.impy.aegis.AuthActivity">
<LinearLayout
android:orientation="vertical"
android:layout_width="344dp"
android:layout_height="495dp"
android:layout_margin="32dp"
tools:layout_editor_absoluteY="8dp"
tools:layout_editor_absoluteX="8dp">
<TextView
android:text="@string/authentication"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textColor="@color/primary_text_inverted"
android:id="@+id/textView2" />
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="12dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textView4"
android:text="@string/authentication_enter_password"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:id="@+id/text_password"/>
<Button
android:id="@+id/button_decrypt"
android:layout_width="125dp"
android:layout_height="wrap_content"
android:text="Decrypt" />
</LinearLayout>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>

View file

@ -39,6 +39,8 @@
<string name="authentication_method_fingerprint">Fingerprint</string>
<string name="authentication_method_fingerprint_description">This allows you to use the fingerprints registered on this device to open the app</string>
<string name="authentication_method_set_password">Password</string>
<string name="authentication_enter_password">Enter your password</string>
<string name="authentication">Open the database</string>
<string name="set_password">Please enter a password</string>
<string name="set_password_confirm">Please confirm the password</string>