diff --git a/service/config/sample.yml b/service/config/sample.yml index 1225b64b2..cb4a21d14 100644 --- a/service/config/sample.yml +++ b/service/config/sample.yml @@ -98,6 +98,8 @@ dynamoDbTables: generator: abcdefg12345678= # random base64-encoded binary sequence ecKeys: tableName: Example_Keys + ecSignedPreKeys: + tableName: Example_EC_Signed_Pre_Keys pqKeys: tableName: Example_PQ_Keys pqLastResortKeys: diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 709b85aeb..c4c094842 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -316,6 +316,7 @@ public class WhisperServerService extends Application { + keys.storeEcSignedPreKeys(a.getUuid(), Map.of(device.getId(), deviceActivationRequest.aciSignedPreKey().get())); keys.storePqLastResort(a.getUuid(), Map.of(device.getId(), deviceActivationRequest.aciPqLastResortPreKey().get())); + keys.storeEcSignedPreKeys(a.getPhoneNumberIdentifier(), Map.of(device.getId(), deviceActivationRequest.pniSignedPreKey().get())); keys.storePqLastResort(a.getPhoneNumberIdentifier(), Map.of(device.getId(), deviceActivationRequest.pniPqLastResortPreKey().get())); }); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/KeysController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/KeysController.java index 9be38a9d7..41f949082 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/KeysController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/KeysController.java @@ -167,7 +167,7 @@ public class KeysController { keys.store( getIdentifier(account, identityType), device.getId(), - preKeys.getPreKeys(), preKeys.getPqPreKeys(), preKeys.getPqLastResortPreKey()); + preKeys.getPreKeys(), preKeys.getPqPreKeys(), preKeys.getSignedPreKey(), preKeys.getPqLastResortPreKey()); } @Timed diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/RegistrationController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/RegistrationController.java index 87f55863a..9db861b4c 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/RegistrationController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/RegistrationController.java @@ -176,7 +176,9 @@ public class RegistrationController { registrationRequest.deviceActivationRequest().gcmToken().ifPresent(gcmRegistrationId -> device.setGcmId(gcmRegistrationId.gcmRegistrationId())); + keysManager.storeEcSignedPreKeys(a.getUuid(), Map.of(Device.MASTER_ID, registrationRequest.deviceActivationRequest().aciSignedPreKey().get())); keysManager.storePqLastResort(a.getUuid(), Map.of(Device.MASTER_ID, registrationRequest.deviceActivationRequest().aciPqLastResortPreKey().get())); + keysManager.storeEcSignedPreKeys(a.getPhoneNumberIdentifier(), Map.of(Device.MASTER_ID, registrationRequest.deviceActivationRequest().pniSignedPreKey().get())); keysManager.storePqLastResort(a.getPhoneNumberIdentifier(), Map.of(Device.MASTER_ID, registrationRequest.deviceActivationRequest().pniPqLastResortPreKey().get())); }); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java index ad349fb60..f7f1db4a5 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java @@ -312,7 +312,10 @@ public class AccountsManager { numberChangedAccount = updateWithRetries( account, - a -> { setPniKeys(account, pniIdentityKey, pniSignedPreKeys, pniRegistrationIds); return true; }, + a -> { + setPniKeys(account, pniIdentityKey, pniSignedPreKeys, pniRegistrationIds); + return true; + }, a -> accounts.changeNumber(a, targetNumber, phoneNumberIdentifier), () -> accounts.getByAccountIdentifier(uuid).orElseThrow(), AccountChangeValidator.NUMBER_CHANGE_VALIDATOR); @@ -322,6 +325,8 @@ public class AccountsManager { keysManager.delete(phoneNumberIdentifier); keysManager.delete(originalPhoneNumberIdentifier); + keysManager.storeEcSignedPreKeys(phoneNumberIdentifier, pniSignedPreKeys); + if (pniPqLastResortPreKeys != null) { keysManager.storePqLastResort( phoneNumberIdentifier, @@ -362,6 +367,7 @@ public class AccountsManager { final List pqEnabledDeviceIDs = keysManager.getPqEnabledDevices(pni); keysManager.delete(pni); + keysManager.storeEcSignedPreKeys(pni, pniSignedPreKeys); if (pniPqLastResortPreKeys != null) { keysManager.storePqLastResort(pni, pqEnabledDeviceIDs.stream().collect(Collectors.toMap(Function.identity(), pniPqLastResortPreKeys::get))); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/KeysManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/KeysManager.java index a78bedf2d..a767711cc 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/KeysManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/KeysManager.java @@ -14,6 +14,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import javax.annotation.Nullable; import org.whispersystems.textsecuregcm.entities.ECPreKey; +import org.whispersystems.textsecuregcm.entities.ECSignedPreKey; import org.whispersystems.textsecuregcm.entities.KEMSignedPreKey; import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; @@ -21,26 +22,30 @@ public class KeysManager { private final SingleUseECPreKeyStore ecPreKeys; private final SingleUseKEMPreKeyStore pqPreKeys; + private final RepeatedUseECSignedPreKeyStore ecSignedPreKeys; private final RepeatedUseKEMSignedPreKeyStore pqLastResortKeys; public KeysManager( final DynamoDbAsyncClient dynamoDbAsyncClient, final String ecTableName, final String pqTableName, + final String ecSignedPreKeysTableName, final String pqLastResortTableName) { this.ecPreKeys = new SingleUseECPreKeyStore(dynamoDbAsyncClient, ecTableName); this.pqPreKeys = new SingleUseKEMPreKeyStore(dynamoDbAsyncClient, pqTableName); + this.ecSignedPreKeys = new RepeatedUseECSignedPreKeyStore(dynamoDbAsyncClient, ecSignedPreKeysTableName); this.pqLastResortKeys = new RepeatedUseKEMSignedPreKeyStore(dynamoDbAsyncClient, pqLastResortTableName); } public void store(final UUID identifier, final long deviceId, final List keys) { - store(identifier, deviceId, keys, null, null); + store(identifier, deviceId, keys, null, null, null); } public void store( final UUID identifier, final long deviceId, @Nullable final List ecKeys, @Nullable final List pqKeys, + @Nullable final ECSignedPreKey ecSignedPreKey, @Nullable final KEMSignedPreKey pqLastResortKey) { final List> storeFutures = new ArrayList<>(); @@ -53,6 +58,10 @@ public class KeysManager { storeFutures.add(pqPreKeys.store(identifier, deviceId, pqKeys)); } + if (ecSignedPreKey != null) { + storeFutures.add(ecSignedPreKeys.store(identifier, deviceId, ecSignedPreKey)); + } + if (pqLastResortKey != null) { storeFutures.add(pqLastResortKeys.store(identifier, deviceId, pqLastResortKey)); } @@ -60,6 +69,10 @@ public class KeysManager { CompletableFuture.allOf(storeFutures.toArray(new CompletableFuture[0])).join(); } + public void storeEcSignedPreKeys(final UUID identifier, final Map keys) { + ecSignedPreKeys.store(identifier, keys).join(); + } + public void storePqLastResort(final UUID identifier, final Map keys) { pqLastResortKeys.store(identifier, keys).join(); } @@ -80,6 +93,10 @@ public class KeysManager { return pqLastResortKeys.find(identifier, deviceId).join(); } + public CompletableFuture> getEcSignedPreKey(final UUID identifier, final long deviceId) { + return ecSignedPreKeys.find(identifier, deviceId); + } + public List getPqEnabledDevices(final UUID identifier) { return pqLastResortKeys.getDeviceIdsWithKeys(identifier).collectList().block(); } @@ -96,6 +113,7 @@ public class KeysManager { CompletableFuture.allOf( ecPreKeys.delete(accountUuid), pqPreKeys.delete(accountUuid), + ecSignedPreKeys.delete(accountUuid), pqLastResortKeys.delete(accountUuid)) .join(); } @@ -104,6 +122,7 @@ public class KeysManager { CompletableFuture.allOf( ecPreKeys.delete(accountUuid, deviceId), pqPreKeys.delete(accountUuid, deviceId), + ecSignedPreKeys.delete(accountUuid, deviceId), pqLastResortKeys.delete(accountUuid, deviceId)) .join(); } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/AssignUsernameCommand.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/AssignUsernameCommand.java index ac1d78d5a..7285487a6 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/workers/AssignUsernameCommand.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/AssignUsernameCommand.java @@ -165,6 +165,7 @@ public class AssignUsernameCommand extends EnvironmentCommand { private RepeatedUseKEMSignedPreKeyStore keyStore; @@ -22,14 +20,14 @@ class RepeatedUseKEMSignedPreKeyStoreTest extends RepeatedUseSignedPreKeyStoreTe @RegisterExtension static final DynamoDbExtension DYNAMO_DB_EXTENSION = - new DynamoDbExtension(DynamoDbExtensionSchema.Tables.REPEATED_USE_SIGNED_PRE_KEYS); + new DynamoDbExtension(DynamoDbExtensionSchema.Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS); private static final ECKeyPair IDENTITY_KEY_PAIR = Curve.generateKeyPair(); @BeforeEach void setUp() { keyStore = new RepeatedUseKEMSignedPreKeyStore(DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), - DynamoDbExtensionSchema.Tables.REPEATED_USE_SIGNED_PRE_KEYS.tableName()); + DynamoDbExtensionSchema.Tables.REPEATED_USE_KEM_SIGNED_PRE_KEYS.tableName()); } @Override diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DeviceControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DeviceControllerTest.java index 3d6e47b53..1f34c2b1c 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DeviceControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DeviceControllerTest.java @@ -315,6 +315,8 @@ class DeviceControllerTest { verify(pendingDevicesManager).remove(AuthHelper.VALID_NUMBER); verify(messagesManager).clear(eq(AuthHelper.VALID_UUID), eq(42L)); verify(clientPresenceManager).disconnectPresence(AuthHelper.VALID_UUID, Device.MASTER_ID); + verify(keysManager).storeEcSignedPreKeys(AuthHelper.VALID_UUID, Map.of(response.getDeviceId(), aciSignedPreKey.get())); + verify(keysManager).storeEcSignedPreKeys(AuthHelper.VALID_PNI, Map.of(response.getDeviceId(), pniSignedPreKey.get())); verify(keysManager).storePqLastResort(AuthHelper.VALID_UUID, Map.of(response.getDeviceId(), aciPqLastResortPreKey.get())); verify(keysManager).storePqLastResort(AuthHelper.VALID_PNI, Map.of(response.getDeviceId(), pniPqLastResortPreKey.get())); } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeysControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeysControllerTest.java index 2e9fd83f7..9b099758c 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeysControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/KeysControllerTest.java @@ -687,7 +687,7 @@ class KeysControllerTest { assertThat(response.getStatus()).isEqualTo(204); ArgumentCaptor> listCaptor = ArgumentCaptor.forClass(List.class); - verify(KEYS).store(eq(AuthHelper.VALID_UUID), eq(1L), listCaptor.capture(), isNull(), isNull()); + verify(KEYS).store(eq(AuthHelper.VALID_UUID), eq(1L), listCaptor.capture(), isNull(), eq(signedPreKey), isNull()); assertThat(listCaptor.getValue()).containsExactly(preKey); @@ -718,7 +718,7 @@ class KeysControllerTest { ArgumentCaptor> ecCaptor = ArgumentCaptor.forClass(List.class); ArgumentCaptor> pqCaptor = ArgumentCaptor.forClass(List.class); - verify(KEYS).store(eq(AuthHelper.VALID_UUID), eq(1L), ecCaptor.capture(), pqCaptor.capture(), eq(pqLastResortPreKey)); + verify(KEYS).store(eq(AuthHelper.VALID_UUID), eq(1L), ecCaptor.capture(), pqCaptor.capture(), eq(signedPreKey), eq(pqLastResortPreKey)); assertThat(ecCaptor.getValue()).containsExactly(preKey); assertThat(pqCaptor.getValue()).containsExactly(pqPreKey); @@ -820,7 +820,7 @@ class KeysControllerTest { assertThat(response.getStatus()).isEqualTo(204); ArgumentCaptor> listCaptor = ArgumentCaptor.forClass(List.class); - verify(KEYS).store(eq(AuthHelper.VALID_PNI), eq(1L), listCaptor.capture(), isNull(), isNull()); + verify(KEYS).store(eq(AuthHelper.VALID_PNI), eq(1L), listCaptor.capture(), isNull(), eq(signedPreKey), isNull()); assertThat(listCaptor.getValue()).containsExactly(preKey); @@ -852,7 +852,7 @@ class KeysControllerTest { ArgumentCaptor> ecCaptor = ArgumentCaptor.forClass(List.class); ArgumentCaptor> pqCaptor = ArgumentCaptor.forClass(List.class); - verify(KEYS).store(eq(AuthHelper.VALID_PNI), eq(1L), ecCaptor.capture(), pqCaptor.capture(), eq(pqLastResortPreKey)); + verify(KEYS).store(eq(AuthHelper.VALID_PNI), eq(1L), ecCaptor.capture(), pqCaptor.capture(), eq(signedPreKey), eq(pqLastResortPreKey)); assertThat(ecCaptor.getValue()).containsExactly(preKey); assertThat(pqCaptor.getValue()).containsExactly(pqPreKey); @@ -896,7 +896,7 @@ class KeysControllerTest { assertThat(response.getStatus()).isEqualTo(204); ArgumentCaptor> listCaptor = ArgumentCaptor.forClass(List.class); - verify(KEYS).store(eq(AuthHelper.DISABLED_UUID), eq(1L), listCaptor.capture(), isNull(), isNull()); + verify(KEYS).store(eq(AuthHelper.DISABLED_UUID), eq(1L), listCaptor.capture(), isNull(), eq(signedPreKey), isNull()); List capturedList = listCaptor.getValue(); assertThat(capturedList.size()).isEqualTo(1);