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