private async Task buildSessionForDevicesAsync(Dictionary <uint, SessionCipher> sessions, IList <SignalProtocolAddress> devices) { if (devices.Count <= 0) { return; } SignalProtocolAddress device = devices[0]; devices.RemoveAt(0); // Check if there exists already a session for this device: if (OMEMO_HELPER.OMEMO_STORE.ContainsSession(device)) { // If yes, the load it: SessionCipher cipher = OMEMO_HELPER.loadCipher(device); sessions.Add(device.getDeviceId(), cipher); Logger.Info("[OmemoSessionBuildHelper] Session for " + device.ToString() + " loaded from cache."); } else { // Else try to build a new one by requesting the devices bundle information: OmemoBundleInformationResultMessage bundleMsg = await requestBundleInformationAsync(device); if (!(bundleMsg is null)) { SignalProtocolAddress address = OMEMO_HELPER.newSession(CHAT_JID, bundleMsg); SessionCipher cipher = OMEMO_HELPER.loadCipher(address); sessions.Add(device.getDeviceId(), cipher); Logger.Info("[OmemoSessionBuildHelper] Session with " + device.ToString() + " established."); }
private void createSessionForNextDevice() { if ((toDoDevicesRemote == null || toDoDevicesRemote.Count <= 0) && (toDoDevicesOwn == null || toDoDevicesOwn.Count <= 0)) { // All sessions created: if (SESSION.DEVICE_SESSIONS.Count <= 0) { setState(OmemoSessionBuildHelperState.ERROR); ON_SESSION_RESULT(this, new OmemoSessionBuildResult(OmemoSessionBuildError.TARGET_DOES_NOT_SUPPORT_OMEMO)); } else { setState(OmemoSessionBuildHelperState.ESTABLISHED); ON_SESSION_RESULT(this, new OmemoSessionBuildResult(SESSION)); } } else { if (toDoDevicesRemote == null || toDoDevicesRemote.Count <= 0) { curAddress = new SignalProtocolAddress(BARE_ACCOUNT_JID, toDoDevicesOwn[0]); toDoDevicesOwn.RemoveAt(0); } else { curAddress = new SignalProtocolAddress(CHAT_JID, toDoDevicesRemote[0]); toDoDevicesRemote.RemoveAt(0); } if (OMEMO_HELPER.containsSession(curAddress)) { SessionCipher cipher = OMEMO_HELPER.loadCipher(curAddress); SESSION.DEVICE_SESSIONS.Add(curAddress.getDeviceId(), cipher); createSessionForNextDevice(); } else { requestBundleInformation(); } } }
/// <summary> /// Decrypts the content of BASE_64_PAYLOAD. Loads the SessionCipher from the given OmemoHelper object and saves the result in MESSAGE. /// Sets ENCRYPTED to false. /// </summary> /// <param name="helper">The current OmemoHelper object of the current account.</param> /// <param name="localeDeciceId">The local device id.</param> /// <returns>True on success.</returns> public async Task <bool> decryptAsync(OmemoHelper helper, uint localeDeciceId) { SignalProtocolAddress remoteAddress = new SignalProtocolAddress(Utils.getBareJidFromFullJid(FROM), SOURCE_DEVICE_ID); return(await decryptAsync(helper.loadCipher(remoteAddress), remoteAddress, localeDeciceId, helper)); }
private async Task buildSessionForDevicesAsync(Dictionary <uint, SessionCipher> sessions, IList <SignalProtocolAddress> devices) { if (devices.Count <= 0) { return; } SignalProtocolAddress device = devices[0]; devices.RemoveAt(0); // Validate the device fingerprint: OmemoFingerprint fingerprint = OMEMO_HELPER.OMEMO_STORE.LoadFingerprint(device); if (!(fingerprint is null) && !OMEMO_HELPER.OMEMO_STORE.IsFingerprintTrusted(fingerprint)) { Logger.Warn("[OmemoSessionBuildHelper] Not building a session with " + device.ToString() + " - key not trusted."); await buildSessionForDevicesAsync(sessions, devices); return; } // Check if there exists already a session for this device: if (OMEMO_HELPER.OMEMO_STORE.ContainsSession(device)) { // If yes, the load it: SessionCipher cipher = OMEMO_HELPER.loadCipher(device); sessions.Add(device.getDeviceId(), cipher); Logger.Info("[OmemoSessionBuildHelper] Session for " + device.ToString() + " loaded from cache."); } else { // Else try to build a new one by requesting the devices bundle information: OmemoBundleInformationResultMessage bundleMsg = await requestBundleInformationAsync(device); if (!(bundleMsg is null)) { OMEMO_HELPER.newSession(device.getName(), bundleMsg); // Validate fingerprints: if (fingerprint is null) { fingerprint = new OmemoFingerprint(bundleMsg.BUNDLE_INFO.PUBLIC_IDENTITY_KEY, device); OMEMO_HELPER.OMEMO_STORE.StoreFingerprint(fingerprint); } else { OmemoFingerprint receivedFingerprint = new OmemoFingerprint(bundleMsg.BUNDLE_INFO.PUBLIC_IDENTITY_KEY, device); // Make sure the fingerprint did not change or somebody is doing an attack: if (!fingerprint.checkIdentityKey(receivedFingerprint.IDENTITY_PUB_KEY)) { Logger.Warn("[OmemoSessionBuildHelper] Unable to establish session with " + device.ToString() + " - other fingerprint received than stored locally."); await buildSessionForDevicesAsync(sessions, devices); return; } } // Check if the fingerprint is trusted: if (OMEMO_HELPER.OMEMO_STORE.IsFingerprintTrusted(fingerprint)) { SessionCipher cipher = OMEMO_HELPER.loadCipher(device); sessions.Add(device.getDeviceId(), cipher); Logger.Info("[OmemoSessionBuildHelper] Session with " + device.ToString() + " established."); } else { Logger.Warn("[OmemoSessionBuildHelper] Unable to establish session with " + device.ToString() + " - key not trusted."); } }
/// <summary> /// Decrypts the content of BASE_64_PAYLOAD with the given SessionCipher and saves the result in MESSAGE. /// Sets ENCRYPTED to false. /// </summary> /// <param name="cipher">The SessionCipher for decrypting the content of BASE_64_PAYLOAD.</param> public bool decrypt(OmemoHelper omemoHelper, uint localOmemoDeviceId) { try { // 1. Check if the message contains a key for the local device: OmemoKey key = getOmemoKey(localOmemoDeviceId); if (key == null) { Logger.Info("Discarded received OMEMO message - doesn't contain device id!"); return(false); } // 2. Load the cipher: SignalProtocolAddress address = new SignalProtocolAddress(Utils.getBareJidFromFullJid(FROM), SOURCE_DEVICE_ID); SessionCipher cipher = omemoHelper.loadCipher(address); byte[] encryptedKeyAuthTag = Convert.FromBase64String(key.BASE_64_KEY); byte[] decryptedKeyAuthTag = null; if (key.IS_PRE_KEY) { decryptedKeyAuthTag = cipher.decrypt(new PreKeySignalMessage(encryptedKeyAuthTag)); // ToDo republish the bundle info and remove used pre key } else { decryptedKeyAuthTag = cipher.decrypt(new SignalMessage(encryptedKeyAuthTag)); } // 3. Check if the cipher got loaded successfully: if (decryptedKeyAuthTag == null) { Logger.Info("Discarded received OMEMO message - failed to decrypt keyAuthTag is null!"); return(false); } // 4. Decrypt the payload: byte[] aesIv = Convert.FromBase64String(BASE_64_IV); byte[] aesKey = new byte[16]; byte[] aesAuthTag = new byte[decryptedKeyAuthTag.Length - aesKey.Length]; Buffer.BlockCopy(decryptedKeyAuthTag, 0, aesKey, 0, aesKey.Length); Buffer.BlockCopy(decryptedKeyAuthTag, aesKey.Length, aesAuthTag, 0, aesAuthTag.Length); Aes128Gcm aes = new Aes128Gcm() { key = aesKey, authTag = aesAuthTag, iv = aesIv }; byte[] encryptedData = Convert.FromBase64String(BASE_64_PAYLOAD); byte[] decryptedData = aes.decrypt(encryptedData); // 5. Convert decrypted data to Unicode string: MESSAGE = Encoding.Unicode.GetString(decryptedData); ENCRYPTED = false; return(true); } catch (Exception e) { Logger.Info("Discarded received OMEMO message - failed to decrypt with:" + e.Message); } return(false); }