From 5a11e4765334ee0d51bb2c5c8b32877138800c57 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 19 Jul 2019 18:46:11 +0200 Subject: [PATCH] Fix small crypto code --- .../{AESProvider.java => AESProvider.kt} | 20 +++-- .../keepass/crypto/CipherFactory.java | 2 +- .../keepass/crypto/NativeAESCipherSpi.java | 47 ++++-------- .../crypto/{NativeLib.java => NativeLib.kt} | 34 ++++----- .../keepass/crypto/PwStreamCipherFactory.java | 73 ------------------- .../keepass/crypto/StreamCipherFactory.kt | 70 ++++++++++++++++++ .../crypto/finalkey/NativeFinalKey.java | 4 +- .../crypto/keyDerivation/Argon2Native.java | 2 +- .../keepass/database/file/load/ImporterV4.kt | 4 +- .../database/file/save/PwDbV4Output.kt | 4 +- 10 files changed, 117 insertions(+), 143 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/crypto/{AESProvider.java => AESProvider.kt} (64%) rename app/src/main/java/com/kunzisoft/keepass/crypto/{NativeLib.java => NativeLib.kt} (56%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/crypto/PwStreamCipherFactory.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/crypto/StreamCipherFactory.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/AESProvider.java b/app/src/main/java/com/kunzisoft/keepass/crypto/AESProvider.kt similarity index 64% rename from app/src/main/java/com/kunzisoft/keepass/crypto/AESProvider.java rename to app/src/main/java/com/kunzisoft/keepass/crypto/AESProvider.kt index 4ef047276..989115163 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/AESProvider.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/AESProvider.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,20 +17,18 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.crypto; +package com.kunzisoft.keepass.crypto -import java.security.Provider; +import java.security.Provider -public final class AESProvider extends Provider { +class AESProvider : Provider("AESProvider", 1.0, "") { + init { + put("Cipher.AES", NativeAESCipherSpi::class.java.name) + } - /** - * - */ - private static final long serialVersionUID = -3846349284296062658L; + companion object { - public AESProvider() { - super("AESProvider", 1.0, ""); - put("Cipher.AES",NativeAESCipherSpi.class.getName()); + private const val serialVersionUID = -3846349284296062658L } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/CipherFactory.java b/app/src/main/java/com/kunzisoft/keepass/crypto/CipherFactory.java index 1646ab759..f2328edb0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/CipherFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/CipherFactory.java @@ -51,7 +51,7 @@ public class CipherFactory { public static Cipher getInstance(String transformation, boolean androidOverride) throws NoSuchAlgorithmException, NoSuchPaddingException { // Return the native AES if it is possible - if ( (!deviceBlacklisted()) && (!androidOverride) && hasNativeImplementation(transformation) && NativeLib.loaded() ) { + if ( (!deviceBlacklisted()) && (!androidOverride) && hasNativeImplementation(transformation) && NativeLib.INSTANCE.loaded() ) { return Cipher.getInstance(transformation, new AESProvider()); } else { return Cipher.getInstance(transformation); diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/NativeAESCipherSpi.java b/app/src/main/java/com/kunzisoft/keepass/crypto/NativeAESCipherSpi.java index 15d09bdc4..c27635f5d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/NativeAESCipherSpi.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/NativeAESCipherSpi.java @@ -53,8 +53,7 @@ public class NativeAESCipherSpi extends CipherSpi { private final int AES_BLOCK_SIZE = 16; private byte[] mIV; - private boolean mIsInited = false; - private boolean mEncrypting = false; + private boolean mIsInit = false; private long mCtxPtr; private boolean mPadding = false; @@ -68,7 +67,7 @@ public class NativeAESCipherSpi extends CipherSpi { private static void addToCleanupQueue(NativeAESCipherSpi ref, long ptr) { Log.d(TAG, "queued cipher context: " + ptr); - mCleanup.put(new PhantomReference(ref, mQueue), ptr); + mCleanup.put(new PhantomReference<>(ref, mQueue), ptr); } /** Work with the garbage collector to clean up openssl memory when the cipher @@ -92,7 +91,6 @@ public class NativeAESCipherSpi extends CipherSpi { } } } - } private static native void nCleanup(long ctxPtr); @@ -155,11 +153,7 @@ public class NativeAESCipherSpi extends CipherSpi { } int finalAmt = nFinal(mCtxPtr, mPadding, output, outputOffset + updateAmt, outputSize - updateAmt); - - int out = updateAmt + finalAmt; - - - return out; + return updateAmt + finalAmt; } private native int nFinal(long ctxPtr, boolean usePadding, byte[] output, int outputOffest, int outputSize) @@ -231,22 +225,19 @@ public class NativeAESCipherSpi extends CipherSpi { } catch (InvalidParameterSpecException e) { throw new InvalidAlgorithmParameterException(e); } - } private void init(int opmode, Key key, IvParameterSpec params) { - if ( mIsInited ) { + if (mIsInit) { // Do not allow multiple inits - assert(true); throw new RuntimeException("Don't allow multiple inits"); } else { - NativeLib.init(); - mIsInited = true; + NativeLib.INSTANCE.init(); + mIsInit = true; } mIV = params.getIV(); - mEncrypting = opmode == Cipher.ENCRYPT_MODE; - mCtxPtr = nInit(mEncrypting, key.getEncoded(), mIV); + mCtxPtr = nInit(opmode == Cipher.ENCRYPT_MODE, key.getEncoded(), mIV); addToCleanupQueue(this, mCtxPtr); } @@ -263,26 +254,23 @@ public class NativeAESCipherSpi extends CipherSpi { protected void engineSetPadding(String padding) throws NoSuchPaddingException { - if ( ! mIsInited ) { - NativeLib.init(); + if ( !mIsInit) { + NativeLib.INSTANCE.init(); } - if ( padding.length() == 0 ) { return; } - - if ( ! padding.equals("PKCS5Padding") ) { + if ( !padding.equals("PKCS5Padding") ) { throw new NoSuchPaddingException("Only supports PKCS5Padding."); } mPadding = true; - } @Override protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { int maxSize = engineGetOutputSize(inputLen); - byte output[] = new byte[maxSize]; + byte[] output = new byte[maxSize]; int updateSize = update(input, inputOffset, inputLen, output, 0); @@ -302,24 +290,15 @@ public class NativeAESCipherSpi extends CipherSpi { byte[] output, int outputOffset) throws ShortBufferException { int result = update(input, inputOffset, inputLen, output, outputOffset); - if ( result == -1 ) { throw new ShortBufferException("Insufficient buffer."); } - return result; - } - int update(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { + private int update(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { int outputSize = engineGetOutputSize(inputLen); - - int out = nUpdate(mCtxPtr, input, inputOffset, inputLen, output, outputOffset, outputSize); - - - return out; - - + return nUpdate(mCtxPtr, input, inputOffset, inputLen, output, outputOffset, outputSize); } private native int nUpdate(long ctxPtr, byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset, int outputSize); diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.java b/app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.kt similarity index 56% rename from app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.java rename to app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.kt index 88a774220..06dbd2ef8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,30 +17,30 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.crypto; +package com.kunzisoft.keepass.crypto -public class NativeLib { - private static boolean isLoaded = false; - private static boolean loadSuccess = false; +object NativeLib { + private var isLoaded = false + private var loadSuccess = false - public static boolean loaded() { - return init(); + fun loaded(): Boolean { + return init() } - public static boolean init() { - if ( ! isLoaded ) { + fun init(): Boolean { + if (!isLoaded) { try { - System.loadLibrary("final-key"); - System.loadLibrary("argon2"); - } catch ( UnsatisfiedLinkError e) { - return false; + System.loadLibrary("final-key") + System.loadLibrary("argon2") + } catch (e: UnsatisfiedLinkError) { + return false } - isLoaded = true; - loadSuccess = true; + + isLoaded = true + loadSuccess = true } - return loadSuccess; - + return loadSuccess } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/PwStreamCipherFactory.java b/app/src/main/java/com/kunzisoft/keepass/crypto/PwStreamCipherFactory.java deleted file mode 100644 index 2d5643dd1..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/PwStreamCipherFactory.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.crypto; - -import org.spongycastle.crypto.StreamCipher; -import org.spongycastle.crypto.engines.ChaCha7539Engine; -import org.spongycastle.crypto.engines.Salsa20Engine; -import org.spongycastle.crypto.params.KeyParameter; -import org.spongycastle.crypto.params.ParametersWithIV; - -public class PwStreamCipherFactory { - public static StreamCipher getInstance(CrsAlgorithm alg, byte[] key) { - if ( alg == CrsAlgorithm.Salsa20 ) { - return getSalsa20(key); - } else if (alg == CrsAlgorithm.ChaCha20) { - return getChaCha20(key); - } else { - return null; - } - } - - - private static final byte[] SALSA_IV = new byte[]{ (byte)0xE8, 0x30, 0x09, 0x4B, - (byte)0x97, 0x20, 0x5D, 0x2A }; - - private static StreamCipher getSalsa20(byte[] key) { - // Build stream cipher key - byte[] key32 = CryptoUtil.hashSha256(key); - - KeyParameter keyParam = new KeyParameter(key32); - ParametersWithIV ivParam = new ParametersWithIV(keyParam, SALSA_IV); - - StreamCipher cipher = new Salsa20Engine(); - cipher.init(true, ivParam); - - return cipher; - } - - private static StreamCipher getChaCha20(byte[] key) { - // Build stream cipher key - byte[] hash = CryptoUtil.hashSha512(key); - byte[] key32 = new byte[32]; - byte[] iv = new byte[12]; - - System.arraycopy(hash, 0, key32, 0, 32); - System.arraycopy(hash, 32, iv, 0, 12); - - KeyParameter keyParam = new KeyParameter(key32); - ParametersWithIV ivParam = new ParametersWithIV(keyParam, iv); - - StreamCipher cipher = new ChaCha7539Engine(); - cipher.init(true, ivParam); - - return cipher; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/StreamCipherFactory.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/StreamCipherFactory.kt new file mode 100644 index 000000000..6ccf62f3c --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/StreamCipherFactory.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.crypto + +import org.spongycastle.crypto.StreamCipher +import org.spongycastle.crypto.engines.ChaCha7539Engine +import org.spongycastle.crypto.engines.Salsa20Engine +import org.spongycastle.crypto.params.KeyParameter +import org.spongycastle.crypto.params.ParametersWithIV + +object StreamCipherFactory { + + private val SALSA_IV = byteArrayOf(0xE8.toByte(), 0x30, 0x09, 0x4B, 0x97.toByte(), 0x20, 0x5D, 0x2A) + + fun getInstance(alg: CrsAlgorithm?, key: ByteArray): StreamCipher? { + return when { + alg === CrsAlgorithm.Salsa20 -> getSalsa20(key) + alg === CrsAlgorithm.ChaCha20 -> getChaCha20(key) + else -> null + } + } + + private fun getSalsa20(key: ByteArray): StreamCipher { + // Build stream cipher key + val key32 = CryptoUtil.hashSha256(key) + + val keyParam = KeyParameter(key32) + val ivParam = ParametersWithIV(keyParam, SALSA_IV) + + val cipher = Salsa20Engine() + cipher.init(true, ivParam) + + return cipher + } + + private fun getChaCha20(key: ByteArray): StreamCipher { + // Build stream cipher key + val hash = CryptoUtil.hashSha512(key) + val key32 = ByteArray(32) + val iv = ByteArray(12) + + System.arraycopy(hash, 0, key32, 0, 32) + System.arraycopy(hash, 32, iv, 0, 12) + + val keyParam = KeyParameter(key32) + val ivParam = ParametersWithIV(keyParam, iv) + + val cipher = ChaCha7539Engine() + cipher.init(true, ivParam) + + return cipher + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeFinalKey.java b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeFinalKey.java index 66fae82b7..e1652d677 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeFinalKey.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeFinalKey.java @@ -27,12 +27,12 @@ import java.io.IOException; public class NativeFinalKey extends FinalKey { public static boolean availble() { - return NativeLib.init(); + return NativeLib.INSTANCE.init(); } @Override public byte[] transformMasterKey(byte[] seed, byte[] key, long rounds) throws IOException { - NativeLib.init(); + NativeLib.INSTANCE.init(); return nTransformMasterKey(seed, key, rounds); diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Native.java b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Native.java index bd3da3b1a..098641bff 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Native.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/keyDerivation/Argon2Native.java @@ -28,7 +28,7 @@ public class Argon2Native { public static byte[] transformKey(byte[] password, byte[] salt, int parallelism, long memory, long iterations, byte[] secretKey, byte[] associatedData, long version) throws IOException { - NativeLib.init(); + NativeLib.INSTANCE.init(); return nTransformMasterKey(password, salt, parallelism, memory, iterations, secretKey, associatedData, version); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt index 8fe82be86..c2010cdc1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.file.load import biz.source_code.base64Coder.Base64Coder import com.kunzisoft.keepass.R import com.kunzisoft.keepass.crypto.CipherFactory -import com.kunzisoft.keepass.crypto.PwStreamCipherFactory +import com.kunzisoft.keepass.crypto.StreamCipherFactory import com.kunzisoft.keepass.crypto.engine.CipherEngine import com.kunzisoft.keepass.database.file.PwCompressionAlgorithm import com.kunzisoft.keepass.database.element.* @@ -183,7 +183,7 @@ class ImporterV4(private val streamDir: File) : Importer() { loadInnerHeader(isXml, header) } - randomStream = PwStreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey) + randomStream = StreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey) if (randomStream == null) { throw ArcFourException() diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt index 6dd42de2b..832db93bc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt @@ -24,7 +24,7 @@ import android.util.Xml import biz.source_code.base64Coder.Base64Coder import com.kunzisoft.keepass.crypto.CipherFactory import com.kunzisoft.keepass.crypto.CrsAlgorithm -import com.kunzisoft.keepass.crypto.PwStreamCipherFactory +import com.kunzisoft.keepass.crypto.StreamCipherFactory import com.kunzisoft.keepass.crypto.engine.CipherEngine import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory import com.kunzisoft.keepass.database.* @@ -275,7 +275,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt } random.nextBytes(header.innerRandomStreamKey) - randomStream = PwStreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey) + randomStream = StreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey) if (randomStream == null) { throw PwDbOutputException("Invalid random cipher") }