update to qtls based on Go 1.14's TLS implementation

This commit is contained in:
Marten Seemann 2020-02-27 14:55:22 +07:00
parent 42bf68205c
commit 6fe4878f0e
6 changed files with 122 additions and 20 deletions

2
go.mod
View file

@ -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

15
go.sum
View file

@ -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=

View file

@ -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())

View file

@ -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:

View file

@ -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))),
}))
}

View file

@ -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}))
})
})