示例#1
0
 public static byte[] SerializeCore(this XMessageMetaData m)
 {
     byte[] serialized = PocoSerializer.Begin()
                         .Append((byte)m.MessageType)
                         .Append(m.SenderLocalMessageId)
                         .Append(m.SenderPublicKey)
                         .Finish();
     return(serialized);
 }
示例#2
0
        public static XMessageMetaData DeserializeMessageMetadata(this byte[] messageMetadata)
        {
            var m = new XMessageMetaData();

            var ser = PocoSerializer.GetDeserializer(messageMetadata);

            m.MessageType          = (MessageType)ser.MakeByte(0);
            m.SenderLocalMessageId = ser.MakeInt32(1);
            m.SenderPublicKey      = ser.MakeByteArray(2);

            return(m);
        }
示例#3
0
        void EncryptWithStrategy(Message message, KeyMaterial64 keyMaterial, RoundsExponent roundsExponent, KeyMaterial64 initialMetadataKeyMaterial)
        {
            XMessageMetaData messageMetadata = new XMessageMetaData {
                MessageType = message.MessageType
            };

            switch (message.MessageType)
            {
            case MessageType.Text:
                message.TextCipher = EncryptTextToBytes(message.ThreadText, keyMaterial, roundsExponent);
                messageMetadata.SenderLocalMessageId = int.Parse(message.Id);
                break;

            case MessageType.Media:
                message.ImageCipher = this.ixdsCryptoService.DefaultEncrypt(message.ThreadMedia, keyMaterial);
                messageMetadata.SenderLocalMessageId = int.Parse(message.Id);
                break;

            case MessageType.TextAndMedia:
            case MessageType.File:
                message.TextCipher  = EncryptTextToBytes(message.ThreadText, keyMaterial, roundsExponent);
                message.ImageCipher = this.ixdsCryptoService.DefaultEncrypt(message.ThreadMedia, keyMaterial);
                messageMetadata.SenderLocalMessageId = int.Parse(message.Id);
                break;

            case MessageType.DeliveryReceipt:
            case MessageType.ReadReceipt:
                messageMetadata.SenderLocalMessageId = int.Parse(message.SenderLocalMessageId);
                break;

            default:
                throw new Exception("Invalid MessageType");
            }

            if (initialMetadataKeyMaterial != null)             // initialMetadataKeyMaterial is only for resend requests
            {
                messageMetadata.SenderPublicKey = this.profileViewModel.PublicKey;
                message.MetaCipher = this.ixdsCryptoService.DefaultEncrypt(messageMetadata.SerializeCore(), initialMetadataKeyMaterial);
            }
            else
            {
                messageMetadata.SenderPublicKey = this.ixdsCryptoService.GetRandom(32).Result.X;
                message.MetaCipher = this.ixdsCryptoService.DefaultEncrypt(messageMetadata.SerializeCore(), keyMaterial);
            }

            if (!message.MessageType.IsReceipt())
            {
                message.EncryptedE2EEncryptionKey = this.ixdsCryptoService.DefaultEncrypt(keyMaterial.GetBytes(), this.ixdsCryptoService.SymmetricKeyRepository.GetMasterRandomKey());
            }
        }
示例#4
0
        /// <summary>
        /// Fully decrypts a message of all types, both control and content messages (including images), if an end-to-en-encryption key can be determined.
        /// </summary>
        async Task <Message> TryDecryptMessageToFindSenderEnryptDecryptionKeyAndSaveIt(XMessage xmessage)
        {
            try
            {
                IReadOnlyList <Identity> contacts = await this.repo.GetAllContacts();

                var decryptedMessage = new Message
                {
                    // XMessage fields
                    RecipientId = "1", // drop my Id

                    TextCipher  = xmessage.TextCipher,
                    ImageCipher = xmessage.ImageCipher,

                    DynamicPublicKey   = xmessage.DynamicPublicKey,
                    DynamicPublicKeyId = xmessage.DynamicPublicKeyId,
                    PrivateKeyHint     = xmessage.PrivateKeyHint,

                    LocalMessageState = LocalMessageState.JustReceived,
                    SendMessageState  = SendMessageState.None,
                    Side = MessageSide.You,

                    // This is what we try to decrypt here
                    MessageType = MessageType.None,
                    SenderId    = null,
                };


                Response <CipherV2> decodeMetaResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.MetaCipher, null);
                if (!decodeMetaResponse.IsSuccess)
                {
                    return(null); // garbled, no chance, ignore!
                }
                foreach (Identity identity in contacts)
                {
                    GetE2EDecryptionKeyResult getE2EDecryptionKeyResult = await this.e2eRatchet.GetEndToEndDecryptionKeyAsync(identity.Id, xmessage.DynamicPublicKey, xmessage.PrivateKeyHint);

                    if (getE2EDecryptionKeyResult.E2EDecryptionKeyType == E2EDecryptionKeyType.UnavailableDynamicPrivateKey)
                    {
                        continue; // there was a privateKeyHint != 0 in the message, but the dynamic private key was not in the ratchet for user in the loop
                    }
                    KeyMaterial64 keyMaterial64 = getE2EDecryptionKeyResult.E2EDecryptionKeyMaterial;

                    var decryptMetaResponse = this.ixdsCryptoService.BinaryDecrypt(decodeMetaResponse.Result, keyMaterial64, null);
                    if (!decryptMetaResponse.IsSuccess)
                    {
                        continue;
                    }

                    await this.e2eRatchet.SaveIncomingDynamicPublicKeyOnSuccessfulDecryptionAsync(identity.Id, xmessage.DynamicPublicKey,
                                                                                                  xmessage.DynamicPublicKeyId);

                    XMessageMetaData metadata = decryptMetaResponse.Result.GetBytes().DeserializeMessageMetadata();

                    decryptedMessage.SenderId             = identity.Id;
                    decryptedMessage.MessageType          = metadata.MessageType;
                    decryptedMessage.SenderLocalMessageId = metadata.SenderLocalMessageId.ToString();


                    if (decryptedMessage.MessageType.IsReceipt())
                    {
                        return(decryptedMessage);
                    }

                    if (decryptedMessage.MessageType.IsContent())
                    {
                        if (decryptedMessage.MessageType == MessageType.Text || decryptedMessage.MessageType == MessageType.TextAndMedia || decryptedMessage.MessageType == MessageType.File)
                        {
                            // if the message has text, decrypt all text
                            var decodeTextResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.TextCipher, null);
                            if (!decodeTextResponse.IsSuccess)
                            {
                                return(null); // something is wrong, should have worked
                            }
                            var decrpytTextResponse = this.ixdsCryptoService.Decrypt(decodeTextResponse.Result, keyMaterial64, null);
                            if (!decrpytTextResponse.IsSuccess)
                            {
                                return(null); // something is wrong, should have worked
                            }
                            decryptedMessage.ThreadText = decrpytTextResponse.Result.Text;
                        }

                        if (decryptedMessage.MessageType == MessageType.Media || decryptedMessage.MessageType == MessageType.TextAndMedia || decryptedMessage.MessageType == MessageType.File)
                        {
                            // if the message has image content, decrypt the image content
                            var decodeImageResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.ImageCipher, null);
                            if (!decodeImageResponse.IsSuccess)
                            {
                                return(null); // something is wrong, should have worked
                            }
                            var decrpytImageResponse = this.ixdsCryptoService.BinaryDecrypt(decodeImageResponse.Result, keyMaterial64, null);
                            if (!decrpytImageResponse.IsSuccess)
                            {
                                return(null); // something is wrong, should have worked
                            }
                            decryptedMessage.ThreadMedia = decrpytImageResponse.Result.GetBytes();
                        }

                        decryptedMessage.EncryptedE2EEncryptionKey = this.ixdsCryptoService.DefaultEncrypt(keyMaterial64.GetBytes(), this.ixdsCryptoService.SymmetricKeyRepository.GetMasterRandomKey());
                        decryptedMessage.LocalMessageState         = LocalMessageState.Integrated;
                        await this.repo.AddMessage(decryptedMessage);
                    }
                    else
                    {
                        throw new Exception($"Invalid MessageType {decryptedMessage.MessageType}");
                    }

                    return(decryptedMessage);
                } // foreach



                // If we are here, assume it's a new contact's message or RESENT message:
                CipherV2      encryptedMetaData          = decodeMetaResponse.Result;
                KeyMaterial64 initialKey                 = this.e2eRatchet.GetInitialE2EDecryptionKey(xmessage.DynamicPublicKey);
                var           decryptInitialMetaResponse = this.ixdsCryptoService.BinaryDecrypt(encryptedMetaData, initialKey, null);
                if (decryptInitialMetaResponse.IsSuccess)
                {
                    XMessageMetaData initialMessageMetadata =
                        decryptInitialMetaResponse.Result.GetBytes().DeserializeMessageMetadata();
                    var incomingPublicKey = initialMessageMetadata.SenderPublicKey;

                    // If we received several resent messages, the first from that incoming contact has already produced that contact:
                    var  contact           = contacts.SingleOrDefault(c => ByteArrays.AreAllBytesEqual(c.StaticPublicKey, incomingPublicKey));
                    bool isIncomingContact = false;

                    if (contact == null)
                    {
                        // Create new contact and save it to make the ratchet work normally
                        isIncomingContact = true;
                        var date            = DateTime.UtcNow;
                        var incomingContact = new Identity
                        {
                            Id = Guid.NewGuid().ToString(),
                            StaticPublicKey = incomingPublicKey,
                            ContactState    = ContactState.Valid,
                            Name            = "Anonymous",
                            FirstSeenUtc    = date,
                            LastSeenUtc     = date
                        };

                        await this.repo.AddContact(incomingContact);

                        contact = incomingContact;
                    }



                    await this.e2eRatchet.SaveIncomingDynamicPublicKeyOnSuccessfulDecryptionAsync(contact.Id, xmessage.DynamicPublicKey,
                                                                                                  xmessage.DynamicPublicKeyId);

                    // add metadata to message
                    decryptedMessage.SenderId             = contact.Id;
                    decryptedMessage.MessageType          = initialMessageMetadata.MessageType;
                    decryptedMessage.SenderLocalMessageId = initialMessageMetadata.SenderLocalMessageId.ToString();

                    if (decryptedMessage.MessageType.IsContent())
                    {
                        var success = true;

                        GetE2EDecryptionKeyResult getE2EDecryptionKeyResult = await this.e2eRatchet.GetEndToEndDecryptionKeyAsync(contact.Id, xmessage.DynamicPublicKey, xmessage.PrivateKeyHint);

                        KeyMaterial64 keyMaterial64 = getE2EDecryptionKeyResult.E2EDecryptionKeyMaterial;
                        await this.e2eRatchet.GetEndToEndDecryptionKeyAsync(contact.Id, xmessage.DynamicPublicKey, xmessage.PrivateKeyHint);

                        if (decryptedMessage.MessageType == MessageType.Text || decryptedMessage.MessageType == MessageType.TextAndMedia)
                        {
                            // if the message has text, decrypt all text
                            var decodeTextResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.TextCipher, null);
                            if (!decodeTextResponse.IsSuccess)
                            {
                                success = false; // something is wrong, should have worked
                            }
                            else
                            {
                                var decrpytTextResponse = this.ixdsCryptoService.Decrypt(decodeTextResponse.Result, keyMaterial64, null);
                                if (!decrpytTextResponse.IsSuccess)
                                {
                                    success = false; // something is wrong, should have worked
                                }
                                else
                                {
                                    decryptedMessage.ThreadText = decrpytTextResponse.Result.Text;
                                }
                            }
                        }

                        if (decryptedMessage.MessageType == MessageType.Media || decryptedMessage.MessageType == MessageType.TextAndMedia)
                        {
                            // if the message has image content, decrypt the image content
                            var decodeImageResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.ImageCipher, null);
                            if (!decodeImageResponse.IsSuccess)
                            {
                                success = false; // something is wrong, should have worked
                            }
                            else
                            {
                                var decrpytImageResponse = this.ixdsCryptoService.BinaryDecrypt(decodeImageResponse.Result, keyMaterial64, null);
                                if (!decrpytImageResponse.IsSuccess)
                                {
                                    success = false; // something is wrong, should have worked
                                }
                                else
                                {
                                    decryptedMessage.ThreadMedia = decrpytImageResponse.Result.GetBytes();
                                }
                            }
                        }

                        if (success)
                        {
                            decryptedMessage.EncryptedE2EEncryptionKey = this.ixdsCryptoService.DefaultEncrypt(keyMaterial64.GetBytes(), this.ixdsCryptoService.SymmetricKeyRepository.GetMasterRandomKey());
                            decryptedMessage.LocalMessageState         = LocalMessageState.Integrated;
                            await this.contactListManager.ChatWorker_ContactUpdateReceived(null, contact.Id);

                            await this.repo.AddMessage(decryptedMessage);

                            return(decryptedMessage);
                        }
                        else
                        {
                            if (isIncomingContact)
                            {
                                await this.repo.DeleteContacts(new[] { contact.Id }); // delete the just incoming contact if the was an error anyway.
                            }

                            return(null);
                        }
                    }
                    else
                    {
                        return(null);
                    }
                }

                // nope, resend request (I hope we don't loop here!)
                string networkPayloadHash = NetworkPayloadHash.ComputeAsGuidString(xmessage.SerializedPayload);
                if (!this._resendsRequested.Contains(networkPayloadHash))
                {
                    var response = await this.chatClient.UploadResendRequest(new XResendRequest { Id = networkPayloadHash, RecipientId = null });

                    if (response.IsSuccess)
                    {
                        this._resendsRequested.Add(networkPayloadHash);
                    }
                }
            }
            catch (Exception e)
            {
                this.logger.LogError(e.Message);
            }

            return(null);
        }