Example #1
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.
        }
Example #2
0
 //--------------------------------------------------------Constructor:----------------------------------------------------------------\\
 #region --Constructors--
 public OmemoFingerprint(ECPubKeyModel identityKey, OmemoProtocolAddress address, DateTime lastSeen, bool trusted)
 {
     IDENTITY_KEY  = identityKey;
     ADDRESS       = address;
     this.lastSeen = lastSeen;
     this.trusted  = trusted;
 }
 //--------------------------------------------------------Constructor:----------------------------------------------------------------\\
 #region --Constructors--
 public OmemoDecryptionContext(OmemoProtocolAddress receiverAddress, IdentityKeyPairModel receiverIdentityKey, SignedPreKeyModel receiverSignedPreKey, IEnumerable <PreKeyModel> receiverPreKeys, bool trustedKeysOnly, IExtendedOmemoStorage storage)
 {
     RECEIVER_ADDRESS        = receiverAddress;
     RECEIVER_IDENTITY_KEY   = receiverIdentityKey;
     RECEIVER_SIGNED_PRE_KEY = receiverSignedPreKey;
     RECEIVER_PRE_KEYS       = receiverPreKeys;
     TRUSTED_KEYS_ONLY       = trustedKeysOnly;
     STORAGE = storage;
 }
Example #4
0
        public void StoreSession(OmemoProtocolAddress address, OmemoSessionModel session)
        {
            OmemoDeviceModel device;

            if (string.Equals(address.BARE_JID, dbAccount.bareJid))
            {
                device = dbAccount.omemoInfo.devices.Where(d => address.DEVICE_ID == d.deviceId).FirstOrDefault();
            }
            else
            {
                ChatModel chat;
                using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
                {
                    chat = DataCache.INSTANCE.GetChat(dbAccount.bareJid, address.BARE_JID, semaLock);
                }
                if (chat is null)
                {
                    throw new InvalidOperationException("Failed to store session. Chat '" + address.BARE_JID + "' does not exist.");
                }
                device = chat.omemoInfo.devices.Where(d => d.deviceId == address.DEVICE_ID).FirstOrDefault();
            }

            if (device is null)
            {
                throw new InvalidOperationException("Failed to store session. Device '" + address.ToString() + "' does not exist.");
            }
            if (device.session is null)
            {
                device.session = session;
            }

            bool newSession = device.session.id != session.id;
            OmemoSessionModel oldSession = null;

            if (newSession)
            {
                oldSession     = device.session;
                device.session = session;
            }

            device.Update();

            // Remove the old session:
            if (newSession)
            {
                using (MainDbContext ctx = new MainDbContext())
                {
                    ctx.Remove(oldSession);
                }
            }
        }
Example #5
0
 public OmemoSessionModel LoadSession(OmemoProtocolAddress address)
 {
     if (string.Equals(address.BARE_JID, dbAccount.bareJid))
     {
         return(dbAccount.omemoInfo.devices.Where(d => d.deviceId == address.DEVICE_ID).FirstOrDefault()?.session);
     }
     else
     {
         using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
         {
             return(DataCache.INSTANCE.GetChat(dbAccount.bareJid, address.BARE_JID, semaLock)?.omemoInfo?.devices?.Where(d => d.deviceId == address.DEVICE_ID).FirstOrDefault()?.session);
         }
     }
 }
Example #6
0
        private async Task buildSessionForDevicesAsync(OmemoDeviceGroup deviceGroup, IList <OmemoProtocolAddress> devices)
        {
            if (devices.Count <= 0)
            {
                return;
            }
            OmemoProtocolAddress device = devices[0];

            devices.RemoveAt(0);

            OmemoFingerprint fingerprint = OMEMO_HELPER.OMEMO_STORAGE.LoadFingerprint(device);
            // Check if there exists already a session for this device:
            OmemoSessionModel session = OMEMO_HELPER.OMEMO_STORAGE.LoadSession(device);

            if (session is null)
            {
                // Try to build a new session by requesting the devices bundle information:
                OmemoBundleInformationResultMessage bundleMsg = await requestBundleInformationAsync(device);

                if (!(bundleMsg is null) && !(bundleMsg.BUNDLE_INFO.bundle is null))
                {
                    int preKeyIndex = bundleMsg.BUNDLE_INFO.bundle.GetRandomPreKeyIndex();
                    session = new OmemoSessionModel(bundleMsg.BUNDLE_INFO.bundle, preKeyIndex, CONNECTION.account.omemoIdentityKey);

                    // Validate fingerprints:
                    if (fingerprint is null)
                    {
                        fingerprint = new OmemoFingerprint(bundleMsg.BUNDLE_INFO.bundle.identityKey, device);
                        OMEMO_HELPER.OMEMO_STORAGE.StoreFingerprint(fingerprint);
                    }
                    else
                    {
                        OmemoFingerprint receivedFingerprint = new OmemoFingerprint(bundleMsg.BUNDLE_INFO.bundle.identityKey, device);
                        // Make sure the fingerprint did not change or somebody is performing an attack:
                        if (!fingerprint.checkIdentityKey(receivedFingerprint.IDENTITY_KEY))
                        {
                            Logger.Warn("[OmemoSessionBuildHelper] Unable to establish session with " + device.ToString() + " - other fingerprint received than stored locally.");
                            await buildSessionForDevicesAsync(deviceGroup, devices);

                            return;
                        }
                    }
                }
                else
                {
                    Logger.Warn("[OmemoSessionBuildHelper] Unable to establish session with: " + device.ToString());
                }
            }
Example #7
0
        public OmemoFingerprint LoadFingerprint(OmemoProtocolAddress address)
        {
            OmemoFingerprintModel fingerprint;

            if (string.Equals(address.BARE_JID, dbAccount.bareJid))
            {
                fingerprint = dbAccount.omemoInfo.devices.Where(d => d.deviceId == address.DEVICE_ID).FirstOrDefault()?.fingerprint;
            }
            else
            {
                using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
                {
                    fingerprint = DataCache.INSTANCE.GetChat(dbAccount.bareJid, address.BARE_JID, semaLock)?.omemoInfo?.devices?.Where(d => d.deviceId == address.DEVICE_ID).FirstOrDefault()?.fingerprint;
                }
            }
            return(fingerprint?.ToOmemoFingerprint(address));
        }
Example #8
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()}'.");
            }
        }
Example #9
0
        /// <summary>
        /// [WIP]<para/>
        /// Parses XMPP IRIs and URIs based on RFC 5122 and returns the result.
        /// </summary>
        /// <param name="uri">The URI or IRI that should get parsed.</param>
        /// <returns>The URI or IRI result or null if an error occurred.</returns>
        public static IUriAction parse(Uri uri)
        {
            if (!string.IsNullOrEmpty(uri?.OriginalString))
            {
                if (string.Equals(uri.Scheme.ToLowerInvariant(), "xmpp"))
                {
                    string tmp = uri.OriginalString;

                    // 1. remove 'xmpp:'
                    tmp = tmp.Substring(5);

                    // 2. Authority
                    string authority = null;
                    if (tmp.StartsWith("//"))
                    {
                        tmp.Substring(2);
                        int authEnd = tmp.IndexOf('/');
                        if (authEnd < 0)
                        {
                            authEnd = tmp.IndexOf('?');
                            if (authEnd < 0)
                            {
                                authEnd = tmp.IndexOf('#');
                                if (authEnd < 0)
                                {
                                    authEnd = tmp.Length <= 0 ? 0 : tmp.Length - 1;
                                }
                            }
                            authority = tmp.Substring(0, authEnd);
                            tmp       = tmp.Substring(authEnd + 1);
                        }
                    }

                    if (string.Equals(uri.AbsolutePath, "iot-register"))
                    {
                        WwwFormUrlDecoder query = parseUriQuery(uri);
                        if (query is null)
                        {
                            return(null);
                        }

                        IWwwFormUrlDecoderEntry macEntry = query.FirstOrDefault(x => x.Name.StartsWith("mac"));
                        if (macEntry is null || string.IsNullOrEmpty(macEntry.Value))
                        {
                            Logger.Error("None or invalid IoT MAC address: " + uri.OriginalString);
                            return(null);
                        }
                        IWwwFormUrlDecoderEntry algoEntry = query.FirstOrDefault(x => x.Name.StartsWith("algo"));
                        if (algoEntry is null || string.IsNullOrEmpty(algoEntry.Value))
                        {
                            Logger.Error("None or invalid IoT key algorithm: " + uri.OriginalString);
                            return(null);
                        }
                        IWwwFormUrlDecoderEntry keyEntry = query.FirstOrDefault(x => x.Name.StartsWith("key"));
                        if (keyEntry is null || string.IsNullOrEmpty(keyEntry.Value))
                        {
                            Logger.Error("None or invalid IoT key: " + uri.OriginalString);
                            return(null);
                        }
                        return(new RegisterIoTUriAction(macEntry.Value, algoEntry.Value, keyEntry.Value));
                    }
                    else
                    {
                        // Check if is OMEMO fingerprint URI:
                        WwwFormUrlDecoder query = parseUriQuery(uri);
                        if (query is null)
                        {
                            return(null);
                        }

                        IWwwFormUrlDecoderEntry entry = query.FirstOrDefault(x => x.Name.StartsWith("omemo-sid-"));
                        if (!(entry is null))
                        {
                            ECPubKeyModel pubKey = null;
                            try
                            {
                                byte[] fingerprintBytes = SharedUtils.HexStringToByteArray(entry.Value);
                                pubKey = new ECPubKeyModel(fingerprintBytes);
                            }
                            catch (Exception e)
                            {
                                Logger.Error("Failed to parse XMPP URI. Parsing fingerprint failed: " + entry.Value, e);
                                return(null);
                            }

                            if (uint.TryParse(entry.Name.Replace("omemo-sid-", "").Trim(), out uint deviceId))
                            {
                                OmemoProtocolAddress address = new OmemoProtocolAddress(uri.LocalPath, deviceId);
                                return(new OmemoFingerprintUriAction(new OmemoFingerprint(pubKey, address)));
                            }
                            else
                            {
                                Logger.Warn("Failed to parse XMPP URI. Invalid device ID: " + entry.Name);
                            }
                        }
                    }
                }
                else
                {
                    Logger.Warn("Failed to parse XMPP URI. No 'xmpp' scheme.");
                }
            }
            return(null);
        }
Example #10
0
 public OmemoFingerprint LoadFingerprint(OmemoProtocolAddress address)
 {
     return(FINGERPRINTS.ContainsKey(address) ? FINGERPRINTS[address] : null);
 }
Example #11
0
 public void StoreSession(OmemoProtocolAddress address, OmemoSessionModel session)
 {
     SESSIONS[address] = session;
 }
Example #12
0
        //--------------------------------------------------------Constructor:----------------------------------------------------------------\\
        #region --Constructors--


        #endregion
        //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\
        #region --Set-, Get- Methods--


        #endregion
        //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\
        #region --Misc Methods (Public)--
        public OmemoSessionModel LoadSession(OmemoProtocolAddress address)
        {
            return(SESSIONS.ContainsKey(address) ? SESSIONS[address] : null);
        }
Example #13
0
 public OmemoFingerprint(ECPubKeyModel identityKey, OmemoProtocolAddress address) : this(identityKey, address, DateTime.MinValue, false)
 {
 }