Always check prekey signatures when new prekeys are uploaded

This commit is contained in:
Jonathan Klabunde Tomer 2023-05-04 11:31:45 -07:00 committed by GitHub
parent bc68b67cdf
commit e38911b2c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 165 additions and 40 deletions

View File

@ -61,6 +61,7 @@ public class KeysController {
private final Keys keys;
private final AccountsManager accounts;
private static final String IDENTITY_KEY_CHANGE_COUNTER_NAME = name(KeysController.class, "identityKeyChange");
private static final String IDENTITY_KEY_CHANGE_FORBIDDEN_COUNTER_NAME = name(KeysController.class, "identityKeyChangeForbidden");
private static final String IDENTITY_TYPE_TAG_NAME = "identityType";
@ -85,6 +86,7 @@ public class KeysController {
@Timed
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@ChangesDeviceEnabledState
public void setKeys(@Auth final DisabledPermittedAuthenticatedAccount disabledPermittedAuth,
@NotNull @Valid final PreKeyState preKeys,
@ -100,21 +102,21 @@ public class KeysController {
updateAccount = true;
}
if (!preKeys.getIdentityKey().equals(usePhoneNumberIdentity ? account.getPhoneNumberIdentityKey() : account.getIdentityKey())) {
final String oldIdentityKey = usePhoneNumberIdentity ? account.getPhoneNumberIdentityKey() : account.getIdentityKey();
if (!preKeys.getIdentityKey().equals(oldIdentityKey)) {
updateAccount = true;
final boolean hasIdentityKey = StringUtils.isNotBlank(oldIdentityKey);
final Tags tags = Tags.of(UserAgentTagUtil.getPlatformTag(userAgent))
.and(HAS_IDENTITY_KEY_TAG_NAME, String.valueOf(hasIdentityKey))
.and(IDENTITY_TYPE_TAG_NAME, usePhoneNumberIdentity ? "pni" : "aci");
if (!device.isMaster()) {
final boolean hasIdentityKey = usePhoneNumberIdentity ?
StringUtils.isNotBlank(account.getPhoneNumberIdentityKey()) :
StringUtils.isNotBlank(account.getIdentityKey());
final Tags tags = Tags.of(UserAgentTagUtil.getPlatformTag(userAgent))
.and(HAS_IDENTITY_KEY_TAG_NAME, String.valueOf(hasIdentityKey))
.and(IDENTITY_TYPE_TAG_NAME, usePhoneNumberIdentity ? "pni" : "aci");
Metrics.counter(IDENTITY_KEY_CHANGE_FORBIDDEN_COUNTER_NAME, tags).increment();
throw new ForbiddenException();
}
Metrics.counter(IDENTITY_KEY_CHANGE_COUNTER_NAME, tags).increment();
}
if (updateAccount) {

View File

@ -11,6 +11,7 @@ import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
@ -24,4 +25,12 @@ public record ChangeNumberRequest(String sessionId,
@NotNull @Valid Map<Long, @NotNull @Valid SignedPreKey> devicePniSignedPrekeys,
@NotNull Map<Long, Integer> pniRegistrationIds) implements PhoneVerificationRequest {
@AssertTrue
public boolean isSignatureValidOnEachSignedPreKey() {
if (devicePniSignedPrekeys == null) {
return true;
}
return devicePniSignedPrekeys.values().parallelStream()
.allMatch(spk -> PreKeySignatureValidator.validatePreKeySignature(pniIdentityKey, spk));
}
}

View File

@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.AssertTrue;
import javax.annotation.Nullable;
import javax.validation.constraints.NotBlank;
@ -18,4 +19,14 @@ public record ChangePhoneNumberRequest(@NotBlank String number,
@Nullable List<IncomingMessage> deviceMessages,
@Nullable Map<Long, SignedPreKey> devicePniSignedPrekeys,
@Nullable Map<Long, Integer> pniRegistrationIds) {
@AssertTrue
public boolean isSignatureValidOnEachSignedPreKey() {
if (devicePniSignedPrekeys == null) {
return true;
}
return devicePniSignedPrekeys.values().parallelStream()
.allMatch(spk -> PreKeySignatureValidator.validatePreKeySignature(pniIdentityKey, spk));
}
}

View File

@ -11,6 +11,7 @@ import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
@ -34,4 +35,11 @@ public record PhoneNumberIdentityKeyDistributionRequest(
@Valid
@Schema(description="The new registration ID to use for the phone-number identity of each device")
Map<Long, Integer> pniRegistrationIds) {
@AssertTrue
public boolean isSignatureValidOnEachSignedPreKey() {
return devicePniSignedPrekeys.values().parallelStream()
.allMatch(spk -> PreKeySignatureValidator.validatePreKeySignature(pniIdentityKey, spk));
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.entities;
import static com.codahale.metrics.MetricRegistry.name;
import io.micrometer.core.instrument.Metrics;
import java.util.Base64;
import org.signal.libsignal.protocol.InvalidKeyException;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
public abstract class PreKeySignatureValidator {
public static final boolean validatePreKeySignature(final String identityKeyB64, final SignedPreKey spk) {
try {
final byte[] identityKeyBytes = Base64.getDecoder().decode(identityKeyB64);
final byte[] prekeyBytes = Base64.getDecoder().decode(spk.getPublicKey());
final byte[] prekeySignatureBytes = Base64.getDecoder().decode(spk.getSignature());
final ECPublicKey identityKey = Curve.decodePoint(identityKeyBytes, 0);
return identityKey.verifySignature(prekeyBytes, prekeySignatureBytes);
} catch (IllegalArgumentException | InvalidKeyException e) {
Metrics.counter(name(PreKeySignatureValidator.class, "invalidPreKeySignature")).increment();
return false;
}
}
}

View File

@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@ -48,4 +49,8 @@ public class PreKeyState {
return identityKey;
}
@AssertTrue
public boolean isSignatureValid() {
return PreKeySignatureValidator.validatePreKeySignature(identityKey, signedPreKey);
}
}

View File

@ -48,6 +48,8 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import javax.ws.rs.client.Entity;
@ -65,6 +67,8 @@ import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.stubbing.Answer;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.signal.libsignal.usernames.BaseUsernameException;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
@ -122,6 +126,7 @@ import org.whispersystems.textsecuregcm.storage.UsernameHashNotAvailableExceptio
import org.whispersystems.textsecuregcm.storage.UsernameReservationNotFoundException;
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
import org.whispersystems.textsecuregcm.tests.util.KeysHelper;
import org.whispersystems.textsecuregcm.util.MockUtils;
import org.whispersystems.textsecuregcm.util.SystemMapper;
import org.whispersystems.textsecuregcm.util.TestClock;
@ -1622,7 +1627,8 @@ class AccountControllerTest {
void testChangePhoneNumberChangePrekeys() throws Exception {
final String number = "+18005559876";
final String code = "987654";
final String pniIdentityKey = "changed-pni-identity-key";
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
final String pniIdentityKey = KeysHelper.serializeIdentityKey(pniIdentityKeyPair);
final byte[] sessionId = "session-id".getBytes(StandardCharsets.UTF_8);
Device device2 = mock(Device.class);
@ -1648,7 +1654,7 @@ class AccountControllerTest {
var deviceMessages = List.of(
new IncomingMessage(1, 2, 2, "content2"),
new IncomingMessage(1, 3, 3, "content3"));
var deviceKeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey(), 3L, new SignedPreKey());
var deviceKeys = List.of(1L, 2L, 3L).stream().collect(Collectors.toMap(Function.identity(), n -> KeysHelper.signedPreKey(n + 100, pniIdentityKeyPair)));
final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 47, 3L, 89);
@ -1674,7 +1680,8 @@ class AccountControllerTest {
@Test
void testChangePhoneNumberSameNumberChangePrekeys() throws Exception {
final String code = "987654";
final String pniIdentityKey = "changed-pni-identity-key";
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
final String pniIdentityKey = KeysHelper.serializeIdentityKey(pniIdentityKeyPair);
final byte[] sessionId = "session-id".getBytes(StandardCharsets.UTF_8);
Device device2 = mock(Device.class);
@ -1700,7 +1707,7 @@ class AccountControllerTest {
var deviceMessages = List.of(
new IncomingMessage(1, 2, 2, "content2"),
new IncomingMessage(1, 3, 3, "content3"));
var deviceKeys = Map.of(1L, new SignedPreKey(), 2L, new SignedPreKey(), 3L, new SignedPreKey());
var deviceKeys = List.of(1L, 2L, 3L).stream().collect(Collectors.toMap(Function.identity(), n -> KeysHelper.signedPreKey(n + 100, pniIdentityKeyPair)));
final Map<Long, Integer> registrationIds = Map.of(1L, 17, 2L, 47, 3L, 89);

View File

@ -23,6 +23,8 @@ import java.util.concurrent.CompletableFuture;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.controllers.MismatchedDevicesException;
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
@ -34,6 +36,7 @@ import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import org.whispersystems.textsecuregcm.tests.util.KeysHelper;
class AccountsManagerChangeNumberIntegrationTest {
@ -144,7 +147,8 @@ class AccountsManagerChangeNumberIntegrationTest {
final String originalNumber = "+18005551111";
final String secondNumber = "+18005552222";
final int rotatedPniRegistrationId = 17;
final SignedPreKey rotatedSignedPreKey = new SignedPreKey(1, "test", "test");
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
final SignedPreKey rotatedSignedPreKey = KeysHelper.signedPreKey(1L, pniIdentityKeyPair);
final AccountAttributes accountAttributes = new AccountAttributes(true, rotatedPniRegistrationId + 1, "test", null, true, new Device.DeviceCapabilities());
final Account account = accountsManager.create(originalNumber, "password", null, accountAttributes, new ArrayList<>());
@ -153,7 +157,7 @@ class AccountsManagerChangeNumberIntegrationTest {
final UUID originalUuid = account.getUuid();
final UUID originalPni = account.getPhoneNumberIdentifier();
final String pniIdentityKey = "changed-pni-identity-key";
final String pniIdentityKey = KeysHelper.serializeIdentityKey(pniIdentityKeyPair);
final Map<Long, SignedPreKey> preKeys = Map.of(Device.MASTER_ID, rotatedSignedPreKey);
final Map<Long, Integer> registrationIds = Map.of(Device.MASTER_ID, rotatedPniRegistrationId);

View File

@ -24,6 +24,7 @@ import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
import java.time.Duration;
import java.util.Base64;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@ -39,6 +40,8 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
@ -59,6 +62,7 @@ import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.Keys;
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
import org.whispersystems.textsecuregcm.tests.util.KeysHelper;
@ExtendWith(DropwizardExtensionsSupport.class)
class KeysControllerTest {
@ -76,6 +80,12 @@ class KeysControllerTest {
private static final int SAMPLE_PNI_REGISTRATION_ID = 1717;
private final ECKeyPair IDENTITY_KEY_PAIR = Curve.generateKeyPair();
private final String IDENTITY_KEY = KeysHelper.serializeIdentityKey(IDENTITY_KEY_PAIR);
private final ECKeyPair PNI_IDENTITY_KEY_PAIR = Curve.generateKeyPair();
private final String PNI_IDENTITY_KEY = KeysHelper.serializeIdentityKey(PNI_IDENTITY_KEY_PAIR);
private final PreKey SAMPLE_KEY = new PreKey(1234, "test1");
private final PreKey SAMPLE_KEY2 = new PreKey(5667, "test3");
private final PreKey SAMPLE_KEY3 = new PreKey(334, "test5");
@ -83,14 +93,14 @@ class KeysControllerTest {
private final PreKey SAMPLE_KEY_PNI = new PreKey(7777, "test7");
private final SignedPreKey SAMPLE_SIGNED_KEY = new SignedPreKey( 1111, "foofoo", "sig11" );
private final SignedPreKey SAMPLE_SIGNED_KEY2 = new SignedPreKey( 2222, "foobar", "sig22" );
private final SignedPreKey SAMPLE_SIGNED_KEY3 = new SignedPreKey( 3333, "barfoo", "sig33" );
private final SignedPreKey SAMPLE_SIGNED_PNI_KEY = new SignedPreKey( 4444, "foofoopni", "sig44" );
private final SignedPreKey SAMPLE_SIGNED_PNI_KEY2 = new SignedPreKey( 5555, "foobarpni", "sig55" );
private final SignedPreKey SAMPLE_SIGNED_PNI_KEY3 = new SignedPreKey( 6666, "barfoopni", "sig66" );
private final SignedPreKey VALID_DEVICE_SIGNED_KEY = new SignedPreKey(89898, "zoofarb", "sigvalid");
private final SignedPreKey VALID_DEVICE_PNI_SIGNED_KEY = new SignedPreKey(7777, "zoofarber", "sigvalidest");
private final SignedPreKey SAMPLE_SIGNED_KEY = KeysHelper.signedPreKey( 1111, IDENTITY_KEY_PAIR);
private final SignedPreKey SAMPLE_SIGNED_KEY2 = KeysHelper.signedPreKey( 2222, IDENTITY_KEY_PAIR);
private final SignedPreKey SAMPLE_SIGNED_KEY3 = KeysHelper.signedPreKey( 3333, IDENTITY_KEY_PAIR);
private final SignedPreKey SAMPLE_SIGNED_PNI_KEY = KeysHelper.signedPreKey( 4444, PNI_IDENTITY_KEY_PAIR);
private final SignedPreKey SAMPLE_SIGNED_PNI_KEY2 = KeysHelper.signedPreKey( 5555, PNI_IDENTITY_KEY_PAIR);
private final SignedPreKey SAMPLE_SIGNED_PNI_KEY3 = KeysHelper.signedPreKey( 6666, PNI_IDENTITY_KEY_PAIR);
private final SignedPreKey VALID_DEVICE_SIGNED_KEY = KeysHelper.signedPreKey(89898, IDENTITY_KEY_PAIR);
private final SignedPreKey VALID_DEVICE_PNI_SIGNED_KEY = KeysHelper.signedPreKey(7777, PNI_IDENTITY_KEY_PAIR);
private final static Keys KEYS = mock(Keys.class );
private final static AccountsManager accounts = mock(AccountsManager.class );
@ -153,8 +163,8 @@ class KeysControllerTest {
when(existsAccount.getDevice(22L)).thenReturn(Optional.empty());
when(existsAccount.getDevices()).thenReturn(allDevices);
when(existsAccount.isEnabled()).thenReturn(true);
when(existsAccount.getIdentityKey()).thenReturn("existsidentitykey");
when(existsAccount.getPhoneNumberIdentityKey()).thenReturn("existspniidentitykey");
when(existsAccount.getIdentityKey()).thenReturn(IDENTITY_KEY);
when(existsAccount.getPhoneNumberIdentityKey()).thenReturn(PNI_IDENTITY_KEY);
when(existsAccount.getNumber()).thenReturn(EXISTS_NUMBER);
when(existsAccount.getUnidentifiedAccessKey()).thenReturn(Optional.of("1337".getBytes()));
@ -234,7 +244,7 @@ class KeysControllerTest {
@Test
void putSignedPreKeyV2() {
SignedPreKey test = new SignedPreKey(9998, "fooozzz", "baaarzzz");
SignedPreKey test = KeysHelper.signedPreKey(9998, IDENTITY_KEY_PAIR);
Response response = resources.getJerseyTest()
.target("/v2/keys/signed")
.request()
@ -250,7 +260,7 @@ class KeysControllerTest {
@Test
void putPhoneNumberIdentitySignedPreKeyV2() {
final SignedPreKey replacementKey = new SignedPreKey(9998, "fooozzz", "baaarzzz");
final SignedPreKey replacementKey = KeysHelper.signedPreKey(9998, PNI_IDENTITY_KEY_PAIR);
Response response = resources.getJerseyTest()
.target("/v2/keys/signed")
@ -268,7 +278,7 @@ class KeysControllerTest {
@Test
void disabledPutSignedPreKeyV2() {
SignedPreKey test = new SignedPreKey(9999, "fooozzz", "baaarzzz");
SignedPreKey test = KeysHelper.signedPreKey(9999, IDENTITY_KEY_PAIR);
Response response = resources.getJerseyTest()
.target("/v2/keys/signed")
.request()
@ -514,8 +524,9 @@ class KeysControllerTest {
@Test
void putKeysTestV2() {
final PreKey preKey = new PreKey(31337, "foobar");
final SignedPreKey signedPreKey = new SignedPreKey(31338, "foobaz", "myvalidsig");
final String identityKey = "barbar";
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
final SignedPreKey signedPreKey = KeysHelper.signedPreKey(31338, identityKeyPair);
final String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair);
List<PreKey> preKeys = new LinkedList<PreKey>() {{
add(preKey);
@ -540,15 +551,16 @@ class KeysControllerTest {
assertThat(capturedList.get(0).getKeyId()).isEqualTo(31337);
assertThat(capturedList.get(0).getPublicKey()).isEqualTo("foobar");
verify(AuthHelper.VALID_ACCOUNT).setIdentityKey(eq("barbar"));
verify(AuthHelper.VALID_ACCOUNT).setIdentityKey(eq(identityKey));
verify(AuthHelper.VALID_DEVICE).setSignedPreKey(eq(signedPreKey));
verify(accounts).update(eq(AuthHelper.VALID_ACCOUNT), any());
}
@Test
void putKeysByPhoneNumberIdentifierTestV2() {
final SignedPreKey signedPreKey = new SignedPreKey(31338, "foobaz", "myvalidsig");
final String identityKey = "barbar";
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
final SignedPreKey signedPreKey = KeysHelper.signedPreKey(31338, identityKeyPair);
final String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair);
List<PreKey> preKeys = List.of(new PreKey(31337, "foobar"));
@ -572,16 +584,32 @@ class KeysControllerTest {
assertThat(capturedList.get(0).getKeyId()).isEqualTo(31337);
assertThat(capturedList.get(0).getPublicKey()).isEqualTo("foobar");
verify(AuthHelper.VALID_ACCOUNT).setPhoneNumberIdentityKey(eq("barbar"));
verify(AuthHelper.VALID_ACCOUNT).setPhoneNumberIdentityKey(eq(identityKey));
verify(AuthHelper.VALID_DEVICE).setPhoneNumberIdentitySignedPreKey(eq(signedPreKey));
verify(accounts).update(eq(AuthHelper.VALID_ACCOUNT), any());
}
@Test
void putPrekeyWithInvalidSignature() {
final SignedPreKey badSignedPreKey = new SignedPreKey(1L, "foo", "bar");
PreKeyState preKeyState = new PreKeyState(IDENTITY_KEY, badSignedPreKey, List.of());
Response response =
resources.getJerseyTest()
.target("/v2/keys")
.queryParam("identity", "aci")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(preKeyState, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(422);
}
@Test
void disabledPutKeysTestV2() {
final PreKey preKey = new PreKey(31337, "foobar");
final SignedPreKey signedPreKey = new SignedPreKey(31338, "foobaz", "myvalidsig");
final String identityKey = "barbar";
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
final SignedPreKey signedPreKey = KeysHelper.signedPreKey(31338, identityKeyPair);
final String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair);
List<PreKey> preKeys = new LinkedList<PreKey>() {{
add(preKey);
@ -606,7 +634,7 @@ class KeysControllerTest {
assertThat(capturedList.get(0).getKeyId()).isEqualTo(31337);
assertThat(capturedList.get(0).getPublicKey()).isEqualTo("foobar");
verify(AuthHelper.DISABLED_ACCOUNT).setIdentityKey(eq("barbar"));
verify(AuthHelper.DISABLED_ACCOUNT).setIdentityKey(eq(identityKey));
verify(AuthHelper.DISABLED_DEVICE).setSignedPreKey(eq(signedPreKey));
verify(accounts).update(eq(AuthHelper.DISABLED_ACCOUNT), any());
}
@ -614,12 +642,11 @@ class KeysControllerTest {
@Test
void putIdentityKeyNonPrimary() {
final PreKey preKey = new PreKey(31337, "foobar");
final SignedPreKey signedPreKey = new SignedPreKey(31338, "foobaz", "myvalidsig");
final String identityKey = "barbar";
final SignedPreKey signedPreKey = KeysHelper.signedPreKey(31338, IDENTITY_KEY_PAIR);
List<PreKey> preKeys = List.of(preKey);
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, preKeys);
PreKeyState preKeyState = new PreKeyState(IDENTITY_KEY, signedPreKey, preKeys);
Response response =
resources.getJerseyTest()

View File

@ -0,0 +1,23 @@
/*
* Copyright 2013 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.tests.util;
import java.util.Base64;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
public final class KeysHelper {
public static String serializeIdentityKey(ECKeyPair keyPair) {
return Base64.getEncoder().encodeToString(keyPair.getPublicKey().serialize());
}
public static SignedPreKey signedPreKey(long id, final ECKeyPair signingKey) {
final byte[] pubKey = Curve.generateKeyPair().getPublicKey().serialize();
final byte[] sig = signingKey.getPrivateKey().calculateSignature(pubKey);
return new SignedPreKey(id, Base64.getEncoder().encodeToString(pubKey), Base64.getEncoder().encodeToString(sig));
}
}