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

@ -109,8 +109,8 @@ public final class Operations {
registrationPassword, registrationPassword,
accountAttributes, accountAttributes,
true, true,
Optional.of(Base64.getEncoder().encodeToString(aciIdentityKeyPair.getPublicKey().serialize())), Optional.of(aciIdentityKeyPair.getPublicKey().serialize()),
Optional.of(Base64.getEncoder().encodeToString(pniIdentityKeyPair.getPublicKey().serialize())), Optional.of(pniIdentityKeyPair.getPublicKey().serialize()),
Optional.of(generateSignedECPreKey(1, aciIdentityKeyPair)), Optional.of(generateSignedECPreKey(1, aciIdentityKeyPair)),
Optional.of(generateSignedECPreKey(2, pniIdentityKeyPair)), Optional.of(generateSignedECPreKey(2, pniIdentityKeyPair)),
Optional.of(generateSignedKEMPreKey(3, aciIdentityKeyPair)), Optional.of(generateSignedKEMPreKey(3, aciIdentityKeyPair)),

View File

@ -8,7 +8,6 @@ package org.whispersystems.textsecuregcm.auth;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.util.Base64;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECPrivateKey; import org.signal.libsignal.protocol.ecc.ECPrivateKey;
@ -35,7 +34,7 @@ public class CertificateGenerator {
SenderCertificate.Certificate.Builder builder = SenderCertificate.Certificate.newBuilder() SenderCertificate.Certificate.Builder builder = SenderCertificate.Certificate.newBuilder()
.setSenderDevice(Math.toIntExact(device.getId())) .setSenderDevice(Math.toIntExact(device.getId()))
.setExpires(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(expiresDays)) .setExpires(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(expiresDays))
.setIdentityKey(ByteString.copyFrom(Base64.getDecoder().decode(account.getIdentityKey()))) .setIdentityKey(ByteString.copyFrom(account.getIdentityKey()))
.setSigner(serverCertificate) .setSigner(serverCertificate)
.setSenderUuid(account.getUuid().toString()); .setSenderUuid(account.getUuid().toString());

View File

@ -31,6 +31,8 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException; import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.apache.commons.lang3.ArrayUtils;
import org.signal.libsignal.zkgroup.auth.ServerZkAuthOperations; import org.signal.libsignal.zkgroup.auth.ServerZkAuthOperations;
import org.signal.libsignal.zkgroup.calllinks.CallLinkAuthCredentialResponse; import org.signal.libsignal.zkgroup.calllinks.CallLinkAuthCredentialResponse;
import org.signal.libsignal.zkgroup.GenericServerSecretParams; import org.signal.libsignal.zkgroup.GenericServerSecretParams;
@ -38,7 +40,6 @@ import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.CertificateGenerator; import org.whispersystems.textsecuregcm.auth.CertificateGenerator;
import org.whispersystems.textsecuregcm.entities.DeliveryCertificate; import org.whispersystems.textsecuregcm.entities.DeliveryCertificate;
import org.whispersystems.textsecuregcm.entities.GroupCredentials; import org.whispersystems.textsecuregcm.entities.GroupCredentials;
import org.whispersystems.textsecuregcm.util.Util;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Path("/v1/certificate") @Path("/v1/certificate")
@ -74,7 +75,7 @@ public class CertificateController {
@QueryParam("includeE164") @DefaultValue("true") boolean includeE164) @QueryParam("includeE164") @DefaultValue("true") boolean includeE164)
throws InvalidKeyException { throws InvalidKeyException {
if (Util.isEmpty(auth.getAccount().getIdentityKey())) { if (ArrayUtils.isEmpty(auth.getAccount().getIdentityKey())) {
throw new WebApplicationException(Response.Status.BAD_REQUEST); throw new WebApplicationException(Response.Status.BAD_REQUEST);
} }

View File

@ -13,19 +13,14 @@ import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.Tags;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
@ -40,7 +35,8 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException; import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.whispersystems.textsecuregcm.auth.Anonymous; import org.whispersystems.textsecuregcm.auth.Anonymous;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.ChangesDeviceEnabledState; import org.whispersystems.textsecuregcm.auth.ChangesDeviceEnabledState;
@ -120,11 +116,11 @@ public class KeysController {
updateAccount = true; updateAccount = true;
} }
final String oldIdentityKey = usePhoneNumberIdentity ? account.getPhoneNumberIdentityKey() : account.getIdentityKey(); final byte[] oldIdentityKey = usePhoneNumberIdentity ? account.getPhoneNumberIdentityKey() : account.getIdentityKey();
if (!preKeys.getIdentityKey().equals(oldIdentityKey)) { if (!Arrays.equals(preKeys.getIdentityKey(), oldIdentityKey)) {
updateAccount = true; updateAccount = true;
final boolean hasIdentityKey = StringUtils.isNotBlank(oldIdentityKey); final boolean hasIdentityKey = ArrayUtils.isNotEmpty(oldIdentityKey);
final Tags tags = Tags.of(UserAgentTagUtil.getPlatformTag(userAgent)) final Tags tags = Tags.of(UserAgentTagUtil.getPlatformTag(userAgent))
.and(HAS_IDENTITY_KEY_TAG_NAME, String.valueOf(hasIdentityKey)) .and(HAS_IDENTITY_KEY_TAG_NAME, String.valueOf(hasIdentityKey))
.and(IDENTITY_TYPE_TAG_NAME, usePhoneNumberIdentity ? "pni" : "aci"); .and(IDENTITY_TYPE_TAG_NAME, usePhoneNumberIdentity ? "pni" : "aci");
@ -183,7 +179,7 @@ public class KeysController {
@HeaderParam(HttpHeaders.USER_AGENT) String userAgent) @HeaderParam(HttpHeaders.USER_AGENT) String userAgent)
throws RateLimitExceededException { throws RateLimitExceededException {
if (!auth.isPresent() && !accessKey.isPresent()) { if (auth.isEmpty() && accessKey.isEmpty()) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED); throw new WebApplicationException(Response.Status.UNAUTHORIZED);
} }
@ -225,7 +221,7 @@ public class KeysController {
} }
} }
final String identityKey = usePhoneNumberIdentity ? target.getPhoneNumberIdentityKey() : target.getIdentityKey(); final byte[] identityKey = usePhoneNumberIdentity ? target.getPhoneNumberIdentityKey() : target.getIdentityKey();
if (responseItems.isEmpty()) { if (responseItems.isEmpty()) {
return Response.status(404).build(); return Response.status(404).build();

View File

@ -383,13 +383,8 @@ public class ProfileController {
if (account.getIdentityKey() == null || account.getPhoneNumberIdentityKey() == null) { if (account.getIdentityKey() == null || account.getPhoneNumberIdentityKey() == null) {
return; return;
} }
byte[] identityKeyBytes; final byte[] identityKeyBytes =
try { usePhoneNumberIdentity ? account.getPhoneNumberIdentityKey() : account.getIdentityKey();
identityKeyBytes = Base64.getDecoder().decode(usePhoneNumberIdentity ? account.getPhoneNumberIdentityKey()
: account.getIdentityKey());
} catch (IllegalArgumentException ignored) {
return;
}
md.reset(); md.reset();
byte[] digest = md.digest(identityKeyBytes); byte[] digest = md.digest(identityKeyBytes);
byte[] fingerprint = Util.truncate(digest, 4); byte[] fingerprint = Util.truncate(digest, 4);

View File

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

View File

@ -15,6 +15,7 @@ import javax.annotation.Nullable;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.AssertTrue; import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter; import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
@ -36,7 +37,8 @@ public record ChangeNumberRequest(
@JsonProperty("reglock") @Nullable String registrationLock, @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") @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=""" @Schema(description="""
A list of synchronization messages to send to companion devices to supply the private keys 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; package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -27,7 +30,8 @@ public record ChangePhoneNumberRequest(
@JsonProperty("reglock") @Nullable String registrationLock, @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") @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=""" @Schema(description="""
A list of synchronization messages to send to companion devices to supply the private keys 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.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.AssertTrue; import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter; import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
public record PhoneNumberIdentityKeyDistributionRequest( public record PhoneNumberIdentityKeyDistributionRequest(
@NotBlank @NotEmpty
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@Schema(description="the new identity key for this account's phone-number identity") @Schema(description="the new identity key for this account's phone-number identity")
String pniIdentityKey, byte[] pniIdentityKey,
@NotNull @NotNull
@Valid @Valid

View File

@ -6,16 +6,19 @@ package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import java.util.List; import java.util.List;
public class PreKeyResponse { public class PreKeyResponse {
@JsonProperty @JsonProperty
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@Schema(description="the public identity key for the requested identity") @Schema(description="the public identity key for the requested identity")
private String identityKey; private byte[] identityKey;
@JsonProperty @JsonProperty
@Schema(description="information about each requested device") @Schema(description="information about each requested device")
@ -23,13 +26,13 @@ public class PreKeyResponse {
public PreKeyResponse() {} public PreKeyResponse() {}
public PreKeyResponse(String identityKey, List<PreKeyResponseItem> devices) { public PreKeyResponse(byte[] identityKey, List<PreKeyResponseItem> devices) {
this.identityKey = identityKey; this.identityKey = identityKey;
this.devices = devices; this.devices = devices;
} }
@VisibleForTesting @VisibleForTesting
public String getIdentityKey() { public byte[] getIdentityKey() {
return identityKey; 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.Counter;
import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Metrics;
import java.util.Base64;
import java.util.Collection; import java.util.Collection;
import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.InvalidKeyException;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
@ -18,9 +17,8 @@ public abstract class PreKeySignatureValidator {
public static final Counter INVALID_SIGNATURE_COUNTER = public static final Counter INVALID_SIGNATURE_COUNTER =
Metrics.counter(name(PreKeySignatureValidator.class, "invalidPreKeySignature")); 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 { try {
final byte[] identityKeyBytes = Base64.getDecoder().decode(identityKeyB64);
final ECPublicKey identityKey = Curve.decodePoint(identityKeyBytes, 0); final ECPublicKey identityKey = Curve.decodePoint(identityKeyBytes, 0);
final boolean success = spks.stream() final boolean success = spks.stream()

View File

@ -5,8 +5,11 @@
package org.whispersystems.textsecuregcm.entities; package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.validation.Valid; import javax.validation.Valid;
@ -48,23 +51,24 @@ public class PreKeyState {
private SignedPreKey pqLastResortPreKey; private SignedPreKey pqLastResortPreKey;
@JsonProperty @JsonProperty
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@NotEmpty @NotEmpty
@NotNull @NotNull
@Schema(description="Required. " + @Schema(description="Required. " +
"The public identity key for this identity (account or phone-number identity). " + "The public identity key for this identity (account or phone-number identity). " +
"If this device is not the primary device for the account, " + "If this device is not the primary device for the account, " +
"must match the existing stored identity key for this identity.") "must match the existing stored identity key for this identity.")
private String identityKey; private byte[] identityKey;
public PreKeyState() {} public PreKeyState() {}
@VisibleForTesting @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); this(identityKey, signedPreKey, keys, null, null);
} }
@VisibleForTesting @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.identityKey = identityKey;
this.signedPreKey = signedPreKey; this.signedPreKey = signedPreKey;
this.preKeys = keys; this.preKeys = keys;
@ -88,7 +92,7 @@ public class PreKeyState {
return pqLastResortPreKey; return pqLastResortPreKey;
} }
public String getIdentityKey() { public byte[] getIdentityKey() {
return identityKey; 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.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped; import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter; import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import org.whispersystems.textsecuregcm.util.OptionalBase64ByteArrayDeserializer;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.AssertTrue; 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 provided, an account will be created "atomically," and all other properties needed for
atomic account creation must also be present. atomic account creation must also be present.
""") """)
Optional<String> aciIdentityKey, @JsonDeserialize(using = OptionalBase64ByteArrayDeserializer.class)
Optional<byte[]> aciIdentityKey,
@Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = """ @Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = """
The PNI-associated identity key for the account, encoded as a base64 string. If 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 provided, an account will be created "atomically," and all other properties needed for
atomic account creation must also be present. atomic account creation must also be present.
""") """)
Optional<String> pniIdentityKey, @JsonDeserialize(using = OptionalBase64ByteArrayDeserializer.class)
Optional<byte[]> pniIdentityKey,
@JsonUnwrapped @JsonUnwrapped
@JsonProperty(access = JsonProperty.Access.READ_ONLY) @JsonProperty(access = JsonProperty.Access.READ_ONLY)
@ -72,8 +76,8 @@ public record RegistrationRequest(@Schema(requiredMode = Schema.RequiredMode.NOT
@JsonProperty("recoveryPassword") byte[] recoveryPassword, @JsonProperty("recoveryPassword") byte[] recoveryPassword,
@JsonProperty("accountAttributes") AccountAttributes accountAttributes, @JsonProperty("accountAttributes") AccountAttributes accountAttributes,
@JsonProperty("skipDeviceTransfer") boolean skipDeviceTransfer, @JsonProperty("skipDeviceTransfer") boolean skipDeviceTransfer,
@JsonProperty("aciIdentityKey") Optional<String> aciIdentityKey, @JsonProperty("aciIdentityKey") Optional<byte[]> aciIdentityKey,
@JsonProperty("pniIdentityKey") Optional<String> pniIdentityKey, @JsonProperty("pniIdentityKey") Optional<byte[]> pniIdentityKey,
@JsonProperty("aciSignedPreKey") Optional<@Valid SignedPreKey> aciSignedPreKey, @JsonProperty("aciSignedPreKey") Optional<@Valid SignedPreKey> aciSignedPreKey,
@JsonProperty("pniSignedPreKey") Optional<@Valid SignedPreKey> pniSignedPreKey, @JsonProperty("pniSignedPreKey") Optional<@Valid SignedPreKey> pniSignedPreKey,
@JsonProperty("aciPqLastResortPreKey") Optional<@Valid SignedPreKey> aciPqLastResortPreKey, @JsonProperty("aciPqLastResortPreKey") Optional<@Valid SignedPreKey> aciPqLastResortPreKey,
@ -97,7 +101,7 @@ public record RegistrationRequest(@Schema(requiredMode = Schema.RequiredMode.NOT
} }
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private static boolean validatePreKeySignature(final Optional<String> maybeIdentityKey, private static boolean validatePreKeySignature(final Optional<byte[]> maybeIdentityKey,
final Optional<SignedPreKey> maybeSignedPreKey) { final Optional<SignedPreKey> maybeSignedPreKey) {
return maybeSignedPreKey.map(signedPreKey -> maybeIdentityKey return maybeSignedPreKey.map(signedPreKey -> maybeIdentityKey

View File

@ -24,6 +24,7 @@ import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock; import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
import org.whispersystems.textsecuregcm.entities.AccountAttributes; import org.whispersystems.textsecuregcm.entities.AccountAttributes;
import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities; import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import org.whispersystems.textsecuregcm.util.ByteArrayBase64UrlAdapter; import org.whispersystems.textsecuregcm.util.ByteArrayBase64UrlAdapter;
import org.whispersystems.textsecuregcm.util.Util; import org.whispersystems.textsecuregcm.util.Util;
@ -57,10 +58,14 @@ public class Account {
private List<Device> devices = new ArrayList<>(); private List<Device> devices = new ArrayList<>();
@JsonProperty @JsonProperty
private String identityKey; @JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
private byte[] identityKey;
@JsonProperty("pniIdentityKey") @JsonProperty("pniIdentityKey")
private String phoneNumberIdentityKey; @JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
private byte[] phoneNumberIdentityKey;
@JsonProperty("cpv") @JsonProperty("cpv")
private String currentProfileVersion; private String currentProfileVersion;
@ -282,23 +287,23 @@ public class Account {
this.canonicallyDiscoverable = canonicallyDiscoverable; this.canonicallyDiscoverable = canonicallyDiscoverable;
} }
public void setIdentityKey(String identityKey) { public void setIdentityKey(byte[] identityKey) {
requireNotStale(); requireNotStale();
this.identityKey = identityKey; this.identityKey = identityKey;
} }
public String getIdentityKey() { public byte[] getIdentityKey() {
requireNotStale(); requireNotStale();
return identityKey; return identityKey;
} }
public String getPhoneNumberIdentityKey() { public byte[] getPhoneNumberIdentityKey() {
return phoneNumberIdentityKey; return phoneNumberIdentityKey;
} }
public void setPhoneNumberIdentityKey(final String phoneNumberIdentityKey) { public void setPhoneNumberIdentityKey(final byte[] phoneNumberIdentityKey) {
this.phoneNumberIdentityKey = phoneNumberIdentityKey; this.phoneNumberIdentityKey = phoneNumberIdentityKey;
} }

View File

@ -28,7 +28,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -242,11 +241,12 @@ public class AccountsManager {
} }
} }
public Account changeNumber(final Account account, final String number, public Account changeNumber(final Account account,
@Nullable final String pniIdentityKey, final String number,
@Nullable final Map<Long, SignedPreKey> pniSignedPreKeys, @Nullable final byte[] pniIdentityKey,
@Nullable final Map<Long, SignedPreKey> pniPqLastResortPreKeys, @Nullable final Map<Long, SignedPreKey> pniSignedPreKeys,
@Nullable final Map<Long, Integer> pniRegistrationIds) throws InterruptedException, MismatchedDevicesException { @Nullable final Map<Long, SignedPreKey> pniPqLastResortPreKeys,
@Nullable final Map<Long, Integer> pniRegistrationIds) throws InterruptedException, MismatchedDevicesException {
final String originalNumber = account.getNumber(); final String originalNumber = account.getNumber();
final UUID originalPhoneNumberIdentifier = account.getPhoneNumberIdentifier(); final UUID originalPhoneNumberIdentifier = account.getPhoneNumberIdentifier();
@ -308,7 +308,7 @@ public class AccountsManager {
} }
public Account updatePniKeys(final Account account, public Account updatePniKeys(final Account account,
final String pniIdentityKey, final byte[] pniIdentityKey,
final Map<Long, SignedPreKey> pniSignedPreKeys, final Map<Long, SignedPreKey> pniSignedPreKeys,
@Nullable final Map<Long, SignedPreKey> pniPqLastResortPreKeys, @Nullable final Map<Long, SignedPreKey> pniPqLastResortPreKeys,
final Map<Long, Integer> pniRegistrationIds) throws MismatchedDevicesException { final Map<Long, Integer> pniRegistrationIds) throws MismatchedDevicesException {
@ -327,7 +327,7 @@ public class AccountsManager {
} }
private boolean setPniKeys(final Account account, private boolean setPniKeys(final Account account,
@Nullable final String pniIdentityKey, @Nullable final byte[] pniIdentityKey,
@Nullable final Map<Long, SignedPreKey> pniSignedPreKeys, @Nullable final Map<Long, SignedPreKey> pniSignedPreKeys,
@Nullable final Map<Long, Integer> pniRegistrationIds) { @Nullable final Map<Long, Integer> pniRegistrationIds) {
if (ObjectUtils.allNull(pniIdentityKey, pniSignedPreKeys, pniRegistrationIds)) { if (ObjectUtils.allNull(pniIdentityKey, pniSignedPreKeys, pniRegistrationIds)) {
@ -336,7 +336,7 @@ public class AccountsManager {
throw new IllegalArgumentException("PNI identity key, signed pre-keys, and registration IDs must be all null or all non-null"); throw new IllegalArgumentException("PNI identity key, signed pre-keys, and registration IDs must be all null or all non-null");
} }
boolean changed = !pniIdentityKey.equals(account.getPhoneNumberIdentityKey()); boolean changed = !Arrays.equals(pniIdentityKey, account.getPhoneNumberIdentityKey());
for (Device device : account.getDevices()) { for (Device device : account.getDevices()) {
if (!device.isEnabled()) { if (!device.isEnabled()) {

View File

@ -5,7 +5,6 @@
package org.whispersystems.textsecuregcm.storage; package org.whispersystems.textsecuregcm.storage;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -40,7 +39,7 @@ public class ChangeNumberManager {
} }
public Account changeNumber(final Account account, final String number, public Account changeNumber(final Account account, final String number,
@Nullable final String pniIdentityKey, @Nullable final byte[] pniIdentityKey,
@Nullable final Map<Long, SignedPreKey> deviceSignedPreKeys, @Nullable final Map<Long, SignedPreKey> deviceSignedPreKeys,
@Nullable final Map<Long, SignedPreKey> devicePqLastResortPreKeys, @Nullable final Map<Long, SignedPreKey> devicePqLastResortPreKeys,
@Nullable final List<IncomingMessage> deviceMessages, @Nullable final List<IncomingMessage> deviceMessages,
@ -80,7 +79,7 @@ public class ChangeNumberManager {
} }
public Account updatePniKeys(final Account account, public Account updatePniKeys(final Account account,
final String pniIdentityKey, final byte[] pniIdentityKey,
final Map<Long, SignedPreKey> deviceSignedPreKeys, final Map<Long, SignedPreKey> deviceSignedPreKeys,
@Nullable final Map<Long, SignedPreKey> devicePqLastResortPreKeys, @Nullable final Map<Long, SignedPreKey> devicePqLastResortPreKeys,
final List<IncomingMessage> deviceMessages, final List<IncomingMessage> deviceMessages,

View File

@ -31,4 +31,3 @@ public class ByteArrayAdapter {
} }
} }
} }

View File

@ -0,0 +1,22 @@
package org.whispersystems.textsecuregcm.util;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.util.Base64;
import java.util.Optional;
public class OptionalBase64ByteArrayDeserializer extends JsonDeserializer<Optional<byte[]>> {
@Override
public Optional<byte[]> deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException {
return Optional.of(Base64.getDecoder().decode(jsonParser.getValueAsString()));
}
@Override
public Optional<byte[]> getNullValue(DeserializationContext ctxt) {
return Optional.empty();
}
}

View File

@ -21,8 +21,8 @@ import org.whispersystems.textsecuregcm.storage.Device;
class CertificateGeneratorTest { class CertificateGeneratorTest {
private static final String SIGNING_CERTIFICATE = "CiUIDBIhBbTz4h1My+tt+vw+TVscgUe/DeHS0W02tPWAWbTO2xc3EkD+go4bJnU0AcnFfbOLKoiBfCzouZtDYMOVi69rE7r4U9cXREEqOkUmU2WJBjykAxWPCcSTmVTYHDw7hkSp/puG"; private static final String SIGNING_CERTIFICATE = "CiUIDBIhBbTz4h1My+tt+vw+TVscgUe/DeHS0W02tPWAWbTO2xc3EkD+go4bJnU0AcnFfbOLKoiBfCzouZtDYMOVi69rE7r4U9cXREEqOkUmU2WJBjykAxWPCcSTmVTYHDw7hkSp/puG";
private static final String SIGNING_KEY = "ABOxG29xrfq4E7IrW11Eg7+HBbtba9iiS0500YoBjn4="; private static final String SIGNING_KEY = "ABOxG29xrfq4E7IrW11Eg7+HBbtba9iiS0500YoBjn4=";
private static final String IDENTITY_KEY = "BcxxDU9FGMda70E7+Uvm7pnQcEdXQ64aJCpPUeRSfcFo"; private static final byte[] IDENTITY_KEY = Base64.getDecoder().decode("BcxxDU9FGMda70E7+Uvm7pnQcEdXQ64aJCpPUeRSfcFo");
@Test @Test
void testCreateFor() throws IOException, InvalidKeyException, org.signal.libsignal.protocol.InvalidKeyException { void testCreateFor() throws IOException, InvalidKeyException, org.signal.libsignal.protocol.InvalidKeyException {

View File

@ -334,9 +334,9 @@ class AccountControllerTest {
}); });
when(changeNumberManager.changeNumber(any(), any(), any(), any(), any(), any(), any())).thenAnswer((Answer<Account>) invocation -> { when(changeNumberManager.changeNumber(any(), any(), any(), any(), any(), any(), any())).thenAnswer((Answer<Account>) invocation -> {
final Account account = invocation.getArgument(0, Account.class); final Account account = invocation.getArgument(0);
final String number = invocation.getArgument(1, String.class); final String number = invocation.getArgument(1);
final String pniIdentityKey = invocation.getArgument(2, String.class); final byte[] pniIdentityKey = invocation.getArgument(2);
final UUID uuid = account.getUuid(); final UUID uuid = account.getUuid();
final UUID pni = number.equals(account.getNumber()) ? account.getPhoneNumberIdentifier() : UUID.randomUUID(); final UUID pni = number.equals(account.getNumber()) ? account.getPhoneNumberIdentifier() : UUID.randomUUID();
@ -358,8 +358,8 @@ class AccountControllerTest {
}); });
when(changeNumberManager.updatePniKeys(any(), any(), any(), any(), any(), any())).thenAnswer((Answer<Account>) invocation -> { when(changeNumberManager.updatePniKeys(any(), any(), any(), any(), any(), any())).thenAnswer((Answer<Account>) invocation -> {
final Account account = invocation.getArgument(0, Account.class); final Account account = invocation.getArgument(0);
final String pniIdentityKey = invocation.getArgument(1, String.class); final byte[] pniIdentityKey = invocation.getArgument(1);
final String number = account.getNumber(); final String number = account.getNumber();
final UUID uuid = account.getUuid(); final UUID uuid = account.getUuid();
@ -1641,7 +1641,7 @@ class AccountControllerTest {
final String number = "+18005559876"; final String number = "+18005559876";
final String code = "987654"; final String code = "987654";
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair(); final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
final String pniIdentityKey = KeysHelper.serializeIdentityKey(pniIdentityKeyPair); final byte[] pniIdentityKey = pniIdentityKeyPair.getPublicKey().serialize();
final byte[] sessionId = "session-id".getBytes(StandardCharsets.UTF_8); final byte[] sessionId = "session-id".getBytes(StandardCharsets.UTF_8);
Device device2 = mock(Device.class); Device device2 = mock(Device.class);
@ -1695,7 +1695,7 @@ class AccountControllerTest {
void testChangePhoneNumberSameNumberChangePrekeys() throws Exception { void testChangePhoneNumberSameNumberChangePrekeys() throws Exception {
final String code = "987654"; final String code = "987654";
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair(); final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
final String pniIdentityKey = KeysHelper.serializeIdentityKey(pniIdentityKeyPair); final byte[] pniIdentityKey = pniIdentityKeyPair.getPublicKey().serialize();
final byte[] sessionId = "session-id".getBytes(StandardCharsets.UTF_8); final byte[] sessionId = "session-id".getBytes(StandardCharsets.UTF_8);
Device device2 = mock(Device.class); Device device2 = mock(Device.class);

View File

@ -138,9 +138,9 @@ class AccountControllerV2Test {
when(changeNumberManager.changeNumber(any(), any(), any(), any(), any(), any(), any())).thenAnswer( when(changeNumberManager.changeNumber(any(), any(), any(), any(), any(), any(), any())).thenAnswer(
(Answer<Account>) invocation -> { (Answer<Account>) invocation -> {
final Account account = invocation.getArgument(0, Account.class); final Account account = invocation.getArgument(0);
final String number = invocation.getArgument(1, String.class); final String number = invocation.getArgument(1);
final String pniIdentityKey = invocation.getArgument(2, String.class); final byte[] pniIdentityKey = invocation.getArgument(2);
final UUID uuid = account.getUuid(); final UUID uuid = account.getUuid();
final List<Device> devices = account.getDevices(); final List<Device> devices = account.getDevices();
@ -180,7 +180,7 @@ class AccountControllerV2Test {
.header(HttpHeaders.AUTHORIZATION, .header(HttpHeaders.AUTHORIZATION,
AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity( .put(Entity.entity(
new ChangeNumberRequest(encodeSessionId("session"), null, NEW_NUMBER, "123", "123", new ChangeNumberRequest(encodeSessionId("session"), null, NEW_NUMBER, "123", "123".getBytes(StandardCharsets.UTF_8),
Collections.emptyList(), Collections.emptyList(),
Collections.emptyMap(), null, Collections.emptyMap()), Collections.emptyMap(), null, Collections.emptyMap()),
MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class); MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class);
@ -203,7 +203,7 @@ class AccountControllerV2Test {
AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity( .put(Entity.entity(
new ChangeNumberRequest(encodeSessionId("session"), null, AuthHelper.VALID_NUMBER, null, new ChangeNumberRequest(encodeSessionId("session"), null, AuthHelper.VALID_NUMBER, null,
"pni-identity-key", "pni-identity-key".getBytes(StandardCharsets.UTF_8),
Collections.emptyList(), Collections.emptyList(),
Collections.emptyMap(), null, Collections.emptyMap()), Collections.emptyMap(), null, Collections.emptyMap()),
MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class); MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class);
@ -462,8 +462,8 @@ class AccountControllerV2Test {
void setUp() throws Exception { void setUp() throws Exception {
when(changeNumberManager.updatePniKeys(any(), any(), any(), any(), any(), any())).thenAnswer( when(changeNumberManager.updatePniKeys(any(), any(), any(), any(), any(), any())).thenAnswer(
(Answer<Account>) invocation -> { (Answer<Account>) invocation -> {
final Account account = invocation.getArgument(0, Account.class); final Account account = invocation.getArgument(0);
final String pniIdentityKey = invocation.getArgument(1, String.class); final byte[] pniIdentityKey = invocation.getArgument(1);
final UUID uuid = account.getUuid(); final UUID uuid = account.getUuid();
final UUID pni = account.getPhoneNumberIdentifier(); final UUID pni = account.getPhoneNumberIdentifier();
@ -498,7 +498,7 @@ class AccountControllerV2Test {
AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(requestJson()), AccountIdentityResponse.class); .put(Entity.json(requestJson()), AccountIdentityResponse.class);
verify(changeNumberManager).updatePniKeys(eq(AuthHelper.VALID_ACCOUNT), eq("pni-identity-key"), any(), any(), any(), any()); verify(changeNumberManager).updatePniKeys(eq(AuthHelper.VALID_ACCOUNT), eq("pni-identity-key".getBytes(StandardCharsets.UTF_8)), any(), any(), any(), any());
assertEquals(AuthHelper.VALID_UUID, accountIdentityResponse.uuid()); assertEquals(AuthHelper.VALID_UUID, accountIdentityResponse.uuid());
assertEquals(AuthHelper.VALID_NUMBER, accountIdentityResponse.number()); assertEquals(AuthHelper.VALID_NUMBER, accountIdentityResponse.number());
@ -554,15 +554,15 @@ class AccountControllerV2Test {
* Valid request JSON for a {@link org.whispersystems.textsecuregcm.entities.PhoneNumberIdentityKeyDistributionRequest} * Valid request JSON for a {@link org.whispersystems.textsecuregcm.entities.PhoneNumberIdentityKeyDistributionRequest}
*/ */
private static String requestJson() { private static String requestJson() {
return """ return String.format("""
{ {
"pniIdentityKey": "pni-identity-key", "pniIdentityKey": "%s",
"deviceMessages": [], "deviceMessages": [],
"devicePniSignedPrekeys": {}, "devicePniSignedPrekeys": {},
"devicePniSignedPqPrekeys": {}, "devicePniSignedPqPrekeys": {},
"pniRegistrationIds": {} "pniRegistrationIds": {}
} }
"""; """, Base64.getEncoder().encodeToString("pni-identity-key".getBytes(StandardCharsets.UTF_8)));
} }
/** /**
@ -798,8 +798,8 @@ class AccountControllerV2Test {
account.setUnrestrictedUnidentifiedAccess(unrestrictedUnidentifiedAccess); account.setUnrestrictedUnidentifiedAccess(unrestrictedUnidentifiedAccess);
account.setDiscoverableByPhoneNumber(discoverableByPhoneNumber); account.setDiscoverableByPhoneNumber(discoverableByPhoneNumber);
account.setBadges(Clock.systemUTC(), new ArrayList<>(badges)); account.setBadges(Clock.systemUTC(), new ArrayList<>(badges));
account.setIdentityKey(KeysHelper.serializeIdentityKey(aciIdentityKeyPair)); account.setIdentityKey(aciIdentityKeyPair.getPublicKey().serialize());
account.setPhoneNumberIdentityKey(KeysHelper.serializeIdentityKey(pniIdentityKeyPair)); account.setPhoneNumberIdentityKey(pniIdentityKeyPair.getPublicKey().serialize());
assert !devices.isEmpty(); assert !devices.isEmpty();

View File

@ -124,10 +124,10 @@ class ProfileControllerTest {
private static final ServerZkProfileOperations zkProfileOperations = mock(ServerZkProfileOperations.class); private static final ServerZkProfileOperations zkProfileOperations = mock(ServerZkProfileOperations.class);
private static final byte[] UNIDENTIFIED_ACCESS_KEY = "test-uak".getBytes(StandardCharsets.UTF_8); private static final byte[] UNIDENTIFIED_ACCESS_KEY = "test-uak".getBytes(StandardCharsets.UTF_8);
private static final String ACCOUNT_IDENTITY_KEY = "barz"; private static final byte[] ACCOUNT_IDENTITY_KEY = "barz".getBytes(StandardCharsets.UTF_8);
private static final String ACCOUNT_PHONE_NUMBER_IDENTITY_KEY = "bazz"; private static final byte[] ACCOUNT_PHONE_NUMBER_IDENTITY_KEY = "bazz".getBytes(StandardCharsets.UTF_8);
private static final String ACCOUNT_TWO_IDENTITY_KEY = "bar"; private static final byte[] ACCOUNT_TWO_IDENTITY_KEY = "bar".getBytes(StandardCharsets.UTF_8);
private static final String ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY = "baz"; private static final byte[] ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY = "baz".getBytes(StandardCharsets.UTF_8);
private static final String BASE_64_URL_USERNAME_HASH = "9p6Tip7BFefFOJzv4kv4GyXEYsBVfk_WbjNejdlOvQE"; private static final String BASE_64_URL_USERNAME_HASH = "9p6Tip7BFefFOJzv4kv4GyXEYsBVfk_WbjNejdlOvQE";
private static final byte[] USERNAME_HASH = Base64.getUrlDecoder().decode(BASE_64_URL_USERNAME_HASH); private static final byte[] USERNAME_HASH = Base64.getUrlDecoder().decode(BASE_64_URL_USERNAME_HASH);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -1153,13 +1153,13 @@ class ProfileControllerTest {
try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request() try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request()
.post(Entity.json(new BatchIdentityCheckRequest(List.of( .post(Entity.json(new BatchIdentityCheckRequest(List.of(
new BatchIdentityCheckRequest.Element(AuthHelper.VALID_UUID, null, new BatchIdentityCheckRequest.Element(AuthHelper.VALID_UUID, null,
convertStringToFingerprint(ACCOUNT_IDENTITY_KEY)), convertKeyToFingerprint(ACCOUNT_IDENTITY_KEY)),
new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_PNI_TWO, new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_PNI_TWO,
convertStringToFingerprint(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY)), convertKeyToFingerprint(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY)),
new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_UUID_TWO, new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_UUID_TWO,
convertStringToFingerprint(ACCOUNT_TWO_IDENTITY_KEY)), convertKeyToFingerprint(ACCOUNT_TWO_IDENTITY_KEY)),
new BatchIdentityCheckRequest.Element(AuthHelper.INVALID_UUID, null, new BatchIdentityCheckRequest.Element(AuthHelper.INVALID_UUID, null,
convertStringToFingerprint(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY)) convertKeyToFingerprint(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY))
))))) { ))))) {
assertThat(response).isNotNull(); assertThat(response).isNotNull();
assertThat(response.getStatus()).isEqualTo(200); assertThat(response.getStatus()).isEqualTo(200);
@ -1170,11 +1170,11 @@ class ProfileControllerTest {
final Condition<BatchIdentityCheckResponse.Element> isAnExpectedUuid = new Condition<>(element -> { final Condition<BatchIdentityCheckResponse.Element> isAnExpectedUuid = new Condition<>(element -> {
if (AuthHelper.VALID_UUID.equals(element.aci())) { if (AuthHelper.VALID_UUID.equals(element.aci())) {
return Arrays.equals(Base64.getDecoder().decode(ACCOUNT_IDENTITY_KEY), element.identityKey()); return Arrays.equals(ACCOUNT_IDENTITY_KEY, element.identityKey());
} else if (AuthHelper.VALID_PNI_TWO.equals(element.uuid())) { } else if (AuthHelper.VALID_PNI_TWO.equals(element.uuid())) {
return Arrays.equals(Base64.getDecoder().decode(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY), element.identityKey()); return Arrays.equals(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY, element.identityKey());
} else if (AuthHelper.VALID_UUID_TWO.equals(element.uuid())) { } else if (AuthHelper.VALID_UUID_TWO.equals(element.uuid())) {
return Arrays.equals(Base64.getDecoder().decode(ACCOUNT_TWO_IDENTITY_KEY), element.identityKey()); return Arrays.equals(ACCOUNT_TWO_IDENTITY_KEY, element.identityKey());
} else { } else {
return false; return false;
} }
@ -1182,12 +1182,14 @@ class ProfileControllerTest {
try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request() try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request()
.post(Entity.json(new BatchIdentityCheckRequest(List.of( .post(Entity.json(new BatchIdentityCheckRequest(List.of(
new BatchIdentityCheckRequest.Element(AuthHelper.VALID_UUID, null, convertStringToFingerprint("else1234")), new BatchIdentityCheckRequest.Element(AuthHelper.VALID_UUID, null,
convertKeyToFingerprint("else1234".getBytes(StandardCharsets.UTF_8))),
new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_PNI_TWO, new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_PNI_TWO,
convertStringToFingerprint("another1")), convertKeyToFingerprint("another1".getBytes(StandardCharsets.UTF_8))),
new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_UUID_TWO, new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_UUID_TWO,
convertStringToFingerprint("another2")), convertKeyToFingerprint("another2".getBytes(StandardCharsets.UTF_8))),
new BatchIdentityCheckRequest.Element(AuthHelper.INVALID_UUID, null, convertStringToFingerprint("456")) new BatchIdentityCheckRequest.Element(AuthHelper.INVALID_UUID, null,
convertKeyToFingerprint("456".getBytes(StandardCharsets.UTF_8)))
))))) { ))))) {
assertThat(response).isNotNull(); assertThat(response).isNotNull();
assertThat(response.getStatus()).isEqualTo(200); assertThat(response.getStatus()).isEqualTo(200);
@ -1200,13 +1202,13 @@ class ProfileControllerTest {
} }
final List<BatchIdentityCheckRequest.Element> largeElementList = new ArrayList<>(List.of( final List<BatchIdentityCheckRequest.Element> largeElementList = new ArrayList<>(List.of(
new BatchIdentityCheckRequest.Element(AuthHelper.VALID_UUID, null, convertStringToFingerprint("else1234")), new BatchIdentityCheckRequest.Element(AuthHelper.VALID_UUID, null, convertKeyToFingerprint("else1234".getBytes(StandardCharsets.UTF_8))),
new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_PNI_TWO, convertStringToFingerprint("another1")), new BatchIdentityCheckRequest.Element(null, AuthHelper.VALID_PNI_TWO, convertKeyToFingerprint("another1".getBytes(StandardCharsets.UTF_8))),
new BatchIdentityCheckRequest.Element(AuthHelper.INVALID_UUID, null, convertStringToFingerprint("456")))); new BatchIdentityCheckRequest.Element(AuthHelper.INVALID_UUID, null, convertKeyToFingerprint("456".getBytes(StandardCharsets.UTF_8)))));
for (int i = 0; i < 900; i++) { for (int i = 0; i < 900; i++) {
largeElementList.add( largeElementList.add(
new BatchIdentityCheckRequest.Element(UUID.randomUUID(), null, convertStringToFingerprint("abcd"))); new BatchIdentityCheckRequest.Element(UUID.randomUUID(), null, convertKeyToFingerprint("abcd".getBytes(StandardCharsets.UTF_8))));
} }
try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request() try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request()
@ -1226,9 +1228,9 @@ class ProfileControllerTest {
final Condition<BatchIdentityCheckResponse.Element> isAnExpectedUuid = new Condition<>(element -> { final Condition<BatchIdentityCheckResponse.Element> isAnExpectedUuid = new Condition<>(element -> {
if (AuthHelper.VALID_UUID.equals(element.aci())) { if (AuthHelper.VALID_UUID.equals(element.aci())) {
return Arrays.equals(Base64.getDecoder().decode(ACCOUNT_IDENTITY_KEY), element.identityKey()); return Arrays.equals(ACCOUNT_IDENTITY_KEY, element.identityKey());
} else if (AuthHelper.VALID_PNI_TWO.equals(element.uuid())) { } else if (AuthHelper.VALID_PNI_TWO.equals(element.uuid())) {
return Arrays.equals(Base64.getDecoder().decode(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY), element.identityKey()); return Arrays.equals(ACCOUNT_TWO_PHONE_NUMBER_IDENTITY_KEY, element.identityKey());
} else { } else {
return false; return false;
} }
@ -1243,9 +1245,9 @@ class ProfileControllerTest {
{ "aci": "%s", "fingerprint": "%s" } { "aci": "%s", "fingerprint": "%s" }
] ]
} }
""", AuthHelper.VALID_UUID, Base64.getEncoder().encodeToString(convertStringToFingerprint("else1234")), """, AuthHelper.VALID_UUID, Base64.getEncoder().encodeToString(convertKeyToFingerprint("else1234".getBytes(StandardCharsets.UTF_8))),
AuthHelper.VALID_PNI_TWO, Base64.getEncoder().encodeToString(convertStringToFingerprint("another1")), AuthHelper.VALID_PNI_TWO, Base64.getEncoder().encodeToString(convertKeyToFingerprint("another1".getBytes(StandardCharsets.UTF_8))),
AuthHelper.INVALID_UUID, Base64.getEncoder().encodeToString(convertStringToFingerprint("456"))); AuthHelper.INVALID_UUID, Base64.getEncoder().encodeToString(convertKeyToFingerprint("456".getBytes(StandardCharsets.UTF_8))));
try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request() try (final Response response = resources.getJerseyTest().target("/v1/profile/identity_check/batch").request()
.post(Entity.entity(json, "application/json"))) { .post(Entity.entity(json, "application/json"))) {
@ -1311,13 +1313,13 @@ class ProfileControllerTest {
] ]
} }
""", AuthHelper.VALID_UUID, AuthHelper.VALID_PNI, """, AuthHelper.VALID_UUID, AuthHelper.VALID_PNI,
Base64.getEncoder().encodeToString(convertStringToFingerprint("else1234")))) Base64.getEncoder().encodeToString(convertKeyToFingerprint("else1234".getBytes(StandardCharsets.UTF_8)))))
); );
} }
private static byte[] convertStringToFingerprint(String base64) { private static byte[] convertKeyToFingerprint(byte[] key) {
try { try {
return Util.truncate(MessageDigest.getInstance("SHA-256").digest(Base64.getDecoder().decode(base64)), 4); return Util.truncate(MessageDigest.getInstance("SHA-256").digest(key), 4);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }

View File

@ -368,8 +368,8 @@ class RegistrationControllerTest {
} }
static Stream<Arguments> atomicAccountCreationConflictingChannel() { static Stream<Arguments> atomicAccountCreationConflictingChannel() {
final Optional<String> aciIdentityKey; final Optional<byte[]> aciIdentityKey;
final Optional<String> pniIdentityKey; final Optional<byte[]> pniIdentityKey;
final Optional<SignedPreKey> aciSignedPreKey; final Optional<SignedPreKey> aciSignedPreKey;
final Optional<SignedPreKey> pniSignedPreKey; final Optional<SignedPreKey> pniSignedPreKey;
final Optional<SignedPreKey> aciPqLastResortPreKey; final Optional<SignedPreKey> aciPqLastResortPreKey;
@ -378,8 +378,8 @@ class RegistrationControllerTest {
final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair(); final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair();
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair(); final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
aciIdentityKey = Optional.of(KeysHelper.serializeIdentityKey(aciIdentityKeyPair)); aciIdentityKey = Optional.of(aciIdentityKeyPair.getPublicKey().serialize());
pniIdentityKey = Optional.of(KeysHelper.serializeIdentityKey(pniIdentityKeyPair)); pniIdentityKey = Optional.of(pniIdentityKeyPair.getPublicKey().serialize());
aciSignedPreKey = Optional.of(KeysHelper.signedECPreKey(1, aciIdentityKeyPair)); aciSignedPreKey = Optional.of(KeysHelper.signedECPreKey(1, aciIdentityKeyPair));
pniSignedPreKey = Optional.of(KeysHelper.signedECPreKey(2, pniIdentityKeyPair)); pniSignedPreKey = Optional.of(KeysHelper.signedECPreKey(2, pniIdentityKeyPair));
aciPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair)); aciPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair));
@ -457,8 +457,8 @@ class RegistrationControllerTest {
} }
static Stream<Arguments> atomicAccountCreationPartialSignedPreKeys() { static Stream<Arguments> atomicAccountCreationPartialSignedPreKeys() {
final Optional<String> aciIdentityKey; final Optional<byte[]> aciIdentityKey;
final Optional<String> pniIdentityKey; final Optional<byte[]> pniIdentityKey;
final Optional<SignedPreKey> aciSignedPreKey; final Optional<SignedPreKey> aciSignedPreKey;
final Optional<SignedPreKey> pniSignedPreKey; final Optional<SignedPreKey> pniSignedPreKey;
final Optional<SignedPreKey> aciPqLastResortPreKey; final Optional<SignedPreKey> aciPqLastResortPreKey;
@ -467,8 +467,8 @@ class RegistrationControllerTest {
final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair(); final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair();
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair(); final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
aciIdentityKey = Optional.of(KeysHelper.serializeIdentityKey(aciIdentityKeyPair)); aciIdentityKey = Optional.of(aciIdentityKeyPair.getPublicKey().serialize());
pniIdentityKey = Optional.of(KeysHelper.serializeIdentityKey(pniIdentityKeyPair)); pniIdentityKey = Optional.of(pniIdentityKeyPair.getPublicKey().serialize());
aciSignedPreKey = Optional.of(KeysHelper.signedECPreKey(1, aciIdentityKeyPair)); aciSignedPreKey = Optional.of(KeysHelper.signedECPreKey(1, aciIdentityKeyPair));
pniSignedPreKey = Optional.of(KeysHelper.signedECPreKey(2, pniIdentityKeyPair)); pniSignedPreKey = Optional.of(KeysHelper.signedECPreKey(2, pniIdentityKeyPair));
aciPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair)); aciPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair));
@ -570,8 +570,8 @@ class RegistrationControllerTest {
@MethodSource @MethodSource
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
void atomicAccountCreationSuccess(final RegistrationRequest registrationRequest, void atomicAccountCreationSuccess(final RegistrationRequest registrationRequest,
final String expectedAciIdentityKey, final byte[] expectedAciIdentityKey,
final String expectedPniIdentityKey, final byte[] expectedPniIdentityKey,
final SignedPreKey expectedAciSignedPreKey, final SignedPreKey expectedAciSignedPreKey,
final SignedPreKey expectedPniSignedPreKey, final SignedPreKey expectedPniSignedPreKey,
final SignedPreKey expectedAciPqLastResortPreKey, final SignedPreKey expectedAciPqLastResortPreKey,
@ -636,8 +636,8 @@ class RegistrationControllerTest {
} }
private static Stream<Arguments> atomicAccountCreationSuccess() { private static Stream<Arguments> atomicAccountCreationSuccess() {
final Optional<String> aciIdentityKey; final Optional<byte[]> aciIdentityKey;
final Optional<String> pniIdentityKey; final Optional<byte[]> pniIdentityKey;
final Optional<SignedPreKey> aciSignedPreKey; final Optional<SignedPreKey> aciSignedPreKey;
final Optional<SignedPreKey> pniSignedPreKey; final Optional<SignedPreKey> pniSignedPreKey;
final Optional<SignedPreKey> aciPqLastResortPreKey; final Optional<SignedPreKey> aciPqLastResortPreKey;
@ -646,8 +646,8 @@ class RegistrationControllerTest {
final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair(); final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair();
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair(); final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
aciIdentityKey = Optional.of(KeysHelper.serializeIdentityKey(aciIdentityKeyPair)); aciIdentityKey = Optional.of(aciIdentityKeyPair.getPublicKey().serialize());
pniIdentityKey = Optional.of(KeysHelper.serializeIdentityKey(pniIdentityKeyPair)); pniIdentityKey = Optional.of(pniIdentityKeyPair.getPublicKey().serialize());
aciSignedPreKey = Optional.of(KeysHelper.signedECPreKey(1, aciIdentityKeyPair)); aciSignedPreKey = Optional.of(KeysHelper.signedECPreKey(1, aciIdentityKeyPair));
pniSignedPreKey = Optional.of(KeysHelper.signedECPreKey(2, pniIdentityKeyPair)); pniSignedPreKey = Optional.of(KeysHelper.signedECPreKey(2, pniIdentityKeyPair));
aciPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair)); aciPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair));

View File

@ -5,9 +5,7 @@
package org.whispersystems.textsecuregcm.storage; package org.whispersystems.textsecuregcm.storage;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -157,7 +155,7 @@ class AccountsManagerChangeNumberIntegrationTest {
final UUID originalUuid = account.getUuid(); final UUID originalUuid = account.getUuid();
final UUID originalPni = account.getPhoneNumberIdentifier(); final UUID originalPni = account.getPhoneNumberIdentifier();
final String pniIdentityKey = KeysHelper.serializeIdentityKey(pniIdentityKeyPair); final byte[] pniIdentityKey = pniIdentityKeyPair.getPublicKey().serialize();
final Map<Long, SignedPreKey> preKeys = Map.of(Device.MASTER_ID, rotatedSignedPreKey); final Map<Long, SignedPreKey> preKeys = Map.of(Device.MASTER_ID, rotatedSignedPreKey);
final Map<Long, Integer> registrationIds = Map.of(Device.MASTER_ID, rotatedPniRegistrationId); final Map<Long, Integer> registrationIds = Map.of(Device.MASTER_ID, rotatedPniRegistrationId);
@ -174,7 +172,7 @@ class AccountsManagerChangeNumberIntegrationTest {
assertEquals(Optional.empty(), deletedAccounts.findUuid(originalNumber)); assertEquals(Optional.empty(), deletedAccounts.findUuid(originalNumber));
assertEquals(Optional.empty(), deletedAccounts.findUuid(secondNumber)); assertEquals(Optional.empty(), deletedAccounts.findUuid(secondNumber));
assertEquals(pniIdentityKey, updatedAccount.getPhoneNumberIdentityKey()); assertArrayEquals(pniIdentityKey, updatedAccount.getPhoneNumberIdentityKey());
assertEquals(OptionalInt.of(rotatedPniRegistrationId), assertEquals(OptionalInt.of(rotatedPniRegistrationId),
updatedAccount.getMasterDevice().orElseThrow().getPhoneNumberIdentityRegistrationId()); updatedAccount.getMasterDevice().orElseThrow().getPhoneNumberIdentityRegistrationId());

View File

@ -20,11 +20,11 @@ import static org.mockito.Mockito.when;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands; import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Clock; import java.time.Clock;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Optional; import java.util.Optional;
import java.util.Random;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@ -41,7 +41,6 @@ import org.mockito.stubbing.Answer;
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash; import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.entities.AccountAttributes; import org.whispersystems.textsecuregcm.entities.AccountAttributes;
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager; import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
import org.whispersystems.textsecuregcm.push.ClientPresenceManager; import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient; import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
@ -143,7 +142,7 @@ class AccountsManagerConcurrentModificationIntegrationTest {
final boolean discoverableByPhoneNumber = false; final boolean discoverableByPhoneNumber = false;
final String currentProfileVersion = "cpv"; final String currentProfileVersion = "cpv";
final String identityKey = "ikey"; final byte[] identityKey = "ikey".getBytes(StandardCharsets.UTF_8);
final byte[] unidentifiedAccessKey = new byte[]{1}; final byte[] unidentifiedAccessKey = new byte[]{1};
final String pin = "1234"; final String pin = "1234";
final String registrationLock = "reglock"; final String registrationLock = "reglock";
@ -185,12 +184,12 @@ class AccountsManagerConcurrentModificationIntegrationTest {
return JsonHelpers.fromJson(redisSetArgumentCapture.getValue(), Account.class); return JsonHelpers.fromJson(redisSetArgumentCapture.getValue(), Account.class);
} }
private void verifyAccount(final String name, final Account account, final boolean discoverableByPhoneNumber, final String currentProfileVersion, final String identityKey, final byte[] unidentifiedAccessKey, final String pin, final String clientRegistrationLock, final boolean unrestrictedUnidentifiedAccess, final long lastSeen) { private void verifyAccount(final String name, final Account account, final boolean discoverableByPhoneNumber, final String currentProfileVersion, final byte[] identityKey, final byte[] unidentifiedAccessKey, final String pin, final String clientRegistrationLock, final boolean unrestrictedUnidentifiedAccess, final long lastSeen) {
assertAll(name, assertAll(name,
() -> assertEquals(discoverableByPhoneNumber, account.isDiscoverableByPhoneNumber()), () -> assertEquals(discoverableByPhoneNumber, account.isDiscoverableByPhoneNumber()),
() -> assertEquals(currentProfileVersion, account.getCurrentProfileVersion().orElseThrow()), () -> assertEquals(currentProfileVersion, account.getCurrentProfileVersion().orElseThrow()),
() -> assertEquals(identityKey, account.getIdentityKey()), () -> assertArrayEquals(identityKey, account.getIdentityKey()),
() -> assertArrayEquals(unidentifiedAccessKey, account.getUnidentifiedAccessKey().orElseThrow()), () -> assertArrayEquals(unidentifiedAccessKey, account.getUnidentifiedAccessKey().orElseThrow()),
() -> assertTrue(account.getRegistrationLock().verify(clientRegistrationLock)), () -> assertTrue(account.getRegistrationLock().verify(clientRegistrationLock)),
() -> assertEquals(unrestrictedUnidentifiedAccess, account.isUnrestrictedUnidentifiedAccess()) () -> assertEquals(unrestrictedUnidentifiedAccess, account.isUnrestrictedUnidentifiedAccess())

View File

@ -5,11 +5,7 @@
package org.whispersystems.textsecuregcm.storage; package org.whispersystems.textsecuregcm.storage;
import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.argThat;
@ -28,6 +24,8 @@ import static org.mockito.Mockito.when;
import io.lettuce.core.RedisException; import io.lettuce.core.RedisException;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands; import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import java.nio.charset.StandardCharsets;
import java.time.Clock; import java.time.Clock;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
@ -470,10 +468,10 @@ class AccountsManagerTest {
.doAnswer(ACCOUNT_UPDATE_ANSWER) .doAnswer(ACCOUNT_UPDATE_ANSWER)
.when(accounts).update(any()); .when(accounts).update(any());
account = accountsManager.update(account, a -> a.setIdentityKey("identity-key")); account = accountsManager.update(account, a -> a.setIdentityKey("identity-key".getBytes(StandardCharsets.UTF_8)));
assertEquals(1, account.getVersion()); assertEquals(1, account.getVersion());
assertEquals("identity-key", account.getIdentityKey()); assertArrayEquals("identity-key".getBytes(StandardCharsets.UTF_8), account.getIdentityKey());
verify(accounts, times(1)).getByAccountIdentifier(uuid); verify(accounts, times(1)).getByAccountIdentifier(uuid);
verify(accounts, times(2)).update(any()); verify(accounts, times(2)).update(any());
@ -674,7 +672,7 @@ class AccountsManagerTest {
Account account = AccountsHelper.generateTestAccount(number, UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]); Account account = AccountsHelper.generateTestAccount(number, UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[16]);
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> accountsManager.changeNumber( () -> accountsManager.changeNumber(
account, number, "new-identity-key", Map.of(1L, new SignedPreKey()), null, Map.of(1L, 101)), account, number, "new-identity-key".getBytes(StandardCharsets.UTF_8), Map.of(1L, new SignedPreKey()), null, Map.of(1L, 101)),
"AccountsManager should not allow use of changeNumber with new PNI keys but without changing number"); "AccountsManager should not allow use of changeNumber with new PNI keys but without changing number");
verify(accounts, never()).update(any()); verify(accounts, never()).update(any());
@ -739,7 +737,7 @@ class AccountsManagerTest {
final List<Device> devices = List.of(DevicesHelper.createDevice(1L, 0L, 101), DevicesHelper.createDevice(2L, 0L, 102)); final List<Device> devices = List.of(DevicesHelper.createDevice(1L, 0L, 101), DevicesHelper.createDevice(2L, 0L, 102));
final Account account = AccountsHelper.generateTestAccount(originalNumber, uuid, originalPni, devices, new byte[16]); final Account account = AccountsHelper.generateTestAccount(originalNumber, uuid, originalPni, devices, new byte[16]);
final Account updatedAccount = accountsManager.changeNumber( final Account updatedAccount = accountsManager.changeNumber(
account, targetNumber, "new-pni-identity-key", newSignedKeys, newSignedPqKeys, newRegistrationIds); account, targetNumber, "new-pni-identity-key".getBytes(StandardCharsets.UTF_8), newSignedKeys, newSignedPqKeys, newRegistrationIds);
assertEquals(targetNumber, updatedAccount.getNumber()); assertEquals(targetNumber, updatedAccount.getNumber());
@ -782,19 +780,19 @@ class AccountsManagerTest {
UUID oldPni = account.getPhoneNumberIdentifier(); UUID oldPni = account.getPhoneNumberIdentifier();
Map<Long, SignedPreKey> oldSignedPreKeys = account.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getSignedPreKey)); Map<Long, SignedPreKey> oldSignedPreKeys = account.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getSignedPreKey));
final Account updatedAccount = accountsManager.updatePniKeys(account, "new-pni-identity-key", newSignedKeys, null, newRegistrationIds); final Account updatedAccount = accountsManager.updatePniKeys(account, "new-pni-identity-key".getBytes(StandardCharsets.UTF_8), newSignedKeys, null, newRegistrationIds);
// non-PNI stuff should not change // non-PNI stuff should not change
assertEquals(oldUuid, updatedAccount.getUuid()); assertEquals(oldUuid, updatedAccount.getUuid());
assertEquals(number, updatedAccount.getNumber()); assertEquals(number, updatedAccount.getNumber());
assertEquals(oldPni, updatedAccount.getPhoneNumberIdentifier()); assertEquals(oldPni, updatedAccount.getPhoneNumberIdentifier());
assertEquals(null, updatedAccount.getIdentityKey()); assertNull(updatedAccount.getIdentityKey());
assertEquals(oldSignedPreKeys, updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getSignedPreKey))); assertEquals(oldSignedPreKeys, updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getSignedPreKey)));
assertEquals(Map.of(1L, 101, 2L, 102), assertEquals(Map.of(1L, 101, 2L, 102),
updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getRegistrationId))); updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getRegistrationId)));
// PNI stuff should // PNI stuff should
assertEquals("new-pni-identity-key", updatedAccount.getPhoneNumberIdentityKey()); assertArrayEquals("new-pni-identity-key".getBytes(StandardCharsets.UTF_8), updatedAccount.getPhoneNumberIdentityKey());
assertEquals(newSignedKeys, assertEquals(newSignedKeys,
updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getPhoneNumberIdentitySignedPreKey))); updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getPhoneNumberIdentitySignedPreKey)));
assertEquals(newRegistrationIds, assertEquals(newRegistrationIds,
@ -829,19 +827,19 @@ class AccountsManagerTest {
Map<Long, SignedPreKey> oldSignedPreKeys = account.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getSignedPreKey)); Map<Long, SignedPreKey> oldSignedPreKeys = account.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getSignedPreKey));
final Account updatedAccount = final Account updatedAccount =
accountsManager.updatePniKeys(account, "new-pni-identity-key", newSignedKeys, newSignedPqKeys, newRegistrationIds); accountsManager.updatePniKeys(account, "new-pni-identity-key".getBytes(StandardCharsets.UTF_8), newSignedKeys, newSignedPqKeys, newRegistrationIds);
// non-PNI-keys stuff should not change // non-PNI-keys stuff should not change
assertEquals(oldUuid, updatedAccount.getUuid()); assertEquals(oldUuid, updatedAccount.getUuid());
assertEquals(number, updatedAccount.getNumber()); assertEquals(number, updatedAccount.getNumber());
assertEquals(oldPni, updatedAccount.getPhoneNumberIdentifier()); assertEquals(oldPni, updatedAccount.getPhoneNumberIdentifier());
assertEquals(null, updatedAccount.getIdentityKey()); assertNull(updatedAccount.getIdentityKey());
assertEquals(oldSignedPreKeys, updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getSignedPreKey))); assertEquals(oldSignedPreKeys, updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getSignedPreKey)));
assertEquals(Map.of(1L, 101, 2L, 102), assertEquals(Map.of(1L, 101, 2L, 102),
updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getRegistrationId))); updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getRegistrationId)));
// PNI keys should // PNI keys should
assertEquals("new-pni-identity-key", updatedAccount.getPhoneNumberIdentityKey()); assertArrayEquals("new-pni-identity-key".getBytes(StandardCharsets.UTF_8), updatedAccount.getPhoneNumberIdentityKey());
assertEquals(newSignedKeys, assertEquals(newSignedKeys,
updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getPhoneNumberIdentitySignedPreKey))); updatedAccount.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::getPhoneNumberIdentitySignedPreKey)));
assertEquals(newRegistrationIds, assertEquals(newRegistrationIds,

View File

@ -14,6 +14,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
import java.util.Collections; import java.util.Collections;
@ -105,7 +106,7 @@ public class ChangeNumberManagerTest {
Account account = mock(Account.class); Account account = mock(Account.class);
when(account.getNumber()).thenReturn("+18005551234"); when(account.getNumber()).thenReturn("+18005551234");
var prekeys = Map.of(1L, new SignedPreKey()); var prekeys = Map.of(1L, new SignedPreKey());
final String pniIdentityKey = "pni-identity-key"; final byte[] pniIdentityKey = "pni-identity-key".getBytes(StandardCharsets.UTF_8);
changeNumberManager.changeNumber(account, "+18025551234", pniIdentityKey, prekeys, null, Collections.emptyList(), Collections.emptyMap()); changeNumberManager.changeNumber(account, "+18025551234", pniIdentityKey, prekeys, null, Collections.emptyList(), Collections.emptyMap());
verify(accountsManager).changeNumber(account, "+18025551234", pniIdentityKey, prekeys, null, Collections.emptyMap()); verify(accountsManager).changeNumber(account, "+18025551234", pniIdentityKey, prekeys, null, Collections.emptyMap());
@ -131,7 +132,7 @@ public class ChangeNumberManagerTest {
when(account.getDevice(2L)).thenReturn(Optional.of(d2)); when(account.getDevice(2L)).thenReturn(Optional.of(d2));
when(account.getDevices()).thenReturn(List.of(d2)); when(account.getDevices()).thenReturn(List.of(d2));
final String pniIdentityKey = "pni-identity-key"; final byte[] pniIdentityKey = "pni-identity-key".getBytes();
final Map<Long, SignedPreKey> prekeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey()); final Map<Long, SignedPreKey> prekeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey());
final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 19); final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 19);
@ -174,7 +175,7 @@ public class ChangeNumberManagerTest {
when(account.getDevice(2L)).thenReturn(Optional.of(d2)); when(account.getDevice(2L)).thenReturn(Optional.of(d2));
when(account.getDevices()).thenReturn(List.of(d2)); when(account.getDevices()).thenReturn(List.of(d2));
final String pniIdentityKey = "pni-identity-key"; final byte[] pniIdentityKey = "pni-identity-key".getBytes(StandardCharsets.UTF_8);
final Map<Long, SignedPreKey> prekeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey()); final Map<Long, SignedPreKey> prekeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey());
final Map<Long, SignedPreKey> pqPrekeys = Map.of(3L, new SignedPreKey(), 4L, new SignedPreKey()); final Map<Long, SignedPreKey> pqPrekeys = Map.of(3L, new SignedPreKey(), 4L, new SignedPreKey());
final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 19); final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 19);
@ -216,7 +217,7 @@ public class ChangeNumberManagerTest {
when(account.getDevice(2L)).thenReturn(Optional.of(d2)); when(account.getDevice(2L)).thenReturn(Optional.of(d2));
when(account.getDevices()).thenReturn(List.of(d2)); when(account.getDevices()).thenReturn(List.of(d2));
final String pniIdentityKey = "pni-identity-key"; final byte[] pniIdentityKey = "pni-identity-key".getBytes();
final Map<Long, SignedPreKey> prekeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey()); final Map<Long, SignedPreKey> prekeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey());
final Map<Long, SignedPreKey> pqPrekeys = Map.of(3L, new SignedPreKey(), 4L, new SignedPreKey()); final Map<Long, SignedPreKey> pqPrekeys = Map.of(3L, new SignedPreKey(), 4L, new SignedPreKey());
final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 19); final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 19);
@ -256,7 +257,7 @@ public class ChangeNumberManagerTest {
when(account.getDevice(2L)).thenReturn(Optional.of(d2)); when(account.getDevice(2L)).thenReturn(Optional.of(d2));
when(account.getDevices()).thenReturn(List.of(d2)); when(account.getDevices()).thenReturn(List.of(d2));
final String pniIdentityKey = "pni-identity-key"; final byte[] pniIdentityKey = "pni-identity-key".getBytes();
final Map<Long, SignedPreKey> prekeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey()); final Map<Long, SignedPreKey> prekeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey());
final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 19); final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 19);
@ -295,7 +296,7 @@ public class ChangeNumberManagerTest {
when(account.getDevice(2L)).thenReturn(Optional.of(d2)); when(account.getDevice(2L)).thenReturn(Optional.of(d2));
when(account.getDevices()).thenReturn(List.of(d2)); when(account.getDevices()).thenReturn(List.of(d2));
final String pniIdentityKey = "pni-identity-key"; final byte[] pniIdentityKey = "pni-identity-key".getBytes();
final Map<Long, SignedPreKey> prekeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey()); final Map<Long, SignedPreKey> prekeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey());
final Map<Long, SignedPreKey> pqPrekeys = Map.of(3L, new SignedPreKey(), 4L, new SignedPreKey()); final Map<Long, SignedPreKey> pqPrekeys = Map.of(3L, new SignedPreKey(), 4L, new SignedPreKey());
final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 19); final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 19);
@ -346,7 +347,7 @@ public class ChangeNumberManagerTest {
final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 47, 3L, 89); final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 47, 3L, 89);
assertThrows(StaleDevicesException.class, assertThrows(StaleDevicesException.class,
() -> changeNumberManager.changeNumber(account, "+18005559876", "pni-identity-key", preKeys, null, messages, registrationIds)); () -> changeNumberManager.changeNumber(account, "+18005559876", "pni-identity-key".getBytes(StandardCharsets.UTF_8), preKeys, null, messages, registrationIds));
} }
@Test @Test
@ -376,7 +377,7 @@ public class ChangeNumberManagerTest {
final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 47, 3L, 89); final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 47, 3L, 89);
assertThrows(StaleDevicesException.class, assertThrows(StaleDevicesException.class,
() -> changeNumberManager.updatePniKeys(account, "pni-identity-key", preKeys, null, messages, registrationIds)); () -> changeNumberManager.updatePniKeys(account, "pni-identity-key".getBytes(StandardCharsets.UTF_8), preKeys, null, messages, registrationIds));
} }
@Test @Test
@ -405,6 +406,6 @@ public class ChangeNumberManagerTest {
final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 47, 3L, 89); final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 47, 3L, 89);
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> changeNumberManager.changeNumber(account, "+18005559876", "pni-identity-key", null, null, messages, registrationIds)); () -> changeNumberManager.changeNumber(account, "+18005559876", "pni-identity-key".getBytes(StandardCharsets.UTF_8), null, null, messages, registrationIds));
} }
} }

View File

@ -113,8 +113,7 @@ class CertificateControllerTest {
assertEquals(certificate.getSenderDevice(), 1L); assertEquals(certificate.getSenderDevice(), 1L);
assertTrue(certificate.hasSenderUuid()); assertTrue(certificate.hasSenderUuid());
assertEquals(AuthHelper.VALID_UUID.toString(), certificate.getSenderUuid()); assertEquals(AuthHelper.VALID_UUID.toString(), certificate.getSenderUuid());
assertArrayEquals(certificate.getIdentityKey().toByteArray(), assertArrayEquals(certificate.getIdentityKey().toByteArray(), AuthHelper.VALID_IDENTITY);
Base64.getDecoder().decode(AuthHelper.VALID_IDENTITY));
} }
@Test @Test
@ -142,8 +141,7 @@ class CertificateControllerTest {
assertEquals(certificate.getSender(), AuthHelper.VALID_NUMBER); assertEquals(certificate.getSender(), AuthHelper.VALID_NUMBER);
assertEquals(certificate.getSenderDevice(), 1L); assertEquals(certificate.getSenderDevice(), 1L);
assertEquals(certificate.getSenderUuid(), AuthHelper.VALID_UUID.toString()); assertEquals(certificate.getSenderUuid(), AuthHelper.VALID_UUID.toString());
assertArrayEquals(certificate.getIdentityKey().toByteArray(), assertArrayEquals(certificate.getIdentityKey().toByteArray(), AuthHelper.VALID_IDENTITY);
Base64.getDecoder().decode(AuthHelper.VALID_IDENTITY));
} }
@Test @Test
@ -172,8 +170,7 @@ class CertificateControllerTest {
assertTrue(StringUtils.isBlank(certificate.getSender())); assertTrue(StringUtils.isBlank(certificate.getSender()));
assertEquals(certificate.getSenderDevice(), 1L); assertEquals(certificate.getSenderDevice(), 1L);
assertEquals(certificate.getSenderUuid(), AuthHelper.VALID_UUID.toString()); assertEquals(certificate.getSenderUuid(), AuthHelper.VALID_UUID.toString());
assertArrayEquals(certificate.getIdentityKey().toByteArray(), assertArrayEquals(certificate.getIdentityKey().toByteArray(), AuthHelper.VALID_IDENTITY);
Base64.getDecoder().decode(AuthHelper.VALID_IDENTITY));
} }
@Test @Test

View File

@ -278,8 +278,8 @@ class DeviceControllerTest {
aciPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair)); aciPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair));
pniPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(4, pniIdentityKeyPair)); pniPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(4, pniIdentityKeyPair));
when(account.getIdentityKey()).thenReturn(KeysHelper.serializeIdentityKey(aciIdentityKeyPair)); when(account.getIdentityKey()).thenReturn(aciIdentityKeyPair.getPublicKey().serialize());
when(account.getPhoneNumberIdentityKey()).thenReturn(KeysHelper.serializeIdentityKey(pniIdentityKeyPair)); when(account.getPhoneNumberIdentityKey()).thenReturn(pniIdentityKeyPair.getPublicKey().serialize());
final LinkDeviceRequest request = new LinkDeviceRequest("5678901", final LinkDeviceRequest request = new LinkDeviceRequest("5678901",
new AccountAttributes(fetchesMessages, 1234, null, null, true, null), new AccountAttributes(fetchesMessages, 1234, null, null, true, null),
@ -364,8 +364,8 @@ class DeviceControllerTest {
aciPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair)); aciPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair));
pniPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(4, pniIdentityKeyPair)); pniPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(4, pniIdentityKeyPair));
when(account.getIdentityKey()).thenReturn(KeysHelper.serializeIdentityKey(aciIdentityKeyPair)); when(account.getIdentityKey()).thenReturn(aciIdentityKeyPair.getPublicKey().serialize());
when(account.getPhoneNumberIdentityKey()).thenReturn(KeysHelper.serializeIdentityKey(pniIdentityKeyPair)); when(account.getPhoneNumberIdentityKey()).thenReturn(pniIdentityKeyPair.getPublicKey().serialize());
final LinkDeviceRequest request = new LinkDeviceRequest("5678901", final LinkDeviceRequest request = new LinkDeviceRequest("5678901",
new AccountAttributes(fetchesMessages, 1234, null, null, true, null), new AccountAttributes(fetchesMessages, 1234, null, null, true, null),
@ -393,8 +393,8 @@ class DeviceControllerTest {
@ParameterizedTest @ParameterizedTest
@MethodSource @MethodSource
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
void linkDeviceAtomicMissingProperty(final String aciIdentityKey, void linkDeviceAtomicMissingProperty(final byte[] aciIdentityKey,
final String pniIdentityKey, final byte[] pniIdentityKey,
final Optional<SignedPreKey> aciSignedPreKey, final Optional<SignedPreKey> aciSignedPreKey,
final Optional<SignedPreKey> pniSignedPreKey, final Optional<SignedPreKey> pniSignedPreKey,
final Optional<SignedPreKey> aciPqLastResortPreKey, final Optional<SignedPreKey> aciPqLastResortPreKey,
@ -440,8 +440,8 @@ class DeviceControllerTest {
final Optional<SignedPreKey> aciPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair)); final Optional<SignedPreKey> aciPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair));
final Optional<SignedPreKey> pniPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(4, pniIdentityKeyPair)); final Optional<SignedPreKey> pniPqLastResortPreKey = Optional.of(KeysHelper.signedKEMPreKey(4, pniIdentityKeyPair));
final String aciIdentityKey = KeysHelper.serializeIdentityKey(aciIdentityKeyPair); final byte[] aciIdentityKey = aciIdentityKeyPair.getPublicKey().serialize();
final String pniIdentityKey = KeysHelper.serializeIdentityKey(pniIdentityKeyPair); final byte[] pniIdentityKey = pniIdentityKeyPair.getPublicKey().serialize();
return Stream.of( return Stream.of(
Arguments.of(aciIdentityKey, pniIdentityKey, Optional.empty(), pniSignedPreKey, aciPqLastResortPreKey, pniPqLastResortPreKey), Arguments.of(aciIdentityKey, pniIdentityKey, Optional.empty(), pniSignedPreKey, aciPqLastResortPreKey, pniPqLastResortPreKey),
@ -453,8 +453,8 @@ class DeviceControllerTest {
@ParameterizedTest @ParameterizedTest
@MethodSource @MethodSource
void linkDeviceAtomicInvalidSignature(final String aciIdentityKey, void linkDeviceAtomicInvalidSignature(final byte[] aciIdentityKey,
final String pniIdentityKey, final byte[] pniIdentityKey,
final SignedPreKey aciSignedPreKey, final SignedPreKey aciSignedPreKey,
final SignedPreKey pniSignedPreKey, final SignedPreKey pniSignedPreKey,
final SignedPreKey aciPqLastResortPreKey, final SignedPreKey aciPqLastResortPreKey,
@ -500,8 +500,8 @@ class DeviceControllerTest {
final SignedPreKey aciPqLastResortPreKey = KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair); final SignedPreKey aciPqLastResortPreKey = KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair);
final SignedPreKey pniPqLastResortPreKey = KeysHelper.signedKEMPreKey(4, pniIdentityKeyPair); final SignedPreKey pniPqLastResortPreKey = KeysHelper.signedKEMPreKey(4, pniIdentityKeyPair);
final String aciIdentityKey = KeysHelper.serializeIdentityKey(aciIdentityKeyPair); final byte[] aciIdentityKey = aciIdentityKeyPair.getPublicKey().serialize();
final String pniIdentityKey = KeysHelper.serializeIdentityKey(pniIdentityKeyPair); final byte[] pniIdentityKey = pniIdentityKeyPair.getPublicKey().serialize();
return Stream.of( return Stream.of(
Arguments.of(aciIdentityKey, pniIdentityKey, signedPreKeyWithBadSignature(aciSignedPreKey), pniSignedPreKey, aciPqLastResortPreKey, pniPqLastResortPreKey), Arguments.of(aciIdentityKey, pniIdentityKey, signedPreKeyWithBadSignature(aciSignedPreKey), pniSignedPreKey, aciPqLastResortPreKey, pniPqLastResortPreKey),

View File

@ -80,10 +80,10 @@ class KeysControllerTest {
private static final int SAMPLE_PNI_REGISTRATION_ID = 1717; private static final int SAMPLE_PNI_REGISTRATION_ID = 1717;
private final ECKeyPair IDENTITY_KEY_PAIR = Curve.generateKeyPair(); private final ECKeyPair IDENTITY_KEY_PAIR = Curve.generateKeyPair();
private final String IDENTITY_KEY = KeysHelper.serializeIdentityKey(IDENTITY_KEY_PAIR); private final byte[] IDENTITY_KEY = IDENTITY_KEY_PAIR.getPublicKey().serialize();
private final ECKeyPair PNI_IDENTITY_KEY_PAIR = Curve.generateKeyPair(); private final ECKeyPair PNI_IDENTITY_KEY_PAIR = Curve.generateKeyPair();
private final String PNI_IDENTITY_KEY = KeysHelper.serializeIdentityKey(PNI_IDENTITY_KEY_PAIR); private final byte[] PNI_IDENTITY_KEY = PNI_IDENTITY_KEY_PAIR.getPublicKey().serialize();
private final PreKey SAMPLE_KEY = KeysHelper.ecPreKey(1234); private final PreKey SAMPLE_KEY = KeysHelper.ecPreKey(1234);
private final PreKey SAMPLE_KEY2 = KeysHelper.ecPreKey(5667); private final PreKey SAMPLE_KEY2 = KeysHelper.ecPreKey(5667);
@ -658,7 +658,7 @@ class KeysControllerTest {
final PreKey preKey = KeysHelper.ecPreKey(31337); final PreKey preKey = KeysHelper.ecPreKey(31337);
final ECKeyPair identityKeyPair = Curve.generateKeyPair(); final ECKeyPair identityKeyPair = Curve.generateKeyPair();
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair); final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
final String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair); final byte[] identityKey = identityKeyPair.getPublicKey().serialize();
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey)); PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey));
@ -688,7 +688,7 @@ class KeysControllerTest {
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair); final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
final SignedPreKey pqPreKey = KeysHelper.signedECPreKey(31339, identityKeyPair); final SignedPreKey pqPreKey = KeysHelper.signedECPreKey(31339, identityKeyPair);
final SignedPreKey pqLastResortPreKey = KeysHelper.signedECPreKey(31340, identityKeyPair); final SignedPreKey pqLastResortPreKey = KeysHelper.signedECPreKey(31340, identityKeyPair);
final String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair); final byte[] identityKey = identityKeyPair.getPublicKey().serialize();
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey), List.of(pqPreKey), pqLastResortPreKey); PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey), List.of(pqPreKey), pqLastResortPreKey);
@ -718,7 +718,7 @@ class KeysControllerTest {
final PreKey preKey = KeysHelper.ecPreKey(31337); final PreKey preKey = KeysHelper.ecPreKey(31337);
final ECKeyPair identityKeyPair = Curve.generateKeyPair(); final ECKeyPair identityKeyPair = Curve.generateKeyPair();
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair); final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
final String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair); final byte[] identityKey = identityKeyPair.getPublicKey().serialize();
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey)); PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey));
@ -749,7 +749,7 @@ class KeysControllerTest {
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair); final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
final SignedPreKey pqPreKey = KeysHelper.signedECPreKey(31339, identityKeyPair); final SignedPreKey pqPreKey = KeysHelper.signedECPreKey(31339, identityKeyPair);
final SignedPreKey pqLastResortPreKey = KeysHelper.signedECPreKey(31340, identityKeyPair); final SignedPreKey pqLastResortPreKey = KeysHelper.signedECPreKey(31340, identityKeyPair);
final String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair); final byte[] identityKey = identityKeyPair.getPublicKey().serialize();
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey), List.of(pqPreKey), pqLastResortPreKey); PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey), List.of(pqPreKey), pqLastResortPreKey);
@ -795,7 +795,7 @@ class KeysControllerTest {
final PreKey preKey = KeysHelper.ecPreKey(31337); final PreKey preKey = KeysHelper.ecPreKey(31337);
final ECKeyPair identityKeyPair = Curve.generateKeyPair(); final ECKeyPair identityKeyPair = Curve.generateKeyPair();
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair); final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
final String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair); final byte[] identityKey = identityKeyPair.getPublicKey().serialize();
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey)); PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey));

View File

@ -63,7 +63,7 @@ public class AuthHelper {
public static final UUID UNDISCOVERABLE_UUID = UUID.randomUUID(); public static final UUID UNDISCOVERABLE_UUID = UUID.randomUUID();
public static final String UNDISCOVERABLE_PASSWORD = "IT'S A SECRET TO EVERYBODY."; public static final String UNDISCOVERABLE_PASSWORD = "IT'S A SECRET TO EVERYBODY.";
public static final String VALID_IDENTITY = "BcxxDU9FGMda70E7+Uvm7pnQcEdXQ64aJCpPUeRSfcFo"; public static final byte[] VALID_IDENTITY = Base64.getDecoder().decode("BcxxDU9FGMda70E7+Uvm7pnQcEdXQ64aJCpPUeRSfcFo");
public static AccountsManager ACCOUNTS_MANAGER = mock(AccountsManager.class); public static AccountsManager ACCOUNTS_MANAGER = mock(AccountsManager.class);
public static Account VALID_ACCOUNT = mock(Account.class ); public static Account VALID_ACCOUNT = mock(Account.class );

View File

@ -5,7 +5,6 @@
package org.whispersystems.textsecuregcm.tests.util; package org.whispersystems.textsecuregcm.tests.util;
import java.util.Base64;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair; import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.signal.libsignal.protocol.kem.KEMKeyPair; import org.signal.libsignal.protocol.kem.KEMKeyPair;
@ -14,14 +13,11 @@ import org.whispersystems.textsecuregcm.entities.PreKey;
import org.whispersystems.textsecuregcm.entities.SignedPreKey; import org.whispersystems.textsecuregcm.entities.SignedPreKey;
public final class KeysHelper { public final class KeysHelper {
public static String serializeIdentityKey(ECKeyPair keyPair) {
return Base64.getEncoder().encodeToString(keyPair.getPublicKey().serialize());
}
public static PreKey ecPreKey(final long id) { public static PreKey ecPreKey(final long id) {
return new PreKey(id, Curve.generateKeyPair().getPublicKey().serialize()); return new PreKey(id, Curve.generateKeyPair().getPublicKey().serialize());
} }
public static SignedPreKey signedECPreKey(long id, final ECKeyPair identityKeyPair) { public static SignedPreKey signedECPreKey(long id, final ECKeyPair identityKeyPair) {
final byte[] pubKey = Curve.generateKeyPair().getPublicKey().serialize(); final byte[] pubKey = Curve.generateKeyPair().getPublicKey().serialize();
final byte[] sig = identityKeyPair.getPrivateKey().calculateSignature(pubKey); final byte[] sig = identityKeyPair.getPrivateKey().calculateSignature(pubKey);