Exemplo n.º 1
0
        public void TestBadMessageBundle()
        {
            ISignalProtocolStore aliceStore          = new TestInMemorySignalProtocolStore();
            SessionBuilder       aliceSessionBuilder = new SessionBuilder(aliceStore, BobAddress);

            ISignalProtocolStore 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, BobAddress);
            CiphertextMessage outgoingMessageOne = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage));

            Assert.AreEqual(CiphertextMessage.PrekeyType, outgoingMessageOne.GetMessageType());

            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, AliceAddress);

            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));
        }
Exemplo n.º 2
0
        public void TestRepeatBundleMessageV3()
        {
            ISignalProtocolStore aliceStore          = new TestInMemorySignalProtocolStore();
            SessionBuilder       aliceSessionBuilder = new SessionBuilder(aliceStore, BobAddress);

            ISignalProtocolStore 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, BobAddress);
            CiphertextMessage outgoingMessageOne = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage));
            CiphertextMessage outgoingMessageTwo = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage));

            Assert.AreEqual(CiphertextMessage.PrekeyType, outgoingMessageOne.GetMessageType());
            Assert.AreEqual(CiphertextMessage.PrekeyType, outgoingMessageTwo.GetMessageType());

            PreKeySignalMessage incomingMessage = new PreKeySignalMessage(outgoingMessageOne.Serialize());

            SessionCipher bobSessionCipher = new SessionCipher(bobStore, AliceAddress);

            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));
        }
Exemplo n.º 3
0
        public void TestOptionalOneTimePreKey()
        {
            ISignalProtocolStore aliceStore          = new TestInMemorySignalProtocolStore();
            SessionBuilder       aliceSessionBuilder = new SessionBuilder(aliceStore, BobAddress);

            ISignalProtocolStore 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(BobAddress));
            Assert.AreEqual((uint)3, aliceStore.LoadSession(BobAddress).GetSessionState().GetSessionVersion());

            String            originalMessage    = "L'homme est condamné à être libre";
            SessionCipher     aliceSessionCipher = new SessionCipher(aliceStore, BobAddress);
            CiphertextMessage outgoingMessage    = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage));

            Assert.AreEqual(outgoingMessage.GetMessageType(), CiphertextMessage.PrekeyType);

            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, AliceAddress);

            byte[] plaintext = bobSessionCipher.Decrypt(incomingMessage);

            Assert.IsTrue(bobStore.ContainsSession(AliceAddress));
            Assert.AreEqual((uint)3, bobStore.LoadSession(AliceAddress).GetSessionState().GetSessionVersion());
            Assert.IsNotNull(bobStore.LoadSession(AliceAddress).GetSessionState().GetAliceBaseKey());
            Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext));
        }
        /// <summary>
        /// Build a new session from a {@link org.whispersystems.libsignal.state.PreKeyBundle} retrieved from
        /// a server.
        /// </summary>
        /// @param preKey A PreKey for the destination recipient, retrieved from a server.
        /// @throws InvalidKeyException when the {@link org.whispersystems.libsignal.state.PreKeyBundle} is
        ///                             badly formatted.
        /// @throws org.whispersystems.libsignal.UntrustedIdentityException when the sender's
        ///                                                                  {@link IdentityKey} is not
        ///                                                                  trusted.
        ///
        public void Process(PreKeyBundle preKey)
        {
            lock (SessionCipher.SessionLock)
            {
                if (!_identityKeyStore.IsTrustedIdentity(_remoteAddress, preKey.GetIdentityKey(), Direction.Sending))
                {
                    throw new UntrustedIdentityException(_remoteAddress.Name, preKey.GetIdentityKey());
                }

                if (preKey.GetSignedPreKey() != null &&
                    !Curve.VerifySignature(preKey.GetIdentityKey().GetPublicKey(),
                                           preKey.GetSignedPreKey().Serialize(),
                                           preKey.GetSignedPreKeySignature()))
                {
                    throw new InvalidKeyException("Invalid signature on device key!");
                }

                if (preKey.GetSignedPreKey() == null)
                {
                    throw new InvalidKeyException("No signed prekey!");
                }

                SessionRecord      sessionRecord     = _sessionStore.LoadSession(_remoteAddress);
                EcKeyPair          ourBaseKey        = Curve.GenerateKeyPair();
                IEcPublicKey       theirSignedPreKey = preKey.GetSignedPreKey();
                IEcPublicKey       test = preKey.GetPreKey();
                May <IEcPublicKey> theirOneTimePreKey   = (test == null) ? May <IEcPublicKey> .NoValue : new May <IEcPublicKey>(test);
                May <uint>         theirOneTimePreKeyId = theirOneTimePreKey.HasValue ? new May <uint>(preKey.GetPreKeyId()) :
                                                          May <uint> .NoValue;

                AliceSignalProtocolParameters.Builder parameters = AliceSignalProtocolParameters.NewBuilder();

                parameters.SetOurBaseKey(ourBaseKey)
                .SetOurIdentityKey(_identityKeyStore.GetIdentityKeyPair())
                .SetTheirIdentityKey(preKey.GetIdentityKey())
                .SetTheirSignedPreKey(theirSignedPreKey)
                .SetTheirRatchetKey(theirSignedPreKey)
                .SetTheirOneTimePreKey(theirOneTimePreKey);

                if (!sessionRecord.IsFresh())
                {
                    sessionRecord.ArchiveCurrentState();
                }

                RatchetingSession.InitializeSession(sessionRecord.GetSessionState(), parameters.Create());

                sessionRecord.GetSessionState().SetUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.GetSignedPreKeyId(), ourBaseKey.GetPublicKey());
                sessionRecord.GetSessionState().SetLocalRegistrationId(_identityKeyStore.GetLocalRegistrationId());
                sessionRecord.GetSessionState().SetRemoteRegistrationId(preKey.GetRegistrationId());
                sessionRecord.GetSessionState().SetAliceBaseKey(ourBaseKey.GetPublicKey().Serialize());

                _identityKeyStore.SaveIdentity(_remoteAddress, preKey.GetIdentityKey());

                _sessionStore.StoreSession(_remoteAddress, sessionRecord);
            }
        }
Exemplo n.º 5
0
        /**
         * Build a new session from a {@link org.whispersystems.libaxolotl.state.PreKeyBundle} retrieved from
         * a server.
         *
         * @param preKey A PreKey for the destination recipient, retrieved from a server.
         * @throws InvalidKeyException when the {@link org.whispersystems.libaxolotl.state.PreKeyBundle} is
         *                             badly formatted.
         * @throws org.whispersystems.libaxolotl.UntrustedIdentityException when the sender's
         *                                                                  {@link IdentityKey} is not
         *                                                                  trusted.
         */

        public void Process(PreKeyBundle preKey)
        {
            lock (SessionCipher.SESSION_LOCK)
            {
                if (!identityKeyStore.IsTrustedIdentity(remoteAddress.GetName(), preKey.GetIdentityKey()))
                {
                    throw new UntrustedIdentityException(remoteAddress.GetName(), preKey.GetIdentityKey());
                }

                if (preKey.GetSignedPreKey() != null &&
                    !Curve.VerifySignature(preKey.GetIdentityKey().GetPublicKey(),
                                           preKey.GetSignedPreKey().Serialize(),
                                           preKey.GetSignedPreKeySignature()))
                {
                    throw new InvalidKeyException("Invalid Signature on Device Key!");
                }

                if (preKey.GetSignedPreKey() == null && preKey.GetPreKey() == null)
                {
                    throw new InvalidKeyException("Both Signed and Unsigned Prekeys are Absent!");
                }

                bool              supportsV3        = preKey.GetSignedPreKey() != null;
                SessionRecord     sessionRecord     = sessionStore.LoadSession(remoteAddress);
                ECKeyPair         ourBaseKey        = Curve.GenerateKeyPair();
                ECPublicKey       theirSignedPreKey = supportsV3 ? preKey.GetSignedPreKey() : preKey.GetPreKey();
                ECPublicKey       test = preKey.GetPreKey(); // TODO: Cleanup
                May <ECPublicKey> theirOneTimePreKey   = (test == null) ? May <ECPublicKey> .NoValue : new May <ECPublicKey>(test);
                May <uint>        theirOneTimePreKeyId = theirOneTimePreKey.HasValue ? new May <uint>(preKey.GetPreKeyId()) : May <uint> .NoValue;

                AliceAxolotlParameters.Builder parameters = AliceAxolotlParameters.NewBuilder();

                parameters.SetOurBaseKey(ourBaseKey)
                .SetOurIdentityKey(identityKeyStore.GetIdentityKeyPair())
                .SetTheirIdentityKey(preKey.GetIdentityKey())
                .SetTheirSignedPreKey(theirSignedPreKey)
                .SetTheirRatchetKey(theirSignedPreKey)
                .SetTheirOneTimePreKey(supportsV3 ? theirOneTimePreKey : May <ECPublicKey> .NoValue);

                if (!sessionRecord.IsFresh())
                {
                    sessionRecord.ArchiveCurrentState();
                }

                RatchetingSession.InitializeSession(sessionRecord.GetSessionState(),
                                                    supportsV3 ? (uint)3 : 2,
                                                    parameters.Create());

                sessionRecord.GetSessionState().SetUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.GetSignedPreKeyId(), ourBaseKey.GetPublicKey());
                sessionRecord.GetSessionState().SetLocalRegistrationId(identityKeyStore.GetLocalRegistrationId());
                sessionRecord.GetSessionState().SetRemoteRegistrationId(preKey.GetRegistrationId());
                sessionRecord.GetSessionState().SetAliceBaseKey(ourBaseKey.GetPublicKey().Serialize());

                sessionStore.StoreSession(remoteAddress, sessionRecord);
                identityKeyStore.SaveIdentity(remoteAddress.GetName(), preKey.GetIdentityKey());
            }
        }
        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));
            }
        }
Exemplo n.º 7
0
        public void TestBasicPreKeyV3()
        {
            ISignalProtocolStore aliceStore          = new TestInMemorySignalProtocolStore();
            SessionBuilder       aliceSessionBuilder = new SessionBuilder(aliceStore, BobAddress);

            ISignalProtocolStore 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(BobAddress));
            Assert.AreEqual((uint)3, aliceStore.LoadSession(BobAddress).GetSessionState().GetSessionVersion());

            String            originalMessage    = "L'homme est condamné à être libre";
            SessionCipher     aliceSessionCipher = new SessionCipher(aliceStore, BobAddress);
            CiphertextMessage outgoingMessage    = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage));

            Assert.AreEqual(CiphertextMessage.PrekeyType, outgoingMessage.GetMessageType());

            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, AliceAddress);

            byte[] plaintext = bobSessionCipher.Decrypt(incomingMessage, new BobDecryptionCallback(bobStore, originalMessage));

            Assert.IsTrue(bobStore.ContainsSession(AliceAddress));
            Assert.AreEqual((uint)3, bobStore.LoadSession(AliceAddress).GetSessionState().GetSessionVersion());
            Assert.IsNotNull(bobStore.LoadSession(AliceAddress).GetSessionState().GetAliceBaseKey());
            Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext));

            CiphertextMessage bobOutgoingMessage = bobSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage));

            Assert.AreEqual(CiphertextMessage.WhisperType, bobOutgoingMessage.GetMessageType());

            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, BobAddress);
            aliceSessionCipher  = new SessionCipher(aliceStore, BobAddress);

            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(AliceAddress, 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
            }
        }