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. }
//--------------------------------------------------------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; }
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); } } }
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); } } }
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()); } }
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)); }
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()}'."); } }
/// <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); }
public OmemoFingerprint LoadFingerprint(OmemoProtocolAddress address) { return(FINGERPRINTS.ContainsKey(address) ? FINGERPRINTS[address] : null); }
public void StoreSession(OmemoProtocolAddress address, OmemoSessionModel session) { SESSIONS[address] = session; }
//--------------------------------------------------------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); }
public OmemoFingerprint(ECPubKeyModel identityKey, OmemoProtocolAddress address) : this(identityKey, address, DateTime.MinValue, false) { }