diff --git a/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java index 77f393ed05..b212fad57a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java @@ -50,6 +50,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.pnikosis.materialishprogress.ProgressWheel; import org.signal.core.util.concurrent.LifecycleDisposable; +import org.signal.core.util.concurrent.RxExtensions; import org.signal.core.util.concurrent.SimpleTask; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.components.RecyclerViewFastScroller; @@ -70,6 +71,8 @@ import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery; import org.thoughtcrime.securesms.groups.SelectionLimits; import org.thoughtcrime.securesms.groups.ui.GroupLimitDialog; import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.profiles.manage.UsernameRepository; +import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.UsernameAciFetchResult; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.CommunicationActions; @@ -682,11 +685,18 @@ public final class ContactSelectionListFragment extends LoggingFragment { AlertDialog loadingDialog = SimpleProgressDialog.show(requireContext()); SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> { - return UsernameUtil.fetchAciForUsername(username); - }, uuid -> { + try { + return RxExtensions.safeBlockingGet(UsernameRepository.fetchAciForUsername(UsernameUtil.sanitizeUsernameFromSearch(username))); + } catch (InterruptedException e) { + Log.w(TAG, "Interrupted?", e); + return UsernameAciFetchResult.NetworkError.INSTANCE; + } + }, result -> { loadingDialog.dismiss(); - if (uuid.isPresent()) { - Recipient recipient = Recipient.externalUsername(uuid.get(), username); + + // TODO Could be more specific with errors + if (result instanceof UsernameAciFetchResult.Success success) { + Recipient recipient = Recipient.externalUsername(success.getAci(), username); SelectedContact selected = SelectedContact.forUsername(recipient.getId(), username); if (onContactSelectedListener != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/colorpicker/UsernameLinkQrColorPickerViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/colorpicker/UsernameLinkQrColorPickerViewModel.kt index 0f6b475bd2..8a1273df90 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/colorpicker/UsernameLinkQrColorPickerViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/colorpicker/UsernameLinkQrColorPickerViewModel.kt @@ -15,9 +15,9 @@ import org.thoughtcrime.securesms.components.settings.app.usernamelinks.QrCodeSt import org.thoughtcrime.securesms.components.settings.app.usernamelinks.UsernameQrCodeColorScheme import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.toLink import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.storage.StorageSyncHelper -import org.thoughtcrime.securesms.util.UsernameUtil class UsernameLinkQrColorPickerViewModel : ViewModel() { @@ -39,7 +39,7 @@ class UsernameLinkQrColorPickerViewModel : ViewModel() { if (usernameLink != null) { disposable += Single - .fromCallable { QrCodeData.forData(UsernameUtil.generateLink(usernameLink), 64) } + .fromCallable { QrCodeData.forData(usernameLink.toLink(), 64) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { qrData -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/UsernameLinkSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/UsernameLinkSettingsViewModel.kt index 27b68e57be..c2452b23c3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/UsernameLinkSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/UsernameLinkSettingsViewModel.kt @@ -34,9 +34,9 @@ import org.thoughtcrime.securesms.components.settings.app.usernamelinks.main.Use import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.profiles.manage.UsernameRepository +import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.toLink import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.NetworkUtil -import org.thoughtcrime.securesms.util.UsernameUtil import org.whispersystems.signalservice.api.push.UsernameLinkComponents import java.util.Optional @@ -48,7 +48,7 @@ class UsernameLinkSettingsViewModel : ViewModel() { UsernameLinkSettingsState( activeTab = ActiveTab.Code, username = SignalStore.account().username!!, - usernameLinkState = SignalStore.account().usernameLink?.let { UsernameLinkState.Present(UsernameUtil.generateLink(it)) } ?: UsernameLinkState.NotSet, + usernameLinkState = SignalStore.account().usernameLink?.let { UsernameLinkState.Present(it.toLink()) } ?: UsernameLinkState.NotSet, qrCodeState = QrCodeState.Loading, qrCodeColorScheme = SignalStore.misc().usernameQrCodeColorScheme ) @@ -61,7 +61,7 @@ class UsernameLinkSettingsViewModel : ViewModel() { init { disposable += usernameLink .observeOn(Schedulers.io()) - .map { link -> link.map { UsernameUtil.generateLink(it) } } + .map { link -> link.map { it.toLink() } } .flatMapSingle { generateQrCodeData(it) } .observeOn(AndroidSchedulers.mainThread()) .subscribe { qrData -> @@ -122,7 +122,7 @@ class UsernameLinkSettingsViewModel : ViewModel() { _state.value = _state.value.copy( usernameLinkState = if (components.isPresent) { - val link = UsernameUtil.generateLink(components.get()) + val link = components.get().toLink() UsernameLinkState.Present(link) } else { UsernameLinkState.NotSet @@ -152,7 +152,7 @@ class UsernameLinkSettingsViewModel : ViewModel() { indeterminateProgress = true ) - disposable += UsernameRepository.convertLinkToUsernameAndAci(url) + disposable += UsernameRepository.fetchUsernameAndAciFromLink(url) .map { result -> when (result) { is UsernameRepository.UsernameLinkConversionResult.Success -> QrScanResult.Success(Recipient.externalUsername(result.aci, result.username.toString())) diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt index 422afb6bfb..52cfcfafe1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt @@ -23,10 +23,14 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager import org.whispersystems.signalservice.api.push.ServiceId.ACI import org.whispersystems.signalservice.api.push.UsernameLinkComponents import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException +import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotAssociatedWithAnAccountException import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotReservedException import org.whispersystems.signalservice.api.push.exceptions.UsernameMalformedException import org.whispersystems.signalservice.api.push.exceptions.UsernameTakenException +import org.whispersystems.signalservice.api.util.UuidUtil +import org.whispersystems.signalservice.api.util.toByteArray import java.io.IOException +import java.util.UUID /** * Performs various actions around usernames and username links. @@ -77,6 +81,10 @@ import java.io.IOException object UsernameRepository { private val TAG = Log.tag(UsernameRepository::class.java) + private val URL_REGEX = """(https://)?signal.me/?#eu/([a-zA-Z0-9+\-_/]+)""".toRegex() + + val BASE_URL = "https://signal.me/#eu/" + private val accountManager: SignalServiceAccountManager get() = ApplicationDependencies.getSignalServiceAccountManager() /** @@ -163,8 +171,9 @@ object UsernameRepository { /** * Given a full username link, this will do the necessary parsing and network lookups to resolve it to a (username, ACI) pair. */ - fun convertLinkToUsernameAndAci(url: String): Single { - val components: UsernameLinkComponents = UsernameUtil.parseLink(url) ?: return Single.just(UsernameLinkConversionResult.Invalid) + @JvmStatic + fun fetchUsernameAndAciFromLink(url: String): Single { + val components: UsernameLinkComponents = parseLink(url) ?: return Single.just(UsernameLinkConversionResult.Invalid) return Single .fromCallable { @@ -176,7 +185,7 @@ object UsernameRepository { username = Username.fromLink(link) - val aci = accountManager.getAciByUsernameHash(UsernameUtil.hashUsernameToBase64(username.toString())) + val aci = accountManager.getAciByUsername(username) UsernameLinkConversionResult.Success(username, aci) } catch (e: IOException) { @@ -199,6 +208,49 @@ object UsernameRepository { .subscribeOn(Schedulers.io()) } + @JvmStatic + fun fetchAciForUsername(username: String): Single { + return Single.fromCallable { + try { + val aci: ACI = ApplicationDependencies.getSignalServiceAccountManager().getAciByUsername(Username(username)) + UsernameAciFetchResult.Success(aci) + } catch (e: UsernameIsNotAssociatedWithAnAccountException) { + Log.w(TAG, "[fetchAciFromUsername] Failed to get ACI for username hash", e) + UsernameAciFetchResult.NotFound + } catch (e: IOException) { + Log.w(TAG, "[fetchAciFromUsername] Hit network error while trying to resolve ACI from username", e) + UsernameAciFetchResult.NetworkError + } + } + } + + /** + * Parses out the [UsernameLinkComponents] from a link if possible, otherwise null. + * You need to make a separate network request to convert these components into a username. + */ + @JvmStatic + fun parseLink(url: String): UsernameLinkComponents? { + val match: MatchResult = URL_REGEX.find(url) ?: return null + val path: String = match.groups[2]?.value ?: return null + val allBytes: ByteArray = Base64.decode(path) + + if (allBytes.size != 48) { + return null + } + + val entropy: ByteArray = allBytes.slice(0 until 32).toByteArray() + val serverId: ByteArray = allBytes.slice(32 until allBytes.size).toByteArray() + val serverIdUuid: UUID = UuidUtil.parseOrNull(serverId) ?: return null + + return UsernameLinkComponents(entropy = entropy, serverId = serverIdUuid) + } + + fun UsernameLinkComponents.toLink(): String { + val combined: ByteArray = this.entropy + this.serverId.toByteArray() + val base64 = Base64.encodeUrlSafeWithoutPadding(combined) + return BASE_URL + base64 + } + @WorkerThread private fun reserveUsernameInternal(nickname: String): Result { return try { @@ -320,4 +372,10 @@ object UsernameRepository { /** No user exists for the given link. */ data class NotFound(val username: Username?) : UsernameLinkConversionResult() } + + sealed class UsernameAciFetchResult { + class Success(val aci: ACI) : UsernameAciFetchResult() + object NotFound : UsernameAciFetchResult() + object NetworkError : UsernameAciFetchResult() + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java index 2c1b2d7bd6..dcbdea56f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java @@ -22,6 +22,7 @@ import androidx.fragment.app.FragmentActivity; import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import org.signal.core.util.concurrent.RxExtensions; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SimpleTask; import org.signal.core.util.logging.Log; @@ -42,6 +43,9 @@ import org.thoughtcrime.securesms.groups.ui.invitesandrequests.joining.GroupJoin import org.thoughtcrime.securesms.groups.ui.invitesandrequests.joining.GroupJoinUpdateRequiredBottomSheetDialogFragment; import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl; import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.profiles.manage.UsernameRepository; +import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.UsernameAciFetchResult; +import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.UsernameLinkConversionResult; import org.thoughtcrime.securesms.proxy.ProxyBottomSheetFragment; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId; @@ -296,15 +300,12 @@ public class CommunicationActions { */ public static void handlePotentialSignalMeUrl(@NonNull FragmentActivity activity, @NonNull String potentialUrl) { String e164 = SignalMeUtil.parseE164FromLink(activity, potentialUrl); - UsernameLinkComponents username = SignalMeUtil.parseUsernameComponentsFromLink(potentialUrl); + UsernameLinkComponents username = UsernameRepository.parseLink(potentialUrl); if (e164 != null) { handleE164Link(activity, e164); } else if (username != null) { - handleUsernameLink(activity, username); - } - - if (e164 != null || username != null) { + handleUsernameLink(activity, potentialUrl); } } @@ -460,25 +461,21 @@ public class CommunicationActions { }); } - private static void handleUsernameLink(Activity activity, UsernameLinkComponents link) { + private static void handleUsernameLink(Activity activity, String link) { SimpleProgressDialog.DismissibleDialog dialog = SimpleProgressDialog.showDelayed(activity, 500, 500); SimpleTask.run(() -> { try { - byte[] encryptedUsername = ApplicationDependencies.getSignalServiceAccountManager().getEncryptedUsernameFromLinkServerId(link.getServerId()); - Username username = Username.fromLink(new Username.UsernameLink(link.getEntropy(), encryptedUsername)); - Optional serviceId = UsernameUtil.fetchAciForUsername(username.getUsername()); + UsernameLinkConversionResult result = RxExtensions.safeBlockingGet(UsernameRepository.fetchUsernameAndAciFromLink(link)); - if (serviceId.isPresent()) { - return Recipient.externalUsername(serviceId.get(), username.getUsername()); + // TODO we could be better here and report different types of errors to the UI + if (result instanceof UsernameLinkConversionResult.Success success) { + return Recipient.externalUsername(success.getAci(), success.getUsername().getUsername()); } else { return null; } - } catch (IOException e) { - Log.w(TAG, "Failed to fetch encrypted username", e); - return null; - } catch (BaseUsernameException e) { - Log.w(TAG, "Invalid username", e); + } catch (InterruptedException e) { + Log.w(TAG, "Interrupted?", e); return null; } }, recipient -> { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SignalMeUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/SignalMeUtil.kt index f774cfb200..53f3f6d6d4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SignalMeUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SignalMeUtil.kt @@ -2,17 +2,11 @@ package org.thoughtcrime.securesms.util import android.content.Context import com.google.i18n.phonenumbers.PhoneNumberUtil -import org.signal.core.util.Base64 import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter -import org.whispersystems.signalservice.api.push.UsernameLinkComponents -import org.whispersystems.signalservice.api.util.UuidUtil -import java.io.IOException -import java.io.UnsupportedEncodingException import java.util.Locale internal object SignalMeUtil { private val E164_REGEX = """^(https|sgnl)://signal\.me/#p/(\+[0-9]+)$""".toRegex() - private val USERNAME_REGEX = """^(https|sgnl)://signal\.me/#eu/(.+)$""".toRegex() /** * If this is a valid signal.me link and has a valid e164, it will return the e164. Otherwise, it will return null. @@ -33,31 +27,4 @@ internal object SignalMeUtil { } } } - - /** - * If this is a valid signal.me link and has valid username link components, it will return those components. Otherwise, it will return null. - */ - @JvmStatic - fun parseUsernameComponentsFromLink(link: String?): UsernameLinkComponents? { - if (link.isNullOrBlank()) { - return null - } - - return USERNAME_REGEX.find(link)?.let { match -> - val usernameLinkBase64: String = match.groups[2]?.value ?: return@let null - - try { - val usernameLinkData: ByteArray = Base64.decode(usernameLinkBase64).takeIf { it.size == 48 } ?: return@let null - val entropy: ByteArray = usernameLinkData.sliceArray(0 until 32) - val uuidBytes: ByteArray = usernameLinkData.sliceArray(32 until usernameLinkData.size) - val uuid = UuidUtil.parseOrNull(uuidBytes) - - UsernameLinkComponents(entropy, uuid) - } catch (e: UnsupportedEncodingException) { - null - } catch (e: IOException) { - null - } - } - } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/UsernameUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/UsernameUtil.kt index 094bf9fb77..6defc731c5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/UsernameUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/UsernameUtil.kt @@ -1,21 +1,7 @@ package org.thoughtcrime.securesms.util -import androidx.annotation.WorkerThread -import org.signal.core.util.Base64 import org.signal.core.util.logging.Log -import org.signal.libsignal.usernames.BaseUsernameException -import org.signal.libsignal.usernames.Username -import org.thoughtcrime.securesms.database.SignalDatabase.Companion.recipients -import org.thoughtcrime.securesms.dependencies.ApplicationDependencies -import org.thoughtcrime.securesms.recipients.Recipient -import org.whispersystems.signalservice.api.push.ServiceId -import org.whispersystems.signalservice.api.push.UsernameLinkComponents -import org.whispersystems.signalservice.api.util.UuidUtil -import org.whispersystems.signalservice.api.util.toByteArray -import java.io.IOException import java.util.Locale -import java.util.Optional -import java.util.UUID import java.util.regex.Pattern object UsernameUtil { @@ -24,24 +10,29 @@ object UsernameUtil { const val MAX_LENGTH = 32 private val FULL_PATTERN = Pattern.compile(String.format(Locale.US, "^[a-zA-Z_][a-zA-Z0-9_]{%d,%d}$", MIN_LENGTH - 1, MAX_LENGTH - 1), Pattern.CASE_INSENSITIVE) private val DIGIT_START_PATTERN = Pattern.compile("^[0-9].*$") - private val URL_PATTERN = """(https://)?signal.me/?#eu/([a-zA-Z0-9+\-_/]+)""".toRegex() private const val BASE_URL_SCHEMELESS = "signal.me/#eu/" private const val BASE_URL = "https://$BASE_URL_SCHEMELESS" private val SEARCH_PATTERN = Pattern.compile( String.format( Locale.US, - "^[a-zA-Z_][a-zA-Z0-9_]{%d,%d}(.[0-9]+)?$", + "^@?[a-zA-Z_][a-zA-Z0-9_]{%d,%d}(.[0-9]+)?$", MIN_LENGTH - 1, MAX_LENGTH - 1, Pattern.CASE_INSENSITIVE ) ) + @JvmStatic fun isValidUsernameForSearch(value: String): Boolean { return value.isNotEmpty() && SEARCH_PATTERN.matcher(value).matches() } + @JvmStatic + fun sanitizeUsernameFromSearch(value: String): String { + return value.replace("[^a-zA-Z0-9_.]".toRegex(), "") + } + @JvmStatic fun checkUsername(value: String?): InvalidReason? { return when { @@ -66,81 +57,6 @@ object UsernameUtil { } } - @JvmStatic - @WorkerThread - fun fetchAciForUsername(username: String): Optional { - val localId = recipients.getByUsername(username) - - if (localId.isPresent) { - val recipient = Recipient.resolved(localId.get()) - if (recipient.serviceId.isPresent) { - Log.i(TAG, "Found username locally -- using associated UUID.") - return recipient.serviceId - } else { - Log.w(TAG, "Found username locally, but it had no associated UUID! Clearing it.") - recipients.clearUsernameIfExists(username) - } - } - - Log.d(TAG, "No local user with this username. Searching remotely.") - - return try { - fetchAciForUsernameHash(Base64.encodeUrlSafeWithoutPadding(Username(username).hash)) - } catch (e: BaseUsernameException) { - Optional.empty() - } - } - - /** - * Hashes a username to a url-safe base64 string. - * @throws BaseUsernameException If the username is invalid and un-hashable. - */ - @Throws(BaseUsernameException::class) - fun hashUsernameToBase64(username: String?): String { - return Base64.encodeUrlSafeWithoutPadding(Username.hash(username)) - } - - @JvmStatic - @WorkerThread - fun fetchAciForUsernameHash(base64UrlSafeEncodedUsernameHash: String): Optional { - return try { - val aci = ApplicationDependencies.getSignalServiceAccountManager().getAciByUsernameHash(base64UrlSafeEncodedUsernameHash) - Optional.ofNullable(aci) - } catch (e: IOException) { - Log.w(TAG, "Failed to get ACI for username hash", e) - Optional.empty() - } - } - - /** - * Generates a username link from the provided [UsernameLinkComponents]. - */ - fun generateLink(components: UsernameLinkComponents): String { - val combined: ByteArray = components.entropy + components.serverId.toByteArray() - val base64 = Base64.encodeUrlSafeWithoutPadding(combined) - return BASE_URL + base64 - } - - /** - * Parses out the [UsernameLinkComponents] from a link if possible, otherwise null. - * You need to make a separate network request to convert these components into a username. - */ - fun parseLink(url: String): UsernameLinkComponents? { - val match: MatchResult = URL_PATTERN.find(url) ?: return null - val path: String = match.groups[2]?.value ?: return null - val allBytes: ByteArray = Base64.decode(path) - - if (allBytes.size != 48) { - return null - } - - val entropy: ByteArray = allBytes.slice(0 until 32).toByteArray() - val serverId: ByteArray = allBytes.slice(32 until allBytes.size).toByteArray() - val serverIdUuid: UUID = UuidUtil.parseOrNull(serverId) ?: return null - - return UsernameLinkComponents(entropy = entropy, serverId = serverIdUuid) - } - enum class InvalidReason { TOO_SHORT, TOO_LONG, diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java index ad761c1dc8..c546a13311 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java @@ -84,7 +84,6 @@ import org.whispersystems.signalservice.internal.websocket.DefaultResponseMapper import org.signal.core.util.Base64; import java.io.IOException; -import java.security.KeyStore; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -761,8 +760,8 @@ public class SignalServiceAccountManager { } } - public ACI getAciByUsernameHash(String usernameHash) throws IOException { - return this.pushServiceSocket.getAciByUsernameHash(usernameHash); + public ACI getAciByUsername(Username username) throws IOException { + return this.pushServiceSocket.getAciByUsernameHash(Base64.encodeUrlSafeWithoutPadding(username.getHash())); } public ReserveUsernameResponse reserveUsername(List usernameHashes) throws IOException { 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 46ede03a3a..14eff3ba96 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 @@ -1068,7 +1068,7 @@ public class PushServiceSocket { byte[] randomness = new byte[32]; random.nextBytes(randomness); - byte[] proof = Username.generateProof(username, randomness); + byte[] proof = new Username(username).generateProofWithRandomness(randomness); ConfirmUsernameRequest confirmUsernameRequest = new ConfirmUsernameRequest(reserveUsernameResponse.getUsernameHash(), Base64.encodeUrlSafeWithoutPadding(proof));