diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt index 91f917d4ba..513022f14c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt @@ -594,7 +594,15 @@ class IncomingMessageObserver( val committed = SignalDatabase.tryRunInTransaction { for (response in batch) { SignalTrace.beginSection("IncomingMessageObserver#perMessageTransaction") - val result = processEnvelope(bufferedStore, response.envelope, response.serverDeliveredTimestamp, batchCache) + val result = when (response) { + is EnvelopeResponse.Parsed -> { + processEnvelope(bufferedStore, response.envelope, response.serverDeliveredTimestamp, batchCache) + } + is EnvelopeResponse.Unparseable -> { + Log.w(TAG, "Unparseable envelope. Nothing to process, but we'll still ack it.") + null + } + } bufferedStore.flushToDisk() SignalTrace.endSection() @@ -643,10 +651,16 @@ class IncomingMessageObserver( for ((index, response) in batch.withIndex()) { SignalTrace.beginSection("IncomingMessageObserver#perMessageTransaction") - val results = SignalDatabase.runInTransaction { - val result = processEnvelope(bufferedStore, response.envelope, response.serverDeliveredTimestamp, batchCache) - bufferedStore.flushToDisk() - result + val results = when (response) { + is EnvelopeResponse.Parsed -> SignalDatabase.runInTransaction { + val result = processEnvelope(bufferedStore, response.envelope, response.serverDeliveredTimestamp, batchCache) + bufferedStore.flushToDisk() + result + } + is EnvelopeResponse.Unparseable -> { + Log.w(TAG, "Unparseable envelope. Nothing to process, but we'll still ack it.") + null + } } SignalTrace.endSection() 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 8d0ba437f2..cdd7bdadc4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt @@ -153,148 +153,166 @@ object MessageDecryptor { val localAddress = SignalServiceAddress(destination, SignalStore.account.e164) val cipher = SignalServiceCipher(localAddress, SignalStore.account.deviceId, bufferedStore, ReentrantSessionLock.INSTANCE, SealedSenderAccessUtil.getCertificateValidator()) - return try { - val startTimeNanos = System.nanoTime() - SignalTrace.beginSection("MessageDecryptor#cipherDecrypt") - val cipherResult: SignalServiceCipherResult? = cipher.decrypt(envelope, serverDeliveredTimestamp) + val startTimeNanos = System.nanoTime() + SignalTrace.beginSection("MessageDecryptor#cipherDecrypt") + + val cipherResult: SignalServiceCipherResult? = try { + cipher.decrypt(envelope, serverDeliveredTimestamp) + } catch (e: Exception) { + return buildResultForDecryptionFailure(context, envelope, serverDeliveredTimestamp, followUpOperations, e) + } finally { SignalTrace.endSection() - val endTimeNanos = System.nanoTime() + } - val hadSealedSenderSource = Util.allAreNull(envelope.sourceServiceId, envelope.sourceServiceIdBinary) + val endTimeNanos = System.nanoTime() - val envelope = if (cipherResult?.metadata?.sourceServiceId != null) { - envelope.newBuilder() - .sourceServiceIdBinary(cipherResult.metadata.sourceServiceId.toByteString()) - .sourceDeviceId(cipherResult.metadata.sourceDeviceId) - .build() + val hadSealedSenderSource = Util.allAreNull(envelope.sourceServiceId, envelope.sourceServiceIdBinary) + + @Suppress("NAME_SHADOWING") + val envelope = if (cipherResult?.metadata?.sourceServiceId != null) { + envelope.newBuilder() + .sourceServiceIdBinary(cipherResult.metadata.sourceServiceId.toByteString()) + .sourceDeviceId(cipherResult.metadata.sourceDeviceId) + .build() + } else { + envelope + } + + if (cipherResult == null) { + Log.w(TAG, "${logPrefix(envelope)} Decryption resulted in a null result!", true) + return Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) + } + + if (cipherResult.metadata.sourceServiceId is PNI && hadSealedSenderSource) { + Log.w(TAG, "${logPrefix(envelope)} Invalid message! Sealed sender used for a PNI.") + return Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) + } + + Log.d(TAG, "${logPrefix(envelope, cipherResult)} Successfully decrypted the envelope in ${(endTimeNanos - startTimeNanos).nanoseconds.toDouble(DurationUnit.MILLISECONDS).roundedString(2)} ms (GUID ${UuidUtil.getStringUUID(envelope.serverGuid, envelope.serverGuidBinary)}). Delivery latency: ${serverDeliveredTimestamp - envelope.serverTimestamp!!} ms, Urgent: ${envelope.urgent}") + + val validationResult: EnvelopeContentValidator.Result = EnvelopeContentValidator.validate(envelope, cipherResult.content, SignalStore.account.aci!!, cipherResult.metadata.ciphertextMessageType) + + if (validationResult is EnvelopeContentValidator.Result.Invalid) { + Log.w(TAG, "${logPrefix(envelope, cipherResult)} Invalid content! ${validationResult.reason}", validationResult.throwable) + + if (RemoteConfig.internalUser) { + postInvalidMessageNotification(context, validationResult.reason) + } + + return Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) + } + + if (validationResult is EnvelopeContentValidator.Result.UnsupportedDataMessage) { + Log.w(TAG, "${logPrefix(envelope, cipherResult)} Unsupported DataMessage! Our version: ${validationResult.ourVersion}, their version: ${validationResult.theirVersion}") + return Result.UnsupportedDataMessage(envelope, serverDeliveredTimestamp, cipherResult.toErrorMetadata(), followUpOperations.toUnmodifiableList()) + } + + // Must handle SKDM's immediately, because subsequent decryptions could rely on it + if (cipherResult.content.senderKeyDistributionMessage != null) { + handleSenderKeyDistributionMessage( + envelope, + cipherResult.metadata.sourceServiceId, + cipherResult.metadata.sourceDeviceId, + SenderKeyDistributionMessage(cipherResult.content.senderKeyDistributionMessage!!.toByteArray()), + bufferedProtocolStore.getAciStore() + ) + } + + if (cipherResult.content.pniSignatureMessage != null) { + if (cipherResult.metadata.sourceServiceId is ACI) { + handlePniSignatureMessage( + envelope, + bufferedProtocolStore, + cipherResult.metadata.sourceServiceId as ACI, + cipherResult.metadata.sourceE164, + cipherResult.metadata.sourceDeviceId, + cipherResult.content.pniSignatureMessage!! + ) } else { - envelope + Log.w(TAG, "${logPrefix(envelope)} Ignoring PNI signature because the sourceServiceId isn't an ACI!") } + } else if (cipherResult.content.pniSignatureMessage != null) { + Log.w(TAG, "${logPrefix(envelope)} Ignoring PNI signature because the feature flag is disabled!") + } - if (cipherResult == null) { - Log.w(TAG, "${logPrefix(envelope)} Decryption resulted in a null result!", true) - return Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) - } + // TODO We can move this to the "message processing" stage once we give it access to the envelope. But for now it'll stay here. + if (envelope.report_spam_token != null && envelope.report_spam_token!!.size > 0) { + val sender = RecipientId.from(cipherResult.metadata.sourceServiceId) + SignalDatabase.recipients.setReportingToken(sender, envelope.report_spam_token!!.toByteArray()) + } - if (cipherResult.metadata.sourceServiceId is PNI && hadSealedSenderSource) { - Log.w(TAG, "${logPrefix(envelope)} Invalid message! Sealed sender used for a PNI.") - return Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) - } + return Result.Success(envelope, serverDeliveredTimestamp, cipherResult.content, cipherResult.metadata, followUpOperations.toUnmodifiableList()) + } - Log.d(TAG, "${logPrefix(envelope, cipherResult)} Successfully decrypted the envelope in ${(endTimeNanos - startTimeNanos).nanoseconds.toDouble(DurationUnit.MILLISECONDS).roundedString(2)} ms (GUID ${UuidUtil.getStringUUID(envelope.serverGuid, envelope.serverGuidBinary)}). Delivery latency: ${serverDeliveredTimestamp - envelope.serverTimestamp!!} ms, Urgent: ${envelope.urgent}") - - val validationResult: EnvelopeContentValidator.Result = EnvelopeContentValidator.validate(envelope, cipherResult.content, SignalStore.account.aci!!, cipherResult.metadata.ciphertextMessageType) - - if (validationResult is EnvelopeContentValidator.Result.Invalid) { - Log.w(TAG, "${logPrefix(envelope, cipherResult)} Invalid content! ${validationResult.reason}", validationResult.throwable) + private fun buildResultForDecryptionFailure( + context: Context, + envelope: Envelope, + serverDeliveredTimestamp: Long, + followUpOperations: MutableList, + e: Exception + ): Result { + return when (e) { + is ProtocolInvalidKeyIdException, + is ProtocolInvalidKeyException, + is ProtocolUntrustedIdentityException, + is ProtocolNoSessionException, + is ProtocolInvalidMessageException -> { + Log.w(TAG, "${logPrefix(envelope, e)} Decryption error!", e, true) if (RemoteConfig.internalUser) { - postInvalidMessageNotification(context, validationResult.reason) + postDecryptionErrorNotification(context) } - return Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) - } - - if (validationResult is EnvelopeContentValidator.Result.UnsupportedDataMessage) { - Log.w(TAG, "${logPrefix(envelope, cipherResult)} Unsupported DataMessage! Our version: ${validationResult.ourVersion}, their version: ${validationResult.theirVersion}") - return Result.UnsupportedDataMessage(envelope, serverDeliveredTimestamp, cipherResult.toErrorMetadata(), followUpOperations.toUnmodifiableList()) - } - - // Must handle SKDM's immediately, because subsequent decryptions could rely on it - if (cipherResult.content.senderKeyDistributionMessage != null) { - handleSenderKeyDistributionMessage( - envelope, - cipherResult.metadata.sourceServiceId, - cipherResult.metadata.sourceDeviceId, - SenderKeyDistributionMessage(cipherResult.content.senderKeyDistributionMessage!!.toByteArray()), - bufferedProtocolStore.getAciStore() - ) - } - - if (cipherResult.content.pniSignatureMessage != null) { - if (cipherResult.metadata.sourceServiceId is ACI) { - handlePniSignatureMessage( - envelope, - bufferedProtocolStore, - cipherResult.metadata.sourceServiceId as ACI, - cipherResult.metadata.sourceE164, - cipherResult.metadata.sourceDeviceId, - cipherResult.content.pniSignatureMessage!! - ) + if (RemoteConfig.retryReceipts) { + buildResultForDecryptionError(context, envelope, serverDeliveredTimestamp, followUpOperations, e) } else { - Log.w(TAG, "${logPrefix(envelope)} Ignoring PNI signature because the sourceServiceId isn't an ACI!") - } - } else if (cipherResult.content.pniSignatureMessage != null) { - Log.w(TAG, "${logPrefix(envelope)} Ignoring PNI signature because the feature flag is disabled!") - } + Log.w(TAG, "${logPrefix(envelope, e)} Retry receipts disabled! Enqueuing a session reset job, which will also insert an error message.", e, true) - // TODO We can move this to the "message processing" stage once we give it access to the envelope. But for now it'll stay here. - if (envelope.report_spam_token != null && envelope.report_spam_token!!.size > 0) { - val sender = RecipientId.from(cipherResult.metadata.sourceServiceId) - SignalDatabase.recipients.setReportingToken(sender, envelope.report_spam_token!!.toByteArray()) - } - - Result.Success(envelope, serverDeliveredTimestamp, cipherResult.content, cipherResult.metadata, followUpOperations.toUnmodifiableList()) - } catch (e: Exception) { - when (e) { - is ProtocolInvalidKeyIdException, - is ProtocolInvalidKeyException, - is ProtocolUntrustedIdentityException, - is ProtocolNoSessionException, - is ProtocolInvalidMessageException -> { - check(e is ProtocolException) - Log.w(TAG, "${logPrefix(envelope, e)} Decryption error!", e, true) - - if (RemoteConfig.internalUser) { - postDecryptionErrorNotification(context) + followUpOperations += FollowUpOperation { + Recipient.external(e.sender)?.let { + AutomaticSessionResetJob(it.id, e.senderDevice, envelope.clientTimestamp!!).asChain() + } ?: null.logW(TAG, "${logPrefix(envelope, e)} Failed to create a recipient with the provided identifier!") } - if (RemoteConfig.retryReceipts) { - buildResultForDecryptionError(context, envelope, serverDeliveredTimestamp, followUpOperations, e) - } else { - Log.w(TAG, "${logPrefix(envelope, e)} Retry receipts disabled! Enqueuing a session reset job, which will also insert an error message.", e, true) - - followUpOperations += FollowUpOperation { - Recipient.external(e.sender)?.let { - AutomaticSessionResetJob(it.id, e.senderDevice, envelope.clientTimestamp!!).asChain() - } ?: null.logW(TAG, "${logPrefix(envelope, e)} Failed to create a recipient with the provided identifier!") - } - - Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) - } - } - - is ProtocolDuplicateMessageException -> { - Log.w(TAG, "${logPrefix(envelope, e)} Duplicate message!", e) Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) } + } - is InvalidMetadataVersionException, - is InvalidMetadataMessageException, - is InvalidMessageStructureException -> { - Log.w(TAG, "${logPrefix(envelope)} Invalid message structure!", e, true) - Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) + is ProtocolDuplicateMessageException -> { + Log.w(TAG, "${logPrefix(envelope, e)} Duplicate message!", e) + Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) + } + + is InvalidMetadataVersionException, + is InvalidMetadataMessageException, + is InvalidMessageStructureException -> { + Log.w(TAG, "${logPrefix(envelope)} Invalid message structure!", e, true) + Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) + } + + is SelfSendException -> { + Log.i(TAG, "[${envelope.clientTimestamp}] Dropping sealed sender message from self!", e) + Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) + } + + is ProtocolInvalidVersionException -> { + Log.w(TAG, "${logPrefix(envelope, e)} Invalid version!", e, true) + Result.InvalidVersion(envelope, serverDeliveredTimestamp, e.toErrorMetadata(), followUpOperations.toUnmodifiableList()) + } + + is ProtocolLegacyMessageException -> { + Log.w(TAG, "${logPrefix(envelope, e)} Legacy message!", e, true) + Result.LegacyMessage(envelope, serverDeliveredTimestamp, e.toErrorMetadata(), followUpOperations) + } + + else -> { + Log.w(TAG, "${logPrefix(envelope)} Encountered an unexpected exception! Dropping the envelope so we don't block the queue.", e, true) + + if (RemoteConfig.internalUser) { + postInvalidMessageNotification(context, "Unexpected exception: ${e.javaClass.simpleName}") } - is SelfSendException -> { - Log.i(TAG, "[${envelope.clientTimestamp}] Dropping sealed sender message from self!", e) - Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) - } - - is ProtocolInvalidVersionException -> { - Log.w(TAG, "${logPrefix(envelope, e)} Invalid version!", e, true) - Result.InvalidVersion(envelope, serverDeliveredTimestamp, e.toErrorMetadata(), followUpOperations.toUnmodifiableList()) - } - - is ProtocolLegacyMessageException -> { - Log.w(TAG, "${logPrefix(envelope, e)} Legacy message!", e, true) - Result.LegacyMessage(envelope, serverDeliveredTimestamp, e.toErrorMetadata(), followUpOperations) - } - - else -> { - Log.w(TAG, "Encountered an unexpected exception! Throwing!", e, true) - throw e - } + Result.Ignore(envelope, serverDeliveredTimestamp, followUpOperations.toUnmodifiableList()) } } } diff --git a/build.gradle.kts b/build.gradle.kts index b82220cbde..b5f5a4b8f1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,7 +31,7 @@ buildscript { classpath(libs.gradle) classpath(libs.androidx.navigation.safe.args.gradle.plugin) classpath(libs.protobuf.gradle.plugin) - classpath("com.squareup.wire:wire-gradle-plugin:6.4.0") { + classpath("com.squareup.wire:wire-gradle-plugin:6.4.5") { exclude(group = "com.squareup.wire", module = "wire-swift-generator") exclude(group = "com.squareup.wire", module = "wire-grpc-client") exclude(group = "com.squareup.wire", module = "wire-grpc-jvm") diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 61b3b2fa4e..de181d324b 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -5597,6 +5597,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -5605,6 +5613,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -5613,6 +5629,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -5621,6 +5645,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -5637,6 +5669,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -5653,11 +5693,24 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + + + + + + @@ -5666,6 +5719,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java index 854be1b6b3..a751705b6d 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java @@ -54,7 +54,6 @@ import org.whispersystems.signalservice.internal.push.Envelope; import org.whispersystems.signalservice.internal.push.OutgoingPushMessage; import org.whispersystems.signalservice.internal.push.PushTransportDetails; -import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; @@ -139,7 +138,7 @@ public class SignalServiceCipher { try { if (envelope.content != null) { Plaintext plaintext = decryptInternal(envelope, serverDeliveredTimestamp); - Content content = Content.ADAPTER.decode(plaintext.getData()); + Content content = decodeContent(plaintext.getData()); return new SignalServiceCipherResult( content, @@ -156,7 +155,15 @@ public class SignalServiceCipher { } else { return null; } - } catch (IOException | IllegalArgumentException e) { + } catch (IllegalArgumentException e) { + throw new InvalidMetadataMessageException(e); + } + } + + private static Content decodeContent(byte[] data) throws InvalidMetadataMessageException { + try { + return Content.ADAPTER.decode(data); + } catch (Exception e) { throw new InvalidMetadataMessageException(e); } } diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt index b6ec0c655b..e6ac74f998 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidator.kt @@ -217,8 +217,8 @@ object EnvelopeContentValidator { } private fun validateSyncMessage(envelope: Envelope, syncMessage: SyncMessage, localAci: ACI): Result { - // Source serviceId was already determined to be a valid serviceId in general - val sourceServiceId = ServiceId.parseOrThrow(envelope.sourceServiceId, envelope.sourceServiceIdBinary) + val sourceServiceId = ServiceId.parseOrNull(envelope.sourceServiceId, envelope.sourceServiceIdBinary) + ?: return Result.Invalid("[SyncMessage] Missing or invalid source ServiceId!") if (sourceServiceId != localAci) { return Result.Invalid("[SyncMessage] Source was not our own account!") diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeResponse.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeResponse.kt index 76ae3cd30c..71ac847dca 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeResponse.kt +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/messages/EnvelopeResponse.kt @@ -6,8 +6,19 @@ import org.whispersystems.signalservice.internal.push.Envelope /** * Represents an envelope off the wire, paired with the metadata needed to process it. */ -class EnvelopeResponse( - val envelope: Envelope, - val serverDeliveredTimestamp: Long, - val websocketRequest: WebSocketRequestMessage -) +sealed class EnvelopeResponse { + + abstract val websocketRequest: WebSocketRequestMessage + + /** An envelope we successfully parsed and can hand off for processing. */ + class Parsed( + val envelope: Envelope, + val serverDeliveredTimestamp: Long, + override val websocketRequest: WebSocketRequestMessage + ) : EnvelopeResponse() + + /** An envelope whose body could not be parsed at all. There is nothing to process, but it still needs to be acked. */ + class Unparseable( + override val websocketRequest: WebSocketRequestMessage + ) : EnvelopeResponse() +} diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/websocket/SignalWebSocket.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/websocket/SignalWebSocket.kt index d6d5e98a56..d0eeecd585 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/websocket/SignalWebSocket.kt +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/websocket/SignalWebSocket.kt @@ -456,7 +456,6 @@ sealed class SignalWebSocket( } } - @Throws(IOException::class) private fun WebSocketRequestMessage.toEnvelopeResponse(): EnvelopeResponse { val timestamp = this.findHeader() @@ -464,9 +463,14 @@ sealed class SignalWebSocket( Log.w(TAG, "Failed to parse $SERVER_DELIVERED_TIMESTAMP_HEADER") } - val envelope = Envelope.ADAPTER.decode(this.body!!.toByteArray()) + val envelope = try { + Envelope.ADAPTER.decode(this.body!!.toByteArray()) + } catch (e: Exception) { + Log.w(TAG, "Failed to parse envelope!", e) + return EnvelopeResponse.Unparseable(this) + } - return EnvelopeResponse(envelope, timestamp ?: 0, this) + return EnvelopeResponse.Parsed(envelope, timestamp ?: 0, this) } private fun WebSocketRequestMessage.findHeader(): Long? { diff --git a/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidatorTest.kt b/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidatorTest.kt index 14cc4be3f0..57b3118eba 100644 --- a/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidatorTest.kt +++ b/lib/libsignal-service/src/test/java/org/whispersystems/signalservice/api/messages/EnvelopeContentValidatorTest.kt @@ -1139,4 +1139,14 @@ class EnvelopeContentValidatorTest { val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE) assert(result is EnvelopeContentValidator.Result.Valid) } + + @Test + fun `validate - ensure a sync message with no source is marked invalid rather than throwing`() { + val content = Content( + syncMessage = SyncMessage() + ) + + val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE) + assert(result is EnvelopeContentValidator.Result.Invalid) + } } diff --git a/wire-handler/lib/build.gradle.kts b/wire-handler/lib/build.gradle.kts index 8d094a3bdc..072abd438a 100644 --- a/wire-handler/lib/build.gradle.kts +++ b/wire-handler/lib/build.gradle.kts @@ -14,5 +14,5 @@ repositories { } dependencies { - implementation("com.squareup.wire:wire-schema:6.4.0") + implementation("com.squareup.wire:wire-schema:6.4.5") }