Convert `PreKeyState` to a record

This commit is contained in:
Jon Chambers 2023-11-28 22:03:10 -05:00 committed by Jon Chambers
parent 9ecfe15ac4
commit f10f772e94
3 changed files with 57 additions and 101 deletions

View File

@ -138,15 +138,15 @@ public class KeysController {
final boolean usePhoneNumberIdentity = usePhoneNumberIdentity(identityType); final boolean usePhoneNumberIdentity = usePhoneNumberIdentity(identityType);
if (preKeys.getSignedPreKey() != null && if (preKeys.signedPreKey() != null &&
!preKeys.getSignedPreKey().equals(usePhoneNumberIdentity ? device.getSignedPreKey(IdentityType.PNI) !preKeys.signedPreKey().equals(usePhoneNumberIdentity ? device.getSignedPreKey(IdentityType.PNI)
: device.getSignedPreKey(IdentityType.ACI))) { : device.getSignedPreKey(IdentityType.ACI))) {
updateAccount = true; updateAccount = true;
} }
final IdentityKey oldIdentityKey = final IdentityKey oldIdentityKey =
usePhoneNumberIdentity ? account.getIdentityKey(IdentityType.PNI) : account.getIdentityKey(IdentityType.ACI); usePhoneNumberIdentity ? account.getIdentityKey(IdentityType.PNI) : account.getIdentityKey(IdentityType.ACI);
if (!Objects.equals(preKeys.getIdentityKey(), oldIdentityKey)) { if (!Objects.equals(preKeys.identityKey(), oldIdentityKey)) {
updateAccount = true; updateAccount = true;
final boolean hasIdentityKey = oldIdentityKey != null; final boolean hasIdentityKey = oldIdentityKey != null;
@ -170,26 +170,26 @@ public class KeysController {
if (updateAccount) { if (updateAccount) {
account = accounts.update(account, a -> { account = accounts.update(account, a -> {
if (preKeys.getSignedPreKey() != null) { if (preKeys.signedPreKey() != null) {
a.getDevice(device.getId()).ifPresent(d -> { a.getDevice(device.getId()).ifPresent(d -> {
if (usePhoneNumberIdentity) { if (usePhoneNumberIdentity) {
d.setPhoneNumberIdentitySignedPreKey(preKeys.getSignedPreKey()); d.setPhoneNumberIdentitySignedPreKey(preKeys.signedPreKey());
} else { } else {
d.setSignedPreKey(preKeys.getSignedPreKey()); d.setSignedPreKey(preKeys.signedPreKey());
} }
}); });
} }
if (usePhoneNumberIdentity) { if (usePhoneNumberIdentity) {
a.setPhoneNumberIdentityKey(preKeys.getIdentityKey()); a.setPhoneNumberIdentityKey(preKeys.identityKey());
} else { } else {
a.setIdentityKey(preKeys.getIdentityKey()); a.setIdentityKey(preKeys.identityKey());
} }
}); });
} }
return keys.store(getIdentifier(account, identityType), device.getId(), return keys.store(getIdentifier(account, identityType), device.getId(),
preKeys.getPreKeys(), preKeys.getPqPreKeys(), preKeys.getSignedPreKey(), preKeys.getPqLastResortPreKey()) preKeys.preKeys(), preKeys.pqPreKeys(), preKeys.signedPreKey(), preKeys.pqLastResortPreKey())
.thenApply(Util.ASYNC_EMPTY_RESPONSE); .thenApply(Util.ASYNC_EMPTY_RESPONSE);
} }

View File

@ -4,105 +4,59 @@
*/ */
package org.whispersystems.textsecuregcm.entities; package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.VisibleForTesting;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKey;
import org.whispersystems.textsecuregcm.util.IdentityKeyAdapter; import org.whispersystems.textsecuregcm.util.IdentityKeyAdapter;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.AssertTrue; import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class PreKeyState { public record PreKeyState(
@Valid
@Schema(description = """
A list of unsigned elliptic-curve prekeys to use for this device. If present and not empty, replaces all stored
unsigned EC prekeys for the device; if absent or empty, any stored unsigned EC prekeys for the device are not
deleted.
""")
List<@Valid ECPreKey> preKeys,
@JsonProperty @Valid
@Valid @Schema(description = """
@Schema(description = """ An optional signed elliptic-curve prekey to use for this device. If present, replaces the stored signed
A list of unsigned elliptic-curve prekeys to use for this device. If present and not empty, replaces all stored elliptic-curve prekey for the device; if absent, the stored signed prekey is not deleted. If present, must have
unsigned EC prekeys for the device; if absent or empty, any stored unsigned EC prekeys for the device are not a valid signature from the identity key in this request.
deleted. """)
""") ECSignedPreKey signedPreKey,
private List<@Valid ECPreKey> preKeys;
@JsonProperty @Valid
@Valid @Schema(description = """
@Schema(description = """ A list of signed post-quantum one-time prekeys to use for this device. Each key must have a valid signature from
An optional signed elliptic-curve prekey to use for this device. If present, replaces the stored signed the identity key in this request. If present and not empty, replaces all stored unsigned PQ prekeys for the
elliptic-curve prekey for the device; if absent, the stored signed prekey is not deleted. If present, must have a device; if absent or empty, any stored unsigned PQ prekeys for the device are not deleted.
valid signature from the identity key in this request. """)
""") List<@Valid KEMSignedPreKey> pqPreKeys,
private ECSignedPreKey signedPreKey;
@JsonProperty @Valid
@Valid @Schema(description = """
@Schema(description = """ An optional signed last-resort post-quantum prekey to use for this device. If present, replaces the stored
A list of signed post-quantum one-time prekeys to use for this device. Each key must have a valid signature from signed post-quantum last-resort prekey for the device; if absent, a stored last-resort prekey will *not* be
the identity key in this request. If present and not empty, replaces all stored unsigned PQ prekeys for the deleted. If present, must have a valid signature from the identity key in this request.
device; if absent or empty, any stored unsigned PQ prekeys for the device are not deleted. """)
""") KEMSignedPreKey pqLastResortPreKey,
private List<@Valid KEMSignedPreKey> pqPreKeys;
@JsonProperty @JsonSerialize(using = IdentityKeyAdapter.Serializer.class)
@Valid @JsonDeserialize(using = IdentityKeyAdapter.Deserializer.class)
@Schema(description = """ @NotNull
An optional signed last-resort post-quantum prekey to use for this device. If present, replaces the stored signed @Schema(description = """
post-quantum last-resort prekey for the device; if absent, a stored last-resort prekey will *not* be deleted. If Required. The public identity key for this identity (account or phone-number identity). If this device is not
present, must have a valid signature from the identity key in this request. the primary device for the account, must match the existing stored identity key for this identity.
""") """)
private KEMSignedPreKey pqLastResortPreKey; IdentityKey identityKey
) {
@JsonProperty
@JsonSerialize(using = IdentityKeyAdapter.Serializer.class)
@JsonDeserialize(using = IdentityKeyAdapter.Deserializer.class)
@NotNull
@Schema(description = """
Required. The public identity key for this identity (account or phone-number identity). If this device is not the
primary device for the account, must match the existing stored identity key for this identity.
""")
private IdentityKey identityKey;
public PreKeyState() {
}
@VisibleForTesting
public PreKeyState(IdentityKey identityKey, ECSignedPreKey signedPreKey, List<ECPreKey> keys) {
this(identityKey, signedPreKey, keys, null, null);
}
@VisibleForTesting
public PreKeyState(IdentityKey identityKey, ECSignedPreKey signedPreKey, List<ECPreKey> keys,
List<KEMSignedPreKey> pqKeys, KEMSignedPreKey pqLastResortKey) {
this.identityKey = identityKey;
this.signedPreKey = signedPreKey;
this.preKeys = keys;
this.pqPreKeys = pqKeys;
this.pqLastResortPreKey = pqLastResortKey;
}
public List<ECPreKey> getPreKeys() {
return preKeys;
}
public ECSignedPreKey getSignedPreKey() {
return signedPreKey;
}
public List<KEMSignedPreKey> getPqPreKeys() {
return pqPreKeys;
}
public KEMSignedPreKey getPqLastResortPreKey() {
return pqLastResortPreKey;
}
public IdentityKey getIdentityKey() {
return identityKey;
}
@AssertTrue @AssertTrue
public boolean isSignatureValidOnEachSignedKey() { public boolean isSignatureValidOnEachSignedKey() {

View File

@ -732,7 +732,7 @@ class KeysControllerTest {
final ECSignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair); final ECSignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
final IdentityKey identityKey = new IdentityKey(identityKeyPair.getPublicKey()); final IdentityKey identityKey = new IdentityKey(identityKeyPair.getPublicKey());
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey)); final PreKeyState preKeyState = new PreKeyState(List.of(preKey), signedPreKey, null, null, identityKey);
Response response = Response response =
resources.getJerseyTest() resources.getJerseyTest()
@ -763,7 +763,8 @@ class KeysControllerTest {
final KEMSignedPreKey pqLastResortPreKey = KeysHelper.signedKEMPreKey(31340, identityKeyPair); final KEMSignedPreKey pqLastResortPreKey = KeysHelper.signedKEMPreKey(31340, identityKeyPair);
final IdentityKey identityKey = new IdentityKey(identityKeyPair.getPublicKey()); final IdentityKey identityKey = new IdentityKey(identityKeyPair.getPublicKey());
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey), List.of(pqPreKey), pqLastResortPreKey); final PreKeyState preKeyState =
new PreKeyState(List.of(preKey), signedPreKey, List.of(pqPreKey), pqLastResortPreKey, identityKey);
Response response = Response response =
resources.getJerseyTest() resources.getJerseyTest()
@ -866,7 +867,7 @@ class KeysControllerTest {
final ECSignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair); final ECSignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
final IdentityKey identityKey = new IdentityKey(identityKeyPair.getPublicKey()); final IdentityKey identityKey = new IdentityKey(identityKeyPair.getPublicKey());
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey)); final PreKeyState preKeyState = new PreKeyState(List.of(preKey), signedPreKey, null, null, identityKey);
Response response = Response response =
resources.getJerseyTest() resources.getJerseyTest()
@ -898,7 +899,8 @@ class KeysControllerTest {
final KEMSignedPreKey pqLastResortPreKey = KeysHelper.signedKEMPreKey(31340, identityKeyPair); final KEMSignedPreKey pqLastResortPreKey = KeysHelper.signedKEMPreKey(31340, identityKeyPair);
final IdentityKey identityKey = new IdentityKey(identityKeyPair.getPublicKey()); final IdentityKey identityKey = new IdentityKey(identityKeyPair.getPublicKey());
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey), List.of(pqPreKey), pqLastResortPreKey); final PreKeyState preKeyState =
new PreKeyState(List.of(preKey), signedPreKey, List.of(pqPreKey), pqLastResortPreKey, identityKey);
Response response = Response response =
resources.getJerseyTest() resources.getJerseyTest()
@ -926,7 +928,7 @@ class KeysControllerTest {
@Test @Test
void putPrekeyWithInvalidSignature() { void putPrekeyWithInvalidSignature() {
final ECSignedPreKey badSignedPreKey = KeysHelper.signedECPreKey(1, Curve.generateKeyPair()); final ECSignedPreKey badSignedPreKey = KeysHelper.signedECPreKey(1, Curve.generateKeyPair());
PreKeyState preKeyState = new PreKeyState(IDENTITY_KEY, badSignedPreKey, List.of()); final PreKeyState preKeyState = new PreKeyState(List.of(), badSignedPreKey, null, null, IDENTITY_KEY);
Response response = Response response =
resources.getJerseyTest() resources.getJerseyTest()
.target("/v2/keys") .target("/v2/keys")
@ -945,7 +947,7 @@ class KeysControllerTest {
final ECSignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair); final ECSignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
final IdentityKey identityKey = new IdentityKey(identityKeyPair.getPublicKey()); final IdentityKey identityKey = new IdentityKey(identityKeyPair.getPublicKey());
PreKeyState preKeyState = new PreKeyState(identityKey, signedPreKey, List.of(preKey)); final PreKeyState preKeyState = new PreKeyState(List.of(preKey), signedPreKey, null, null, identityKey);
Response response = Response response =
resources.getJerseyTest() resources.getJerseyTest()
@ -975,9 +977,9 @@ class KeysControllerTest {
final ECPreKey preKey = KeysHelper.ecPreKey(31337); final ECPreKey preKey = KeysHelper.ecPreKey(31337);
final ECSignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, IDENTITY_KEY_PAIR); final ECSignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, IDENTITY_KEY_PAIR);
List<ECPreKey> preKeys = List.of(preKey); final List<ECPreKey> preKeys = List.of(preKey);
PreKeyState preKeyState = new PreKeyState(IDENTITY_KEY, signedPreKey, preKeys); final PreKeyState preKeyState = new PreKeyState(preKeys, signedPreKey, null, null, IDENTITY_KEY);
Response response = Response response =
resources.getJerseyTest() resources.getJerseyTest()