mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Prepare hardware key in main credential
This commit is contained in:
@@ -42,8 +42,7 @@ open class AssignMainCredentialInDatabaseRunnable (
|
||||
mBackupKey = ByteArray(database.masterKey.size)
|
||||
System.arraycopy(database.masterKey, 0, mBackupKey!!, 0, mBackupKey!!.size)
|
||||
|
||||
val uriInputStream = UriUtil.getUriInputStream(context.contentResolver, mMainCredential.keyFileUri)
|
||||
database.assignMasterKey(mMainCredential.masterPassword, uriInputStream)
|
||||
database.assignMasterKey(context.contentResolver, mMainCredential)
|
||||
} catch (e: Exception) {
|
||||
erase(mBackupKey)
|
||||
setError(e)
|
||||
|
||||
@@ -604,6 +604,20 @@ class Database {
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun getKeyFileData(contentResolver: ContentResolver, keyFileUri: Uri?): ByteArray? {
|
||||
try {
|
||||
keyFileUri?.let { uri ->
|
||||
UriUtil.getUriInputStream(contentResolver, uri)?.use { keyFileInputStream ->
|
||||
return keyFileInputStream.readBytes()
|
||||
}
|
||||
}
|
||||
} catch (e: OutOfMemoryError) {
|
||||
throw LoadDatabaseException("Keyfile too large")
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@Throws(LoadDatabaseException::class)
|
||||
fun loadData(uri: Uri,
|
||||
mainCredential: MainCredential,
|
||||
@@ -620,14 +634,7 @@ class Database {
|
||||
// Check if the file is writable
|
||||
this.isReadOnly = readOnly
|
||||
|
||||
// Pass KeyFile Uri as InputStreams
|
||||
var keyFileInputStream: InputStream? = null
|
||||
try {
|
||||
// Get keyFile inputStream
|
||||
mainCredential.keyFileUri?.let { keyFile ->
|
||||
keyFileInputStream = UriUtil.getUriInputStream(contentResolver, keyFile)
|
||||
}
|
||||
|
||||
// Read database stream for the first time
|
||||
readDatabaseStream(contentResolver, uri,
|
||||
{ databaseInputStream ->
|
||||
@@ -641,7 +648,8 @@ class Database {
|
||||
) {
|
||||
databaseKDB.retrieveMasterKey(
|
||||
mainCredential.masterPassword,
|
||||
keyFileInputStream
|
||||
getKeyFileData(contentResolver, mainCredential.keyFileUri),
|
||||
mainCredential.hardwareKeyData
|
||||
)
|
||||
}
|
||||
databaseKDB
|
||||
@@ -657,7 +665,8 @@ class Database {
|
||||
progressTaskUpdater) {
|
||||
databaseKDBX.retrieveMasterKey(
|
||||
mainCredential.masterPassword,
|
||||
keyFileInputStream,
|
||||
getKeyFileData(contentResolver, mainCredential.keyFileUri),
|
||||
mainCredential.hardwareKeyData
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -671,7 +680,6 @@ class Database {
|
||||
} catch (e: Exception) {
|
||||
throw LoadDatabaseException(e)
|
||||
} finally {
|
||||
keyFileInputStream?.close()
|
||||
dataModifiedSinceLastLoading = false
|
||||
}
|
||||
}
|
||||
@@ -695,18 +703,9 @@ class Database {
|
||||
val databaseToMerge = Database()
|
||||
databaseToMerge.fileUri = databaseToMergeUri ?: this.fileUri
|
||||
|
||||
// Pass KeyFile Uri as InputStreams
|
||||
var keyFileInputStream: InputStream? = null
|
||||
try {
|
||||
val databaseUri = databaseToMerge.fileUri
|
||||
if (databaseUri != null) {
|
||||
if (databaseToMergeMainCredential != null) {
|
||||
// Get keyFile inputStream
|
||||
databaseToMergeMainCredential.keyFileUri?.let { keyFile ->
|
||||
keyFileInputStream = UriUtil.getUriInputStream(contentResolver, keyFile)
|
||||
}
|
||||
}
|
||||
|
||||
databaseToMerge.readDatabaseStream(contentResolver, databaseUri,
|
||||
{ databaseInputStream ->
|
||||
val databaseToMergeKDB = DatabaseKDB()
|
||||
@@ -715,7 +714,11 @@ class Database {
|
||||
if (databaseToMergeMainCredential != null) {
|
||||
databaseToMergeKDB.retrieveMasterKey(
|
||||
databaseToMergeMainCredential.masterPassword,
|
||||
keyFileInputStream,
|
||||
getKeyFileData(
|
||||
contentResolver,
|
||||
databaseToMergeMainCredential.keyFileUri,
|
||||
),
|
||||
databaseToMergeMainCredential.hardwareKeyData
|
||||
)
|
||||
} else {
|
||||
databaseToMergeKDB.masterKey = masterKey
|
||||
@@ -731,7 +734,11 @@ class Database {
|
||||
if (databaseToMergeMainCredential != null) {
|
||||
databaseToMergeKDBX.retrieveMasterKey(
|
||||
databaseToMergeMainCredential.masterPassword,
|
||||
keyFileInputStream,
|
||||
getKeyFileData(
|
||||
contentResolver,
|
||||
databaseToMergeMainCredential.keyFileUri
|
||||
),
|
||||
databaseToMergeMainCredential.hardwareKeyData
|
||||
)
|
||||
} else {
|
||||
databaseToMergeKDBX.masterKey = masterKey
|
||||
@@ -769,7 +776,6 @@ class Database {
|
||||
} catch (e: Exception) {
|
||||
throw LoadDatabaseException(e)
|
||||
} finally {
|
||||
keyFileInputStream?.close()
|
||||
databaseToMerge.clearAndClose()
|
||||
}
|
||||
}
|
||||
@@ -1024,9 +1030,19 @@ class Database {
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun assignMasterKey(key: String?, keyInputStream: InputStream?) {
|
||||
mDatabaseKDB?.retrieveMasterKey(key, keyInputStream)
|
||||
mDatabaseKDBX?.retrieveMasterKey(key, keyInputStream)
|
||||
fun assignMasterKey(contentResolver: ContentResolver,
|
||||
mainCredential: MainCredential) {
|
||||
val keyFileData = getKeyFileData(contentResolver, mainCredential.keyFileUri)
|
||||
mDatabaseKDB?.retrieveMasterKey(
|
||||
mainCredential.masterPassword,
|
||||
keyFileData,
|
||||
mainCredential.hardwareKeyData
|
||||
)
|
||||
mDatabaseKDBX?.retrieveMasterKey(
|
||||
mainCredential.masterPassword,
|
||||
keyFileData,
|
||||
mainCredential.hardwareKeyData
|
||||
)
|
||||
mDatabaseKDBX?.keyLastChanged = DateInstant()
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ import com.kunzisoft.keepass.database.element.node.NodeIdInt
|
||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||
import com.kunzisoft.keepass.database.element.node.NodeVersioned
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
|
||||
class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
@@ -117,14 +116,15 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {
|
||||
|
||||
return if (key != null && keyInputStream != null) {
|
||||
getCompositeKey(key, keyInputStream)
|
||||
} else if (key != null) { // key.length() >= 0
|
||||
getPasswordKey(key)
|
||||
} else if (keyInputStream != null) { // key == null
|
||||
getFileKey(keyInputStream)
|
||||
override fun getMasterKey(passwordKey: String?,
|
||||
keyFileData: ByteArray?,
|
||||
hardwareKey: ByteArray?): ByteArray {
|
||||
return if (passwordKey != null && keyFileData != null) {
|
||||
getCompositeKey(passwordKey, keyFileData, null) ?: byteArrayOf()
|
||||
} else if (passwordKey != null) { // key.length() >= 0
|
||||
getPasswordKey(passwordKey)
|
||||
} else if (keyFileData != null) { // key == null
|
||||
getFileKey(keyFileData)
|
||||
} else {
|
||||
throw IllegalArgumentException("Key cannot be empty.")
|
||||
}
|
||||
|
||||
@@ -529,19 +529,11 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
public override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {
|
||||
|
||||
var masterKey = byteArrayOf()
|
||||
|
||||
if (key != null && keyInputStream != null) {
|
||||
return getCompositeKey(key, keyInputStream)
|
||||
} else if (key != null) { // key.length() >= 0
|
||||
masterKey = getPasswordKey(key)
|
||||
} else if (keyInputStream != null) { // key == null
|
||||
masterKey = getFileKey(keyInputStream)
|
||||
}
|
||||
|
||||
return HashManager.hashSha256(masterKey)
|
||||
public override fun getMasterKey(passwordKey: String?,
|
||||
keyFileData: ByteArray?,
|
||||
hardwareKey: ByteArray?): ByteArray {
|
||||
return getCompositeKey(passwordKey, keyFileData, hardwareKey)
|
||||
?: HashManager.hashSha256(byteArrayOf())
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
|
||||
@@ -92,18 +92,30 @@ abstract class DatabaseVersioned<
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
protected abstract fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray
|
||||
protected abstract fun getMasterKey(passwordKey: String?,
|
||||
keyFileData: ByteArray?,
|
||||
hardwareKey: ByteArray?): ByteArray
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun retrieveMasterKey(key: String?, keyfileInputStream: InputStream?) {
|
||||
masterKey = getMasterKey(key, keyfileInputStream)
|
||||
fun retrieveMasterKey(key: String?,
|
||||
keyFileData: ByteArray?,
|
||||
hardwareKeyData: ByteArray?) {
|
||||
masterKey = getMasterKey(key, keyFileData, hardwareKeyData)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
protected fun getCompositeKey(key: String, keyfileInputStream: InputStream): ByteArray {
|
||||
val fileKey = getFileKey(keyfileInputStream)
|
||||
val passwordKey = getPasswordKey(key)
|
||||
return HashManager.hashSha256(passwordKey, fileKey)
|
||||
protected fun getCompositeKey(passwordKey: String?,
|
||||
keyFileData: ByteArray?,
|
||||
hardwareKeyData: ByteArray?): ByteArray? {
|
||||
if (passwordKey == null && keyFileData == null && hardwareKeyData == null)
|
||||
return null
|
||||
val passwordBytes = if (passwordKey != null) getPasswordKey(passwordKey) else null
|
||||
val keyFileBytes = if (keyFileData != null) getFileKey(keyFileData) else null
|
||||
return HashManager.hashSha256(
|
||||
passwordBytes,
|
||||
keyFileBytes,
|
||||
hardwareKeyData
|
||||
)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
@@ -117,10 +129,8 @@ abstract class DatabaseVersioned<
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
protected fun getFileKey(keyInputStream: InputStream): ByteArray {
|
||||
protected fun getFileKey(keyData: ByteArray): ByteArray {
|
||||
try {
|
||||
val keyData = keyInputStream.readBytes()
|
||||
|
||||
// Check XML key file
|
||||
val xmlKeyByteArray = loadXmlKeyFile(ByteArrayInputStream(keyData))
|
||||
if (xmlKeyByteArray != null) {
|
||||
|
||||
@@ -41,11 +41,6 @@ class CipherEncryptDatabase(): Parcelable {
|
||||
parcel.readByteArray(specParameters)
|
||||
}
|
||||
|
||||
fun replaceContent(copy: CipherEncryptDatabase) {
|
||||
this.encryptedValue = copy.encryptedValue
|
||||
this.specParameters = copy.specParameters
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeParcelable(databaseUri, flags)
|
||||
parcel.writeEnum(credentialStorage)
|
||||
|
||||
@@ -1,25 +1,81 @@
|
||||
/*
|
||||
* Copyright 2022 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.keepass.model
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
data class MainCredential(var masterPassword: String? = null, var keyFileUri: Uri? = null): Parcelable {
|
||||
data class MainCredential(var masterPassword: String? = null,
|
||||
var keyFileUri: Uri? = null,
|
||||
var hardwareKeyData: ByteArray? = null): Parcelable {
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readString(),
|
||||
parcel.readParcelable(Uri::class.java.classLoader)) {
|
||||
constructor(parcel: Parcel) : this() {
|
||||
masterPassword = parcel.readString()
|
||||
keyFileUri = parcel.readParcelable(Uri::class.java.classLoader)
|
||||
val hardwareKeyDataLength = parcel.readInt()
|
||||
if (hardwareKeyDataLength >= 0) {
|
||||
hardwareKeyData = ByteArray(hardwareKeyDataLength)
|
||||
parcel.readByteArray(hardwareKeyData!!)
|
||||
} else {
|
||||
hardwareKeyData = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(masterPassword)
|
||||
parcel.writeParcelable(keyFileUri, flags)
|
||||
if (hardwareKeyData != null) {
|
||||
parcel.writeInt(hardwareKeyData!!.size)
|
||||
parcel.writeByteArray(hardwareKeyData)
|
||||
} else {
|
||||
parcel.writeInt(-1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as MainCredential
|
||||
|
||||
if (masterPassword != other.masterPassword) return false
|
||||
if (keyFileUri != other.keyFileUri) return false
|
||||
if (hardwareKeyData != null) {
|
||||
if (other.hardwareKeyData == null) return false
|
||||
if (!hardwareKeyData.contentEquals(other.hardwareKeyData)) return false
|
||||
} else if (other.hardwareKeyData != null) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = masterPassword?.hashCode() ?: 0
|
||||
result = 31 * result + (keyFileUri?.hashCode() ?: 0)
|
||||
result = 31 * result + (hardwareKeyData?.contentHashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<MainCredential> {
|
||||
override fun createFromParcel(parcel: Parcel): MainCredential {
|
||||
return MainCredential(parcel)
|
||||
|
||||
@@ -39,9 +39,10 @@ object HashManager {
|
||||
return messageDigest
|
||||
}
|
||||
|
||||
fun hashSha256(vararg data: ByteArray): ByteArray {
|
||||
fun hashSha256(vararg data: ByteArray?): ByteArray {
|
||||
val hash: MessageDigest = getHash256()
|
||||
for (byteArray in data) {
|
||||
if (byteArray != null)
|
||||
hash.update(byteArray)
|
||||
}
|
||||
return hash.digest()
|
||||
@@ -57,9 +58,10 @@ object HashManager {
|
||||
return messageDigest
|
||||
}
|
||||
|
||||
private fun hashSha512(vararg data: ByteArray): ByteArray {
|
||||
private fun hashSha512(vararg data: ByteArray?): ByteArray {
|
||||
val hash: MessageDigest = getHash512()
|
||||
for (byteArray in data) {
|
||||
if (byteArray != null)
|
||||
hash.update(byteArray)
|
||||
}
|
||||
return hash.digest()
|
||||
|
||||
Reference in New Issue
Block a user