When using session resumption on a TLS 1.3 server with VerifyConnection set it will be called twice when a session is resumed. This looks like it's happening because it gets called once in checkForResumption, which calls processCertsFromClient, and then again when readClientCertificate calls VerifyConnection if ClientAuth is set to ignore client certs (also because PSK resumption). This appears to work as expected in 1.2.
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"net"
"net/http"
)
func main() {
tlsVer := uint16(tls.VersionTLS13)
clientConf := &tls.Config{
ClientSessionCache: tls.NewLRUClientSessionCache(5),
MinVersion: tlsVer,
MaxVersion: tlsVer,
InsecureSkipVerify: true,
}
tr := &http.Transport{
TLSClientConfig: clientConf,
}
hc := &http.Client{Transport: tr}
k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "localhost"},
DNSNames: []string{"localhost"},
}
c, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, k.Public(), k)
if err != nil {
panic(err)
}
serverConf := &tls.Config{
MinVersion: tlsVer,
MaxVersion: tlsVer,
VerifyConnection: func(_ tls.ConnectionState) error {
fmt.Println("server verify connection")
return nil
},
Certificates: []tls.Certificate{
{
Certificate: [][]byte{c},
PrivateKey: k,
},
},
}
l, err := net.Listen("tcp", "localhost:8181")
if err != nil {
panic(err)
}
tl := tls.NewListener(l, serverConf)
s := http.Server{Handler: http.NotFoundHandler()}
go func() {
s.Serve(tl)
}()
fmt.Println("first")
hc.Get("https://localhost:8181")
fmt.Println()
fmt.Println("second")
resp, err := hc.Get("https://localhost:8181")
if err != nil {
panic(err)
}
fmt.Println("resumed?", resp.TLS.DidResume)
}
When using session resumption on a TLS 1.3 server with VerifyConnection set it will be called twice when a session is resumed. This looks like it's happening because it gets called once in
checkForResumption, which callsprocessCertsFromClient, and then again whenreadClientCertificatecallsVerifyConnectionif ClientAuth is set to ignore client certs (also because PSK resumption). This appears to work as expected in 1.2.It seems like perhaps
VerifyConnectionshould be decoupled fromprocessCertsFromClientand handled somewhere else inhandshake? (I don't have any concrete suggestion of where would be better though.)Minimal(ish) repro:
cc @FiloSottile @katiehockman