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) {
|
private static SignedPreKey generateSignedECPreKey(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);
|
||||||
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) {
|
private static SignedPreKey generateSignedKEMPreKey(long id, final ECKeyPair identityKeyPair) {
|
||||||
final byte[] pubKey = KEMKeyPair.generate(KEMKeyType.KYBER_1024).getPublicKey().serialize();
|
final byte[] pubKey = KEMKeyPair.generate(KEMKeyType.KYBER_1024).getPublicKey().serialize();
|
||||||
final byte[] sig = identityKeyPair.getPrivateKey().calculateSignature(pubKey);
|
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;
|
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 javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class PreKey {
|
public class PreKey {
|
||||||
|
|
||||||
|
@ -16,22 +22,24 @@ public class PreKey {
|
||||||
private long keyId;
|
private long keyId;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||||
|
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||||
@NotEmpty
|
@NotEmpty
|
||||||
private String publicKey;
|
private byte[] publicKey;
|
||||||
|
|
||||||
public PreKey() {}
|
public PreKey() {}
|
||||||
|
|
||||||
public PreKey(long keyId, String publicKey)
|
public PreKey(long keyId, byte[] publicKey)
|
||||||
{
|
{
|
||||||
this.keyId = keyId;
|
this.keyId = keyId;
|
||||||
this.publicKey = publicKey;
|
this.publicKey = publicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPublicKey() {
|
public byte[] getPublicKey() {
|
||||||
return publicKey;
|
return publicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPublicKey(String publicKey) {
|
public void setPublicKey(byte[] publicKey) {
|
||||||
this.publicKey = publicKey;
|
this.publicKey = publicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,23 +52,17 @@ public class PreKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object object) {
|
public boolean equals(Object o) {
|
||||||
if (object == null || !(object instanceof PreKey)) return false;
|
if (this == o) return true;
|
||||||
PreKey that = (PreKey)object;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
PreKey preKey = (PreKey) o;
|
||||||
if (publicKey == null) {
|
return keyId == preKey.keyId && Arrays.equals(publicKey, preKey.publicKey);
|
||||||
return this.keyId == that.keyId && that.publicKey == null;
|
|
||||||
} else {
|
|
||||||
return this.keyId == that.keyId && this.publicKey.equals(that.publicKey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
if (publicKey == null) {
|
int result = Objects.hash(keyId);
|
||||||
return (int)this.keyId;
|
result = 31 * result + Arrays.hashCode(publicKey);
|
||||||
} else {
|
return result;
|
||||||
return ((int)this.keyId) ^ publicKey.hashCode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,17 +18,13 @@ 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 final boolean validatePreKeySignatures(final String identityKeyB64, final Collection<SignedPreKey> spks) {
|
public static boolean validatePreKeySignatures(final String identityKeyB64, final Collection<SignedPreKey> spks) {
|
||||||
try {
|
try {
|
||||||
final byte[] identityKeyBytes = Base64.getDecoder().decode(identityKeyB64);
|
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().allMatch(spk -> {
|
final boolean success = spks.stream()
|
||||||
final byte[] prekeyBytes = Base64.getDecoder().decode(spk.getPublicKey());
|
.allMatch(spk -> identityKey.verifySignature(spk.getPublicKey(), spk.getSignature()));
|
||||||
final byte[] prekeySignatureBytes = Base64.getDecoder().decode(spk.getSignature());
|
|
||||||
|
|
||||||
return identityKey.verifySignature(prekeyBytes, prekeySignatureBytes);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
INVALID_SIGNATURE_COUNTER.increment();
|
INVALID_SIGNATURE_COUNTER.increment();
|
||||||
|
|
|
@ -6,43 +6,45 @@
|
||||||
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 javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class SignedPreKey extends PreKey {
|
public class SignedPreKey extends PreKey {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
|
||||||
|
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
|
||||||
@NotEmpty
|
@NotEmpty
|
||||||
private String signature;
|
private byte[] signature;
|
||||||
|
|
||||||
public SignedPreKey() {}
|
public SignedPreKey() {}
|
||||||
|
|
||||||
public SignedPreKey(long keyId, String publicKey, String signature) {
|
public SignedPreKey(long keyId, byte[] publicKey, byte[] signature) {
|
||||||
super(keyId, publicKey);
|
super(keyId, publicKey);
|
||||||
this.signature = signature;
|
this.signature = signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSignature() {
|
public byte[] getSignature() {
|
||||||
return signature;
|
return signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object object) {
|
public boolean equals(Object o) {
|
||||||
if (object == null || !(object instanceof SignedPreKey)) return false;
|
if (this == o) return true;
|
||||||
SignedPreKey that = (SignedPreKey) object;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
if (!super.equals(o)) return false;
|
||||||
if (signature == null) {
|
SignedPreKey that = (SignedPreKey) o;
|
||||||
return super.equals(object) && that.signature == null;
|
return Arrays.equals(signature, that.signature);
|
||||||
} else {
|
|
||||||
return super.equals(object) && this.signature.equals(that.signature);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
if (signature == null) {
|
int result = super.hashCode();
|
||||||
return super.hashCode();
|
result = 31 * result + Arrays.hashCode(signature);
|
||||||
} else {
|
return result;
|
||||||
return super.hashCode() ^ signature.hashCode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -374,24 +374,23 @@ public class Keys extends AbstractDynamoDbStore {
|
||||||
return Map.of(
|
return Map.of(
|
||||||
KEY_ACCOUNT_UUID, getPartitionKey(accountUuid),
|
KEY_ACCOUNT_UUID, getPartitionKey(accountUuid),
|
||||||
KEY_DEVICE_ID_KEY_ID, getSortKey(deviceId, spk.getKeyId()),
|
KEY_DEVICE_ID_KEY_ID, getSortKey(deviceId, spk.getKeyId()),
|
||||||
KEY_PUBLIC_KEY, AttributeValues.fromByteArray(Base64.getDecoder().decode(spk.getPublicKey())),
|
KEY_PUBLIC_KEY, AttributeValues.fromByteArray(spk.getPublicKey()),
|
||||||
KEY_SIGNATURE, AttributeValues.fromByteArray(Base64.getDecoder().decode(spk.getSignature())));
|
KEY_SIGNATURE, AttributeValues.fromByteArray(spk.getSignature()));
|
||||||
}
|
}
|
||||||
return Map.of(
|
return Map.of(
|
||||||
KEY_ACCOUNT_UUID, getPartitionKey(accountUuid),
|
KEY_ACCOUNT_UUID, getPartitionKey(accountUuid),
|
||||||
KEY_DEVICE_ID_KEY_ID, getSortKey(deviceId, preKey.getKeyId()),
|
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) {
|
private PreKey getPreKeyFromItem(Map<String, AttributeValue> item) {
|
||||||
final long keyId = item.get(KEY_DEVICE_ID_KEY_ID).b().asByteBuffer().getLong(8);
|
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)) {
|
if (item.containsKey(KEY_SIGNATURE)) {
|
||||||
// All PQ prekeys are signed, and therefore have this attribute. Signed EC prekeys are stored
|
// 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.
|
// 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, extractByteArray(item.get(KEY_SIGNATURE)));
|
||||||
return new SignedPreKey(keyId, publicKey, signature);
|
|
||||||
}
|
}
|
||||||
return new PreKey(keyId, publicKey);
|
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.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.stubbing.Answer;
|
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.AuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.PhoneVerificationTokenManager;
|
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.ChangeNumberRequest;
|
||||||
import org.whispersystems.textsecuregcm.entities.PhoneNumberDiscoverabilityRequest;
|
import org.whispersystems.textsecuregcm.entities.PhoneNumberDiscoverabilityRequest;
|
||||||
import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession;
|
import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession;
|
||||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||||
import org.whispersystems.textsecuregcm.mappers.ImpossiblePhoneNumberExceptionMapper;
|
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.storage.RegistrationRecoveryPasswordsManager;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
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.SystemMapper;
|
||||||
import org.whispersystems.textsecuregcm.util.Util;
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
|
||||||
|
@ -786,12 +788,18 @@ class AccountControllerV2Test {
|
||||||
static Account buildTestAccountForDataReport(final UUID aci, final String number,
|
static Account buildTestAccountForDataReport(final UUID aci, final String number,
|
||||||
final boolean unrestrictedUnidentifiedAccess, final boolean discoverableByPhoneNumber,
|
final boolean unrestrictedUnidentifiedAccess, final boolean discoverableByPhoneNumber,
|
||||||
List<AccountBadge> badges, List<DeviceData> devices) {
|
List<AccountBadge> badges, List<DeviceData> devices) {
|
||||||
|
|
||||||
|
final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair();
|
||||||
|
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
|
||||||
|
|
||||||
final Account account = new Account();
|
final Account account = new Account();
|
||||||
account.setUuid(aci);
|
account.setUuid(aci);
|
||||||
account.setNumber(number, UUID.randomUUID());
|
account.setNumber(number, UUID.randomUUID());
|
||||||
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.setPhoneNumberIdentityKey(KeysHelper.serializeIdentityKey(pniIdentityKeyPair));
|
||||||
|
|
||||||
assert !devices.isEmpty();
|
assert !devices.isEmpty();
|
||||||
|
|
||||||
|
@ -802,7 +810,8 @@ class AccountControllerV2Test {
|
||||||
device.setId(deviceData.id);
|
device.setId(deviceData.id);
|
||||||
device.setAuthTokenHash(passwordTokenHash);
|
device.setAuthTokenHash(passwordTokenHash);
|
||||||
device.setFetchesMessages(true);
|
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.setLastSeen(deviceData.lastSeen().toEpochMilli());
|
||||||
device.setCreated(deviceData.created().toEpochMilli());
|
device.setCreated(deviceData.created().toEpochMilli());
|
||||||
device.setUserAgent(deviceData.userAgent());
|
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.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.mockito.ArgumentCaptor;
|
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.AuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
|
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.storage.ReportMessageManager;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
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.Pair;
|
||||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||||
import org.whispersystems.websocket.Stories;
|
import org.whispersystems.websocket.Stories;
|
||||||
|
@ -163,13 +166,17 @@ class MessageControllerTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
|
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
final List<Device> singleDeviceList = List.of(
|
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(
|
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_ID1, MULTI_DEVICE_REG_ID1, 2222, KeysHelper.signedECPreKey(111, identityKeyPair), 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_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))
|
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<>()),
|
accountsManager.create("+14155551212", "password", null, new AccountAttributes(), new ArrayList<>()),
|
||||||
a -> {
|
a -> {
|
||||||
a.setUnidentifiedAccessKey(new byte[16]);
|
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.removeDevice(1);
|
||||||
a.addDevice(DevicesHelper.createDevice(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.MethodSource;
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.mockito.stubbing.Answer;
|
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.configuration.dynamic.DynamicConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.controllers.MismatchedDevicesException;
|
import org.whispersystems.textsecuregcm.controllers.MismatchedDevicesException;
|
||||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
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.storage.Device.DeviceCapabilities;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.DevicesHelper;
|
import org.whispersystems.textsecuregcm.tests.util.DevicesHelper;
|
||||||
|
import org.whispersystems.textsecuregcm.tests.util.KeysHelper;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
import org.whispersystems.textsecuregcm.tests.util.RedisClusterHelper;
|
||||||
|
|
||||||
class AccountsManagerTest {
|
class AccountsManagerTest {
|
||||||
|
@ -506,7 +509,7 @@ class AccountsManagerTest {
|
||||||
|
|
||||||
Device enabledDevice = new Device();
|
Device enabledDevice = new Device();
|
||||||
enabledDevice.setFetchesMessages(true);
|
enabledDevice.setFetchesMessages(true);
|
||||||
enabledDevice.setSignedPreKey(new SignedPreKey(1L, "key", "signature"));
|
enabledDevice.setSignedPreKey(KeysHelper.signedECPreKey(1, Curve.generateKeyPair()));
|
||||||
enabledDevice.setLastSeen(System.currentTimeMillis());
|
enabledDevice.setLastSeen(System.currentTimeMillis());
|
||||||
final long deviceId = account.getNextDeviceId();
|
final long deviceId = account.getNextDeviceId();
|
||||||
enabledDevice.setId(deviceId);
|
enabledDevice.setId(deviceId);
|
||||||
|
@ -720,12 +723,13 @@ class AccountsManagerTest {
|
||||||
final UUID uuid = UUID.randomUUID();
|
final UUID uuid = UUID.randomUUID();
|
||||||
final UUID originalPni = UUID.randomUUID();
|
final UUID originalPni = UUID.randomUUID();
|
||||||
final UUID targetPni = UUID.randomUUID();
|
final UUID targetPni = UUID.randomUUID();
|
||||||
|
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||||
final Map<Long, SignedPreKey> newSignedKeys = Map.of(
|
final Map<Long, SignedPreKey> newSignedKeys = Map.of(
|
||||||
1L, new SignedPreKey(1L, "pub1", "sig1"),
|
1L, KeysHelper.signedECPreKey(1, identityKeyPair),
|
||||||
2L, new SignedPreKey(2L, "pub2", "sig2"));
|
2L, KeysHelper.signedECPreKey(2, identityKeyPair));
|
||||||
final Map<Long, SignedPreKey> newSignedPqKeys = Map.of(
|
final Map<Long, SignedPreKey> newSignedPqKeys = Map.of(
|
||||||
1L, new SignedPreKey(3L, "pub3", "sig3"),
|
1L, KeysHelper.signedKEMPreKey(3, identityKeyPair),
|
||||||
2L, new SignedPreKey(4L, "pub4", "sig4"));
|
2L, KeysHelper.signedKEMPreKey(4, identityKeyPair));
|
||||||
final Map<Long, Integer> newRegistrationIds = Map.of(1L, 201, 2L, 202);
|
final Map<Long, Integer> newRegistrationIds = Map.of(1L, 201, 2L, 202);
|
||||||
|
|
||||||
final Account existingAccount = AccountsHelper.generateTestAccount(targetNumber, existingAccountUuid, targetPni, new ArrayList<>(), new byte[16]);
|
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(newPni);
|
||||||
verify(keys).delete(originalPni);
|
verify(keys).delete(originalPni);
|
||||||
verify(keys).getPqEnabledDevices(uuid);
|
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);
|
verifyNoMoreInteractions(keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -768,9 +772,10 @@ class AccountsManagerTest {
|
||||||
|
|
||||||
List<Device> devices = List.of(DevicesHelper.createDevice(1L, 0L, 101), DevicesHelper.createDevice(2L, 0L, 102));
|
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]);
|
Account account = AccountsHelper.generateTestAccount(number, UUID.randomUUID(), UUID.randomUUID(), devices, new byte[16]);
|
||||||
|
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||||
Map<Long, SignedPreKey> newSignedKeys = Map.of(
|
Map<Long, SignedPreKey> newSignedKeys = Map.of(
|
||||||
1L, new SignedPreKey(1L, "pub1", "sig1"),
|
1L, KeysHelper.signedECPreKey(1, identityKeyPair),
|
||||||
2L, new SignedPreKey(2L, "pub2", "sig2"));
|
2L, KeysHelper.signedECPreKey(2, identityKeyPair));
|
||||||
Map<Long, Integer> newRegistrationIds = Map.of(1L, 201, 2L, 202);
|
Map<Long, Integer> newRegistrationIds = Map.of(1L, 201, 2L, 202);
|
||||||
|
|
||||||
UUID oldUuid = account.getUuid();
|
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));
|
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]);
|
Account account = AccountsHelper.generateTestAccount(number, UUID.randomUUID(), UUID.randomUUID(), devices, new byte[16]);
|
||||||
Map<Long, SignedPreKey> newSignedKeys = Map.of(
|
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||||
1L, new SignedPreKey(1L, "pub1", "sig1"),
|
final Map<Long, SignedPreKey> newSignedKeys = Map.of(
|
||||||
2L, new SignedPreKey(2L, "pub2", "sig2"));
|
1L, KeysHelper.signedECPreKey(1, identityKeyPair),
|
||||||
Map<Long, SignedPreKey> newSignedPqKeys = Map.of(
|
2L, KeysHelper.signedECPreKey(2, identityKeyPair));
|
||||||
1L, new SignedPreKey(3L, "pub3", "sig3"),
|
final Map<Long, SignedPreKey> newSignedPqKeys = Map.of(
|
||||||
2L, new SignedPreKey(4L, "pub4", "sig4"));
|
1L, KeysHelper.signedKEMPreKey(3, identityKeyPair),
|
||||||
|
2L, KeysHelper.signedKEMPreKey(4, identityKeyPair));
|
||||||
Map<Long, Integer> newRegistrationIds = Map.of(1L, 201, 2L, 202);
|
Map<Long, Integer> newRegistrationIds = Map.of(1L, 201, 2L, 202);
|
||||||
|
|
||||||
UUID oldUuid = account.getUuid();
|
UUID oldUuid = account.getUuid();
|
||||||
|
@ -947,7 +953,7 @@ class AccountsManagerTest {
|
||||||
final Device device = new Device();
|
final Device device = new Device();
|
||||||
device.setId(Device.MASTER_ID);
|
device.setId(Device.MASTER_ID);
|
||||||
device.setFetchesMessages(true);
|
device.setFetchesMessages(true);
|
||||||
device.setSignedPreKey(new SignedPreKey(1, "key", "sig"));
|
device.setSignedPreKey(KeysHelper.signedECPreKey(1, Curve.generateKeyPair()));
|
||||||
device.setLastSeen(lastSeen);
|
device.setLastSeen(lastSeen);
|
||||||
|
|
||||||
return device;
|
return device;
|
||||||
|
|
|
@ -20,9 +20,12 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
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.PreKey;
|
||||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
|
||||||
|
import org.whispersystems.textsecuregcm.tests.util.KeysHelper;
|
||||||
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
||||||
import software.amazon.awssdk.core.SdkBytes;
|
import software.amazon.awssdk.core.SdkBytes;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||||
|
@ -211,9 +214,11 @@ class KeysTest {
|
||||||
void testStorePqLastResort() {
|
void testStorePqLastResort() {
|
||||||
assertEquals(0, getLastResortCount(ACCOUNT_UUID));
|
assertEquals(0, getLastResortCount(ACCOUNT_UUID));
|
||||||
|
|
||||||
|
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||||
|
|
||||||
keys.storePqLastResort(
|
keys.storePqLastResort(
|
||||||
ACCOUNT_UUID,
|
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(2, getLastResortCount(ACCOUNT_UUID));
|
||||||
assertEquals(1L, keys.getLastResort(ACCOUNT_UUID, 1L).get().getKeyId());
|
assertEquals(1L, keys.getLastResort(ACCOUNT_UUID, 1L).get().getKeyId());
|
||||||
assertEquals(2L, keys.getLastResort(ACCOUNT_UUID, 2L).get().getKeyId());
|
assertEquals(2L, keys.getLastResort(ACCOUNT_UUID, 2L).get().getKeyId());
|
||||||
|
@ -221,7 +226,7 @@ class KeysTest {
|
||||||
|
|
||||||
keys.storePqLastResort(
|
keys.storePqLastResort(
|
||||||
ACCOUNT_UUID,
|
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(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(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");
|
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
|
@Test
|
||||||
void testGetPqEnabledDevices() {
|
void testGetPqEnabledDevices() {
|
||||||
keys.store(ACCOUNT_UUID, DEVICE_ID, null, List.of(new SignedPreKey(1L, "pub1", "sig1")), null);
|
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
|
||||||
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"));
|
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);
|
keys.store(ACCOUNT_UUID, DEVICE_ID + 3, null, null, null);
|
||||||
assertIterableEquals(
|
assertIterableEquals(
|
||||||
Set.of(DEVICE_ID + 1, DEVICE_ID + 2),
|
Set.of(DEVICE_ID + 1, DEVICE_ID + 2),
|
||||||
|
@ -291,7 +298,7 @@ class KeysTest {
|
||||||
final byte[] key = new byte[32];
|
final byte[] key = new byte[32];
|
||||||
new SecureRandom().nextBytes(key);
|
new SecureRandom().nextBytes(key);
|
||||||
|
|
||||||
return new PreKey(keyId, Base64.getEncoder().encodeToString(key));
|
return new PreKey(keyId, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SignedPreKey generateTestSignedPreKey(final long keyId) {
|
private static SignedPreKey generateTestSignedPreKey(final long keyId) {
|
||||||
|
@ -302,6 +309,6 @@ class KeysTest {
|
||||||
secureRandom.nextBytes(key);
|
secureRandom.nextBytes(key);
|
||||||
secureRandom.nextBytes(signature);
|
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) {
|
private static SignedPreKey signedPreKeyWithBadSignature(final SignedPreKey signedPreKey) {
|
||||||
return new SignedPreKey(signedPreKey.getKeyId(),
|
return new SignedPreKey(signedPreKey.getKeyId(),
|
||||||
signedPreKey.getPublicKey(),
|
signedPreKey.getPublicKey(),
|
||||||
Base64.getEncoder().encodeToString("incorrect-signature".getBytes(StandardCharsets.UTF_8)));
|
"incorrect-signature".getBytes(StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -26,7 +26,6 @@ import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
|
||||||
import io.dropwizard.testing.junit5.ResourceExtension;
|
import io.dropwizard.testing.junit5.ResourceExtension;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
|
@ -86,18 +85,18 @@ class KeysControllerTest {
|
||||||
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 String PNI_IDENTITY_KEY = KeysHelper.serializeIdentityKey(PNI_IDENTITY_KEY_PAIR);
|
||||||
|
|
||||||
private final PreKey SAMPLE_KEY = new PreKey(1234, "test1");
|
private final PreKey SAMPLE_KEY = KeysHelper.ecPreKey(1234);
|
||||||
private final PreKey SAMPLE_KEY2 = new PreKey(5667, "test3");
|
private final PreKey SAMPLE_KEY2 = KeysHelper.ecPreKey(5667);
|
||||||
private final PreKey SAMPLE_KEY3 = new PreKey(334, "test5");
|
private final PreKey SAMPLE_KEY3 = KeysHelper.ecPreKey(334);
|
||||||
private final PreKey SAMPLE_KEY4 = new PreKey(336, "test6");
|
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_KEY = KeysHelper.signedKEMPreKey(2424, Curve.generateKeyPair());
|
||||||
private final SignedPreKey SAMPLE_PQ_KEY2 = new SignedPreKey(6868, "test3", "sig");
|
private final SignedPreKey SAMPLE_PQ_KEY2 = KeysHelper.signedKEMPreKey(6868, Curve.generateKeyPair());
|
||||||
private final SignedPreKey SAMPLE_PQ_KEY3 = new SignedPreKey(1313, "test5", "sig");
|
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_KEY = KeysHelper.signedECPreKey(1111, IDENTITY_KEY_PAIR);
|
||||||
private final SignedPreKey SAMPLE_SIGNED_KEY2 = KeysHelper.signedECPreKey(2222, IDENTITY_KEY_PAIR);
|
private final SignedPreKey SAMPLE_SIGNED_KEY2 = KeysHelper.signedECPreKey(2222, IDENTITY_KEY_PAIR);
|
||||||
|
@ -656,7 +655,7 @@ class KeysControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void putKeysTestV2() {
|
void putKeysTestV2() {
|
||||||
final PreKey preKey = new PreKey(31337, "foobar");
|
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 String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair);
|
||||||
|
@ -684,7 +683,7 @@ class KeysControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void putKeysPqTestV2() {
|
void putKeysPqTestV2() {
|
||||||
final PreKey preKey = new PreKey(31337, "foobar");
|
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 SignedPreKey pqPreKey = KeysHelper.signedECPreKey(31339, identityKeyPair);
|
final SignedPreKey pqPreKey = KeysHelper.signedECPreKey(31339, identityKeyPair);
|
||||||
|
@ -716,7 +715,7 @@ class KeysControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void putKeysByPhoneNumberIdentifierTestV2() {
|
void putKeysByPhoneNumberIdentifierTestV2() {
|
||||||
final PreKey preKey = new PreKey(31337, "foobar");
|
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 String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair);
|
||||||
|
@ -745,7 +744,7 @@ class KeysControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void putKeysByPhoneNumberIdentifierPqTestV2() {
|
void putKeysByPhoneNumberIdentifierPqTestV2() {
|
||||||
final PreKey preKey = new PreKey(31337, "foobar");
|
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 SignedPreKey pqPreKey = KeysHelper.signedECPreKey(31339, identityKeyPair);
|
final SignedPreKey pqPreKey = KeysHelper.signedECPreKey(31339, identityKeyPair);
|
||||||
|
@ -778,7 +777,7 @@ class KeysControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void putPrekeyWithInvalidSignature() {
|
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());
|
PreKeyState preKeyState = new PreKeyState(IDENTITY_KEY, badSignedPreKey, List.of());
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
|
@ -793,16 +792,12 @@ class KeysControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void disabledPutKeysTestV2() {
|
void disabledPutKeysTestV2() {
|
||||||
final PreKey preKey = new PreKey(31337, "foobar");
|
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 String identityKey = KeysHelper.serializeIdentityKey(identityKeyPair);
|
||||||
|
|
||||||
List<PreKey> preKeys = new LinkedList<PreKey>() {{
|
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey));
|
||||||
add(preKey);
|
|
||||||
}};
|
|
||||||
|
|
||||||
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, preKeys);
|
|
||||||
|
|
||||||
Response response =
|
Response response =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
|
@ -819,7 +814,7 @@ class KeysControllerTest {
|
||||||
List<PreKey> capturedList = listCaptor.getValue();
|
List<PreKey> capturedList = listCaptor.getValue();
|
||||||
assertThat(capturedList.size()).isEqualTo(1);
|
assertThat(capturedList.size()).isEqualTo(1);
|
||||||
assertThat(capturedList.get(0).getKeyId()).isEqualTo(31337);
|
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_ACCOUNT).setIdentityKey(eq(identityKey));
|
||||||
verify(AuthHelper.DISABLED_DEVICE).setSignedPreKey(eq(signedPreKey));
|
verify(AuthHelper.DISABLED_DEVICE).setSignedPreKey(eq(signedPreKey));
|
||||||
|
@ -828,7 +823,7 @@ class KeysControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void putIdentityKeyNonPrimary() {
|
void putIdentityKeyNonPrimary() {
|
||||||
final PreKey preKey = new PreKey(31337, "foobar");
|
final PreKey preKey = KeysHelper.ecPreKey(31337);
|
||||||
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, IDENTITY_KEY_PAIR);
|
final SignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, IDENTITY_KEY_PAIR);
|
||||||
|
|
||||||
List<PreKey> preKeys = List.of(preKey);
|
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.junit.jupiter.api.Test;
|
||||||
import org.whispersystems.textsecuregcm.entities.PreKey;
|
import org.whispersystems.textsecuregcm.entities.PreKey;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
class PreKeyTest {
|
class PreKeyTest {
|
||||||
|
|
||||||
|
private static final byte[] PUBLIC_KEY = Base64.getDecoder().decode("BQ+NbroQtVKyFaCSfqzSw8Wy72Ff22RSa5ERKTv5DIk2");
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void serializeToJSONV2() throws Exception {
|
void serializeToJSONV2() throws Exception {
|
||||||
PreKey preKey = new PreKey(1234, "test");
|
PreKey preKey = new PreKey(1234, PUBLIC_KEY);
|
||||||
|
|
||||||
assertThat("PreKeyV2 Serialization works",
|
assertThat("PreKeyV2 Serialization works",
|
||||||
asJson(preKey),
|
asJson(preKey),
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
package org.whispersystems.textsecuregcm.tests.util;
|
package org.whispersystems.textsecuregcm.tests.util;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.signal.libsignal.protocol.ecc.Curve;
|
||||||
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device;
|
import org.whispersystems.textsecuregcm.storage.Device;
|
||||||
import org.whispersystems.textsecuregcm.util.Util;
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
@ -36,8 +38,8 @@ public class DevicesHelper {
|
||||||
|
|
||||||
public static void setEnabled(Device device, boolean enabled) {
|
public static void setEnabled(Device device, boolean enabled) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
device.setSignedPreKey(new SignedPreKey(RANDOM.nextLong(), "testPublicKey-" + RANDOM.nextLong(),
|
device.setSignedPreKey(KeysHelper.signedECPreKey(RANDOM.nextLong(), Curve.generateKeyPair()));
|
||||||
"testSignature-" + RANDOM.nextLong()));
|
device.setPhoneNumberIdentitySignedPreKey(KeysHelper.signedECPreKey(RANDOM.nextLong(), Curve.generateKeyPair()));
|
||||||
device.setGcmId("testGcmId" + RANDOM.nextLong());
|
device.setGcmId("testGcmId" + RANDOM.nextLong());
|
||||||
device.setLastSeen(Util.todayInMillis());
|
device.setLastSeen(Util.todayInMillis());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -10,22 +10,27 @@ 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;
|
||||||
import org.signal.libsignal.protocol.kem.KEMKeyType;
|
import org.signal.libsignal.protocol.kem.KEMKeyType;
|
||||||
|
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) {
|
public static String serializeIdentityKey(ECKeyPair keyPair) {
|
||||||
return Base64.getEncoder().encodeToString(keyPair.getPublicKey().serialize());
|
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) {
|
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);
|
||||||
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) {
|
public static SignedPreKey signedKEMPreKey(long id, final ECKeyPair identityKeyPair) {
|
||||||
final byte[] pubKey = KEMKeyPair.generate(KEMKeyType.KYBER_1024).getPublicKey().serialize();
|
final byte[] pubKey = KEMKeyPair.generate(KEMKeyType.KYBER_1024).getPublicKey().serialize();
|
||||||
final byte[] sig = identityKeyPair.getPrivateKey().calculateSignature(pubKey);
|
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,
|
"keyId" : 1234,
|
||||||
"publicKey" : "test"
|
"publicKey" : "BQ+NbroQtVKyFaCSfqzSw8Wy72Ff22RSa5ERKTv5DIk2"
|
||||||
}
|
}
|
Loading…
Reference in New Issue