From 9f6b6cb657e33e65a6455ebedaaddcc32a4bd656 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 10 Feb 2024 11:54:06 +0100 Subject: [PATCH] Implement account reregistration with recovery password --- .../internal/RegistrationManagerImpl.java | 118 ++++++++++++------ .../signal/manager/storage/SignalAccount.java | 10 +- 2 files changed, 90 insertions(+), 38 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java index 839bdd48..4518fb01 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java @@ -39,6 +39,7 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.account.PreKeyCollection; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; +import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.push.ServiceId.ACI; import org.whispersystems.signalservice.api.push.ServiceId.PNI; import org.whispersystems.signalservice.api.push.ServiceIdType; @@ -111,6 +112,11 @@ public class RegistrationManagerImpl implements RegistrationManager { } try { + final var recoveryPassword = account.getRecoveryPassword(); + if (recoveryPassword != null && account.isPrimaryDevice() && attemptReregisterAccount(recoveryPassword)) { + return; + } + if (account.getAci() != null && attemptReactivateAccount()) { return; } @@ -156,43 +162,7 @@ public class RegistrationManagerImpl implements RegistrationManager { pin = null; } - //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID))); - final var aci = ACI.parseOrThrow(response.getUuid()); - final var pni = PNI.parseOrThrow(response.getPni()); - account.finishRegistration(aci, pni, masterKey, pin, aciPreKeys, pniPreKeys); - accountFileUpdater.updateAccountIdentifiers(account.getNumber(), aci); - - ManagerImpl m = null; - try { - m = new ManagerImpl(account, pathConfig, accountFileUpdater, serviceEnvironmentConfig, userAgent); - account = null; - - m.refreshPreKeys(); - if (response.isStorageCapable()) { - m.syncRemoteStorage(); - } - // Set an initial empty profile so user can be added to groups - try { - m.updateProfile(UpdateProfile.newBuilder().build()); - } catch (NoClassDefFoundError e) { - logger.warn("Failed to set default profile: {}", e.getMessage()); - } - - try { - m.refreshCurrentUsername(); - } catch (IOException | BaseUsernameException e) { - logger.warn("Failed to refresh current username", e); - } - - if (newManagerListener != null) { - newManagerListener.accept(m); - m = null; - } - } finally { - if (m != null) { - m.close(); - } - } + finishAccountRegistration(response, pin, masterKey, aciPreKeys, pniPreKeys); } @Override @@ -207,6 +177,34 @@ public class RegistrationManagerImpl implements RegistrationManager { return account.isRegistered(); } + private boolean attemptReregisterAccount(final String recoveryPassword) { + try { + if (account.getPniIdentityKeyPair() == null) { + account.setPniIdentityKeyPair(KeyUtils.generateIdentityKeyPair()); + } + + final var aciPreKeys = generatePreKeysForType(account.getAccountData(ServiceIdType.ACI)); + final var pniPreKeys = generatePreKeysForType(account.getAccountData(ServiceIdType.PNI)); + final var response = Utils.handleResponseException(accountManager.registerAccount(null, + recoveryPassword, + account.getAccountAttributes(null), + aciPreKeys, + pniPreKeys, + null, + true)); + finishAccountRegistration(response, + account.getRegistrationLockPin(), + account.getPinBackedMasterKey(), + aciPreKeys, + pniPreKeys); + logger.info("Reregistered existing account, verify is not necessary."); + return true; + } catch (IOException e) { + logger.debug("Failed to reregister account with recovery password", e); + } + return false; + } + private boolean attemptReactivateAccount() { try { final var accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.signalServiceConfiguration(), @@ -254,6 +252,52 @@ public class RegistrationManagerImpl implements RegistrationManager { true)); } + private void finishAccountRegistration( + final VerifyAccountResponse response, + final String pin, + final MasterKey masterKey, + final PreKeyCollection aciPreKeys, + final PreKeyCollection pniPreKeys + ) throws IOException { + //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID))); + final var aci = ACI.parseOrThrow(response.getUuid()); + final var pni = PNI.parseOrThrow(response.getPni()); + account.finishRegistration(aci, pni, masterKey, pin, aciPreKeys, pniPreKeys); + accountFileUpdater.updateAccountIdentifiers(account.getNumber(), aci); + + ManagerImpl m = null; + try { + m = new ManagerImpl(account, pathConfig, accountFileUpdater, serviceEnvironmentConfig, userAgent); + account = null; + + m.refreshPreKeys(); + if (response.isStorageCapable()) { + m.syncRemoteStorage(); + } + // Set an initial empty profile so user can be added to groups + try { + m.updateProfile(UpdateProfile.newBuilder().build()); + } catch (NoClassDefFoundError e) { + logger.warn("Failed to set default profile: {}", e.getMessage()); + } + + try { + m.refreshCurrentUsername(); + } catch (IOException | BaseUsernameException e) { + logger.warn("Failed to refresh current username", e); + } + + if (newManagerListener != null) { + newManagerListener.accept(m); + m = null; + } + } finally { + if (m != null) { + m.close(); + } + } + } + @Override public void close() { if (account != null) { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 762e95ce..e0d6598e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -1351,7 +1351,7 @@ public class SignalAccount implements Closeable { getAccountCapabilities(), encryptedDeviceName, pniAccountData.getLocalRegistrationId(), - null); // TODO recoveryPassword? + getRecoveryPassword()); } public AccountAttributes.Capabilities getAccountCapabilities() { @@ -1533,6 +1533,14 @@ public class SignalAccount implements Closeable { save(); } + public String getRecoveryPassword() { + final var masterKey = getPinBackedMasterKey(); + if (masterKey == null) { + return null; + } + return masterKey.deriveRegistrationRecoveryPassword(); + } + public long getStorageManifestVersion() { return getKeyValueStore().getEntry(storageManifestVersion); } -- 2.50.1