mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-14 23:18:43 +00:00
Use both envelope.type and ciphertextMessageType in the validator.
This commit is contained in:
@@ -4,6 +4,7 @@ import okio.ByteString
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.signal.core.models.ServiceId
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.libsignal.protocol.message.CiphertextMessage
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase.Companion.groups
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.groups.GroupChangeBusyException
|
||||
@@ -47,7 +48,8 @@ class PushProcessMessageJob private constructor(
|
||||
sourceDeviceId = metadata.sourceDeviceId,
|
||||
sealedSender = metadata.sealedSender,
|
||||
groupId = if (metadata.groupId != null) metadata.groupId!!.toByteString() else null,
|
||||
destinationServiceId = ByteString.of(*metadata.destinationServiceId.toByteArray())
|
||||
destinationServiceId = ByteString.of(*metadata.destinationServiceId.toByteArray()),
|
||||
ciphertextMessageType = metadata.ciphertextMessageType
|
||||
),
|
||||
serverDeliveredTimestamp = serverDeliveredTimestamp
|
||||
).encode()
|
||||
@@ -84,7 +86,8 @@ class PushProcessMessageJob private constructor(
|
||||
sourceDeviceId = completeMessage.metadata.sourceDeviceId,
|
||||
sealedSender = completeMessage.metadata.sealedSender,
|
||||
groupId = completeMessage.metadata.groupId?.toByteArray(),
|
||||
destinationServiceId = ServiceId.parseOrThrow(completeMessage.metadata.destinationServiceId.toByteArray())
|
||||
destinationServiceId = ServiceId.parseOrThrow(completeMessage.metadata.destinationServiceId.toByteArray()),
|
||||
ciphertextMessageType = completeMessage.metadata.ciphertextMessageType ?: CiphertextMessage.WHISPER_TYPE
|
||||
),
|
||||
serverDeliveredTimestamp = completeMessage.serverDeliveredTimestamp
|
||||
)
|
||||
|
||||
@@ -177,7 +177,7 @@ object MessageDecryptor {
|
||||
|
||||
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!!)
|
||||
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)
|
||||
|
||||
@@ -8,5 +8,6 @@ class EnvelopeMetadata(
|
||||
val sourceDeviceId: Int,
|
||||
val sealedSender: Boolean,
|
||||
val groupId: ByteArray?,
|
||||
val destinationServiceId: ServiceId
|
||||
val destinationServiceId: ServiceId,
|
||||
val ciphertextMessageType: Int
|
||||
)
|
||||
|
||||
@@ -153,7 +153,8 @@ public class SignalServiceCipher {
|
||||
plaintext.metadata.getSenderDevice(),
|
||||
plaintext.metadata.isNeedsReceipt(),
|
||||
plaintext.metadata.getGroupId().orElse(null),
|
||||
localAddress.getServiceId()
|
||||
localAddress.getServiceId(),
|
||||
plaintext.getCiphertextMessageType()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
@@ -180,6 +181,7 @@ public class SignalServiceCipher {
|
||||
|
||||
byte[] paddedMessage;
|
||||
SignalServiceMetadata metadata;
|
||||
int ciphertextMessageType;
|
||||
|
||||
if (sourceServiceId == null && envelope.type != Envelope.Type.UNIDENTIFIED_SENDER) {
|
||||
throw new InvalidMessageStructureException("Non-UD envelope is missing a UUID!");
|
||||
@@ -189,19 +191,22 @@ public class SignalServiceCipher {
|
||||
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(sourceServiceId.toString(), envelope.sourceDevice);
|
||||
SignalSessionCipher sessionCipher = new SignalSessionCipher(sessionLock, new SessionCipher(signalProtocolStore, sourceAddress));
|
||||
|
||||
paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(envelope.content.toByteArray()));
|
||||
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
|
||||
paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(envelope.content.toByteArray()));
|
||||
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
|
||||
ciphertextMessageType = CiphertextMessage.PREKEY_TYPE;
|
||||
|
||||
signalProtocolStore.clearSenderKeySharedWith(Collections.singleton(sourceAddress));
|
||||
} else if (envelope.type == Envelope.Type.CIPHERTEXT) {
|
||||
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(sourceServiceId.toString(), envelope.sourceDevice);
|
||||
SignalSessionCipher sessionCipher = new SignalSessionCipher(sessionLock, new SessionCipher(signalProtocolStore, sourceAddress));
|
||||
|
||||
paddedMessage = sessionCipher.decrypt(new SignalMessage(envelope.content.toByteArray()));
|
||||
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
|
||||
paddedMessage = sessionCipher.decrypt(new SignalMessage(envelope.content.toByteArray()));
|
||||
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
|
||||
ciphertextMessageType = CiphertextMessage.WHISPER_TYPE;
|
||||
} else if (envelope.type == Envelope.Type.PLAINTEXT_CONTENT) {
|
||||
paddedMessage = new PlaintextContent(envelope.content.toByteArray()).getBody();
|
||||
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
|
||||
paddedMessage = new PlaintextContent(envelope.content.toByteArray()).getBody();
|
||||
metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, serverGuid, Optional.empty(), destinationStr);
|
||||
ciphertextMessageType = CiphertextMessage.PLAINTEXT_CONTENT_TYPE;
|
||||
} else if (envelope.type == Envelope.Type.UNIDENTIFIED_SENDER) {
|
||||
SignalSealedSessionCipher sealedSessionCipher = new SignalSealedSessionCipher(sessionLock, new SealedSessionCipher(signalProtocolStore, localAddress.getServiceId().getRawUuid(), localAddress.getNumber().orElse(null), localDeviceId));
|
||||
DecryptionResult result = sealedSessionCipher.decrypt(certificateValidator, envelope.content.toByteArray(), envelope.serverTimestamp);
|
||||
@@ -214,7 +219,9 @@ public class SignalServiceCipher {
|
||||
needsReceipt = false;
|
||||
}
|
||||
|
||||
if (result.getCiphertextMessageType() == CiphertextMessage.PREKEY_TYPE) {
|
||||
ciphertextMessageType = result.getCiphertextMessageType();
|
||||
|
||||
if (ciphertextMessageType == CiphertextMessage.PREKEY_TYPE) {
|
||||
signalProtocolStore.clearSenderKeySharedWith(Collections.singleton(new SignalProtocolAddress(result.getSenderUuid(), result.getDeviceId())));
|
||||
}
|
||||
|
||||
@@ -227,7 +234,7 @@ public class SignalServiceCipher {
|
||||
PushTransportDetails transportDetails = new PushTransportDetails();
|
||||
byte[] data = transportDetails.getStrippedPaddingMessageBody(paddedMessage);
|
||||
|
||||
return new Plaintext(metadata, data);
|
||||
return new Plaintext(metadata, data, ciphertextMessageType);
|
||||
} catch (DuplicateMessageException e) {
|
||||
throw new ProtocolDuplicateMessageException(e, sourceServiceId.toString(), envelope.sourceDevice);
|
||||
} catch (LegacyMessageException e) {
|
||||
@@ -253,11 +260,13 @@ public class SignalServiceCipher {
|
||||
|
||||
private static class Plaintext {
|
||||
private final SignalServiceMetadata metadata;
|
||||
private final byte[] data;
|
||||
private final byte[] data;
|
||||
private final int ciphertextMessageType;
|
||||
|
||||
private Plaintext(SignalServiceMetadata metadata, byte[] data) {
|
||||
this.metadata = metadata;
|
||||
this.data = data;
|
||||
private Plaintext(SignalServiceMetadata metadata, byte[] data, int ciphertextMessageType) {
|
||||
this.metadata = metadata;
|
||||
this.data = data;
|
||||
this.ciphertextMessageType = ciphertextMessageType;
|
||||
}
|
||||
|
||||
public SignalServiceMetadata getMetadata() {
|
||||
@@ -267,5 +276,9 @@ public class SignalServiceCipher {
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public int getCiphertextMessageType() {
|
||||
return ciphertextMessageType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.whispersystems.signalservice.api.messages
|
||||
import okio.ByteString
|
||||
import org.signal.core.models.ServiceId
|
||||
import org.signal.core.models.ServiceId.ACI
|
||||
import org.signal.libsignal.protocol.message.CiphertextMessage
|
||||
import org.signal.libsignal.protocol.message.DecryptionErrorMessage
|
||||
import org.signal.libsignal.protocol.message.SenderKeyDistributionMessage
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException
|
||||
@@ -33,8 +34,8 @@ object EnvelopeContentValidator {
|
||||
private const val MAX_POLL_CHARACTER_LENGTH = 100
|
||||
private const val MIN_POLL_OPTIONS = 2
|
||||
|
||||
fun validate(envelope: Envelope, content: Content, localAci: ACI): Result {
|
||||
if (envelope.type == Envelope.Type.PLAINTEXT_CONTENT) {
|
||||
fun validate(envelope: Envelope, content: Content, localAci: ACI, ciphertextMessageType: Int): Result {
|
||||
if (envelope.type == Envelope.Type.PLAINTEXT_CONTENT || ciphertextMessageType == CiphertextMessage.PLAINTEXT_CONTENT_TYPE) {
|
||||
validatePlaintextContent(content)?.let { return it }
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,13 @@ package signalservice;
|
||||
option java_package = "org.whispersystems.signalservice.api.crypto.protos";
|
||||
|
||||
message EnvelopeMetadata {
|
||||
required bytes sourceServiceId = 1;
|
||||
optional string sourceE164 = 2;
|
||||
required int32 sourceDeviceId = 3;
|
||||
required bool sealedSender = 4;
|
||||
optional bytes groupId = 5;
|
||||
required bytes destinationServiceId = 6;
|
||||
required bytes sourceServiceId = 1;
|
||||
optional string sourceE164 = 2;
|
||||
required int32 sourceDeviceId = 3;
|
||||
required bool sealedSender = 4;
|
||||
optional bytes groupId = 5;
|
||||
required bytes destinationServiceId = 6;
|
||||
optional int32 ciphertextMessageType = 7;
|
||||
}
|
||||
|
||||
message CompleteMessage {
|
||||
|
||||
@@ -8,6 +8,8 @@ package org.whispersystems.signalservice.api.messages
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.junit.Test
|
||||
import org.signal.core.models.ServiceId
|
||||
import org.signal.libsignal.protocol.message.CiphertextMessage
|
||||
import org.signal.libsignal.protocol.message.DecryptionErrorMessage
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.DataMessage
|
||||
import org.whispersystems.signalservice.internal.push.Envelope
|
||||
@@ -30,7 +32,7 @@ class EnvelopeContentValidatorTest {
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(envelope, content, SELF_ACI)
|
||||
val result = EnvelopeContentValidator.validate(envelope, content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@@ -45,7 +47,7 @@ class EnvelopeContentValidatorTest {
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI)
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@@ -61,7 +63,7 @@ class EnvelopeContentValidatorTest {
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI)
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@@ -77,7 +79,7 @@ class EnvelopeContentValidatorTest {
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI)
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@@ -93,7 +95,7 @@ class EnvelopeContentValidatorTest {
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI)
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@@ -108,7 +110,7 @@ class EnvelopeContentValidatorTest {
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI)
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@@ -120,7 +122,7 @@ class EnvelopeContentValidatorTest {
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI)
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@@ -134,7 +136,132 @@ class EnvelopeContentValidatorTest {
|
||||
)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI)
|
||||
val result = EnvelopeContentValidator.validate(Envelope(), content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - plaintext content via envelope type with valid decryption error message is valid`() {
|
||||
val envelope = Envelope(
|
||||
type = Envelope.Type.PLAINTEXT_CONTENT
|
||||
)
|
||||
|
||||
val content = Content(
|
||||
decryptionErrorMessage = createValidDecryptionErrorMessage()
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(envelope, content, SELF_ACI, CiphertextMessage.PLAINTEXT_CONTENT_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Valid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - plaintext content via ciphertext message type (sealed sender) with valid decryption error message is valid`() {
|
||||
val envelope = Envelope(
|
||||
type = Envelope.Type.UNIDENTIFIED_SENDER
|
||||
)
|
||||
|
||||
val content = Content(
|
||||
decryptionErrorMessage = createValidDecryptionErrorMessage()
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(envelope, content, SELF_ACI, CiphertextMessage.PLAINTEXT_CONTENT_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Valid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - plaintext content via envelope type with unexpected DataMessage is invalid`() {
|
||||
val envelope = Envelope(
|
||||
type = Envelope.Type.PLAINTEXT_CONTENT,
|
||||
timestamp = 1234
|
||||
)
|
||||
|
||||
val content = Content(
|
||||
decryptionErrorMessage = createValidDecryptionErrorMessage(),
|
||||
dataMessage = DataMessage(timestamp = 1234)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(envelope, content, SELF_ACI, CiphertextMessage.PLAINTEXT_CONTENT_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - plaintext content via ciphertext message type (sealed sender) with unexpected DataMessage is invalid`() {
|
||||
val envelope = Envelope(
|
||||
type = Envelope.Type.UNIDENTIFIED_SENDER,
|
||||
timestamp = 1234
|
||||
)
|
||||
|
||||
val content = Content(
|
||||
decryptionErrorMessage = createValidDecryptionErrorMessage(),
|
||||
dataMessage = DataMessage(timestamp = 1234)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(envelope, content, SELF_ACI, CiphertextMessage.PLAINTEXT_CONTENT_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - plaintext content via envelope type without DecryptionErrorMessage is invalid`() {
|
||||
val envelope = Envelope(
|
||||
type = Envelope.Type.PLAINTEXT_CONTENT
|
||||
)
|
||||
|
||||
val content = Content()
|
||||
|
||||
val result = EnvelopeContentValidator.validate(envelope, content, SELF_ACI, CiphertextMessage.PLAINTEXT_CONTENT_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - plaintext content via ciphertext message type (sealed sender) without DecryptionErrorMessage is invalid`() {
|
||||
val envelope = Envelope(
|
||||
type = Envelope.Type.UNIDENTIFIED_SENDER
|
||||
)
|
||||
|
||||
val content = Content()
|
||||
|
||||
val result = EnvelopeContentValidator.validate(envelope, content, SELF_ACI, CiphertextMessage.PLAINTEXT_CONTENT_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - plaintext content with SyncMessage is invalid`() {
|
||||
val envelope = Envelope(
|
||||
type = Envelope.Type.PLAINTEXT_CONTENT
|
||||
)
|
||||
|
||||
val content = Content(
|
||||
decryptionErrorMessage = createValidDecryptionErrorMessage(),
|
||||
syncMessage = org.whispersystems.signalservice.internal.push.SyncMessage()
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(envelope, content, SELF_ACI, CiphertextMessage.PLAINTEXT_CONTENT_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Invalid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `validate - regular encrypted message is not subject to plaintext validation`() {
|
||||
val envelope = Envelope(
|
||||
type = Envelope.Type.CIPHERTEXT,
|
||||
timestamp = 1234
|
||||
)
|
||||
|
||||
val content = Content(
|
||||
dataMessage = DataMessage(timestamp = 1234)
|
||||
)
|
||||
|
||||
val result = EnvelopeContentValidator.validate(envelope, content, SELF_ACI, CiphertextMessage.WHISPER_TYPE)
|
||||
assert(result is EnvelopeContentValidator.Result.Valid)
|
||||
}
|
||||
|
||||
private fun createValidDecryptionErrorMessage(): okio.ByteString {
|
||||
val minimalSenderKeyContent = ByteArray(64)
|
||||
val decryptionErrorMessage = DecryptionErrorMessage.forOriginalMessage(
|
||||
minimalSenderKeyContent,
|
||||
CiphertextMessage.SENDERKEY_TYPE,
|
||||
System.currentTimeMillis(),
|
||||
1
|
||||
)
|
||||
return decryptionErrorMessage.serialize().toByteString()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user