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 d2de43848..8c34d49f3 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java @@ -637,10 +637,10 @@ public class AccountsManager extends RedisPubSubAdapter implemen @Nullable final Map pniPqLastResortPreKeys, @Nullable final Map pniRegistrationIds) throws InterruptedException, MismatchedDevicesException { - final String originalNumber = account.getNumber(); final UUID originalPhoneNumberIdentifier = account.getPhoneNumberIdentifier(); + final UUID targetPhoneNumberIdentifier = phoneNumberIdentifiers.getPhoneNumberIdentifier(targetNumber).join(); - if (originalNumber.equals(targetNumber)) { + if (originalPhoneNumberIdentifier.equals(targetPhoneNumberIdentifier)) { if (pniIdentityKey != null) { throw new IllegalArgumentException("change number must supply a changed phone number; otherwise use updatePniKeys"); } @@ -650,7 +650,6 @@ public class AccountsManager extends RedisPubSubAdapter implemen validateDevices(account, pniSignedPreKeys, pniPqLastResortPreKeys, pniRegistrationIds); final AtomicReference updatedAccount = new AtomicReference<>(); - final UUID targetPhoneNumberIdentifier = phoneNumberIdentifiers.getPhoneNumberIdentifier(targetNumber).join(); accountLockManager.withLock(List.of(account.getPhoneNumberIdentifier(), targetPhoneNumberIdentifier), () -> { redisDelete(account); diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java index 226ecd74e..4e4aa9f34 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java @@ -1054,17 +1054,35 @@ class AccountsManagerTest { final String number = "+14152222222"; Account account = AccountsHelper.generateTestAccount(number, UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH]); + phoneNumberIdentifiersByE164.put(number, account.getPhoneNumberIdentifier()); account = accountsManager.changeNumber(account, number, null, null, null, null); assertEquals(number, account.getNumber()); verify(keysManager, never()).deleteSingleUsePreKeys(any()); } + @Test + void testChangePhoneNumberDifferentNumberSamePni() throws InterruptedException, MismatchedDevicesException { + final String originalNumber = "+22923456789"; + // the canonical form of numbers may change over time, so we use PNIs as stable identifiers + final String newNumber = "+2290123456789"; + + Account account = AccountsHelper.generateTestAccount(originalNumber, UUID.randomUUID(), UUID.randomUUID(), + new ArrayList<>(), new byte[UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH]); + phoneNumberIdentifiersByE164.put(originalNumber, account.getPhoneNumberIdentifier()); + phoneNumberIdentifiersByE164.put(newNumber, account.getPhoneNumberIdentifier()); + account = accountsManager.changeNumber(account, newNumber, null, null, null, null); + + assertEquals(originalNumber, account.getNumber()); + verify(keysManager, never()).deleteSingleUsePreKeys(any()); + } + @Test void testChangePhoneNumberSameNumberWithPniData() { final String number = "+14152222222"; Account account = AccountsHelper.generateTestAccount(number, UUID.randomUUID(), UUID.randomUUID(), new ArrayList<>(), new byte[UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH]); + phoneNumberIdentifiersByE164.put(number, account.getPhoneNumberIdentifier()); final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair(); assertThrows(IllegalArgumentException.class, () -> accountsManager.changeNumber(