mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Fix small crypto code
This commit is contained in:
@@ -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 <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)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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<NativeAESCipherSpi>(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") ) {
|
||||
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);
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
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() {
|
||||
fun init(): Boolean {
|
||||
if (!isLoaded) {
|
||||
try {
|
||||
System.loadLibrary("final-key");
|
||||
System.loadLibrary("argon2");
|
||||
} catch ( UnsatisfiedLinkError e) {
|
||||
return false;
|
||||
}
|
||||
isLoaded = true;
|
||||
loadSuccess = true;
|
||||
System.loadLibrary("final-key")
|
||||
System.loadLibrary("argon2")
|
||||
} catch (e: UnsatisfiedLinkError) {
|
||||
return false
|
||||
}
|
||||
|
||||
return loadSuccess;
|
||||
isLoaded = true
|
||||
loadSuccess = true
|
||||
}
|
||||
|
||||
return loadSuccess
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<PwDatabaseV4>() {
|
||||
loadInnerHeader(isXml, header)
|
||||
}
|
||||
|
||||
randomStream = PwStreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey)
|
||||
randomStream = StreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey)
|
||||
|
||||
if (randomStream == null) {
|
||||
throw ArcFourException()
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user