diff --git a/app/build.gradle b/app/build.gradle index 1569e20c..27c1421e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,11 +26,9 @@ android { } } - dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:recyclerview-v7:25.0.0' - 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' @@ -40,5 +38,6 @@ dependencies { compile 'com.android.support:support-v4:25.0.0' compile 'com.yarolegovich:lovely-dialog:1.0.4' compile 'com.mattprecious.swirl:swirl:1.0.0' + compile 'com.madgag.spongycastle:core:1.56.0.0' testCompile 'junit:junit:4.12' } diff --git a/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java b/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java index ba34968b..b485b82a 100644 --- a/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java +++ b/app/src/main/java/me/impy/aegis/CustomAuthenticatedSlide.java @@ -101,7 +101,7 @@ public class CustomAuthenticatedSlide extends SlideFragment { InvalidKeyException, NoSuchPaddingException { char[] password = getPassword(true); byte[] salt = CryptoUtils.generateSalt(); - SecretKey key = slot.deriveKey(password, salt, CryptoUtils.CRYPTO_ITERATION_COUNT); + SecretKey key = slot.deriveKey(password, salt, CryptoUtils.CRYPTO_SCRYPT_N, CryptoUtils.CRYPTO_SCRYPT_r, CryptoUtils.CRYPTO_SCRYPT_p); CryptoUtils.zero(password); return Slot.createCipher(key, mode); diff --git a/app/src/main/java/me/impy/aegis/EditProfileBottomSheetdialog.java b/app/src/main/java/me/impy/aegis/EditProfileBottomSheetdialog.java index bf2240ed..1326a605 100644 --- a/app/src/main/java/me/impy/aegis/EditProfileBottomSheetdialog.java +++ b/app/src/main/java/me/impy/aegis/EditProfileBottomSheetdialog.java @@ -39,4 +39,4 @@ public class EditProfileBottomSheetdialog extends BottomSheetDialogFragment { public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.bottom_sheet_edit_profile, container, false); } -} \ No newline at end of file +} diff --git a/app/src/main/java/me/impy/aegis/IntroActivity.java b/app/src/main/java/me/impy/aegis/IntroActivity.java index a9b3f54b..88d3be69 100644 --- a/app/src/main/java/me/impy/aegis/IntroActivity.java +++ b/app/src/main/java/me/impy/aegis/IntroActivity.java @@ -74,7 +74,7 @@ public class IntroActivity extends MaterialIntroActivity { private void setException(Exception e) { Intent result = new Intent(); result.putExtra("exception", e); - setResult(RESULT_OK, result); + setResult(RESULT_EXCEPTION, result); } @Override 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 4938bbbe..f2fd2b87 100644 --- a/app/src/main/java/me/impy/aegis/crypto/CryptoUtils.java +++ b/app/src/main/java/me/impy/aegis/crypto/CryptoUtils.java @@ -2,12 +2,14 @@ package me.impy.aegis.crypto; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; -import java.security.spec.KeySpec; import java.util.Arrays; import javax.crypto.BadPaddingException; @@ -16,26 +18,30 @@ import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.crypto.generators.SCrypt; public class CryptoUtils { + public static final String CRYPTO_CIPHER_RAW = "AES/ECB/NoPadding"; + 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; - // TODO: decide on a 'secure-enough' iteration count - 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 - public static final String CRYPTO_DERIVE_ALGO = "PBKDF2WithHmacSHA1"; - public static SecretKey deriveKey(char[] password, byte[] salt, int iterations) throws NoSuchAlgorithmException, InvalidKeySpecException { - SecretKeyFactory factory = SecretKeyFactory.getInstance(CRYPTO_DERIVE_ALGO); - KeySpec spec = new PBEKeySpec(password, salt, iterations, CRYPTO_KEY_SIZE * 8); - return factory.generateSecret(spec); + public static final int CRYPTO_SCRYPT_N = 2 << 14; + public static final int CRYPTO_SCRYPT_r = 8; + public static final int CRYPTO_SCRYPT_p = 1; + + public static SecretKey deriveKey(char[] password, byte[] salt, int n, int r, int p) throws NoSuchAlgorithmException, InvalidKeySpecException { + byte[] bytes = toBytes(password); + byte[] keyBytes = SCrypt.generate(bytes, salt, n, r, p, CRYPTO_KEY_SIZE); + zero(bytes); + SecretKey key = new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES"); + zero(keyBytes); + return key; } public static Cipher createCipher(SecretKey key, int opmode) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException { @@ -108,4 +114,12 @@ public class CryptoUtils { public static void zero(byte[] data) { Arrays.fill(data, (byte) 0); } + + private static byte[] toBytes(char[] chars) { + CharBuffer charBuf = CharBuffer.wrap(chars); + ByteBuffer byteBuf = Charset.forName("UTF-8").encode(charBuf); + byte[] bytes = Arrays.copyOfRange(byteBuf.array(), 0, byteBuf.limit()); + zero(byteBuf.array()); + return bytes; + } } diff --git a/app/src/main/java/me/impy/aegis/crypto/slots/PasswordSlot.java b/app/src/main/java/me/impy/aegis/crypto/slots/PasswordSlot.java index b7d7c287..37ad7827 100644 --- a/app/src/main/java/me/impy/aegis/crypto/slots/PasswordSlot.java +++ b/app/src/main/java/me/impy/aegis/crypto/slots/PasswordSlot.java @@ -9,7 +9,9 @@ import me.impy.aegis.crypto.CryptoUtils; import me.impy.aegis.util.LittleByteBuffer; public class PasswordSlot extends RawSlot { - private long _iterationCount; + private int _n; + private int _r; + private int _p; private byte[] _salt; public PasswordSlot() { @@ -21,7 +23,9 @@ public class PasswordSlot extends RawSlot { byte[] bytes = super.serialize(); LittleByteBuffer buffer = LittleByteBuffer.wrap(bytes); buffer.position(super.getSize()); - buffer.putLong(_iterationCount); + buffer.putInt(_n); + buffer.putInt(_r); + buffer.putInt(_p); buffer.put(_salt); return buffer.array(); } @@ -31,26 +35,29 @@ public class PasswordSlot extends RawSlot { super.deserialize(data); LittleByteBuffer buffer = LittleByteBuffer.wrap(data); buffer.position(super.getSize()); - _iterationCount = buffer.getLong(); + _n = buffer.getInt(); + _r = buffer.getInt(); + _p = buffer.getInt(); _salt = new byte[CryptoUtils.CRYPTO_SALT_SIZE]; buffer.get(_salt); } - public SecretKey deriveKey(char[] password, byte[] salt, int iterations) throws InvalidKeySpecException, NoSuchAlgorithmException { - SecretKey key = CryptoUtils.deriveKey(password, salt, iterations); - _iterationCount = iterations; + public SecretKey deriveKey(char[] password, byte[] salt, int n, int r, int p) throws InvalidKeySpecException, NoSuchAlgorithmException { + SecretKey key = CryptoUtils.deriveKey(password, salt, n, r, p); + _n = n; + _r = r; + _p = p; _salt = salt; return key; } public SecretKey deriveKey(char[] password) throws InvalidKeySpecException, NoSuchAlgorithmException { - SecretKey key = CryptoUtils.deriveKey(password, _salt, (int)_iterationCount); - return key; + return CryptoUtils.deriveKey(password, _salt, _n, _r, _p); } @Override public int getSize() { - return 1 + CryptoUtils.CRYPTO_KEY_SIZE + /* iterations */ 8 + CryptoUtils.CRYPTO_SALT_SIZE; + return 1 + CryptoUtils.CRYPTO_KEY_SIZE + /* _n, _r, _p */ 4 + 4 + 4 + CryptoUtils.CRYPTO_SALT_SIZE; } @Override