From d1d73fef3048942467149ae8e3ddf0224e2e92a7 Mon Sep 17 00:00:00 2001 From: Nicholas Date: Fri, 3 Mar 2023 09:51:27 -0500 Subject: [PATCH] Support multiple sequential captcha challenges. --- .../RegistrationSessionProcessor.kt | 5 +++ .../fragments/EnterPhoneNumberFragment.java | 3 ++ .../viewmodel/BaseRegistrationViewModel.java | 9 +++++- .../exceptions/TokenNotAcceptedException.kt | 6 ++++ .../internal/push/PushServiceSocket.java | 32 ++++++++++++++++++- 5 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 libsignal/service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/TokenNotAcceptedException.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/RegistrationSessionProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/RegistrationSessionProcessor.kt index 066d625559e..17c9684fb8b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/RegistrationSessionProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/RegistrationSessionProcessor.kt @@ -8,6 +8,7 @@ import org.whispersystems.signalservice.api.push.exceptions.MustRequestNewCodeEx import org.whispersystems.signalservice.api.push.exceptions.NoSuchSessionException import org.whispersystems.signalservice.api.push.exceptions.NonNormalizedPhoneNumberException import org.whispersystems.signalservice.api.push.exceptions.RateLimitException +import org.whispersystems.signalservice.api.push.exceptions.TokenNotAcceptedException import org.whispersystems.signalservice.api.util.Preconditions import org.whispersystems.signalservice.internal.ServiceResponse import org.whispersystems.signalservice.internal.ServiceResponseProcessor @@ -41,6 +42,10 @@ sealed class RegistrationSessionProcessor(response: ServiceResponse handleRequiredChallenges(Registratio } if (hasCaptchaToken() && processor.captchaRequired()) { - return verifyAccountRepository.verifyCaptcha(sessionId, Objects.requireNonNull(getCaptchaToken()), e164, getRegistrationSecret()) + Log.d(TAG, "Submitting completed captcha challenge"); + final String captcha = Objects.requireNonNull(getCaptchaToken()); + clearCaptchaResponse(); + return verifyAccountRepository.verifyCaptcha(sessionId, captcha, e164, getRegistrationSecret()) .map(RegistrationSessionProcessor.RegistrationSessionProcessorForSession::new); } else { String challenge = processor.getChallenge(); + Log.d(TAG, "Handling challenge of type " + challenge); if (challenge != null) { switch (challenge) { case RegistrationSessionProcessor.PUSH_CHALLENGE_KEY: diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/TokenNotAcceptedException.kt b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/TokenNotAcceptedException.kt new file mode 100644 index 00000000000..c8973e1ee33 --- /dev/null +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/TokenNotAcceptedException.kt @@ -0,0 +1,6 @@ +package org.whispersystems.signalservice.api.push.exceptions + +/** + * Exception representing that the submitted information was not accepted (e.g. the push challenge token or captcha did not match) + */ +class TokenNotAcceptedException : NonSuccessfulResponseCodeException(403) diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java index 5ac79f4cfd1..867cd26c6e2 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java @@ -86,6 +86,7 @@ import org.whispersystems.signalservice.api.push.exceptions.RemoteAttestationResponseExpiredException; import org.whispersystems.signalservice.api.push.exceptions.ResumeLocationInvalidException; import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException; +import org.whispersystems.signalservice.api.push.exceptions.TokenNotAcceptedException; import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotAssociatedWithAnAccountException; import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotReservedException; @@ -348,7 +349,7 @@ public RegistrationSessionMetadataResponse patchVerificationSession(String sessi String path = VERIFICATION_SESSION_PATH + "/" + sessionId; final UpdateVerificationSessionRequestBody requestBody = new UpdateVerificationSessionRequestBody(captchaToken, pushToken, pushChallengeToken, mcc, mnc); - try (Response response = makeServiceRequest(path, "PATCH", jsonRequestBody(JsonUtil.toJson(requestBody)), NO_HEADERS, new RegistrationSessionResponseHandler(), Optional.empty(), false)) { + try (Response response = makeServiceRequest(path, "PATCH", jsonRequestBody(JsonUtil.toJson(requestBody)), NO_HEADERS, new PatchRegistrationSessionResponseHandler(), Optional.empty(), false)) { return parseSessionMetadataResponse(response); } } @@ -2543,6 +2544,35 @@ public void handle(int responseCode, ResponseBody body) throws NonSuccessfulResp } } + + private static class PatchRegistrationSessionResponseHandler implements ResponseCodeHandler { + + @Override + public void handle(int responseCode, ResponseBody body) throws NonSuccessfulResponseCodeException, PushNetworkException { + switch (responseCode) { + case 403: + throw new TokenNotAcceptedException(); + case 404: + throw new NoSuchSessionException(); + case 409: + RegistrationSessionMetadataJson response; + try { + response = JsonUtil.fromJson(body.string(), RegistrationSessionMetadataJson.class); + } catch (IOException e) { + Log.e(TAG, "Unable to read response body.", e); + throw new NonSuccessfulResponseCodeException(409); + } + if (response.pushChallengedRequired()) { + throw new PushChallengeRequiredException(); + } else if (response.captchaRequired()) { + throw new CaptchaRequiredException(); + } else { + throw new HttpConflictException(); + } + } + } + } + private static class RegistrationCodeRequestResponseHandler implements ResponseCodeHandler { @Override public void handle(int responseCode, ResponseBody body) throws NonSuccessfulResponseCodeException, PushNetworkException { switch (responseCode) {