]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java
c01ceabf2ece755e4becdd431dcacf826792d3fc
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / ProvisioningManager.java
1 /*
2 Copyright (C) 2015-2021 AsamK and contributors
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17 package org.asamk.signal.manager;
18
19 import org.asamk.signal.manager.config.ServiceConfig;
20 import org.asamk.signal.manager.config.ServiceEnvironment;
21 import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
22 import org.asamk.signal.manager.storage.SignalAccount;
23 import org.asamk.signal.manager.util.KeyUtils;
24 import org.signal.zkgroup.InvalidInputException;
25 import org.signal.zkgroup.profiles.ProfileKey;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28 import org.whispersystems.libsignal.IdentityKeyPair;
29 import org.whispersystems.libsignal.InvalidKeyException;
30 import org.whispersystems.libsignal.util.KeyHelper;
31 import org.whispersystems.signalservice.api.SignalServiceAccountManager;
32 import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
33 import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
34 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
35 import org.whispersystems.signalservice.api.util.SleepTimer;
36 import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
37 import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
38
39 import java.io.File;
40 import java.io.IOException;
41 import java.util.concurrent.TimeoutException;
42
43 public class ProvisioningManager {
44
45 private final static Logger logger = LoggerFactory.getLogger(ProvisioningManager.class);
46
47 private final PathConfig pathConfig;
48 private final ServiceEnvironmentConfig serviceEnvironmentConfig;
49 private final String userAgent;
50
51 private final SignalServiceAccountManager accountManager;
52 private final IdentityKeyPair identityKey;
53 private final int registrationId;
54 private final String password;
55
56 ProvisioningManager(PathConfig pathConfig, ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent) {
57 this.pathConfig = pathConfig;
58 this.serviceEnvironmentConfig = serviceEnvironmentConfig;
59 this.userAgent = userAgent;
60
61 identityKey = KeyUtils.generateIdentityKeyPair();
62 registrationId = KeyHelper.generateRegistrationId(false);
63 password = KeyUtils.createPassword();
64 final SleepTimer timer = new UptimeSleepTimer();
65 GroupsV2Operations groupsV2Operations;
66 try {
67 groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()));
68 } catch (Throwable ignored) {
69 groupsV2Operations = null;
70 }
71 accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
72 new DynamicCredentialsProvider(null, null, password, null, SignalServiceAddress.DEFAULT_DEVICE_ID),
73 userAgent,
74 groupsV2Operations,
75 ServiceConfig.AUTOMATIC_NETWORK_RETRY,
76 timer);
77 }
78
79 public static ProvisioningManager init(
80 File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
81 ) {
82 PathConfig pathConfig = PathConfig.createDefault(settingsPath);
83
84 final ServiceEnvironmentConfig serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(
85 serviceEnvironment,
86 userAgent);
87
88 return new ProvisioningManager(pathConfig, serviceConfiguration, userAgent);
89 }
90
91 public String getDeviceLinkUri() throws TimeoutException, IOException {
92 String deviceUuid = accountManager.getNewDeviceUuid();
93
94 return new DeviceLinkInfo(deviceUuid, identityKey.getPublicKey().getPublicKey()).createDeviceLinkUri();
95 }
96
97 public String finishDeviceLink(String deviceName) throws IOException, InvalidKeyException, TimeoutException, UserAlreadyExists {
98 String signalingKey = KeyUtils.createSignalingKey();
99 SignalServiceAccountManager.NewDeviceRegistrationReturn ret = accountManager.finishNewDeviceRegistration(
100 identityKey,
101 signalingKey,
102 false,
103 true,
104 registrationId,
105 deviceName);
106
107 String username = ret.getNumber();
108 // TODO do this check before actually registering
109 if (SignalAccount.userExists(pathConfig.getDataPath(), username)) {
110 throw new UserAlreadyExists(username, SignalAccount.getFileName(pathConfig.getDataPath(), username));
111 }
112
113 // Create new account with the synced identity
114 byte[] profileKeyBytes = ret.getProfileKey();
115 ProfileKey profileKey;
116 if (profileKeyBytes == null) {
117 profileKey = KeyUtils.createProfileKey();
118 } else {
119 try {
120 profileKey = new ProfileKey(profileKeyBytes);
121 } catch (InvalidInputException e) {
122 throw new IOException("Received invalid profileKey", e);
123 }
124 }
125
126 try (SignalAccount account = SignalAccount.createLinkedAccount(pathConfig.getDataPath(),
127 username,
128 ret.getUuid(),
129 password,
130 ret.getDeviceId(),
131 ret.getIdentity(),
132 registrationId,
133 signalingKey,
134 profileKey)) {
135 account.save();
136
137 try (Manager m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) {
138
139 try {
140 m.refreshPreKeys();
141 } catch (Exception e) {
142 logger.error("Failed to refresh prekeys.");
143 throw e;
144 }
145
146 try {
147 m.requestSyncGroups();
148 m.requestSyncContacts();
149 m.requestSyncBlocked();
150 m.requestSyncConfiguration();
151 m.requestSyncKeys();
152 } catch (Exception e) {
153 logger.error("Failed to request sync messages from linked device.");
154 throw e;
155 }
156
157 m.close(false);
158 }
159
160 account.save();
161 }
162
163 return username;
164 }
165 }