Пример #1
0
        public void Test_OmemoKeyExchangeMessage()
        {
            Random rand = new Random();

            byte[] hmac = new byte[16];
            rand.NextBytes(hmac);
            byte[] sessionKey = new byte[32];
            rand.NextBytes(sessionKey);

            IdentityKeyPairModel identityKey     = KeyHelper.GenerateIdentityKeyPair();
            SignedPreKeyModel    signedPreKey    = KeyHelper.GenerateSignedPreKey(0, identityKey.privKey);
            OmemoSessionModel    refOmemoSession = new OmemoSessionModel(new Bundle()
            {
                identityKey     = identityKey.pubKey,
                preKeys         = KeyHelper.GeneratePreKeys(0, 10),
                preKeySignature = signedPreKey.signature,
                signedPreKey    = signedPreKey.preKey.pubKey,
                signedPreKeyId  = signedPreKey.preKey.keyId
            }, 0, KeyHelper.GenerateIdentityKeyPair())
            {
                ek = KeyHelper.GenerateKeyPair().pubKey
            };
            OmemoMessage refOmemoMessage = new OmemoMessage(refOmemoSession);
            OmemoAuthenticatedMessage refOmemoAuthenticatedMessage = new OmemoAuthenticatedMessage(new byte[16], refOmemoMessage.ToByteArray());
            OmemoKeyExchangeMessage   refOmemoKeyExchangeMessage   = new OmemoKeyExchangeMessage((uint)rand.Next(), (uint)rand.Next(), KeyHelper.GenerateKeyPair().pubKey, KeyHelper.GenerateKeyPair().pubKey, refOmemoAuthenticatedMessage);

            byte[] data = refOmemoKeyExchangeMessage.ToByteArray();
            Assert.IsTrue(data.Length > 0);

            OmemoKeyExchangeMessage omemoKeyExchangeMessage = new OmemoKeyExchangeMessage(data);

            Assert.IsTrue(omemoKeyExchangeMessage.Equals(refOmemoKeyExchangeMessage));
        }
Пример #2
0
 public OmemoMessage(OmemoSessionModel session)
 {
     N          = session.nS;
     PN         = session.pn;
     DH         = session.dhS.pubKey;
     cipherText = null;
 }
Пример #3
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);
                }
            }
        }
Пример #4
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());
                }
            }
Пример #5
0
        /// <summary>
        /// Tries to decrypt the message and returns true on success.
        /// </summary>
        /// <param name="decryptCtx">The <see cref="OmemoDecryptionContext"/> containing all necessary information for decrypting the message.</param>
        /// <param name="storage">An instance of the <see cref="IOmemoStorage"/> interface.</param>
        public void decrypt(OmemoDecryptionContext decryptCtx)
        {
            try
            {
                // Check if the message has been encrypted for us:
                OmemoKeys omemoKeys = keys.Where(k => string.Equals(k.BARE_JID, decryptCtx.RECEIVER_ADDRESS.BARE_JID)).FirstOrDefault();
                if (keys is null)
                {
                    throw new NotForDeviceException("Failed to decrypt message. Not encrypted for JID.");
                }
                decryptCtx.key = omemoKeys.KEYS?.Where(k => k.DEVICE_ID == decryptCtx.RECEIVER_ADDRESS.DEVICE_ID).FirstOrDefault();
                if (decryptCtx.key is null)
                {
                    throw new NotForDeviceException("Failed to decrypt message. Not encrypted for device.");
                }
                decryptCtx.senderAddress = new OmemoProtocolAddress(Utils.getBareJidFromFullJid(FROM), SID);

                // Check if the PreKey and SignedPreKey are still available in case the massage is a key exchange message:
                byte[] data = Convert.FromBase64String(decryptCtx.key.BASE64_PAYLOAD);
                if (decryptCtx.key.KEY_EXCHANGE)
                {
                    decryptCtx.keyExchangeMsg = new OmemoKeyExchangeMessage(data);

                    // Check if there already exists a session. In case yes, compare the ephemeral public key stored in the Double Ratchet:
                    OmemoSessionModel oldSession = decryptCtx.STORAGE.LoadSession(decryptCtx.senderAddress);
                    if (!(oldSession is null) && oldSession.ek.Equals(decryptCtx.keyExchangeMsg.EK))
                    {
                        // Process only the auth message:
                        decryptCtx.authMsg     = decryptCtx.keyExchangeMsg.MESSAGE;
                        decryptCtx.keyExchange = false;
                        Logger.Info($"Received an {nameof(OmemoKeyExchangeMessage)} for a session from '{decryptCtx.senderAddress.ToString()}' that already exists. This could be because we have not yet responded with an own message to the sender.");
                    }
                    else
                    {
                        decryptCtx.keyExchange = true;
                        decryptCtx.authMsg     = decryptCtx.keyExchangeMsg.MESSAGE;
                        if (decryptCtx.keyExchangeMsg.SPK_ID != decryptCtx.RECEIVER_SIGNED_PRE_KEY.preKey.keyId)
                        {
                            throw new NotForDeviceException("Failed to decrypt message. Signed PreKey with id " + decryptCtx.keyExchangeMsg.SPK_ID + " not available any more.");
                        }

                        decryptCtx.usedPreKey = decryptCtx.RECEIVER_PRE_KEYS.Where(k => k.keyId == decryptCtx.keyExchangeMsg.PK_ID).FirstOrDefault();
                        if (decryptCtx.usedPreKey is null)
                        {
                            throw new NotForDeviceException("Failed to decrypt message. PreKey with id " + decryptCtx.keyExchangeMsg.PK_ID + " not available any more.");
                        }
                    }
                }
                else
                {
                    decryptCtx.authMsg = new OmemoAuthenticatedMessage(data);
                }

                // Content can be null in case we have a pure keys exchange message:
                byte[] contentEnc = null;
                if (!string.IsNullOrEmpty(BASE_64_PAYLOAD))
                {
                    contentEnc = Convert.FromBase64String(BASE_64_PAYLOAD);
                }
                else
                {
                    IS_PURE_KEY_EXCHANGE_MESSAGE = true;
                }

                // Check the senders fingerprint and handle new devices:
                ValidateSender(decryptCtx);

                byte[] contentDec = decryptContent(contentEnc, decryptCtx);
                if (!IS_PURE_KEY_EXCHANGE_MESSAGE)
                {
                    string  contentStr  = Encoding.UTF8.GetString(contentDec);
                    XmlNode contentNode = getContentNode(contentStr);
                    parseContentNode(contentNode);
                }

                // In case nothing went wrong, store the session:
                decryptCtx.STORAGE.StoreSession(decryptCtx.senderAddress, decryptCtx.session);
                ENCRYPTED = false;
            }
Пример #6
0
 public void StoreSession(OmemoProtocolAddress address, OmemoSessionModel session)
 {
     SESSIONS[address] = session;
 }
Пример #7
0
 //--------------------------------------------------------Constructor:----------------------------------------------------------------\\
 #region --Constructors--
 public OmemoDevice(uint deviceId, OmemoSessionModel session)
 {
     DEVICE_ID = deviceId;
     SESSION   = session;
 }
Пример #8
0
        /// <summary>
        /// Tries to decrypt the message and returns true on success.
        /// </summary>
        /// <param name="decryptCtx">The <see cref="OmemoDecryptionContext"/> containing all necessary information for decrypting the message.</param>
        /// <param name="storage">An instance of the <see cref="IOmemoStorage"/> interface.</param>
        public void decrypt(OmemoDecryptionContext decryptCtx)
        {
            try
            {
                // Check if the message has been encrypted for us:
                OmemoKeys omemoKeys = keys.Where(k => string.Equals(k.BARE_JID, decryptCtx.RECEIVER_ADDRESS.BARE_JID)).FirstOrDefault();
                if (omemoKeys is null)
                {
                    throw new NotForDeviceException("Failed to decrypt message. Not encrypted for JID.");
                }
                decryptCtx.key = omemoKeys.KEYS?.Where(k => k.DEVICE_ID == decryptCtx.RECEIVER_ADDRESS.DEVICE_ID).FirstOrDefault();
                if (decryptCtx.key is null)
                {
                    throw new NotForDeviceException("Failed to decrypt message. Not encrypted for device.");
                }
                decryptCtx.senderAddress = new OmemoProtocolAddress(Utils.getBareJidFromFullJid(FROM), SID);

                // Check if the PreKey and SignedPreKey are still available in case the massage is a key exchange message:
                byte[] data = Convert.FromBase64String(decryptCtx.key.BASE64_PAYLOAD);
                if (decryptCtx.key.KEY_EXCHANGE)
                {
                    decryptCtx.keyExchangeMsg = new OmemoKeyExchangeMessage(data);

                    // Check if there already exists a session. In case yes, compare the ephemeral public key stored in the Double Ratchet:
                    OmemoSessionModel oldSession = decryptCtx.STORAGE.LoadSession(decryptCtx.senderAddress);
                    if (!(oldSession is null) && oldSession.ek.Equals(decryptCtx.keyExchangeMsg.EK))
                    {
                        // Process only the auth message:
                        decryptCtx.authMsg     = decryptCtx.keyExchangeMsg.MESSAGE;
                        decryptCtx.keyExchange = false;
                        Logger.Info($"Received an {nameof(OmemoKeyExchangeMessage)} for a session from '{decryptCtx.senderAddress}' that already exists. This could be because we have not yet responded with an own message to the sender.");
                    }
                    else
                    {
                        decryptCtx.keyExchange = true;
                        decryptCtx.authMsg     = decryptCtx.keyExchangeMsg.MESSAGE;
                        if (decryptCtx.keyExchangeMsg.SPK_ID != decryptCtx.RECEIVER_SIGNED_PRE_KEY.preKey.keyId)
                        {
                            throw new NotForDeviceException($"Failed to decrypt message. Signed PreKey with id {decryptCtx.keyExchangeMsg.SPK_ID} not available any more.");
                        }

                        decryptCtx.usedPreKey = decryptCtx.RECEIVER_PRE_KEYS.Where(k => k.keyId == decryptCtx.keyExchangeMsg.PK_ID).FirstOrDefault();
                        if (decryptCtx.usedPreKey is null)
                        {
                            throw new NotForDeviceException($"Failed to decrypt message. PreKey with id {decryptCtx.keyExchangeMsg.PK_ID} not available any more.");
                        }
                    }
                }
                else
                {
                    decryptCtx.authMsg = new OmemoAuthenticatedMessage(data);
                }

                // Check the senders fingerprint and handle new devices:
                validateSender(decryptCtx);

                // Load the existing OMEMO session and create a new one if the message is a key exchange message:
                loadSession(decryptCtx);

                if (!IS_PURE_KEY_EXCHANGE_MESSAGE)
                {
                    // Content can be null in case we have a pure keys exchange message:
                    byte[]  contentEnc   = Convert.FromBase64String(BASE_64_PAYLOAD);
                    byte[]  contentDec   = decryptContent(contentEnc, decryptCtx);
                    string  contentStr   = Encoding.UTF8.GetString(contentDec);
                    XmlNode envelopeNode = getEnvelopeNode(contentStr);
                    parseEnvelopeNode(envelopeNode);
                }
                else
                {
                    // For pure key exchange messages we decrypt 32 zero bytes instead the (key || HMAC) combination:
                    byte[] dummyKeyHmac = decryptKeyHmac(decryptCtx);
                    if (!dummyKeyHmac.SequenceEqual(new byte[32]))
                    {
                        throw new OmemoException($"Expected to decrypt 32 zero bytes from pure key exchange messages, but received: {CryptoUtils.ToHexString(dummyKeyHmac)}");
                    }
                }

                // In case nothing went wrong, store the session:
                if (decryptCtx.session.state == SessionState.SEND)
                {
                    decryptCtx.session.state = SessionState.READY;
                }
                decryptCtx.STORAGE.StoreSession(decryptCtx.senderAddress, decryptCtx.session);
                ENCRYPTED = false;
            }