From c012ead143941b98a3559db65643b67e2950ac85 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 28 Jul 2023 13:05:21 -0400 Subject: [PATCH] Validate ServiceIds on envelopes. --- .../securesms/messages/MessageDecryptor.kt | 4 +- .../api/messages/EnvelopeContentValidator.kt | 85 ++++++++++++------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt index f694922f9c..9a4c08cce2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt @@ -85,8 +85,8 @@ object MessageDecryptor { envelope: Envelope, serverDeliveredTimestamp: Long ): Result { - val selfAci: ServiceId = SignalStore.account().requireAci() - val selfPni: ServiceId = SignalStore.account().requirePni() + val selfAci: ACI = SignalStore.account().requireAci() + val selfPni: PNI = SignalStore.account().requirePni() val destination: ServiceId = envelope.getDestination(selfAci, selfPni) diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt index d67a662e08..3726940336 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt @@ -5,7 +5,8 @@ import org.signal.libsignal.zkgroup.InvalidInputException import org.signal.libsignal.zkgroup.groups.GroupMasterKey import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation import org.whispersystems.signalservice.api.InvalidMessageStructureException -import org.whispersystems.signalservice.api.util.UuidUtil +import org.whispersystems.signalservice.api.push.ServiceId +import org.whispersystems.signalservice.api.push.ServiceId.ACI import org.whispersystems.signalservice.internal.push.SignalServiceProtos import org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content @@ -34,6 +35,12 @@ object EnvelopeContentValidator { } } + if (envelope.hasSourceServiceId() && envelope.sourceServiceId.isInvalidServiceId()) { + return Result.Invalid("Envelope had an invalid sourceServiceId!") + } + + // Reminder: envelope.destinationServiceId was already validated since we need that for decryption + return when { envelope.story && !content.meetsStoryFlagCriteria() -> Result.Invalid("Envelope was flagged as a story, but it did not have any story-related content!") content.hasDataMessage() -> validateDataMessage(envelope, content.dataMessage) @@ -67,8 +74,12 @@ object EnvelopeContentValidator { Result.Invalid("[DataMessage] Timestamps don't match! envelope: ${envelope.timestamp}, content: ${dataMessage.timestamp}") } - if (dataMessage.hasQuote() && dataMessage.quote.authorAci.isNullOrInvalidOrUnknownUuid()) { - return Result.Invalid("[DataMessage] Invalid UUID on quote!") + if (dataMessage.hasQuote() && dataMessage.quote.authorAci.isNullOrInvalidAci()) { + return Result.Invalid("[DataMessage] Invalid ACI on quote!") + } + + if (dataMessage.contactList.any { it.hasAvatar() && it.avatar.avatar.isPresentAndInvalid() }) { + return Result.Invalid("[DataMessage] Invalid AttachmentPointer on DataMessage.contactList.avatar!") } if (dataMessage.contactList.any { it.hasAvatar() && it.avatar.avatar.isPresentAndInvalid() }) { @@ -79,8 +90,8 @@ object EnvelopeContentValidator { return Result.Invalid("[DataMessage] Invalid AttachmentPointer on DataMessage.previewList.image!") } - if (dataMessage.bodyRangesList.any { it.hasMentionAci() && it.mentionAci.isNullOrInvalidOrUnknownUuid() }) { - return Result.Invalid("[DataMessage] Invalid UUID on body range!") + if (dataMessage.bodyRangesList.any { it.hasMentionAci() && it.mentionAci.isNullOrInvalidAci() }) { + return Result.Invalid("[DataMessage] Invalid ACI on body range!") } if (dataMessage.hasSticker() && dataMessage.sticker.data.isNullOrInvalid()) { @@ -91,8 +102,8 @@ object EnvelopeContentValidator { if (!dataMessage.reaction.hasTargetSentTimestamp()) { return Result.Invalid("[DataMessage] Missing timestamp on DataMessage.reaction!") } - if (dataMessage.reaction.targetAuthorAci.isNullOrInvalidOrUnknownUuid()) { - return Result.Invalid("[DataMessage] Invalid UUID on DataMessage.reaction!") + if (dataMessage.reaction.targetAuthorAci.isNullOrInvalidAci()) { + return Result.Invalid("[DataMessage] Invalid ACI on DataMessage.reaction!") } } @@ -100,8 +111,8 @@ object EnvelopeContentValidator { return Result.Invalid("[DataMessage] Missing timestamp on DataMessage.delete!") } - if (dataMessage.hasStoryContext() && dataMessage.storyContext.authorAci.isNullOrInvalidOrUnknownUuid()) { - return Result.Invalid("[DataMessage] Invalid UUID on DataMessage.storyContext!") + if (dataMessage.hasStoryContext() && dataMessage.storyContext.authorAci.isNullOrInvalidAci()) { + return Result.Invalid("[DataMessage] Invalid ACI on DataMessage.storyContext!") } if (dataMessage.hasGiftBadge()) { @@ -130,7 +141,7 @@ object EnvelopeContentValidator { private fun validateSyncMessage(envelope: Envelope, syncMessage: SyncMessage): Result { if (syncMessage.hasSent()) { - val validAddress = syncMessage.sent.destinationServiceId.isValidUuid() + val validAddress = syncMessage.sent.destinationServiceId.isValidServiceId() val hasDataGroup = syncMessage.sent.message?.hasGroupV2() ?: false val hasStoryGroup = syncMessage.sent.storyMessage?.hasGroup() ?: false val hasStoryManifest = syncMessage.sent.storyMessageRecipientsList.isNotEmpty() @@ -153,8 +164,8 @@ object EnvelopeContentValidator { } for (status in syncMessage.sent.unidentifiedStatusList) { - if (status.destinationServiceId.isNullOrInvalidUuid()) { - return Result.Invalid("[SyncMessage] Invalid UUID in SyncMessage.sent.unidentifiedStatusList!") + if (status.destinationServiceId.isNullOrInvalidServiceId()) { + return Result.Invalid("[SyncMessage] Invalid ServiceId in SyncMessage.sent.unidentifiedStatusList!") } } @@ -171,32 +182,36 @@ object EnvelopeContentValidator { } } - if (syncMessage.readList.any { it.senderAci.isNullOrInvalidOrUnknownUuid() }) { - return Result.Invalid("[SyncMessage] Invalid UUID in SyncMessage.readList!") + if (syncMessage.readList.any { it.senderAci.isNullOrInvalidAci() }) { + return Result.Invalid("[SyncMessage] Invalid ACI in SyncMessage.readList!") } - if (syncMessage.viewedList.any { it.senderAci.isNullOrInvalidOrUnknownUuid() }) { - return Result.Invalid("[SyncMessage] Invalid UUID in SyncMessage.viewList!") + if (syncMessage.viewedList.any { it.senderAci.isNullOrInvalidAci() }) { + return Result.Invalid("[SyncMessage] Invalid ACI in SyncMessage.viewList!") } - if (syncMessage.hasViewOnceOpen() && syncMessage.viewOnceOpen.senderAci.isNullOrInvalidOrUnknownUuid()) { - return Result.Invalid("[SyncMessage] Invalid UUID in SyncMessage.viewOnceOpen!") + if (syncMessage.hasViewOnceOpen() && syncMessage.viewOnceOpen.senderAci.isNullOrInvalidAci()) { + return Result.Invalid("[SyncMessage] Invalid ACI in SyncMessage.viewOnceOpen!") } - if (syncMessage.hasVerified() && syncMessage.verified.destinationAci.isNullOrInvalidOrUnknownUuid()) { - return Result.Invalid("[SyncMessage] Invalid UUID in SyncMessage.verified!") + if (syncMessage.hasVerified() && syncMessage.verified.destinationAci.isNullOrInvalidAci()) { + return Result.Invalid("[SyncMessage] Invalid ACI in SyncMessage.verified!") } if (syncMessage.stickerPackOperationList.any { !it.hasPackId() }) { return Result.Invalid("[SyncMessage] Missing packId in stickerPackOperationList!") } - if (syncMessage.hasBlocked() && syncMessage.blocked.acisList.any { it.isNullOrInvalidOrUnknownUuid() }) { - return Result.Invalid("[SyncMessage] Invalid UUID in SyncMessage.blocked!") + if (syncMessage.hasBlocked() && syncMessage.blocked.acisList.any { it.isNullOrInvalidAci() }) { + return Result.Invalid("[SyncMessage] Invalid ACI in SyncMessage.blocked!") } - if (syncMessage.hasMessageRequestResponse() && !syncMessage.messageRequestResponse.hasGroupId() && syncMessage.messageRequestResponse.threadAci.isNullOrInvalidOrUnknownUuid()) { - return Result.Invalid("[SyncMessage] Invalid UUID in SyncMessage.messageRequestResponse!") + if (syncMessage.hasMessageRequestResponse() && !syncMessage.messageRequestResponse.hasGroupId() && syncMessage.messageRequestResponse.threadAci.isNullOrInvalidAci()) { + return Result.Invalid("[SyncMessage] Invalid ACI in SyncMessage.messageRequestResponse!") + } + + if (syncMessage.hasOutgoingPayment() && syncMessage.outgoingPayment.recipientServiceId.isNullOrInvalidServiceId()) { + return Result.Invalid("[SyncMessage] Invalid ServiceId in SyncMessage.outgoingPayment!") } return Result.Valid @@ -261,7 +276,7 @@ object EnvelopeContentValidator { return Result.Invalid("[EditMessage] Invalid AttachmentPointer on DataMessage.previewList.image!") } - if (dataMessage.bodyRangesList.any { it.hasMentionAci() && it.mentionAci.isNullOrInvalidOrUnknownUuid() }) { + if (dataMessage.bodyRangesList.any { it.hasMentionAci() && it.mentionAci.isNullOrInvalidAci() }) { return Result.Invalid("[EditMessage] Invalid UUID on body range!") } @@ -284,16 +299,24 @@ object EnvelopeContentValidator { return this != null && this.attachmentIdentifierCase == AttachmentPointer.AttachmentIdentifierCase.ATTACHMENTIDENTIFIER_NOT_SET } - private fun String?.isValidUuid(): Boolean { - return UuidUtil.isUuid(this) + private fun String?.isValidServiceId(): Boolean { + val parsed = ServiceId.parseOrNull(this) + return parsed != null && parsed.isValid } - private fun String?.isNullOrInvalidOrUnknownUuid(): Boolean { - return !UuidUtil.isUuid(this) || this == UuidUtil.UNKNOWN_UUID_STRING + private fun String?.isNullOrInvalidServiceId(): Boolean { + val parsed = ServiceId.parseOrNull(this) + return parsed == null || parsed.isUnknown } - private fun String?.isNullOrInvalidUuid(): Boolean { - return !UuidUtil.isUuid(this) + private fun String.isInvalidServiceId(): Boolean { + val parsed = ServiceId.parseOrNull(this) + return parsed == null || parsed.isUnknown + } + + private fun String?.isNullOrInvalidAci(): Boolean { + val parsed = ACI.parseOrNull(this) + return parsed == null || parsed.isUnknown } private fun Content?.meetsStoryFlagCriteria(): Boolean {