Introduce "service identifiers"

This commit is contained in:
Jon Chambers
2023-07-21 09:34:10 -04:00
committed by GitHub
parent 4a6c7152cf
commit abb32bd919
39 changed files with 1304 additions and 588 deletions

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.identity;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HexFormat;
import java.util.UUID;
import io.swagger.v3.oas.annotations.media.Schema;
import org.whispersystems.textsecuregcm.util.UUIDUtil;
/**
* An identifier for an account based on the account's ACI.
*
* @param uuid the account's ACI UUID
*/
@Schema(
type = "string",
description = "An identifier for an account based on the account's ACI"
)
public record AciServiceIdentifier(UUID uuid) implements ServiceIdentifier {
private static final IdentityType IDENTITY_TYPE = IdentityType.ACI;
@Override
public IdentityType identityType() {
return IDENTITY_TYPE;
}
@Override
public String toServiceIdentifierString() {
return uuid.toString();
}
@Override
public byte[] toCompactByteArray() {
return UUIDUtil.toBytes(uuid);
}
@Override
public byte[] toFixedWidthByteArray() {
final ByteBuffer byteBuffer = ByteBuffer.allocate(17);
byteBuffer.put(IDENTITY_TYPE.getBytePrefix());
byteBuffer.putLong(uuid.getMostSignificantBits());
byteBuffer.putLong(uuid.getLeastSignificantBits());
byteBuffer.flip();
return byteBuffer.array();
}
public static AciServiceIdentifier valueOf(final String string) {
return new AciServiceIdentifier(
UUID.fromString(string.startsWith(IDENTITY_TYPE.getStringPrefix())
? string.substring(IDENTITY_TYPE.getStringPrefix().length()) : string));
}
public static AciServiceIdentifier fromBytes(final byte[] bytes) {
final UUID uuid;
if (bytes.length == 17) {
if (bytes[0] != IDENTITY_TYPE.getBytePrefix()) {
throw new IllegalArgumentException("Unexpected byte array prefix: " + HexFormat.of().formatHex(new byte[] { bytes[0] }));
}
uuid = UUIDUtil.fromBytes(Arrays.copyOfRange(bytes, 1, bytes.length));
} else {
uuid = UUIDUtil.fromBytes(bytes);
}
return new AciServiceIdentifier(uuid);
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.identity;
public enum IdentityType {
ACI((byte) 0x00, "ACI:"),
PNI((byte) 0x01, "PNI:");
private final byte bytePrefix;
private final String stringPrefix;
IdentityType(final byte bytePrefix, final String stringPrefix) {
this.bytePrefix = bytePrefix;
this.stringPrefix = stringPrefix;
}
byte getBytePrefix() {
return bytePrefix;
}
String getStringPrefix() {
return stringPrefix;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.identity;
import io.swagger.v3.oas.annotations.media.Schema;
import org.whispersystems.textsecuregcm.util.UUIDUtil;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HexFormat;
import java.util.UUID;
/**
* An identifier for an account based on the account's phone number identifier (PNI).
*
* @param uuid the account's PNI UUID
*/
@Schema(
type = "string",
description = "An identifier for an account based on the account's phone number identifier (PNI)"
)
public record PniServiceIdentifier(UUID uuid) implements ServiceIdentifier {
private static final IdentityType IDENTITY_TYPE = IdentityType.PNI;
@Override
public IdentityType identityType() {
return IDENTITY_TYPE;
}
@Override
public String toServiceIdentifierString() {
return IDENTITY_TYPE.getStringPrefix() + uuid.toString();
}
@Override
public byte[] toCompactByteArray() {
return toFixedWidthByteArray();
}
@Override
public byte[] toFixedWidthByteArray() {
final ByteBuffer byteBuffer = ByteBuffer.allocate(17);
byteBuffer.put(IDENTITY_TYPE.getBytePrefix());
byteBuffer.putLong(uuid.getMostSignificantBits());
byteBuffer.putLong(uuid.getLeastSignificantBits());
byteBuffer.flip();
return byteBuffer.array();
}
public static PniServiceIdentifier valueOf(final String string) {
if (!string.startsWith(IDENTITY_TYPE.getStringPrefix())) {
throw new IllegalArgumentException("PNI account identifier did not start with \"PNI:\" prefix");
}
return new PniServiceIdentifier(UUID.fromString(string.substring(IDENTITY_TYPE.getStringPrefix().length())));
}
public static PniServiceIdentifier fromBytes(final byte[] bytes) {
if (bytes.length == 17) {
if (bytes[0] != IDENTITY_TYPE.getBytePrefix()) {
throw new IllegalArgumentException("Unexpected byte array prefix: " + HexFormat.of().formatHex(new byte[] { bytes[0] }));
}
return new PniServiceIdentifier(UUIDUtil.fromBytes(Arrays.copyOfRange(bytes, 1, bytes.length)));
}
throw new IllegalArgumentException("Unexpected byte array length: " + bytes.length);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.identity;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.UUID;
/**
* A "service identifier" is a tuple of a UUID and identity type that identifies an account and identity within the
* Signal service.
*/
@Schema(
type = "string",
description = "A service identifier is a tuple of a UUID and identity type that identifies an account and identity within the Signal service.",
subTypes = {AciServiceIdentifier.class, PniServiceIdentifier.class}
)
public interface ServiceIdentifier {
/**
* Returns the identity type of this account identifier.
*
* @return the identity type of this account identifier
*/
IdentityType identityType();
/**
* Returns the UUID for this account identifier.
*
* @return the UUID for this account identifier
*/
UUID uuid();
/**
* Returns a string representation of this account identifier in a format that clients can unambiguously resolve into
* an identity type and UUID.
*
* @return a "strongly-typed" string representation of this account identifier
*/
String toServiceIdentifierString();
/**
* Returns a compact binary representation of this account identifier.
*
* @return a binary representation of this account identifier
*/
byte[] toCompactByteArray();
/**
* Returns a fixed-width binary representation of this account identifier.
*
* @return a binary representation of this account identifier
*/
byte[] toFixedWidthByteArray();
static ServiceIdentifier valueOf(final String string) {
try {
return AciServiceIdentifier.valueOf(string);
} catch (final IllegalArgumentException e) {
return PniServiceIdentifier.valueOf(string);
}
}
static ServiceIdentifier fromBytes(final byte[] bytes) {
try {
return AciServiceIdentifier.fromBytes(bytes);
} catch (final IllegalArgumentException e) {
return PniServiceIdentifier.fromBytes(bytes);
}
}
}