]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/util/NumberVerificationUtils.java
a4c2ba2450fb91738687cf5643a4229aa4a283b5
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / util / NumberVerificationUtils.java
1 package org.asamk.signal.manager.util;
2
3 import org.asamk.signal.manager.api.CaptchaRequiredException;
4 import org.asamk.signal.manager.api.IncorrectPinException;
5 import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException;
6 import org.asamk.signal.manager.api.Pair;
7 import org.asamk.signal.manager.api.PinLockedException;
8 import org.asamk.signal.manager.helper.PinHelper;
9 import org.whispersystems.signalservice.api.KbsPinData;
10 import org.whispersystems.signalservice.api.SignalServiceAccountManager;
11 import org.whispersystems.signalservice.api.kbs.MasterKey;
12 import org.whispersystems.signalservice.internal.ServiceResponse;
13 import org.whispersystems.signalservice.internal.push.LockedException;
14 import org.whispersystems.signalservice.internal.push.RequestVerificationCodeResponse;
15 import org.whispersystems.signalservice.internal.push.VerifyAccountResponse;
16
17 import java.io.IOException;
18 import java.util.Locale;
19 import java.util.Optional;
20
21 public class NumberVerificationUtils {
22
23 public static void requestVerificationCode(
24 SignalServiceAccountManager accountManager, String captcha, boolean voiceVerification
25 ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException {
26 captcha = captcha == null ? null : captcha.replace("signalcaptcha://", "");
27 final ServiceResponse<RequestVerificationCodeResponse> response;
28 final var locale = Utils.getDefaultLocale(Locale.US);
29 if (voiceVerification) {
30 response = accountManager.requestVoiceVerificationCode(locale,
31 Optional.ofNullable(captcha),
32 Optional.empty(),
33 Optional.empty());
34 } else {
35 response = accountManager.requestSmsVerificationCode(locale,
36 false,
37 Optional.ofNullable(captcha),
38 Optional.empty(),
39 Optional.empty());
40 }
41 try {
42 handleResponseException(response);
43 } catch (org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException e) {
44 throw new CaptchaRequiredException(e.getMessage(), e);
45 } catch (org.whispersystems.signalservice.api.push.exceptions.NonNormalizedPhoneNumberException e) {
46 throw new NonNormalizedPhoneNumberException("Phone number is not normalized ("
47 + e.getMessage()
48 + "). Expected normalized: "
49 + e.getNormalizedNumber(), e);
50 }
51 }
52
53 public static Pair<VerifyAccountResponse, MasterKey> verifyNumber(
54 String verificationCode, String pin, PinHelper pinHelper, Verifier verifier
55 ) throws IOException, PinLockedException, IncorrectPinException {
56 verificationCode = verificationCode.replace("-", "");
57 try {
58 final var response = verifyAccountWithCode(verificationCode, null, verifier);
59
60 return new Pair<>(response, null);
61 } catch (LockedException e) {
62 if (pin == null) {
63 throw new PinLockedException(e.getTimeRemaining());
64 }
65
66 KbsPinData registrationLockData;
67 registrationLockData = pinHelper.getRegistrationLockData(pin, e);
68 if (registrationLockData == null) {
69 throw e;
70 }
71
72 var registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock();
73 VerifyAccountResponse response;
74 try {
75 response = verifyAccountWithCode(verificationCode, registrationLock, verifier);
76 } catch (LockedException _e) {
77 throw new AssertionError("KBS Pin appeared to matched but reg lock still failed!");
78 }
79
80 return new Pair<>(response, registrationLockData.getMasterKey());
81 }
82 }
83
84 private static VerifyAccountResponse verifyAccountWithCode(
85 final String verificationCode, final String registrationLock, final Verifier verifier
86 ) throws IOException {
87 final var response = verifier.verify(verificationCode, registrationLock);
88 handleResponseException(response);
89 return response.getResult().get();
90 }
91
92 private static void handleResponseException(final ServiceResponse<?> response) throws IOException {
93 final var throwableOptional = response.getExecutionError().or(response::getApplicationError);
94 if (throwableOptional.isPresent()) {
95 if (throwableOptional.get() instanceof IOException) {
96 throw (IOException) throwableOptional.get();
97 } else {
98 throw new IOException(throwableOptional.get());
99 }
100 }
101 }
102
103 public interface Verifier {
104
105 ServiceResponse<VerifyAccountResponse> verify(
106 String verificationCode, String registrationLock
107 );
108 }
109 }