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 42f564631e..57d6b7d5df 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt @@ -395,7 +395,7 @@ object MessageDecryptor { val aciAddress = SignalProtocolAddress(aci.toString(), deviceId) val pniAddress = SignalProtocolAddress(pni.toString(), deviceId) val aciIdentity = protocolStore.getAciStore().getIdentity(aciAddress) - val pniIdentity = protocolStore.getAciStore().getIdentity(pniAddress) + var pniIdentity = protocolStore.getAciStore().getIdentity(pniAddress) if (aciIdentity == null) { Log.w(TAG, "${logPrefix(envelope, aci)}[validatePniSignature] No identity found for ACI address $aciAddress") @@ -404,7 +404,18 @@ object MessageDecryptor { if (pniIdentity == null) { Log.w(TAG, "${logPrefix(envelope, aci)}[validatePniSignature] No identity found for PNI address $pniAddress") - return + if (deviceId != SignalServiceAddress.DEFAULT_DEVICE_ID) { + pniIdentity = protocolStore.getAciStore().getIdentity(SignalProtocolAddress(pni.toString(), SignalServiceAddress.DEFAULT_DEVICE_ID)) + + if (pniIdentity != null) { + Log.w(TAG, "${logPrefix(envelope, aci)}[validatePniSignature] Found PNI identity when looking up device 1. Using that.") + } else { + Log.w(TAG, "${logPrefix(envelope, aci)}[validatePniSignature] No PNI identity when looking up device 1 either.") + return + } + } else { + return + } } if (pniIdentity.verifyAlternateIdentity(aciIdentity, pniSignatureMessage.signature!!.toByteArray())) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt index 602ab61527..e168112874 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt @@ -6,6 +6,9 @@ import com.mobilecoin.lib.exceptions.SerializationException import okio.ByteString import org.signal.core.util.Hex import org.signal.core.util.orNull +import org.signal.libsignal.protocol.IdentityKey +import org.signal.libsignal.protocol.InvalidKeyException +import org.signal.libsignal.protocol.SignalProtocolAddress import org.signal.libsignal.protocol.util.Pair import org.signal.ringrtc.CallException import org.signal.ringrtc.CallLinkRootKey @@ -96,6 +99,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPoin import org.whispersystems.signalservice.api.push.DistributionId import org.whispersystems.signalservice.api.push.ServiceId import org.whispersystems.signalservice.api.push.ServiceId.ACI +import org.whispersystems.signalservice.api.push.ServiceId.PNI import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.api.storage.StorageKey import org.whispersystems.signalservice.internal.push.Content @@ -169,6 +173,8 @@ object SyncMessageProcessor { log(envelope.timestamp!!, "Processing sent transcript for message with ID ${sent.timestamp!!}") try { + handlePniIdentityKeys(envelope, sent) + if (sent.storyMessage != null || sent.storyMessageRecipients.isNotEmpty()) { handleSynchronizeSentStoryMessage(envelope, sent) return @@ -246,6 +252,34 @@ object SyncMessageProcessor { } } + private fun handlePniIdentityKeys(envelope: Envelope, sent: Sent) { + for (status in sent.unidentifiedStatus) { + if (status.destinationIdentityKey == null) { + continue + } + + val pni = PNI.parsePrefixedOrNull(status.destinationServiceId) + if (pni == null) { + continue + } + + val address = SignalProtocolAddress(pni.toString(), SignalServiceAddress.DEFAULT_DEVICE_ID) + + if (ApplicationDependencies.getProtocolStore().aci().identities().getIdentity(address) != null) { + log(envelope.timestamp!!, "Ignoring identity on sent transcript for $pni because we already have one.") + continue + } + + try { + log(envelope.timestamp!!, "Saving identity from sent transcript for $pni") + val identityKey = IdentityKey(status.destinationIdentityKey!!.toByteArray()) + ApplicationDependencies.getProtocolStore().aci().identities().saveIdentity(address, identityKey) + } catch (e: InvalidKeyException) { + warn(envelope.timestamp!!, "Failed to deserialize identity key for $pni") + } + } + } + private fun getSyncMessageDestination(message: Sent): Recipient { return if (message.message.hasGroupContext) { Recipient.externalPossiblyMigratedGroup(GroupId.v2(message.message!!.groupV2!!.groupMasterKey)) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index 411a449cd3..9104c81518 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -7,6 +7,7 @@ package org.whispersystems.signalservice.api; import org.signal.core.util.Base64; import org.signal.libsignal.metadata.certificate.SenderCertificate; +import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKeyPair; import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.InvalidRegistrationIdException; @@ -1399,9 +1400,21 @@ public class SignalServiceMessageSender { List unidentifiedDeliveryStatuses = new ArrayList<>(sendMessageResults.size()); for (SendMessageResult result : sendMessageResults) { if (result.getSuccess() != null) { + ByteString identity = null; + + if (result.getAddress().getServiceId() instanceof PNI) { + IdentityKey identityKey = aciStore.getIdentity(result.getAddress().getServiceId().toProtocolAddress(SignalServiceAddress.DEFAULT_DEVICE_ID)); + if (identityKey != null) { + identity = ByteString.of(identityKey.getPublicKey().serialize()); + } else { + Log.w(TAG, "[" + timestamp + "] Could not find an identity for PNI when sending sync message! " + result.getAddress().getServiceId()); + } + } + unidentifiedDeliveryStatuses.add(new SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder() .destinationServiceId(result.getAddress().getServiceId().toString()) .unidentified(result.getSuccess().isUnidentified()) + .destinationIdentityKey(identity) .build()); } } diff --git a/libsignal-service/src/main/protowire/SignalService.proto b/libsignal-service/src/main/protowire/SignalService.proto index 49ef328a6b..5e3db8375c 100644 --- a/libsignal-service/src/main/protowire/SignalService.proto +++ b/libsignal-service/src/main/protowire/SignalService.proto @@ -439,9 +439,11 @@ message Verified { message SyncMessage { message Sent { message UnidentifiedDeliveryStatus { - reserved /*destinationE164*/ 1; - optional string destinationServiceId = 3; - optional bool unidentified = 2; + reserved /*destinationE164*/ 1; + optional string destinationServiceId = 3; + optional bool unidentified = 2; + reserved /*destinationPni*/ 4; + optional bytes destinationIdentityKey = 5; } message StoryMessageRecipient { @@ -460,6 +462,8 @@ message SyncMessage { optional StoryMessage storyMessage = 8; repeated StoryMessageRecipient storyMessageRecipients = 9; optional EditMessage editMessage = 10; + reserved /*destinationPni*/ 11; + // NEXT ID: 12 } message Contacts {