mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Refactor exceptions
This commit is contained in:
@@ -22,10 +22,8 @@ package com.kunzisoft.keepass.database.action
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidKeyFileException
|
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
import com.kunzisoft.keepass.utils.UriUtil
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
open class AssignPasswordInDatabaseRunnable @JvmOverloads constructor(
|
open class AssignPasswordInDatabaseRunnable @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -63,10 +61,7 @@ open class AssignPasswordInDatabaseRunnable @JvmOverloads constructor(
|
|||||||
// To save the database
|
// To save the database
|
||||||
super.run()
|
super.run()
|
||||||
finishRun(true)
|
finishRun(true)
|
||||||
} catch (e: LoadDatabaseInvalidKeyFileException) {
|
} catch (e: Exception) {
|
||||||
erase(mBackupKey)
|
|
||||||
finishRun(false, e.message)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
erase(mBackupKey)
|
erase(mBackupKey)
|
||||||
finishRun(false, e.message)
|
finishRun(false, e.message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -294,88 +294,73 @@ class Database {
|
|||||||
fixDuplicateUUID: Boolean,
|
fixDuplicateUUID: Boolean,
|
||||||
progressTaskUpdater: ProgressTaskUpdater?) {
|
progressTaskUpdater: ProgressTaskUpdater?) {
|
||||||
|
|
||||||
|
mUri = uri
|
||||||
|
isReadOnly = false
|
||||||
|
if (uri.scheme == "file") {
|
||||||
|
val file = File(uri.path!!)
|
||||||
|
isReadOnly = !file.canWrite()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass Uris as InputStreams
|
||||||
|
val inputStream: InputStream?
|
||||||
try {
|
try {
|
||||||
|
inputStream = UriUtil.getUriInputStream(contentResolver, uri)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("KPD", "Database::loadData", e)
|
||||||
|
throw LoadDatabaseFileNotFoundException()
|
||||||
|
}
|
||||||
|
|
||||||
mUri = uri
|
// Pass KeyFile Uri as InputStreams
|
||||||
isReadOnly = false
|
var keyFileInputStream: InputStream? = null
|
||||||
if (uri.scheme == "file") {
|
keyfile?.let {
|
||||||
val file = File(uri.path!!)
|
|
||||||
isReadOnly = !file.canWrite()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass Uris as InputStreams
|
|
||||||
val inputStream: InputStream?
|
|
||||||
try {
|
try {
|
||||||
inputStream = UriUtil.getUriInputStream(contentResolver, uri)
|
keyFileInputStream = UriUtil.getUriInputStream(contentResolver, keyfile)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("KPD", "Database::loadData", e)
|
Log.e("KPD", "Database::loadData", e)
|
||||||
throw LoadDatabaseFileNotFoundException()
|
throw LoadDatabaseFileNotFoundException()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass KeyFile Uri as InputStreams
|
|
||||||
var keyFileInputStream: InputStream? = null
|
|
||||||
keyfile?.let {
|
|
||||||
try {
|
|
||||||
keyFileInputStream = UriUtil.getUriInputStream(contentResolver, keyfile)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("KPD", "Database::loadData", e)
|
|
||||||
throw LoadDatabaseFileNotFoundException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load Data
|
|
||||||
|
|
||||||
val bufferedInputStream = BufferedInputStream(inputStream)
|
|
||||||
if (!bufferedInputStream.markSupported()) {
|
|
||||||
throw IOException("Input stream does not support mark.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll end up reading 8 bytes to identify the header. Might as well use two extra.
|
|
||||||
bufferedInputStream.mark(10)
|
|
||||||
|
|
||||||
// Get the file directory to save the attachments
|
|
||||||
val sig1 = LEDataInputStream.readInt(bufferedInputStream)
|
|
||||||
val sig2 = LEDataInputStream.readInt(bufferedInputStream)
|
|
||||||
|
|
||||||
// Return to the start
|
|
||||||
bufferedInputStream.reset()
|
|
||||||
|
|
||||||
when {
|
|
||||||
// Header of database V3
|
|
||||||
PwDbHeaderV3.matchesHeader(sig1, sig2) -> setDatabaseV3(ImporterV3()
|
|
||||||
.openDatabase(bufferedInputStream,
|
|
||||||
password,
|
|
||||||
keyFileInputStream,
|
|
||||||
progressTaskUpdater))
|
|
||||||
|
|
||||||
// Header of database V4
|
|
||||||
PwDbHeaderV4.matchesHeader(sig1, sig2) -> setDatabaseV4(ImporterV4(
|
|
||||||
cacheDirectory,
|
|
||||||
fixDuplicateUUID)
|
|
||||||
.openDatabase(bufferedInputStream,
|
|
||||||
password,
|
|
||||||
keyFileInputStream,
|
|
||||||
progressTaskUpdater))
|
|
||||||
|
|
||||||
// Header not recognized
|
|
||||||
else -> throw LoadDatabaseSignatureException()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mSearchHelper = searchHelper
|
|
||||||
loaded = true
|
|
||||||
|
|
||||||
} catch (e: LoadDatabaseException) {
|
|
||||||
throw e
|
|
||||||
} catch (e: IOException) {
|
|
||||||
if (e.message?.contains("Hash failed with code") == true)
|
|
||||||
throw LoadDatabaseKDFMemoryException(e)
|
|
||||||
else
|
|
||||||
throw LoadDatabaseIOException(e)
|
|
||||||
} catch (e: OutOfMemoryError) {
|
|
||||||
throw LoadDatabaseNoMemoryException(e)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw LoadDatabaseException(e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load Data
|
||||||
|
|
||||||
|
val bufferedInputStream = BufferedInputStream(inputStream)
|
||||||
|
if (!bufferedInputStream.markSupported()) {
|
||||||
|
throw IOException("Input stream does not support mark.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll end up reading 8 bytes to identify the header. Might as well use two extra.
|
||||||
|
bufferedInputStream.mark(10)
|
||||||
|
|
||||||
|
// Get the file directory to save the attachments
|
||||||
|
val sig1 = LEDataInputStream.readInt(bufferedInputStream)
|
||||||
|
val sig2 = LEDataInputStream.readInt(bufferedInputStream)
|
||||||
|
|
||||||
|
// Return to the start
|
||||||
|
bufferedInputStream.reset()
|
||||||
|
|
||||||
|
when {
|
||||||
|
// Header of database V3
|
||||||
|
PwDbHeaderV3.matchesHeader(sig1, sig2) -> setDatabaseV3(ImporterV3()
|
||||||
|
.openDatabase(bufferedInputStream,
|
||||||
|
password,
|
||||||
|
keyFileInputStream,
|
||||||
|
progressTaskUpdater))
|
||||||
|
|
||||||
|
// Header of database V4
|
||||||
|
PwDbHeaderV4.matchesHeader(sig1, sig2) -> setDatabaseV4(ImporterV4(
|
||||||
|
cacheDirectory,
|
||||||
|
fixDuplicateUUID)
|
||||||
|
.openDatabase(bufferedInputStream,
|
||||||
|
password,
|
||||||
|
keyFileInputStream,
|
||||||
|
progressTaskUpdater))
|
||||||
|
|
||||||
|
// Header not recognized
|
||||||
|
else -> throw LoadDatabaseSignatureException()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mSearchHelper = searchHelper
|
||||||
|
loaded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isGroupSearchable(group: GroupVersioned, isOmitBackup: Boolean): Boolean {
|
fun isGroupSearchable(group: GroupVersioned, isOmitBackup: Boolean): Boolean {
|
||||||
@@ -519,7 +504,7 @@ class Database {
|
|||||||
?: false
|
?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
@Throws(IOException::class)
|
||||||
fun retrieveMasterKey(key: String?, keyInputStream: InputStream?) {
|
fun retrieveMasterKey(key: String?, keyInputStream: InputStream?) {
|
||||||
pwDatabaseV3?.retrieveMasterKey(key, keyInputStream)
|
pwDatabaseV3?.retrieveMasterKey(key, keyInputStream)
|
||||||
pwDatabaseV4?.retrieveMasterKey(key, keyInputStream)
|
pwDatabaseV4?.retrieveMasterKey(key, keyInputStream)
|
||||||
|
|||||||
@@ -21,19 +21,12 @@ package com.kunzisoft.keepass.database.element
|
|||||||
|
|
||||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseDuplicateUuidException
|
import com.kunzisoft.keepass.database.exception.LoadDatabaseDuplicateUuidException
|
||||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidKeyFileException
|
|
||||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseKeyFileEmptyException
|
import com.kunzisoft.keepass.database.exception.LoadDatabaseKeyFileEmptyException
|
||||||
import com.kunzisoft.keepass.utils.MemoryUtil
|
import com.kunzisoft.keepass.utils.MemoryUtil
|
||||||
|
import java.io.*
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.io.UnsupportedEncodingException
|
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.NoSuchAlgorithmException
|
import java.security.NoSuchAlgorithmException
|
||||||
import java.util.LinkedHashMap
|
import java.util.*
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
abstract class PwDatabase<
|
abstract class PwDatabase<
|
||||||
GroupId,
|
GroupId,
|
||||||
@@ -78,15 +71,15 @@ abstract class PwDatabase<
|
|||||||
|
|
||||||
var rootGroup: Group? = null
|
var rootGroup: Group? = null
|
||||||
|
|
||||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
@Throws(IOException::class)
|
||||||
protected abstract fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray
|
protected abstract fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray
|
||||||
|
|
||||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
@Throws(IOException::class)
|
||||||
fun retrieveMasterKey(key: String?, keyInputStream: InputStream?) {
|
fun retrieveMasterKey(key: String?, keyInputStream: InputStream?) {
|
||||||
masterKey = getMasterKey(key, keyInputStream)
|
masterKey = getMasterKey(key, keyInputStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
@Throws(IOException::class)
|
||||||
protected fun getCompositeKey(key: String, keyInputStream: InputStream): ByteArray {
|
protected fun getCompositeKey(key: String, keyInputStream: InputStream): ByteArray {
|
||||||
val fileKey = getFileKey(keyInputStream)
|
val fileKey = getFileKey(keyInputStream)
|
||||||
val passwordKey = getPasswordKey(key)
|
val passwordKey = getPasswordKey(key)
|
||||||
@@ -126,7 +119,7 @@ abstract class PwDatabase<
|
|||||||
return messageDigest.digest()
|
return messageDigest.digest()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
@Throws(IOException::class)
|
||||||
protected fun getFileKey(keyInputStream: InputStream): ByteArray {
|
protected fun getFileKey(keyInputStream: InputStream): ByteArray {
|
||||||
|
|
||||||
val keyByteArrayOutputStream = ByteArrayOutputStream()
|
val keyByteArrayOutputStream = ByteArrayOutputStream()
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ package com.kunzisoft.keepass.database.element
|
|||||||
import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory
|
import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory
|
||||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
|
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
|
||||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidKeyFileException
|
|
||||||
import com.kunzisoft.keepass.stream.NullOutputStream
|
import com.kunzisoft.keepass.stream.NullOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@@ -112,7 +111,7 @@ class PwDatabaseV3 : PwDatabase<Int, PwGroupV3, PwEntryV3>() {
|
|||||||
return newId
|
return newId
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {
|
override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {
|
||||||
|
|
||||||
return if (key != null && keyInputStream != null) {
|
return if (key != null && keyInputStream != null) {
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import com.kunzisoft.keepass.crypto.CryptoUtil
|
|||||||
import com.kunzisoft.keepass.crypto.engine.AesEngine
|
import com.kunzisoft.keepass.crypto.engine.AesEngine
|
||||||
import com.kunzisoft.keepass.crypto.engine.CipherEngine
|
import com.kunzisoft.keepass.crypto.engine.CipherEngine
|
||||||
import com.kunzisoft.keepass.crypto.keyDerivation.*
|
import com.kunzisoft.keepass.crypto.keyDerivation.*
|
||||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidKeyFileException
|
|
||||||
import com.kunzisoft.keepass.database.exception.UnknownKDF
|
import com.kunzisoft.keepass.database.exception.UnknownKDF
|
||||||
import com.kunzisoft.keepass.utils.VariantDictionary
|
import com.kunzisoft.keepass.utils.VariantDictionary
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
@@ -238,7 +237,7 @@ class PwDatabaseV4 : PwDatabase<UUID, PwGroupV4, PwEntryV4> {
|
|||||||
return getCustomData().isNotEmpty()
|
return getCustomData().isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
|
@Throws(IOException::class)
|
||||||
public override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {
|
public override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {
|
||||||
|
|
||||||
var masterKey = byteArrayOf()
|
var masterKey = byteArrayOf()
|
||||||
|
|||||||
@@ -5,44 +5,60 @@ import androidx.annotation.StringRes
|
|||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.database.element.PwNodeId
|
import com.kunzisoft.keepass.database.element.PwNodeId
|
||||||
import com.kunzisoft.keepass.database.element.Type
|
import com.kunzisoft.keepass.database.element.Type
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
|
|
||||||
class LoadDatabaseArcFourException :
|
class LoadDatabaseArcFourException : LoadDatabaseException {
|
||||||
LoadDatabaseException(R.string.error_arc4)
|
constructor() : super(R.string.error_arc4)
|
||||||
|
constructor(exception: Throwable) : super(exception, R.string.error_arc4)
|
||||||
|
}
|
||||||
|
|
||||||
class LoadDatabaseFileNotFoundException :
|
class LoadDatabaseFileNotFoundException : LoadDatabaseException {
|
||||||
LoadDatabaseException(R.string.file_not_found_content)
|
constructor() : super(R.string.file_not_found_content)
|
||||||
|
constructor(exception: Throwable) : super(exception, R.string.file_not_found_content)
|
||||||
|
}
|
||||||
|
|
||||||
class LoadDatabaseInvalidAlgorithmException :
|
class LoadDatabaseInvalidAlgorithmException : LoadDatabaseException {
|
||||||
LoadDatabaseException(R.string.invalid_algorithm)
|
constructor() : super(R.string.invalid_algorithm)
|
||||||
|
constructor(exception: Throwable) : super(exception, R.string.invalid_algorithm)
|
||||||
|
}
|
||||||
|
|
||||||
class LoadDatabaseDuplicateUuidException(type: Type, uuid: PwNodeId<*>):
|
class LoadDatabaseDuplicateUuidException(type: Type, uuid: PwNodeId<*>):
|
||||||
LoadDatabaseException(R.string.invalid_db_same_uuid, type.name, uuid.toString())
|
LoadDatabaseException(R.string.invalid_db_same_uuid, type.name, uuid.toString())
|
||||||
|
|
||||||
class LoadDatabaseIOException(exception: IOException) :
|
class LoadDatabaseIOException : LoadDatabaseException {
|
||||||
LoadDatabaseException(exception, R.string.error_load_database)
|
constructor() : super(R.string.error_load_database)
|
||||||
|
constructor(exception: Throwable) : super(exception, R.string.error_load_database)
|
||||||
|
}
|
||||||
|
|
||||||
class LoadDatabaseKDFMemoryException(exception: IOException) :
|
class LoadDatabaseKDFMemoryException : LoadDatabaseException {
|
||||||
LoadDatabaseException(exception, R.string.error_load_database_KDF_memory)
|
constructor() : super(R.string.error_load_database_KDF_memory)
|
||||||
|
constructor(exception: Throwable) : super(exception, R.string.error_load_database_KDF_memory)
|
||||||
|
}
|
||||||
|
|
||||||
class LoadDatabaseSignatureException :
|
class LoadDatabaseSignatureException : LoadDatabaseException {
|
||||||
LoadDatabaseException(R.string.invalid_db_sig)
|
constructor() : super(R.string.invalid_db_sig)
|
||||||
|
constructor(exception: Throwable) : super(exception, R.string.invalid_db_sig)
|
||||||
|
}
|
||||||
|
|
||||||
class LoadDatabaseVersionException :
|
class LoadDatabaseVersionException : LoadDatabaseException {
|
||||||
LoadDatabaseException(R.string.unsupported_db_version)
|
constructor() : super(R.string.unsupported_db_version)
|
||||||
|
constructor(exception: Throwable) : super(exception, R.string.unsupported_db_version)
|
||||||
|
}
|
||||||
|
|
||||||
open class LoadDatabaseInvalidKeyFileException :
|
class LoadDatabaseInvalidCredentialsException : LoadDatabaseException {
|
||||||
LoadDatabaseException(R.string.keyfile_does_not_exist)
|
constructor() : super(R.string.invalid_credentials)
|
||||||
|
constructor(exception: Throwable) : super(exception, R.string.invalid_credentials)
|
||||||
|
}
|
||||||
|
|
||||||
class LoadDatabaseInvalidPasswordException :
|
class LoadDatabaseKeyFileEmptyException : LoadDatabaseException {
|
||||||
LoadDatabaseException(R.string.invalid_password)
|
constructor() : super(R.string.keyfile_is_empty)
|
||||||
|
constructor(exception: Throwable) : super(exception, R.string.keyfile_is_empty)
|
||||||
|
}
|
||||||
|
|
||||||
class LoadDatabaseKeyFileEmptyException :
|
class LoadDatabaseNoMemoryException: LoadDatabaseException {
|
||||||
LoadDatabaseException(R.string.keyfile_is_empty)
|
constructor() : super(R.string.error_out_of_memory)
|
||||||
|
constructor(exception: Throwable) : super(exception, R.string.error_out_of_memory)
|
||||||
class LoadDatabaseNoMemoryException(exception: OutOfMemoryError) :
|
}
|
||||||
LoadDatabaseException(exception, R.string.error_out_of_memory)
|
|
||||||
|
|
||||||
open class LoadDatabaseException : Exception {
|
open class LoadDatabaseException : Exception {
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ package com.kunzisoft.keepass.database.file.load
|
|||||||
import com.kunzisoft.keepass.database.element.PwDatabase
|
import com.kunzisoft.keepass.database.element.PwDatabase
|
||||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
|
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
|
||||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||||
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
abstract class Importer<PwDb : PwDatabase<*, *, *>> {
|
abstract class Importer<PwDb : PwDatabase<*, *, *>> {
|
||||||
@@ -35,10 +33,9 @@ abstract class Importer<PwDb : PwDatabase<*, *, *>> {
|
|||||||
* @param password Pass phrase for infile.
|
* @param password Pass phrase for infile.
|
||||||
* @return new PwDatabase container.
|
* @return new PwDatabase container.
|
||||||
*
|
*
|
||||||
* @throws IOException on any file error.
|
* @throws LoadDatabaseException on database error (contains IO exceptions)
|
||||||
* @throws LoadDatabaseException on database error.
|
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class, LoadDatabaseException::class)
|
@Throws(LoadDatabaseException::class)
|
||||||
abstract fun openDatabase(databaseInputStream: InputStream,
|
abstract fun openDatabase(databaseInputStream: InputStream,
|
||||||
password: String?,
|
password: String?,
|
||||||
keyInputStream: InputStream?,
|
keyInputStream: InputStream?,
|
||||||
|
|||||||
@@ -73,155 +73,163 @@ class ImporterV3 : Importer<PwDatabaseV3>() {
|
|||||||
|
|
||||||
private lateinit var mDatabaseToOpen: PwDatabaseV3
|
private lateinit var mDatabaseToOpen: PwDatabaseV3
|
||||||
|
|
||||||
@Throws(IOException::class, LoadDatabaseException::class)
|
@Throws(LoadDatabaseException::class)
|
||||||
override fun openDatabase(databaseInputStream: InputStream,
|
override fun openDatabase(databaseInputStream: InputStream,
|
||||||
password: String?,
|
password: String?,
|
||||||
keyInputStream: InputStream?,
|
keyInputStream: InputStream?,
|
||||||
progressTaskUpdater: ProgressTaskUpdater?): PwDatabaseV3 {
|
progressTaskUpdater: ProgressTaskUpdater?): PwDatabaseV3 {
|
||||||
|
|
||||||
// Load entire file, most of it's encrypted.
|
|
||||||
val fileSize = databaseInputStream.available()
|
|
||||||
val filebuf = ByteArray(fileSize + 16) // Pad with a blocksize (Twofish uses 128 bits), since Android 4.3 tries to write more to the buffer
|
|
||||||
databaseInputStream.read(filebuf, 0, fileSize) // TODO remove
|
|
||||||
databaseInputStream.close()
|
|
||||||
|
|
||||||
// Parse header (unencrypted)
|
|
||||||
if (fileSize < PwDbHeaderV3.BUF_SIZE)
|
|
||||||
throw IOException("File too short for header")
|
|
||||||
val hdr = PwDbHeaderV3()
|
|
||||||
hdr.loadFromFile(filebuf, 0)
|
|
||||||
|
|
||||||
if (hdr.signature1 != PwDbHeader.PWM_DBSIG_1 || hdr.signature2 != PwDbHeaderV3.DBSIG_2) {
|
|
||||||
throw LoadDatabaseSignatureException()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hdr.matchesVersion()) {
|
|
||||||
throw LoadDatabaseVersionException()
|
|
||||||
}
|
|
||||||
|
|
||||||
progressTaskUpdater?.updateMessage(R.string.retrieving_db_key)
|
|
||||||
mDatabaseToOpen = PwDatabaseV3()
|
|
||||||
mDatabaseToOpen.retrieveMasterKey(password, keyInputStream)
|
|
||||||
|
|
||||||
// Select algorithm
|
|
||||||
if (hdr.flags and PwDbHeaderV3.FLAG_RIJNDAEL != 0) {
|
|
||||||
mDatabaseToOpen.encryptionAlgorithm = PwEncryptionAlgorithm.AESRijndael
|
|
||||||
} else if (hdr.flags and PwDbHeaderV3.FLAG_TWOFISH != 0) {
|
|
||||||
mDatabaseToOpen.encryptionAlgorithm = PwEncryptionAlgorithm.Twofish
|
|
||||||
} else {
|
|
||||||
throw LoadDatabaseInvalidAlgorithmException()
|
|
||||||
}
|
|
||||||
|
|
||||||
mDatabaseToOpen.numberKeyEncryptionRounds = hdr.numKeyEncRounds.toLong()
|
|
||||||
|
|
||||||
// Generate transformedMasterKey from masterKey
|
|
||||||
mDatabaseToOpen.makeFinalKey(hdr.masterSeed, hdr.transformSeed, mDatabaseToOpen.numberKeyEncryptionRounds)
|
|
||||||
|
|
||||||
progressTaskUpdater?.updateMessage(R.string.decrypting_db)
|
|
||||||
// Initialize Rijndael algorithm
|
|
||||||
val cipher: Cipher
|
|
||||||
try {
|
try {
|
||||||
if (mDatabaseToOpen.encryptionAlgorithm === PwEncryptionAlgorithm.AESRijndael) {
|
// Load entire file, most of it's encrypted.
|
||||||
cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding")
|
val fileSize = databaseInputStream.available()
|
||||||
} else if (mDatabaseToOpen.encryptionAlgorithm === PwEncryptionAlgorithm.Twofish) {
|
val filebuf = ByteArray(fileSize + 16) // Pad with a blocksize (Twofish uses 128 bits), since Android 4.3 tries to write more to the buffer
|
||||||
cipher = CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING")
|
databaseInputStream.read(filebuf, 0, fileSize) // TODO remove
|
||||||
} else {
|
databaseInputStream.close()
|
||||||
throw IOException("Encryption algorithm is not supported")
|
|
||||||
|
// Parse header (unencrypted)
|
||||||
|
if (fileSize < PwDbHeaderV3.BUF_SIZE)
|
||||||
|
throw IOException("File too short for header")
|
||||||
|
val hdr = PwDbHeaderV3()
|
||||||
|
hdr.loadFromFile(filebuf, 0)
|
||||||
|
|
||||||
|
if (hdr.signature1 != PwDbHeader.PWM_DBSIG_1 || hdr.signature2 != PwDbHeaderV3.DBSIG_2) {
|
||||||
|
throw LoadDatabaseSignatureException()
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e1: NoSuchAlgorithmException) {
|
if (!hdr.matchesVersion()) {
|
||||||
throw IOException("No such algorithm")
|
throw LoadDatabaseVersionException()
|
||||||
} catch (e1: NoSuchPaddingException) {
|
}
|
||||||
throw IOException("No such pdading")
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
progressTaskUpdater?.updateMessage(R.string.retrieving_db_key)
|
||||||
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(mDatabaseToOpen.finalKey, "AES"), IvParameterSpec(hdr.encryptionIV))
|
mDatabaseToOpen = PwDatabaseV3()
|
||||||
} catch (e1: InvalidKeyException) {
|
mDatabaseToOpen.retrieveMasterKey(password, keyInputStream)
|
||||||
throw IOException("Invalid key")
|
|
||||||
} catch (e1: InvalidAlgorithmParameterException) {
|
|
||||||
throw IOException("Invalid algorithm parameter.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt! The first bytes aren't encrypted (that's the header)
|
// Select algorithm
|
||||||
val encryptedPartSize: Int
|
when {
|
||||||
try {
|
hdr.flags and PwDbHeaderV3.FLAG_RIJNDAEL != 0 -> mDatabaseToOpen.encryptionAlgorithm = PwEncryptionAlgorithm.AESRijndael
|
||||||
encryptedPartSize = cipher.doFinal(filebuf, PwDbHeaderV3.BUF_SIZE, fileSize - PwDbHeaderV3.BUF_SIZE, filebuf, PwDbHeaderV3.BUF_SIZE)
|
hdr.flags and PwDbHeaderV3.FLAG_TWOFISH != 0 -> mDatabaseToOpen.encryptionAlgorithm = PwEncryptionAlgorithm.Twofish
|
||||||
} catch (e1: ShortBufferException) {
|
else -> throw LoadDatabaseInvalidAlgorithmException()
|
||||||
throw IOException("Buffer too short")
|
}
|
||||||
} catch (e1: IllegalBlockSizeException) {
|
|
||||||
throw IOException("Invalid block size")
|
|
||||||
} catch (e1: BadPaddingException) {
|
|
||||||
throw LoadDatabaseInvalidPasswordException()
|
|
||||||
}
|
|
||||||
|
|
||||||
val md: MessageDigest
|
mDatabaseToOpen.numberKeyEncryptionRounds = hdr.numKeyEncRounds.toLong()
|
||||||
try {
|
|
||||||
md = MessageDigest.getInstance("SHA-256")
|
|
||||||
} catch (e: NoSuchAlgorithmException) {
|
|
||||||
throw IOException("No SHA-256 algorithm")
|
|
||||||
}
|
|
||||||
|
|
||||||
val nos = NullOutputStream()
|
// Generate transformedMasterKey from masterKey
|
||||||
val dos = DigestOutputStream(nos, md)
|
mDatabaseToOpen.makeFinalKey(hdr.masterSeed, hdr.transformSeed, mDatabaseToOpen.numberKeyEncryptionRounds)
|
||||||
dos.write(filebuf, PwDbHeaderV3.BUF_SIZE, encryptedPartSize)
|
|
||||||
dos.close()
|
|
||||||
val hash = md.digest()
|
|
||||||
|
|
||||||
if (!Arrays.equals(hash, hdr.contentsHash)) {
|
progressTaskUpdater?.updateMessage(R.string.decrypting_db)
|
||||||
|
// Initialize Rijndael algorithm
|
||||||
|
val cipher: Cipher
|
||||||
|
try {
|
||||||
|
if (mDatabaseToOpen.encryptionAlgorithm === PwEncryptionAlgorithm.AESRijndael) {
|
||||||
|
cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding")
|
||||||
|
} else if (mDatabaseToOpen.encryptionAlgorithm === PwEncryptionAlgorithm.Twofish) {
|
||||||
|
cipher = CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING")
|
||||||
|
} else {
|
||||||
|
throw IOException("Encryption algorithm is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
Log.w(TAG, "Database file did not decrypt correctly. (checksum code is broken)")
|
} catch (e1: NoSuchAlgorithmException) {
|
||||||
throw LoadDatabaseInvalidPasswordException()
|
throw IOException("No such algorithm")
|
||||||
}
|
} catch (e1: NoSuchPaddingException) {
|
||||||
|
throw IOException("No such pdading")
|
||||||
|
}
|
||||||
|
|
||||||
// New manual root because V3 contains multiple root groups (here available with getRootGroups())
|
try {
|
||||||
val newRoot = mDatabaseToOpen.createGroup()
|
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(mDatabaseToOpen.finalKey, "AES"), IvParameterSpec(hdr.encryptionIV))
|
||||||
newRoot.level = -1
|
} catch (e1: InvalidKeyException) {
|
||||||
mDatabaseToOpen.rootGroup = newRoot
|
throw IOException("Invalid key")
|
||||||
|
} catch (e1: InvalidAlgorithmParameterException) {
|
||||||
|
throw IOException("Invalid algorithm parameter.")
|
||||||
|
}
|
||||||
|
|
||||||
// Import all groups
|
// Decrypt! The first bytes aren't encrypted (that's the header)
|
||||||
var pos = PwDbHeaderV3.BUF_SIZE
|
val encryptedPartSize: Int
|
||||||
var newGrp = mDatabaseToOpen.createGroup()
|
try {
|
||||||
run {
|
encryptedPartSize = cipher.doFinal(filebuf, PwDbHeaderV3.BUF_SIZE, fileSize - PwDbHeaderV3.BUF_SIZE, filebuf, PwDbHeaderV3.BUF_SIZE)
|
||||||
|
} catch (e1: ShortBufferException) {
|
||||||
|
throw IOException("Buffer too short")
|
||||||
|
} catch (e1: IllegalBlockSizeException) {
|
||||||
|
throw IOException("Invalid block size")
|
||||||
|
} catch (e1: BadPaddingException) {
|
||||||
|
throw LoadDatabaseInvalidCredentialsException()
|
||||||
|
}
|
||||||
|
|
||||||
|
val md: MessageDigest
|
||||||
|
try {
|
||||||
|
md = MessageDigest.getInstance("SHA-256")
|
||||||
|
} catch (e: NoSuchAlgorithmException) {
|
||||||
|
throw IOException("No SHA-256 algorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
val nos = NullOutputStream()
|
||||||
|
val dos = DigestOutputStream(nos, md)
|
||||||
|
dos.write(filebuf, PwDbHeaderV3.BUF_SIZE, encryptedPartSize)
|
||||||
|
dos.close()
|
||||||
|
val hash = md.digest()
|
||||||
|
|
||||||
|
if (!Arrays.equals(hash, hdr.contentsHash)) {
|
||||||
|
|
||||||
|
Log.w(TAG, "Database file did not decrypt correctly. (checksum code is broken)")
|
||||||
|
throw LoadDatabaseInvalidCredentialsException()
|
||||||
|
}
|
||||||
|
|
||||||
|
// New manual root because V3 contains multiple root groups (here available with getRootGroups())
|
||||||
|
val newRoot = mDatabaseToOpen.createGroup()
|
||||||
|
newRoot.level = -1
|
||||||
|
mDatabaseToOpen.rootGroup = newRoot
|
||||||
|
|
||||||
|
// Import all groups
|
||||||
|
var pos = PwDbHeaderV3.BUF_SIZE
|
||||||
|
var newGrp = mDatabaseToOpen.createGroup()
|
||||||
|
run {
|
||||||
|
var i = 0
|
||||||
|
while (i < hdr.numGroups) {
|
||||||
|
val fieldType = LEDataInputStream.readUShort(filebuf, pos)
|
||||||
|
pos += 2
|
||||||
|
val fieldSize = LEDataInputStream.readInt(filebuf, pos)
|
||||||
|
pos += 4
|
||||||
|
|
||||||
|
if (fieldType == 0xFFFF) {
|
||||||
|
// End-Group record. Save group and count it.
|
||||||
|
mDatabaseToOpen.addGroupIndex(newGrp)
|
||||||
|
newGrp = mDatabaseToOpen.createGroup()
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
readGroupField(mDatabaseToOpen, newGrp, fieldType, filebuf, pos)
|
||||||
|
}
|
||||||
|
pos += fieldSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import all entries
|
||||||
|
var newEnt = mDatabaseToOpen.createEntry()
|
||||||
var i = 0
|
var i = 0
|
||||||
while (i < hdr.numGroups) {
|
while (i < hdr.numEntries) {
|
||||||
val fieldType = LEDataInputStream.readUShort(filebuf, pos)
|
val fieldType = LEDataInputStream.readUShort(filebuf, pos)
|
||||||
pos += 2
|
val fieldSize = LEDataInputStream.readInt(filebuf, pos + 2)
|
||||||
val fieldSize = LEDataInputStream.readInt(filebuf, pos)
|
|
||||||
pos += 4
|
|
||||||
|
|
||||||
if (fieldType == 0xFFFF) {
|
if (fieldType == 0xFFFF) {
|
||||||
// End-Group record. Save group and count it.
|
// End-Group record. Save group and count it.
|
||||||
mDatabaseToOpen.addGroupIndex(newGrp)
|
mDatabaseToOpen.addEntryIndex(newEnt)
|
||||||
newGrp = mDatabaseToOpen.createGroup()
|
newEnt = mDatabaseToOpen.createEntry()
|
||||||
i++
|
i++
|
||||||
} else {
|
} else {
|
||||||
readGroupField(mDatabaseToOpen, newGrp, fieldType, filebuf, pos)
|
readEntryField(mDatabaseToOpen, newEnt, filebuf, pos)
|
||||||
}
|
}
|
||||||
pos += fieldSize
|
pos += 2 + 4 + fieldSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructTreeFromIndex()
|
||||||
|
} catch (e: LoadDatabaseException) {
|
||||||
|
throw e
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw LoadDatabaseIOException(e)
|
||||||
|
} catch (e: OutOfMemoryError) {
|
||||||
|
throw LoadDatabaseNoMemoryException(e)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw LoadDatabaseException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import all entries
|
|
||||||
var newEnt = mDatabaseToOpen.createEntry()
|
|
||||||
var i = 0
|
|
||||||
while (i < hdr.numEntries) {
|
|
||||||
val fieldType = LEDataInputStream.readUShort(filebuf, pos)
|
|
||||||
val fieldSize = LEDataInputStream.readInt(filebuf, pos + 2)
|
|
||||||
|
|
||||||
if (fieldType == 0xFFFF) {
|
|
||||||
// End-Group record. Save group and count it.
|
|
||||||
mDatabaseToOpen.addEntryIndex(newEnt)
|
|
||||||
newEnt = mDatabaseToOpen.createEntry()
|
|
||||||
i++
|
|
||||||
} else {
|
|
||||||
readEntryField(mDatabaseToOpen, newEnt, filebuf, pos)
|
|
||||||
}
|
|
||||||
pos += 2 + 4 + fieldSize
|
|
||||||
}
|
|
||||||
|
|
||||||
constructTreeFromIndex()
|
|
||||||
|
|
||||||
return mDatabaseToOpen
|
return mDatabaseToOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,20 +24,17 @@ import com.kunzisoft.keepass.R
|
|||||||
import com.kunzisoft.keepass.crypto.CipherFactory
|
import com.kunzisoft.keepass.crypto.CipherFactory
|
||||||
import com.kunzisoft.keepass.crypto.StreamCipherFactory
|
import com.kunzisoft.keepass.crypto.StreamCipherFactory
|
||||||
import com.kunzisoft.keepass.crypto.engine.CipherEngine
|
import com.kunzisoft.keepass.crypto.engine.CipherEngine
|
||||||
import com.kunzisoft.keepass.database.element.PwCompressionAlgorithm
|
|
||||||
import com.kunzisoft.keepass.database.element.*
|
import com.kunzisoft.keepass.database.element.*
|
||||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseArcFourException
|
|
||||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
|
|
||||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidPasswordException
|
|
||||||
import com.kunzisoft.keepass.database.file.PwDbHeaderV4
|
|
||||||
import com.kunzisoft.keepass.database.element.security.ProtectedBinary
|
import com.kunzisoft.keepass.database.element.security.ProtectedBinary
|
||||||
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
||||||
|
import com.kunzisoft.keepass.database.exception.*
|
||||||
|
import com.kunzisoft.keepass.database.file.KDBX4DateUtil
|
||||||
|
import com.kunzisoft.keepass.database.file.PwDbHeaderV4
|
||||||
import com.kunzisoft.keepass.stream.BetterCipherInputStream
|
import com.kunzisoft.keepass.stream.BetterCipherInputStream
|
||||||
import com.kunzisoft.keepass.stream.HashedBlockInputStream
|
import com.kunzisoft.keepass.stream.HashedBlockInputStream
|
||||||
import com.kunzisoft.keepass.stream.HmacBlockInputStream
|
import com.kunzisoft.keepass.stream.HmacBlockInputStream
|
||||||
import com.kunzisoft.keepass.stream.LEDataInputStream
|
import com.kunzisoft.keepass.stream.LEDataInputStream
|
||||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||||
import com.kunzisoft.keepass.database.file.KDBX4DateUtil
|
|
||||||
import com.kunzisoft.keepass.utils.MemoryUtil
|
import com.kunzisoft.keepass.utils.MemoryUtil
|
||||||
import com.kunzisoft.keepass.utils.Types
|
import com.kunzisoft.keepass.utils.Types
|
||||||
import org.spongycastle.crypto.StreamCipher
|
import org.spongycastle.crypto.StreamCipher
|
||||||
@@ -46,14 +43,10 @@ import org.xmlpull.v1.XmlPullParserException
|
|||||||
import org.xmlpull.v1.XmlPullParserFactory
|
import org.xmlpull.v1.XmlPullParserFactory
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
import java.security.InvalidAlgorithmParameterException
|
|
||||||
import java.security.InvalidKeyException
|
|
||||||
import java.security.NoSuchAlgorithmException
|
|
||||||
import java.text.ParseException
|
import java.text.ParseException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.zip.GZIPInputStream
|
import java.util.zip.GZIPInputStream
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.NoSuchPaddingException
|
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class ImporterV4(private val streamDir: File,
|
class ImporterV4(private val streamDir: File,
|
||||||
@@ -90,110 +83,120 @@ class ImporterV4(private val streamDir: File,
|
|||||||
private var entryCustomDataKey: String? = null
|
private var entryCustomDataKey: String? = null
|
||||||
private var entryCustomDataValue: String? = null
|
private var entryCustomDataValue: String? = null
|
||||||
|
|
||||||
@Throws(IOException::class, LoadDatabaseException::class)
|
@Throws(LoadDatabaseException::class)
|
||||||
override fun openDatabase(databaseInputStream: InputStream,
|
override fun openDatabase(databaseInputStream: InputStream,
|
||||||
password: String?,
|
password: String?,
|
||||||
keyInputStream: InputStream?,
|
keyInputStream: InputStream?,
|
||||||
progressTaskUpdater: ProgressTaskUpdater?): PwDatabaseV4 {
|
progressTaskUpdater: ProgressTaskUpdater?): PwDatabaseV4 {
|
||||||
|
|
||||||
// TODO performance
|
|
||||||
progressTaskUpdater?.updateMessage(R.string.retrieving_db_key)
|
|
||||||
|
|
||||||
mDatabase = PwDatabaseV4()
|
|
||||||
|
|
||||||
mDatabase.changeDuplicateId = fixDuplicateUUID
|
|
||||||
|
|
||||||
val header = PwDbHeaderV4(mDatabase)
|
|
||||||
|
|
||||||
val headerAndHash = header.loadFromFile(databaseInputStream)
|
|
||||||
version = header.version
|
|
||||||
|
|
||||||
hashOfHeader = headerAndHash.hash
|
|
||||||
val pbHeader = headerAndHash.header
|
|
||||||
|
|
||||||
mDatabase.retrieveMasterKey(password, keyInputStream)
|
|
||||||
mDatabase.makeFinalKey(header.masterSeed)
|
|
||||||
// TODO performance
|
|
||||||
|
|
||||||
progressTaskUpdater?.updateMessage(R.string.decrypting_db)
|
|
||||||
val engine: CipherEngine
|
|
||||||
val cipher: Cipher
|
|
||||||
try {
|
try {
|
||||||
engine = CipherFactory.getInstance(mDatabase.dataCipher)
|
// TODO performance
|
||||||
mDatabase.setDataEngine(engine)
|
progressTaskUpdater?.updateMessage(R.string.retrieving_db_key)
|
||||||
mDatabase.encryptionAlgorithm = engine.getPwEncryptionAlgorithm()
|
|
||||||
cipher = engine.getCipher(Cipher.DECRYPT_MODE, mDatabase.finalKey!!, header.encryptionIV)
|
|
||||||
} catch (e: NoSuchAlgorithmException) {
|
|
||||||
throw IOException("Invalid algorithm.", e)
|
|
||||||
} catch (e: NoSuchPaddingException) {
|
|
||||||
throw IOException("Invalid algorithm.", e)
|
|
||||||
} catch (e: InvalidKeyException) {
|
|
||||||
throw IOException("Invalid algorithm.", e)
|
|
||||||
} catch (e: InvalidAlgorithmParameterException) {
|
|
||||||
throw IOException("Invalid algorithm.", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
val isPlain: InputStream
|
mDatabase = PwDatabaseV4()
|
||||||
if (version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
|
||||||
|
|
||||||
val decrypted = attachCipherStream(databaseInputStream, cipher)
|
mDatabase.changeDuplicateId = fixDuplicateUUID
|
||||||
val dataDecrypted = LEDataInputStream(decrypted)
|
|
||||||
val storedStartBytes: ByteArray?
|
val header = PwDbHeaderV4(mDatabase)
|
||||||
|
|
||||||
|
val headerAndHash = header.loadFromFile(databaseInputStream)
|
||||||
|
version = header.version
|
||||||
|
|
||||||
|
hashOfHeader = headerAndHash.hash
|
||||||
|
val pbHeader = headerAndHash.header
|
||||||
|
|
||||||
|
mDatabase.retrieveMasterKey(password, keyInputStream)
|
||||||
|
mDatabase.makeFinalKey(header.masterSeed)
|
||||||
|
// TODO performance
|
||||||
|
|
||||||
|
progressTaskUpdater?.updateMessage(R.string.decrypting_db)
|
||||||
|
val engine: CipherEngine
|
||||||
|
val cipher: Cipher
|
||||||
try {
|
try {
|
||||||
storedStartBytes = dataDecrypted.readBytes(32)
|
engine = CipherFactory.getInstance(mDatabase.dataCipher)
|
||||||
if (storedStartBytes == null || storedStartBytes.size != 32) {
|
mDatabase.setDataEngine(engine)
|
||||||
throw LoadDatabaseInvalidPasswordException()
|
mDatabase.encryptionAlgorithm = engine.getPwEncryptionAlgorithm()
|
||||||
|
cipher = engine.getCipher(Cipher.DECRYPT_MODE, mDatabase.finalKey!!, header.encryptionIV)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw LoadDatabaseInvalidAlgorithmException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
val isPlain: InputStream
|
||||||
|
if (version < PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||||
|
|
||||||
|
val decrypted = attachCipherStream(databaseInputStream, cipher)
|
||||||
|
val dataDecrypted = LEDataInputStream(decrypted)
|
||||||
|
val storedStartBytes: ByteArray?
|
||||||
|
try {
|
||||||
|
storedStartBytes = dataDecrypted.readBytes(32)
|
||||||
|
if (storedStartBytes == null || storedStartBytes.size != 32) {
|
||||||
|
throw LoadDatabaseInvalidCredentialsException()
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw LoadDatabaseInvalidCredentialsException()
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
|
||||||
throw LoadDatabaseInvalidPasswordException()
|
if (!Arrays.equals(storedStartBytes, header.streamStartBytes)) {
|
||||||
|
throw LoadDatabaseInvalidCredentialsException()
|
||||||
|
}
|
||||||
|
|
||||||
|
isPlain = HashedBlockInputStream(dataDecrypted)
|
||||||
|
} else { // KDBX 4
|
||||||
|
val isData = LEDataInputStream(databaseInputStream)
|
||||||
|
val storedHash = isData.readBytes(32)
|
||||||
|
if (!Arrays.equals(storedHash, hashOfHeader)) {
|
||||||
|
throw LoadDatabaseInvalidCredentialsException()
|
||||||
|
}
|
||||||
|
|
||||||
|
val hmacKey = mDatabase.hmacKey ?: throw LoadDatabaseException()
|
||||||
|
val headerHmac = PwDbHeaderV4.computeHeaderHmac(pbHeader, hmacKey)
|
||||||
|
val storedHmac = isData.readBytes(32)
|
||||||
|
if (storedHmac == null || storedHmac.size != 32) {
|
||||||
|
throw LoadDatabaseInvalidCredentialsException()
|
||||||
|
}
|
||||||
|
// Mac doesn't match
|
||||||
|
if (!Arrays.equals(headerHmac, storedHmac)) {
|
||||||
|
throw LoadDatabaseInvalidCredentialsException()
|
||||||
|
}
|
||||||
|
|
||||||
|
val hmIs = HmacBlockInputStream(isData, true, hmacKey)
|
||||||
|
|
||||||
|
isPlain = attachCipherStream(hmIs, cipher)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Arrays.equals(storedStartBytes, header.streamStartBytes)) {
|
val inputStreamXml: InputStream
|
||||||
throw LoadDatabaseInvalidPasswordException()
|
inputStreamXml = when (mDatabase.compressionAlgorithm) {
|
||||||
|
PwCompressionAlgorithm.GZip -> GZIPInputStream(isPlain)
|
||||||
|
else -> isPlain
|
||||||
}
|
}
|
||||||
|
|
||||||
isPlain = HashedBlockInputStream(dataDecrypted)
|
if (version >= PwDbHeaderV4.FILE_VERSION_32_4) {
|
||||||
} else { // KDBX 4
|
loadInnerHeader(inputStreamXml, header)
|
||||||
val isData = LEDataInputStream(databaseInputStream)
|
|
||||||
val storedHash = isData.readBytes(32)
|
|
||||||
if (!Arrays.equals(storedHash, hashOfHeader)) {
|
|
||||||
throw LoadDatabaseException()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val hmacKey = mDatabase.hmacKey ?: throw LoadDatabaseException()
|
randomStream = StreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey)
|
||||||
val headerHmac = PwDbHeaderV4.computeHeaderHmac(pbHeader, hmacKey)
|
|
||||||
val storedHmac = isData.readBytes(32)
|
if (randomStream == null) {
|
||||||
if (storedHmac == null || storedHmac.size != 32) {
|
throw LoadDatabaseArcFourException()
|
||||||
throw LoadDatabaseException()
|
|
||||||
}
|
|
||||||
// Mac doesn't match
|
|
||||||
if (!Arrays.equals(headerHmac, storedHmac)) {
|
|
||||||
throw LoadDatabaseException()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val hmIs = HmacBlockInputStream(isData, true, hmacKey)
|
readDocumentStreamed(createPullParser(inputStreamXml))
|
||||||
|
|
||||||
isPlain = attachCipherStream(hmIs, cipher)
|
} catch (e: LoadDatabaseException) {
|
||||||
|
throw e
|
||||||
|
} catch (e: XmlPullParserException) {
|
||||||
|
throw LoadDatabaseIOException(e)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
if (e.message?.contains("Hash failed with code") == true)
|
||||||
|
throw LoadDatabaseKDFMemoryException(e)
|
||||||
|
else
|
||||||
|
throw LoadDatabaseIOException(e)
|
||||||
|
} catch (e: OutOfMemoryError) {
|
||||||
|
throw LoadDatabaseNoMemoryException(e)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw LoadDatabaseException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isXml: InputStream
|
|
||||||
isXml = when(mDatabase.compressionAlgorithm) {
|
|
||||||
PwCompressionAlgorithm.GZip -> GZIPInputStream(isPlain)
|
|
||||||
else -> isPlain
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version >= PwDbHeaderV4.FILE_VERSION_32_4) {
|
|
||||||
loadInnerHeader(isXml, header)
|
|
||||||
}
|
|
||||||
|
|
||||||
randomStream = StreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey)
|
|
||||||
|
|
||||||
if (randomStream == null) {
|
|
||||||
throw LoadDatabaseArcFourException()
|
|
||||||
}
|
|
||||||
|
|
||||||
readXmlStreamed(isXml)
|
|
||||||
|
|
||||||
return mDatabase
|
return mDatabase
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,17 +277,6 @@ class ImporterV4(private val streamDir: File,
|
|||||||
Binaries
|
Binaries
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class, LoadDatabaseException::class)
|
|
||||||
private fun readXmlStreamed(readerStream: InputStream) {
|
|
||||||
try {
|
|
||||||
readDocumentStreamed(createPullParser(readerStream))
|
|
||||||
} catch (e: XmlPullParserException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
throw IOException(e.localizedMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(XmlPullParserException::class, IOException::class, LoadDatabaseException::class)
|
@Throws(XmlPullParserException::class, IOException::class, LoadDatabaseException::class)
|
||||||
private fun readDocumentStreamed(xpp: XmlPullParser) {
|
private fun readDocumentStreamed(xpp: XmlPullParser) {
|
||||||
|
|
||||||
@@ -1003,7 +995,7 @@ class ImporterV4(private val streamDir: File,
|
|||||||
try {
|
try {
|
||||||
return String(buf, Charset.forName("UTF-8"))
|
return String(buf, Charset.forName("UTF-8"))
|
||||||
} catch (e: UnsupportedEncodingException) {
|
} catch (e: UnsupportedEncodingException) {
|
||||||
throw IOException(e.localizedMessage)
|
throw IOException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@
|
|||||||
<string name="error_move_folder_in_itself">غير قادر على نقل مجموعة إلى نفسها.</string>
|
<string name="error_move_folder_in_itself">غير قادر على نقل مجموعة إلى نفسها.</string>
|
||||||
<string name="file_not_found_content">تعذر إيجاد الملف. جرِّب فتحه من متصفح ملفات.</string>
|
<string name="file_not_found_content">تعذر إيجاد الملف. جرِّب فتحه من متصفح ملفات.</string>
|
||||||
<string name="file_browser">متصفح الملفات</string>
|
<string name="file_browser">متصفح الملفات</string>
|
||||||
<string name="invalid_password">تعذرت قراءة كلمة السر أو ملف المفتاح.</string>
|
<string name="invalid_credentials">تعذرت قراءة كلمة السر أو ملف المفتاح.</string>
|
||||||
<string name="invalid_db_sig">تعذر تمييز نسق قاعدة البيانات.</string>
|
<string name="invalid_db_sig">تعذر تمييز نسق قاعدة البيانات.</string>
|
||||||
<string name="keyfile_does_not_exist">لا يوجد ملف مفتاح.</string>
|
<string name="keyfile_does_not_exist">لا يوجد ملف مفتاح.</string>
|
||||||
<string name="keyfile_is_empty">ملف المفتاح فارغ.</string>
|
<string name="keyfile_is_empty">ملف المفتاح فارغ.</string>
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
<string name="password">Contrasenya</string>
|
<string name="password">Contrasenya</string>
|
||||||
<string name="install_from_play_store">Instal·la de la Play Store</string>
|
<string name="install_from_play_store">Instal·la de la Play Store</string>
|
||||||
<string name="install_from_f_droid">Instal·la de la F-Droid</string>
|
<string name="install_from_f_droid">Instal·la de la F-Droid</string>
|
||||||
<string name="invalid_password">Contrasenya o arxiu clau invàlids.</string>
|
<string name="invalid_credentials">Contrasenya o arxiu clau invàlids.</string>
|
||||||
<string name="invalid_db_sig">Format de base de dades desconegut.</string>
|
<string name="invalid_db_sig">Format de base de dades desconegut.</string>
|
||||||
<string name="length">Longitud</string>
|
<string name="length">Longitud</string>
|
||||||
<string name="list_size_title">Mida de la llista de grups</string>
|
<string name="list_size_title">Mida de la llista de grups</string>
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
<string name="password">Heslo</string>
|
<string name="password">Heslo</string>
|
||||||
<string name="install_from_play_store">Instalovat z katalogu Play Store</string>
|
<string name="install_from_play_store">Instalovat z katalogu Play Store</string>
|
||||||
<string name="install_from_f_droid">Instalovat z katalogu F-Droid</string>
|
<string name="install_from_f_droid">Instalovat z katalogu F-Droid</string>
|
||||||
<string name="invalid_password">Nesprávné heslo nebo soubor s klíčem.</string>
|
<string name="invalid_credentials">Nesprávné heslo nebo soubor s klíčem.</string>
|
||||||
<string name="invalid_algorithm">Nesprávný algoritmus.</string>
|
<string name="invalid_algorithm">Nesprávný algoritmus.</string>
|
||||||
<string name="invalid_db_sig">Nedaří se rozpoznat formát databáze.</string>
|
<string name="invalid_db_sig">Nedaří se rozpoznat formát databáze.</string>
|
||||||
<string name="keyfile_does_not_exist">Soubor s klíčem neexistuje.</string>
|
<string name="keyfile_does_not_exist">Soubor s klíčem neexistuje.</string>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
<string name="password">Adgangskode</string>
|
<string name="password">Adgangskode</string>
|
||||||
<string name="install_from_play_store">Installer fra Google Play</string>
|
<string name="install_from_play_store">Installer fra Google Play</string>
|
||||||
<string name="install_from_f_droid">Installer fra F-Droid</string>
|
<string name="install_from_f_droid">Installer fra F-Droid</string>
|
||||||
<string name="invalid_password">Ugyldig adgangskode eller nøglefil.</string>
|
<string name="invalid_credentials">Ugyldig adgangskode eller nøglefil.</string>
|
||||||
<string name="invalid_algorithm">Forkert algoritme.</string>
|
<string name="invalid_algorithm">Forkert algoritme.</string>
|
||||||
<string name="invalid_db_sig">Kunne ikke genkende databaseformat.</string>
|
<string name="invalid_db_sig">Kunne ikke genkende databaseformat.</string>
|
||||||
<string name="keyfile_does_not_exist">Nøglefil eksisterer ikke.</string>
|
<string name="keyfile_does_not_exist">Nøglefil eksisterer ikke.</string>
|
||||||
|
|||||||
@@ -92,7 +92,7 @@
|
|||||||
<string name="password">Passwort</string>
|
<string name="password">Passwort</string>
|
||||||
<string name="install_from_play_store">Google Play</string>
|
<string name="install_from_play_store">Google Play</string>
|
||||||
<string name="install_from_f_droid">F-Droid</string>
|
<string name="install_from_f_droid">F-Droid</string>
|
||||||
<string name="invalid_password">Passwort oder Schlüsseldatei ist falsch.</string>
|
<string name="invalid_credentials">Passwort oder Schlüsseldatei ist falsch.</string>
|
||||||
<string name="invalid_algorithm">Falscher Algorithmus.</string>
|
<string name="invalid_algorithm">Falscher Algorithmus.</string>
|
||||||
<string name="invalid_db_sig">Datenbankformat nicht erkannt.</string>
|
<string name="invalid_db_sig">Datenbankformat nicht erkannt.</string>
|
||||||
<string name="keyfile_does_not_exist">Es existiert keine Schlüsseldatei.</string>
|
<string name="keyfile_does_not_exist">Es existiert keine Schlüsseldatei.</string>
|
||||||
|
|||||||
@@ -87,7 +87,7 @@
|
|||||||
<string name="password">Κωδικός Πρόσβασης</string>
|
<string name="password">Κωδικός Πρόσβασης</string>
|
||||||
<string name="install_from_play_store">Εγκατάσταση από το Play Store</string>
|
<string name="install_from_play_store">Εγκατάσταση από το Play Store</string>
|
||||||
<string name="install_from_f_droid">Εγκατάσταση από το F-Droid</string>
|
<string name="install_from_f_droid">Εγκατάσταση από το F-Droid</string>
|
||||||
<string name="invalid_password">Εσφαλμένος κωδικός ή αρχείο κλειδιού.</string>
|
<string name="invalid_credentials">Εσφαλμένος κωδικός ή αρχείο κλειδιού.</string>
|
||||||
<string name="invalid_algorithm">Εσφαλμένος αλγόριθμος.</string>
|
<string name="invalid_algorithm">Εσφαλμένος αλγόριθμος.</string>
|
||||||
<string name="invalid_db_sig">Η μορφή της Βάσης Δεδομένων δεν αναγνωρίζεται.</string>
|
<string name="invalid_db_sig">Η μορφή της Βάσης Δεδομένων δεν αναγνωρίζεται.</string>
|
||||||
<string name="keyfile_does_not_exist">Δεν υπάρχει αρχείο κλειδιού.</string>
|
<string name="keyfile_does_not_exist">Δεν υπάρχει αρχείο κλειδιού.</string>
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
<string name="hint_pass">contraseña</string>
|
<string name="hint_pass">contraseña</string>
|
||||||
<string name="install_from_play_store">Instalar desde Play Store</string>
|
<string name="install_from_play_store">Instalar desde Play Store</string>
|
||||||
<string name="install_from_f_droid">Instalar desde F-Droid</string>
|
<string name="install_from_f_droid">Instalar desde F-Droid</string>
|
||||||
<string name="invalid_password">Contraseña o archivo de clave no válido.</string>
|
<string name="invalid_credentials">Contraseña o archivo de clave no válido.</string>
|
||||||
<string name="invalid_db_sig">Formato de base de datos no reconocido.</string>
|
<string name="invalid_db_sig">Formato de base de datos no reconocido.</string>
|
||||||
<string name="length">Longitud</string>
|
<string name="length">Longitud</string>
|
||||||
<string name="list_size_title">Tamaño de la lista de Grupo</string>
|
<string name="list_size_title">Tamaño de la lista de Grupo</string>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
<string name="password">Pasahitza</string>
|
<string name="password">Pasahitza</string>
|
||||||
<string name="install_from_play_store">Play store-etik Instalatu</string>
|
<string name="install_from_play_store">Play store-etik Instalatu</string>
|
||||||
<string name="install_from_f_droid">F-Droid-etik Instalatu</string>
|
<string name="install_from_f_droid">F-Droid-etik Instalatu</string>
|
||||||
<string name="invalid_password">Pasahitz edo gako fitxategi baliogabea.</string>
|
<string name="invalid_credentials">Pasahitz edo gako fitxategi baliogabea.</string>
|
||||||
<string name="invalid_algorithm">Algoritmo baliogabea.</string>
|
<string name="invalid_algorithm">Algoritmo baliogabea.</string>
|
||||||
<string name="invalid_db_sig">Datubase formato ez ezaguna.</string>
|
<string name="invalid_db_sig">Datubase formato ez ezaguna.</string>
|
||||||
<string name="keyfile_does_not_exist">Gako fitxategia ez da esistitzen.</string>
|
<string name="keyfile_does_not_exist">Gako fitxategia ez da esistitzen.</string>
|
||||||
|
|||||||
@@ -87,7 +87,7 @@
|
|||||||
<string name="password">Salasana</string>
|
<string name="password">Salasana</string>
|
||||||
<string name="install_from_play_store">Asenna Play Storesta</string>
|
<string name="install_from_play_store">Asenna Play Storesta</string>
|
||||||
<string name="install_from_f_droid">Asenna F-Droid</string>
|
<string name="install_from_f_droid">Asenna F-Droid</string>
|
||||||
<string name="invalid_password">Väärä salasana tai avaintiedosto.</string>
|
<string name="invalid_credentials">Väärä salasana tai avaintiedosto.</string>
|
||||||
<string name="invalid_algorithm">Epäkelpo algoritmi.</string>
|
<string name="invalid_algorithm">Epäkelpo algoritmi.</string>
|
||||||
<string name="invalid_db_sig">Salasanatietokannan tyyppiä ei tunnistettu.</string>
|
<string name="invalid_db_sig">Salasanatietokannan tyyppiä ei tunnistettu.</string>
|
||||||
<string name="keyfile_does_not_exist">Avaintiedostoa ei ole olemassa.</string>
|
<string name="keyfile_does_not_exist">Avaintiedostoa ei ole olemassa.</string>
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
<string name="password">Mot de passe</string>
|
<string name="password">Mot de passe</string>
|
||||||
<string name="install_from_play_store">Installer depuis Google Play</string>
|
<string name="install_from_play_store">Installer depuis Google Play</string>
|
||||||
<string name="install_from_f_droid">Installer depuis F-Droid</string>
|
<string name="install_from_f_droid">Installer depuis F-Droid</string>
|
||||||
<string name="invalid_password">Impossible de lire le mot de passe ou le fichier clé.</string>
|
<string name="invalid_credentials">Impossible de lire le mot de passe ou le fichier clé.</string>
|
||||||
<string name="invalid_algorithm">Mauvais algorithme.</string>
|
<string name="invalid_algorithm">Mauvais algorithme.</string>
|
||||||
<string name="invalid_db_sig">Impossible de reconnaître le format de la base de données.</string>
|
<string name="invalid_db_sig">Impossible de reconnaître le format de la base de données.</string>
|
||||||
<string name="keyfile_does_not_exist">Aucun fichier de clé n’existe.</string>
|
<string name="keyfile_does_not_exist">Aucun fichier de clé n’existe.</string>
|
||||||
|
|||||||
@@ -87,7 +87,7 @@
|
|||||||
<string name="password">Jelszó</string>
|
<string name="password">Jelszó</string>
|
||||||
<string name="install_from_play_store">Telepítés a Play Áruházból</string>
|
<string name="install_from_play_store">Telepítés a Play Áruházból</string>
|
||||||
<string name="install_from_f_droid">Telepítés az F-Droidból</string>
|
<string name="install_from_f_droid">Telepítés az F-Droidból</string>
|
||||||
<string name="invalid_password">A jelszó vagy kulcsfájl nem olvasható.</string>
|
<string name="invalid_credentials">A jelszó vagy kulcsfájl nem olvasható.</string>
|
||||||
<string name="invalid_algorithm">Hibás algoritmus.</string>
|
<string name="invalid_algorithm">Hibás algoritmus.</string>
|
||||||
<string name="invalid_db_sig">Az adatbázis formátuma nem ismerhető fel.</string>
|
<string name="invalid_db_sig">Az adatbázis formátuma nem ismerhető fel.</string>
|
||||||
<string name="keyfile_does_not_exist">Nem létezik kulcsfájl.</string>
|
<string name="keyfile_does_not_exist">Nem létezik kulcsfájl.</string>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
<string name="hint_pass">password</string>
|
<string name="hint_pass">password</string>
|
||||||
<string name="install_from_play_store">Installa dal Play Store</string>
|
<string name="install_from_play_store">Installa dal Play Store</string>
|
||||||
<string name="install_from_f_droid">Installa dal F-Droid</string>
|
<string name="install_from_f_droid">Installa dal F-Droid</string>
|
||||||
<string name="invalid_password">Password o file chiave non validi.</string>
|
<string name="invalid_credentials">Password o file chiave non validi.</string>
|
||||||
<string name="invalid_algorithm">Algoritmo errato.</string>
|
<string name="invalid_algorithm">Algoritmo errato.</string>
|
||||||
<string name="invalid_db_sig">Formato database non riconosciuto.</string>
|
<string name="invalid_db_sig">Formato database non riconosciuto.</string>
|
||||||
<string name="keyfile_does_not_exist">Non esiste alcun file chiave.</string>
|
<string name="keyfile_does_not_exist">Non esiste alcun file chiave.</string>
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
<string name="password">סיסמה</string>
|
<string name="password">סיסמה</string>
|
||||||
<string name="install_from_play_store">התקן מחנות Play</string>
|
<string name="install_from_play_store">התקן מחנות Play</string>
|
||||||
<string name="install_from_f_droid">התקן מהאינטרנט</string>
|
<string name="install_from_f_droid">התקן מהאינטרנט</string>
|
||||||
<string name="invalid_password">סיסמה או קובץ מפתח לא מתאימים.</string>
|
<string name="invalid_credentials">סיסמה או קובץ מפתח לא מתאימים.</string>
|
||||||
<string name="invalid_algorithm">אלגוריתם לא חוקי.</string>
|
<string name="invalid_algorithm">אלגוריתם לא חוקי.</string>
|
||||||
<string name="invalid_db_sig">תבנית מסד הנתונים אינה מזוהה.</string>
|
<string name="invalid_db_sig">תבנית מסד הנתונים אינה מזוהה.</string>
|
||||||
<string name="keyfile_does_not_exist">קובץ המפתח לא קיים.</string>
|
<string name="keyfile_does_not_exist">קובץ המפתח לא קיים.</string>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
<string name="hint_pass">パスワード</string>
|
<string name="hint_pass">パスワード</string>
|
||||||
<string name="install_from_play_store">Play Storeで入手</string>
|
<string name="install_from_play_store">Play Storeで入手</string>
|
||||||
<string name="install_from_f_droid">F-Droidで入手</string>
|
<string name="install_from_f_droid">F-Droidで入手</string>
|
||||||
<string name="invalid_password">パスワード/キーファイルが違います。</string>
|
<string name="invalid_credentials">パスワード/キーファイルが違います。</string>
|
||||||
<string name="invalid_db_sig">データベースフォーマットが認識できません。</string>
|
<string name="invalid_db_sig">データベースフォーマットが認識できません。</string>
|
||||||
<string name="length">生成するパスワードの長さ</string>
|
<string name="length">生成するパスワードの長さ</string>
|
||||||
<string name="list_size_title">グループ一覧サイズ</string>
|
<string name="list_size_title">グループ一覧サイズ</string>
|
||||||
|
|||||||
@@ -96,7 +96,7 @@
|
|||||||
<string name="password">비밀번호</string>
|
<string name="password">비밀번호</string>
|
||||||
<string name="install_from_f_droid">F-Droid에서 설치</string>
|
<string name="install_from_f_droid">F-Droid에서 설치</string>
|
||||||
<string name="install_from_play_store">플레이 스토어에서 설치</string>
|
<string name="install_from_play_store">플레이 스토어에서 설치</string>
|
||||||
<string name="invalid_password">비밀번호나 키 파일을 읽을 수 없습니다.</string>
|
<string name="invalid_credentials">비밀번호나 키 파일을 읽을 수 없습니다.</string>
|
||||||
<string name="invalid_algorithm">잘못된 알고리즘입니다.</string>
|
<string name="invalid_algorithm">잘못된 알고리즘입니다.</string>
|
||||||
<string name="invalid_db_sig">데이터베이스 형식을 인식할 수 없습니다.</string>
|
<string name="invalid_db_sig">데이터베이스 형식을 인식할 수 없습니다.</string>
|
||||||
<string name="keyfile_does_not_exist">키 파일이 없습니다.</string>
|
<string name="keyfile_does_not_exist">키 파일이 없습니다.</string>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="about_description">KeePass DX yra KeePass slaptažodžių tvarkyklės realizacija Android platformai</string>
|
<string name="about_description">KeePass DX yra KeePass slaptažodžių tvarkyklės realizacija Android platformai</string>
|
||||||
<string name="clipboard_cleared">Iškarpinė išvalyta.</string>
|
<string name="clipboard_cleared">Iškarpinė išvalyta.</string>
|
||||||
<string name="invalid_password">Neteisingas slaptažodis arba rakto failas.</string>
|
<string name="invalid_credentials">Neteisingas slaptažodis arba rakto failas.</string>
|
||||||
<string name="about_feedback">Atsiliepimai:</string>
|
<string name="about_feedback">Atsiliepimai:</string>
|
||||||
<string name="about_homepage">Pagrindinis puslapis:</string>
|
<string name="about_homepage">Pagrindinis puslapis:</string>
|
||||||
<string name="accept">Priimti</string>
|
<string name="accept">Priimti</string>
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
<string name="password">Parole</string>
|
<string name="password">Parole</string>
|
||||||
<string name="install_from_play_store">Instalēt no Play veikala</string>
|
<string name="install_from_play_store">Instalēt no Play veikala</string>
|
||||||
<string name="install_from_f_droid">Instalēt no F-Droid</string>
|
<string name="install_from_f_droid">Instalēt no F-Droid</string>
|
||||||
<string name="invalid_password">Nederīga parole vai atslēgas fails.</string>
|
<string name="invalid_credentials">Nederīga parole vai atslēgas fails.</string>
|
||||||
<string name="invalid_algorithm">Nederīgs algoritms.</string>
|
<string name="invalid_algorithm">Nederīgs algoritms.</string>
|
||||||
<string name="invalid_db_sig">Datu bāzes formāts nav atpazīts.</string>
|
<string name="invalid_db_sig">Datu bāzes formāts nav atpazīts.</string>
|
||||||
<string name="keyfile_does_not_exist">Atslēgas fails nepastāv.</string>
|
<string name="keyfile_does_not_exist">Atslēgas fails nepastāv.</string>
|
||||||
|
|||||||
@@ -96,7 +96,7 @@
|
|||||||
<string name="password">Passord</string>
|
<string name="password">Passord</string>
|
||||||
<string name="install_from_play_store">Installer fra Play-butikken</string>
|
<string name="install_from_play_store">Installer fra Play-butikken</string>
|
||||||
<string name="install_from_f_droid">Installer fra F-Droid</string>
|
<string name="install_from_f_droid">Installer fra F-Droid</string>
|
||||||
<string name="invalid_password">Ugyldig passord eller nøkkelfil.</string>
|
<string name="invalid_credentials">Ugyldig passord eller nøkkelfil.</string>
|
||||||
<string name="invalid_algorithm">Ugyldig algoritme.</string>
|
<string name="invalid_algorithm">Ugyldig algoritme.</string>
|
||||||
<string name="invalid_db_sig">Fremmed databaseformat.</string>
|
<string name="invalid_db_sig">Fremmed databaseformat.</string>
|
||||||
<string name="keyfile_does_not_exist">Ingen nøkkelfil finnes.</string>
|
<string name="keyfile_does_not_exist">Ingen nøkkelfil finnes.</string>
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
<string name="hint_pass">wachtwoord</string>
|
<string name="hint_pass">wachtwoord</string>
|
||||||
<string name="install_from_play_store">Installeren via Play Store</string>
|
<string name="install_from_play_store">Installeren via Play Store</string>
|
||||||
<string name="install_from_f_droid">Installeren via F-Droid</string>
|
<string name="install_from_f_droid">Installeren via F-Droid</string>
|
||||||
<string name="invalid_password">Ongeldig wachtwoord of sleutelbestand.</string>
|
<string name="invalid_credentials">Ongeldig wachtwoord of sleutelbestand.</string>
|
||||||
<string name="invalid_db_sig">Databankformaat kan niet worden herkend.</string>
|
<string name="invalid_db_sig">Databankformaat kan niet worden herkend.</string>
|
||||||
<string name="length">Lengte</string>
|
<string name="length">Lengte</string>
|
||||||
<string name="list_size_title">Grootte van itemlijst</string>
|
<string name="list_size_title">Grootte van itemlijst</string>
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
<string name="hint_pass">passord</string>
|
<string name="hint_pass">passord</string>
|
||||||
<string name="install_from_play_store">Installer frå Play Store</string>
|
<string name="install_from_play_store">Installer frå Play Store</string>
|
||||||
<string name="install_from_f_droid">Installer frå F-Droid</string>
|
<string name="install_from_f_droid">Installer frå F-Droid</string>
|
||||||
<string name="invalid_password">Ugyldig passord eller nøkkelfil.</string>
|
<string name="invalid_credentials">Ugyldig passord eller nøkkelfil.</string>
|
||||||
<string name="invalid_db_sig">Ukjent databaseformat.</string>
|
<string name="invalid_db_sig">Ukjent databaseformat.</string>
|
||||||
<string name="length">Lengd</string>
|
<string name="length">Lengd</string>
|
||||||
<string name="list_size_title">Gruppelistestorleik</string>
|
<string name="list_size_title">Gruppelistestorleik</string>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
<string name="hint_pass">hasło</string>
|
<string name="hint_pass">hasło</string>
|
||||||
<string name="install_from_play_store">Zainstaluj z Play-Store</string>
|
<string name="install_from_play_store">Zainstaluj z Play-Store</string>
|
||||||
<string name="install_from_f_droid">Zainstaluj z F-Droid</string>
|
<string name="install_from_f_droid">Zainstaluj z F-Droid</string>
|
||||||
<string name="invalid_password">Nieprawidłowe hasło lub plik klucza.</string>
|
<string name="invalid_credentials">Nieprawidłowe hasło lub plik klucza.</string>
|
||||||
<string name="invalid_db_sig">Nie można rozpoznać formatu bazy danych.</string>
|
<string name="invalid_db_sig">Nie można rozpoznać formatu bazy danych.</string>
|
||||||
<string name="length">Długość</string>
|
<string name="length">Długość</string>
|
||||||
<string name="list_size_title">Wielkość listy grup</string>
|
<string name="list_size_title">Wielkość listy grup</string>
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
<string name="hint_pass">senha</string>
|
<string name="hint_pass">senha</string>
|
||||||
<string name="install_from_play_store">Instalar a partir do Google Play</string>
|
<string name="install_from_play_store">Instalar a partir do Google Play</string>
|
||||||
<string name="install_from_f_droid">Instalar a partir do F-Droid</string>
|
<string name="install_from_f_droid">Instalar a partir do F-Droid</string>
|
||||||
<string name="invalid_password">Senha ou arquivo de chaves inválidos.</string>
|
<string name="invalid_credentials">Senha ou arquivo de chaves inválidos.</string>
|
||||||
<string name="invalid_db_sig">Não pôde reconhecer formato do banco de dados.</string>
|
<string name="invalid_db_sig">Não pôde reconhecer formato do banco de dados.</string>
|
||||||
<string name="length">Tamanho</string>
|
<string name="length">Tamanho</string>
|
||||||
<string name="list_size_title">Tamanho da lista de grupos</string>
|
<string name="list_size_title">Tamanho da lista de grupos</string>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
<string name="password">Palavra-chave</string>
|
<string name="password">Palavra-chave</string>
|
||||||
<string name="install_from_play_store">Instalar pela Play Store</string>
|
<string name="install_from_play_store">Instalar pela Play Store</string>
|
||||||
<string name="install_from_f_droid">Instalar pela F-Droid</string>
|
<string name="install_from_f_droid">Instalar pela F-Droid</string>
|
||||||
<string name="invalid_password">Palavra-chave ou ficheiro chave inválidos.</string>
|
<string name="invalid_credentials">Palavra-chave ou ficheiro chave inválidos.</string>
|
||||||
<string name="invalid_algorithm">Algoritmo errado.</string>
|
<string name="invalid_algorithm">Algoritmo errado.</string>
|
||||||
<string name="invalid_db_sig">Não pôde reconhecer formato do banco de dados.</string>
|
<string name="invalid_db_sig">Não pôde reconhecer formato do banco de dados.</string>
|
||||||
<string name="keyfile_does_not_exist">Não existem ficheiros-chave.</string>
|
<string name="keyfile_does_not_exist">Não existem ficheiros-chave.</string>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
<string name="password">Пароль</string>
|
<string name="password">Пароль</string>
|
||||||
<string name="install_from_play_store">Google Play</string>
|
<string name="install_from_play_store">Google Play</string>
|
||||||
<string name="install_from_f_droid">F-Droid</string>
|
<string name="install_from_f_droid">F-Droid</string>
|
||||||
<string name="invalid_password">Неверный пароль или файл ключа.</string>
|
<string name="invalid_credentials">Неверный пароль или файл ключа.</string>
|
||||||
<string name="invalid_algorithm">Неверный алгоритм.</string>
|
<string name="invalid_algorithm">Неверный алгоритм.</string>
|
||||||
<string name="invalid_db_sig">Не удалось определить формат базы.</string>
|
<string name="invalid_db_sig">Не удалось определить формат базы.</string>
|
||||||
<string name="keyfile_does_not_exist">Файл ключа не найден.</string>
|
<string name="keyfile_does_not_exist">Файл ключа не найден.</string>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
<string name="hint_pass">heslo</string>
|
<string name="hint_pass">heslo</string>
|
||||||
<string name="install_from_play_store">Inštalovať z Play Store</string>
|
<string name="install_from_play_store">Inštalovať z Play Store</string>
|
||||||
<string name="install_from_f_droid">Inštalovať z F-Droid</string>
|
<string name="install_from_f_droid">Inštalovať z F-Droid</string>
|
||||||
<string name="invalid_password">Chybné heslo, alebo súbor keyfile.</string>
|
<string name="invalid_credentials">Chybné heslo, alebo súbor keyfile.</string>
|
||||||
<string name="invalid_db_sig">Formát Databázy nerozpoznaný.</string>
|
<string name="invalid_db_sig">Formát Databázy nerozpoznaný.</string>
|
||||||
<string name="length">Dĺžka</string>
|
<string name="length">Dĺžka</string>
|
||||||
<string name="list_size_title">Dĺžka zoznamu skupiny</string>
|
<string name="list_size_title">Dĺžka zoznamu skupiny</string>
|
||||||
|
|||||||
@@ -87,7 +87,7 @@
|
|||||||
<string name="password">Lösenord</string>
|
<string name="password">Lösenord</string>
|
||||||
<string name="install_from_play_store">Installera från Play Store</string>
|
<string name="install_from_play_store">Installera från Play Store</string>
|
||||||
<string name="install_from_f_droid">Installera från F-Droid</string>
|
<string name="install_from_f_droid">Installera från F-Droid</string>
|
||||||
<string name="invalid_password">Ogiltigt lösenord eller nyckelfil.</string>
|
<string name="invalid_credentials">Ogiltigt lösenord eller nyckelfil.</string>
|
||||||
<string name="invalid_algorithm">Ogiltig algoritm.</string>
|
<string name="invalid_algorithm">Ogiltig algoritm.</string>
|
||||||
<string name="invalid_db_sig">Databasformatet är okänt.</string>
|
<string name="invalid_db_sig">Databasformatet är okänt.</string>
|
||||||
<string name="keyfile_does_not_exist">Nyckelfilen existerar inte.</string>
|
<string name="keyfile_does_not_exist">Nyckelfilen existerar inte.</string>
|
||||||
|
|||||||
@@ -96,7 +96,7 @@
|
|||||||
<string name="password">Parola</string>
|
<string name="password">Parola</string>
|
||||||
<string name="install_from_f_droid">F-Droid\'den yükleyin</string>
|
<string name="install_from_f_droid">F-Droid\'den yükleyin</string>
|
||||||
<string name="install_from_play_store">"Play Store\'dan yükleyin "</string>
|
<string name="install_from_play_store">"Play Store\'dan yükleyin "</string>
|
||||||
<string name="invalid_password">Parola veya anahtar dosya okunamadı.</string>
|
<string name="invalid_credentials">Parola veya anahtar dosya okunamadı.</string>
|
||||||
<string name="invalid_algorithm">Yanlış algoritma.</string>
|
<string name="invalid_algorithm">Yanlış algoritma.</string>
|
||||||
<string name="invalid_db_sig">Veritabanı biçimi tanımlanamadı.</string>
|
<string name="invalid_db_sig">Veritabanı biçimi tanımlanamadı.</string>
|
||||||
<string name="keyfile_does_not_exist">Hiç anahtar dosya yok.</string>
|
<string name="keyfile_does_not_exist">Hiç anahtar dosya yok.</string>
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
<string name="hint_pass">пароль</string>
|
<string name="hint_pass">пароль</string>
|
||||||
<string name="install_from_play_store">Інсталювати із Google Play</string>
|
<string name="install_from_play_store">Інсталювати із Google Play</string>
|
||||||
<string name="install_from_f_droid">Інсталювати із F-Droid</string>
|
<string name="install_from_f_droid">Інсталювати із F-Droid</string>
|
||||||
<string name="invalid_password">Невірний пароль або файл ключа.</string>
|
<string name="invalid_credentials">Невірний пароль або файл ключа.</string>
|
||||||
<string name="invalid_db_sig">Формат бази даних не розпізнано.</string>
|
<string name="invalid_db_sig">Формат бази даних не розпізнано.</string>
|
||||||
<string name="length">Довжина</string>
|
<string name="length">Довжина</string>
|
||||||
<string name="list_size_title">Розмір списку груп</string>
|
<string name="list_size_title">Розмір списку груп</string>
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
<string name="hint_pass">密码</string>
|
<string name="hint_pass">密码</string>
|
||||||
<string name="install_from_play_store">从 Play 商店安装</string>
|
<string name="install_from_play_store">从 Play 商店安装</string>
|
||||||
<string name="install_from_f_droid">从 F-Droid 安装</string>
|
<string name="install_from_f_droid">从 F-Droid 安装</string>
|
||||||
<string name="invalid_password">无法读取密码或密钥文件。</string>
|
<string name="invalid_credentials">无法读取密码或密钥文件。</string>
|
||||||
<string name="invalid_db_sig">无法识别数据库格式。</string>
|
<string name="invalid_db_sig">无法识别数据库格式。</string>
|
||||||
<string name="length">长度</string>
|
<string name="length">长度</string>
|
||||||
<string name="list_size_title">列表项目尺寸</string>
|
<string name="list_size_title">列表项目尺寸</string>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
<string name="hint_pass">密碼</string>
|
<string name="hint_pass">密碼</string>
|
||||||
<string name="install_from_play_store">從 Play 商店安裝</string>
|
<string name="install_from_play_store">從 Play 商店安裝</string>
|
||||||
<string name="install_from_f_droid">從 F-Droid 安裝</string>
|
<string name="install_from_f_droid">從 F-Droid 安裝</string>
|
||||||
<string name="invalid_password">無效的密碼或密鑰檔。</string>
|
<string name="invalid_credentials">無效的密碼或密鑰檔。</string>
|
||||||
<string name="invalid_db_sig">資料庫格式無法識別。</string>
|
<string name="invalid_db_sig">資料庫格式無法識別。</string>
|
||||||
<string name="length">長度</string>
|
<string name="length">長度</string>
|
||||||
<string name="list_size_title">群列表尺寸</string>
|
<string name="list_size_title">群列表尺寸</string>
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
<string name="password">Password</string>
|
<string name="password">Password</string>
|
||||||
<string name="install_from_f_droid">Install from F-Droid</string>
|
<string name="install_from_f_droid">Install from F-Droid</string>
|
||||||
<string name="install_from_play_store">Install from Play Store</string>
|
<string name="install_from_play_store">Install from Play Store</string>
|
||||||
<string name="invalid_password">Could not read password or keyfile.</string>
|
<string name="invalid_credentials">Could not read credentials.</string>
|
||||||
<string name="invalid_algorithm">Wrong algorithm.</string>
|
<string name="invalid_algorithm">Wrong algorithm.</string>
|
||||||
<string name="invalid_db_same_uuid">%1$s with the same UUID %2$s already exists.</string>
|
<string name="invalid_db_same_uuid">%1$s with the same UUID %2$s already exists.</string>
|
||||||
<string name="invalid_db_sig">Could not recognize the database format.</string>
|
<string name="invalid_db_sig">Could not recognize the database format.</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user