]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/manager/ProvisioningManager.java
0c26cf77964ffe4ebed3a1994ed402d62ff846a8
[signal-cli] / 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.storage.SignalAccount;
20 import org.asamk.signal.manager.util.KeyUtils;
21 import org.signal.zkgroup.InvalidInputException;
22 import org.signal.zkgroup.profiles.ProfileKey;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25 import org.whispersystems.libsignal.IdentityKeyPair;
26 import org.whispersystems.libsignal.InvalidKeyException;
27 import org.whispersystems.libsignal.util.KeyHelper;
28 import org.whispersystems.signalservice.api.SignalServiceAccountManager;
29 import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
30 import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
31 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
32 import org.whispersystems.signalservice.api.util.SleepTimer;
33 import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
34 import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
35 import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
36
37 import java.io.File;
38 import java.io.IOException;
39 import java.util.concurrent.TimeoutException;
40
41 public class ProvisioningManager {
42
43 private final static Logger logger = LoggerFactory.getLogger(ProvisioningManager.class);
44
45 private final PathConfig pathConfig;
46 private final SignalServiceConfiguration serviceConfiguration;
47 private final String userAgent;
48
49 private final SignalServiceAccountManager accountManager;
50 private final IdentityKeyPair identityKey;
51 private final int registrationId;
52 private final String password;
53
54 public ProvisioningManager(File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent) {
55 this.pathConfig = PathConfig.createDefault(settingsPath);
56 this.serviceConfiguration = serviceConfiguration;
57 this.userAgent = userAgent;
58
59 identityKey = KeyUtils.generateIdentityKeyPair();
60 registrationId = KeyHelper.generateRegistrationId(false);
61 password = KeyUtils.createPassword();
62 final SleepTimer timer = new UptimeSleepTimer();
63 GroupsV2Operations groupsV2Operations;
64 try {
65 groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceConfiguration));
66 } catch (Throwable ignored) {
67 groupsV2Operations = null;
68 }
69 accountManager = new SignalServiceAccountManager(serviceConfiguration,
70 new DynamicCredentialsProvider(null, null, password, null, SignalServiceAddress.DEFAULT_DEVICE_ID),
71 userAgent,
72 groupsV2Operations,
73 ServiceConfig.AUTOMATIC_NETWORK_RETRY,
74 timer);
75 }
76
77 public String getDeviceLinkUri() throws TimeoutException, IOException {
78 String deviceUuid = accountManager.getNewDeviceUuid();
79
80 return new DeviceLinkInfo(deviceUuid, identityKey.getPublicKey().getPublicKey()).createDeviceLinkUri();
81 }
82
83 public String finishDeviceLink(String deviceName) throws IOException, InvalidKeyException, TimeoutException, UserAlreadyExists {
84 String signalingKey = KeyUtils.createSignalingKey();
85 SignalServiceAccountManager.NewDeviceRegistrationReturn ret = accountManager.finishNewDeviceRegistration(
86 identityKey,
87 signalingKey,
88 false,
89 true,
90 registrationId,
91 deviceName);
92
93 String username = ret.getNumber();
94 // TODO do this check before actually registering
95 if (SignalAccount.userExists(pathConfig.getDataPath(), username)) {
96 throw new UserAlreadyExists(username, SignalAccount.getFileName(pathConfig.getDataPath(), username));
97 }
98
99 // Create new account with the synced identity
100 byte[] profileKeyBytes = ret.getProfileKey();
101 ProfileKey profileKey;
102 if (profileKeyBytes == null) {
103 profileKey = KeyUtils.createProfileKey();
104 } else {
105 try {
106 profileKey = new ProfileKey(profileKeyBytes);
107 } catch (InvalidInputException e) {
108 throw new IOException("Received invalid profileKey", e);
109 }
110 }
111
112 try (SignalAccount account = SignalAccount.createLinkedAccount(pathConfig.getDataPath(),
113 username,
114 ret.getUuid(),
115 password,
116 ret.getDeviceId(),
117 ret.getIdentity(),
118 registrationId,
119 signalingKey,
120 profileKey)) {
121 account.save();
122
123 try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) {
124
125 try {
126 m.refreshPreKeys();
127 } catch (Exception e) {
128 logger.error("Failed to refresh prekeys.");
129 throw e;
130 }
131
132 try {
133 m.requestSyncGroups();
134 m.requestSyncContacts();
135 m.requestSyncBlocked();
136 m.requestSyncConfiguration();
137 m.requestSyncKeys();
138 } catch (Exception e) {
139 logger.error("Failed to request sync messages from linked device.");
140 throw e;
141 }
142
143 m.close(false);
144 }
145
146 account.save();
147 }
148
149 return username;
150 }
151 }