Пример #1
0
        public async Task sendOmemoMessageAsync(OmemoEncryptedMessage msg, string srcBareJid, string dstBareJid, bool trustedSrcKeysOnly, bool trustedDstKeysOnly)
        {
            // Check if already trying to build a new session:
            if (MESSAGE_CACHE.ContainsKey(dstBareJid))
            {
                MESSAGE_CACHE[dstBareJid].Item1.Add(msg);
            }
            else
            {
                // If not start a new session build helper:
                OmemoSessionBuildHelper sessionHelper = new OmemoSessionBuildHelper(srcBareJid, dstBareJid, CONNECTION, this, trustedSrcKeysOnly, trustedDstKeysOnly);
                MESSAGE_CACHE[dstBareJid] = new Tuple <List <OmemoEncryptedMessage>, OmemoSessionBuildHelper>(new List <OmemoEncryptedMessage>(), sessionHelper);
                MESSAGE_CACHE[dstBareJid].Item1.Add(msg);
                Tuple <OmemoDeviceListSubscriptionState, DateTime> subscription = OMEMO_STORAGE.LoadDeviceListSubscription(dstBareJid);

                OmemoSessionBuildResult result = await sessionHelper.buildSessionAsync(subscription.Item1);

                if (result.SUCCESS)
                {
                    OMEMO_SESSIONS[dstBareJid] = result.SESSIONS;
                    await sendAllOutstandingMessagesAsync(result.SESSIONS);
                }
                else
                {
                    OmemoSessionBuildErrorEventArgs args = new OmemoSessionBuildErrorEventArgs(dstBareJid, result.ERROR, MESSAGE_CACHE[dstBareJid]?.Item1 ?? new List <OmemoEncryptedMessage>());
                    MESSAGE_CACHE.Remove(dstBareJid);
                    CONNECTION.OnOmemoSessionBuildError(args);
                    Logger.Error("Failed to build OMEMO session for: " + dstBareJid + " with: " + result.ERROR);
                }
            }
        }
Пример #2
0
        public async Task decryptOmemoEncryptedMessageAsync(OmemoEncryptedMessage msg, bool trustedKeysOnly)
        {
            XMPPAccount          account         = CONNECTION.account;
            OmemoProtocolAddress receiverAddress = new OmemoProtocolAddress(account.getBareJid(), account.omemoDeviceId);
            // Try to decrypt the message, in case no exception occurred, everything went fine:
            OmemoDecryptionContext decryptCtx = new OmemoDecryptionContext(receiverAddress, account.omemoIdentityKey, account.omemoSignedPreKey, account.OMEMO_PRE_KEYS, trustedKeysOnly, OMEMO_STORAGE);

            msg.decrypt(decryptCtx);
            Debug.Assert(!msg.ENCRYPTED);
            Logger.Debug("Successfully decrypted an " + nameof(OmemoEncryptedMessage) + " for '" + receiverAddress.BARE_JID + "'.");

            // Republish bundle information in case the message is a key exchange message and used a PreKey:
            if (decryptCtx.keyExchange)
            {
                Logger.Info("Received a OMEMO key exchange message. Republishing bundle for '" + receiverAddress.BARE_JID + "'...");
                PreKeyModel newPreKey = decryptCtx.STORAGE.ReplaceOmemoPreKey(decryptCtx.usedPreKey);
                account.OMEMO_PRE_KEYS.Remove(decryptCtx.usedPreKey);
                account.OMEMO_PRE_KEYS.Add(newPreKey);
                await announceBundleInfoAsync();

                Logger.Info("Bundle for '" + receiverAddress.BARE_JID + "' republished.");
            }

            // Reply with an empty message to confirm the successful key exchange:
            // TODO: This is no good way, since there it would be possible to stalk people without them knowing.
        }
Пример #3
0
        private async Task SendChatMessageAsync(ChatDataTemplate chat, string message)
        {
            MessageMessage toSendMsg;

            string from            = chat.Client.dbAccount.fullJid.FullJid();
            string to              = chat.Chat.bareJid;
            string chatType        = chat.Chat.chatType == ChatType.CHAT ? MessageMessage.TYPE_CHAT : MessageMessage.TYPE_GROUPCHAT;
            bool   reciptRequested = true;

            if (chat.Chat.omemoInfo.enabled)
            {
                if (chat.Chat.chatType == ChatType.CHAT)
                {
                    toSendMsg = new OmemoEncryptedMessage(from, to, message, chatType, reciptRequested);
                }
                else
                {
                    // ToDo: Add MUC OMEMO support
                    throw new NotImplementedException("Sending encrypted messages for MUC is not supported right now!");
                }
            }
            else
            {
                toSendMsg = chat.Chat.chatType == ChatType.CHAT
                    ? new MessageMessage(from, to, message, chatType, reciptRequested)
                    : new MessageMessage(from, to, message, chatType, chat.Chat.muc.nickname, reciptRequested);
            }

            // Create a copy for the DB:
            ChatMessageModel toSendMsgDB = new ChatMessageModel(toSendMsg, chat.Chat)
            {
                state = toSendMsg is OmemoEncryptedMessage ? MessageState.TO_ENCRYPT : MessageState.SENDING
            };

            // Update chat last active:
            chat.Chat.lastActive = DateTime.Now;

            // Update DB:
            chat.Chat.Update();
            await DataCache.INSTANCE.AddChatMessageAsync(toSendMsgDB, chat.Chat);

            // Set the chat message id for later identification:
            toSendMsg.chatMessageId = toSendMsgDB.id;

            // Send the message:
            if (toSendMsg is OmemoEncryptedMessage toSendOmemoMsg)
            {
                await chat.Client.xmppClient.sendOmemoMessageAsync(toSendOmemoMsg, chat.Chat.bareJid, chat.Client.dbAccount.bareJid, chat.Client.dbAccount.omemoInfo.trustedKeysOnly, chat.Chat.omemoInfo.trustedKeysOnly);
            }
            else
            {
                await chat.Client.xmppClient.SendAsync(toSendMsg);
            }
        }
Пример #4
0
 public async Task sendOmemoMessageAsync(OmemoEncryptedMessage msg, string chatJid, string accountJid, bool trustedSrcKeysOnly, bool trustedDstKeysOnly)
 {
     if (connection.omemoHelper is null)
     {
         OmemoSessionBuildError?.Invoke(this, new OmemoSessionBuildErrorEventArgs(chatJid, Network.XML.Messages.XEP_0384.Session.OmemoSessionBuildError.KEY_ERROR, new List <OmemoEncryptedMessage> {
             msg
         }));
         Logger.Error("Failed to send OMEMO message - OmemoHelper is null");
     }
     else
     {
         await connection.omemoHelper.sendOmemoMessageAsync(msg, accountJid, chatJid, trustedSrcKeysOnly, trustedDstKeysOnly);
     }
 }
Пример #5
0
        private async Task <bool> DecryptOmemoEncryptedMessageAsync(OmemoEncryptedMessage msg, bool trustedKeysOnly)
        {
            try
            {
                await client.xmppClient.connection.omemoHelper.decryptOmemoEncryptedMessageAsync(msg, trustedKeysOnly);

                return(true);
            }
            catch (Exception e)
            {
                Logger.Error("Failed to decrypt " + nameof(OmemoEncryptedMessage) + " for '" + client.dbAccount.bareJid + "' with: ", e);
                return(false);
            }
        }
Пример #6
0
        public async Task decryptOmemoEncryptedMessageAsync(OmemoEncryptedMessage msg, bool trustedKeysOnly)
        {
            XMPPAccount          account         = CONNECTION.account;
            OmemoProtocolAddress receiverAddress = new OmemoProtocolAddress(account.getBareJid(), account.omemoDeviceId);
            // Try to decrypt the message, in case no exception occurred, everything went fine:
            OmemoDecryptionContext decryptCtx = new OmemoDecryptionContext(receiverAddress, account.omemoIdentityKey, account.omemoSignedPreKey, account.OMEMO_PRE_KEYS, trustedKeysOnly, OMEMO_STORAGE);

            msg.decrypt(decryptCtx);
            Debug.Assert(!msg.ENCRYPTED);
            Logger.Debug("Successfully decrypted an " + nameof(OmemoEncryptedMessage) + " for '" + receiverAddress.BARE_JID + "'.");

            // Republish bundle information in case the message is a key exchange message and used a PreKey:
            if (decryptCtx.keyExchange)
            {
                Logger.Info("Received a OMEMO key exchange message. Republishing bundle for '" + receiverAddress.BARE_JID + "'...");
                PreKeyModel newPreKey = decryptCtx.STORAGE.ReplaceOmemoPreKey(decryptCtx.usedPreKey);
                account.OMEMO_PRE_KEYS.Remove(decryptCtx.usedPreKey);
                account.OMEMO_PRE_KEYS.Add(newPreKey);
                await announceBundleInfoAsync();

                Logger.Info("Bundle for '" + receiverAddress.BARE_JID + "' republished.");

                // Reply with an empty message to confirm the successful key exchange:
                // TODO: This is no good way, since there it would be possible to stalk people without them knowing.
                OmemoEncryptedMessage reply       = new OmemoEncryptedMessage(msg.getTo(), msg.getFrom(), null, msg.TYPE, false);
                OmemoDeviceGroup      deviceGroup = new OmemoDeviceGroup(decryptCtx.senderAddress.BARE_JID);
                deviceGroup.SESSIONS.Add(decryptCtx.senderAddress.DEVICE_ID, decryptCtx.session);
                try
                {
                    reply.encrypt(CONNECTION.account.omemoDeviceId, CONNECTION.account.omemoIdentityKey, OMEMO_STORAGE, new List <OmemoDeviceGroup> {
                        deviceGroup
                    });
                    await CONNECTION.SendAsync(reply);
                }
                catch (Exception e)
                {
                    Logger.Error("[OMEMO HELPER] Failed to encrypt and the empty OMEMO message reply with: ", e);
                }
                Logger.Info($"Send an empty OMEMO message to confirm the successful key exchange with '{msg.getFrom()}'.");
            }
        }
Пример #7
0
        public void Test_Omemo_Send_Receive_Send()
        {
            // Generate Alices keys:
            IdentityKeyPairModel aliceIdentKey     = KeyHelper.GenerateIdentityKeyPair();
            List <PreKeyModel>   alicePreKeys      = KeyHelper.GeneratePreKeys(0, 1);
            SignedPreKeyModel    aliceSignedPreKey = KeyHelper.GenerateSignedPreKey(0, aliceIdentKey.privKey);
            Bundle aliceBundle = new Bundle()
            {
                identityKey     = aliceIdentKey.pubKey,
                preKeys         = alicePreKeys.Select(key => new PreKeyModel(null, key.pubKey, key.keyId)).ToList(),
                preKeySignature = aliceSignedPreKey.signature,
                signedPreKey    = aliceSignedPreKey.preKey.pubKey,
                signedPreKeyId  = aliceSignedPreKey.preKey.keyId
            };
            InMemmoryOmemoStorage aliceStorage = new InMemmoryOmemoStorage();
            DoubleRachet          aliceRachet  = new DoubleRachet(aliceIdentKey);

            // Generate Bobs keys:
            IdentityKeyPairModel bobIdentKey     = KeyHelper.GenerateIdentityKeyPair();
            List <PreKeyModel>   bobPreKeys      = KeyHelper.GeneratePreKeys(0, 1);
            SignedPreKeyModel    bobSignedPreKey = KeyHelper.GenerateSignedPreKey(0, bobIdentKey.privKey);
            Bundle bobBundle = new Bundle()
            {
                identityKey     = bobIdentKey.pubKey,
                preKeys         = bobPreKeys.Select(key => new PreKeyModel(null, key.pubKey, key.keyId)).ToList(),
                preKeySignature = bobSignedPreKey.signature,
                signedPreKey    = bobSignedPreKey.preKey.pubKey,
                signedPreKeyId  = bobSignedPreKey.preKey.keyId
            };
            InMemmoryOmemoStorage bobStorage = new InMemmoryOmemoStorage();
            DoubleRachet          bobRachet  = new DoubleRachet(bobIdentKey);

            //-----------------OMEOMO Session Building:-----------------
            MessageParser2 parser = new MessageParser2();

            string deviceListMsg            = GetBobsDeviceListMsg();
            List <AbstractMessage> messages = parser.parseMessages(ref deviceListMsg);

            Assert.IsTrue(messages.Count == 1);
            Assert.IsTrue(messages[0] is OmemoDeviceListResultMessage);
            OmemoDeviceListResultMessage devList = messages[0] as OmemoDeviceListResultMessage;

            uint selectedBobDeviceId = devList.DEVICES.getRandomDeviceId();

            Assert.IsTrue(selectedBobDeviceId == BOB_ADDRESS.DEVICE_ID);

            // Alice builds a session to Bob:
            string bundleInfoMsg = GetBobsBundleInfoMsg(bobBundle);

            messages = parser.parseMessages(ref bundleInfoMsg);
            Assert.IsTrue(messages.Count == 1);
            Assert.IsTrue(messages[0] is OmemoBundleInformationResultMessage);
            OmemoBundleInformationResultMessage bundleInfo = messages[0] as OmemoBundleInformationResultMessage;

            Assert.IsTrue(bundleInfo.BUNDLE_INFO.deviceId == BOB_ADDRESS.DEVICE_ID);
            aliceStorage.StoreFingerprint(new OmemoFingerprint(bobBundle.identityKey, BOB_ADDRESS));

            // Encrypt Message 1:
            string msg1 = "Hello OMEMO";
            OmemoEncryptedMessage   omemoEncryptedMessage = new OmemoEncryptedMessage(ALICE_ADDRESS.BARE_JID, BOB_ADDRESS.BARE_JID, msg1, MessageMessage.TYPE_CHAT, false);
            List <OmemoDeviceGroup> bobDevices            = new List <OmemoDeviceGroup>();
            OmemoDeviceGroup        bobDeviceGroup        = new OmemoDeviceGroup(BOB_ADDRESS.BARE_JID);

            bobDeviceGroup.SESSIONS[BOB_ADDRESS.DEVICE_ID] = new OmemoSessionModel(bobBundle, 0, aliceIdentKey);
            bobDevices.Add(bobDeviceGroup);
            omemoEncryptedMessage.encrypt(ALICE_ADDRESS.DEVICE_ID, aliceIdentKey, aliceStorage, bobDevices);
            Assert.IsTrue(omemoEncryptedMessage.ENCRYPTED);
            Assert.IsNotNull(aliceStorage.LoadFingerprint(BOB_ADDRESS));
            Assert.IsNotNull(aliceStorage.LoadSession(BOB_ADDRESS));

            // Decrypt Message 1:
            // Throws an exception in case something goes wrong:
            OmemoDecryptionContext decryptCtx1 = new OmemoDecryptionContext(BOB_ADDRESS, bobIdentKey, bobSignedPreKey, bobPreKeys, false, bobStorage);

            omemoEncryptedMessage.decrypt(decryptCtx1);
            Assert.AreEqual(msg1, omemoEncryptedMessage.MESSAGE);
            Assert.IsFalse(omemoEncryptedMessage.IS_PURE_KEY_EXCHANGE_MESSAGE);
            Assert.IsFalse(omemoEncryptedMessage.ENCRYPTED);
            Assert.IsNotNull(bobStorage.LoadFingerprint(ALICE_ADDRESS));
            Assert.IsNotNull(bobStorage.LoadSession(ALICE_ADDRESS));

            // Encrypt Message 2:
            string msg2 = "Hello OMEMO 2";
            OmemoEncryptedMessage   omemoEncryptedMessage2 = new OmemoEncryptedMessage(BOB_ADDRESS.BARE_JID, ALICE_ADDRESS.BARE_JID, msg2, MessageMessage.TYPE_CHAT, false);
            List <OmemoDeviceGroup> aliceDevices           = new List <OmemoDeviceGroup>();
            OmemoDeviceGroup        aliceDeviceGroup       = new OmemoDeviceGroup(ALICE_ADDRESS.BARE_JID);

            aliceDeviceGroup.SESSIONS[ALICE_ADDRESS.DEVICE_ID] = bobStorage.LoadSession(ALICE_ADDRESS);
            aliceDevices.Add(aliceDeviceGroup);
            omemoEncryptedMessage2.encrypt(BOB_ADDRESS.DEVICE_ID, bobIdentKey, bobStorage, aliceDevices);
            Assert.IsTrue(omemoEncryptedMessage2.ENCRYPTED);

            // Decrypt Message 2:
            // Throws an exception in case something goes wrong:
            OmemoDecryptionContext decryptCtx2 = new OmemoDecryptionContext(ALICE_ADDRESS, aliceIdentKey, aliceSignedPreKey, alicePreKeys, false, aliceStorage);

            omemoEncryptedMessage2.decrypt(decryptCtx2);
            Assert.AreEqual(msg2, omemoEncryptedMessage2.MESSAGE);
            Assert.IsFalse(omemoEncryptedMessage2.IS_PURE_KEY_EXCHANGE_MESSAGE);
            Assert.IsFalse(omemoEncryptedMessage2.ENCRYPTED);

            // Encrypt Message 3:
            string msg3 = "Hello OMEMO 3";
            OmemoEncryptedMessage omemoEncryptedMessage3 = new OmemoEncryptedMessage(ALICE_ADDRESS.BARE_JID, BOB_ADDRESS.BARE_JID, msg3, MessageMessage.TYPE_CHAT, false);

            bobDeviceGroup.SESSIONS[BOB_ADDRESS.DEVICE_ID] = aliceStorage.LoadSession(BOB_ADDRESS);
            omemoEncryptedMessage3.encrypt(ALICE_ADDRESS.DEVICE_ID, aliceIdentKey, aliceStorage, bobDevices);
            Assert.IsFalse(omemoEncryptedMessage3.IS_PURE_KEY_EXCHANGE_MESSAGE);
            Assert.IsTrue(omemoEncryptedMessage3.ENCRYPTED);
            Assert.IsNotNull(aliceStorage.LoadFingerprint(BOB_ADDRESS));
            Assert.IsNotNull(aliceStorage.LoadSession(BOB_ADDRESS));

            // Decrypt Message 3:
            // Throws an exception in case something goes wrong:
            OmemoDecryptionContext decryptCtx3 = new OmemoDecryptionContext(BOB_ADDRESS, bobIdentKey, bobSignedPreKey, bobPreKeys, false, bobStorage);

            omemoEncryptedMessage3.decrypt(decryptCtx3);
            Assert.AreEqual(msg3, omemoEncryptedMessage3.MESSAGE);
            Assert.IsFalse(omemoEncryptedMessage3.IS_PURE_KEY_EXCHANGE_MESSAGE);
            Assert.IsFalse(omemoEncryptedMessage3.ENCRYPTED);
            Assert.IsNotNull(bobStorage.LoadFingerprint(ALICE_ADDRESS));
            Assert.IsNotNull(bobStorage.LoadSession(ALICE_ADDRESS));
        }
Пример #8
0
        private async Task SendChatMessageAsync(ChatModel chat, string message)
        {
            string fromFullJid;

            using (MainDbContext ctx = new MainDbContext())
            {
                fromFullJid = ctx.Accounts.Where(a => string.Equals(a.bareJid, chat.accountBareJid)).Select(a => a.fullJid.FullJid()).FirstOrDefault();
            }

            if (fromFullJid is null)
            {
                Logger.Error($"Failed to send message from background. Account '{chat.bareJid}' does not exist.");
                return;
            }

            string to              = chat.bareJid;
            string chatType        = chat.chatType == ChatType.CHAT ? MessageMessage.TYPE_CHAT : MessageMessage.TYPE_GROUPCHAT;
            bool   reciptRequested = true;

            MessageMessage toSendMsg;

            if (chat.omemoInfo.enabled)
            {
                if (chat.chatType == ChatType.CHAT)
                {
                    toSendMsg = new OmemoEncryptedMessage(fromFullJid, to, message, chatType, reciptRequested);
                }
                else
                {
                    // ToDo: Add MUC OMEMO support
                    throw new NotImplementedException("Sending encrypted messages for MUC is not supported right now!");
                }
            }
            else if (chat.chatType == ChatType.CHAT)
            {
                toSendMsg = new MessageMessage(fromFullJid, to, message, chatType, reciptRequested);
            }
            else
            {
                toSendMsg = new MessageMessage(fromFullJid, to, message, chatType, chat.muc.nickname, reciptRequested);
            }

            // Create a copy for the DB:
            ChatMessageModel toSendMsgDB = new ChatMessageModel(toSendMsg, chat)
            {
                state = toSendMsg is OmemoEncryptedMessage ? MessageState.TO_ENCRYPT : MessageState.SENDING
            };

            // Set the image path and file name:
            if (toSendMsgDB.isImage)
            {
                await DataCache.PrepareImageModelPathAndNameAsync(toSendMsgDB.image);
            }

            // Set the chat message id for later identification:
            toSendMsg.chatMessageId = toSendMsgDB.id;

            // Update chat last active:
            chat.lastActive = DateTime.Now;

            // Update DB:
            chat.Update();
            DataCache.INSTANCE.AddChatMessage(toSendMsgDB, chat);

            Logger.Info("Added to send message in background");

            if (isRunning)
            {
                Client client = ConnectionHandler.INSTANCE.GetClient(chat.bareJid).client;
                if (client is null)
                {
                    Logger.Error($"Failed to send message from background. Client '{chat.bareJid}' does not exist.");
                }
                // Send the message:
                else if (toSendMsg is OmemoEncryptedMessage toSendOmemoMsg)
                {
                    await client.xmppClient.sendOmemoMessageAsync(toSendOmemoMsg, chat.bareJid, client.dbAccount.bareJid, client.dbAccount.omemoInfo.trustedKeysOnly, chat.omemoInfo.trustedKeysOnly);
                }
                else
                {
                    await client.xmppClient.SendAsync(toSendMsg);
                }
            }
            else
            {
                ToastHelper.ShowWillBeSendLaterToast(chat);
            }
        }