Represent pre-key public keys and signatures as byte arrays in DAOs
This commit is contained in:
parent
4a8ad3103c
commit
217b68a1e0
|
@ -318,12 +318,12 @@ public final class Operations {
|
|||
private static SignedPreKey generateSignedECPreKey(long id, final ECKeyPair identityKeyPair) {
|
||||
final byte[] pubKey = Curve.generateKeyPair().getPublicKey().serialize();
|
||||
final byte[] sig = identityKeyPair.getPrivateKey().calculateSignature(pubKey);
|
||||
return new SignedPreKey(id, Base64.getEncoder().encodeToString(pubKey), Base64.getEncoder().encodeToString(sig));
|
||||
return new SignedPreKey(id, pubKey, sig);
|
||||
}
|
||||
|
||||
private static SignedPreKey generateSignedKEMPreKey(long id, final ECKeyPair identityKeyPair) {
|
||||
final byte[] pubKey = KEMKeyPair.generate(KEMKeyType.KYBER_1024).getPublicKey().serialize();
|
||||
final byte[] sig = identityKeyPair.getPrivateKey().calculateSignature(pubKey);
|
||||
return new SignedPreKey(id, Base64.getEncoder().encodeToString(pubKey), Base64.getEncoder().encodeToString(sig));
|
||||
return new SignedPreKey(id, pubKey, sig);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,14 @@
|
|||
package org.whispersystems.textsecuregcm.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
public class PreKey {
|
||||
|
||||
|
@ -16,22 +22,24 @@ public class PreKey {
|
|||
private long keyId;
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotEmpty
|
||||
private String publicKey;
|
||||
private byte[] publicKey;
|
||||
|
||||
public PreKey() {}
|
||||
|
||||
public PreKey(long keyId, String publicKey)
|
||||
public PreKey(long keyId, byte[] publicKey)
|
||||
{
|
||||
this.keyId = keyId;
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
public String getPublicKey() {
|
||||
public byte[] getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public void setPublicKey(String publicKey) {
|
||||
public void setPublicKey(byte[] publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
|
@ -44,23 +52,17 @@ public class PreKey {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object == null || !(object instanceof PreKey)) return false;
|
||||
PreKey that = (PreKey)object;
|
||||
|
||||
if (publicKey == null) {
|
||||
return this.keyId == that.keyId && that.publicKey == null;
|
||||
} else {
|
||||
return this.keyId == that.keyId && this.publicKey.equals(that.publicKey);
|
||||
}
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PreKey preKey = (PreKey) o;
|
||||
return keyId == preKey.keyId && Arrays.equals(publicKey, preKey.publicKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (publicKey == null) {
|
||||
return (int)this.keyId;
|
||||
} else {
|
||||
return ((int)this.keyId) ^ publicKey.hashCode();
|
||||
}
|
||||
int result = Objects.hash(keyId);
|
||||
result = 31 * result + Arrays.hashCode(publicKey);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,17 +18,13 @@ public abstract class PreKeySignatureValidator {
|
|||
public static final Counter INVALID_SIGNATURE_COUNTER =
|
||||
Metrics.counter(name(PreKeySignatureValidator.class, "invalidPreKeySignature"));
|
||||
|
||||
public static final boolean validatePreKeySignatures(final String identityKeyB64, final Collection<SignedPreKey> spks) {
|
||||
public static boolean validatePreKeySignatures(final String identityKeyB64, final Collection<SignedPreKey> spks) {
|
||||
try {
|
||||
final byte[] identityKeyBytes = Base64.getDecoder().decode(identityKeyB64);
|
||||
final ECPublicKey identityKey = Curve.decodePoint(identityKeyBytes, 0);
|
||||
|
||||
final boolean success = spks.stream().allMatch(spk -> {
|
||||
final byte[] prekeyBytes = Base64.getDecoder().decode(spk.getPublicKey());
|
||||
final byte[] prekeySignatureBytes = Base64.getDecoder().decode(spk.getSignature());
|
||||
|
||||
return identityKey.verifySignature(prekeyBytes, prekeySignatureBytes);
|
||||
});
|
||||
final boolean success = spks.stream()
|
||||
.allMatch(spk -> identityKey.verifySignature(spk.getPublicKey(), spk.getSignature()));
|
||||
|
||||
if (!success) {
|
||||
INVALID_SIGNATURE_COUNTER.increment();
|
||||
|
|
|
@ -6,43 +6,45 @@
|
|||
package org.whispersystems.textsecuregcm.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SignedPreKey extends PreKey {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||
@NotEmpty
|
||||
private String signature;
|
||||
private byte[] signature;
|
||||
|
||||
public SignedPreKey() {}
|
||||
|
||||
public SignedPreKey(long keyId, String publicKey, String signature) {
|
||||
public SignedPreKey(long keyId, byte[] publicKey, byte[] signature) {
|
||||
super(keyId, publicKey);
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object == null || !(object instanceof SignedPreKey)) return false;
|
||||
SignedPreKey that = (SignedPreKey) object;
|
||||
|
||||
if (signature == null) {
|
||||
return super.equals(object) && that.signature == null;
|
||||
} else {
|
||||
return super.equals(object) && this.signature.equals(that.signature);
|
||||
}
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
SignedPreKey that = (SignedPreKey) o;
|
||||
return Arrays.equals(signature, that.signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (signature == null) {
|
||||
return super.hashCode();
|
||||
} else {
|
||||
return super.hashCode() ^ signature.hashCode();
|
||||
}
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(signature);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -374,24 +374,23 @@ public class Keys extends AbstractDynamoDbStore {
|
|||
return Map.of(
|
||||
KEY_ACCOUNT_UUID, getPartitionKey(accountUuid),
|
||||
KEY_DEVICE_ID_KEY_ID, getSortKey(deviceId, spk.getKeyId()),
|
||||
KEY_PUBLIC_KEY, AttributeValues.fromByteArray(Base64.getDecoder().decode(spk.getPublicKey())),
|
||||
KEY_SIGNATURE, AttributeValues.fromByteArray(Base64.getDecoder().decode(spk.getSignature())));
|
||||
KEY_PUBLIC_KEY, AttributeValues.fromByteArray(spk.getPublicKey()),
|
||||
KEY_SIGNATURE, AttributeValues.fromByteArray(spk.getSignature()));
|
||||
}
|
||||
return Map.of(
|
||||
KEY_ACCOUNT_UUID, getPartitionKey(accountUuid),
|
||||
KEY_DEVICE_ID_KEY_ID, getSortKey(deviceId, preKey.getKeyId()),
|
||||
KEY_PUBLIC_KEY, AttributeValues.fromByteArray(Base64.getDecoder().decode(preKey.getPublicKey())));
|
||||
KEY_PUBLIC_KEY, AttributeValues.fromByteArray(preKey.getPublicKey()));
|
||||
}
|
||||
|
||||
private PreKey getPreKeyFromItem(Map<String, AttributeValue> item) {
|
||||
final long keyId = item.get(KEY_DEVICE_ID_KEY_ID).b().asByteBuffer().getLong(8);
|
||||
final String publicKey = Base64.getEncoder().encodeToString(extractByteArray(item.get(KEY_PUBLIC_KEY)));
|
||||
final byte[] publicKey = extractByteArray(item.get(KEY_PUBLIC_KEY));
|
||||
|
||||
if (item.containsKey(KEY_SIGNATURE)) {
|
||||
// All PQ prekeys are signed, and therefore have this attribute. Signed EC prekeys are stored
|
||||
// in the Accounts table, so EC prekeys retrieved by this class are never SignedPreKeys.
|
||||
final String signature = Base64.getEncoder().encodeToString(extractByteArray(item.get(KEY_SIGNATURE)));
|
||||
return new SignedPreKey(keyId, publicKey, signature);
|
||||
return new SignedPreKey(keyId, publicKey, extractByteArray(item.get(KEY_SIGNATURE)));
|
||||
}
|
||||
return new PreKey(keyId, publicKey);
|
||||
}
|
||||
|
|
|
@ -61,6 +61,8 @@ import org.junit.jupiter.params.provider.EnumSource;
|
|||
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.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.PhoneVerificationTokenManager;
|
||||
|
@ -72,7 +74,6 @@ import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse;
|
|||
import org.whispersystems.textsecuregcm.entities.ChangeNumberRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.PhoneNumberDiscoverabilityRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession;
|
||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.mappers.ImpossiblePhoneNumberExceptionMapper;
|
||||
|
@ -87,6 +88,7 @@ import org.whispersystems.textsecuregcm.storage.Device;
|
|||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
||||
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.SystemMapper;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
||||
|
@ -786,12 +788,18 @@ class AccountControllerV2Test {
|
|||
static Account buildTestAccountForDataReport(final UUID aci, final String number,
|
||||
final boolean unrestrictedUnidentifiedAccess, final boolean discoverableByPhoneNumber,
|
||||
List<AccountBadge> badges, List<DeviceData> devices) {
|
||||
|
||||
final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair();
|
||||
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
|
||||
|
||||
final Account account = new Account();
|
||||
account.setUuid(aci);
|
||||
account.setNumber(number, UUID.randomUUID());
|
||||
account.setUnrestrictedUnidentifiedAccess(unrestrictedUnidentifiedAccess);
|
||||
account.setDiscoverableByPhoneNumber(discoverableByPhoneNumber);
|
||||
account.setBadges(Clock.systemUTC(), new ArrayList<>(badges));
|
||||
account.setIdentityKey(KeysHelper.serializeIdentityKey(aciIdentityKeyPair));
|
||||
account.setPhoneNumberIdentityKey(KeysHelper.serializeIdentityKey(pniIdentityKeyPair));
|
||||
|
||||
assert !devices.isEmpty();
|
||||
|
||||
|
@ -802,7 +810,8 @@ class AccountControllerV2Test {
|
|||
device.setId(deviceData.id);
|
||||
device.setAuthTokenHash(passwordTokenHash);
|
||||
device.setFetchesMessages(true);
|
||||
device.setSignedPreKey(new SignedPreKey(1, "publicKey", "signature"));
|
||||
device.setSignedPreKey(KeysHelper.signedECPreKey(1, aciIdentityKeyPair));
|
||||
device.setPhoneNumberIdentitySignedPreKey(KeysHelper.signedECPreKey(2, pniIdentityKeyPair));
|
||||
device.setLastSeen(deviceData.lastSeen().toEpochMilli());
|
||||
device.setCreated(deviceData.created().toEpochMilli());
|
||||
device.setUserAgent(deviceData.userAgent());
|
||||
|
|
|
@ -69,6 +69,8 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
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;
|
||||
|
@ -101,6 +103,7 @@ import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
|||
import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
||||
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.Pair;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
import org.whispersystems.websocket.Stories;
|
||||
|
@ -163,13 +166,17 @@ class MessageControllerTest {
|
|||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||
|
||||
|
||||
|
||||
final List<Device> singleDeviceList = List.of(
|
||||
generateTestDevice(SINGLE_DEVICE_ID1, SINGLE_DEVICE_REG_ID1, 1111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis())
|
||||
generateTestDevice(SINGLE_DEVICE_ID1, SINGLE_DEVICE_REG_ID1, 1111, KeysHelper.signedECPreKey(333, identityKeyPair), System.currentTimeMillis(), System.currentTimeMillis())
|
||||
);
|
||||
|
||||
final List<Device> multiDeviceList = List.of(
|
||||
generateTestDevice(MULTI_DEVICE_ID1, MULTI_DEVICE_REG_ID1, 2222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis()),
|
||||
generateTestDevice(MULTI_DEVICE_ID2, MULTI_DEVICE_REG_ID2, 3333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), System.currentTimeMillis()),
|
||||
generateTestDevice(MULTI_DEVICE_ID1, MULTI_DEVICE_REG_ID1, 2222, KeysHelper.signedECPreKey(111, identityKeyPair), System.currentTimeMillis(), System.currentTimeMillis()),
|
||||
generateTestDevice(MULTI_DEVICE_ID2, MULTI_DEVICE_REG_ID2, 3333, KeysHelper.signedECPreKey(222, identityKeyPair), System.currentTimeMillis(), System.currentTimeMillis()),
|
||||
generateTestDevice(MULTI_DEVICE_ID3, MULTI_DEVICE_REG_ID3, 4444, null, System.currentTimeMillis(), System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31))
|
||||
);
|
||||
|
||||
|
|
|
@ -134,11 +134,6 @@ class AccountsManagerConcurrentModificationIntegrationTest {
|
|||
accountsManager.create("+14155551212", "password", null, new AccountAttributes(), new ArrayList<>()),
|
||||
a -> {
|
||||
a.setUnidentifiedAccessKey(new byte[16]);
|
||||
|
||||
final Random random = new Random();
|
||||
final SignedPreKey signedPreKey = new SignedPreKey(random.nextInt(), "testPublicKey-" + random.nextInt(),
|
||||
"testSignature-" + random.nextInt());
|
||||
|
||||
a.removeDevice(1);
|
||||
a.addDevice(DevicesHelper.createDevice(1));
|
||||
});
|
||||
|
|
|
@ -49,6 +49,8 @@ import org.junit.jupiter.params.provider.Arguments;
|
|||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.mockito.stubbing.Answer;
|
||||
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;
|
||||
|
@ -61,6 +63,7 @@ import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2
|
|||
import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.DevicesHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.KeysHelper;
|
||||
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
||||
|
||||
class AccountsManagerTest {
|
||||
|
@ -506,7 +509,7 @@ class AccountsManagerTest {
|
|||
|
||||
Device enabledDevice = new Device();
|
||||
enabledDevice.setFetchesMessages(true);
|
||||
enabledDevice.setSignedPreKey(new SignedPreKey(1L, "key", "signature"));
|
||||
enabledDevice.setSignedPreKey(KeysHelper.signedECPreKey(1, Curve.generateKeyPair()));
|
||||
enabledDevice.setLastSeen(System.currentTimeMillis());
|
||||
final long deviceId = account.getNextDeviceId();
|
||||
enabledDevice.setId(deviceId);
|
||||
|
@ -720,12 +723,13 @@ class AccountsManagerTest {
|
|||
final UUID uuid = UUID.randomUUID();
|
||||
final UUID originalPni = UUID.randomUUID();
|
||||
final UUID targetPni = UUID.randomUUID();
|
||||
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||
final Map<Long, SignedPreKey> newSignedKeys = Map.of(
|
||||
1L, new SignedPreKey(1L, "pub1", "sig1"),
|
||||
2L, new SignedPreKey(2L, "pub2", "sig2"));
|
||||
1L, KeysHelper.signedECPreKey(1, identityKeyPair),
|
||||
2L, KeysHelper.signedECPreKey(2, identityKeyPair));
|
||||
final Map<Long, SignedPreKey> newSignedPqKeys = Map.of(
|
||||
1L, new SignedPreKey(3L, "pub3", "sig3"),
|
||||
2L, new SignedPreKey(4L, "pub4", "sig4"));
|
||||
1L, KeysHelper.signedKEMPreKey(3, identityKeyPair),
|
||||
2L, KeysHelper.signedKEMPreKey(4, identityKeyPair));
|
||||
final Map<Long, Integer> newRegistrationIds = Map.of(1L, 201, 2L, 202);
|
||||
|
||||
final Account existingAccount = AccountsHelper.generateTestAccount(targetNumber, existingAccountUuid, targetPni, new ArrayList<>(), new byte[16]);
|
||||
|
@ -747,7 +751,7 @@ class AccountsManagerTest {
|
|||
verify(keys).delete(newPni);
|
||||
verify(keys).delete(originalPni);
|
||||
verify(keys).getPqEnabledDevices(uuid);
|
||||
verify(keys).storePqLastResort(eq(newPni), eq(Map.of(1L, new SignedPreKey(3L, "pub3", "sig3"))));
|
||||
verify(keys).storePqLastResort(eq(newPni), eq(Map.of(1L, newSignedPqKeys.get(1L))));
|
||||
verifyNoMoreInteractions(keys);
|
||||
}
|
||||
|
||||
|
@ -768,9 +772,10 @@ class AccountsManagerTest {
|
|||
|
||||
List<Device> devices = List.of(DevicesHelper.createDevice(1L, 0L, 101), DevicesHelper.createDevice(2L, 0L, 102));
|
||||
Account account = AccountsHelper.generateTestAccount(number, UUID.randomUUID(), UUID.randomUUID(), devices, new byte[16]);
|
||||
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||
Map<Long, SignedPreKey> newSignedKeys = Map.of(
|
||||
1L, new SignedPreKey(1L, "pub1", "sig1"),
|
||||
2L, new SignedPreKey(2L, "pub2", "sig2"));
|
||||
1L, KeysHelper.signedECPreKey(1, identityKeyPair),
|
||||
2L, KeysHelper.signedECPreKey(2, identityKeyPair));
|
||||
Map<Long, Integer> newRegistrationIds = Map.of(1L, 201, 2L, 202);
|
||||
|
||||
UUID oldUuid = account.getUuid();
|
||||
|
@ -807,12 +812,13 @@ class AccountsManagerTest {
|
|||
|
||||
List<Device> devices = List.of(DevicesHelper.createDevice(1L, 0L, 101), DevicesHelper.createDevice(2L, 0L, 102));
|
||||
Account account = AccountsHelper.generateTestAccount(number, UUID.randomUUID(), UUID.randomUUID(), devices, new byte[16]);
|
||||
Map<Long, SignedPreKey> newSignedKeys = Map.of(
|
||||
1L, new SignedPreKey(1L, "pub1", "sig1"),
|
||||
2L, new SignedPreKey(2L, "pub2", "sig2"));
|
||||
Map<Long, SignedPreKey> newSignedPqKeys = Map.of(
|
||||
1L, new SignedPreKey(3L, "pub3", "sig3"),
|
||||
2L, new SignedPreKey(4L, "pub4", "sig4"));
|
||||
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||
final Map<Long, SignedPreKey> newSignedKeys = Map.of(
|
||||
1L, KeysHelper.signedECPreKey(1, identityKeyPair),
|
||||
2L, KeysHelper.signedECPreKey(2, identityKeyPair));
|
||||
final Map<Long, SignedPreKey> newSignedPqKeys = Map.of(
|
||||
1L, KeysHelper.signedKEMPreKey(3, identityKeyPair),
|
||||
2L, KeysHelper.signedKEMPreKey(4, identityKeyPair));
|
||||
Map<Long, Integer> newRegistrationIds = Map.of(1L, 201, 2L, 202);
|
||||
|
||||
UUID oldUuid = account.getUuid();
|
||||
|
@ -947,7 +953,7 @@ class AccountsManagerTest {
|
|||
final Device device = new Device();
|
||||
device.setId(Device.MASTER_ID);
|
||||
device.setFetchesMessages(true);
|
||||
device.setSignedPreKey(new SignedPreKey(1, "key", "sig"));
|
||||
device.setSignedPreKey(KeysHelper.signedECPreKey(1, Curve.generateKeyPair()));
|
||||
device.setLastSeen(lastSeen);
|
||||
|
||||
return device;
|
||||
|
|
|
@ -20,9 +20,12 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
|||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.signal.libsignal.protocol.ecc.ECKeyPair;
|
||||
import org.whispersystems.textsecuregcm.entities.PreKey;
|
||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||
import org.whispersystems.textsecuregcm.tests.util.KeysHelper;
|
||||
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
||||
import software.amazon.awssdk.core.SdkBytes;
|
||||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||
|
@ -211,9 +214,11 @@ class KeysTest {
|
|||
void testStorePqLastResort() {
|
||||
assertEquals(0, getLastResortCount(ACCOUNT_UUID));
|
||||
|
||||
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||
|
||||
keys.storePqLastResort(
|
||||
ACCOUNT_UUID,
|
||||
Map.of(1L, new SignedPreKey(1L, "pub1", "sig1"), 2L, new SignedPreKey(2L, "pub2", "sig2")));
|
||||
Map.of(1L, KeysHelper.signedKEMPreKey(1, identityKeyPair), 2L, KeysHelper.signedKEMPreKey(2, identityKeyPair)));
|
||||
assertEquals(2, getLastResortCount(ACCOUNT_UUID));
|
||||
assertEquals(1L, keys.getLastResort(ACCOUNT_UUID, 1L).get().getKeyId());
|
||||
assertEquals(2L, keys.getLastResort(ACCOUNT_UUID, 2L).get().getKeyId());
|
||||
|
@ -221,7 +226,7 @@ class KeysTest {
|
|||
|
||||
keys.storePqLastResort(
|
||||
ACCOUNT_UUID,
|
||||
Map.of(1L, new SignedPreKey(3L, "pub3", "sig3"), 3L, new SignedPreKey(4L, "pub4", "sig4")));
|
||||
Map.of(1L, KeysHelper.signedKEMPreKey(3, identityKeyPair), 3L, KeysHelper.signedKEMPreKey(4, identityKeyPair)));
|
||||
assertEquals(3, getLastResortCount(ACCOUNT_UUID), "storing new last-resort keys should not create duplicates");
|
||||
assertEquals(3L, keys.getLastResort(ACCOUNT_UUID, 1L).get().getKeyId(), "storing new last-resort keys should overwrite old ones");
|
||||
assertEquals(2L, keys.getLastResort(ACCOUNT_UUID, 2L).get().getKeyId(), "storing new last-resort keys should leave untouched ones alone");
|
||||
|
@ -242,9 +247,11 @@ class KeysTest {
|
|||
|
||||
@Test
|
||||
void testGetPqEnabledDevices() {
|
||||
keys.store(ACCOUNT_UUID, DEVICE_ID, null, List.of(new SignedPreKey(1L, "pub1", "sig1")), null);
|
||||
keys.store(ACCOUNT_UUID, DEVICE_ID + 1, null, null, new SignedPreKey(2L, "pub2", "sig2"));
|
||||
keys.store(ACCOUNT_UUID, DEVICE_ID + 2, null, List.of(new SignedPreKey(3L, "pub3", "sig3")), new SignedPreKey(4L, "pub4", "sig4"));
|
||||
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||
|
||||
keys.store(ACCOUNT_UUID, DEVICE_ID, null, List.of(KeysHelper.signedKEMPreKey(1, identityKeyPair)), null);
|
||||
keys.store(ACCOUNT_UUID, DEVICE_ID + 1, null, null, KeysHelper.signedKEMPreKey(2, identityKeyPair));
|
||||
keys.store(ACCOUNT_UUID, DEVICE_ID + 2, null, List.of(KeysHelper.signedKEMPreKey(3, identityKeyPair)), KeysHelper.signedKEMPreKey(4, identityKeyPair));
|
||||
keys.store(ACCOUNT_UUID, DEVICE_ID + 3, null, null, null);
|
||||
assertIterableEquals(
|
||||
Set.of(DEVICE_ID + 1, DEVICE_ID + 2),
|
||||
|
@ -291,7 +298,7 @@ class KeysTest {
|
|||
final byte[] key = new byte[32];
|
||||
new SecureRandom().nextBytes(key);
|
||||
|
||||
return new PreKey(keyId, Base64.getEncoder().encodeToString(key));
|
||||
return new PreKey(keyId, key);
|
||||
}
|
||||
|
||||
private static SignedPreKey generateTestSignedPreKey(final long keyId) {
|
||||
|
@ -302,6 +309,6 @@ class KeysTest {
|
|||
secureRandom.nextBytes(key);
|
||||
secureRandom.nextBytes(signature);
|
||||
|
||||
return new SignedPreKey(keyId, Base64.getEncoder().encodeToString(key), Base64.getEncoder().encodeToString(signature));
|
||||
return new SignedPreKey(keyId, key, signature);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -514,7 +514,7 @@ class DeviceControllerTest {
|
|||
private static SignedPreKey signedPreKeyWithBadSignature(final SignedPreKey signedPreKey) {
|
||||
return new SignedPreKey(signedPreKey.getKeyId(),
|
||||
signedPreKey.getPublicKey(),
|
||||
Base64.getEncoder().encodeToString("incorrect-signature".getBytes(StandardCharsets.UTF_8)));
|
||||
"incorrect-signature".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -26,7 +26,6 @@ import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
|||
import io.dropwizard.testing.junit5.ResourceExtension;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
|
@ -86,18 +85,18 @@ class KeysControllerTest {
|
|||
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");
|
||||
private final PreKey SAMPLE_KEY4 = new PreKey(336, "test6");
|
||||
private final PreKey SAMPLE_KEY = KeysHelper.ecPreKey(1234);
|
||||
private final PreKey SAMPLE_KEY2 = KeysHelper.ecPreKey(5667);
|
||||
private final PreKey SAMPLE_KEY3 = KeysHelper.ecPreKey(334);
|
||||
private final PreKey SAMPLE_KEY4 = KeysHelper.ecPreKey(336);
|
||||
|
||||
private final PreKey SAMPLE_KEY_PNI = new PreKey(7777, "test7");
|
||||
private final PreKey SAMPLE_KEY_PNI = KeysHelper.ecPreKey(7777);
|
||||
|
||||
private final SignedPreKey SAMPLE_PQ_KEY = new SignedPreKey(2424, "test1", "sig");
|
||||
private final SignedPreKey SAMPLE_PQ_KEY2 = new SignedPreKey(6868, "test3", "sig");
|
||||
private final SignedPreKey SAMPLE_PQ_KEY3 = new SignedPreKey(1313, "test5", "sig");
|
||||
private final SignedPreKey SAMPLE_PQ_KEY = KeysHelper.signedKEMPreKey(2424, Curve.generateKeyPair());
|
||||
private final SignedPreKey SAMPLE_PQ_KEY2 = KeysHelper.signedKEMPreKey(6868, Curve.generateKeyPair());
|
||||
private final SignedPreKey SAMPLE_PQ_KEY3 = KeysHelper.signedKEMPreKey(1313, Curve.generateKeyPair());
|
||||
|
||||
private final SignedPreKey SAMPLE_PQ_KEY_PNI = new SignedPreKey(8888, "test7", "sig");
|
||||
private final SignedPreKey SAMPLE_PQ_KEY_PNI = KeysHelper.signedKEMPreKey(8888, Curve.generateKeyPair());
|
||||
|
||||
private final SignedPreKey SAMPLE_SIGNED_KEY = KeysHelper.signedECPreKey(1111, IDENTITY_KEY_PAIR);
|
||||
private final SignedPreKey SAMPLE_SIGNED_KEY2 = KeysHelper.signedECPreKey(2222, IDENTITY_KEY_PAIR);
|
||||
|
@ -656,7 +655,7 @@ class KeysControllerTest {
|
|||
|
||||
@Test
|
||||
void putKeysTestV2() {
|
||||
final PreKey preKey = new PreKey(31337, "foobar");
|
||||
final PreKey preKey = KeysHelper.ecPreKey(31337);
|
||||
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
|
||||
final String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair);
|
||||
|
@ -684,7 +683,7 @@ class KeysControllerTest {
|
|||
|
||||
@Test
|
||||
void putKeysPqTestV2() {
|
||||
final PreKey preKey = new PreKey(31337, "foobar");
|
||||
final PreKey preKey = KeysHelper.ecPreKey(31337);
|
||||
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
|
||||
final SignedPreKey pqPreKey = KeysHelper.signedECPreKey(31339, identityKeyPair);
|
||||
|
@ -716,7 +715,7 @@ class KeysControllerTest {
|
|||
|
||||
@Test
|
||||
void putKeysByPhoneNumberIdentifierTestV2() {
|
||||
final PreKey preKey = new PreKey(31337, "foobar");
|
||||
final PreKey preKey = KeysHelper.ecPreKey(31337);
|
||||
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
|
||||
final String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair);
|
||||
|
@ -745,7 +744,7 @@ class KeysControllerTest {
|
|||
|
||||
@Test
|
||||
void putKeysByPhoneNumberIdentifierPqTestV2() {
|
||||
final PreKey preKey = new PreKey(31337, "foobar");
|
||||
final PreKey preKey = KeysHelper.ecPreKey(31337);
|
||||
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
|
||||
final SignedPreKey pqPreKey = KeysHelper.signedECPreKey(31339, identityKeyPair);
|
||||
|
@ -778,7 +777,7 @@ class KeysControllerTest {
|
|||
|
||||
@Test
|
||||
void putPrekeyWithInvalidSignature() {
|
||||
final SignedPreKey badSignedPreKey = new SignedPreKey(1L, "foo", "bar");
|
||||
final SignedPreKey badSignedPreKey = KeysHelper.signedECPreKey(1, Curve.generateKeyPair());
|
||||
PreKeyState preKeyState = new PreKeyState(IDENTITY_KEY, badSignedPreKey, List.of());
|
||||
Response response =
|
||||
resources.getJerseyTest()
|
||||
|
@ -793,16 +792,12 @@ class KeysControllerTest {
|
|||
|
||||
@Test
|
||||
void disabledPutKeysTestV2() {
|
||||
final PreKey preKey = new PreKey(31337, "foobar");
|
||||
final PreKey preKey = KeysHelper.ecPreKey(31337);
|
||||
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
|
||||
final String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair);
|
||||
|
||||
List<PreKey> preKeys = new LinkedList<PreKey>() {{
|
||||
add(preKey);
|
||||
}};
|
||||
|
||||
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, preKeys);
|
||||
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey));
|
||||
|
||||
Response response =
|
||||
resources.getJerseyTest()
|
||||
|
@ -819,7 +814,7 @@ class KeysControllerTest {
|
|||
List<PreKey> capturedList = listCaptor.getValue();
|
||||
assertThat(capturedList.size()).isEqualTo(1);
|
||||
assertThat(capturedList.get(0).getKeyId()).isEqualTo(31337);
|
||||
assertThat(capturedList.get(0).getPublicKey()).isEqualTo("foobar");
|
||||
assertThat(capturedList.get(0).getPublicKey()).isEqualTo(preKey.getPublicKey());
|
||||
|
||||
verify(AuthHelper.DISABLED_ACCOUNT).setIdentityKey(eq(identityKey));
|
||||
verify(AuthHelper.DISABLED_DEVICE).setSignedPreKey(eq(signedPreKey));
|
||||
|
@ -828,7 +823,7 @@ class KeysControllerTest {
|
|||
|
||||
@Test
|
||||
void putIdentityKeyNonPrimary() {
|
||||
final PreKey preKey = new PreKey(31337, "foobar");
|
||||
final PreKey preKey = KeysHelper.ecPreKey(31337);
|
||||
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, IDENTITY_KEY_PAIR);
|
||||
|
||||
List<PreKey> preKeys = List.of(preKey);
|
||||
|
|
|
@ -14,11 +14,15 @@ import static org.whispersystems.textsecuregcm.tests.util.JsonHelpers.jsonFixtur
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.whispersystems.textsecuregcm.entities.PreKey;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
class PreKeyTest {
|
||||
|
||||
private static final byte[] PUBLIC_KEY = Base64.getDecoder().decode("BQ+NbroQtVKyFaCSfqzSw8Wy72Ff22RSa5ERKTv5DIk2");
|
||||
|
||||
@Test
|
||||
void serializeToJSONV2() throws Exception {
|
||||
PreKey preKey = new PreKey(1234, "test");
|
||||
PreKey preKey = new PreKey(1234, PUBLIC_KEY);
|
||||
|
||||
assertThat("PreKeyV2 Serialization works",
|
||||
asJson(preKey),
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
package org.whispersystems.textsecuregcm.tests.util;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
@ -36,8 +38,8 @@ public class DevicesHelper {
|
|||
|
||||
public static void setEnabled(Device device, boolean enabled) {
|
||||
if (enabled) {
|
||||
device.setSignedPreKey(new SignedPreKey(RANDOM.nextLong(), "testPublicKey-" + RANDOM.nextLong(),
|
||||
"testSignature-" + RANDOM.nextLong()));
|
||||
device.setSignedPreKey(KeysHelper.signedECPreKey(RANDOM.nextLong(), Curve.generateKeyPair()));
|
||||
device.setPhoneNumberIdentitySignedPreKey(KeysHelper.signedECPreKey(RANDOM.nextLong(), Curve.generateKeyPair()));
|
||||
device.setGcmId("testGcmId" + RANDOM.nextLong());
|
||||
device.setLastSeen(Util.todayInMillis());
|
||||
} else {
|
||||
|
|
|
@ -10,22 +10,27 @@ import org.signal.libsignal.protocol.ecc.Curve;
|
|||
import org.signal.libsignal.protocol.ecc.ECKeyPair;
|
||||
import org.signal.libsignal.protocol.kem.KEMKeyPair;
|
||||
import org.signal.libsignal.protocol.kem.KEMKeyType;
|
||||
import org.whispersystems.textsecuregcm.entities.PreKey;
|
||||
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 PreKey ecPreKey(final long id) {
|
||||
return new PreKey(id, Curve.generateKeyPair().getPublicKey().serialize());
|
||||
}
|
||||
|
||||
public static SignedPreKey signedECPreKey(long id, final ECKeyPair identityKeyPair) {
|
||||
final byte[] pubKey = Curve.generateKeyPair().getPublicKey().serialize();
|
||||
final byte[] sig = identityKeyPair.getPrivateKey().calculateSignature(pubKey);
|
||||
return new SignedPreKey(id, Base64.getEncoder().encodeToString(pubKey), Base64.getEncoder().encodeToString(sig));
|
||||
return new SignedPreKey(id, pubKey, sig);
|
||||
}
|
||||
|
||||
public static SignedPreKey signedKEMPreKey(long id, final ECKeyPair identityKeyPair) {
|
||||
final byte[] pubKey = KEMKeyPair.generate(KEMKeyType.KYBER_1024).getPublicKey().serialize();
|
||||
final byte[] sig = identityKeyPair.getPrivateKey().calculateSignature(pubKey);
|
||||
return new SignedPreKey(id, Base64.getEncoder().encodeToString(pubKey), Base64.getEncoder().encodeToString(sig));
|
||||
return new SignedPreKey(id, pubKey, sig);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"keyId" : 1234,
|
||||
"publicKey" : "test"
|
||||
"publicKey" : "BQ+NbroQtVKyFaCSfqzSw8Wy72Ff22RSa5ERKTv5DIk2"
|
||||
}
|
Loading…
Reference in New Issue