X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/6eb486e85869995263c543db94e6b07ac2691db7..a8bbdb54d006f157a009ece0cae5bf72fb636ced:/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index d27d911e..b0bfebf8 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -29,6 +29,8 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; +import org.whispersystems.signalservice.api.util.DeviceNameUtil; import org.whispersystems.signalservice.api.util.SleepTimer; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; @@ -47,7 +49,7 @@ public class ProvisioningManager { private final String userAgent; private final SignalServiceAccountManager accountManager; - private final IdentityKeyPair identityKey; + private final IdentityKeyPair tempIdentityKey; private final int registrationId; private final String password; @@ -56,7 +58,7 @@ public class ProvisioningManager { this.serviceEnvironmentConfig = serviceEnvironmentConfig; this.userAgent = userAgent; - identityKey = KeyUtils.generateIdentityKeyPair(); + tempIdentityKey = KeyUtils.generateIdentityKeyPair(); registrationId = KeyHelper.generateRegistrationId(false); password = KeyUtils.createPassword(); final SleepTimer timer = new UptimeSleepTimer(); @@ -87,33 +89,39 @@ public class ProvisioningManager { public URI getDeviceLinkUri() throws TimeoutException, IOException { var deviceUuid = accountManager.getNewDeviceUuid(); - return new DeviceLinkInfo(deviceUuid, identityKey.getPublicKey().getPublicKey()).createDeviceLinkUri(); + return new DeviceLinkInfo(deviceUuid, tempIdentityKey.getPublicKey().getPublicKey()).createDeviceLinkUri(); } public Manager finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExists { - var ret = accountManager.getNewDeviceRegistration(identityKey); + var ret = accountManager.getNewDeviceRegistration(tempIdentityKey); var number = ret.getNumber(); - if (SignalAccount.userExists(pathConfig.getDataPath(), number)) { + logger.info("Received link information from {}, linking in progress ...", number); + + if (SignalAccount.userExists(pathConfig.getDataPath(), number) && !canRelinkExistingAccount(number)) { throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.getDataPath(), number)); } + var encryptedDeviceName = deviceName == null + ? null + : DeviceNameUtil.encryptDeviceName(deviceName, ret.getIdentity().getPrivateKey()); + var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(), false, true, registrationId, - deviceName); + encryptedDeviceName); // Create new account with the synced identity var profileKey = ret.getProfileKey() == null ? KeyUtils.createProfileKey() : ret.getProfileKey(); SignalAccount account = null; try { - account = SignalAccount.createLinkedAccount(pathConfig.getDataPath(), + account = SignalAccount.createOrUpdateLinkedAccount(pathConfig.getDataPath(), number, ret.getUuid(), password, - deviceName, + encryptedDeviceName, deviceId, ret.getIdentity(), registrationId, @@ -126,7 +134,7 @@ public class ProvisioningManager { try { m.refreshPreKeys(); } catch (Exception e) { - logger.error("Failed to refresh prekeys."); + logger.error("Failed to check new account state."); throw e; } @@ -153,4 +161,31 @@ public class ProvisioningManager { } } } + + private boolean canRelinkExistingAccount(final String number) throws UserAlreadyExists, IOException { + final SignalAccount signalAccount; + try { + signalAccount = SignalAccount.load(pathConfig.getDataPath(), number, false); + } catch (IOException e) { + logger.debug("Account in use or failed to load.", e); + return false; + } + + try (signalAccount) { + if (signalAccount.isMasterDevice()) { + logger.debug("Account is a master device."); + return false; + } + + final var m = new Manager(signalAccount, pathConfig, serviceEnvironmentConfig, userAgent); + try (m) { + m.checkAccountState(); + } catch (AuthorizationFailedException ignored) { + return true; + } + + logger.debug("Account is still successfully linked."); + return false; + } + } }