From 7269cc2b6a7de862ec678d02dab372156ed61607 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Fri, 18 Aug 2017 22:12:45 +0200 Subject: [PATCH] Verify integrity of decrypted slots and display a dialog on error --- .../main/java/me/impy/aegis/AuthActivity.java | 21 ++++++++++++++---- .../java/me/impy/aegis/IntroActivity.java | 2 ++ .../me/impy/aegis/crypto/CryptoUtils.java | 22 ++++++++++++++++++- .../java/me/impy/aegis/crypto/MasterKey.java | 4 ++++ .../aegis/crypto/slots/SlotCollection.java | 20 ++++++++++++++++- doc/db.md | 14 +++++++++--- 6 files changed, 74 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/me/impy/aegis/AuthActivity.java b/app/src/main/java/me/impy/aegis/AuthActivity.java index ddf641d9..5045855d 100644 --- a/app/src/main/java/me/impy/aegis/AuthActivity.java +++ b/app/src/main/java/me/impy/aegis/AuthActivity.java @@ -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); diff --git a/app/src/main/java/me/impy/aegis/IntroActivity.java b/app/src/main/java/me/impy/aegis/IntroActivity.java index b48ee94f..f2918a86 100644 --- a/app/src/main/java/me/impy/aegis/IntroActivity.java +++ b/app/src/main/java/me/impy/aegis/IntroActivity.java @@ -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 diff --git a/app/src/main/java/me/impy/aegis/crypto/CryptoUtils.java b/app/src/main/java/me/impy/aegis/crypto/CryptoUtils.java index 4a5c7007..697f6af3 100644 --- a/app/src/main/java/me/impy/aegis/crypto/CryptoUtils.java +++ b/app/src/main/java/me/impy/aegis/crypto/CryptoUtils.java @@ -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); diff --git a/app/src/main/java/me/impy/aegis/crypto/MasterKey.java b/app/src/main/java/me/impy/aegis/crypto/MasterKey.java index 84079d2b..b4b8eb1f 100644 --- a/app/src/main/java/me/impy/aegis/crypto/MasterKey.java +++ b/app/src/main/java/me/impy/aegis/crypto/MasterKey.java @@ -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); + } } diff --git a/app/src/main/java/me/impy/aegis/crypto/slots/SlotCollection.java b/app/src/main/java/me/impy/aegis/crypto/slots/SlotCollection.java index c88ca69a..00baa4e6 100644 --- a/app/src/main/java/me/impy/aegis/crypto/slots/SlotCollection.java +++ b/app/src/main/java/me/impy/aegis/crypto/slots/SlotCollection.java @@ -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, Serializable { private List _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, 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, 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, Serializable { public Iterator iterator() { return _slots.iterator(); } + + public void setMasterHash(byte[] masterHash) { + _masterHash = masterHash; + } + + public byte[] getMasterHash() { + return _masterHash; + } } diff --git a/doc/db.md b/doc/db.md index 3d44156b..3d34b1a5 100644 --- a/doc/db.md +++ b/doc/db.md @@ -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.