mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-09-28 07:51:42 +01:00
Improve envelope decoding validations.
This commit is contained in:
@@ -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
@@ -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")
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
+10
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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!")
|
||||
|
||||
+16
-5
@@ -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()
|
||||
}
|
||||
|
||||
+7
-3
@@ -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? {
|
||||
|
||||
+10
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,5 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.squareup.wire:wire-schema:6.4.0")
|
||||
implementation("com.squareup.wire:wire-schema:6.4.5")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user