diff --git a/go.mod b/go.mod index 8b68359..c660d66 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( github.com/sirupsen/logrus v1.7.0 // indirect github.com/ziutek/mymysql v1.5.4 // indirect gitlab.com/yawning/obfs4.git v0.0.0-20210511220700-e330d1b7024b + gitlab.com/yawning/utls.git v0.0.12-1 golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect golang.org/x/net v0.0.0-20210510120150-4163338589ed golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 diff --git a/internal/netxlite/tlshandshaker.go b/internal/netxlite/tlshandshaker.go index 64fe5df..4a08afb 100644 --- a/internal/netxlite/tlshandshaker.go +++ b/internal/netxlite/tlshandshaker.go @@ -5,6 +5,8 @@ import ( "crypto/tls" "net" "time" + + utls "gitlab.com/yawning/utls.git" ) // TLSHandshaker is the generic TLS handshaker. @@ -105,4 +107,41 @@ func (h *TLSHandshakerLogger) Handshake( return tlsconn, state, nil } +// UTLSConn implements TLSConn and uses a utls UConn as its underlying connection +type UTLSConn struct { + *utls.UConn +} + +// NewConnUTLS creates a NewConn function creating a utls connection with a specified ClientHelloID +func NewConnUTLS(clientHello *utls.ClientHelloID) func(conn net.Conn, config *tls.Config) TLSConn { + return func(conn net.Conn, config *tls.Config) TLSConn { + uConfig := &utls.Config{ + RootCAs: config.RootCAs, + NextProtos: config.NextProtos, + ServerName: config.ServerName, + InsecureSkipVerify: config.InsecureSkipVerify, + DynamicRecordSizingDisabled: config.DynamicRecordSizingDisabled, + } + tlsConn := utls.UClient(conn, uConfig, *clientHello) + return &UTLSConn{tlsConn} + } +} + +func (c *UTLSConn) ConnectionState() tls.ConnectionState { + uState := c.Conn.ConnectionState() + return tls.ConnectionState{ + Version: uState.Version, + HandshakeComplete: uState.HandshakeComplete, + DidResume: uState.DidResume, + CipherSuite: uState.CipherSuite, + NegotiatedProtocol: uState.NegotiatedProtocol, + ServerName: uState.ServerName, + PeerCertificates: uState.PeerCertificates, + VerifiedChains: uState.VerifiedChains, + SignedCertificateTimestamps: uState.SignedCertificateTimestamps, + OCSPResponse: uState.OCSPResponse, + TLSUnique: uState.TLSUnique, + } +} + var _ TLSHandshaker = &TLSHandshakerLogger{} diff --git a/internal/netxlite/tlshandshaker_test.go b/internal/netxlite/tlshandshaker_test.go index 817a63b..7a25f54 100644 --- a/internal/netxlite/tlshandshaker_test.go +++ b/internal/netxlite/tlshandshaker_test.go @@ -15,6 +15,7 @@ import ( "github.com/apex/log" "github.com/ooni/probe-cli/v3/internal/netxmocks" + utls "gitlab.com/yawning/utls.git" ) func TestTLSHandshakerConfigurableWithError(t *testing.T) { @@ -177,3 +178,18 @@ func TestTLSHandshakerLoggerFailure(t *testing.T) { t.Fatal("expected zero ConnectionState here") } } + +func TestUTLSHandshakerChrome(t *testing.T) { + h := &TLSHandshakerConfigurable{ + NewConn: NewConnUTLS(&utls.HelloChrome_Auto), + } + cfg := &tls.Config{ServerName: "google.com"} + conn, err := net.Dial("tcp", "google.com:443") + conn, _, err = h.Handshake(context.Background(), conn, cfg) + if err != nil { + t.Fatal("unexpected error", err) + } + if conn == nil { + t.Fatal("nil connection") + } +}