Represent identity keys as byte arrays

This commit is contained in:
Jon Chambers
2023-05-19 10:35:42 -04:00
committed by Jon Chambers
parent 796863341d
commit d832eaa759
32 changed files with 224 additions and 195 deletions

View File

@@ -6,13 +6,19 @@
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import java.util.List;
import java.util.UUID;
public class BaseProfileResponse {
@JsonProperty
private String identityKey;
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
private byte[] identityKey;
@JsonProperty
private String unidentifiedAccess;
@@ -32,7 +38,7 @@ public class BaseProfileResponse {
public BaseProfileResponse() {
}
public BaseProfileResponse(final String identityKey,
public BaseProfileResponse(final byte[] identityKey,
final String unidentifiedAccess,
final boolean unrestrictedUnidentifiedAccess,
final UserCapabilities capabilities,
@@ -47,7 +53,7 @@ public class BaseProfileResponse {
this.uuid = uuid;
}
public String getIdentityKey() {
public byte[] getIdentityKey() {
return identityKey;
}

View File

@@ -15,6 +15,7 @@ import javax.annotation.Nullable;
import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
@@ -36,7 +37,8 @@ public record ChangeNumberRequest(
@JsonProperty("reglock") @Nullable String registrationLock,
@Schema(description="the new public identity key to use for the phone-number identity associated with the new phone number")
@NotBlank String pniIdentityKey,
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@NotEmpty byte[] pniIdentityKey,
@Schema(description="""
A list of synchronization messages to send to companion devices to supply the private keys

View File

@@ -6,7 +6,10 @@
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.v3.oas.annotations.media.Schema;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -27,7 +30,8 @@ public record ChangePhoneNumberRequest(
@JsonProperty("reglock") @Nullable String registrationLock,
@Schema(description="the new public identity key to use for the phone-number identity associated with the new phone number")
@Nullable String pniIdentityKey,
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@Nullable byte[] pniIdentityKey,
@Schema(description="""
A list of synchronization messages to send to companion devices to supply the private keys

View File

@@ -10,17 +10,18 @@ import io.swagger.v3.oas.annotations.media.Schema;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
public record PhoneNumberIdentityKeyDistributionRequest(
@NotBlank
@NotEmpty
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@Schema(description="the new identity key for this account's phone-number identity")
String pniIdentityKey,
byte[] pniIdentityKey,
@NotNull
@Valid

View File

@@ -6,16 +6,19 @@ package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.VisibleForTesting;
import io.swagger.v3.oas.annotations.media.Schema;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import java.util.List;
public class PreKeyResponse {
@JsonProperty
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@Schema(description="the public identity key for the requested identity")
private String identityKey;
private byte[] identityKey;
@JsonProperty
@Schema(description="information about each requested device")
@@ -23,13 +26,13 @@ public class PreKeyResponse {
public PreKeyResponse() {}
public PreKeyResponse(String identityKey, List<PreKeyResponseItem> devices) {
public PreKeyResponse(byte[] identityKey, List<PreKeyResponseItem> devices) {
this.identityKey = identityKey;
this.devices = devices;
}
@VisibleForTesting
public String getIdentityKey() {
public byte[] getIdentityKey() {
return identityKey;
}

View File

@@ -8,7 +8,6 @@ import static com.codahale.metrics.MetricRegistry.name;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Metrics;
import java.util.Base64;
import java.util.Collection;
import org.signal.libsignal.protocol.InvalidKeyException;
import org.signal.libsignal.protocol.ecc.Curve;
@@ -18,9 +17,8 @@ public abstract class PreKeySignatureValidator {
public static final Counter INVALID_SIGNATURE_COUNTER =
Metrics.counter(name(PreKeySignatureValidator.class, "invalidPreKeySignature"));
public static boolean validatePreKeySignatures(final String identityKeyB64, final Collection<SignedPreKey> spks) {
public static boolean validatePreKeySignatures(final byte[] identityKeyBytes, final Collection<SignedPreKey> spks) {
try {
final byte[] identityKeyBytes = Base64.getDecoder().decode(identityKeyB64);
final ECPublicKey identityKey = Curve.decodePoint(identityKeyBytes, 0);
final boolean success = spks.stream()

View File

@@ -5,8 +5,11 @@
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.google.common.annotations.VisibleForTesting;
import io.swagger.v3.oas.annotations.media.Schema;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import java.util.ArrayList;
import java.util.List;
import javax.validation.Valid;
@@ -48,23 +51,24 @@ public class PreKeyState {
private SignedPreKey pqLastResortPreKey;
@JsonProperty
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@NotEmpty
@NotNull
@Schema(description="Required. " +
"The public identity key for this identity (account or phone-number identity). " +
"If this device is not the primary device for the account, " +
"must match the existing stored identity key for this identity.")
private String identityKey;
private byte[] identityKey;
public PreKeyState() {}
@VisibleForTesting
public PreKeyState(String identityKey, SignedPreKey signedPreKey, List<PreKey> keys) {
public PreKeyState(byte[] identityKey, SignedPreKey signedPreKey, List<PreKey> keys) {
this(identityKey, signedPreKey, keys, null, null);
}
@VisibleForTesting
public PreKeyState(String identityKey, SignedPreKey signedPreKey, List<PreKey> keys, List<SignedPreKey> pqKeys, SignedPreKey pqLastResortKey) {
public PreKeyState(byte[] identityKey, SignedPreKey signedPreKey, List<PreKey> keys, List<SignedPreKey> pqKeys, SignedPreKey pqLastResortKey) {
this.identityKey = identityKey;
this.signedPreKey = signedPreKey;
this.preKeys = keys;
@@ -88,7 +92,7 @@ public class PreKeyState {
return pqLastResortPreKey;
}
public String getIdentityKey() {
public byte[] getIdentityKey() {
return identityKey;
}

View File

@@ -9,9 +9,11 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.VisibleForTesting;
import io.swagger.v3.oas.annotations.media.Schema;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import org.whispersystems.textsecuregcm.util.OptionalBase64ByteArrayDeserializer;
import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
@@ -53,14 +55,16 @@ public record RegistrationRequest(@Schema(requiredMode = Schema.RequiredMode.NOT
provided, an account will be created "atomically," and all other properties needed for
atomic account creation must also be present.
""")
Optional<String> aciIdentityKey,
@JsonDeserialize(using = OptionalBase64ByteArrayDeserializer.class)
Optional<byte[]> aciIdentityKey,
@Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = """
The PNI-associated identity key for the account, encoded as a base64 string. If
provided, an account will be created "atomically," and all other properties needed for
atomic account creation must also be present.
""")
Optional<String> pniIdentityKey,
@JsonDeserialize(using = OptionalBase64ByteArrayDeserializer.class)
Optional<byte[]> pniIdentityKey,
@JsonUnwrapped
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@@ -72,8 +76,8 @@ public record RegistrationRequest(@Schema(requiredMode = Schema.RequiredMode.NOT
@JsonProperty("recoveryPassword") byte[] recoveryPassword,
@JsonProperty("accountAttributes") AccountAttributes accountAttributes,
@JsonProperty("skipDeviceTransfer") boolean skipDeviceTransfer,
@JsonProperty("aciIdentityKey") Optional<String> aciIdentityKey,
@JsonProperty("pniIdentityKey") Optional<String> pniIdentityKey,
@JsonProperty("aciIdentityKey") Optional<byte[]> aciIdentityKey,
@JsonProperty("pniIdentityKey") Optional<byte[]> pniIdentityKey,
@JsonProperty("aciSignedPreKey") Optional<@Valid SignedPreKey> aciSignedPreKey,
@JsonProperty("pniSignedPreKey") Optional<@Valid SignedPreKey> pniSignedPreKey,
@JsonProperty("aciPqLastResortPreKey") Optional<@Valid SignedPreKey> aciPqLastResortPreKey,
@@ -97,7 +101,7 @@ public record RegistrationRequest(@Schema(requiredMode = Schema.RequiredMode.NOT
}
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private static boolean validatePreKeySignature(final Optional<String> maybeIdentityKey,
private static boolean validatePreKeySignature(final Optional<byte[]> maybeIdentityKey,
final Optional<SignedPreKey> maybeSignedPreKey) {
return maybeSignedPreKey.map(signedPreKey -> maybeIdentityKey