HandshakeType SendClientHello(ref int offset) { _pendingConnState = new ConnectionState(); _handshakeData = new HandshakeData { HandshakeHash1 = Hasher.Create(TLSHashAlgorithm.SHA256), HandshakeHash2 = Hasher.Create(TLSHashAlgorithm.SHA256), HandshakeHash1_384 = Hasher.Create(TLSHashAlgorithm.SHA384), HandshakeHash2_384 = Hasher.Create(TLSHashAlgorithm.SHA384), HandshakeHash1_MD5SHA1 = Hasher.Create(TLSHashAlgorithm.MD5SHA1), HandshakeHash2_MD5SHA1 = Hasher.Create(TLSHashAlgorithm.MD5SHA1), CertificateVerifyHash_MD5 = Hasher.Create(TLSHashAlgorithm.MD5), CertificateVerifyHash_SHA1 = Hasher.Create(TLSHashAlgorithm.SHA1) }; // Highest version supported offset += Utils.WriteUInt16(_buf, offset, (ushort)HighestTlsVersionSupported); // Client random var timestamp = (uint)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; _pendingConnState.ClientRandom = new byte[32]; _rng.GetBytes(_pendingConnState.ClientRandom); Utils.WriteUInt32(_pendingConnState.ClientRandom, 0, timestamp); Buffer.BlockCopy(_pendingConnState.ClientRandom, 0, _buf, offset, 32); offset += 32; // No session id _buf[offset++] = 0; // Cipher suites var supportedCipherSuites = CipherSuiteInfo.Supported; /* if (HighestTlsVersionSupported != TlsVersion.TLSv1_2) supportedCipherSuites = supportedCipherSuites.Where(cs => cs.IsAllowedBefore1_2).ToArray(); */ offset += Utils.WriteUInt16(_buf, offset, (ushort)(supportedCipherSuites.Length * sizeof(ushort))); foreach (var suite in supportedCipherSuites) { offset += Utils.WriteUInt16(_buf, offset, (ushort)suite.Id); } // Compression methods _buf[offset++] = 1; // Length _buf[offset++] = 0; // "null" compression method // Extensions length, fill in later var extensionLengthOffset = offset; offset += 2; // Renegotiation extension offset += Utils.WriteUInt16(_buf, offset, (ushort)ExtensionType.RenegotiationInfo); if (_connState.SecureRenegotiation) { // Extension length offset += Utils.WriteUInt16(_buf, offset, 13); // Renegotiated connection length _buf[offset++] = 12; // Renegotiated connection data Buffer.BlockCopy(_connState.ClientVerifyData, 0, _buf, offset, 12); offset += 12; } else { // Extension length offset += Utils.WriteUInt16(_buf, offset, 1); // Renegotiated connection length _buf[offset++] = 0; } // SNI extension if (_hostName != null) { // TODO: IDN Unicode -> Punycode // NOTE: IP addresses should not use SNI extension, per specification. System.Net.IPAddress ip; if (!System.Net.IPAddress.TryParse(_hostName, out ip)) { offset += Utils.WriteUInt16(_buf, offset, (ushort)ExtensionType.ServerName); var byteLen = Encoding.ASCII.GetBytes(_hostName, 0, _hostName.Length, _buf, offset + 7); offset += Utils.WriteUInt16(_buf, offset, (ushort)(5 + byteLen)); offset += Utils.WriteUInt16(_buf, offset, (ushort)(3 + byteLen)); _buf[offset++] = 0; // host_name offset += Utils.WriteUInt16(_buf, offset, (ushort)byteLen); offset += byteLen; } } if (HighestTlsVersionSupported == TlsVersion.TLSv1_2) { // Signature algorithms extension. At least IIS 7.5 needs this or it immediately resets the connection. // Used to specify what kind of server certificate hash/signature algorithms we can use to verify it. offset += Utils.WriteUInt16(_buf, offset, (ushort)ExtensionType.SignatureAlgorithms); offset += Utils.WriteUInt16(_buf, offset, 20); offset += Utils.WriteUInt16(_buf, offset, 18); _buf[offset++] = (byte)TLSHashAlgorithm.SHA1; _buf[offset++] = (byte)SignatureAlgorithm.ECDSA; _buf[offset++] = (byte)TLSHashAlgorithm.SHA256; _buf[offset++] = (byte)SignatureAlgorithm.ECDSA; _buf[offset++] = (byte)TLSHashAlgorithm.SHA384; _buf[offset++] = (byte)SignatureAlgorithm.ECDSA; _buf[offset++] = (byte)TLSHashAlgorithm.SHA512; _buf[offset++] = (byte)SignatureAlgorithm.ECDSA; _buf[offset++] = (byte)TLSHashAlgorithm.SHA1; _buf[offset++] = (byte)SignatureAlgorithm.RSA; _buf[offset++] = (byte)TLSHashAlgorithm.SHA256; _buf[offset++] = (byte)SignatureAlgorithm.RSA; _buf[offset++] = (byte)TLSHashAlgorithm.SHA384; _buf[offset++] = (byte)SignatureAlgorithm.RSA; _buf[offset++] = (byte)TLSHashAlgorithm.SHA512; _buf[offset++] = (byte)SignatureAlgorithm.RSA; _buf[offset++] = (byte)TLSHashAlgorithm.SHA1; _buf[offset++] = (byte)SignatureAlgorithm.DSA; } if (supportedCipherSuites.Any(s => s.KeyExchange == KeyExchange.ECDHE_RSA || s.KeyExchange == KeyExchange.ECDHE_ECDSA)) { // Supported Elliptic Curves Extension offset += Utils.WriteUInt16(_buf, offset, (ushort)ExtensionType.SupportedEllipticCurves); offset += Utils.WriteUInt16(_buf, offset, 8); offset += Utils.WriteUInt16(_buf, offset, 6); offset += Utils.WriteUInt16(_buf, offset, (ushort)NamedCurve.secp256r1); offset += Utils.WriteUInt16(_buf, offset, (ushort)NamedCurve.secp384r1); offset += Utils.WriteUInt16(_buf, offset, (ushort)NamedCurve.secp521r1); // Supported Point Formats Extension offset += Utils.WriteUInt16(_buf, offset, (ushort)ExtensionType.SupportedPointFormats); offset += Utils.WriteUInt16(_buf, offset, 2); _buf[offset++] = 1; // Length _buf[offset++] = 0; // Uncompressed } Utils.WriteUInt16(_buf, extensionLengthOffset, (ushort)(offset - (extensionLengthOffset + 2))); return HandshakeType.ClientHello; }
void ParseFinishedMessage(byte[] buf) { byte[] hash = Utils.PRF(_connState.PRFAlgorithm, _connState.MasterSecret, "server finished", _handshakeData.HandshakeHash2.Final(), 12); if (buf.Length != 4 + 12 || !hash.SequenceEqual(buf.Skip(4))) SendAlertFatal(AlertDescription.DecryptError); if (_connState.SecureRenegotiation) _connState.ServerVerifyData = hash; _handshakeData = null; }