Beispiel #1
0
        /// <summary>
        /// Perform a client-side handshake.
        /// </summary>
        /// <returns>A HandshakeResult class containing information about the handshake attempt.</returns>
        private HandshakeResult _PerformHandshake(List <CipherSuiteIdentifier> allowed_suites)
        {
            Suite = new CipherSuite()
            {
                Cipher = new IdentityCipher(),
                MAC    = new IdentityMAC()
            };

            SendMessage(MessageHelpers.CreateClientHello(this, allowed_suites));

            Message msg = Receive();

            if (msg == null)
            {
                var result = new HandshakeResult(HandshakeResultType.ConnectionClosed, "Connection closed.");
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            if (!msg.CheckType(MessageType.ServerHello, 0x00))
            {
                var result = new HandshakeResult(HandshakeResultType.UnexpectedMessage, "Received message of type {0}/0x{1:X} while expecting ServerHello/0x00. Terminating handshake.", msg.Type, msg.Subtype);
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            var chosen_suite = msg.Store["chosen_suite"];

            if (chosen_suite.Length == 0)
            {
                var result = new HandshakeResult(HandshakeResultType.NoCipherSuite, "Server refused to pick a cipher suite.");
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            if (!allowed_suites.Any(s => s.Serialize().SequenceEqual(chosen_suite)))
            {
                var result = new HandshakeResult(HandshakeResultType.NoCipherSuite, "Server picked a forbidden suite.");
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            Suite = new CipherSuiteIdentifier(chosen_suite).CreateSuite();
            var real_cipher = Suite.Cipher;
            var real_mac    = Suite.MAC;

            Suite.Cipher = new IdentityCipher();
            Suite.MAC    = new IdentityMAC();
            Suite.Initialize();

            SendMessage(MessageHelpers.CreateAuthRequest(this));

            msg = Receive();

            if (msg == null)
            {
                var result = new HandshakeResult(HandshakeResultType.ConnectionClosed, "Connection closed.");
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            if (!msg.CheckType(MessageType.AuthResponse, 0x00))
            {
                var result = new HandshakeResult(HandshakeResultType.UnexpectedMessage, "Received message of type {0}/0x{1:X} while expecting AuthRequest/0x00. Terminating handshake.", msg.Type, msg.Subtype);
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            byte[] rsa_public_key  = msg.Store["rsa_public_key"];
            byte[] rsa_signature   = msg.Store["rsa_signature"];
            byte[] ecdh_public_key = msg.Store["ecdh_public_key"];
            byte[] ecdh_signature  = msg.Store["ecdh_signature"];

            PeerSignature = rsa_signature;

            byte[] shared_salt    = msg.Store["shared_salt"];
            byte[] salt_signature = msg.Store["shared_salt_signature"];

            byte[]   timestamp    = msg.Store["timestamp"];
            DateTime timestamp_dt = MessageHelpers.GetDateTime(BitConverter.ToInt64(timestamp, 0));
            TimeSpan difference   = (DateTime.UtcNow - timestamp_dt).Duration();

            if (!timestamp.SequenceEqual(ecdh_public_key.Skip(ecdh_public_key.Length - 8)))
            {
                var result = new HandshakeResult(HandshakeResultType.UntrustedTimestamp, "Timestamp mismatch between ECDH public key and explicit timestamp. Terminating handshake.");
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            if (difference > MaximumTimeMismatch)
            {
                var result = new HandshakeResult(HandshakeResultType.ReplayAttack, "Timestamp difference between client and server exceeds allowed window of {0}(provided timestamp is {1}, our clock is {2}). Terminating handshake.", MaximumTimeMismatch, timestamp_dt, DateTime.UtcNow);
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            Log.Info("Clock drift between peers is {0}.", difference);

            if (!RsaHelpers.VerifyData(rsa_public_key, rsa_signature, CertificateAuthority))
            {
                var result = new HandshakeResult(HandshakeResultType.UntrustedStaticPublicKey, "Failed to verify RSA public key against certificate authority. Terminating handshake.");
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            var parameters = (RsaKeyParameters)RsaHelpers.PemDeserialize(Encoding.UTF8.GetString(rsa_public_key));

            if (!RsaHelpers.VerifyData(ecdh_public_key, ecdh_signature, parameters))
            {
                var result = new HandshakeResult(HandshakeResultType.UntrustedEphemeralPublicKey, "Failed to verify ECDH public key authenticity. Terminating handshake.");
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            Suite.SharedSalt = shared_salt;
            Suite.Cipher     = real_cipher;
            Suite.MAC        = real_mac;
            var shared_secret = Suite.FinalizeKeyExchange(ecdh_public_key);

            StartThreads();

            var result_final = new HandshakeResult(HandshakeResultType.Successful, "Handshake successful.")
            {
                TimeDrift = difference.TotalSeconds
            };

            Log.Info(result_final.Message);
            Log.Info("Cipher: {0}, key exchange: {1}, MAC: {2}", Suite.Cipher.HumanName, Suite.KeyExchange.HumanName, Suite.MAC.HumanName);
            return(result_final);
        }
Beispiel #2
0
        /// <summary>
        /// Perform a server-side handshake.
        /// </summary>
        /// <returns>A HandshakeResult class containing information about the handshake attempt.</returns>
        private HandshakeResult _PerformHandshake(List <CipherSuiteIdentifier> allowed_suites)
        {
            Suite = new CipherSuite()
            {
                Cipher = new IdentityCipher(),
                MAC    = new IdentityMAC()
            };

            Message msg = Receive();

            Log.Info(msg.Type);

            if (msg == null)
            {
                var result = new HandshakeResult(HandshakeResultType.ConnectionClosed, "Connection closed.");
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            if (!msg.CheckType(MessageType.ClientHello, 0x00))
            {
                var result = new HandshakeResult(HandshakeResultType.UnexpectedMessage, "Received message of type {0}/0x{1:X2} while expecting ClientHello/0x00. Terminating handshake.", msg.Type, msg.Subtype);
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            var peer_suites = new List <CipherSuiteIdentifier>();
            var suite_data  = msg.Store["allowed_suites"];

            Log.Debug("{0} bytes of allowed suites", suite_data.Length);

            for (int i = 0; i < suite_data.Length; i += CipherSuiteIdentifier.IdentifierLength)
            {
                peer_suites.Add(new CipherSuiteIdentifier(suite_data, i));
            }

            peer_suites = peer_suites.Distinct().ToList();

            var suite_scores = new Dictionary <CipherSuiteIdentifier, int>();

            for (int i = 0; i < allowed_suites.Count; i++)
            {
                for (int j = 0; j < peer_suites.Count; j++)
                {
                    var our_suite   = allowed_suites[i];
                    var their_suite = peer_suites[j];

                    if (our_suite != their_suite)
                    {
                        continue;
                    }

                    suite_scores[our_suite] = i + j;
                }
            }

            var chosen_suite = suite_scores.OrderBy(p => p.Value).First().Key;

            SendMessage(MessageHelpers.CreateServerHello(this, chosen_suite));

            Suite = chosen_suite.CreateSuite();
            var real_cipher = Suite.Cipher;
            var real_mac    = Suite.MAC;

            Suite.Cipher = new IdentityCipher(); // temporarily set suite cipher to IdentityCipher so we can continue handshake
            Suite.MAC    = new IdentityMAC();    // likewise
            Suite.Initialize();

            msg = Receive();

            if (!msg.CheckType(MessageType.AuthRequest, 0x00))
            {
                var result = new HandshakeResult(HandshakeResultType.UnexpectedMessage, "Received message of type {0}/0x{1:X2} while expecting AuthRequest/0x00. Terminating handshake.", msg.Type, msg.Subtype);
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            byte[] rsa_public_key  = msg.Store["rsa_public_key"];
            byte[] rsa_signature   = msg.Store["rsa_signature"];
            byte[] ecdh_public_key = msg.Store["ecdh_public_key"];
            byte[] ecdh_signature  = msg.Store["ecdh_signature"];
            PeerSignature = rsa_signature;

            byte[]   timestamp    = msg.Store["timestamp"];
            DateTime timestamp_dt = MessageHelpers.GetDateTime(BitConverter.ToInt64(timestamp, 0));
            TimeSpan difference   = (DateTime.UtcNow - timestamp_dt).Duration();

            if (msg.Store.ContainsKey("attestation_token"))
            {
                AttestationToken = msg.Store["attestation_token"];
            }

            if (!timestamp.SequenceEqual(ecdh_public_key.Skip(ecdh_public_key.Length - 8)))
            {
                var result = new HandshakeResult(HandshakeResultType.UntrustedTimestamp, "Timestamp mismatch between ECDH public key and explicit timestamp. Terminating handshake.");
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            if (difference > MaximumTimeMismatch)
            {
                var result = new HandshakeResult(HandshakeResultType.ReplayAttack, "Timestamp difference between client and server exceeds allowed window of {0}(provided timestamp is {1}, our clock is {2}). Terminating handshake.", MaximumTimeMismatch, timestamp_dt, DateTime.UtcNow);
                Log.Error(result.Message);
                Tunnel.Close();
                return(result);
            }

            Log.Info("Clock drift between peers is {0}.", difference);

            if (AuthenticateClient)
            {
                if (!RsaHelpers.VerifyData(rsa_public_key, rsa_signature, CertificateAuthority))
                {
                    var result = new HandshakeResult(HandshakeResultType.UntrustedStaticPublicKey, "Failed to verify RSA public key against certificate authority. Terminating handshake.");
                    Log.Error(result.Message);
                    Tunnel.Close();
                    return(result);
                }
            }

            var parameters = (RsaKeyParameters)RsaHelpers.PemDeserialize(Encoding.UTF8.GetString(rsa_public_key));

            if (AuthenticateClient)
            {
                if (!RsaHelpers.VerifyData(ecdh_public_key, ecdh_signature, parameters))
                {
                    var result = new HandshakeResult(HandshakeResultType.UntrustedEphemeralPublicKey, "Failed to verify ECDH public key authenticity. Terminating handshake.");
                    Log.Error(result.Message);
                    Tunnel.Close();
                    return(result);
                }
            }

            Suite.SharedSalt = new byte[16];
            RNG.GetBytes(Suite.SharedSalt);

            SendMessage(MessageHelpers.CreateAuthResponse(this));

            Suite.Cipher = real_cipher;
            Suite.MAC    = real_mac;
            var shared_secret = Suite.FinalizeKeyExchange(ecdh_public_key);

            StartThreads();

            var result_final = new HandshakeResult(HandshakeResultType.Successful, "Handshake successful.");

            result_final.TimeDrift = difference.TotalSeconds;
            Log.Info(result_final.Message);
            Log.Info("Cipher: {0}, key exchange: {1}, MAC: {2}", Suite.Cipher.HumanName, Suite.KeyExchange.HumanName, Suite.MAC.HumanName);
            return(result_final);
        }