private async Task HandleSessionResetMessage(SignalServiceEnvelope envelope, SignalServiceContent content, SignalServiceDataMessage dataMessage, bool isSync, long timestamp) { SignalMessageDirection type; SignalContact author; SignalMessageStatus status; SignalConversation conversation; string prefix; string conversationId; long composedTimestamp; if (isSync) { var sent = content.SynchronizeMessage.Sent; type = SignalMessageDirection.Synced; status = SignalMessageStatus.Confirmed; composedTimestamp = sent.Timestamp; author = null; prefix = "You have"; conversationId = sent.Destination.ForceGetValue(); } else { status = 0; type = SignalMessageDirection.Incoming; author = await SignalDBContext.GetOrCreateContactLocked(envelope.GetSource(), timestamp); prefix = $"{author.ThreadDisplayName} has"; composedTimestamp = envelope.GetTimestamp(); conversationId = envelope.GetSource(); } LibsignalDBContext.DeleteAllSessions(conversationId); conversation = await SignalDBContext.GetOrCreateContactLocked(conversationId, 0); SignalMessage sm = new SignalMessage() { Direction = type, Type = SignalMessageType.SessionReset, Status = status, Author = author, Content = new SignalMessageContent() { Content = $"{prefix} reset the session." }, ThreadId = conversationId, DeviceId = (uint)envelope.GetSourceDevice(), Receipts = 0, ComposedTimestamp = composedTimestamp, ReceivedTimestamp = timestamp, }; await SignalLibHandle.Instance.SaveAndDispatchSignalMessage(sm, null, conversation); }
private byte[] Decrypt(SignalServiceEnvelope envelope, byte[] ciphertext) { SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.GetSource(), (uint)envelope.GetSourceDevice()); SessionCipher sessionCipher = new SessionCipher(SignalProtocolStore, sourceAddress); byte[] paddedMessage; if (envelope.IsPreKeySignalMessage()) { paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext)); } else if (envelope.IsSignalMessage()) { paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext)); } else { throw new InvalidMessageException("Unknown type: " + envelope.GetEnvelopeType() + " from " + envelope.GetSource()); } PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); return(transportDetails.GetStrippedPaddingMessageBody(paddedMessage)); }
private async Task HandleSignalMessage(SignalServiceEnvelope envelope, SignalServiceContent content, SignalServiceDataMessage dataMessage, bool isSync, long timestamp) { SignalMessageDirection type; SignalContact author; SignalMessageStatus status; SignalConversation conversation; long composedTimestamp; string body = dataMessage.Body ?? ""; if (dataMessage.Group != null) { var rawId = dataMessage.Group.GroupId; var threadId = Base64.EncodeBytes(rawId); conversation = await SignalDBContext.GetOrCreateGroupLocked(threadId, timestamp); if (!conversation.CanReceive) { SignalServiceGroup group = new SignalServiceGroup() { Type = SignalServiceGroup.GroupType.REQUEST_INFO, GroupId = rawId }; SignalServiceDataMessage requestInfoMessage = new SignalServiceDataMessage() { Group = group, Timestamp = Util.CurrentTimeMillis() }; SignalLibHandle.Instance.OutgoingQueue.Add(new SignalServiceDataMessageSendable(requestInfoMessage, envelope.GetSourceAddress())); } composedTimestamp = envelope.GetTimestamp(); } else { if (isSync) { var sent = content.SynchronizeMessage.Sent; conversation = await SignalDBContext.GetOrCreateContactLocked(sent.Destination.ForceGetValue(), timestamp); composedTimestamp = sent.Timestamp; } else { conversation = await SignalDBContext.GetOrCreateContactLocked(envelope.GetSource(), timestamp); composedTimestamp = envelope.GetTimestamp(); } } if (isSync) { type = SignalMessageDirection.Synced; status = SignalMessageStatus.Confirmed; author = null; } else { status = 0; type = SignalMessageDirection.Incoming; author = await SignalDBContext.GetOrCreateContactLocked(envelope.GetSource(), timestamp); } if (author != null && author.Blocked) { // Don't save blocked messages return; } List <SignalAttachment> attachments = new List <SignalAttachment>(); SignalMessage message = new SignalMessage() { Direction = type, Status = status, Author = author, Content = new SignalMessageContent() { Content = body.Truncate(2000) }, ThreadId = conversation.ThreadId, DeviceId = (uint)envelope.GetSourceDevice(), Receipts = 0, ComposedTimestamp = composedTimestamp, ReceivedTimestamp = timestamp, AttachmentsCount = (uint)attachments.Count, Attachments = attachments }; if (dataMessage.Attachments != null) { var receivedAttachments = dataMessage.Attachments; foreach (var receivedAttachment in receivedAttachments) { var pointer = receivedAttachment.AsPointer(); SignalAttachment sa = new SignalAttachment() { Message = message, Status = (uint)SignalAttachmentStatus.Default, SentFileName = pointer.FileName, ContentType = receivedAttachment.ContentType, Key = pointer.Key, Relay = pointer.Relay, StorageId = pointer.Id, Size = (long)pointer.Size, Digest = pointer.Digest }; attachments.Add(sa); } // Make sure to update attachments count message.AttachmentsCount = (uint)attachments.Count; } await SignalLibHandle.Instance.SaveAndDispatchSignalMessage(message, null, conversation); }
private async Task HandleGroupUpdateMessage(SignalServiceEnvelope envelope, SignalServiceContent content, SignalServiceDataMessage dataMessage, bool isSync, long timestamp) { if (dataMessage.Group != null) //TODO check signal droid: group messages have different types! { SignalServiceGroup group = dataMessage.Group; string groupid = Base64.EncodeBytes(group.GroupId); SignalGroup g = new SignalGroup(); string displayname = "Unknown group"; string avatarfile = null; if (group.Name != null) { displayname = group.Name; } var dbgroup = SignalDBContext.InsertOrUpdateGroupLocked(groupid, displayname, avatarfile, true, timestamp); if (group.Members != null) { foreach (var member in group.Members) { SignalDBContext.InsertOrUpdateGroupMembershipLocked(dbgroup.Id, (await SignalDBContext.GetOrCreateContactLocked(member, 0)).Id); } } /* insert message into conversation */ SignalMessageDirection type; SignalContact author; SignalMessageStatus status; string prefix; long composedTimestamp; if (isSync) { var sent = content.SynchronizeMessage.Sent; type = SignalMessageDirection.Synced; status = SignalMessageStatus.Confirmed; composedTimestamp = sent.Timestamp; author = null; prefix = "You have"; } else { status = 0; type = SignalMessageDirection.Incoming; author = await SignalDBContext.GetOrCreateContactLocked(envelope.GetSource(), timestamp); prefix = $"{author.ThreadDisplayName} has"; composedTimestamp = envelope.GetTimestamp(); } SignalMessage sm = new SignalMessage() { Direction = type, Type = SignalMessageType.GroupUpdate, Status = status, Author = author, Content = new SignalMessageContent() { Content = $"{prefix} updated the group." }, ThreadId = groupid, DeviceId = (uint)envelope.GetSourceDevice(), Receipts = 0, ComposedTimestamp = composedTimestamp, ReceivedTimestamp = timestamp, }; SignalDBContext.SaveMessageLocked(sm); dbgroup.MessagesCount += 1; if (sm.Direction == SignalMessageDirection.Incoming) { dbgroup.UnreadCount += 1; } else { dbgroup.UnreadCount = 0; dbgroup.LastSeenMessageIndex = dbgroup.MessagesCount; } dbgroup.LastMessage = sm; await SignalLibHandle.Instance.DispatchAddOrUpdateConversation(dbgroup, sm); } else { Logger.LogError("HandleGroupUpdateMessage() received group update without group info"); } }
private async Task HandleGroupLeaveMessage(SignalServiceEnvelope envelope, SignalServiceContent content, SignalServiceDataMessage dataMessage, bool isSync, long timestamp) { SignalServiceGroup sentGroup = dataMessage.Group; if (sentGroup != null) { string groupid = Base64.EncodeBytes(sentGroup.GroupId); SignalGroup group = await SignalDBContext.GetOrCreateGroupLocked(groupid, 0); if (isSync) { SignalContact author = await SignalDBContext.GetOrCreateContactLocked(envelope.GetSource(), 0); SignalMessage sm = new SignalMessage() { Direction = SignalMessageDirection.Incoming, Type = SignalMessageType.GroupLeave, Status = SignalMessageStatus.Received, Author = author, Content = new SignalMessageContent() { Content = $"You have left the group." }, ThreadId = groupid, DeviceId = (uint)envelope.GetSourceDevice(), Receipts = 0, ComposedTimestamp = envelope.GetTimestamp(), ReceivedTimestamp = timestamp, }; SignalConversation updatedConversation = SignalDBContext.RemoveMemberFromGroup(groupid, author, sm); await SignalLibHandle.Instance.DispatchAddOrUpdateConversation(updatedConversation, sm); } else { SignalContact author = await SignalDBContext.GetOrCreateContactLocked(envelope.GetSource(), 0); SignalMessage sm = new SignalMessage() { Direction = SignalMessageDirection.Incoming, Type = SignalMessageType.GroupLeave, Status = SignalMessageStatus.Received, Author = author, Content = new SignalMessageContent() { Content = $"{author.ThreadDisplayName} has left the group." }, ThreadId = groupid, DeviceId = (uint)envelope.GetSourceDevice(), Receipts = 0, ComposedTimestamp = envelope.GetTimestamp(), ReceivedTimestamp = timestamp, }; SignalConversation updatedConversation = SignalDBContext.RemoveMemberFromGroup(groupid, author, sm); await SignalLibHandle.Instance.DispatchAddOrUpdateConversation(updatedConversation, sm); } } else { Logger.LogError("HandleGroupLeaveMessage() received group update without group info"); } }
private async Task HandleExpirationUpdateMessage(SignalServiceEnvelope envelope, SignalServiceContent content, SignalServiceDataMessage message, bool isSync, long timestamp) { SignalMessageDirection type; SignalContact author; SignalMessageStatus status; string prefix; SignalConversation conversation; long composedTimestamp; if (isSync) { var sent = content.SynchronizeMessage.Sent; type = SignalMessageDirection.Synced; status = SignalMessageStatus.Confirmed; composedTimestamp = sent.Timestamp; author = null; prefix = "You have"; if (message.Group != null) { conversation = await SignalDBContext.GetOrCreateGroupLocked(Base64.EncodeBytes(message.Group.GroupId), 0); } else { conversation = await SignalDBContext.GetOrCreateContactLocked(sent.Destination.ForceGetValue(), 0); } } else { status = 0; type = SignalMessageDirection.Incoming; author = await SignalDBContext.GetOrCreateContactLocked(envelope.GetSource(), timestamp); prefix = $"{author.ThreadDisplayName} has"; composedTimestamp = envelope.GetTimestamp(); if (message.Group != null) { conversation = await SignalDBContext.GetOrCreateGroupLocked(Base64.EncodeBytes(message.Group.GroupId), 0); } else { conversation = await SignalDBContext.GetOrCreateContactLocked(envelope.GetSource(), 0); } } conversation.ExpiresInSeconds = (uint)message.ExpiresInSeconds; SignalDBContext.UpdateExpiresInLocked(conversation); SignalMessage sm = new SignalMessage() { Direction = type, Type = SignalMessageType.ExpireUpdate, Status = status, Author = author, Content = new SignalMessageContent() { Content = $"{prefix} set the expiration timer to {message.ExpiresInSeconds} seconds." }, ThreadId = conversation.ThreadId, DeviceId = (uint)envelope.GetSourceDevice(), Receipts = 0, ComposedTimestamp = composedTimestamp, ReceivedTimestamp = timestamp, }; await SignalLibHandle.Instance.SaveAndDispatchSignalMessage(sm, null, conversation); }
private Plaintext Decrypt(SignalServiceEnvelope envelope, byte[] ciphertext) { try { SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.GetSource(), (uint)envelope.GetSourceDevice()); SessionCipher sessionCipher = new SessionCipher(SignalProtocolStore, sourceAddress); SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(SignalProtocolStore, new SignalProtocolAddress(LocalAddress.E164number, 1)); byte[] paddedMessage; Metadata metadata; uint sessionVersion; if (envelope.IsPreKeySignalMessage()) { paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext)); metadata = new Metadata(envelope.GetSource(), envelope.GetSourceDevice(), envelope.GetTimestamp(), false); sessionVersion = sessionCipher.getSessionVersion(); } else if (envelope.IsSignalMessage()) { paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext)); metadata = new Metadata(envelope.GetSource(), envelope.GetSourceDevice(), envelope.GetTimestamp(), false); sessionVersion = sessionCipher.getSessionVersion(); } else if (envelope.IsUnidentifiedSender()) { var results = sealedSessionCipher.Decrypt(CertificateValidator, ciphertext, (long)envelope.Envelope.ServerTimestamp); paddedMessage = results.Item2; metadata = new Metadata(results.Item1.Name, (int)results.Item1.DeviceId, (long)envelope.Envelope.Timestamp, true); sessionVersion = (uint)sealedSessionCipher.GetSessionVersion(new SignalProtocolAddress(metadata.Sender, (uint)metadata.SenderDevice)); } else { throw new InvalidMessageException("Unknown type: " + envelope.GetEnvelopeType() + " from " + envelope.GetSource()); } PushTransportDetails transportDetails = new PushTransportDetails(sessionVersion); byte[] data = transportDetails.GetStrippedPaddingMessageBody(paddedMessage); return(new Plaintext(metadata, data)); } catch (DuplicateMessageException e) { throw new ProtocolDuplicateMessageException(e, envelope.GetSource(), envelope.GetSourceDevice()); } catch (LegacyMessageException e) { throw new ProtocolLegacyMessageException(e, envelope.GetSource(), envelope.GetSourceDevice()); } catch (InvalidMessageException e) { throw new ProtocolInvalidMessageException(e, envelope.GetSource(), envelope.GetSourceDevice()); } catch (InvalidKeyIdException e) { throw new ProtocolInvalidKeyIdException(e, envelope.GetSource(), envelope.GetSourceDevice()); } catch (InvalidKeyException e) { throw new ProtocolInvalidKeyException(e, envelope.GetSource(), envelope.GetSourceDevice()); } catch (libsignal.exceptions.UntrustedIdentityException e) { throw new ProtocolUntrustedIdentityException(e, envelope.GetSource(), envelope.GetSourceDevice()); } catch (InvalidVersionException e) { throw new ProtocolInvalidVersionException(e, envelope.GetSource(), envelope.GetSourceDevice()); } catch (NoSessionException e) { throw new ProtocolNoSessionException(e, envelope.GetSource(), envelope.GetSourceDevice()); } }
/// <summary> /// Decrypt a received <see cref="SignalServiceEnvelope"/> /// </summary> /// <param name="envelope">The received SignalServiceEnvelope</param> /// <returns>a decrypted SignalServiceContent</returns> public SignalServiceContent Decrypt(SignalServiceEnvelope envelope) { try { SignalServiceContent content = new SignalServiceContent(); if (envelope.HasLegacyMessage()) { DataMessage message = DataMessage.Parser.ParseFrom(Decrypt(envelope, envelope.GetLegacyMessage())); content = new SignalServiceContent() { Message = CreateSignalServiceMessage(envelope, message) }; } else if (envelope.HasContent()) { Content message = Content.Parser.ParseFrom(Decrypt(envelope, envelope.GetContent())); if (message.DataMessageOneofCase == Content.DataMessageOneofOneofCase.DataMessage) { content = new SignalServiceContent() { Message = CreateSignalServiceMessage(envelope, message.DataMessage) }; } else if (message.SyncMessageOneofCase == Content.SyncMessageOneofOneofCase.SyncMessage && LocalAddress.E164number == envelope.GetSource()) { content = new SignalServiceContent() { SynchronizeMessage = CreateSynchronizeMessage(envelope, message.SyncMessage) }; } else if (message.CallMessageOneofCase == Content.CallMessageOneofOneofCase.CallMessage) { content = new SignalServiceContent() { CallMessage = CreateCallMessage(message.CallMessage) }; } else if (message.ReceiptMessageOneofCase == Content.ReceiptMessageOneofOneofCase.ReceiptMessage) { content = new SignalServiceContent() { ReadMessage = CreateReceiptMessage(envelope, message.ReceiptMessage) }; } } return(content); } catch (InvalidProtocolBufferException e) { throw new InvalidMessageException(e); } }