mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-14 22:12:55 +00:00
Verify integrity of decrypted slots and display a dialog on error
This commit is contained in:
parent
1ae9364c5e
commit
7269cc2b6a
6 changed files with 74 additions and 9 deletions
|
@ -2,11 +2,13 @@ package me.impy.aegis;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.hardware.fingerprint.FingerprintManager;
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -14,14 +16,12 @@ import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.RadioButton;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import java.lang.reflect.UndeclaredThrowableException;
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
import me.impy.aegis.crypto.CryptoUtils;
|
import me.impy.aegis.crypto.CryptoUtils;
|
||||||
|
@ -101,13 +101,26 @@ public class AuthActivity extends AppCompatActivity implements FingerprintUiHelp
|
||||||
throw new UndeclaredThrowableException(e);
|
throw new UndeclaredThrowableException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// send the master key back to the main activity
|
|
||||||
setKey(masterKey);
|
setKey(masterKey);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setKey(MasterKey key) {
|
private void setKey(MasterKey key) {
|
||||||
|
if (!Arrays.equals(slots.getMasterHash(), key.getHash())) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setMessage("Unable to decrypt the master key with the given key.");
|
||||||
|
builder.setCancelable(false);
|
||||||
|
builder.setPositiveButton("OK",
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
dialog.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.create().show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// send the master key back to the main activity
|
// send the master key back to the main activity
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra("key", key);
|
result.putExtra("key", key);
|
||||||
|
|
|
@ -109,6 +109,8 @@ public class IntroActivity extends AppIntro {
|
||||||
}
|
}
|
||||||
|
|
||||||
SlotCollection slots = databaseFile.getSlots();
|
SlotCollection slots = databaseFile.getSlots();
|
||||||
|
slots.setMasterHash(masterKey.getHash());
|
||||||
|
|
||||||
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
|
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
|
||||||
try {
|
try {
|
||||||
// encrypt the master key with a key derived from the user's password
|
// encrypt the master key with a key derived from the user's password
|
||||||
|
|
|
@ -2,11 +2,13 @@ package me.impy.aegis.crypto;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
@ -24,10 +26,14 @@ import javax.crypto.spec.SecretKeySpec;
|
||||||
import org.spongycastle.crypto.generators.SCrypt;
|
import org.spongycastle.crypto.generators.SCrypt;
|
||||||
|
|
||||||
public class CryptoUtils {
|
public class CryptoUtils {
|
||||||
|
public static final String CRYPTO_HASH = "SHA-256";
|
||||||
|
public static final byte CRYPTO_HASH_SIZE = 32;
|
||||||
|
|
||||||
public static final String CRYPTO_CIPHER_RAW = "AES/ECB/NoPadding";
|
public static final String CRYPTO_CIPHER_RAW = "AES/ECB/NoPadding";
|
||||||
|
public static final byte CRYPTO_KEY_SIZE = 32;
|
||||||
|
|
||||||
public static final String CRYPTO_CIPHER_AEAD = "AES/GCM/NoPadding";
|
public static final String CRYPTO_CIPHER_AEAD = "AES/GCM/NoPadding";
|
||||||
public static final byte CRYPTO_TAG_SIZE = 16;
|
public static final byte CRYPTO_TAG_SIZE = 16;
|
||||||
public static final byte CRYPTO_KEY_SIZE = 32;
|
|
||||||
public static final byte CRYPTO_NONCE_SIZE = 12;
|
public static final byte CRYPTO_NONCE_SIZE = 12;
|
||||||
public static final byte CRYPTO_SALT_SIZE = 32;
|
public static final byte CRYPTO_SALT_SIZE = 32;
|
||||||
|
|
||||||
|
@ -86,6 +92,20 @@ public class CryptoUtils {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] hashKey(SecretKey key) {
|
||||||
|
MessageDigest hash;
|
||||||
|
try {
|
||||||
|
hash = MessageDigest.getInstance(CRYPTO_HASH);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new UndeclaredThrowableException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bytes = key.getEncoded();
|
||||||
|
hash.update(bytes);
|
||||||
|
CryptoUtils.zero(bytes);
|
||||||
|
return hash.digest();
|
||||||
|
}
|
||||||
|
|
||||||
public static SecretKey generateKey() throws NoSuchAlgorithmException {
|
public static SecretKey generateKey() throws NoSuchAlgorithmException {
|
||||||
KeyGenerator generator = KeyGenerator.getInstance("AES");
|
KeyGenerator generator = KeyGenerator.getInstance("AES");
|
||||||
generator.init(CRYPTO_KEY_SIZE * 8);
|
generator.init(CRYPTO_KEY_SIZE * 8);
|
||||||
|
|
|
@ -51,4 +51,8 @@ public class MasterKey implements Serializable {
|
||||||
Cipher cipher = CryptoUtils.createCipher(_key, Cipher.DECRYPT_MODE, params.Nonce);
|
Cipher cipher = CryptoUtils.createCipher(_key, Cipher.DECRYPT_MODE, params.Nonce);
|
||||||
return CryptoUtils.decrypt(bytes, cipher, params);
|
return CryptoUtils.decrypt(bytes, cipher, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] getHash() {
|
||||||
|
return CryptoUtils.hashKey(_key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,14 @@ import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
|
import me.impy.aegis.crypto.CryptoUtils;
|
||||||
import me.impy.aegis.util.LittleByteBuffer;
|
import me.impy.aegis.util.LittleByteBuffer;
|
||||||
|
|
||||||
public class SlotCollection implements Iterable<Slot>, Serializable {
|
public class SlotCollection implements Iterable<Slot>, Serializable {
|
||||||
private List<Slot> _slots = new ArrayList<>();
|
private List<Slot> _slots = new ArrayList<>();
|
||||||
|
private byte[] _masterHash;
|
||||||
|
|
||||||
public static byte[] serialize(SlotCollection slots) {
|
public static byte[] serialize(SlotCollection slots) {
|
||||||
// yep, no streams at this api level
|
// yep, no streams at this api level
|
||||||
|
@ -16,8 +20,10 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
|
||||||
for (Slot slot : slots) {
|
for (Slot slot : slots) {
|
||||||
size += slot.getSize();
|
size += slot.getSize();
|
||||||
}
|
}
|
||||||
|
size += CryptoUtils.CRYPTO_HASH_SIZE;
|
||||||
|
|
||||||
LittleByteBuffer buffer = LittleByteBuffer.allocate(size);
|
LittleByteBuffer buffer = LittleByteBuffer.allocate(size);
|
||||||
|
buffer.put(slots.getMasterHash());
|
||||||
|
|
||||||
for (Slot slot : slots) {
|
for (Slot slot : slots) {
|
||||||
byte[] bytes = slot.serialize();
|
byte[] bytes = slot.serialize();
|
||||||
|
@ -27,8 +33,12 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SlotCollection deserialize(byte[] data) throws Exception {
|
public static SlotCollection deserialize(byte[] data) throws Exception {
|
||||||
SlotCollection slots = new SlotCollection();
|
|
||||||
LittleByteBuffer buffer = LittleByteBuffer.wrap(data);
|
LittleByteBuffer buffer = LittleByteBuffer.wrap(data);
|
||||||
|
byte[] masterHash = new byte[CryptoUtils.CRYPTO_HASH_SIZE];
|
||||||
|
buffer.get(masterHash);
|
||||||
|
|
||||||
|
SlotCollection slots = new SlotCollection();
|
||||||
|
slots.setMasterHash(masterHash);
|
||||||
|
|
||||||
while (buffer.remaining() > 0) {
|
while (buffer.remaining() > 0) {
|
||||||
Slot slot;
|
Slot slot;
|
||||||
|
@ -86,4 +96,12 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
|
||||||
public Iterator<Slot> iterator() {
|
public Iterator<Slot> iterator() {
|
||||||
return _slots.iterator();
|
return _slots.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMasterHash(byte[] masterHash) {
|
||||||
|
_masterHash = masterHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getMasterHash() {
|
||||||
|
return _masterHash;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
14
doc/db.md
14
doc/db.md
|
@ -54,9 +54,17 @@ ID can be one of:
|
||||||
|
|
||||||
#### Slots
|
#### Slots
|
||||||
|
|
||||||
This section contains a list of slots. All slots contain the master key
|
This section contains a SHA-256 hash of the master key and a list of slots. The
|
||||||
encrypted with raw AES. The key that is used for encryption depends on the slot
|
hash is used to verify the integrity of a decrypted slot. Note that this is just
|
||||||
type.
|
for convenience, not security.
|
||||||
|
|
||||||
|
| Length | Contents |
|
||||||
|
|:-------|:--------------------------|
|
||||||
|
| `32` | `uint8_t` Master Key Hash |
|
||||||
|
| `?` | Slots |
|
||||||
|
|
||||||
|
All slots contain the master key encrypted with raw AES. The key that is used for
|
||||||
|
encryption depends on the slot type.
|
||||||
|
|
||||||
A slot has the following structure.
|
A slot has the following structure.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue