Skip to content

Bug: Handshake may fail when using specific ClientHelloID #104

Closed
@gaukas

Description

@gaukas

Original Issue

The TLS Handshake below will fail with errMsg local error: tls: unexpected message

tlsUConn := UClient(tcpConn, &Config{ServerName: hostname}, ch)
tlsUConn.Handshake()

when hostname is served by CloudFlare* and ch is set to HelloChrome_Auto.

* Including their site www.cloudflare.com, their DoH servers 1.1.1.1, 1.0.0.1, cloudflare-dns.com, and all my sites with CloudFlare.

Rough investigation

The error was thrown in *Conn.readHandshake() here by

return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))

In other words, an unknown handshake type as data[0] has been received. The unknown byte is 0x19(DEC: 25) which is undefined.

After a few more attempts, all three HelloChrome_70, HelloChrome_72, HelloChrome_83 will be triggering exact the same error.

All other ClientHelloID (except for HelloCustom) work great when handshake with CloudFlare served hosts.

Test Sample

cloudflare_test.go
package tls

import (
	"net"
	"testing"
)

var (
	tlsClientHelloIDs = map[string]ClientHelloID{
		// "golang":  HelloGolang,
		// "random":  HelloRandomized,
		// "firefox":        HelloFirefox_Auto,
		// "HelloChrome_58":        HelloChrome_58,
		// "HelloChrome_62":        HelloChrome_62,
		// "HelloChrome_70":        HelloChrome_70,
		// "HelloChrome_72":        HelloChrome_72,
		// "HelloChrome_83":        HelloChrome_83,
		// "HelloFirefox_55":       HelloFirefox_55,
		// "HelloFirefox_56":       HelloFirefox_56,
		// "HelloFirefox_63":       HelloFirefox_63,
		// "HelloFirefox_65":       HelloFirefox_65,
		// "HelloIOS_11_1":         HelloIOS_11_1,
		// "HelloIOS_12_1":         HelloIOS_12_1,
		// "HelloRandomized":       HelloRandomized,
		// "HelloRandomizedALPN":   HelloRandomizedALPN,
		// "HelloRandomizedNoALPN": HelloRandomizedNoALPN,
		// "HelloCustom": HelloCustom,
		// "ios":            HelloIOS_Auto,
	}

	tlsHostnames = []string{
		// "cloudflare-dns.com",
		// "8.8.8.8",
		// "9.9.9.9",
		// "1.0.0.1",
		"www.cloudflare.com",
		// "caddyserver.com",
		// "github.com",
		// "pkg.go.dev",
	}
)

func TestCloudflareHandshake(t *testing.T) {
	for name, ch := range tlsClientHelloIDs {
		t.Run(name, func(t *testing.T) {
			for _, hostname := range tlsHostnames {
				t.Run(hostname, func(t *testing.T) {
					tcpConn, tcpErr := net.Dial("tcp", hostname+":443")
					if tcpErr != nil {
						t.Fatal("net.Dial():", tcpErr)
					}
					defer tcpConn.Close()
					tlsUConn := UClient(tcpConn, &Config{
						ServerName: hostname,
					}, ch)
					handshakeErr := tlsUConn.Handshake()
					if handshakeErr != nil {
						t.Fatal("tls.Handshake():", handshakeErr)
					}
				})
			}
		})
	}
}

Works better if you print the error byte in conn.go:1061

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugUnexpected behavior confirmed and should be fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions