private KeyExchangeMessage processInitiate(KeyExchangeMessage message)
        {
            uint          flags         = KeyExchangeMessage.RESPONSE_FLAG;
            SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress);

            if (!Curve.verifySignature(message.getIdentityKey().getPublicKey(),
                                       message.getBaseKey().serialize(),
                                       message.getBaseKeySignature()))
            {
                throw new InvalidKeyException("Bad signature!");
            }

            SymmetricSignalProtocolParameters.Builder builder = SymmetricSignalProtocolParameters.newBuilder();

            if (!sessionRecord.getSessionState().hasPendingKeyExchange())
            {
                builder.setOurIdentityKey(identityKeyStore.GetIdentityKeyPair())
                .setOurBaseKey(Curve.generateKeyPair())
                .setOurRatchetKey(Curve.generateKeyPair());
            }
            else
            {
                builder.setOurIdentityKey(sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey())
                .setOurBaseKey(sessionRecord.getSessionState().getPendingKeyExchangeBaseKey())
                .setOurRatchetKey(sessionRecord.getSessionState().getPendingKeyExchangeRatchetKey());
                flags |= KeyExchangeMessage.SIMULTAENOUS_INITIATE_FLAG;
            }

            builder.setTheirBaseKey(message.getBaseKey())
            .setTheirRatchetKey(message.getRatchetKey())
            .setTheirIdentityKey(message.getIdentityKey());

            SymmetricSignalProtocolParameters parameters = builder.create();

            if (!sessionRecord.isFresh())
            {
                sessionRecord.archiveCurrentState();
            }

            RatchetingSession.initializeSession(sessionRecord.getSessionState(),
                                                parameters);

            sessionStore.StoreSession(remoteAddress, sessionRecord);
            identityKeyStore.SaveIdentity(remoteAddress.getName(), message.getIdentityKey());

            byte[] baseKeySignature = Curve.calculateSignature(parameters.getOurIdentityKey().getPrivateKey(),
                                                               parameters.getOurBaseKey().getPublicKey().serialize());

            return(new KeyExchangeMessage(sessionRecord.getSessionState().getSessionVersion(),
                                          message.getSequence(), flags,
                                          parameters.getOurBaseKey().getPublicKey(),
                                          baseKeySignature, parameters.getOurRatchetKey().getPublicKey(),
                                          parameters.getOurIdentityKey().getPublicKey()));
        }
        private void processResponse(KeyExchangeMessage message)
        {
            SessionRecord sessionRecord                  = sessionStore.LoadSession(remoteAddress);
            SessionState  sessionState                   = sessionRecord.getSessionState();
            bool          hasPendingKeyExchange          = sessionState.hasPendingKeyExchange();
            bool          isSimultaneousInitiateResponse = message.isResponseForSimultaneousInitiate();

            if (!hasPendingKeyExchange || sessionState.getPendingKeyExchangeSequence() != message.getSequence())
            {
                Debug.WriteLine("No matching sequence for response. Is simultaneous initiate response: " + isSimultaneousInitiateResponse);
                if (!isSimultaneousInitiateResponse)
                {
                    throw new StaleKeyExchangeException();
                }
                else
                {
                    return;
                }
            }

            SymmetricSignalProtocolParameters.Builder parameters = SymmetricSignalProtocolParameters.newBuilder();

            parameters.setOurBaseKey(sessionRecord.getSessionState().getPendingKeyExchangeBaseKey())
            .setOurRatchetKey(sessionRecord.getSessionState().getPendingKeyExchangeRatchetKey())
            .setOurIdentityKey(sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey())
            .setTheirBaseKey(message.getBaseKey())
            .setTheirRatchetKey(message.getRatchetKey())
            .setTheirIdentityKey(message.getIdentityKey());

            if (!sessionRecord.isFresh())
            {
                sessionRecord.archiveCurrentState();
            }

            RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters.create());

            if (!Curve.verifySignature(message.getIdentityKey().getPublicKey(),
                                       message.getBaseKey().serialize(),
                                       message.getBaseKeySignature()))
            {
                throw new InvalidKeyException("Base key signature doesn't match!");
            }

            sessionStore.StoreSession(remoteAddress, sessionRecord);
            identityKeyStore.SaveIdentity(remoteAddress.getName(), message.getIdentityKey());
        }