/// <summary>
        /// This method asks the server for a list of identifiers paired with method
        /// names and -parameter types. This is used when invoking methods server side.
        /// </summary>
        protected override void SyncInterface(Type serviceType, 
            string username = null, string password = null)
        {
            if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
            {
                //do zk protocol authentication
                var sr = new ZkProtocol();

                // Step 1. Client sends username and ephemeral hash of random number.
                var aRand = sr.CryptRand();
                var aClientEphemeral = sr.GetClientEphemeralA(aRand);

                // send username and aClientEphemeral to server
                _binWriter.Write((int)MessageType.ZkInitiate);
                _binWriter.Write(username);
                _binWriter.Write(aClientEphemeral); //always 32 bytes

                // get response from server
                var userFound = _binReader.ReadBoolean();
                if (!userFound)
                {
                    throw new InvalidCredentialException("authentication failed");
                }
                var salt = _binReader.ReadBytes(32);
                var bServerEphemeral = _binReader.ReadBytes(32);

                // Step 3. Client and server calculate random scramble of ephemeral hash values exchanged.
                var clientScramble = sr.CalculateRandomScramble(aClientEphemeral, bServerEphemeral);

                // Step 4. Client computes session key
                var clientSessionKey = sr.ClientComputeSessionKey(salt, username, password,
                    aClientEphemeral, bServerEphemeral, clientScramble);

                // Step 6. Client creates hash of session key and sends to server. Server creates same key and verifies.
                var clientSessionHash = sr.ClientCreateSessionHash(username, salt, aClientEphemeral,
                    bServerEphemeral, clientSessionKey);
                // send to server and server verifies
                _binWriter.Write((int)MessageType.ZkProof);
                _binWriter.Write(clientSessionHash); //always 32 bytes

                // get response
                var serverVerified = _binReader.ReadBoolean();
                if (!serverVerified)
                {
                    throw new InvalidCredentialException("authentication failed");
                }
                var serverSessionHash = _binReader.ReadBytes(32);
                var clientServerSessionHash = sr.ServerCreateSessionHash(aClientEphemeral,
                    clientSessionHash, clientSessionKey);
                if (!serverSessionHash.IsEqualTo(clientServerSessionHash))
                {
                    throw new InvalidCredentialException("authentication failed");
                }
                _zkCrypto = new ZkCrypto(clientSessionKey, clientScramble);
            }

            if (!_syncInfoCache.TryGetValue(serviceType, out _syncInfo))
            {
                //write the message type
                _binWriter.Write((int)MessageType.SyncInterface);
                if (null != _zkCrypto)
                {
                    //sync interface with encryption
                    var assemName = serviceType.FullName;
                    var assemblyNameEncrypted = _zkCrypto.Encrypt(assemName.ConvertToBytes());
                    _binWriter.Write(assemblyNameEncrypted.Length);
                    _binWriter.Write(assemblyNameEncrypted);
                }
                else
                {
                    _binWriter.Write(serviceType.FullName);
                }
                //read sync data
                var len = _binReader.ReadInt32();
                //len is zero when AssemblyQualifiedName not same version or not found
            #if (!NET35)
                if (len == 0) throw new TypeAccessException("SyncInterface failed. Type or version of type unknown.");
            #else
                if (len == 0) throw new Exception("SyncInterface failed. Type or version of type unknown.");
            #endif
                var bytes = _binReader.ReadBytes(len);
                if (null != _zkCrypto)
                {
                    bytes = _zkCrypto.Decrypt(bytes);
                }
                _syncInfo = bytes.ToDeserializedObject<ServiceSyncInfo>();
                _syncInfoCache.AddOrUpdate(serviceType, _syncInfo, (t, info) => _syncInfo);
            }
        }
        public void SimpleProtocolTest()
        {
            var sr = new ZkProtocol();
            var username = "******";
            var pwd = "cc3a6a12-0e5b-47fb-ae45-3485e34582d4";

            // prerequisit: generate password hash that would be stored on server
            var pwdHash = sr.HashCredentials(username, pwd);

            // Step 1. Client sends username and ephemeral hash of random number.
            var aRand = sr.CryptRand();
            var aClientEphemeral = sr.GetClientEphemeralA(aRand);
            // send username and aClientEphemeral to server

            // Step 2. Server looks up username, gets pwd hash, and sends client ephemeral has of params.
            var bRand = sr.CryptRand();
            var bServerEphemeral = sr.GetServerEphemeralB(pwdHash.Salt, pwdHash.Verifier, bRand);
            // send salt and bServerEphemeral to client
            var clientSalt = pwdHash.Salt;

            // Step 3. Client and server calculate random scramble of ephemeral hash values exchanged.
            var clientScramble = sr.CalculateRandomScramble(aClientEphemeral, bServerEphemeral);
            var serverScramble = sr.CalculateRandomScramble(aClientEphemeral, bServerEphemeral);

            var scrambleSame = clientScramble.IsEqualTo(serverScramble);

            // Step 4. Client computes session key
            var clientSessionKey = sr.ClientComputeSessionKey(clientSalt, username, pwd, aClientEphemeral, bServerEphemeral, clientScramble);

            // Step 5. Server computes session key
            var serverSessionKey = sr.ServerComputeSessionKey(pwdHash.Salt, pwdHash.Key, aClientEphemeral, bServerEphemeral, serverScramble);

            var sessionKeysSame = clientSessionKey.IsEqualTo(serverSessionKey);

            // Step 6. Client creates hash of session key and sends to server. Server creates same key and verifies.
            var clientSessionHash = sr.ClientCreateSessionHash(username, pwdHash.Salt, aClientEphemeral,
                bServerEphemeral, clientSessionKey);
            // send to server and server verifies
            // server validates clientSessionHash is same as serverClientSessionHash
            var serverClientSessionHash = sr.ClientCreateSessionHash(username, pwdHash.Salt, aClientEphemeral,
                bServerEphemeral, serverSessionKey);

            var clientEqualToServer = clientSessionHash.IsEqualTo(serverClientSessionHash);

            // Step 7. Server creates hash of session key and sends to client. Client creates same key and verifies.
            var serverSessionHash = sr.ServerCreateSessionHash(aClientEphemeral, clientSessionHash, serverSessionKey);
            // server sends serverSessionHash to client
            // validate that serverSessionHash is same as clientServerSessionHash
            var clientServerSessionHash = sr.ServerCreateSessionHash(aClientEphemeral, clientSessionHash, clientSessionKey);

            var serverEqualToClient = serverSessionHash.IsEqualTo(clientServerSessionHash);

            //proof
            Assert.IsTrue(sessionKeysSame);
            Assert.IsTrue(scrambleSame);
            Assert.IsTrue(clientEqualToServer);
            Assert.IsTrue(serverEqualToClient);

            var data = sr.Combine(sr.CryptRand(), sr.CryptRand(), sr.CryptRand());
            var crypto = new ZkCrypto(clientSessionKey, clientScramble);
            var encrypted = crypto.Encrypt(data);
            var decrypted = crypto.Decrypt(encrypted);
            var cryptSame = data.IsEqualTo(decrypted);
            Assert.IsTrue(cryptSame);
        }