Verify integrity of decrypted slots and display a dialog on error

This commit is contained in:
Alexander Bakker 2017-08-18 22:12:45 +02:00
parent 1ae9364c5e
commit 7269cc2b6a
6 changed files with 74 additions and 9 deletions

View file

@ -2,11 +2,13 @@ package me.impy.aegis;
import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
@ -14,14 +16,12 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.TextView;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import me.impy.aegis.crypto.CryptoUtils;
@ -101,13 +101,26 @@ public class AuthActivity extends AppCompatActivity implements FingerprintUiHelp
throw new UndeclaredThrowableException(e);
}
// send the master key back to the main activity
setKey(masterKey);
}
});
}
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
Intent result = new Intent();
result.putExtra("key", key);

View file

@ -109,6 +109,8 @@ public class IntroActivity extends AppIntro {
}
SlotCollection slots = databaseFile.getSlots();
slots.setMasterHash(masterKey.getHash());
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
try {
// encrypt the master key with a key derived from the user's password

View file

@ -2,11 +2,13 @@ package me.impy.aegis.crypto;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
@ -24,10 +26,14 @@ import javax.crypto.spec.SecretKeySpec;
import org.spongycastle.crypto.generators.SCrypt;
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 byte CRYPTO_KEY_SIZE = 32;
public static final String CRYPTO_CIPHER_AEAD = "AES/GCM/NoPadding";
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_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 {
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(CRYPTO_KEY_SIZE * 8);

View file

@ -51,4 +51,8 @@ public class MasterKey implements Serializable {
Cipher cipher = CryptoUtils.createCipher(_key, Cipher.DECRYPT_MODE, params.Nonce);
return CryptoUtils.decrypt(bytes, cipher, params);
}
public byte[] getHash() {
return CryptoUtils.hashKey(_key);
}
}

View file

@ -5,10 +5,14 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.crypto.SecretKey;
import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.util.LittleByteBuffer;
public class SlotCollection implements Iterable<Slot>, Serializable {
private List<Slot> _slots = new ArrayList<>();
private byte[] _masterHash;
public static byte[] serialize(SlotCollection slots) {
// yep, no streams at this api level
@ -16,8 +20,10 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
for (Slot slot : slots) {
size += slot.getSize();
}
size += CryptoUtils.CRYPTO_HASH_SIZE;
LittleByteBuffer buffer = LittleByteBuffer.allocate(size);
buffer.put(slots.getMasterHash());
for (Slot slot : slots) {
byte[] bytes = slot.serialize();
@ -27,8 +33,12 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
}
public static SlotCollection deserialize(byte[] data) throws Exception {
SlotCollection slots = new SlotCollection();
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) {
Slot slot;
@ -86,4 +96,12 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
public Iterator<Slot> iterator() {
return _slots.iterator();
}
public void setMasterHash(byte[] masterHash) {
_masterHash = masterHash;
}
public byte[] getMasterHash() {
return _masterHash;
}
}

View file

@ -54,9 +54,17 @@ ID can be one of:
#### Slots
This section contains a list of slots. All slots contain the master key
encrypted with raw AES. The key that is used for encryption depends on the slot
type.
This section contains a SHA-256 hash of the master key and a list of slots. The
hash is used to verify the integrity of a decrypted slot. Note that this is just
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.