Introduce core-models module.

Needed to share some key components with feature modules without having
to depend on libsignal-service.
This commit is contained in:
Greyson Parrelli
2025-11-28 13:39:39 -05:00
committed by jeffrey-signal
parent abf32bf6ff
commit 706d89db87
364 changed files with 2085 additions and 1016 deletions

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.core.util
import java.security.SecureRandom
object RandomUtil {
fun getSecureBytes(size: Int): ByteArray {
val secret = ByteArray(size)
SecureRandom().nextBytes(secret)
return secret
}
}

View File

@@ -0,0 +1,10 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.core.util
import java.util.UUID
fun UUID.toByteArray(): ByteArray = UuidUtil.toByteArray(this)

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.core.util
import okio.ByteString
import java.nio.ByteBuffer
import java.util.Optional
import java.util.UUID
import java.util.regex.Pattern
object UuidUtil {
@JvmField
val UNKNOWN_UUID: UUID = UUID(0, 0)
val UNKNOWN_UUID_STRING: String = UNKNOWN_UUID.toString()
private val UUID_PATTERN: Pattern = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", Pattern.CASE_INSENSITIVE)
fun parse(uuid: String?): Optional<UUID> {
return Optional.ofNullable(parseOrNull(uuid))
}
@JvmStatic
fun parseOrNull(uuid: String?): UUID? {
return uuid?.takeIf { isUuid(it) }?.let { parseOrThrow(it) }
}
fun parseOrUnknown(uuid: String?): UUID {
return parseOrNull(uuid) ?: UNKNOWN_UUID
}
@JvmStatic
fun parseOrThrow(uuid: String): UUID {
return UUID.fromString(uuid)
}
@JvmStatic
fun parseOrThrow(bytes: ByteArray): UUID {
val byteBuffer = ByteBuffer.wrap(bytes)
val high = byteBuffer.getLong()
val low = byteBuffer.getLong()
return UUID(high, low)
}
fun parseOrThrow(bytes: ByteString): UUID {
return parseOrNull(bytes.toByteArray())!!
}
fun isUuid(uuid: String?): Boolean {
return uuid != null && UUID_PATTERN.matcher(uuid).matches()
}
@JvmStatic
fun toByteArray(uuid: UUID): ByteArray {
val buffer = ByteBuffer.wrap(ByteArray(16))
buffer.putLong(uuid.mostSignificantBits)
buffer.putLong(uuid.leastSignificantBits)
return buffer.array()
}
@JvmStatic
fun toByteString(uuid: UUID): ByteString {
return ByteString.of(*toByteArray(uuid))
}
@JvmStatic
fun fromByteString(bytes: ByteString): UUID {
return parseOrThrow(bytes.toByteArray())
}
@JvmStatic
fun fromByteStringOrNull(bytes: ByteString?): UUID? {
if (bytes == null) {
return null
}
return parseOrNull(bytes.toByteArray())
}
@JvmStatic
fun getStringUUID(stringId: String?, bytes: ByteString?): String? {
val uuid = parseOrNull(bytes)
return uuid?.toString() ?: stringId
}
@JvmStatic
fun fromByteStringOrUnknown(bytes: ByteString?): UUID {
val uuid = fromByteStringOrNull(bytes)
return uuid ?: UNKNOWN_UUID
}
fun parseOrNull(byteArray: ByteArray?): UUID? {
return if (byteArray != null && byteArray.size == 16) parseOrThrow(byteArray) else null
}
fun parseOrNull(byteString: ByteString?): UUID? {
return if (byteString != null) parseOrNull(byteString.toByteArray()) else null
}
fun fromByteStrings(byteStringCollection: Collection<ByteString>): List<UUID> {
val result = ArrayList<UUID>(byteStringCollection.size)
for (byteString in byteStringCollection) {
result.add(fromByteString(byteString))
}
return result
}
/**
* Keep only UUIDs that are not the [.UNKNOWN_UUID].
*/
fun filterKnown(uuids: MutableCollection<UUID?>): MutableList<UUID?> {
val result = ArrayList<UUID?>(uuids.size)
for (uuid in uuids) {
if (UNKNOWN_UUID != uuid) {
result.add(uuid)
}
}
return result
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.signal.core.util;
import org.junit.Test;
import org.signal.libsignal.protocol.util.Hex;
import java.io.IOException;
import java.util.UUID;
import okio.ByteString;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
public final class UuidUtilTest {
@Test
public void toByteArray() throws IOException {
UUID uuid = UUID.fromString("67dfd496-ea02-4720-b13d-83a462168b1d");
byte[] serialized = UuidUtil.toByteArray(uuid);
assertArrayEquals(Hex.fromStringCondensed("67dfd496ea024720b13d83a462168b1d"), serialized);
}
@Test
public void toByteArray_alternativeValues() throws IOException {
UUID uuid = UUID.fromString("b70df6ac-3b21-4b39-a514-613561f51e2a");
byte[] serialized = UuidUtil.toByteArray(uuid);
assertArrayEquals(Hex.fromStringCondensed("b70df6ac3b214b39a514613561f51e2a"), serialized);
}
@Test
public void parseOrThrow_from_byteArray() throws IOException {
byte[] bytes = Hex.fromStringCondensed("3dc48790568b49c19bd6ab6604a5bc32");
UUID uuid = UuidUtil.parseOrThrow(bytes);
assertEquals("3dc48790-568b-49c1-9bd6-ab6604a5bc32", uuid.toString());
}
@Test
public void parseOrThrow_from_byteArray_alternativeValues() throws IOException {
byte[] bytes = Hex.fromStringCondensed("b83dfb0b67f141aa992e030c167cd011");
UUID uuid = UuidUtil.parseOrThrow(bytes);
assertEquals("b83dfb0b-67f1-41aa-992e-030c167cd011", uuid.toString());
}
@Test
public void byte_string_round_trip() {
UUID uuid = UUID.fromString("67dfd496-ea02-4720-b13d-83a462168b1d");
UUID result = UuidUtil.fromByteString(ByteString.of(UuidUtil.toByteArray(uuid)));
assertEquals(uuid, result);
}
}