mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Refactor KDB cipher and better tests
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
package com.kunzisoft.keepass.database.crypto
|
||||
|
||||
|
||||
import com.kunzisoft.encrypt.CipherEngineFactory
|
||||
import com.kunzisoft.encrypt.CipherFactory
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
@@ -31,7 +31,7 @@ class AesEngine : CipherEngine() {
|
||||
|
||||
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
|
||||
override fun getCipher(opmode: Int, key: ByteArray, IV: ByteArray): Cipher {
|
||||
return CipherEngineFactory.getAES(opmode, key, IV)
|
||||
return CipherFactory.getAES(opmode, key, IV)
|
||||
}
|
||||
|
||||
override fun getEncryptionAlgorithm(): EncryptionAlgorithm {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.crypto
|
||||
|
||||
import com.kunzisoft.encrypt.CipherEngineFactory
|
||||
import com.kunzisoft.encrypt.CipherFactory
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
@@ -34,7 +34,7 @@ class ChaCha20Engine : CipherEngine() {
|
||||
|
||||
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
|
||||
override fun getCipher(opmode: Int, key: ByteArray, IV: ByteArray): Cipher {
|
||||
return CipherEngineFactory.getChacha20(opmode, key, IV)
|
||||
return CipherFactory.getChacha20(opmode, key, IV)
|
||||
}
|
||||
|
||||
override fun getEncryptionAlgorithm(): EncryptionAlgorithm {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.crypto
|
||||
|
||||
import com.kunzisoft.encrypt.CipherEngineFactory
|
||||
import com.kunzisoft.encrypt.CipherFactory
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
@@ -30,7 +30,7 @@ class TwofishEngine : CipherEngine() {
|
||||
|
||||
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
|
||||
override fun getCipher(opmode: Int, key: ByteArray, IV: ByteArray): Cipher {
|
||||
return CipherEngineFactory.getTwofish(opmode, key, IV)
|
||||
return CipherFactory.getTwofish(opmode, key, IV)
|
||||
}
|
||||
|
||||
override fun getEncryptionAlgorithm(): EncryptionAlgorithm {
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
package com.kunzisoft.keepass.database.file.input
|
||||
|
||||
import com.kunzisoft.encrypt.CipherFactory
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
@@ -35,12 +34,11 @@ import com.kunzisoft.keepass.database.file.DatabaseHeaderKDB
|
||||
import com.kunzisoft.keepass.stream.*
|
||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||
import java.io.*
|
||||
import java.security.*
|
||||
import java.security.DigestInputStream
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.util.*
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.NoSuchPaddingException
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
|
||||
/**
|
||||
@@ -131,32 +129,14 @@ class DatabaseInputKDB(cacheDirectory: File)
|
||||
mDatabase.numberKeyEncryptionRounds)
|
||||
|
||||
progressTaskUpdater?.updateMessage(R.string.decrypting_db)
|
||||
// Initialize Rijndael algorithm
|
||||
val cipher: Cipher = try {
|
||||
// TODO Encapsulate
|
||||
when {
|
||||
mDatabase.encryptionAlgorithm === EncryptionAlgorithm.AESRijndael -> {
|
||||
CipherFactory.getInstance("AES/CBC/PKCS5Padding")
|
||||
}
|
||||
mDatabase.encryptionAlgorithm === EncryptionAlgorithm.Twofish -> {
|
||||
CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING")
|
||||
}
|
||||
else -> throw IOException("Encryption algorithm is not supported")
|
||||
}
|
||||
} catch (e1: NoSuchAlgorithmException) {
|
||||
throw IOException("No such algorithm")
|
||||
} catch (e1: NoSuchPaddingException) {
|
||||
throw IOException("No such pdading")
|
||||
}
|
||||
|
||||
try {
|
||||
cipher.init(Cipher.DECRYPT_MODE,
|
||||
SecretKeySpec(mDatabase.finalKey, "AES"),
|
||||
IvParameterSpec(header.encryptionIV))
|
||||
} catch (e1: InvalidKeyException) {
|
||||
throw IOException("Invalid key")
|
||||
} catch (e1: InvalidAlgorithmParameterException) {
|
||||
throw IOException("Invalid algorithm parameter.")
|
||||
val cipher: Cipher = try {
|
||||
mDatabase.encryptionAlgorithm
|
||||
.cipherEngine.getCipher(Cipher.DECRYPT_MODE,
|
||||
mDatabase.finalKey ?: ByteArray(0),
|
||||
header.encryptionIV)
|
||||
} catch (e: Exception) {
|
||||
throw IOException("Algorithm not supported.", e)
|
||||
}
|
||||
|
||||
val messageDigest: MessageDigest
|
||||
|
||||
@@ -156,7 +156,7 @@ class DatabaseInputKDBX(cacheDirectory: File)
|
||||
val isPlain: InputStream
|
||||
if (mDatabase.kdbxVersion.toKotlinLong() < DatabaseHeaderKDBX.FILE_VERSION_32_4.toKotlinLong()) {
|
||||
|
||||
val decrypted = attachCipherStream(databaseInputStream, cipher)
|
||||
val decrypted = CipherInputStream(databaseInputStream, cipher)
|
||||
val dataDecrypted = LittleEndianDataInputStream(decrypted)
|
||||
val storedStartBytes: ByteArray?
|
||||
try {
|
||||
@@ -193,11 +193,10 @@ class DatabaseInputKDBX(cacheDirectory: File)
|
||||
|
||||
val hmIs = HmacBlockInputStream(isData, true, hmacKey)
|
||||
|
||||
isPlain = attachCipherStream(hmIs, cipher)
|
||||
isPlain = CipherInputStream(hmIs, cipher)
|
||||
}
|
||||
|
||||
val inputStreamXml: InputStream
|
||||
inputStreamXml = when (mDatabase.compressionAlgorithm) {
|
||||
val inputStreamXml: InputStream = when (mDatabase.compressionAlgorithm) {
|
||||
CompressionAlgorithm.GZip -> GZIPInputStream(isPlain)
|
||||
else -> isPlain
|
||||
}
|
||||
@@ -232,10 +231,6 @@ class DatabaseInputKDBX(cacheDirectory: File)
|
||||
return mDatabase
|
||||
}
|
||||
|
||||
private fun attachCipherStream(inputStream: InputStream, cipher: Cipher): InputStream {
|
||||
return CipherInputStream(inputStream, cipher)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun loadInnerHeader(inputStream: InputStream, header: DatabaseHeaderKDBX) {
|
||||
val lis = LittleEndianDataInputStream(inputStream)
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.file.output
|
||||
|
||||
import com.kunzisoft.encrypt.CipherFactory
|
||||
import com.kunzisoft.encrypt.UnsignedInt
|
||||
import com.kunzisoft.encrypt.stream.LittleEndianDataOutputStream
|
||||
import com.kunzisoft.encrypt.stream.NullOutputStream
|
||||
@@ -37,8 +36,7 @@ import java.security.*
|
||||
import java.util.*
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.CipherOutputStream
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import javax.crypto.NoSuchPaddingException
|
||||
|
||||
class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
|
||||
outputStream: OutputStream)
|
||||
@@ -67,31 +65,27 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
|
||||
|
||||
val finalKey = getFinalKey(header)
|
||||
|
||||
val cipher: Cipher
|
||||
cipher = try {
|
||||
when {
|
||||
// TODO Encapsulate
|
||||
mDatabaseKDB.encryptionAlgorithm === EncryptionAlgorithm.AESRijndael->
|
||||
CipherFactory.getInstance("AES/CBC/PKCS5Padding")
|
||||
mDatabaseKDB.encryptionAlgorithm === EncryptionAlgorithm.Twofish ->
|
||||
CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING")
|
||||
else ->
|
||||
throw Exception()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw DatabaseOutputException("Algorithm not supported.", e)
|
||||
val cipher: Cipher = try {
|
||||
mDatabaseKDB.encryptionAlgorithm
|
||||
.cipherEngine.getCipher(Cipher.ENCRYPT_MODE,
|
||||
finalKey ?: ByteArray(0),
|
||||
header.encryptionIV)
|
||||
} catch (e1: NoSuchAlgorithmException) {
|
||||
throw IOException("No such algorithm")
|
||||
} catch (e1: NoSuchPaddingException) {
|
||||
throw IOException("No such padding")
|
||||
} catch (e1: InvalidKeyException) {
|
||||
throw IOException("Invalid key")
|
||||
} catch (e1: InvalidAlgorithmParameterException) {
|
||||
throw IOException("Invalid algorithm parameter.")
|
||||
}
|
||||
|
||||
try {
|
||||
cipher.init(Cipher.ENCRYPT_MODE,
|
||||
SecretKeySpec(finalKey, "AES"),
|
||||
IvParameterSpec(header.encryptionIV))
|
||||
val cos = CipherOutputStream(mOutputStream, cipher)
|
||||
val bos = BufferedOutputStream(cos)
|
||||
outputPlanGroupAndEntries(bos)
|
||||
bos.flush()
|
||||
bos.close()
|
||||
|
||||
} catch (e: InvalidKeyException) {
|
||||
throw DatabaseOutputException("Invalid key", e)
|
||||
} catch (e: InvalidAlgorithmParameterException) {
|
||||
|
||||
@@ -23,23 +23,82 @@ import com.kunzisoft.encrypt.aes.AndroidAESKeyTransformer
|
||||
import com.kunzisoft.encrypt.aes.NativeAESKeyTransformer
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.*
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import javax.crypto.CipherInputStream
|
||||
import javax.crypto.CipherOutputStream
|
||||
|
||||
class AESTest {
|
||||
|
||||
private val mRand = Random()
|
||||
|
||||
@Test
|
||||
fun testAES() {
|
||||
// Test both an old and an even number to test my flip variable
|
||||
testAESFinalKey(5)
|
||||
testAESFinalKey(6)
|
||||
fun testAESByteArray() {
|
||||
// Generate random input
|
||||
val input = ByteArray(mRand.nextInt(494) + 18)
|
||||
mRand.nextBytes(input)
|
||||
// Generate key
|
||||
val keyArray = ByteArray(32)
|
||||
mRand.nextBytes(keyArray)
|
||||
// Generate IV
|
||||
val ivArray = ByteArray(16)
|
||||
mRand.nextBytes(ivArray)
|
||||
|
||||
val androidEncrypt = CipherFactory.getAES(Cipher.ENCRYPT_MODE, keyArray, ivArray).doFinal(input)
|
||||
val nativeEncrypt = CipherFactory.getAES(Cipher.ENCRYPT_MODE, keyArray, ivArray, true).doFinal(input)
|
||||
|
||||
assertArrayEquals("Check AES encryption", androidEncrypt, nativeEncrypt)
|
||||
|
||||
val androidDecrypt = CipherFactory.getAES(Cipher.DECRYPT_MODE, keyArray, ivArray).doFinal(androidEncrypt)
|
||||
val nativeDecrypt = CipherFactory.getAES(Cipher.DECRYPT_MODE, keyArray, ivArray, true).doFinal(nativeEncrypt)
|
||||
|
||||
assertArrayEquals("Check AES encryption/decryption", androidDecrypt, nativeDecrypt)
|
||||
|
||||
val androidMixDecrypt = CipherFactory.getAES(Cipher.DECRYPT_MODE, keyArray, ivArray).doFinal(nativeEncrypt)
|
||||
val nativeMixDecrypt = CipherFactory.getAES(Cipher.DECRYPT_MODE, keyArray, ivArray, true).doFinal(androidEncrypt)
|
||||
|
||||
assertArrayEquals("Check AES mix encryption/decryption", androidMixDecrypt, nativeMixDecrypt)
|
||||
}
|
||||
|
||||
private fun testAESFinalKey(rounds: Long) {
|
||||
@Test
|
||||
fun testAESStream() {
|
||||
// Generate random input
|
||||
val input = ByteArray(mRand.nextInt(494) + 18)
|
||||
mRand.nextBytes(input)
|
||||
// Generate key
|
||||
val keyArray = ByteArray(32)
|
||||
mRand.nextBytes(keyArray)
|
||||
// Generate IV
|
||||
val ivArray = ByteArray(16)
|
||||
mRand.nextBytes(ivArray)
|
||||
|
||||
val androidEncrypt = CipherFactory.getAES(Cipher.ENCRYPT_MODE, keyArray, ivArray)
|
||||
val androidDecrypt = CipherFactory.getAES(Cipher.DECRYPT_MODE, keyArray, ivArray)
|
||||
val androidOutputStream = ByteArrayOutputStream()
|
||||
CipherInputStream(ByteArrayInputStream(input), androidEncrypt).use { cipherInputStream ->
|
||||
CipherOutputStream(androidOutputStream, androidDecrypt).use { outputStream ->
|
||||
outputStream.write(cipherInputStream.readBytes())
|
||||
}
|
||||
}
|
||||
val androidOut = androidOutputStream.toByteArray()
|
||||
|
||||
val nativeEncrypt = CipherFactory.getAES(Cipher.ENCRYPT_MODE, keyArray, ivArray)
|
||||
val nativeDecrypt = CipherFactory.getAES(Cipher.DECRYPT_MODE, keyArray, ivArray)
|
||||
val nativeOutputStream = ByteArrayOutputStream()
|
||||
CipherInputStream(ByteArrayInputStream(input), nativeEncrypt).use { cipherInputStream ->
|
||||
CipherOutputStream(nativeOutputStream, nativeDecrypt).use { outputStream ->
|
||||
outputStream.write(cipherInputStream.readBytes())
|
||||
}
|
||||
}
|
||||
val nativeOut = nativeOutputStream.toByteArray()
|
||||
|
||||
assertArrayEquals("Check AES encryption/decryption", androidOut, nativeOut)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAESKDF() {
|
||||
val seed = ByteArray(32)
|
||||
val key = ByteArray(32)
|
||||
val nativeKey: ByteArray?
|
||||
@@ -49,49 +108,11 @@ class AESTest {
|
||||
mRand.nextBytes(key)
|
||||
|
||||
val androidAESKey = AndroidAESKeyTransformer()
|
||||
androidKey = androidAESKey.transformMasterKey(seed, key, rounds)
|
||||
androidKey = androidAESKey.transformMasterKey(seed, key, 60000)
|
||||
|
||||
val nativeAESKey = NativeAESKeyTransformer()
|
||||
nativeKey = nativeAESKey.transformMasterKey(seed, key, rounds)
|
||||
nativeKey = nativeAESKey.transformMasterKey(seed, key, 60000)
|
||||
|
||||
assertArrayEquals("Does not match", androidKey, nativeKey)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEncrypt() {
|
||||
// Test above below and at the blocksize
|
||||
testFinal(15)
|
||||
testFinal(16)
|
||||
testFinal(17)
|
||||
|
||||
// Test random larger sizes
|
||||
val size = mRand.nextInt(494) + 18
|
||||
testFinal(size)
|
||||
}
|
||||
|
||||
private fun testFinal(dataSize: Int) {
|
||||
// Generate some input
|
||||
val input = ByteArray(dataSize)
|
||||
mRand.nextBytes(input)
|
||||
|
||||
// Generate key
|
||||
val keyArray = ByteArray(32)
|
||||
mRand.nextBytes(keyArray)
|
||||
val key = SecretKeySpec(keyArray, "AES")
|
||||
|
||||
// Generate IV
|
||||
val ivArray = ByteArray(16)
|
||||
mRand.nextBytes(ivArray)
|
||||
val iv = IvParameterSpec(ivArray)
|
||||
|
||||
val android = CipherFactory.getInstance("AES/CBC/PKCS5Padding", true)
|
||||
android.init(Cipher.ENCRYPT_MODE, key, iv)
|
||||
val outAndroid = android.doFinal(input, 0, dataSize)
|
||||
|
||||
val nat = CipherFactory.getInstance("AES/CBC/PKCS5Padding")
|
||||
nat.init(Cipher.ENCRYPT_MODE, key, iv)
|
||||
val outNative = nat.doFinal(input, 0, dataSize)
|
||||
|
||||
assertArrayEquals("Arrays differ on size: $dataSize", outAndroid, outNative)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.kunzisoft.encrypt
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.NoSuchPaddingException
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class CipherEngineFactory {
|
||||
|
||||
companion object {
|
||||
|
||||
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
|
||||
fun getAES(opmode: Int, key: ByteArray, IV: ByteArray): Cipher {
|
||||
// TODO native
|
||||
val androidOverride = false
|
||||
val cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding", androidOverride)
|
||||
cipher.init(opmode, SecretKeySpec(key, "AES"), IvParameterSpec(IV))
|
||||
return cipher
|
||||
}
|
||||
|
||||
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
|
||||
fun getTwofish(opmode: Int, key: ByteArray, IV: ByteArray): Cipher {
|
||||
// TODO native
|
||||
val androidOverride = false
|
||||
val cipher: Cipher = if (opmode == Cipher.ENCRYPT_MODE) {
|
||||
CipherFactory.getInstance("Twofish/CBC/ZeroBytePadding", androidOverride)
|
||||
} else {
|
||||
CipherFactory.getInstance("Twofish/CBC/NoPadding", androidOverride)
|
||||
}
|
||||
cipher.init(opmode, SecretKeySpec(key, "AES"), IvParameterSpec(IV))
|
||||
return cipher
|
||||
}
|
||||
|
||||
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
|
||||
fun getChacha20(opmode: Int, key: ByteArray, IV: ByteArray): Cipher {
|
||||
val cipher = Cipher.getInstance("Chacha7539", BouncyCastleProvider())
|
||||
cipher.init(opmode, SecretKeySpec(key, "ChaCha7539"), IvParameterSpec(IV))
|
||||
return cipher
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +1,51 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX 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.
|
||||
*
|
||||
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.encrypt
|
||||
|
||||
import android.os.Build
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.security.Security
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.NoSuchPaddingException
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
object CipherFactory {
|
||||
|
||||
private var blacklistInit = false
|
||||
private var blacklisted: Boolean = false
|
||||
|
||||
init {
|
||||
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME)
|
||||
Security.addProvider(BouncyCastleProvider())
|
||||
}
|
||||
|
||||
fun deviceBlacklisted(): Boolean {
|
||||
if (!blacklistInit) {
|
||||
blacklistInit = true
|
||||
// The Acer Iconia A500 is special and seems to always crash in the native crypto libraries
|
||||
blacklisted = Build.MODEL == "A500"
|
||||
}
|
||||
return blacklisted
|
||||
}
|
||||
|
||||
private fun hasNativeImplementation(transformation: String): Boolean {
|
||||
return transformation == "AES/CBC/PKCS5Padding"
|
||||
}
|
||||
|
||||
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class)
|
||||
fun getInstance(transformation: String, androidOverride: Boolean = false): Cipher {
|
||||
// Return the native AES if it is possible
|
||||
return if (!deviceBlacklisted() && !androidOverride && hasNativeImplementation(transformation) && NativeLib.loaded()) {
|
||||
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
|
||||
fun getAES(opmode: Int, key: ByteArray, IV: ByteArray, forceNative: Boolean = false): Cipher {
|
||||
val transformation = "AES/CBC/PKCS5Padding"
|
||||
val cipher = if (forceNative || (!NativeBlockList.isBlocked && NativeLib.loaded())) {
|
||||
Cipher.getInstance(transformation, AESProvider())
|
||||
} else {
|
||||
Cipher.getInstance(transformation)
|
||||
}
|
||||
cipher.init(opmode, SecretKeySpec(key, "AES"), IvParameterSpec(IV))
|
||||
return cipher
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
|
||||
fun getTwofish(opmode: Int, key: ByteArray, IV: ByteArray): Cipher {
|
||||
val cipher: Cipher = if (opmode == Cipher.ENCRYPT_MODE) {
|
||||
Cipher.getInstance("Twofish/CBC/ZeroBytePadding")
|
||||
} else {
|
||||
Cipher.getInstance("Twofish/CBC/NoPadding")
|
||||
}
|
||||
// TODO Verify KDB TwoFish
|
||||
// CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING")
|
||||
cipher.init(opmode, SecretKeySpec(key, "AES"), IvParameterSpec(IV))
|
||||
return cipher
|
||||
}
|
||||
|
||||
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
|
||||
fun getChacha20(opmode: Int, key: ByteArray, IV: ByteArray): Cipher {
|
||||
val cipher = Cipher.getInstance("Chacha7539", BouncyCastleProvider())
|
||||
cipher.init(opmode, SecretKeySpec(key, "ChaCha7539"), IvParameterSpec(IV))
|
||||
return cipher
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.kunzisoft.encrypt
|
||||
|
||||
import android.os.Build
|
||||
|
||||
object NativeBlockList {
|
||||
val isBlocked: Boolean by lazy {
|
||||
Build.MODEL == "A500"
|
||||
}
|
||||
}
|
||||
@@ -19,13 +19,12 @@
|
||||
*/
|
||||
package com.kunzisoft.encrypt.aes
|
||||
|
||||
import com.kunzisoft.encrypt.CipherFactory.deviceBlacklisted
|
||||
import com.kunzisoft.encrypt.NativeBlockList
|
||||
|
||||
object AESKeyTransformerFactory : KeyTransformer() {
|
||||
override fun transformMasterKey(seed: ByteArray?, key: ByteArray?, rounds: Long?): ByteArray? {
|
||||
// Prefer the native final key implementation
|
||||
val keyTransformer = if (!deviceBlacklisted()
|
||||
&& NativeAESKeyTransformer.available()) {
|
||||
val keyTransformer = if (!NativeBlockList.isBlocked && NativeAESKeyTransformer.available()) {
|
||||
NativeAESKeyTransformer()
|
||||
} else {
|
||||
// Fall back on the android crypto implementation
|
||||
|
||||
Reference in New Issue
Block a user