]> nmode's Git Repositories - signal-cli/commitdiff
Implement account reregistration with recovery password
authorAsamK <asamk@gmx.de>
Sat, 10 Feb 2024 10:54:06 +0000 (11:54 +0100)
committerAsamK <asamk@gmx.de>
Sat, 10 Feb 2024 10:54:06 +0000 (11:54 +0100)
lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java
lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java

index 839bdd485015172be22a27f2e6e566cbfe9a3ec7..4518fb01c86efc1c4f0113ac6f8a92fdc50080f7 100644 (file)
@@ -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) {
index 762e95ce4912a7e980a6606c37bee9259424d07f..e0d6598e49f39ce6b150d52c6ec7294569a6dd5b 100644 (file)
@@ -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);
     }