Improve envelope decoding validations.

This commit is contained in:
Greyson Parrelli
2026-08-10 16:32:56 -04:00
parent 04b5fab41f
commit 22cecfea78
10 changed files with 266 additions and 141 deletions
@@ -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()
@@ -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<FollowUpOperation>,
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())
}
}
}
+1 -1
View File
@@ -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")
+61
View File
@@ -5597,6 +5597,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="201a37f0091d0362ba934f10fcecdabf80df8d8c3dc210f6a0c8161a2935eced" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-compiler" version="6.4.5">
<artifact name="wire-compiler-6.4.5.jar">
<sha256 value="69ae4fdb5439b3c1514af9f0ac2a9f54813e40525b0c334510b03ede29c0856b" origin="Generated by Gradle"/>
</artifact>
<artifact name="wire-compiler-6.4.5.module">
<sha256 value="0b8491c125c7b70da7a343d93c9f5c3df826c2265afd53fbf89e14dd5afb5b98" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-gradle-plugin" version="6.4.0">
<artifact name="wire-gradle-plugin-6.4.0.jar">
<sha256 value="a47df0923eafc4bab93050270dcb7ced331880d77aa4df9a423479028bdcdb94" origin="Generated by Gradle"/>
@@ -5605,6 +5613,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="596ee0b11c3407fb4fd596d5db58dd87abf30e98904cdee3e3c621eb9812c0c8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-gradle-plugin" version="6.4.5">
<artifact name="wire-gradle-plugin-6.4.5.jar">
<sha256 value="ef80a1a011f0f17cec6410ea74e011ce57950c6f76ed568747ba7aa9ca7e2113" origin="Generated by Gradle"/>
</artifact>
<artifact name="wire-gradle-plugin-6.4.5.module">
<sha256 value="f524ffe6f95d0d22f7bf89ab96aabf75777ef0348a7de78fa769632c29913f2c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-java-generator" version="6.4.0">
<artifact name="wire-java-generator-6.4.0.jar">
<sha256 value="5dc1105e4d3095ad7de68ae353f1c20031f9d06d14cf9e80fafae777e8ed6a82" origin="Generated by Gradle"/>
@@ -5613,6 +5629,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="3fdc80f73366cc8d8339fc31d56fb3525add158679c1eff5186e495815363149" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-java-generator" version="6.4.5">
<artifact name="wire-java-generator-6.4.5.jar">
<sha256 value="5dc1105e4d3095ad7de68ae353f1c20031f9d06d14cf9e80fafae777e8ed6a82" origin="Generated by Gradle"/>
</artifact>
<artifact name="wire-java-generator-6.4.5.module">
<sha256 value="58ed343612884e4719bc4aecad2c06f996c94f529bb83f2af28e079b9ac42480" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-kotlin-generator" version="6.4.0">
<artifact name="wire-kotlin-generator-6.4.0.jar">
<sha256 value="e74cba1267580999f76c4b4da27dea3ab5e5ea00c9364b9e65a0a52ac08e41f1" origin="Generated by Gradle"/>
@@ -5621,6 +5645,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="23ace8279265ec28c7331615aeb5da021cfa59d6ddc1cddf946fe257435b01a7" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-kotlin-generator" version="6.4.5">
<artifact name="wire-kotlin-generator-6.4.5.jar">
<sha256 value="e74cba1267580999f76c4b4da27dea3ab5e5ea00c9364b9e65a0a52ac08e41f1" origin="Generated by Gradle"/>
</artifact>
<artifact name="wire-kotlin-generator-6.4.5.module">
<sha256 value="d7135b63d643c2a1d123544e43045e4c28775d167c433f20f1c759b8afeea006" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-runtime" version="5.2.1">
<artifact name="wire-runtime-5.2.1.module">
<sha256 value="8ab81d1979d886d962a60b56f254590de381344b82559df3303478ff9e631232" origin="Generated by Gradle"/>
@@ -5637,6 +5669,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="55757ff61fb04d3cb1e7167edfa08583226cb08b6b76e5992f9d6da22fca12e8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-runtime" version="6.4.5">
<artifact name="wire-runtime-6.4.5.module">
<sha256 value="9227ebf170024938b548eed98fb3b76891358257c1412f518b53aec3db858ad4" origin="Generated by Gradle"/>
</artifact>
<artifact name="wire-runtime-metadata-6.4.5.jar">
<sha256 value="f5d0a99dd738a8eb534d55005117142b426a67386c466241635725d739eafafc" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-runtime-jvm" version="5.2.1">
<artifact name="wire-runtime-jvm-5.2.1.jar">
<sha256 value="da392e28917a7467dae41f50323d453722f2a66b95d479c1ac565b09b056e697" origin="Generated by Gradle"/>
@@ -5653,11 +5693,24 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="c6a410799a0aba56021281b368749c1703452dc37aab24ee4f058f864a36547c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-runtime-jvm" version="6.4.5">
<artifact name="wire-runtime-jvm-6.4.5.jar">
<sha256 value="525cb6f4d838555ed882ebf1567bb4069ab0aa5a5bc69a3fce0be27e6fbb5b4c" origin="Generated by Gradle"/>
</artifact>
<artifact name="wire-runtime-jvm-6.4.5.module">
<sha256 value="46a4a3d1478949fd0b5a01ea136775a2bb2bd4952ef8891e086ae73a189276dc" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-schema" version="6.4.0">
<artifact name="wire-schema-6.4.0.module">
<sha256 value="8779a4ad1879010a74bd638eb578a02b94e4cefe7b9004fed01778cb38304ae7" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-schema" version="6.4.5">
<artifact name="wire-schema-6.4.5.module">
<sha256 value="a93757fe679c90485b17ec4a971ebe09a07381e9f6e34c12658099296025eda3" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-schema-jvm" version="6.4.0">
<artifact name="wire-schema-jvm-6.4.0.jar">
<sha256 value="b6177b19b2d6479f57802bb2b33c621f7272d8d210e398be083a9ef4a9d5858f" origin="Generated by Gradle"/>
@@ -5666,6 +5719,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="93ae6cd97e473831ebcee4c1caeeecefddc1404c3bb211e1a02b069a5edfde05" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-schema-jvm" version="6.4.5">
<artifact name="wire-schema-jvm-6.4.5.jar">
<sha256 value="b6177b19b2d6479f57802bb2b33c621f7272d8d210e398be083a9ef4a9d5858f" origin="Generated by Gradle"/>
</artifact>
<artifact name="wire-schema-jvm-6.4.5.module">
<sha256 value="39fd3c6f51fb94e782ded85d493605a411e02d1a1046b9e0f8b8bca173306585" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.sun.istack" name="istack-commons-runtime" version="3.0.8">
<artifact name="istack-commons-runtime-3.0.8.jar">
<sha256 value="4ffabb06be454a05e4398e20c77fa2b6308d4b88dfbef7ca30a76b5b7d5505ef" origin="Generated by Gradle"/>
@@ -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);
}
}
@@ -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!")
@@ -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()
}
@@ -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? {
@@ -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)
}
}
+1 -1
View File
@@ -14,5 +14,5 @@ repositories {
}
dependencies {
implementation("com.squareup.wire:wire-schema:6.4.0")
implementation("com.squareup.wire:wire-schema:6.4.5")
}