*/
package org.asamk.signal.manager;
+import org.asamk.signal.manager.config.ServiceConfig;
+import org.asamk.signal.manager.config.ServiceEnvironment;
+import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.util.KeyUtils;
-import org.signal.zkgroup.InvalidInputException;
-import org.signal.zkgroup.profiles.ProfileKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.IdentityKeyPair;
-import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.KeyHelper;
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.util.DeviceNameUtil;
import org.whispersystems.signalservice.api.util.SleepTimer;
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
-import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
import java.io.File;
import java.io.IOException;
+import java.net.URI;
import java.util.concurrent.TimeoutException;
public class ProvisioningManager {
private final static Logger logger = LoggerFactory.getLogger(ProvisioningManager.class);
private final PathConfig pathConfig;
- private final SignalServiceConfiguration serviceConfiguration;
+ private final ServiceEnvironmentConfig serviceEnvironmentConfig;
private final String userAgent;
private final SignalServiceAccountManager accountManager;
- private final IdentityKeyPair identityKey;
+ private final IdentityKeyPair tempIdentityKey;
private final int registrationId;
private final String password;
- public ProvisioningManager(File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent) {
- this.pathConfig = PathConfig.createDefault(settingsPath);
- this.serviceConfiguration = serviceConfiguration;
+ ProvisioningManager(PathConfig pathConfig, ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent) {
+ this.pathConfig = pathConfig;
+ this.serviceEnvironmentConfig = serviceEnvironmentConfig;
this.userAgent = userAgent;
- identityKey = KeyUtils.generateIdentityKeyPair();
+ tempIdentityKey = KeyUtils.generateIdentityKeyPair();
registrationId = KeyHelper.generateRegistrationId(false);
password = KeyUtils.createPassword();
final SleepTimer timer = new UptimeSleepTimer();
GroupsV2Operations groupsV2Operations;
try {
- groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceConfiguration));
+ groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()));
} catch (Throwable ignored) {
groupsV2Operations = null;
}
- accountManager = new SignalServiceAccountManager(serviceConfiguration,
- new DynamicCredentialsProvider(null, null, password, null, SignalServiceAddress.DEFAULT_DEVICE_ID),
+ accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
+ new DynamicCredentialsProvider(null, null, password, SignalServiceAddress.DEFAULT_DEVICE_ID),
userAgent,
groupsV2Operations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY,
timer);
}
- public String getDeviceLinkUri() throws TimeoutException, IOException {
- String deviceUuid = accountManager.getNewDeviceUuid();
+ public static ProvisioningManager init(
+ File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
+ ) {
+ var pathConfig = PathConfig.createDefault(settingsPath);
- return new DeviceLinkInfo(deviceUuid, identityKey.getPublicKey().getPublicKey()).createDeviceLinkUri();
+ final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
+
+ return new ProvisioningManager(pathConfig, serviceConfiguration, userAgent);
}
- public String finishDeviceLink(String deviceName) throws IOException, InvalidKeyException, TimeoutException, UserAlreadyExists {
- String signalingKey = KeyUtils.createSignalingKey();
- SignalServiceAccountManager.NewDeviceRegistrationReturn ret = accountManager.finishNewDeviceRegistration(
- identityKey,
- signalingKey,
- false,
- true,
- registrationId,
- deviceName);
+ public URI getDeviceLinkUri() throws TimeoutException, IOException {
+ var deviceUuid = accountManager.getNewDeviceUuid();
- String username = ret.getNumber();
- // TODO do this check before actually registering
- if (SignalAccount.userExists(pathConfig.getDataPath(), username)) {
- throw new UserAlreadyExists(username, SignalAccount.getFileName(pathConfig.getDataPath(), username));
- }
+ return new DeviceLinkInfo(deviceUuid, tempIdentityKey.getPublicKey().getPublicKey()).createDeviceLinkUri();
+ }
- // Create new account with the synced identity
- byte[] profileKeyBytes = ret.getProfileKey();
- ProfileKey profileKey;
- if (profileKeyBytes == null) {
- profileKey = KeyUtils.createProfileKey();
- } else {
- try {
- profileKey = new ProfileKey(profileKeyBytes);
- } catch (InvalidInputException e) {
- throw new IOException("Received invalid profileKey", e);
- }
+ public Manager finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExists {
+ var ret = accountManager.getNewDeviceRegistration(tempIdentityKey);
+ var number = ret.getNumber();
+
+ logger.info("Received link information from {}, linking in progress ...", number);
+
+ if (SignalAccount.userExists(pathConfig.getDataPath(), number)) {
+ throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.getDataPath(), number));
}
- try (SignalAccount account = SignalAccount.createLinkedAccount(pathConfig.getDataPath(),
- username,
- ret.getUuid(),
- password,
- ret.getDeviceId(),
- ret.getIdentity(),
+ var encryptedDeviceName = deviceName == null
+ ? null
+ : DeviceNameUtil.encryptDeviceName(deviceName, ret.getIdentity().getPrivateKey());
+
+ var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(),
+ false,
+ true,
registrationId,
- signalingKey,
- profileKey)) {
- account.save();
+ encryptedDeviceName);
+
+ // Create new account with the synced identity
+ var profileKey = ret.getProfileKey() == null ? KeyUtils.createProfileKey() : ret.getProfileKey();
- try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) {
+ SignalAccount account = null;
+ try {
+ account = SignalAccount.createLinkedAccount(pathConfig.getDataPath(),
+ number,
+ ret.getUuid(),
+ password,
+ encryptedDeviceName,
+ deviceId,
+ ret.getIdentity(),
+ registrationId,
+ profileKey);
+
+ Manager m = null;
+ try {
+ m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent);
try {
m.refreshPreKeys();
}
try {
- m.requestSyncGroups();
- m.requestSyncContacts();
- m.requestSyncBlocked();
- m.requestSyncConfiguration();
- m.requestSyncKeys();
+ m.requestAllSyncData();
} catch (Exception e) {
logger.error("Failed to request sync messages from linked device.");
throw e;
}
- m.close(false);
- }
+ final var result = m;
+ account = null;
+ m = null;
- account.save();
+ return result;
+ } finally {
+ if (m != null) {
+ m.close();
+ }
+ }
+ } finally {
+ if (account != null) {
+ account.close();
+ }
}
-
- return username;
}
}