utls icon indicating copy to clipboard operation
utls copied to clipboard

HelloFirefox* gets an ECDSA verification failure

Open danielboros opened this issue 2 years ago • 4 comments

Seems to happen on facebook.com as well as fbcdn.net. Minimal recreate below.

package main

import (
	"fmt"
	"net"
	"net/http"
	"strings"
	"time"

	utls "github.com/refraction-networking/utls"
	"golang.org/x/net/http2"
)

func main() {
	hostWithoutPort := "facebook.com"
	ips, err := net.LookupIP(hostWithoutPort)
	if err != nil {
		panic(err)
	}
	var ip string
	if len(ips) > 0 {
		ip = ips[0].String()
		if strings.Contains(ip, ":") {
			ip = fmt.Sprintf("[%s]", ip)
		}
	} // IPv6

	config := utls.Config{
		ServerName: hostWithoutPort,

		// TODO: Possibly remove InsecureSkipVerify
		InsecureSkipVerify: true,
	}
	dialConn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", ip, "443"), 10*time.Second)
	if err != nil {
		panic(err)
	}
	utlsConn := utls.UClient(dialConn, &config, utls.HelloFirefox_Auto)

	err = utlsConn.Handshake()
	if err != nil {
		panic(err)
	}

	h2 := &http2.Transport{}
	conn, err := h2.NewClientConn(utlsConn)
	if err != nil {
		panic(err)
	}

	req, err := http.NewRequest(http.MethodGet, "https://facebook.com", nil)
	if err != nil {
		panic(err)
	}

	resp, err := conn.RoundTrip(req)
	if err != nil {
		panic(err)
	}
	fmt.Printf("resp: %+v\n", resp)
}

Changing utls.HelloFirefox_Auto to utls.HelloChrome_Auto appears to resolve the issue.

danielboros avatar Dec 20 '23 03:12 danielboros

It is a known issue caused by TLS extension delegated_credentials (34) (draft-ietf-tls-subcerts-15). Firefox parrots in uTLS advertise this extension by FakeDelegatedCredentialsExtension without implementing any support of it, which causes issue if the server selected it and proceeded.

While adding support to delegated_credentials is not currently planned, we welcome any community contribution in implementing ANY currently unsupported extensions. FYI, cloudflare/go has implemented it which could be a good reference.

gaukas avatar Dec 20 '23 18:12 gaukas

Sounds like I will take on this adventure. Will update here when I make some progress.

danielboros avatar Dec 20 '23 18:12 danielboros

The only issue is that we are required to implement the delegated credential extension in the x509 parser as well

@gaukas what do you suggest? a local copy like dicttls ?

jjsaunier avatar Dec 22 '23 05:12 jjsaunier

Hmm. That's pretty tough.

I am not super familiar with this extension yet, but from cloudflare/go I can see it is due to our certificate handling (from crypto/tls) is directly using the x509 package which did not support some specific functionality for this extension or something.

Not an expert in this but is it possible for us to override a standard library to a remote package (say cloudflare/go/crypto/x509)? I personally don't believe vendor-ing a mission critical package would bring us more benefits than trouble, at least in the long run.

I would rather suggest instead trying persuading go team to accept the required change and backport it into currently maintained versions. But afterall, if we really want to vendor it, we might want to minimize the impact by strictly limiting the use of our x509 which would go out-of-date very easily.

gaukas avatar Dec 22 '23 05:12 gaukas