fix: UUIDUtils and fixed AAGUID #1421

This commit is contained in:
J-Jamet
2025-08-22 11:04:53 +02:00
parent 6672085d84
commit 80b16bccf1
11 changed files with 171 additions and 127 deletions

View File

@@ -22,7 +22,8 @@ package com.kunzisoft.keepass.database.element.entry
import android.util.Log
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
import com.kunzisoft.keepass.utils.UuidUtil
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
import com.kunzisoft.keepass.utils.UUIDUtils.asUUID
import java.util.concurrent.ConcurrentHashMap
class FieldReferencesEngine(private val mDatabase: DatabaseKDBX) {
@@ -79,7 +80,7 @@ class FieldReferencesEngine(private val mDatabase: DatabaseKDBX) {
'A' -> entryFound?.decodeUrlKey(newRecursionLevel)
'P' -> entryFound?.decodePasswordKey(newRecursionLevel)
'N' -> entryFound?.decodeNotesKey(newRecursionLevel)
'I' -> UuidUtil.toHexString(entryFound?.nodeId?.id)
'I' -> entryFound?.nodeId?.id?.asHexString()
else -> null
}
refsCache[fullReference] = data
@@ -127,7 +128,7 @@ class FieldReferencesEngine(private val mDatabase: DatabaseKDBX) {
'P' -> mDatabase.getEntryByPassword(searchQuery, recursionLevel)
'N' -> mDatabase.getEntryByNotes(searchQuery, recursionLevel)
'I' -> {
UuidUtil.fromHexString(searchQuery)?.let { uuid ->
searchQuery.asUUID()?.let { uuid ->
mDatabase.getEntryById(NodeIdUUID(uuid))
}
}

View File

@@ -22,9 +22,9 @@ package com.kunzisoft.keepass.database.element.node
import android.os.Parcel
import android.os.ParcelUuid
import android.os.Parcelable
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
import com.kunzisoft.keepass.utils.readParcelableCompat
import com.kunzisoft.keepass.utils.UuidUtil
import java.util.*
import java.util.UUID
class NodeIdUUID : NodeId<UUID> {
@@ -62,7 +62,7 @@ class NodeIdUUID : NodeId<UUID> {
}
override fun toString(): String {
return UuidUtil.toHexString(id) ?: id.toString()
return id.asHexString() ?: id.toString()
}
override fun toVisualString(): String {

View File

@@ -24,7 +24,8 @@ import com.kunzisoft.keepass.database.element.Field
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.utils.UuidUtil
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
import com.kunzisoft.keepass.utils.UUIDUtils.asUUID
class TemplateEngineCompatible(database: DatabaseKDBX): TemplateEngine(database) {
@@ -33,7 +34,7 @@ class TemplateEngineCompatible(database: DatabaseKDBX): TemplateEngine(database)
}
override fun getTemplate(entryKDBX: EntryKDBX): Template? {
UuidUtil.fromHexString(entryKDBX.getCustomFieldValue(TEMPLATE_ENTRY_UUID))?.let { templateUUID ->
entryKDBX.getCustomFieldValue(TEMPLATE_ENTRY_UUID).asUUID()?.let { templateUUID ->
return getTemplateByCache(templateUUID)
}
return null
@@ -48,7 +49,7 @@ class TemplateEngineCompatible(database: DatabaseKDBX): TemplateEngine(database)
}
private fun getTemplateUUIDField(template: Template): Field? {
UuidUtil.toHexString(template.uuid)?.let { uuidString ->
template.uuid.asHexString()?.let { uuidString ->
return Field(TEMPLATE_ENTRY_UUID,
ProtectedString(false, uuidString))
}

View File

@@ -28,7 +28,7 @@ import com.kunzisoft.keepass.model.PasskeyEntryFields.FIELD_RELYING_PARTY
import com.kunzisoft.keepass.model.PasskeyEntryFields.isPasskeyExclusion
import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_FIELD
import com.kunzisoft.keepass.otp.OtpEntryFields.isOtpExclusion
import com.kunzisoft.keepass.utils.UuidUtil
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
import com.kunzisoft.keepass.utils.inTheSameDomainAs
class SearchHelper {
@@ -166,7 +166,7 @@ class SearchHelper {
return true
}
if (searchParameters.searchInUUIDs) {
val hexString = UuidUtil.toHexString(entry.nodeId.id) ?: ""
val hexString = entry.nodeId.id.asHexString() ?: ""
if (checkSearchQuery(hexString, searchParameters))
return true
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.utils
import java.nio.ByteBuffer
import java.util.Locale
import java.util.UUID
object UUIDUtils {
fun UUID.asHexString(): String? {
try {
val buf = uuidTo16Bytes(this)
val len = buf.size
if (len == 0) {
return ""
}
val sb = StringBuilder()
var bt: Short
var high: Char
var low: Char
for (b in buf) {
bt = (b.toInt() and 0xFF).toShort()
high = (bt.toInt() ushr 4).toChar()
low = (bt.toInt() and 0x0F).toChar()
sb.append(byteToChar(high))
sb.append(byteToChar(low))
}
return sb.toString()
} catch (e: Exception) {
return null
}
}
fun String.asUUID(): UUID? {
if (this.length != 32) return null
val charArray = this.lowercase(Locale.getDefault()).toCharArray()
val leastSignificantChars = CharArray(16)
val mostSignificantChars = CharArray(16)
var i = 31
while (i >= 0) {
if (i >= 16) {
mostSignificantChars[32 - i] = charArray[i]
mostSignificantChars[31 - i] = charArray[i - 1]
} else {
leastSignificantChars[16 - i] = charArray[i]
leastSignificantChars[15 - i] = charArray[i - 1]
}
i = i - 2
}
val standardUUIDString = StringBuilder()
standardUUIDString.append(leastSignificantChars)
standardUUIDString.append(mostSignificantChars)
standardUUIDString.insert(8, '-')
standardUUIDString.insert(13, '-')
standardUUIDString.insert(18, '-')
standardUUIDString.insert(23, '-')
return try {
UUID.fromString(standardUUIDString.toString())
} catch (e: Exception) {
null
}
}
fun ByteArray.asUUID(): UUID {
val bb = ByteBuffer.wrap(this)
val firstLong = bb.getLong()
val secondLong = bb.getLong()
return UUID(firstLong, secondLong)
}
fun UUID.asBytes(): ByteArray {
return ByteBuffer.allocate(16).apply {
putLong(mostSignificantBits)
putLong(leastSignificantBits)
}.array()
}
fun String.asUUIDBytes(): ByteArray {
return this.asUUID()?.asBytes() ?: ByteArray(16)
}
// Use short to represent unsigned byte
private fun byteToChar(bt: Char): Char {
return if (bt.code >= 10) {
('A'.code + bt.code - 10).toChar()
} else {
('0'.code + bt.code).toChar()
}
}
}

View File

@@ -1,101 +0,0 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.utils;
import java.util.UUID;
import static com.kunzisoft.keepass.utils.StreamBytesUtilsKt.uuidTo16Bytes;
import org.jetbrains.annotations.Nullable;
public class UuidUtil {
public static @Nullable String toHexString(@Nullable UUID uuid) {
if (uuid == null) { return null; }
try {
byte[] buf = uuidTo16Bytes(uuid);
int len = buf.length;
if (len == 0) {
return "";
}
StringBuilder sb = new StringBuilder();
short bt;
char high, low;
for (byte b : buf) {
bt = (short) (b & 0xFF);
high = (char) (bt >>> 4);
low = (char) (bt & 0x0F);
sb.append(byteToChar(high));
sb.append(byteToChar(low));
}
return sb.toString();
} catch (Exception e) {
return null;
}
}
public static @Nullable UUID fromHexString(@Nullable String hexString) {
if (hexString == null)
return null;
if (hexString.length() != 32)
return null;
char[] charArray = hexString.toLowerCase().toCharArray();
char[] leastSignificantChars = new char[16];
char[] mostSignificantChars = new char[16];
for (int i = 31; i >= 0; i = i-2) {
if (i >= 16) {
mostSignificantChars[32-i] = charArray[i];
mostSignificantChars[31-i] = charArray[i-1];
} else {
leastSignificantChars[16-i] = charArray[i];
leastSignificantChars[15-i] = charArray[i-1];
}
}
StringBuilder standardUUIDString = new StringBuilder();
standardUUIDString.append(leastSignificantChars);
standardUUIDString.append(mostSignificantChars);
standardUUIDString.insert(8, '-');
standardUUIDString.insert(13, '-');
standardUUIDString.insert(18, '-');
standardUUIDString.insert(23, '-');
try {
return UUID.fromString(standardUUIDString.toString());
} catch (Exception e) {
return null;
}
}
// Use short to represent unsigned byte
private static char byteToChar(char bt) {
if (bt >= 10) {
return (char)('A' + bt - 10);
}
else {
return (char)('0' + bt);
}
}
}

View File

@@ -1,15 +1,43 @@
/*
* Copyright 2025 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.tests.utils
import com.kunzisoft.keepass.utils.UuidUtil
import com.kunzisoft.keepass.utils.UUIDUtils.asBytes
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
import com.kunzisoft.keepass.utils.UUIDUtils.asUUID
import junit.framework.TestCase
import java.util.*
import java.util.UUID
class UUIDTest: TestCase() {
fun testUUID() {
fun testUUIDString() {
val randomUUID = UUID.randomUUID()
val hexStringUUID = UuidUtil.toHexString(randomUUID)
val retrievedUUID = UuidUtil.fromHexString(hexStringUUID)
val hexStringUUID = randomUUID.asHexString()
val retrievedUUID = hexStringUUID?.asUUID()
assertEquals(randomUUID, retrievedUUID)
}
fun testUUIDBytes() {
val randomUUID = UUID.randomUUID()
val byteArrayUUID = randomUUID.asBytes()
val retrievedUUID = byteArrayUUID.asUUID()
assertEquals(randomUUID, retrievedUUID)
}
}