/** * Encrypt a message. * * @param paddedMessage The plaintext message bytes, optionally padded to a constant multiple. * @return A ciphertext message encrypted to the recipient+device tuple. */ public CiphertextMessage encrypt(byte[] paddedMessage) { lock (SESSION_LOCK) { SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress); SessionState sessionState = sessionRecord.getSessionState(); ChainKey chainKey = sessionState.getSenderChainKey(); MessageKeys messageKeys = chainKey.getMessageKeys(); ECPublicKey senderEphemeral = sessionState.getSenderRatchetKey(); uint previousCounter = sessionState.getPreviousCounter(); uint sessionVersion = sessionState.getSessionVersion(); byte[] ciphertextBody = getCiphertext(sessionVersion, messageKeys, paddedMessage); CiphertextMessage ciphertextMessage = new SignalMessage(sessionVersion, messageKeys.getMacKey(), senderEphemeral, chainKey.getIndex(), previousCounter, ciphertextBody, sessionState.getLocalIdentityKey(), sessionState.getRemoteIdentityKey()); if (sessionState.hasUnacknowledgedPreKeyMessage()) { SessionState.UnacknowledgedPreKeyMessageItems items = sessionState.getUnacknowledgedPreKeyMessageItems(); uint localRegistrationId = sessionState.GetLocalRegistrationId(); ciphertextMessage = new PreKeySignalMessage(sessionVersion, localRegistrationId, items.getPreKeyId(), items.getSignedPreKeyId(), items.getBaseKey(), sessionState.getLocalIdentityKey(), (SignalMessage)ciphertextMessage); } sessionState.setSenderChainKey(chainKey.getNextChainKey()); sessionStore.StoreSession(remoteAddress, sessionRecord); return(ciphertextMessage); } }
public void testBadMessageBundle() { SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); ECKeyPair bobPreKeyPair = Curve.generateKeyPair(); ECKeyPair bobSignedPreKeyPair = Curve.generateKeyPair(); byte[] bobSignedPreKeySignature = Curve.calculateSignature(bobStore.GetIdentityKeyPair().getPrivateKey(), bobSignedPreKeyPair.getPublicKey().serialize()); PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 31337, bobPreKeyPair.getPublicKey(), 22, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature, bobStore.GetIdentityKeyPair().getPublicKey()); bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)); bobStore.StoreSignedPreKey(22, new SignedPreKeyRecord(22, DateUtil.currentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature)); aliceSessionBuilder.process(bobPreKey); String originalMessage = "L'homme est condamné à être libre"; SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS); CiphertextMessage outgoingMessageOne = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.PREKEY_TYPE, outgoingMessageOne.getType()); byte[] goodMessage = outgoingMessageOne.serialize(); byte[] badMessage = new byte[goodMessage.Length]; Array.Copy(goodMessage, 0, badMessage, 0, badMessage.Length); badMessage[badMessage.Length - 10] ^= 0x01; PreKeySignalMessage incomingMessage = new PreKeySignalMessage(badMessage); SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS); byte[] plaintext = new byte[0]; try { plaintext = bobSessionCipher.decrypt(incomingMessage); throw new Exception("Decrypt should have failed!"); } catch (InvalidMessageException) { // good. } Assert.IsTrue(bobStore.ContainsPreKey(31337)); plaintext = bobSessionCipher.decrypt(new PreKeySignalMessage(goodMessage)); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); Assert.IsFalse(bobStore.ContainsPreKey(31337)); }
public void testRepeatBundleMessageV3() { SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); ECKeyPair bobPreKeyPair = Curve.generateKeyPair(); ECKeyPair bobSignedPreKeyPair = Curve.generateKeyPair(); byte[] bobSignedPreKeySignature = Curve.calculateSignature(bobStore.GetIdentityKeyPair().getPrivateKey(), bobSignedPreKeyPair.getPublicKey().serialize()); PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 31337, bobPreKeyPair.getPublicKey(), 22, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature, bobStore.GetIdentityKeyPair().getPublicKey()); bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)); bobStore.StoreSignedPreKey(22, new SignedPreKeyRecord(22, DateUtil.currentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature)); aliceSessionBuilder.process(bobPreKey); String originalMessage = "L'homme est condamné à être libre"; SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS); CiphertextMessage outgoingMessageOne = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); CiphertextMessage outgoingMessageTwo = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.PREKEY_TYPE, outgoingMessageOne.getType()); Assert.AreEqual(CiphertextMessage.PREKEY_TYPE, outgoingMessageTwo.getType()); PreKeySignalMessage incomingMessage = new PreKeySignalMessage(outgoingMessageOne.serialize()); SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS); byte[] plaintext = bobSessionCipher.decrypt(incomingMessage); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); CiphertextMessage bobOutgoingMessage = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); byte[] alicePlaintext = aliceSessionCipher.decrypt(new SignalMessage(bobOutgoingMessage.serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(alicePlaintext)); // The test PreKeySignalMessage incomingMessageTwo = new PreKeySignalMessage(outgoingMessageTwo.serialize()); plaintext = bobSessionCipher.decrypt(new PreKeySignalMessage(incomingMessageTwo.serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); bobOutgoingMessage = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); alicePlaintext = aliceSessionCipher.decrypt(new SignalMessage(bobOutgoingMessage.serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(alicePlaintext)); }
/** * Build a new session from a received {@link PreKeySignalMessage}. * * After a session is constructed in this way, the embedded {@link SignalMessage} * can be decrypted. * * @param message The received {@link PreKeySignalMessage}. * @throws org.whispersystems.libsignal.InvalidKeyIdException when there is no local * {@link org.whispersystems.libsignal.state.PreKeyRecord} * that corresponds to the PreKey ID in * the message. * @throws org.whispersystems.libsignal.InvalidKeyException when the message is formatted incorrectly. * @throws org.whispersystems.libsignal.UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. */ /*package*/ internal May <uint> process(SessionRecord sessionRecord, PreKeySignalMessage message) { uint messageVersion = message.getMessageVersion(); IdentityKey theirIdentityKey = message.getIdentityKey(); if (!identityKeyStore.IsTrustedIdentity(remoteAddress.getName(), theirIdentityKey)) { throw new UntrustedIdentityException(remoteAddress.getName(), theirIdentityKey); } May <uint> unsignedPreKeyId = processV3(sessionRecord, message); identityKeyStore.SaveIdentity(remoteAddress.getName(), theirIdentityKey); return(unsignedPreKeyId); }
/// <summary> /// Build a new session from a received {@link PreKeySignalMessage}. /// After a session is constructed in this way, the embedded {@link SignalMessage} /// can be decrypted. /// </summary> /// <param name="message">The received {@link PreKeySignalMessage}.</param> /// <exception cref="InvalidKeyIdException">when there is no local {@link org.whispersystems.libsignal.state.PreKeyRecord} that corresponds to the PreKey ID in the message.</exception> /// <exception cref="InvalidKeyException">when the message is formatted incorrectly.</exception> /// <exception cref="UntrustedIdentityException">when the {@link IdentityKey} of the sender is untrusted.</exception> /// /*package*/ internal May <uint> Process(SessionRecord sessionRecord, PreKeySignalMessage message) { uint messageVersion = message.GetMessageVersion(); IdentityKey theirIdentityKey = message.GetIdentityKey(); if (!_identityKeyStore.IsTrustedIdentity(_remoteAddress, theirIdentityKey, Direction.Receiving)) { throw new UntrustedIdentityException(_remoteAddress.Name, theirIdentityKey); } May <uint> unsignedPreKeyId = ProcessV3(sessionRecord, message); _identityKeyStore.SaveIdentity(_remoteAddress, theirIdentityKey); return(unsignedPreKeyId); }
private May <uint> processV3(SessionRecord sessionRecord, PreKeySignalMessage message) { if (sessionRecord.hasSessionState(message.getMessageVersion(), message.getBaseKey().serialize())) { Debug.WriteLine("We've already setup a session for this V3 message, letting bundled message fall through..."); return(May <uint> .NoValue); } ECKeyPair ourSignedPreKey = signedPreKeyStore.LoadSignedPreKey(message.getSignedPreKeyId()).getKeyPair(); BobSignalProtocolParameters.Builder parameters = BobSignalProtocolParameters.newBuilder(); parameters.setTheirBaseKey(message.getBaseKey()) .setTheirIdentityKey(message.getIdentityKey()) .setOurIdentityKey(identityKeyStore.GetIdentityKeyPair()) .setOurSignedPreKey(ourSignedPreKey) .setOurRatchetKey(ourSignedPreKey); if (message.getPreKeyId().HasValue) { parameters.setOurOneTimePreKey(new May <ECKeyPair>(preKeyStore.LoadPreKey(message.getPreKeyId().ForceGetValue()).getKeyPair())); } else { parameters.setOurOneTimePreKey(May <ECKeyPair> .NoValue); } if (!sessionRecord.isFresh()) { sessionRecord.archiveCurrentState(); } RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters.create()); sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.GetLocalRegistrationId()); sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId()); sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize()); if (message.getPreKeyId().HasValue&& message.getPreKeyId().ForceGetValue() != Medium.MAX_VALUE) { return(message.getPreKeyId()); } else { return(May <uint> .NoValue); } }
public void testOptionalOneTimePreKey() { SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); ECKeyPair bobPreKeyPair = Curve.generateKeyPair(); ECKeyPair bobSignedPreKeyPair = Curve.generateKeyPair(); byte[] bobSignedPreKeySignature = Curve.calculateSignature(bobStore.GetIdentityKeyPair().getPrivateKey(), bobSignedPreKeyPair.getPublicKey().serialize()); PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 0, null, 22, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature, bobStore.GetIdentityKeyPair().getPublicKey()); aliceSessionBuilder.process(bobPreKey); Assert.IsTrue(aliceStore.ContainsSession(BOB_ADDRESS)); Assert.AreEqual((uint)3, aliceStore.LoadSession(BOB_ADDRESS).getSessionState().getSessionVersion()); String originalMessage = "L'homme est condamné à être libre"; SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS); CiphertextMessage outgoingMessage = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(outgoingMessage.getType(), CiphertextMessage.PREKEY_TYPE); PreKeySignalMessage incomingMessage = new PreKeySignalMessage(outgoingMessage.serialize()); Assert.IsFalse(incomingMessage.getPreKeyId().HasValue); bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)); bobStore.StoreSignedPreKey(22, new SignedPreKeyRecord(22, DateUtil.currentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature)); SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS); byte[] plaintext = bobSessionCipher.decrypt(incomingMessage); Assert.IsTrue(bobStore.ContainsSession(ALICE_ADDRESS)); Assert.AreEqual((uint)3, bobStore.LoadSession(ALICE_ADDRESS).getSessionState().getSessionVersion()); Assert.IsNotNull(bobStore.LoadSession(ALICE_ADDRESS).getSessionState().getAliceBaseKey()); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); }
/** * Encrypt a message. * * @param paddedMessage The plaintext message bytes, optionally padded to a constant multiple. * @return A ciphertext message encrypted to the recipient+device tuple. */ public CiphertextMessage Encrypt(byte[] paddedMessage) { lock (SessionLock) { SessionRecord sessionRecord = _sessionStore.LoadSession(_remoteAddress); SessionState sessionState = sessionRecord.GetSessionState(); ChainKey chainKey = sessionState.GetSenderChainKey(); MessageKeys messageKeys = chainKey.GetMessageKeys(); IEcPublicKey senderEphemeral = sessionState.GetSenderRatchetKey(); uint previousCounter = sessionState.GetPreviousCounter(); uint sessionVersion = sessionState.GetSessionVersion(); byte[] ciphertextBody = GetCiphertext(messageKeys, paddedMessage); CiphertextMessage ciphertextMessage = new SignalMessage(sessionVersion, messageKeys.GetMacKey(), senderEphemeral, chainKey.GetIndex(), previousCounter, ciphertextBody, sessionState.GetLocalIdentityKey(), sessionState.GetRemoteIdentityKey()); if (sessionState.HasUnacknowledgedPreKeyMessage()) { SessionState.UnacknowledgedPreKeyMessageItems items = sessionState.GetUnacknowledgedPreKeyMessageItems(); uint localRegistrationId = sessionState.GetLocalRegistrationId(); ciphertextMessage = new PreKeySignalMessage(sessionVersion, localRegistrationId, items.GetPreKeyId(), items.GetSignedPreKeyId(), items.GetBaseKey(), sessionState.GetLocalIdentityKey(), (SignalMessage)ciphertextMessage); } sessionState.SetSenderChainKey(chainKey.GetNextChainKey()); if (!_identityKeyStore.IsTrustedIdentity(_remoteAddress, sessionState.GetRemoteIdentityKey(), Direction.Sending)) { throw new UntrustedIdentityException(_remoteAddress.Name, sessionState.GetRemoteIdentityKey()); } _identityKeyStore.SaveIdentity(_remoteAddress, sessionState.GetRemoteIdentityKey()); _sessionStore.StoreSession(_remoteAddress, sessionRecord); return(ciphertextMessage); } }
/** * Decrypt a message. * * @param ciphertext The {@link PreKeySignalMessage} to decrypt. * @param callback A callback that is triggered after decryption is complete, * but before the updated session state has been committed to the session * DB. This allows some implementations to store the committed plaintext * to a DB first, in case they are concerned with a crash happening between * the time the session state is updated but before they're able to store * the plaintext to disk. * * @return The plaintext. * @throws InvalidMessageException if the input is not valid ciphertext. * @throws DuplicateMessageException if the input is a message that has already been received. * @throws LegacyMessageException if the input is a message formatted by a protocol version that * is no longer supported. * @throws InvalidKeyIdException when there is no local {@link org.whispersystems.libsignal.state.PreKeyRecord} * that corresponds to the PreKey ID in the message. * @throws InvalidKeyException when the message is formatted incorrectly. * @throws UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. */ public byte[] decrypt(PreKeySignalMessage ciphertext, DecryptionCallback callback) { lock (SESSION_LOCK) { SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress); May <uint> unsignedPreKeyId = sessionBuilder.process(sessionRecord, ciphertext); byte[] plaintext = decrypt(sessionRecord, ciphertext.getSignalMessage()); callback.handlePlaintext(plaintext); sessionStore.StoreSession(remoteAddress, sessionRecord); if (unsignedPreKeyId.HasValue) { preKeyStore.RemovePreKey(unsignedPreKeyId.ForceGetValue()); } return(plaintext); } }
/** * Decrypt a message. * * @param ciphertext The {@link PreKeySignalMessage} to decrypt. * @param callback A callback that is triggered after decryption is complete, * but before the updated session state has been committed to the session * DB. This allows some implementations to store the committed plaintext * to a DB first, in case they are concerned with a crash happening between * the time the session state is updated but before they're able to store * the plaintext to disk. * * @return The plaintext. * @throws InvalidMessageException if the input is not valid ciphertext. * @throws DuplicateMessageException if the input is a message that has already been received. * @throws LegacyMessageException if the input is a message formatted by a protocol version that * is no longer supported. * @throws InvalidKeyIdException when there is no local {@link org.whispersystems.libsignal.state.PreKeyRecord} * that corresponds to the PreKey ID in the message. * @throws InvalidKeyException when the message is formatted incorrectly. * @throws UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. */ public byte[] Decrypt(PreKeySignalMessage ciphertext, IDecryptionCallback callback) { lock (SessionLock) { SessionRecord sessionRecord = _sessionStore.LoadSession(_remoteAddress); May <uint> unsignedPreKeyId = _sessionBuilder.Process(sessionRecord, ciphertext); byte[] plaintext = Decrypt(sessionRecord, ciphertext.GetSignalMessage()); callback.HandlePlaintext(plaintext); _sessionStore.StoreSession(_remoteAddress, sessionRecord); if (unsignedPreKeyId.HasValue) { _preKeyStore.RemovePreKey(unsignedPreKeyId.ForceGetValue()); } return(plaintext); } }
private void handleUntrustedIdentityMessage(SignalServiceEnvelope envelope, May <long> smsMessageId) { try { var database = DatabaseFactory.getTextMessageDatabase(); //getEncryptingSmsDatabase(context); Recipients recipients = RecipientFactory.getRecipientsFromString(envelope.getSource(), false); long recipientId = recipients.getPrimaryRecipient().getRecipientId(); PreKeySignalMessage whisperMessage = new PreKeySignalMessage(envelope.getLegacyMessage()); IdentityKey identityKey = whisperMessage.getIdentityKey(); String encoded = Base64.encodeBytes(envelope.getLegacyMessage()); IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), encoded, May <SignalServiceGroup> .NoValue); if (!smsMessageId.HasValue) { IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded); Pair <long, long> messageAndThreadId = database.InsertMessageInbox(bundleMessage); database.SetMismatchedIdentity(messageAndThreadId.first(), recipientId, identityKey); //MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), messageAndThreadId.second); } else { var messageId = smsMessageId.ForceGetValue(); database.UpdateMessageBody(messageId, encoded); database.MarkAsPreKeyBundle(messageId); database.SetMismatchedIdentity(messageId, recipientId, identityKey); } } catch (InvalidMessageException e) { throw new InvalidOperationException(e.Message); } catch (InvalidVersionException e) { throw new InvalidOperationException(e.Message); } }
public void testBasicPreKeyV3() { SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); ECKeyPair bobPreKeyPair = Curve.generateKeyPair(); ECKeyPair bobSignedPreKeyPair = Curve.generateKeyPair(); byte[] bobSignedPreKeySignature = Curve.calculateSignature(bobStore.GetIdentityKeyPair().getPrivateKey(), bobSignedPreKeyPair.getPublicKey().serialize()); PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 31337, bobPreKeyPair.getPublicKey(), 22, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature, bobStore.GetIdentityKeyPair().getPublicKey()); aliceSessionBuilder.process(bobPreKey); Assert.IsTrue(aliceStore.ContainsSession(BOB_ADDRESS)); Assert.AreEqual((uint)3, aliceStore.LoadSession(BOB_ADDRESS).getSessionState().getSessionVersion()); String originalMessage = "L'homme est condamné à être libre"; SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS); CiphertextMessage outgoingMessage = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.PREKEY_TYPE, outgoingMessage.getType()); PreKeySignalMessage incomingMessage = new PreKeySignalMessage(outgoingMessage.serialize()); bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)); bobStore.StoreSignedPreKey(22, new SignedPreKeyRecord(22, DateUtil.currentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature)); SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS); byte[] plaintext = bobSessionCipher.decrypt(incomingMessage, new BobDecryptionCallback(bobStore, originalMessage)); Assert.IsTrue(bobStore.ContainsSession(ALICE_ADDRESS)); Assert.AreEqual((uint)3, bobStore.LoadSession(ALICE_ADDRESS).getSessionState().getSessionVersion()); Assert.IsNotNull(bobStore.LoadSession(ALICE_ADDRESS).getSessionState().getAliceBaseKey()); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); CiphertextMessage bobOutgoingMessage = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.WHISPER_TYPE, bobOutgoingMessage.getType()); byte[] alicePlaintext = aliceSessionCipher.decrypt(new SignalMessage(bobOutgoingMessage.serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(alicePlaintext)); runInteraction(aliceStore, bobStore); aliceStore = new TestInMemorySignalProtocolStore(); aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS); bobPreKeyPair = Curve.generateKeyPair(); bobSignedPreKeyPair = Curve.generateKeyPair(); bobSignedPreKeySignature = Curve.calculateSignature(bobStore.GetIdentityKeyPair().getPrivateKey(), bobSignedPreKeyPair.getPublicKey().serialize()); bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 31338, bobPreKeyPair.getPublicKey(), 23, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature, bobStore.GetIdentityKeyPair().getPublicKey()); bobStore.StorePreKey(31338, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)); bobStore.StoreSignedPreKey(23, new SignedPreKeyRecord(23, DateUtil.currentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature)); aliceSessionBuilder.process(bobPreKey); outgoingMessage = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); try { plaintext = bobSessionCipher.decrypt(new PreKeySignalMessage(outgoingMessage.serialize())); throw new Exception("shouldn't be trusted!"); } catch (UntrustedIdentityException) { bobStore.SaveIdentity(ALICE_ADDRESS.getName(), new PreKeySignalMessage(outgoingMessage.serialize()).getIdentityKey()); } plaintext = bobSessionCipher.decrypt(new PreKeySignalMessage(outgoingMessage.serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 31337, Curve.generateKeyPair().getPublicKey(), 23, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature, aliceStore.GetIdentityKeyPair().getPublicKey()); try { aliceSessionBuilder.process(bobPreKey); throw new Exception("shoulnd't be trusted!"); } catch (UntrustedIdentityException) { // good } }
public void Test_Libsignal_Enc_Dec_2() { // Generate Alices keys: IdentityKeyPair aliceIdentKey = CryptoUtils.generateOmemoIdentityKeyPair(); IList <PreKeyRecord> alicePreKeys = CryptoUtils.generateOmemoPreKeys(); SignedPreKeyRecord aliceSignedPreKey = CryptoUtils.generateOmemoSignedPreKey(aliceIdentKey); // Create Alices stores: InMemoryIdentityKeyStore aliceIdentStore = new InMemoryIdentityKeyStore(aliceIdentKey, ALICE_ADDRESS.getDeviceId()); InMemoryPreKeyStore alicePreKeyStore = new InMemoryPreKeyStore(); foreach (PreKeyRecord key in alicePreKeys) { alicePreKeyStore.StorePreKey(key.getId(), key); } InMemorySignedPreKeyStore aliceSignedPreKeyStore = new InMemorySignedPreKeyStore(); aliceSignedPreKeyStore.StoreSignedPreKey(aliceSignedPreKey.getId(), aliceSignedPreKey); InMemorySessionStore aliceSessionStore = new InMemorySessionStore(); // Generate Bobs keys: IdentityKeyPair bobIdentKey = CryptoUtils.generateOmemoIdentityKeyPair(); IList <PreKeyRecord> bobPreKeys = CryptoUtils.generateOmemoPreKeys(); SignedPreKeyRecord bobSignedPreKey = CryptoUtils.generateOmemoSignedPreKey(bobIdentKey); // Create Bobs stores: InMemoryIdentityKeyStore bobIdentStore = new InMemoryIdentityKeyStore(bobIdentKey, BOB_ADDRESS.getDeviceId()); InMemoryPreKeyStore bobPreKeyStore = new InMemoryPreKeyStore(); foreach (PreKeyRecord key in bobPreKeys) { bobPreKeyStore.StorePreKey(key.getId(), key); } InMemorySignedPreKeyStore bobSignedPreKeyStore = new InMemorySignedPreKeyStore(); bobSignedPreKeyStore.StoreSignedPreKey(bobSignedPreKey.getId(), bobSignedPreKey); InMemorySessionStore bobSessionStore = new InMemorySessionStore(); // Alice builds a session to Bob: SessionBuilder sessionBuilder = new SessionBuilder(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentStore, BOB_ADDRESS); PreKeyBundle bobPreKey = new PreKeyBundle(BOB_ADDRESS.getDeviceId(), BOB_ADDRESS.getDeviceId(), bobPreKeys[0].getId(), bobPreKeys[0].getKeyPair().getPublicKey(), bobSignedPreKey.getId(), bobSignedPreKey.getKeyPair().getPublicKey(), bobSignedPreKey.getSignature(), bobIdentKey.getPublicKey()); sessionBuilder.process(bobPreKey); // Check if session exists: Assert.IsTrue(aliceSessionStore.ContainsSession(BOB_ADDRESS)); Assert.IsTrue(aliceSessionStore.LoadSession(BOB_ADDRESS).getSessionState().getSessionVersion() == 3); // Alice sends a message: string aliceOrigMsg = "$(rm -rvf .)"; SessionCipher aliceSessionCipher = new SessionCipher(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentStore, BOB_ADDRESS); CiphertextMessage aliceOutMsg = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(aliceOrigMsg)); // Check if successfully encrypted: Assert.IsTrue(aliceOutMsg.getType() == CiphertextMessage.PREKEY_TYPE); // Bob receives the message: PreKeySignalMessage bobInMsg = new PreKeySignalMessage(aliceOutMsg.serialize()); SessionCipher bobSessionCipher = new SessionCipher(bobSessionStore, bobPreKeyStore, bobSignedPreKeyStore, bobIdentStore, ALICE_ADDRESS); byte[] bobData = bobSessionCipher.decrypt(bobInMsg); string bobRecMsg = Encoding.UTF8.GetString(bobData); // Check if successfully send: Assert.AreEqual(aliceOrigMsg, bobRecMsg); Assert.IsTrue(bobSessionStore.ContainsSession(ALICE_ADDRESS)); //---------------------------Connection/App get restarted:--------------------------- // Bob answers: string bobOrigMsg = ":(){ :|:& };:"; // Simulate a chat break: bobSessionCipher = new SessionCipher(bobSessionStore, bobPreKeyStore, bobSignedPreKeyStore, bobIdentStore, ALICE_ADDRESS); CiphertextMessage bobOutMsg = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes(bobOrigMsg)); // Alice receives the message: aliceSessionCipher = new SessionCipher(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentStore, BOB_ADDRESS); SignalMessage aliceInMsg = new SignalMessage(bobOutMsg.serialize()); byte[] aliceData = aliceSessionCipher.decrypt(aliceInMsg); string aliceRecMsg = Encoding.UTF8.GetString(aliceData); // Check if successfully send: Assert.AreEqual(bobOrigMsg, aliceRecMsg); Assert.IsTrue(bobSessionStore.ContainsSession(ALICE_ADDRESS)); }
static void Main(string[] args) { // 1. Sender setup // At install time, a libsignal client needs to generate its identity keys, registration id, and prekeys. var senderIdentityKeyPair = KeyHelper.GenerateIdentityKeyPair(); var senderRegistrationId = KeyHelper.GenerateRegistrationId(false); var senderPreKeys = KeyHelper.GeneratePreKeys(0, 100); var senderSignedPreKey = KeyHelper.GenerateSignedPreKey(senderIdentityKeyPair, KeyHelper.GenerateSenderKeyId()); var senderAddress = new SignalProtocolAddress("sender", 1); // TODO: Store identityKeyPair somewhere durable and safe. // TODO: Store registrationId somewhere durable and safe. // Store preKeys in PreKeyStore. var senderPreKeyStore = new InMemoryPreKeyStore(); foreach (var senderPreKey in senderPreKeys) { senderPreKeyStore.StorePreKey(senderPreKey.GetId(), senderPreKey); } // Store signed prekey in SignedPreKeyStore. var senderSignedPreKeyStore = new InMemorySignedPreKeyStore(); senderSignedPreKeyStore.StoreSignedPreKey(senderSignedPreKey.GetId(), senderSignedPreKey); var senderSessionStore = new InMemorySessionStore(); var senderIdentityStore = new InMemoryIdentityKeyStore(senderIdentityKeyPair, senderRegistrationId); var senderProtocolStore = new InMemorySignalProtocolStore(senderIdentityKeyPair, senderRegistrationId); var senderPreKeyBundle = new PreKeyBundle( senderProtocolStore.GetLocalRegistrationId(), senderAddress.DeviceId, senderPreKeys[0].GetId(), senderPreKeys[0].GetKeyPair().GetPublicKey(), senderSignedPreKey.GetId(), senderSignedPreKey.GetKeyPair().GetPublicKey(), senderSignedPreKey.GetSignature(), senderProtocolStore.GetIdentityKeyPair().GetPublicKey() ); senderProtocolStore.StorePreKey(senderPreKeys[0].GetId(), new PreKeyRecord(senderPreKeyBundle.GetPreKeyId(), senderPreKeys[0].GetKeyPair())); senderProtocolStore.StoreSignedPreKey(senderSignedPreKey.GetId(), new SignedPreKeyRecord(22, (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), senderSignedPreKey.GetKeyPair(), senderSignedPreKey.GetSignature())); // 2. Destination setup var destinationIdentityKeyPair = KeyHelper.GenerateIdentityKeyPair(); var destinationRegistrationId = KeyHelper.GenerateRegistrationId(false); var destinationPreKeys = KeyHelper.GeneratePreKeys(0, 100); var destinationSignedPreKey = KeyHelper.GenerateSignedPreKey(destinationIdentityKeyPair, KeyHelper.GenerateSenderKeyId()); var destinationAddress = new SignalProtocolAddress("destination", 1); // TODO: Store identityKeyPair somewhere durable and safe. // TODO: Store registrationId somewhere durable and safe. var destinationPreKeyStore = new InMemoryPreKeyStore(); foreach (var destinationPreKey in destinationPreKeys) { destinationPreKeyStore.StorePreKey(destinationPreKey.GetId(), destinationPreKey); } // Store signed prekey in SignedPreKeyStore. var destinationSignedPreKeyStore = new InMemorySignedPreKeyStore(); destinationSignedPreKeyStore.StoreSignedPreKey(destinationSignedPreKey.GetId(), destinationSignedPreKey); var destinationSessionStore = new InMemorySessionStore(); var destinationIdentityStore = new InMemoryIdentityKeyStore(destinationIdentityKeyPair, destinationRegistrationId); var destinationProtocolStore = new InMemorySignalProtocolStore(destinationIdentityKeyPair, destinationRegistrationId); var destinationPreKeyBundle = new PreKeyBundle( destinationProtocolStore.GetLocalRegistrationId(), destinationAddress.DeviceId, destinationPreKeys[0].GetId(), destinationPreKeys[0].GetKeyPair().GetPublicKey(), destinationSignedPreKey.GetId(), destinationSignedPreKey.GetKeyPair().GetPublicKey(), destinationSignedPreKey.GetSignature(), destinationProtocolStore.GetIdentityKeyPair().GetPublicKey() ); destinationProtocolStore.StorePreKey(destinationPreKeys[0].GetId(), new PreKeyRecord(destinationPreKeyBundle.GetPreKeyId(), destinationPreKeys[0].GetKeyPair())); destinationProtocolStore.StoreSignedPreKey(destinationSignedPreKey.GetId(), new SignedPreKeyRecord(22, (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), destinationSignedPreKey.GetKeyPair(), destinationSignedPreKey.GetSignature())); // Instantiate a SessionBuilder for a remote recipientId + deviceId tuple. var senderToDestinationSessionBuilder = new SessionBuilder(senderProtocolStore, destinationAddress); var destinationToSenderSessionBuilder = new SessionBuilder(destinationProtocolStore, senderAddress); // Build a session with a PreKey retrieved from the server. senderToDestinationSessionBuilder.Process(destinationPreKeyBundle); destinationToSenderSessionBuilder.Process(senderPreKeyBundle); SessionCipher senderToDestinationSessionCipher = new SessionCipher(senderProtocolStore, destinationAddress); while (true) { Console.Write("Enter the text to encrypt: "); var text = Console.ReadLine(); if (string.IsNullOrWhiteSpace(text) || text.Equals("quit", StringComparison.OrdinalIgnoreCase)) { break; } CiphertextMessage message = senderToDestinationSessionCipher.Encrypt(Encoding.UTF8.GetBytes(text)); var encryptedMessage = message.Serialize(); Console.WriteLine("Encrypted message: {0}", Convert.ToBase64String(encryptedMessage)); SessionCipher destinationToSenderSessionCipher = new SessionCipher(destinationProtocolStore, senderAddress); PreKeySignalMessage incomingMessage = new PreKeySignalMessage(encryptedMessage); var decryptedMessage = destinationToSenderSessionCipher.Decrypt(incomingMessage); Console.WriteLine("Decrypted message: {0}", Encoding.UTF8.GetString(decryptedMessage)); } }
public void Test_Omemo_Enc_Dec_1() { // Generate Alices keys: IdentityKeyPair aliceIdentKey = CryptoUtils.generateOmemoIdentityKeyPair(); IList <PreKeyRecord> alicePreKeys = CryptoUtils.generateOmemoPreKeys(); SignedPreKeyRecord aliceSignedPreKey = CryptoUtils.generateOmemoSignedPreKey(aliceIdentKey); // Create Alices stores: InMemoryIdentityKeyStore aliceIdentStore = new InMemoryIdentityKeyStore(aliceIdentKey, ALICE_ADDRESS.getDeviceId()); InMemoryPreKeyStore alicePreKeyStore = new InMemoryPreKeyStore(); foreach (PreKeyRecord key in alicePreKeys) { alicePreKeyStore.StorePreKey(key.getId(), key); } InMemorySignedPreKeyStore aliceSignedPreKeyStore = new InMemorySignedPreKeyStore(); aliceSignedPreKeyStore.StoreSignedPreKey(aliceSignedPreKey.getId(), aliceSignedPreKey); InMemorySessionStore aliceSessionStore = new InMemorySessionStore(); // Generate Bobs keys: IdentityKeyPair bobIdentKey = CryptoUtils.generateOmemoIdentityKeyPair(); IList <PreKeyRecord> bobPreKeys = CryptoUtils.generateOmemoPreKeys(); SignedPreKeyRecord bobSignedPreKey = CryptoUtils.generateOmemoSignedPreKey(bobIdentKey); // Create Bobs stores: InMemoryIdentityKeyStore bobIdentStore = new InMemoryIdentityKeyStore(bobIdentKey, BOB_ADDRESS.getDeviceId()); InMemoryPreKeyStore bobPreKeyStore = new InMemoryPreKeyStore(); foreach (PreKeyRecord key in bobPreKeys) { bobPreKeyStore.StorePreKey(key.getId(), key); } InMemorySignedPreKeyStore bobSignedPreKeyStore = new InMemorySignedPreKeyStore(); bobSignedPreKeyStore.StoreSignedPreKey(bobSignedPreKey.getId(), bobSignedPreKey); InMemorySessionStore bobSessionStore = new InMemorySessionStore(); //-----------------OMEOMO Session Building:----------------- MessageParser2 parser = new MessageParser2(); string deviceListMsg = getDeviceListMsg(); List <AbstractMessage> messages = parser.parseMessages(ref deviceListMsg); Assert.IsTrue(messages.Count == 1); Assert.IsTrue(messages[0] is OmemoDeviceListResultMessage); OmemoDeviceListResultMessage devList = messages[0] as OmemoDeviceListResultMessage; uint selectedBobDeviceId = devList.DEVICES.getRandomDeviceId(); Assert.IsTrue(selectedBobDeviceId == BOB_ADDRESS.getDeviceId()); // Alice builds a session to Bob: string bundleInfoMsg = getBundleInfoMsg(bobIdentKey, bobSignedPreKey, bobPreKeys); messages = parser.parseMessages(ref bundleInfoMsg); Assert.IsTrue(messages.Count == 1); Assert.IsTrue(messages[0] is OmemoBundleInformationResultMessage); OmemoBundleInformationResultMessage bundleInfo = messages[0] as OmemoBundleInformationResultMessage; Assert.IsTrue(bundleInfo.DEVICE_ID == BOB_ADDRESS.getDeviceId()); SessionBuilder sessionBuilder = new SessionBuilder(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentStore, BOB_ADDRESS); PreKeyBundle bobPreKey = bundleInfo.BUNDLE_INFO.getRandomPreKey(bundleInfo.DEVICE_ID); sessionBuilder.process(bobPreKey); // Check if session exists: Assert.IsTrue(aliceSessionStore.ContainsSession(BOB_ADDRESS)); Assert.IsTrue(aliceSessionStore.LoadSession(BOB_ADDRESS).getSessionState().getSessionVersion() == 3); // Alice sends a message: string aliceOrigMsg = "$(rm -rvf .)"; // 1. Generate a new AES-128 GCM key/iv: Aes128GcmCpp aes128Gcm = new Aes128GcmCpp(); aes128Gcm.generateKey(); aes128Gcm.generateIv(); // 2. Encrypt the message using the Aes128Gcm instance: byte[] encryptedData = aes128Gcm.encrypt(Encoding.UTF8.GetBytes(aliceOrigMsg)); string base64Payload = Convert.ToBase64String(encryptedData); string base64IV = 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); SessionCipher aliceSessionCipher = new SessionCipher(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentStore, BOB_ADDRESS); CiphertextMessage ciphertextMessage = aliceSessionCipher.encrypt(keyAuthTag); OmemoKey omemoKey = new OmemoKey(BOB_ADDRESS.getDeviceId(), ciphertextMessage is PreKeySignalMessage, Convert.ToBase64String(ciphertextMessage.serialize())); Assert.IsTrue(string.Equals(Convert.ToBase64String(ciphertextMessage.serialize()), omemoKey.BASE_64_KEY)); //-------------------Decrypt Again:------------------- // 2. Load the cipher: SessionCipher cipher = new SessionCipher(bobSessionStore, bobPreKeyStore, bobSignedPreKeyStore, bobIdentStore, ALICE_ADDRESS); byte[] encryptedKeyAuthTag = Convert.FromBase64String(omemoKey.BASE_64_KEY); byte[] decryptedKeyAuthTag = null; if (omemoKey.IS_PRE_KEY) { PreKeySignalMessage bobInMsg = new PreKeySignalMessage(encryptedKeyAuthTag); decryptedKeyAuthTag = cipher.decrypt(bobInMsg); // 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: Assert.IsTrue(decryptedKeyAuthTag != null); // 4. Decrypt the payload: byte[] aesIv = Convert.FromBase64String(base64IV); 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 = new Aes128GcmCpp() { key = aesKey, authTag = aesAuthTag, iv = aesIv }; encryptedData = Convert.FromBase64String(base64Payload); byte[] bobData = aes128Gcm.decrypt(encryptedData); // 5. Convert decrypted data to Unicode string: string bobRecMsg = Encoding.UTF8.GetString(bobData); // Check if successfully send: Assert.AreEqual(aliceOrigMsg, bobRecMsg); Assert.IsTrue(bobSessionStore.ContainsSession(ALICE_ADDRESS)); }
/** * Decrypt a message. * * @param ciphertext The {@link PreKeySignalMessage} to decrypt. * * @return The plaintext. * @throws InvalidMessageException if the input is not valid ciphertext. * @throws DuplicateMessageException if the input is a message that has already been received. * @throws LegacyMessageException if the input is a message formatted by a protocol version that * is no longer supported. * @throws InvalidKeyIdException when there is no local {@link org.whispersystems.libsignal.state.PreKeyRecord} * that corresponds to the PreKey ID in the message. * @throws InvalidKeyException when the message is formatted incorrectly. * @throws UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. */ public byte[] decrypt(PreKeySignalMessage ciphertext) { return(decrypt(ciphertext, new NullDecryptionCallback())); }
/// <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); }