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

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