Fix small crypto code

This commit is contained in:
J-Jamet
2019-07-19 18:46:11 +02:00
parent d0faf0f1b6
commit 5a11e47653
10 changed files with 117 additions and 143 deletions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePass DX.
* *
@@ -17,20 +17,18 @@
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
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)
}
/** companion object {
*
*/
private static final long serialVersionUID = -3846349284296062658L;
public AESProvider() { private const val serialVersionUID = -3846349284296062658L
super("AESProvider", 1.0, "");
put("Cipher.AES",NativeAESCipherSpi.class.getName());
} }
} }

View File

@@ -51,7 +51,7 @@ public class CipherFactory {
public static Cipher getInstance(String transformation, boolean androidOverride) throws NoSuchAlgorithmException, NoSuchPaddingException { public static Cipher getInstance(String transformation, boolean androidOverride) throws NoSuchAlgorithmException, NoSuchPaddingException {
// Return the native AES if it is possible // 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()); return Cipher.getInstance(transformation, new AESProvider());
} else { } else {
return Cipher.getInstance(transformation); return Cipher.getInstance(transformation);

View File

@@ -53,8 +53,7 @@ public class NativeAESCipherSpi extends CipherSpi {
private final int AES_BLOCK_SIZE = 16; private final int AES_BLOCK_SIZE = 16;
private byte[] mIV; private byte[] mIV;
private boolean mIsInited = false; private boolean mIsInit = false;
private boolean mEncrypting = false;
private long mCtxPtr; private long mCtxPtr;
private boolean mPadding = false; private boolean mPadding = false;
@@ -68,7 +67,7 @@ public class NativeAESCipherSpi extends CipherSpi {
private static void addToCleanupQueue(NativeAESCipherSpi ref, long ptr) { private static void addToCleanupQueue(NativeAESCipherSpi ref, long ptr) {
Log.d(TAG, "queued cipher context: " + ptr); Log.d(TAG, "queued cipher context: " + ptr);
mCleanup.put(new PhantomReference<NativeAESCipherSpi>(ref, mQueue), ptr); mCleanup.put(new PhantomReference<>(ref, mQueue), ptr);
} }
/** Work with the garbage collector to clean up openssl memory when the cipher /** 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); 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 finalAmt = nFinal(mCtxPtr, mPadding, output, outputOffset + updateAmt, outputSize - updateAmt);
return updateAmt + finalAmt;
int out = updateAmt + finalAmt;
return out;
} }
private native int nFinal(long ctxPtr, boolean usePadding, byte[] output, int outputOffest, int outputSize) 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) { } catch (InvalidParameterSpecException e) {
throw new InvalidAlgorithmParameterException(e); throw new InvalidAlgorithmParameterException(e);
} }
} }
private void init(int opmode, Key key, IvParameterSpec params) { private void init(int opmode, Key key, IvParameterSpec params) {
if ( mIsInited ) { if (mIsInit) {
// Do not allow multiple inits // Do not allow multiple inits
assert(true);
throw new RuntimeException("Don't allow multiple inits"); throw new RuntimeException("Don't allow multiple inits");
} else { } else {
NativeLib.init(); NativeLib.INSTANCE.init();
mIsInited = true; mIsInit = true;
} }
mIV = params.getIV(); mIV = params.getIV();
mEncrypting = opmode == Cipher.ENCRYPT_MODE; mCtxPtr = nInit(opmode == Cipher.ENCRYPT_MODE, key.getEncoded(), mIV);
mCtxPtr = nInit(mEncrypting, key.getEncoded(), mIV);
addToCleanupQueue(this, mCtxPtr); addToCleanupQueue(this, mCtxPtr);
} }
@@ -263,26 +254,23 @@ public class NativeAESCipherSpi extends CipherSpi {
protected void engineSetPadding(String padding) protected void engineSetPadding(String padding)
throws NoSuchPaddingException { throws NoSuchPaddingException {
if ( ! mIsInited ) { if ( !mIsInit) {
NativeLib.init(); NativeLib.INSTANCE.init();
} }
if ( padding.length() == 0 ) { if ( padding.length() == 0 ) {
return; return;
} }
if ( !padding.equals("PKCS5Padding") ) {
if ( ! padding.equals("PKCS5Padding") ) {
throw new NoSuchPaddingException("Only supports PKCS5Padding."); throw new NoSuchPaddingException("Only supports PKCS5Padding.");
} }
mPadding = true; mPadding = true;
} }
@Override @Override
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
int maxSize = engineGetOutputSize(inputLen); int maxSize = engineGetOutputSize(inputLen);
byte output[] = new byte[maxSize]; byte[] output = new byte[maxSize];
int updateSize = update(input, inputOffset, inputLen, output, 0); int updateSize = update(input, inputOffset, inputLen, output, 0);
@@ -302,24 +290,15 @@ public class NativeAESCipherSpi extends CipherSpi {
byte[] output, int outputOffset) throws ShortBufferException { byte[] output, int outputOffset) throws ShortBufferException {
int result = update(input, inputOffset, inputLen, output, outputOffset); int result = update(input, inputOffset, inputLen, output, outputOffset);
if ( result == -1 ) { if ( result == -1 ) {
throw new ShortBufferException("Insufficient buffer."); throw new ShortBufferException("Insufficient buffer.");
} }
return result; 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 outputSize = engineGetOutputSize(inputLen);
return nUpdate(mCtxPtr, input, inputOffset, inputLen, output, outputOffset, outputSize);
int out = nUpdate(mCtxPtr, input, inputOffset, inputLen, output, outputOffset, outputSize);
return out;
} }
private native int nUpdate(long ctxPtr, byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset, int outputSize); private native int nUpdate(long ctxPtr, byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset, int outputSize);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. * Copyright 2019 Jeremy Jamet / Kunzisoft.
* *
* This file is part of KeePass DX. * This file is part of KeePass DX.
* *
@@ -17,30 +17,30 @@
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>. * along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package com.kunzisoft.keepass.crypto; package com.kunzisoft.keepass.crypto
public class NativeLib { object NativeLib {
private static boolean isLoaded = false; private var isLoaded = false
private static boolean loadSuccess = false; private var loadSuccess = false
public static boolean loaded() { fun loaded(): Boolean {
return init(); return init()
} }
public static boolean init() { fun init(): Boolean {
if ( ! isLoaded ) { if (!isLoaded) {
try { try {
System.loadLibrary("final-key"); System.loadLibrary("final-key")
System.loadLibrary("argon2"); System.loadLibrary("argon2")
} catch ( UnsatisfiedLinkError e) { } catch (e: UnsatisfiedLinkError) {
return false; return false
} }
isLoaded = true;
loadSuccess = true; isLoaded = true
loadSuccess = true
} }
return loadSuccess; return loadSuccess
} }
} }

View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*/
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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*
*/
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
}
}

View File

@@ -27,12 +27,12 @@ import java.io.IOException;
public class NativeFinalKey extends FinalKey { public class NativeFinalKey extends FinalKey {
public static boolean availble() { public static boolean availble() {
return NativeLib.init(); return NativeLib.INSTANCE.init();
} }
@Override @Override
public byte[] transformMasterKey(byte[] seed, byte[] key, long rounds) throws IOException { public byte[] transformMasterKey(byte[] seed, byte[] key, long rounds) throws IOException {
NativeLib.init(); NativeLib.INSTANCE.init();
return nTransformMasterKey(seed, key, rounds); return nTransformMasterKey(seed, key, rounds);

View File

@@ -28,7 +28,7 @@ public class Argon2Native {
public static byte[] transformKey(byte[] password, byte[] salt, int parallelism, public static byte[] transformKey(byte[] password, byte[] salt, int parallelism,
long memory, long iterations, byte[] secretKey, long memory, long iterations, byte[] secretKey,
byte[] associatedData, long version) throws IOException { byte[] associatedData, long version) throws IOException {
NativeLib.init(); NativeLib.INSTANCE.init();
return nTransformMasterKey(password, salt, parallelism, memory, iterations, secretKey, associatedData, version); return nTransformMasterKey(password, salt, parallelism, memory, iterations, secretKey, associatedData, version);
} }

View File

@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.file.load
import biz.source_code.base64Coder.Base64Coder import biz.source_code.base64Coder.Base64Coder
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.crypto.CipherFactory 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.crypto.engine.CipherEngine
import com.kunzisoft.keepass.database.file.PwCompressionAlgorithm import com.kunzisoft.keepass.database.file.PwCompressionAlgorithm
import com.kunzisoft.keepass.database.element.* import com.kunzisoft.keepass.database.element.*
@@ -183,7 +183,7 @@ class ImporterV4(private val streamDir: File) : Importer<PwDatabaseV4>() {
loadInnerHeader(isXml, header) loadInnerHeader(isXml, header)
} }
randomStream = PwStreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey) randomStream = StreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey)
if (randomStream == null) { if (randomStream == null) {
throw ArcFourException() throw ArcFourException()

View File

@@ -24,7 +24,7 @@ import android.util.Xml
import biz.source_code.base64Coder.Base64Coder import biz.source_code.base64Coder.Base64Coder
import com.kunzisoft.keepass.crypto.CipherFactory import com.kunzisoft.keepass.crypto.CipherFactory
import com.kunzisoft.keepass.crypto.CrsAlgorithm 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.engine.CipherEngine
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
import com.kunzisoft.keepass.database.* import com.kunzisoft.keepass.database.*
@@ -275,7 +275,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
} }
random.nextBytes(header.innerRandomStreamKey) random.nextBytes(header.innerRandomStreamKey)
randomStream = PwStreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey) randomStream = StreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey)
if (randomStream == null) { if (randomStream == null) {
throw PwDbOutputException("Invalid random cipher") throw PwDbOutputException("Invalid random cipher")
} }