diff --git a/common.go b/common.go index 12500ab..a3d75d6 100644 --- a/common.go +++ b/common.go @@ -191,11 +191,12 @@ const ( // ClientSessionState contains the state needed by clients to resume TLS // sessions. type ClientSessionState struct { - sessionTicket []uint8 // Encrypted ticket used for session resumption with server - vers uint16 // SSL/TLS version negotiated for the session - cipherSuite uint16 // Ciphersuite negotiated for the session - masterSecret []byte // MasterSecret generated by client on a full handshake - serverCertificates []*x509.Certificate // Certificate chain presented by the server + sessionTicket []uint8 // Encrypted ticket used for session resumption with server + vers uint16 // SSL/TLS version negotiated for the session + cipherSuite uint16 // Ciphersuite negotiated for the session + masterSecret []byte // MasterSecret generated by client on a full handshake + serverCertificates []*x509.Certificate // Certificate chain presented by the server + verifiedChains [][]*x509.Certificate // Certificate chains we built for verification } // ClientSessionCache is a cache of ClientSessionState objects that can be used diff --git a/handshake_client.go b/handshake_client.go index 6b09264..0b591d7 100644 --- a/handshake_client.go +++ b/handshake_client.go @@ -547,6 +547,7 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { // Restore masterSecret and peerCerts from previous state hs.masterSecret = hs.session.masterSecret c.peerCertificates = hs.session.serverCertificates + c.verifiedChains = hs.session.verifiedChains return true, nil } return false, nil @@ -604,6 +605,7 @@ func (hs *clientHandshakeState) readSessionTicket() error { cipherSuite: hs.suite.id, masterSecret: hs.masterSecret, serverCertificates: c.peerCertificates, + verifiedChains: c.verifiedChains, } return nil diff --git a/handshake_client_test.go b/handshake_client_test.go index 5fc57b0..664fe8d 100644 --- a/handshake_client_test.go +++ b/handshake_client_test.go @@ -406,10 +406,20 @@ func TestClientResumption(t *testing.T) { CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA}, Certificates: testConfig.Certificates, } + + issuer, err := x509.ParseCertificate(testRSACertificateIssuer) + if err != nil { + panic(err) + } + + rootCAs := x509.NewCertPool() + rootCAs.AddCert(issuer) + clientConfig := &Config{ CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, - InsecureSkipVerify: true, ClientSessionCache: NewLRUClientSessionCache(32), + RootCAs: rootCAs, + ServerName: "example.golang", } testResumeState := func(test string, didResume bool) { @@ -420,6 +430,9 @@ func TestClientResumption(t *testing.T) { if hs.DidResume != didResume { t.Fatalf("%s resumed: %v, expected: %v", test, hs.DidResume, didResume) } + if didResume && (hs.PeerCertificates == nil || hs.VerifiedChains == nil) { + t.Fatalf("expected non-nil certificates after resumption. Got peerCertificates: %#v, verifedCertificates: %#v", hs.PeerCertificates, hs.VerifiedChains) + } } getTicket := func() []byte { diff --git a/tls_test.go b/tls_test.go index 8e22c9c..c45c103 100644 --- a/tls_test.go +++ b/tls_test.go @@ -307,3 +307,28 @@ func TestVerifyHostname(t *testing.T) { t.Fatalf("verify www.google.com succeeded with InsecureSkipVerify=true") } } + +func TestVerifyHostnameResumed(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + config := &Config{ + ClientSessionCache: NewLRUClientSessionCache(32), + } + for i := 0; i < 2; i++ { + c, err := Dial("tcp", "www.google.com:https", config) + if err != nil { + t.Fatalf("Dial #%d: %v", i, err) + } + cs := c.ConnectionState() + if i > 0 && !cs.DidResume { + t.Fatalf("Subsequent connection unexpectedly didn't resume") + } + if cs.VerifiedChains == nil { + t.Fatalf("Dial #%d: cs.VerifiedChains == nil", i) + } + if err := c.VerifyHostname("www.google.com"); err != nil { + t.Fatalf("verify www.google.com #%d: %v", i, err) + } + c.Close() + } +}