/// <summary> /// Creates a new handshake request(client-side) and returns it. /// </summary> /// <param name="link">The ClientLink to create the request packet for.</param> /// <returns>The created message.</returns> public static Message CreateAuthRequest(ClientLink link) { Message msg = new Message(MessageType.AuthRequest, 0x00); byte[] timestamp = GetTimestamp(); msg.Store["ecdh_public_key"] = link.Suite.GetKeyExchangeData().Concat(timestamp).ToArray(); msg.Store["timestamp"] = timestamp; if (link.AuthenticateSelf) { msg.Store["rsa_public_key"] = Encoding.UTF8.GetBytes(RsaHelpers.PemSerialize(link.Certificate.Public)); msg.Store["rsa_signature"] = link.Signature; msg.Store["ecdh_signature"] = RsaHelpers.SignData(msg.Store["ecdh_public_key"], link.Certificate); } else { msg.Store["rsa_public_key"] = new byte[0]; msg.Store["rsa_signature"] = new byte[0]; msg.Store["ecdh_signature"] = new byte[0]; } if (link.AttestationToken != null) { msg.Store["attestation_token"] = link.AttestationToken; } return(msg); }
/// <summary> /// Loads a keypair and signature from the provided strings. /// </summary> /// <param name="ca">The public key of the certificate authority in text form.</param> /// <param name="key">Both our public and private key, concatenated, in text form.</param> /// <param name="sign">The signature in Base64.</param> public void LoadCertificatesFromText(string ca, string key, string sign) { CertificateAuthority = (RsaKeyParameters)RsaHelpers.PemDeserialize(Encoding.UTF8.GetString(Convert.FromBase64String(ca))); Log.Debug("Loaded certificate authority."); Certificate = (AsymmetricCipherKeyPair)RsaHelpers.PemDeserialize(Encoding.UTF8.GetString(Convert.FromBase64String(key))); Log.Debug("Loaded certificate."); Signature = Convert.FromBase64String(sign); Log.Debug("Loaded signature."); }
/// <summary> /// Creates a new handshake response(server-side) and returns it. /// </summary> /// <param name="link">The ServerLink to create the resposne packet for.</param> /// <returns>The created message.</returns> public static Message CreateAuthResponse(EncryptedLink link) { Message msg = new Message(MessageType.AuthResponse, 0x00); byte[] timestamp = GetTimestamp(); msg.Store["rsa_public_key"] = Encoding.UTF8.GetBytes(RsaHelpers.PemSerialize(link.Certificate.Public)); msg.Store["rsa_signature"] = link.Signature; msg.Store["ecdh_public_key"] = link.Suite.GetKeyExchangeData().Concat(timestamp).ToArray(); msg.Store["ecdh_signature"] = RsaHelpers.SignData(msg.Store["ecdh_public_key"], link.Certificate); msg.Store["shared_salt"] = link.Suite.SharedSalt; msg.Store["shared_salt_signature"] = RsaHelpers.SignData(link.Suite.SharedSalt, link.Certificate); msg.Store["timestamp"] = timestamp; return(msg); }
/// <summary> /// Loads a keypair and signature from the provided paths. /// </summary> /// <param name="ca_path">The file that contains the certificate authority public key.</param> /// <param name="key_path">The file that contains our public/private keypair.</param> /// <param name="sign_path">The file that contains our signature.</param> public void LoadCertificatesFromFiles(string ca_path, string key_path, string sign_path) { if (File.Exists(ca_path)) { CertificateAuthority = (RsaKeyParameters)RsaHelpers.PemDeserialize(File.ReadAllText(ca_path)); Log.Debug("Loaded certificate authority from {0}", ca_path); } if (File.Exists(key_path)) { Certificate = (AsymmetricCipherKeyPair)RsaHelpers.PemDeserialize(File.ReadAllText(key_path)); Log.Debug("Loaded certificate from {0}", key_path); } if (File.Exists(sign_path)) { Signature = File.ReadAllBytes(sign_path); Log.Debug("Loaded signature from {0}", sign_path); } }
/// <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); }
/// <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); }