// 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
// 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
cipherSuite: hs.suite.id,
masterSecret: hs.masterSecret,
serverCertificates: c.peerCertificates,
+ verifiedChains: c.verifiedChains,
}
return nil
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) {
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 {
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()
+ }
+}