mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Refactor HMAC methods
This commit is contained in:
@@ -17,17 +17,35 @@
|
||||
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.stream
|
||||
package com.kunzisoft.keepass.database.crypto
|
||||
|
||||
import com.kunzisoft.encrypt.UnsignedLong
|
||||
import com.kunzisoft.encrypt.stream.NullOutputStream
|
||||
import com.kunzisoft.encrypt.stream.write8BytesLong
|
||||
import java.io.IOException
|
||||
import java.security.DigestOutputStream
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
object HmacBlock {
|
||||
|
||||
fun getHmacSha256(blockKey: ByteArray): Mac {
|
||||
val hmac: Mac
|
||||
try {
|
||||
hmac = Mac.getInstance("HmacSHA256")
|
||||
val signingKey = SecretKeySpec(blockKey, "HmacSHA256")
|
||||
hmac.init(signingKey)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw IOException("No HmacAlogirthm")
|
||||
} catch (e: InvalidKeyException) {
|
||||
throw IOException("Invalid Hmac Key")
|
||||
}
|
||||
return hmac
|
||||
}
|
||||
|
||||
object HmacBlockStream {
|
||||
fun getHmacKey64(key: ByteArray, blockIndex: UnsignedLong): ByteArray {
|
||||
val hash: MessageDigest
|
||||
try {
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.crypto.kdf
|
||||
|
||||
import com.kunzisoft.encrypt.CryptoUtil
|
||||
import com.kunzisoft.encrypt.HashManager
|
||||
import com.kunzisoft.encrypt.UnsignedLong
|
||||
import com.kunzisoft.encrypt.aes.AESKeyTransformerFactory
|
||||
import com.kunzisoft.encrypt.stream.bytes16ToUuid
|
||||
@@ -48,12 +48,12 @@ class AesKdf : KdfEngine() {
|
||||
|
||||
var seed = kdfParameters.getByteArray(PARAM_SEED)
|
||||
if (seed != null && seed.size != 32) {
|
||||
seed = CryptoUtil.hashSha256(seed)
|
||||
seed = HashManager.hashSha256(seed)
|
||||
}
|
||||
|
||||
var currentMasterKey = masterKey
|
||||
if (currentMasterKey.size != 32) {
|
||||
currentMasterKey = CryptoUtil.hashSha256(currentMasterKey)
|
||||
currentMasterKey = HashManager.hashSha256(currentMasterKey)
|
||||
}
|
||||
|
||||
val rounds = kdfParameters.getUInt64(PARAM_ROUNDS)?.toKotlinLong()
|
||||
|
||||
@@ -22,8 +22,9 @@ package com.kunzisoft.keepass.database.element.database
|
||||
import android.content.res.Resources
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.kunzisoft.encrypt.CryptoUtil
|
||||
import com.kunzisoft.encrypt.HashManager
|
||||
import com.kunzisoft.encrypt.UnsignedInt
|
||||
import com.kunzisoft.encrypt.stream.longTo8Bytes
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.action.node.NodeHandler
|
||||
import com.kunzisoft.keepass.database.crypto.AesEngine
|
||||
@@ -56,9 +57,11 @@ import java.io.InputStream
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.util.*
|
||||
import javax.crypto.Mac
|
||||
import javax.xml.XMLConstants
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import javax.xml.parsers.ParserConfigurationException
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
@@ -362,13 +365,13 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
|
||||
var transformedMasterKey = kdfEngine.transform(masterKey, keyDerivationFunctionParameters)
|
||||
if (transformedMasterKey.size != 32) {
|
||||
transformedMasterKey = CryptoUtil.hashSha256(transformedMasterKey)
|
||||
transformedMasterKey = HashManager.hashSha256(transformedMasterKey)
|
||||
}
|
||||
|
||||
val cmpKey = ByteArray(65)
|
||||
System.arraycopy(masterSeed, 0, cmpKey, 0, 32)
|
||||
System.arraycopy(transformedMasterKey, 0, cmpKey, 32, 32)
|
||||
finalKey = CryptoUtil.resizeKey(cmpKey, 0, 64, dataEngine.keyLength())
|
||||
finalKey = resizeKey(cmpKey, dataEngine.keyLength())
|
||||
|
||||
val messageDigest: MessageDigest
|
||||
try {
|
||||
@@ -383,6 +386,49 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
}
|
||||
}
|
||||
|
||||
private fun resizeKey(inBytes: ByteArray, cbOut: Int): ByteArray {
|
||||
if (cbOut == 0) return ByteArray(0)
|
||||
|
||||
val hash: ByteArray = if (cbOut <= 32) {
|
||||
HashManager.hashSha256(inBytes, 0, 64)
|
||||
} else {
|
||||
HashManager.hashSha512(inBytes, 0, 64)
|
||||
}
|
||||
|
||||
if (cbOut == hash.size) {
|
||||
return hash
|
||||
}
|
||||
|
||||
val ret = ByteArray(cbOut)
|
||||
if (cbOut < hash.size) {
|
||||
System.arraycopy(hash, 0, ret, 0, cbOut)
|
||||
} else {
|
||||
var pos = 0
|
||||
var r: Long = 0
|
||||
while (pos < cbOut) {
|
||||
val hmac: Mac
|
||||
try {
|
||||
hmac = Mac.getInstance("HmacSHA256")
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
|
||||
val pbR = longTo8Bytes(r)
|
||||
val part = hmac.doFinal(pbR)
|
||||
|
||||
val copy = min(cbOut - pos, part.size)
|
||||
System.arraycopy(part, 0, ret, pos, copy)
|
||||
pos += copy
|
||||
r++
|
||||
|
||||
Arrays.fill(part, 0.toByte())
|
||||
}
|
||||
}
|
||||
|
||||
Arrays.fill(hash, 0.toByte())
|
||||
return ret
|
||||
}
|
||||
|
||||
override fun loadXmlKeyFile(keyInputStream: InputStream): ByteArray? {
|
||||
try {
|
||||
val documentBuilderFactory = DocumentBuilderFactory.newInstance()
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.kunzisoft.encrypt.UnsignedInt
|
||||
import com.kunzisoft.encrypt.UnsignedLong
|
||||
import com.kunzisoft.encrypt.stream.*
|
||||
import com.kunzisoft.keepass.database.action.node.NodeHandler
|
||||
import com.kunzisoft.keepass.database.crypto.HmacBlock
|
||||
import com.kunzisoft.keepass.database.crypto.VariantDictionary
|
||||
import com.kunzisoft.keepass.database.crypto.kdf.AesKdf
|
||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory
|
||||
@@ -35,16 +36,13 @@ import com.kunzisoft.keepass.database.element.group.GroupKDBX
|
||||
import com.kunzisoft.keepass.database.element.node.NodeKDBXInterface
|
||||
import com.kunzisoft.keepass.database.exception.VersionDatabaseException
|
||||
import com.kunzisoft.keepass.stream.CopyInputStream
|
||||
import com.kunzisoft.keepass.stream.HmacBlockStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.security.DigestInputStream
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader() {
|
||||
var innerRandomStreamKey: ByteArray = ByteArray(32)
|
||||
@@ -327,19 +325,8 @@ class DatabaseHeaderKDBX(private val databaseV4: DatabaseKDBX) : DatabaseHeader(
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun computeHeaderHmac(header: ByteArray, key: ByteArray): ByteArray {
|
||||
val blockKey = HmacBlockStream.getHmacKey64(key, UnsignedLong.MAX)
|
||||
|
||||
val hmac: Mac
|
||||
try {
|
||||
hmac = Mac.getInstance("HmacSHA256")
|
||||
val signingKey = SecretKeySpec(blockKey, "HmacSHA256")
|
||||
hmac.init(signingKey)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw IOException("No HmacAlogirthm")
|
||||
} catch (e: InvalidKeyException) {
|
||||
throw IOException("Invalid Hmac Key")
|
||||
}
|
||||
|
||||
val blockKey = HmacBlock.getHmacKey64(key, UnsignedLong.MAX)
|
||||
val hmac: Mac = HmacBlock.getHmacSha256(blockKey)
|
||||
return hmac.doFinal(header)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.kunzisoft.encrypt.UnsignedLong
|
||||
import com.kunzisoft.encrypt.stream.*
|
||||
import com.kunzisoft.keepass.database.crypto.CipherEngine
|
||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||
import com.kunzisoft.keepass.database.crypto.HmacBlock
|
||||
import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.DateInstant
|
||||
import com.kunzisoft.keepass.database.element.DeletedObject
|
||||
@@ -60,6 +61,7 @@ import java.util.*
|
||||
import java.util.zip.GZIPInputStream
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.CipherInputStream
|
||||
import javax.crypto.Mac
|
||||
import kotlin.math.min
|
||||
|
||||
class DatabaseInputKDBX(cacheDirectory: File,
|
||||
@@ -181,7 +183,11 @@ class DatabaseInputKDBX(cacheDirectory: File,
|
||||
}
|
||||
|
||||
val hmacKey = mDatabase.hmacKey ?: throw LoadDatabaseException()
|
||||
val headerHmac = DatabaseHeaderKDBX.computeHeaderHmac(pbHeader, hmacKey)
|
||||
|
||||
val blockKey = HmacBlock.getHmacKey64(hmacKey, UnsignedLong.MAX)
|
||||
val hmac: Mac = HmacBlock.getHmacSha256(blockKey)
|
||||
val headerHmac = hmac.doFinal(pbHeader)
|
||||
|
||||
val storedHmac = databaseInputStream.readBytesLength(32)
|
||||
if (storedHmac.size != 32) {
|
||||
throw InvalidCredentialsDatabaseException()
|
||||
|
||||
@@ -22,25 +22,23 @@ package com.kunzisoft.keepass.database.file.output
|
||||
import com.kunzisoft.encrypt.UnsignedInt
|
||||
import com.kunzisoft.encrypt.UnsignedLong
|
||||
import com.kunzisoft.encrypt.stream.*
|
||||
import com.kunzisoft.keepass.database.crypto.HmacBlock
|
||||
import com.kunzisoft.keepass.database.crypto.VariantDictionary
|
||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfParameters
|
||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
||||
import com.kunzisoft.keepass.database.exception.DatabaseOutputException
|
||||
import com.kunzisoft.keepass.database.file.DatabaseHeader
|
||||
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX
|
||||
import com.kunzisoft.keepass.stream.HmacBlockStream
|
||||
import com.kunzisoft.keepass.stream.MacOutputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.security.DigestOutputStream
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class DatabaseHeaderOutputKDBX @Throws(DatabaseOutputException::class)
|
||||
class DatabaseHeaderOutputKDBX @Throws(IOException::class)
|
||||
constructor(private val databaseKDBX: DatabaseKDBX,
|
||||
private val header: DatabaseHeaderKDBX,
|
||||
outputStream: OutputStream) {
|
||||
@@ -68,16 +66,7 @@ constructor(private val databaseKDBX: DatabaseKDBX,
|
||||
}
|
||||
|
||||
val hmacKey = databaseKDBX.hmacKey ?: throw DatabaseOutputException("HmacKey is not defined")
|
||||
val hmac: Mac
|
||||
try {
|
||||
hmac = Mac.getInstance("HmacSHA256")
|
||||
val signingKey = SecretKeySpec(HmacBlockStream.getHmacKey64(hmacKey, UnsignedLong.MAX), "HmacSHA256")
|
||||
hmac.init(signingKey)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw DatabaseOutputException(e)
|
||||
} catch (e: InvalidKeyException) {
|
||||
throw DatabaseOutputException(e)
|
||||
}
|
||||
val hmac: Mac = HmacBlock.getHmacSha256(HmacBlock.getHmacKey64(hmacKey, UnsignedLong.MAX))
|
||||
|
||||
dos = DigestOutputStream(outputStream, md)
|
||||
mos = MacOutputStream(dos, hmac)
|
||||
|
||||
@@ -23,13 +23,11 @@ import com.kunzisoft.encrypt.UnsignedLong
|
||||
import com.kunzisoft.encrypt.stream.bytes4ToUInt
|
||||
import com.kunzisoft.encrypt.stream.readBytesLength
|
||||
import com.kunzisoft.encrypt.stream.uLongTo8Bytes
|
||||
import com.kunzisoft.keepass.database.crypto.HmacBlock
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.util.*
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class HmacBlockInputStream(private val baseStream: InputStream, private val verify: Boolean, private val key: ByteArray) : InputStream() {
|
||||
|
||||
@@ -104,19 +102,8 @@ class HmacBlockInputStream(private val baseStream: InputStream, private val veri
|
||||
buffer = baseStream.readBytesLength(blockSize.toKotlinInt())
|
||||
|
||||
if (verify) {
|
||||
val cmpHmac: ByteArray
|
||||
val blockKey = HmacBlockStream.getHmacKey64(key, blockIndex)
|
||||
val hmac: Mac
|
||||
try {
|
||||
hmac = Mac.getInstance("HmacSHA256")
|
||||
val signingKey = SecretKeySpec(blockKey, "HmacSHA256")
|
||||
hmac.init(signingKey)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw IOException("Invalid Hmac")
|
||||
} catch (e: InvalidKeyException) {
|
||||
throw IOException("Invalid Hmac")
|
||||
}
|
||||
|
||||
val blockKey = HmacBlock.getHmacKey64(key, blockIndex)
|
||||
val hmac: Mac = HmacBlock.getHmacSha256(blockKey)
|
||||
hmac.update(pbBlockIndex)
|
||||
hmac.update(pbBlockSize)
|
||||
|
||||
@@ -124,7 +111,7 @@ class HmacBlockInputStream(private val baseStream: InputStream, private val veri
|
||||
hmac.update(buffer)
|
||||
}
|
||||
|
||||
cmpHmac = hmac.doFinal()
|
||||
val cmpHmac: ByteArray = hmac.doFinal()
|
||||
Arrays.fill(blockKey, 0.toByte())
|
||||
|
||||
if (!cmpHmac.contentEquals(storedHmac)) {
|
||||
|
||||
@@ -23,12 +23,10 @@ import com.kunzisoft.encrypt.UnsignedInt
|
||||
import com.kunzisoft.encrypt.UnsignedLong
|
||||
import com.kunzisoft.encrypt.stream.uIntTo4Bytes
|
||||
import com.kunzisoft.encrypt.stream.uLongTo8Bytes
|
||||
import com.kunzisoft.keepass.database.crypto.HmacBlock
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class HmacBlockOutputStream(private val baseStream: OutputStream,
|
||||
private val key: ByteArray)
|
||||
@@ -90,20 +88,8 @@ class HmacBlockOutputStream(private val baseStream: OutputStream,
|
||||
val bufBlockIndex = uLongTo8Bytes(blockIndex)
|
||||
val blockSizeBuf = uIntTo4Bytes(UnsignedInt(bufferPos))
|
||||
|
||||
val blockHmac: ByteArray
|
||||
val blockKey = HmacBlockStream.getHmacKey64(key, blockIndex)
|
||||
|
||||
val hmac: Mac
|
||||
try {
|
||||
hmac = Mac.getInstance("HmacSHA256")
|
||||
val signingKey = SecretKeySpec(blockKey, "HmacSHA256")
|
||||
hmac.init(signingKey)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw IOException("Invalid Hmac")
|
||||
} catch (e: InvalidKeyException) {
|
||||
throw IOException("Invalid HMAC")
|
||||
}
|
||||
|
||||
val blockKey = HmacBlock.getHmacKey64(key, blockIndex)
|
||||
val hmac: Mac = HmacBlock.getHmacSha256(blockKey)
|
||||
hmac.update(bufBlockIndex)
|
||||
hmac.update(blockSizeBuf)
|
||||
|
||||
@@ -111,8 +97,7 @@ class HmacBlockOutputStream(private val baseStream: OutputStream,
|
||||
hmac.update(buffer, 0, bufferPos)
|
||||
}
|
||||
|
||||
blockHmac = hmac.doFinal()
|
||||
|
||||
val blockHmac: ByteArray = hmac.doFinal()
|
||||
baseStream.write(blockHmac)
|
||||
baseStream.write(blockSizeBuf)
|
||||
|
||||
|
||||
@@ -20,59 +20,12 @@
|
||||
package com.kunzisoft.encrypt
|
||||
|
||||
import com.kunzisoft.encrypt.stream.NullOutputStream
|
||||
import com.kunzisoft.encrypt.stream.longTo8Bytes
|
||||
import java.io.IOException
|
||||
import java.security.DigestOutputStream
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.util.*
|
||||
import javax.crypto.Mac
|
||||
import kotlin.math.min
|
||||
|
||||
object CryptoUtil {
|
||||
|
||||
fun resizeKey(inBytes: ByteArray, inOffset: Int, cbIn: Int, cbOut: Int): ByteArray {
|
||||
if (cbOut == 0) return ByteArray(0)
|
||||
|
||||
val hash: ByteArray = if (cbOut <= 32) {
|
||||
hashSha256(inBytes, inOffset, cbIn)
|
||||
} else {
|
||||
hashSha512(inBytes, inOffset, cbIn)
|
||||
}
|
||||
|
||||
if (cbOut == hash.size) {
|
||||
return hash
|
||||
}
|
||||
|
||||
val ret = ByteArray(cbOut)
|
||||
if (cbOut < hash.size) {
|
||||
System.arraycopy(hash, 0, ret, 0, cbOut)
|
||||
} else {
|
||||
var pos = 0
|
||||
var r: Long = 0
|
||||
while (pos < cbOut) {
|
||||
val hmac: Mac
|
||||
try {
|
||||
hmac = Mac.getInstance("HmacSHA256")
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
|
||||
val pbR = longTo8Bytes(r)
|
||||
val part = hmac.doFinal(pbR)
|
||||
|
||||
val copy = min(cbOut - pos, part.size)
|
||||
System.arraycopy(part, 0, ret, pos, copy)
|
||||
pos += copy
|
||||
r++
|
||||
|
||||
Arrays.fill(part, 0.toByte())
|
||||
}
|
||||
}
|
||||
|
||||
Arrays.fill(hash, 0.toByte())
|
||||
return ret
|
||||
}
|
||||
object HashManager {
|
||||
|
||||
fun hashSha256(data: ByteArray, offset: Int = 0, count: Int = data.size): ByteArray {
|
||||
return hashGen("SHA-256", data, offset, count)
|
||||
@@ -20,7 +20,7 @@
|
||||
package com.kunzisoft.encrypt.stream
|
||||
|
||||
import com.kunzisoft.encrypt.CrsAlgorithm
|
||||
import com.kunzisoft.encrypt.CryptoUtil
|
||||
import com.kunzisoft.encrypt.HashManager
|
||||
import org.bouncycastle.crypto.engines.ChaCha7539Engine
|
||||
import org.bouncycastle.crypto.engines.Salsa20Engine
|
||||
import org.bouncycastle.crypto.params.KeyParameter
|
||||
@@ -41,7 +41,7 @@ object StreamCipherFactory {
|
||||
|
||||
private fun getSalsa20(key: ByteArray): StreamCipher {
|
||||
// Build stream cipher key
|
||||
val key32 = CryptoUtil.hashSha256(key)
|
||||
val key32 = HashManager.hashSha256(key)
|
||||
|
||||
val keyParam = KeyParameter(key32)
|
||||
val ivParam = ParametersWithIV(keyParam, SALSA_IV)
|
||||
@@ -54,7 +54,7 @@ object StreamCipherFactory {
|
||||
|
||||
private fun getChaCha20(key: ByteArray): StreamCipher {
|
||||
// Build stream cipher key
|
||||
val hash = CryptoUtil.hashSha512(key)
|
||||
val hash = HashManager.hashSha512(key)
|
||||
val key32 = ByteArray(32)
|
||||
val iv = ByteArray(12)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user