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(
@JsonProperty
@Valid @Valid
@Schema(description = """ @Schema(description = """
A list of unsigned elliptic-curve prekeys to use for this device. If present and not empty, replaces all stored 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 unsigned EC prekeys for the device; if absent or empty, any stored unsigned EC prekeys for the device are not
deleted. deleted.
""") """)
private List<@Valid ECPreKey> preKeys; 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 An optional signed elliptic-curve prekey to use for this device. If present, replaces the stored signed
elliptic-curve prekey for the device; if absent, the stored signed prekey is not deleted. If present, must have a elliptic-curve prekey for the device; if absent, the stored signed prekey is not deleted. If present, must have
valid signature from the identity key in this request. a valid signature from the identity key in this request.
""") """)
private ECSignedPreKey signedPreKey; ECSignedPreKey signedPreKey,
@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 A list of signed post-quantum one-time prekeys to use for this device. Each key must have a valid signature from
the identity key in this request. If present and not empty, replaces all stored unsigned PQ prekeys for the the identity key in this request. If present and not empty, replaces all stored unsigned PQ prekeys for the
device; if absent or empty, any stored unsigned PQ prekeys for the device are not deleted. device; if absent or empty, any stored unsigned PQ prekeys for the device are not deleted.
""") """)
private List<@Valid KEMSignedPreKey> pqPreKeys; List<@Valid KEMSignedPreKey> pqPreKeys,
@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 signed An optional signed last-resort post-quantum prekey to use for this device. If present, replaces the stored
post-quantum last-resort prekey for the device; if absent, a stored last-resort prekey will *not* be deleted. If signed post-quantum last-resort prekey for the device; if absent, a stored last-resort prekey will *not* be
present, must have a valid signature from the identity key in this request. deleted. If present, must have a valid signature from the identity key in this request.
""") """)
private KEMSignedPreKey pqLastResortPreKey; KEMSignedPreKey pqLastResortPreKey,
@JsonProperty
@JsonSerialize(using = IdentityKeyAdapter.Serializer.class) @JsonSerialize(using = IdentityKeyAdapter.Serializer.class)
@JsonDeserialize(using = IdentityKeyAdapter.Deserializer.class) @JsonDeserialize(using = IdentityKeyAdapter.Deserializer.class)
@NotNull @NotNull
@Schema(description = """ @Schema(description = """
Required. The public identity key for this identity (account or phone-number identity). If this device is not the Required. The public identity key for this identity (account or phone-number identity). If this device is not
primary device for the account, must match the existing stored identity key for this identity. the primary device for the account, must match the existing stored identity key for this identity.
""") """)
private IdentityKey identityKey; 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()