Refactor exceptions

This commit is contained in:
J-Jamet
2019-10-07 15:19:13 +02:00
parent ab1fc8c5d5
commit cc20b7503c
40 changed files with 369 additions and 385 deletions

View File

@@ -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)
} }

View File

@@ -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)

View File

@@ -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()

View File

@@ -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) {

View File

@@ -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()

View File

@@ -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 {

View File

@@ -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?,

View File

@@ -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
} }

View File

@@ -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)
} }
} }

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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é nexiste.</string> <string name="keyfile_does_not_exist">Aucun fichier de clé nexiste.</string>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>