//--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ #region --Misc Methods (Public)-- /// <summary> /// Encrypts the content of MESSAGE with the given SessionCipher and saves the result in BASE_64_PAYLOAD. /// </summary> /// <param name="omemoSession">A storage object containing all SessionCipher for the target OMEMO devices.</param> /// <param name="sourceDeviceId">The sender OMEMO device id.</param> public void encrypt(OmemoSession omemoSession, uint sourceDeviceId) { SOURCE_DEVICE_ID = sourceDeviceId; // 1. Generate a new AES-128 GCM key/iv: Aes128Gcm aes128Gcm = new Aes128Gcm(); aes128Gcm.generateKey(); aes128Gcm.generateIv(); // 2. Encrypt the message using the Aes128Gcm instance: byte[] encryptedData = aes128Gcm.encrypt(Encoding.Unicode.GetBytes(MESSAGE)); BASE_64_PAYLOAD = Convert.ToBase64String(encryptedData); BASE_64_IV = Convert.ToBase64String(aes128Gcm.iv); // 3. Concatenate key and authentication tag: byte[] keyAuthTag = new byte[aes128Gcm.authTag.Length + aes128Gcm.key.Length]; Buffer.BlockCopy(aes128Gcm.key, 0, keyAuthTag, 0, aes128Gcm.key.Length); Buffer.BlockCopy(aes128Gcm.authTag, 0, keyAuthTag, aes128Gcm.key.Length, aes128Gcm.authTag.Length); // 4. Encrypt the key/authTag pair with libsignal for each deviceId: KEYS = new List <OmemoKey>(); CiphertextMessage ciphertextMessage; foreach (KeyValuePair <uint, SessionCipher> pair in omemoSession.DEVICE_SESSIONS) { ciphertextMessage = pair.Value.encrypt(keyAuthTag); // Create a new OmemoKey object with the target device id, whether it's the first time the session got established and the encrypted key: OmemoKey key = new OmemoKey(pair.Key, ciphertextMessage is PreKeySignalMessage, Convert.ToBase64String(ciphertextMessage.serialize())); KEYS.Add(key); } ENCRYPTED = true; }
private void encryptForDevices(Dictionary <uint, SessionCipher> devices, byte[] keyAuthTag) { CiphertextMessage ciphertextMessage; foreach (KeyValuePair <uint, SessionCipher> pair in devices) { Logger.Debug("[OmemoMessageMessage]: Encrypting for deviceId: " + pair.Key); ciphertextMessage = pair.Value.encrypt(keyAuthTag); // Create a new OmemoKey object with the target device id, whether it's the first time the session got established and the encrypted key: OmemoKey key = new OmemoKey(pair.Key, ciphertextMessage is PreKeySignalMessage, Convert.ToBase64String(ciphertextMessage.serialize())); KEYS.Add(key); } }
/// <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> /// <param name="remoteAddress">The SignalProtocolAddress of the sender.</param> /// <param name="localeDeciceId">The local device id.</param> /// <param name="helper">The current OmemoHelper object of the current account. If null, won't remove used PreKey.</param> /// <returns>True on success.</returns> public async Task <bool> decryptAsync(SessionCipher cipher, SignalProtocolAddress remoteAddress, uint localeDeciceId, OmemoHelper helper) { try { // 1. Check if the message contains a key for the local device: OmemoKey key = getOmemoKey(localeDeciceId); if (key is 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); byte[] encryptedKeyAuthTag = Convert.FromBase64String(key.BASE_64_KEY); byte[] decryptedKeyAuthTag = null; if (key.IS_PRE_KEY) { PreKeySignalMessage preKeySignalMessage = new PreKeySignalMessage(encryptedKeyAuthTag); decryptedKeyAuthTag = cipher.decrypt(preKeySignalMessage); if (!(helper is null)) { May <uint> preKey = preKeySignalMessage.getPreKeyId(); if (preKey.HasValue) { Logger.Info("Removing used PreKey."); await helper.removePreKeyAndRepublishAsync(preKey.ForceGetValue()); } else { Logger.Error("Failed to get value from PreKeySignalMessage."); } } } else { decryptedKeyAuthTag = cipher.decrypt(new SignalMessage(encryptedKeyAuthTag)); } // 3. Check if the cipher got loaded successfully: if (decryptedKeyAuthTag is 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); Aes128GcmCpp aes128Gcm = new Aes128GcmCpp() { key = aesKey, authTag = aesAuthTag, iv = aesIv }; byte[] encryptedData = Convert.FromBase64String(BASE_64_PAYLOAD); byte[] decryptedData = aes128Gcm.decrypt(encryptedData); // 5. Convert decrypted data to Unicode string: MESSAGE = Encoding.UTF8.GetString(decryptedData); ENCRYPTED = false; return(true); } catch (Exception e) { Logger.Info("Discarded received OMEMO message - failed to decrypt with: " + e.Message); } return(false); }
/// <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); }