diff --git a/go.mod b/go.mod index c1d417f4..e067e0e9 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/golang/mock v1.4.0 github.com/golang/protobuf v1.3.1 github.com/marten-seemann/qpack v0.1.0 - github.com/marten-seemann/qtls v0.7.2 + github.com/marten-seemann/qtls v0.8.0 github.com/onsi/ginkgo v1.11.0 github.com/onsi/gomega v1.8.1 golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d diff --git a/go.sum b/go.sum index 7f965b5c..b01ad144 100644 --- a/go.sum +++ b/go.sum @@ -65,8 +65,8 @@ github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/qpack v0.1.0 h1:/0M7lkda/6mus9B8u34Asqm8ZhHAAt9Ho0vniNuVSVg= github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI= -github.com/marten-seemann/qtls v0.7.2 h1:/Hq1sYrHGnoi+AZGAeFrrae4n401H5+B7/9ez0M/BMg= -github.com/marten-seemann/qtls v0.7.2/go.mod h1:2MdmPXnAOf1oOfu871hbP8oYrClgWsykCQvi0S96jnw= +github.com/marten-seemann/qtls v0.8.0 h1:aj+MPLibzKByw8CmG0WvWgbtBkctYPAXeB11cQJC8mo= +github.com/marten-seemann/qtls v0.8.0/go.mod h1:Lao6jDqlCfxyLKYFmZXGm2LSHBgVn+P+ROOex6YkT+k= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -125,7 +125,6 @@ golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -139,7 +138,6 @@ golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7 h1:Qe/u+eY379X4He4GBMFZYu3pmh1ML5yT1aL1ndNM1zQ= golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -150,11 +148,9 @@ golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAG golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -163,12 +159,10 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -205,7 +199,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/handshake/crypto_setup_test.go b/internal/handshake/crypto_setup_test.go index 56a6723a..466c9e08 100644 --- a/internal/handshake/crypto_setup_test.go +++ b/internal/handshake/crypto_setup_test.go @@ -103,7 +103,7 @@ var _ = Describe("Crypto Setup TLS", func() { Expect(getCertificateErr).To(MatchError("GetCertificate")) _, getClientCertificateErr := qtlsConf.GetClientCertificate(nil) Expect(getClientCertificateErr).To(MatchError("GetClientCertificate")) - cconf, err := qtlsConf.GetConfigForClient(&tls.ClientHelloInfo{ServerName: "foo.bar"}) + cconf, err := qtlsConf.GetConfigForClient(&qtls.ClientHelloInfo{ServerName: "foo.bar"}) Expect(err).ToNot(HaveOccurred()) Expect(cconf.ServerName).To(Equal("foo.bar")) Expect(cconf.AlternativeRecordLayer).ToNot(BeNil()) diff --git a/internal/handshake/qtls.go b/internal/handshake/qtls.go index 24e620a7..5bc3ee06 100644 --- a/internal/handshake/qtls.go +++ b/internal/handshake/qtls.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "net" "time" + "unsafe" "github.com/marten-seemann/qtls" @@ -58,10 +59,10 @@ func tlsConfigToQtlsConfig( if maxVersion < qtls.VersionTLS13 { maxVersion = qtls.VersionTLS13 } - var getConfigForClient func(ch *tls.ClientHelloInfo) (*qtls.Config, error) + var getConfigForClient func(ch *qtls.ClientHelloInfo) (*qtls.Config, error) if c.GetConfigForClient != nil { - getConfigForClient = func(ch *tls.ClientHelloInfo) (*qtls.Config, error) { - tlsConf, err := c.GetConfigForClient(ch) + getConfigForClient = func(ch *qtls.ClientHelloInfo) (*qtls.Config, error) { + tlsConf, err := c.GetConfigForClient((*tls.ClientHelloInfo)(unsafe.Pointer(ch))) if err != nil { return nil, err } @@ -78,12 +79,12 @@ func tlsConfigToQtlsConfig( conf := &qtls.Config{ Rand: c.Rand, Time: c.Time, - Certificates: c.Certificates, + Certificates: *(*[]qtls.Certificate)(unsafe.Pointer(&c.Certificates)), // NameToCertificate is deprecated, but we still need to copy it if the user sets it. //nolint:staticcheck - NameToCertificate: c.NameToCertificate, - GetCertificate: c.GetCertificate, - GetClientCertificate: c.GetClientCertificate, + NameToCertificate: *(*map[string]*qtls.Certificate)(unsafe.Pointer(&c.NameToCertificate)), + GetCertificate: *(*func(*qtls.ClientHelloInfo) (*qtls.Certificate, error))(unsafe.Pointer(&c.GetCertificate)), + GetClientCertificate: *(*func(*qtls.CertificateRequestInfo) (*qtls.Certificate, error))(unsafe.Pointer(&c.GetClientCertificate)), GetConfigForClient: getConfigForClient, VerifyPeerCertificate: c.VerifyPeerCertificate, RootCAs: c.RootCAs, @@ -117,6 +118,18 @@ func tlsConfigToQtlsConfig( return conf } +// qtlsConfigToTLSConfig is used to transform a qtls.Config to a tls.Config. +// It is used to create the tls.Config in the ClientHelloInfo. +// It doesn't copy all values, but only those used by ClientHelloInfo.SupportsCertificate. +func qtlsConfigToTLSConfig(config *qtls.Config) *tls.Config { + return &tls.Config{ + MinVersion: config.MinVersion, + MaxVersion: config.MaxVersion, + CipherSuites: config.CipherSuites, + CurvePreferences: config.CurvePreferences, + } +} + func cipherSuiteName(id uint16) string { switch id { case qtls.TLS_AES_128_GCM_SHA256: diff --git a/internal/handshake/unsafe.go b/internal/handshake/unsafe.go index 8238a5c7..e44afb69 100644 --- a/internal/handshake/unsafe.go +++ b/internal/handshake/unsafe.go @@ -1,24 +1,41 @@ package handshake // This package uses unsafe to convert between: +// * qtls.Certificate and tls.Certificate +// * qtls.CertificateRequestInfo and tls.CertificateRequestInfo +// * qtls.ClientHelloInfo and tls.ClientHelloInfo // * qtls.ConnectionState and tls.ConnectionState // * qtls.ClientSessionState and tls.ClientSessionState // We check in init() that this conversion actually is safe. import ( "crypto/tls" + "net" "reflect" + "unsafe" "github.com/marten-seemann/qtls" ) func init() { + if !structsEqual(&tls.Certificate{}, &qtls.Certificate{}) { + panic("qtls.Certificate not compatible with tls.Certificate") + } + if !structsEqual(&tls.CertificateRequestInfo{}, &qtls.CertificateRequestInfo{}) { + panic("qtls.CertificateRequestInfo not compatible with tls.CertificateRequestInfo") + } if !structsEqual(&tls.ClientSessionState{}, &qtls.ClientSessionState{}) { panic("qtls.ClientSessionState not compatible with tls.ClientSessionState") } if !structsEqual(&tls.ClientSessionState{}, &clientSessionState{}) { panic("clientSessionState not compatible with tls.ClientSessionState") } + if !structsEqual(&tls.ClientHelloInfo{}, &clientHelloInfo{}) { + panic("clientHelloInfo not compatible with tls.ClientHelloInfo") + } + if !structsEqual(&qtls.ClientHelloInfo{}, &qtlsClientHelloInfo{}) { + panic("qtlsClientHelloInfo not compatible with qtls.ClientHelloInfo") + } } func structsEqual(a, b interface{}) bool { @@ -36,3 +53,44 @@ func structsEqual(a, b interface{}) bool { } return true } + +type clientHelloInfo struct { + CipherSuites []uint16 + ServerName string + SupportedCurves []tls.CurveID + SupportedPoints []uint8 + SignatureSchemes []tls.SignatureScheme + SupportedProtos []string + SupportedVersions []uint16 + Conn net.Conn + + config *tls.Config +} + +type qtlsClientHelloInfo struct { + CipherSuites []uint16 + ServerName string + SupportedCurves []tls.CurveID + SupportedPoints []uint8 + SignatureSchemes []tls.SignatureScheme + SupportedProtos []string + SupportedVersions []uint16 + Conn net.Conn + + config *qtls.Config +} + +func toTLSClientHelloInfo(chi *qtls.ClientHelloInfo) *tls.ClientHelloInfo { + qtlsCHI := (*qtlsClientHelloInfo)(unsafe.Pointer(chi)) + return (*tls.ClientHelloInfo)(unsafe.Pointer(&clientHelloInfo{ + CipherSuites: chi.CipherSuites, + ServerName: chi.ServerName, + SupportedCurves: chi.SupportedCurves, + SupportedPoints: chi.SupportedPoints, + SignatureSchemes: chi.SignatureSchemes, + SupportedProtos: chi.SupportedProtos, + SupportedVersions: chi.SupportedVersions, + Conn: chi.Conn, + config: qtlsConfigToTLSConfig((*qtls.Config)(unsafe.Pointer(qtlsCHI.config))), + })) +} diff --git a/internal/handshake/unsafe_test.go b/internal/handshake/unsafe_test.go index 360a07bb..2b1a2df4 100644 --- a/internal/handshake/unsafe_test.go +++ b/internal/handshake/unsafe_test.go @@ -1,6 +1,11 @@ package handshake import ( + "crypto/tls" + "net" + "unsafe" + + "github.com/marten-seemann/qtls" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) @@ -57,4 +62,37 @@ var _ = Describe("Unsafe checks", func() { Expect(structsEqual(&target{}, &interchangedFields{})).To(BeFalse()) Expect(structsEqual(&target{}, &renamedCallbackFunctionParams{})).To(BeTrue()) }) + + It("converts a qtls.ClientHelloInfo to a tls.ClientHelloInfo", func() { + chi := &qtlsClientHelloInfo{ + CipherSuites: []uint16{1, 2, 3}, + ServerName: "foo.bar", + SupportedCurves: []qtls.CurveID{4, 5, 6}, + SupportedPoints: []uint8{7, 8, 9}, + SignatureSchemes: []qtls.SignatureScheme{10, 11, 12}, + SupportedProtos: []string{"foo", "bar"}, + SupportedVersions: []uint16{13, 14, 15}, + Conn: &net.UDPConn{}, + config: &qtls.Config{ + MinVersion: tls.VersionTLS10, + MaxVersion: tls.VersionTLS12, + CipherSuites: []uint16{16, 17, 18}, + CurvePreferences: []qtls.CurveID{19, 20, 21}, + }, + } + tlsCHI := toTLSClientHelloInfo((*qtls.ClientHelloInfo)(unsafe.Pointer(chi))) + Expect(tlsCHI.CipherSuites).To(Equal([]uint16{1, 2, 3})) + Expect(tlsCHI.ServerName).To(Equal("foo.bar")) + Expect(tlsCHI.SupportedCurves).To(Equal([]tls.CurveID{4, 5, 6})) + Expect(tlsCHI.SupportedPoints).To(Equal([]uint8{7, 8, 9})) + Expect(tlsCHI.SignatureSchemes).To(Equal([]tls.SignatureScheme{10, 11, 12})) + Expect(tlsCHI.SupportedProtos).To(Equal([]string{"foo", "bar"})) + Expect(tlsCHI.SupportedVersions).To(Equal([]uint16{13, 14, 15})) + Expect(tlsCHI.Conn).To(Equal(&net.UDPConn{})) + c := (*clientHelloInfo)(unsafe.Pointer(tlsCHI)) + Expect(c.config.CipherSuites).To(Equal([]uint16{16, 17, 18})) + Expect(c.config.MinVersion).To(BeEquivalentTo(tls.VersionTLS10)) + Expect(c.config.MaxVersion).To(BeEquivalentTo(tls.VersionTLS12)) + Expect(c.config.CurvePreferences).To(Equal([]tls.CurveID{19, 20, 21})) + }) })