]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/manager/RegistrationManager.java
Improve logging for provisioning
[signal-cli] / src / main / java / org / asamk / signal / manager / RegistrationManager.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.helper.PinHelper;
20 import org.asamk.signal.manager.storage.SignalAccount;
21 import org.asamk.signal.manager.util.KeyUtils;
22 import org.signal.zkgroup.profiles.ProfileKey;
23 import org.whispersystems.libsignal.IdentityKeyPair;
24 import org.whispersystems.libsignal.util.KeyHelper;
25 import org.whispersystems.libsignal.util.guava.Optional;
26 import org.whispersystems.signalservice.api.KbsPinData;
27 import org.whispersystems.signalservice.api.KeyBackupService;
28 import org.whispersystems.signalservice.api.KeyBackupServicePinException;
29 import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
30 import org.whispersystems.signalservice.api.SignalServiceAccountManager;
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.api.util.UuidUtil;
35 import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
36 import org.whispersystems.signalservice.internal.push.LockedException;
37 import org.whispersystems.signalservice.internal.push.VerifyAccountResponse;
38 import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
39
40 import java.io.File;
41 import java.io.IOException;
42 import java.util.Locale;
43
44 public class RegistrationManager implements AutoCloseable {
45
46 private SignalAccount account;
47 private final PathConfig pathConfig;
48 private final SignalServiceConfiguration serviceConfiguration;
49 private final String userAgent;
50
51 private final SignalServiceAccountManager accountManager;
52 private final PinHelper pinHelper;
53
54 public RegistrationManager(
55 SignalAccount account,
56 PathConfig pathConfig,
57 SignalServiceConfiguration serviceConfiguration,
58 String userAgent
59 ) {
60 this.account = account;
61 this.pathConfig = pathConfig;
62 this.serviceConfiguration = serviceConfiguration;
63 this.userAgent = userAgent;
64
65 final SleepTimer timer = new UptimeSleepTimer();
66 this.accountManager = new SignalServiceAccountManager(serviceConfiguration, new DynamicCredentialsProvider(
67 // Using empty UUID, because registering doesn't work otherwise
68 null,
69 account.getUsername(),
70 account.getPassword(),
71 account.getSignalingKey(),
72 SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, null, timer);
73 final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager);
74 this.pinHelper = new PinHelper(keyBackupService);
75 }
76
77 public static RegistrationManager init(
78 String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent
79 ) throws IOException {
80 PathConfig pathConfig = PathConfig.createDefault(settingsPath);
81
82 if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) {
83 IdentityKeyPair identityKey = KeyUtils.generateIdentityKeyPair();
84 int registrationId = KeyHelper.generateRegistrationId(false);
85
86 ProfileKey profileKey = KeyUtils.createProfileKey();
87 SignalAccount account = SignalAccount.create(pathConfig.getDataPath(),
88 username,
89 identityKey,
90 registrationId,
91 profileKey);
92 account.save();
93
94 return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
95 }
96
97 SignalAccount account = SignalAccount.load(pathConfig.getDataPath(), username);
98
99 return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
100 }
101
102 public void register(boolean voiceVerification, String captcha) throws IOException {
103 if (account.getPassword() == null) {
104 account.setPassword(KeyUtils.createPassword());
105 }
106
107 if (voiceVerification) {
108 accountManager.requestVoiceVerificationCode(Locale.getDefault(),
109 Optional.fromNullable(captcha),
110 Optional.absent());
111 } else {
112 accountManager.requestSmsVerificationCode(false, Optional.fromNullable(captcha), Optional.absent());
113 }
114
115 account.save();
116 }
117
118 public void verifyAccount(
119 String verificationCode, String pin
120 ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException {
121 verificationCode = verificationCode.replace("-", "");
122 if (account.getSignalingKey() == null) {
123 account.setSignalingKey(KeyUtils.createSignalingKey());
124 }
125 VerifyAccountResponse response;
126 try {
127 response = verifyAccountWithCode(verificationCode, pin, null);
128 account.setPinMasterKey(null);
129 } catch (LockedException e) {
130 if (pin == null) {
131 throw e;
132 }
133
134 KbsPinData registrationLockData = pinHelper.getRegistrationLockData(pin, e);
135 if (registrationLockData == null) {
136 throw e;
137 }
138
139 String registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock();
140 try {
141 response = verifyAccountWithCode(verificationCode, null, registrationLock);
142 } catch (LockedException _e) {
143 throw new AssertionError("KBS Pin appeared to matched but reg lock still failed!");
144 }
145 account.setPinMasterKey(registrationLockData.getMasterKey());
146 }
147
148 // TODO response.isStorageCapable()
149 //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
150
151 account.setDeviceId(SignalServiceAddress.DEFAULT_DEVICE_ID);
152 account.setMultiDevice(false);
153 account.setRegistered(true);
154 account.setUuid(UuidUtil.parseOrNull(response.getUuid()));
155 account.setRegistrationLockPin(pin);
156 account.getSignalProtocolStore()
157 .saveIdentity(account.getSelfAddress(),
158 account.getSignalProtocolStore().getIdentityKeyPair().getPublicKey(),
159 TrustLevel.TRUSTED_VERIFIED);
160
161 try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) {
162
163 m.refreshPreKeys();
164
165 m.close(false);
166 }
167
168 account.save();
169 }
170
171 private VerifyAccountResponse verifyAccountWithCode(
172 final String verificationCode, final String legacyPin, final String registrationLock
173 ) throws IOException {
174 return accountManager.verifyAccountWithCode(verificationCode,
175 account.getSignalingKey(),
176 account.getSignalProtocolStore().getLocalRegistrationId(),
177 true,
178 legacyPin,
179 registrationLock,
180 account.getSelfUnidentifiedAccessKey(),
181 account.isUnrestrictedUnidentifiedAccess(),
182 ServiceConfig.capabilities,
183 account.isDiscoverableByPhoneNumber());
184 }
185
186 @Override
187 public void close() throws Exception {
188 if (account != null) {
189 account.close();
190 account = null;
191 }
192 }
193 }