From 6bcced37215b2af31d9e2558d30592e403af0632 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 26 Jul 2023 20:15:57 -0400 Subject: [PATCH] Fix a RRP recovery path. --- .../viewmodel/RegistrationViewModel.java | 14 ++++++--- .../internal/push/BackupAuthCheckRequest.kt | 24 ++++++++++++-- .../internal/push/PushServiceSocket.java | 31 ++++++------------- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java index 578713ab242..b026cb534d0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java @@ -31,7 +31,6 @@ import org.whispersystems.signalservice.api.SvrNoDataException; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.kbs.PinHashUtil; -import org.whispersystems.signalservice.api.push.ServiceIdType; import org.whispersystems.signalservice.api.push.exceptions.IncorrectCodeException; import org.whispersystems.signalservice.api.push.exceptions.IncorrectRegistrationRecoveryPasswordException; import org.whispersystems.signalservice.internal.ServiceResponse; @@ -346,12 +345,12 @@ private Single verifyReRegisterWithRecoveryPassword(@No if (hasRecoveryPassword) { return Single.just(true); } else { - return checkForValidKbsAuthCredentials(); + return checkForValidSvrAuthCredentials(); } }); } - private Single checkForValidKbsAuthCredentials() { + private Single checkForValidSvrAuthCredentials() { final List svrAuthTokenList = SignalStore.svr().getAuthTokenList(); List usernamePasswords = svrAuthTokenList .stream() @@ -370,7 +369,14 @@ private Single checkForValidKbsAuthCredentials() { } return registrationRepository.getSvrAuthCredential(getRegistrationData(), usernamePasswords) - .flatMap(p -> Single.just(p.getValid() != null)) + .flatMap(p -> { + if (p.hasValidSvr2AuthCredential()) { + setSvrAuthCredentials(new SvrAuthCredentialSet(null, p.requireSvr2AuthCredential())); + return Single.just(true); + } else { + return Single.just(false); + } + }) .onErrorReturnItem(false) .observeOn(AndroidSchedulers.mainThread()); } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/BackupAuthCheckRequest.kt b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/BackupAuthCheckRequest.kt index 31243bea1ae..dabb05ce4a5 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/BackupAuthCheckRequest.kt +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/BackupAuthCheckRequest.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonCreator import okio.ByteString.Companion.encode import org.whispersystems.signalservice.internal.ServiceResponse import org.whispersystems.signalservice.internal.ServiceResponseProcessor +import java.io.IOException import java.nio.charset.StandardCharsets /** @@ -23,7 +24,7 @@ data class BackupAuthCheckResponse @JsonCreator constructor( ) { private val actualMatches = matches["matches"] ?: emptyMap() - val match: String? = actualMatches.entries.firstOrNull { it.value.toString() == "match" }?.key?.toBasic() + val match: AuthCredentials? = actualMatches.entries.firstOrNull { it.value.toString() == "match" }?.key?.toAuthCredential() val invalid: List = actualMatches.filterValues { it.toString() == "invalid" }.keys.map { it.toBasic() } /** Server expects and returns values as : but we prefer the full encoded Basic auth header format */ @@ -31,6 +32,19 @@ data class BackupAuthCheckResponse @JsonCreator constructor( return "Basic ${encode(StandardCharsets.ISO_8859_1).base64()}" } + private fun String.toAuthCredential(): AuthCredentials { + val firstColonIndex = this.indexOf(":") + + if (firstColonIndex < 0) { + throw IOException("Invalid credential returned!") + } + + val username = this.substring(0, firstColonIndex) + val password = this.substring(firstColonIndex + 1) + + return AuthCredentials.create(username, password) + } + fun merge(other: BackupAuthCheckResponse): BackupAuthCheckResponse { return BackupAuthCheckResponse(this.matches + other.matches) } @@ -44,7 +58,11 @@ class BackupAuthCheckProcessor(response: ServiceResponse> performIdentityCheck(@Nonn public Single> checkBackupAuthCredentials(@Nonnull BackupAuthCheckRequest request, @Nonnull ResponseMapper responseMapper) { - return Single - .zip( - createBackupAuthCheckSingle(BACKUP_AUTH_CHECK_V1, request, responseMapper), - createBackupAuthCheckSingle(BACKUP_AUTH_CHECK_V2, request, responseMapper), - (v1, v2) -> { - if (v1.getResult().isPresent() && v2.getResult().isPresent()) { - BackupAuthCheckResponse v1Result = v1.getResult().get(); - BackupAuthCheckResponse v2Result = v2.getResult().get(); - BackupAuthCheckResponse merged = v1Result.merge(v2Result); - - return new ServiceResponse<>(v2.getStatus(), null, merged, null, null); - } else if (v1.getResult().isEmpty() && v2.getResult().isEmpty()) { - return v2; - } else if (v2.getResult().isEmpty()) { - return v1; - } else { - return v2; - } - } - ) + Single> requestSingle = Single.fromCallable(() -> { + try (Response response = getServiceConnection(BACKUP_AUTH_CHECK, "POST", jsonRequestBody(JsonUtil.toJson(request)), Collections.emptyMap(), Optional.empty(), false)) { + String body = response.body() != null ? readBodyString(response.body()): ""; + return responseMapper.map(response.code(), body, response::header, false); + } + }); + + return requestSingle .subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) .onErrorReturn(ServiceResponse::forUnknownError);