mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-04-04 05:17:36 +03:00
fix: Separate Main credential, remove ContentProvider from Database
This commit is contained in:
parent
7db0c4f7d3
commit
6742a8893c
25 changed files with 339 additions and 279 deletions
|
@ -19,9 +19,7 @@
|
|||
*/
|
||||
package com.kunzisoft.keepass.database.element
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import com.kunzisoft.androidclearchroma.ChromaUtil
|
||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||
|
@ -56,8 +54,6 @@ import com.kunzisoft.keepass.database.search.SearchParameters
|
|||
import com.kunzisoft.keepass.hardware.HardwareKey
|
||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||
import com.kunzisoft.keepass.utils.*
|
||||
import com.kunzisoft.keepass.utils.UriHelper.getUriInputStream
|
||||
import com.kunzisoft.keepass.utils.UriHelper.getUriOutputStream
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
|
||||
|
@ -68,9 +64,6 @@ open class Database {
|
|||
private var mDatabaseKDB: DatabaseKDB? = null
|
||||
private var mDatabaseKDBX: DatabaseKDBX? = null
|
||||
|
||||
var fileUri: Uri? = null
|
||||
private set
|
||||
|
||||
private var mSearchHelper: SearchHelper = SearchHelper()
|
||||
|
||||
var isReadOnly = false
|
||||
|
@ -548,22 +541,19 @@ open class Database {
|
|||
}
|
||||
|
||||
fun createData(
|
||||
databaseUri: Uri,
|
||||
databaseName: String,
|
||||
rootName: String,
|
||||
templateGroupName: String?
|
||||
) {
|
||||
setDatabaseKDBX(DatabaseKDBX(databaseName, rootName, templateGroupName))
|
||||
this.fileUri = databaseUri
|
||||
// Set Database state
|
||||
this.dataModifiedSinceLastLoading = false
|
||||
}
|
||||
|
||||
@Throws(DatabaseInputException::class)
|
||||
fun loadData(
|
||||
contentResolver: ContentResolver,
|
||||
databaseUri: Uri,
|
||||
mainCredential: MainCredential,
|
||||
databaseStream: InputStream,
|
||||
masterCredential: MasterCredential,
|
||||
challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray,
|
||||
readOnly: Boolean,
|
||||
cacheDirectory: File,
|
||||
|
@ -571,16 +561,12 @@ open class Database {
|
|||
fixDuplicateUUID: Boolean,
|
||||
progressTaskUpdater: ProgressTaskUpdater?
|
||||
) {
|
||||
|
||||
// Save database URI
|
||||
this.fileUri = databaseUri
|
||||
|
||||
// Check if the file is writable
|
||||
this.isReadOnly = readOnly
|
||||
|
||||
try {
|
||||
// Read database stream for the first time
|
||||
readDatabaseStream(contentResolver, databaseUri,
|
||||
readDatabaseStream(databaseStream,
|
||||
{ databaseInputStream ->
|
||||
val databaseKDB = DatabaseKDB().apply {
|
||||
binaryCache.cacheDirectory = cacheDirectory
|
||||
|
@ -591,9 +577,8 @@ open class Database {
|
|||
progressTaskUpdater
|
||||
) {
|
||||
databaseKDB.deriveMasterKey(
|
||||
contentResolver,
|
||||
mainCredential
|
||||
)
|
||||
masterCredential
|
||||
)
|
||||
}
|
||||
setDatabaseKDB(databaseKDB)
|
||||
},
|
||||
|
@ -607,8 +592,7 @@ open class Database {
|
|||
openDatabase(databaseInputStream,
|
||||
progressTaskUpdater) {
|
||||
databaseKDBX.deriveMasterKey(
|
||||
contentResolver,
|
||||
mainCredential,
|
||||
masterCredential,
|
||||
challengeResponseRetriever
|
||||
)
|
||||
}
|
||||
|
@ -633,9 +617,8 @@ open class Database {
|
|||
|
||||
@Throws(DatabaseInputException::class)
|
||||
fun mergeData(
|
||||
contentResolver: ContentResolver,
|
||||
databaseToMergeUri: Uri?,
|
||||
databaseToMergeMainCredential: MainCredential?,
|
||||
databaseToMergeStream: InputStream,
|
||||
databaseToMergeMasterCredential: MasterCredential?,
|
||||
databaseToMergeChallengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray,
|
||||
isRAMSufficient: (memoryWanted: Long) -> Boolean,
|
||||
progressTaskUpdater: ProgressTaskUpdater?
|
||||
|
@ -647,71 +630,58 @@ open class Database {
|
|||
|
||||
// New database instance to get new changes
|
||||
val databaseToMerge = Database()
|
||||
databaseToMerge.fileUri = databaseToMergeUri ?: this.fileUri
|
||||
|
||||
try {
|
||||
val databaseUri = databaseToMerge.fileUri
|
||||
if (databaseUri != null) {
|
||||
readDatabaseStream(contentResolver, databaseUri,
|
||||
{ databaseInputStream ->
|
||||
val databaseToMergeKDB = DatabaseKDB()
|
||||
DatabaseInputKDB(databaseToMergeKDB)
|
||||
.openDatabase(databaseInputStream, progressTaskUpdater) {
|
||||
if (databaseToMergeMainCredential != null) {
|
||||
databaseToMergeKDB.deriveMasterKey(
|
||||
contentResolver,
|
||||
databaseToMergeMainCredential
|
||||
)
|
||||
} else {
|
||||
this@Database.mDatabaseKDB?.let { thisDatabaseKDB ->
|
||||
databaseToMergeKDB.copyMasterKeyFrom(thisDatabaseKDB)
|
||||
}
|
||||
}
|
||||
}
|
||||
databaseToMerge.setDatabaseKDB(databaseToMergeKDB)
|
||||
},
|
||||
{ databaseInputStream ->
|
||||
val databaseToMergeKDBX = DatabaseKDBX()
|
||||
DatabaseInputKDBX(databaseToMergeKDBX).apply {
|
||||
setMethodToCheckIfRAMIsSufficient(isRAMSufficient)
|
||||
openDatabase(databaseInputStream, progressTaskUpdater) {
|
||||
if (databaseToMergeMainCredential != null) {
|
||||
databaseToMergeKDBX.deriveMasterKey(
|
||||
contentResolver,
|
||||
databaseToMergeMainCredential,
|
||||
databaseToMergeChallengeResponseRetriever
|
||||
)
|
||||
} else {
|
||||
this@Database.mDatabaseKDBX?.let { thisDatabaseKDBX ->
|
||||
databaseToMergeKDBX.copyMasterKeyFrom(thisDatabaseKDBX)
|
||||
}
|
||||
readDatabaseStream(databaseToMergeStream,
|
||||
{ databaseInputStream ->
|
||||
val databaseToMergeKDB = DatabaseKDB()
|
||||
DatabaseInputKDB(databaseToMergeKDB)
|
||||
.openDatabase(databaseInputStream, progressTaskUpdater) {
|
||||
if (databaseToMergeMasterCredential != null) {
|
||||
databaseToMergeKDB.deriveMasterKey(
|
||||
databaseToMergeMasterCredential
|
||||
)
|
||||
} else {
|
||||
this@Database.mDatabaseKDB?.let { thisDatabaseKDB ->
|
||||
databaseToMergeKDB.copyMasterKeyFrom(thisDatabaseKDB)
|
||||
}
|
||||
}
|
||||
}
|
||||
databaseToMerge.setDatabaseKDBX(databaseToMergeKDBX)
|
||||
}
|
||||
)
|
||||
loaded = true
|
||||
|
||||
mDatabaseKDBX?.let { currentDatabaseKDBX ->
|
||||
val databaseMerger = DatabaseKDBXMerger(currentDatabaseKDBX).apply {
|
||||
this.isRAMSufficient = isRAMSufficient
|
||||
}
|
||||
databaseToMerge.mDatabaseKDB?.let { databaseKDBToMerge ->
|
||||
databaseMerger.merge(databaseKDBToMerge)
|
||||
if (databaseToMergeUri != null) {
|
||||
this.dataModifiedSinceLastLoading = true
|
||||
}
|
||||
}
|
||||
databaseToMerge.mDatabaseKDBX?.let { databaseKDBXToMerge ->
|
||||
databaseMerger.merge(databaseKDBXToMerge)
|
||||
if (databaseToMergeUri != null) {
|
||||
this.dataModifiedSinceLastLoading = true
|
||||
databaseToMerge.setDatabaseKDB(databaseToMergeKDB)
|
||||
},
|
||||
{ databaseInputStream ->
|
||||
val databaseToMergeKDBX = DatabaseKDBX()
|
||||
DatabaseInputKDBX(databaseToMergeKDBX).apply {
|
||||
setMethodToCheckIfRAMIsSufficient(isRAMSufficient)
|
||||
openDatabase(databaseInputStream, progressTaskUpdater) {
|
||||
if (databaseToMergeMasterCredential != null) {
|
||||
databaseToMergeKDBX.deriveMasterKey(
|
||||
databaseToMergeMasterCredential,
|
||||
databaseToMergeChallengeResponseRetriever
|
||||
)
|
||||
} else {
|
||||
this@Database.mDatabaseKDBX?.let { thisDatabaseKDBX ->
|
||||
databaseToMergeKDBX.copyMasterKeyFrom(thisDatabaseKDBX)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
databaseToMerge.setDatabaseKDBX(databaseToMergeKDBX)
|
||||
}
|
||||
)
|
||||
loaded = true
|
||||
|
||||
mDatabaseKDBX?.let { currentDatabaseKDBX ->
|
||||
val databaseMerger = DatabaseKDBXMerger(currentDatabaseKDBX).apply {
|
||||
this.isRAMSufficient = isRAMSufficient
|
||||
}
|
||||
databaseToMerge.mDatabaseKDB?.let { databaseKDBToMerge ->
|
||||
databaseMerger.merge(databaseKDBToMerge)
|
||||
this.dataModifiedSinceLastLoading = true
|
||||
}
|
||||
databaseToMerge.mDatabaseKDBX?.let { databaseKDBXToMerge ->
|
||||
databaseMerger.merge(databaseKDBXToMerge)
|
||||
this.dataModifiedSinceLastLoading = true
|
||||
}
|
||||
} else {
|
||||
throw UnknownDatabaseLocationException()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to merge the database")
|
||||
|
@ -725,49 +695,43 @@ open class Database {
|
|||
|
||||
@Throws(DatabaseInputException::class)
|
||||
fun reloadData(
|
||||
contentResolver: ContentResolver,
|
||||
databaseStream: InputStream,
|
||||
isRAMSufficient: (memoryWanted: Long) -> Boolean,
|
||||
progressTaskUpdater: ProgressTaskUpdater?
|
||||
) {
|
||||
|
||||
// Retrieve the stream from the old database URI
|
||||
try {
|
||||
val oldDatabaseUri = fileUri
|
||||
if (oldDatabaseUri != null) {
|
||||
readDatabaseStream(contentResolver, oldDatabaseUri,
|
||||
{ databaseInputStream ->
|
||||
val databaseKDB = DatabaseKDB()
|
||||
mDatabaseKDB?.let {
|
||||
databaseKDB.binaryCache = it.binaryCache
|
||||
}
|
||||
DatabaseInputKDB(databaseKDB)
|
||||
.openDatabase(databaseInputStream, progressTaskUpdater) {
|
||||
this@Database.mDatabaseKDB?.let { thisDatabaseKDB ->
|
||||
databaseKDB.copyMasterKeyFrom(thisDatabaseKDB)
|
||||
}
|
||||
}
|
||||
setDatabaseKDB(databaseKDB)
|
||||
},
|
||||
{ databaseInputStream ->
|
||||
val databaseKDBX = DatabaseKDBX()
|
||||
mDatabaseKDBX?.let {
|
||||
databaseKDBX.binaryCache = it.binaryCache
|
||||
}
|
||||
DatabaseInputKDBX(databaseKDBX).apply {
|
||||
setMethodToCheckIfRAMIsSufficient(isRAMSufficient)
|
||||
openDatabase(databaseInputStream, progressTaskUpdater) {
|
||||
this@Database.mDatabaseKDBX?.let { thisDatabaseKDBX ->
|
||||
databaseKDBX.copyMasterKeyFrom(thisDatabaseKDBX)
|
||||
}
|
||||
}
|
||||
}
|
||||
setDatabaseKDBX(databaseKDBX)
|
||||
// Retrieve the stream from the old database
|
||||
readDatabaseStream(databaseStream,
|
||||
{ databaseInputStream ->
|
||||
val databaseKDB = DatabaseKDB()
|
||||
mDatabaseKDB?.let {
|
||||
databaseKDB.binaryCache = it.binaryCache
|
||||
}
|
||||
)
|
||||
loaded = true
|
||||
} else {
|
||||
throw UnknownDatabaseLocationException()
|
||||
}
|
||||
DatabaseInputKDB(databaseKDB)
|
||||
.openDatabase(databaseInputStream, progressTaskUpdater) {
|
||||
this@Database.mDatabaseKDB?.let { thisDatabaseKDB ->
|
||||
databaseKDB.copyMasterKeyFrom(thisDatabaseKDB)
|
||||
}
|
||||
}
|
||||
setDatabaseKDB(databaseKDB)
|
||||
},
|
||||
{ databaseInputStream ->
|
||||
val databaseKDBX = DatabaseKDBX()
|
||||
mDatabaseKDBX?.let {
|
||||
databaseKDBX.binaryCache = it.binaryCache
|
||||
}
|
||||
DatabaseInputKDBX(databaseKDBX).apply {
|
||||
setMethodToCheckIfRAMIsSufficient(isRAMSufficient)
|
||||
openDatabase(databaseInputStream, progressTaskUpdater) {
|
||||
this@Database.mDatabaseKDBX?.let { thisDatabaseKDBX ->
|
||||
databaseKDBX.copyMasterKeyFrom(thisDatabaseKDBX)
|
||||
}
|
||||
}
|
||||
}
|
||||
setDatabaseKDBX(databaseKDBX)
|
||||
}
|
||||
)
|
||||
loaded = true
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to reload the database")
|
||||
if (e is DatabaseException)
|
||||
|
@ -780,16 +744,12 @@ open class Database {
|
|||
|
||||
@Throws(Exception::class)
|
||||
private fun readDatabaseStream(
|
||||
contentResolver: ContentResolver,
|
||||
databaseUri: Uri,
|
||||
databaseStream: InputStream,
|
||||
openDatabaseKDB: (InputStream) -> Unit,
|
||||
openDatabaseKDBX: (InputStream) -> Unit
|
||||
) {
|
||||
try {
|
||||
// Load Data, pass Uris as InputStreams
|
||||
val databaseStream = contentResolver.getUriInputStream(databaseUri)
|
||||
?: throw UnknownDatabaseLocationException()
|
||||
|
||||
// Load Data by InputStream
|
||||
BufferedInputStream(databaseStream).use { databaseInputStream ->
|
||||
|
||||
// We'll end up reading 8 bytes to identify the header. Might as well use two extra.
|
||||
|
@ -822,63 +782,54 @@ open class Database {
|
|||
|
||||
@Throws(DatabaseOutputException::class)
|
||||
fun saveData(
|
||||
contentResolver: ContentResolver,
|
||||
cacheDir: File,
|
||||
databaseCopyUri: Uri?,
|
||||
mainCredential: MainCredential?,
|
||||
cacheFile: File,
|
||||
databaseOutputStream: OutputStream?,
|
||||
isNewLocation: Boolean,
|
||||
masterCredential: MasterCredential?,
|
||||
challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray
|
||||
) {
|
||||
val saveUri = databaseCopyUri ?: this.fileUri
|
||||
// Build temp database file to avoid file corruption if error
|
||||
val cacheFile = File(cacheDir, saveUri.hashCode().toString())
|
||||
try {
|
||||
if (saveUri != null) {
|
||||
// Save in a temp memory to avoid exception
|
||||
cacheFile.outputStream().use { outputStream ->
|
||||
mDatabaseKDB?.let { databaseKDB ->
|
||||
DatabaseOutputKDB(databaseKDB).apply {
|
||||
writeDatabase(outputStream) {
|
||||
if (mainCredential != null) {
|
||||
databaseKDB.deriveMasterKey(
|
||||
contentResolver,
|
||||
mainCredential
|
||||
)
|
||||
} else {
|
||||
// No master key change
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?: mDatabaseKDBX?.let { databaseKDBX ->
|
||||
DatabaseOutputKDBX(databaseKDBX).apply {
|
||||
writeDatabase(outputStream) {
|
||||
if (mainCredential != null) {
|
||||
// Build new master key from MainCredential
|
||||
databaseKDBX.deriveMasterKey(
|
||||
contentResolver,
|
||||
mainCredential,
|
||||
challengeResponseRetriever
|
||||
)
|
||||
} else {
|
||||
// Reuse composite key parts
|
||||
databaseKDBX.deriveCompositeKey(
|
||||
challengeResponseRetriever
|
||||
)
|
||||
}
|
||||
// Save in a temp memory to avoid exception
|
||||
cacheFile.outputStream().use { outputStream ->
|
||||
mDatabaseKDB?.let { databaseKDB ->
|
||||
DatabaseOutputKDB(databaseKDB).apply {
|
||||
writeDatabase(outputStream) {
|
||||
if (masterCredential != null) {
|
||||
databaseKDB.deriveMasterKey(
|
||||
masterCredential
|
||||
)
|
||||
} else {
|
||||
// No master key change
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copy from the cache to the final stream
|
||||
contentResolver.getUriOutputStream(saveUri)?.use { outputStream ->
|
||||
cacheFile.inputStream().use { inputStream ->
|
||||
inputStream.readAllBytes { buffer ->
|
||||
outputStream.write(buffer)
|
||||
?: mDatabaseKDBX?.let { databaseKDBX ->
|
||||
DatabaseOutputKDBX(databaseKDBX).apply {
|
||||
writeDatabase(outputStream) {
|
||||
if (masterCredential != null) {
|
||||
// Build new master key from MainCredential
|
||||
databaseKDBX.deriveMasterKey(
|
||||
masterCredential,
|
||||
challengeResponseRetriever
|
||||
)
|
||||
} else {
|
||||
// Reuse composite key parts
|
||||
databaseKDBX.deriveCompositeKey(
|
||||
challengeResponseRetriever
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw UnknownDatabaseLocationException()
|
||||
}
|
||||
// Copy from the cache to the final stream
|
||||
databaseOutputStream?.use { outputStream ->
|
||||
cacheFile.inputStream().use { inputStream ->
|
||||
inputStream.readAllBytes { buffer ->
|
||||
outputStream.write(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to save database", e)
|
||||
|
@ -892,7 +843,7 @@ open class Database {
|
|||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Cache file $cacheFile cannot be deleted", e)
|
||||
}
|
||||
if (databaseCopyUri == null) {
|
||||
if (isNewLocation) {
|
||||
this.dataModifiedSinceLastLoading = false
|
||||
}
|
||||
}
|
||||
|
@ -1010,11 +961,10 @@ open class Database {
|
|||
}
|
||||
}
|
||||
|
||||
fun clearAndClose(filesDirectory: File? = null) {
|
||||
open fun clearAndClose(filesDirectory: File? = null) {
|
||||
clearIndexesAndBinaries(filesDirectory)
|
||||
this.mDatabaseKDB = null
|
||||
this.mDatabaseKDBX = null
|
||||
this.fileUri = null
|
||||
this.loaded = false
|
||||
}
|
||||
|
||||
|
@ -1029,11 +979,11 @@ open class Database {
|
|||
}
|
||||
}
|
||||
|
||||
fun validatePasswordEncoding(mainCredential: MainCredential): Boolean {
|
||||
val password = mainCredential.password
|
||||
val containsKeyFile = mainCredential.keyFileUri != null
|
||||
return mDatabaseKDB?.validatePasswordEncoding(password, containsKeyFile)
|
||||
?: mDatabaseKDBX?.validatePasswordEncoding(password, containsKeyFile)
|
||||
fun isValidCredential(masterCredential: MasterCredential): Boolean {
|
||||
val password = masterCredential.password
|
||||
val containsKeyFile = masterCredential.keyFileData != null
|
||||
return mDatabaseKDB?.isValidCredential(password, containsKeyFile)
|
||||
?: mDatabaseKDBX?.isValidCredential(password, containsKeyFile)
|
||||
?: false
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
*/
|
||||
package com.kunzisoft.keepass.database.element
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.Base64
|
||||
|
@ -29,8 +27,9 @@ import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
|||
import com.kunzisoft.keepass.hardware.HardwareKey
|
||||
import com.kunzisoft.keepass.utils.StringUtil.removeSpaceChars
|
||||
import com.kunzisoft.keepass.utils.StringUtil.toHexString
|
||||
import com.kunzisoft.keepass.utils.UriHelper.getUriInputStream
|
||||
import com.kunzisoft.keepass.utils.readByteArrayCompat
|
||||
import com.kunzisoft.keepass.utils.readEnum
|
||||
import com.kunzisoft.keepass.utils.writeByteArrayCompat
|
||||
import com.kunzisoft.keepass.utils.writeEnum
|
||||
import org.apache.commons.codec.binary.Hex
|
||||
import org.w3c.dom.Node
|
||||
|
@ -43,19 +42,19 @@ import javax.xml.XMLConstants
|
|||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import javax.xml.parsers.ParserConfigurationException
|
||||
|
||||
data class MainCredential(var password: String? = null,
|
||||
var keyFileUri: Uri? = null,
|
||||
var hardwareKey: HardwareKey? = null): Parcelable {
|
||||
data class MasterCredential(var password: String? = null,
|
||||
var keyFileData: ByteArray? = null,
|
||||
var hardwareKey: HardwareKey? = null): Parcelable {
|
||||
|
||||
constructor(parcel: Parcel) : this() {
|
||||
password = parcel.readString()
|
||||
keyFileUri = parcel.readParcelable(Uri::class.java.classLoader)
|
||||
keyFileData = parcel.readByteArrayCompat()
|
||||
hardwareKey = parcel.readEnum<HardwareKey>()
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(password)
|
||||
parcel.writeParcelable(keyFileUri, flags)
|
||||
parcel.writeByteArrayCompat(keyFileData)
|
||||
parcel.writeEnum(hardwareKey)
|
||||
}
|
||||
|
||||
|
@ -67,10 +66,10 @@ data class MainCredential(var password: String? = null,
|
|||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as MainCredential
|
||||
other as MasterCredential
|
||||
|
||||
if (password != other.password) return false
|
||||
if (keyFileUri != other.keyFileUri) return false
|
||||
if (!keyFileData.contentEquals(other.keyFileData)) return false
|
||||
if (hardwareKey != other.hardwareKey) return false
|
||||
|
||||
return true
|
||||
|
@ -78,21 +77,21 @@ data class MainCredential(var password: String? = null,
|
|||
|
||||
override fun hashCode(): Int {
|
||||
var result = password?.hashCode() ?: 0
|
||||
result = 31 * result + (keyFileUri?.hashCode() ?: 0)
|
||||
result = 31 * result + (keyFileData?.hashCode() ?: 0)
|
||||
result = 31 * result + (hardwareKey?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<MainCredential> {
|
||||
override fun createFromParcel(parcel: Parcel): MainCredential {
|
||||
return MainCredential(parcel)
|
||||
companion object CREATOR : Parcelable.Creator<MasterCredential> {
|
||||
override fun createFromParcel(parcel: Parcel): MasterCredential {
|
||||
return MasterCredential(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<MainCredential?> {
|
||||
override fun newArray(size: Int): Array<MasterCredential?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
|
||||
private val TAG = MainCredential::class.java.simpleName
|
||||
private val TAG = MasterCredential::class.java.simpleName
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun retrievePasswordKey(key: String,
|
||||
|
@ -107,17 +106,14 @@ data class MainCredential(var password: String? = null,
|
|||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun retrieveFileKey(contentResolver: ContentResolver,
|
||||
keyFileUri: Uri?,
|
||||
allowXML: Boolean): ByteArray {
|
||||
if (keyFileUri == null)
|
||||
throw IOException("Keyfile URI is null")
|
||||
val keyData = getKeyFileData(contentResolver, keyFileUri)
|
||||
?: throw IOException("No data retrieved")
|
||||
fun retrieveKeyFileDecodedKey(
|
||||
keyFileData: ByteArray,
|
||||
allowXML: Boolean
|
||||
): ByteArray {
|
||||
try {
|
||||
// Check XML key file
|
||||
val xmlKeyByteArray = if (allowXML)
|
||||
loadXmlKeyFile(ByteArrayInputStream(keyData))
|
||||
loadXmlKeyFile(ByteArrayInputStream(keyFileData))
|
||||
else
|
||||
null
|
||||
if (xmlKeyByteArray != null) {
|
||||
|
@ -125,16 +121,16 @@ data class MainCredential(var password: String? = null,
|
|||
}
|
||||
|
||||
// Check 32 bytes key file
|
||||
when (keyData.size) {
|
||||
32 -> return keyData
|
||||
when (keyFileData.size) {
|
||||
32 -> return keyFileData
|
||||
64 -> try {
|
||||
return Hex.decodeHex(String(keyData).toCharArray())
|
||||
return Hex.decodeHex(String(keyFileData).toCharArray())
|
||||
} catch (ignoredException: Exception) {
|
||||
// Key is not base 64, treat it as binary data
|
||||
}
|
||||
}
|
||||
// Hash file as binary data
|
||||
return HashManager.hashSha256(keyData)
|
||||
return HashManager.hashSha256(keyFileData)
|
||||
} catch (e: Exception) {
|
||||
throw IOException("Unable to load the keyfile.", e)
|
||||
}
|
||||
|
@ -145,15 +141,6 @@ data class MainCredential(var password: String? = null,
|
|||
return HashManager.hashSha256(keyData)
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun getKeyFileData(contentResolver: ContentResolver,
|
||||
keyFileUri: Uri): ByteArray? {
|
||||
contentResolver.getUriInputStream(keyFileUri)?.use { keyFileInputStream ->
|
||||
return keyFileInputStream.readBytes()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun loadXmlKeyFile(keyInputStream: InputStream): ByteArray? {
|
||||
try {
|
||||
val documentBuilderFactory = DocumentBuilderFactory.newInstance()
|
|
@ -19,13 +19,12 @@
|
|||
|
||||
package com.kunzisoft.keepass.database.element.database
|
||||
|
||||
import android.content.ContentResolver
|
||||
import com.kunzisoft.encrypt.HashManager
|
||||
import com.kunzisoft.encrypt.aes.AESTransformer
|
||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory
|
||||
import com.kunzisoft.keepass.database.element.MainCredential
|
||||
import com.kunzisoft.keepass.database.element.MasterCredential
|
||||
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
||||
import com.kunzisoft.keepass.database.element.entry.EntryKDB
|
||||
import com.kunzisoft.keepass.database.element.group.GroupKDB
|
||||
|
@ -129,25 +128,23 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
|||
}
|
||||
|
||||
fun deriveMasterKey(
|
||||
contentResolver: ContentResolver,
|
||||
mainCredential: MainCredential
|
||||
masterCredential: MasterCredential
|
||||
) {
|
||||
// Exception when no password
|
||||
if (mainCredential.hardwareKey != null)
|
||||
if (masterCredential.hardwareKey != null)
|
||||
throw HardwareKeyDatabaseException()
|
||||
if (mainCredential.password == null && mainCredential.keyFileUri == null)
|
||||
if (masterCredential.password == null && masterCredential.keyFileData == null)
|
||||
throw EmptyKeyDatabaseException()
|
||||
|
||||
// Retrieve plain data
|
||||
val password = mainCredential.password
|
||||
val keyFileUri = mainCredential.keyFileUri
|
||||
val passwordBytes = if (password != null) MainCredential.retrievePasswordKey(
|
||||
val password = masterCredential.password
|
||||
val keyFileData = masterCredential.keyFileData
|
||||
val passwordBytes = if (password != null) MasterCredential.retrievePasswordKey(
|
||||
password,
|
||||
passwordEncoding
|
||||
) else null
|
||||
val keyFileBytes = if (keyFileUri != null) MainCredential.retrieveFileKey(
|
||||
contentResolver,
|
||||
keyFileUri,
|
||||
val keyFileBytes = if (keyFileData != null) MasterCredential.retrieveKeyFileDecodedKey(
|
||||
keyFileData,
|
||||
false
|
||||
) else null
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
*/
|
||||
package com.kunzisoft.keepass.database.element.database
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.kunzisoft.encrypt.HashManager
|
||||
|
@ -226,24 +225,22 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||
}
|
||||
|
||||
fun deriveMasterKey(
|
||||
contentResolver: ContentResolver,
|
||||
mainCredential: MainCredential,
|
||||
masterCredential: MasterCredential,
|
||||
challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray,
|
||||
) {
|
||||
// Retrieve each plain credential
|
||||
val password = mainCredential.password
|
||||
val keyFileUri = mainCredential.keyFileUri
|
||||
val hardwareKey = mainCredential.hardwareKey
|
||||
val passwordBytes = if (password != null) MainCredential.retrievePasswordKey(
|
||||
val password = masterCredential.password
|
||||
val keyFileData = masterCredential.keyFileData
|
||||
val hardwareKey = masterCredential.hardwareKey
|
||||
val passwordBytes = if (password != null) MasterCredential.retrievePasswordKey(
|
||||
password,
|
||||
passwordEncoding
|
||||
) else null
|
||||
val keyFileBytes = if (keyFileUri != null) MainCredential.retrieveFileKey(
|
||||
contentResolver,
|
||||
keyFileUri,
|
||||
val keyFileBytes = if (keyFileData != null) MasterCredential.retrieveKeyFileDecodedKey(
|
||||
keyFileData,
|
||||
true
|
||||
) else null
|
||||
val hardwareKeyBytes = if (hardwareKey != null) MainCredential.retrieveHardwareKey(
|
||||
val hardwareKeyBytes = if (hardwareKey != null) MasterCredential.retrieveHardwareKey(
|
||||
challengeResponseRetriever.invoke(hardwareKey, transformSeed)
|
||||
) else null
|
||||
|
||||
|
@ -272,7 +269,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||
keyFileBytes
|
||||
)
|
||||
} else {
|
||||
val hardwareKeyBytes = MainCredential.retrieveHardwareKey(
|
||||
val hardwareKeyBytes = MasterCredential.retrieveHardwareKey(
|
||||
challengeResponseRetriever.invoke(hardwareKey, transformSeed)
|
||||
)
|
||||
this.masterKey = composedKeyToMasterKey(
|
||||
|
@ -873,10 +870,10 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
|||
}
|
||||
}
|
||||
|
||||
override fun validatePasswordEncoding(password: String?, containsKeyFile: Boolean): Boolean {
|
||||
override fun isValidCredential(password: String?, containsKeyFile: Boolean): Boolean {
|
||||
if (password == null)
|
||||
return true
|
||||
return super.validatePasswordEncoding(password, containsKeyFile)
|
||||
return super.isValidCredential(password, containsKeyFile)
|
||||
}
|
||||
|
||||
override fun clearIndexes() {
|
||||
|
|
|
@ -93,7 +93,7 @@ abstract class DatabaseVersioned<
|
|||
return null
|
||||
}
|
||||
|
||||
open fun validatePasswordEncoding(password: String?, containsKeyFile: Boolean): Boolean {
|
||||
open fun isValidCredential(password: String?, containsKeyFile: Boolean): Boolean {
|
||||
if (password == null && !containsKeyFile)
|
||||
return false
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue