Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/boringssl.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/ssl/test
diff options
context:
space:
mode:
authorDavid Benjamin <davidben@chromium.org>2014-11-17 11:19:02 +0300
committerAdam Langley <agl@google.com>2014-11-22 00:35:39 +0300
commitfe8eb9a6031aedf6426e2dda2ffcdb274fa5c9c1 (patch)
tree1cd4e3bd419e5c6eaefa8ab8500136f29c19ac79 /ssl/test
parentae3e487d51d3414769ed89b06683f3cbde5c7828 (diff)
Add tests for session-ID-based resumption.
This implements session IDs in client and server in runner.go. Change-Id: I26655f996b7b44c7eb56340ef6a415d3f2ac3503 Reviewed-on: https://boringssl-review.googlesource.com/2350 Reviewed-by: Adam Langley <agl@google.com>
Diffstat (limited to 'ssl/test')
-rw-r--r--ssl/test/runner/common.go124
-rw-r--r--ssl/test/runner/handshake_client.go82
-rw-r--r--ssl/test/runner/handshake_server.go53
-rw-r--r--ssl/test/runner/runner.go73
4 files changed, 232 insertions, 100 deletions
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 01b7581d..476a2a4f 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -203,6 +203,7 @@ const (
// ClientSessionState contains the state needed by clients to resume TLS
// sessions.
type ClientSessionState struct {
+ sessionId []uint8 // Session ID supplied by the server. nil if the session has a ticket.
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
@@ -225,6 +226,19 @@ type ClientSessionCache interface {
Put(sessionKey string, cs *ClientSessionState)
}
+// ServerSessionCache is a cache of sessionState objects that can be used by a
+// client to resume a TLS session with a given server. ServerSessionCache
+// implementations should expect to be called concurrently from different
+// goroutines.
+type ServerSessionCache interface {
+ // Get searches for a sessionState associated with the given session
+ // ID. On return, ok is true if one was found.
+ Get(sessionId string) (session *sessionState, ok bool)
+
+ // Put adds the sessionState to the cache with the given session ID.
+ Put(sessionId string, session *sessionState)
+}
+
// A Config structure is used to configure a TLS client or server.
// After one has been passed to a TLS function it must not be
// modified. A Config may be reused; the tls package will also not
@@ -311,10 +325,14 @@ type Config struct {
// connections using that key are compromised.
SessionTicketKey [32]byte
- // SessionCache is a cache of ClientSessionState entries for TLS session
- // resumption.
+ // ClientSessionCache is a cache of ClientSessionState entries
+ // for TLS session resumption.
ClientSessionCache ClientSessionCache
+ // ServerSessionCache is a cache of sessionState entries for TLS session
+ // resumption.
+ ServerSessionCache ServerSessionCache
+
// MinVersion contains the minimum SSL/TLS version that is acceptable.
// If zero, then SSLv3 is taken as the minimum.
MinVersion uint16
@@ -732,8 +750,8 @@ type handshakeMessage interface {
unmarshal([]byte) bool
}
-// lruSessionCache is a ClientSessionCache implementation that uses an LRU
-// caching strategy.
+// lruSessionCache is a client or server session cache implementation
+// that uses an LRU caching strategy.
type lruSessionCache struct {
sync.Mutex
@@ -744,27 +762,11 @@ type lruSessionCache struct {
type lruSessionCacheEntry struct {
sessionKey string
- state *ClientSessionState
-}
-
-// NewLRUClientSessionCache returns a ClientSessionCache with the given
-// capacity that uses an LRU strategy. If capacity is < 1, a default capacity
-// is used instead.
-func NewLRUClientSessionCache(capacity int) ClientSessionCache {
- const defaultSessionCacheCapacity = 64
-
- if capacity < 1 {
- capacity = defaultSessionCacheCapacity
- }
- return &lruSessionCache{
- m: make(map[string]*list.Element),
- q: list.New(),
- capacity: capacity,
- }
+ state interface{}
}
// Put adds the provided (sessionKey, cs) pair to the cache.
-func (c *lruSessionCache) Put(sessionKey string, cs *ClientSessionState) {
+func (c *lruSessionCache) Put(sessionKey string, cs interface{}) {
c.Lock()
defer c.Unlock()
@@ -790,9 +792,9 @@ func (c *lruSessionCache) Put(sessionKey string, cs *ClientSessionState) {
c.m[sessionKey] = elem
}
-// Get returns the ClientSessionState value associated with a given key. It
-// returns (nil, false) if no value is found.
-func (c *lruSessionCache) Get(sessionKey string) (*ClientSessionState, bool) {
+// Get returns the value associated with a given key. It returns (nil,
+// false) if no value is found.
+func (c *lruSessionCache) Get(sessionKey string) (interface{}, bool) {
c.Lock()
defer c.Unlock()
@@ -803,6 +805,78 @@ func (c *lruSessionCache) Get(sessionKey string) (*ClientSessionState, bool) {
return nil, false
}
+// lruClientSessionCache is a ClientSessionCache implementation that
+// uses an LRU caching strategy.
+type lruClientSessionCache struct {
+ lruSessionCache
+}
+
+func (c *lruClientSessionCache) Put(sessionKey string, cs *ClientSessionState) {
+ c.lruSessionCache.Put(sessionKey, cs)
+}
+
+func (c *lruClientSessionCache) Get(sessionKey string) (*ClientSessionState, bool) {
+ cs, ok := c.lruSessionCache.Get(sessionKey)
+ if !ok {
+ return nil, false
+ }
+ return cs.(*ClientSessionState), true
+}
+
+// lruServerSessionCache is a ServerSessionCache implementation that
+// uses an LRU caching strategy.
+type lruServerSessionCache struct {
+ lruSessionCache
+}
+
+func (c *lruServerSessionCache) Put(sessionId string, session *sessionState) {
+ c.lruSessionCache.Put(sessionId, session)
+}
+
+func (c *lruServerSessionCache) Get(sessionId string) (*sessionState, bool) {
+ cs, ok := c.lruSessionCache.Get(sessionId)
+ if !ok {
+ return nil, false
+ }
+ return cs.(*sessionState), true
+}
+
+// NewLRUClientSessionCache returns a ClientSessionCache with the given
+// capacity that uses an LRU strategy. If capacity is < 1, a default capacity
+// is used instead.
+func NewLRUClientSessionCache(capacity int) ClientSessionCache {
+ const defaultSessionCacheCapacity = 64
+
+ if capacity < 1 {
+ capacity = defaultSessionCacheCapacity
+ }
+ return &lruClientSessionCache{
+ lruSessionCache{
+ m: make(map[string]*list.Element),
+ q: list.New(),
+ capacity: capacity,
+ },
+ }
+}
+
+// NewLRUServerSessionCache returns a ServerSessionCache with the given
+// capacity that uses an LRU strategy. If capacity is < 1, a default capacity
+// is used instead.
+func NewLRUServerSessionCache(capacity int) ServerSessionCache {
+ const defaultSessionCacheCapacity = 64
+
+ if capacity < 1 {
+ capacity = defaultSessionCacheCapacity
+ }
+ return &lruServerSessionCache{
+ lruSessionCache{
+ m: make(map[string]*list.Element),
+ q: list.New(),
+ capacity: capacity,
+ },
+ }
+}
+
// TODO(jsing): Make these available to both crypto/x509 and crypto/tls.
type dsaSignature struct {
R, S *big.Int
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index c4dff89a..1b156056 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -136,18 +136,17 @@ NextCipherSuite:
var session *ClientSessionState
var cacheKey string
sessionCache := c.config.ClientSessionCache
- if c.config.SessionTicketsDisabled {
- sessionCache = nil
- }
if sessionCache != nil {
- hello.ticketSupported = true
+ hello.ticketSupported = !c.config.SessionTicketsDisabled
// Try to resume a previously negotiated TLS session, if
// available.
cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
candidateSession, ok := sessionCache.Get(cacheKey)
if ok {
+ ticketOk := !c.config.SessionTicketsDisabled || candidateSession.sessionTicket == nil
+
// Check that the ciphersuite/version used for the
// previous session are still valid.
cipherSuiteOk := false
@@ -160,36 +159,40 @@ NextCipherSuite:
versOk := candidateSession.vers >= c.config.minVersion() &&
candidateSession.vers <= c.config.maxVersion()
- if versOk && cipherSuiteOk {
+ if ticketOk && versOk && cipherSuiteOk {
session = candidateSession
}
}
}
if session != nil {
- hello.sessionTicket = session.sessionTicket
- if c.config.Bugs.CorruptTicket {
- hello.sessionTicket = make([]byte, len(session.sessionTicket))
- copy(hello.sessionTicket, session.sessionTicket)
- if len(hello.sessionTicket) > 0 {
- offset := 40
- if offset > len(hello.sessionTicket) {
- offset = len(hello.sessionTicket) - 1
+ if session.sessionTicket != nil {
+ hello.sessionTicket = session.sessionTicket
+ if c.config.Bugs.CorruptTicket {
+ hello.sessionTicket = make([]byte, len(session.sessionTicket))
+ copy(hello.sessionTicket, session.sessionTicket)
+ if len(hello.sessionTicket) > 0 {
+ offset := 40
+ if offset > len(hello.sessionTicket) {
+ offset = len(hello.sessionTicket) - 1
+ }
+ hello.sessionTicket[offset] ^= 0x40
}
- hello.sessionTicket[offset] ^= 0x40
}
- }
- // A random session ID is used to detect when the
- // server accepted the ticket and is resuming a session
- // (see RFC 5077).
- sessionIdLen := 16
- if c.config.Bugs.OversizedSessionId {
- sessionIdLen = 33
- }
- hello.sessionId = make([]byte, sessionIdLen)
- if _, err := io.ReadFull(c.config.rand(), hello.sessionId); err != nil {
- c.sendAlert(alertInternalError)
- return errors.New("tls: short read from Rand: " + err.Error())
+ // A random session ID is used to detect when the
+ // server accepted the ticket and is resuming a session
+ // (see RFC 5077).
+ sessionIdLen := 16
+ if c.config.Bugs.OversizedSessionId {
+ sessionIdLen = 33
+ }
+ hello.sessionId = make([]byte, sessionIdLen)
+ if _, err := io.ReadFull(c.config.rand(), hello.sessionId); err != nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: short read from Rand: " + err.Error())
+ }
+ } else {
+ hello.sessionId = session.sessionId
}
}
@@ -730,11 +733,26 @@ func (hs *clientHandshakeState) readFinished() error {
}
func (hs *clientHandshakeState) readSessionTicket() error {
+ c := hs.c
+
+ // Create a session with no server identifier. Either a
+ // session ID or session ticket will be attached.
+ session := &ClientSessionState{
+ vers: c.vers,
+ cipherSuite: hs.suite.id,
+ masterSecret: hs.masterSecret,
+ handshakeHash: hs.finishedHash.server.Sum(nil),
+ serverCertificates: c.peerCertificates,
+ }
+
if !hs.serverHello.ticketSupported {
+ if hs.session == nil && len(hs.serverHello.sessionId) > 0 {
+ session.sessionId = hs.serverHello.sessionId
+ hs.session = session
+ }
return nil
}
- c := hs.c
msg, err := c.readHandshake()
if err != nil {
return err
@@ -745,14 +763,8 @@ func (hs *clientHandshakeState) readSessionTicket() error {
return unexpectedMessageError(sessionTicketMsg, msg)
}
- hs.session = &ClientSessionState{
- sessionTicket: sessionTicketMsg.ticket,
- vers: c.vers,
- cipherSuite: hs.suite.id,
- masterSecret: hs.masterSecret,
- handshakeHash: hs.finishedHash.server.Sum(nil),
- serverCertificates: c.peerCertificates,
- }
+ session.sessionTicket = sessionTicketMsg.ticket
+ hs.session = session
hs.writeServerHash(sessionTicketMsg.marshal())
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 32814d3b..b087b468 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -335,13 +335,25 @@ Curves:
func (hs *serverHandshakeState) checkForResumption() bool {
c := hs.c
- if c.config.SessionTicketsDisabled {
- return false
- }
+ if len(hs.clientHello.sessionTicket) > 0 {
+ if c.config.SessionTicketsDisabled {
+ return false
+ }
- var ok bool
- if hs.sessionState, ok = c.decryptTicket(hs.clientHello.sessionTicket); !ok {
- return false
+ var ok bool
+ if hs.sessionState, ok = c.decryptTicket(hs.clientHello.sessionTicket); !ok {
+ return false
+ }
+ } else {
+ if c.config.ServerSessionCache == nil {
+ return false
+ }
+
+ var ok bool
+ sessionId := string(hs.clientHello.sessionId)
+ if hs.sessionState, ok = c.config.ServerSessionCache.Get(sessionId); !ok {
+ return false
+ }
}
// Never resume a session for a different SSL version.
@@ -416,10 +428,19 @@ func (hs *serverHandshakeState) doFullHandshake() error {
hs.hello.ocspStapling = true
}
- hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled
+ hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled && c.vers > VersionSSL30
hs.hello.cipherSuite = hs.suite.id
c.extendedMasterSecret = hs.hello.extendedMasterSecret
+ // Generate a session ID if we're to save the session.
+ if !hs.hello.ticketSupported && config.ServerSessionCache != nil {
+ hs.hello.sessionId = make([]byte, 32)
+ if _, err := io.ReadFull(config.rand(), hs.hello.sessionId); err != nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: short read from Rand: " + err.Error())
+ }
+ }
+
hs.finishedHash = newFinishedHash(c.vers, hs.suite)
hs.writeClientHash(hs.clientHello.marshal())
hs.writeServerHash(hs.hello.marshal())
@@ -733,14 +754,7 @@ func (hs *serverHandshakeState) readFinished(isResume bool) error {
}
func (hs *serverHandshakeState) sendSessionTicket() error {
- if !hs.hello.ticketSupported || hs.c.config.Bugs.SkipNewSessionTicket {
- return nil
- }
-
c := hs.c
- m := new(newSessionTicketMsg)
-
- var err error
state := sessionState{
vers: c.vers,
cipherSuite: hs.suite.id,
@@ -748,6 +762,17 @@ func (hs *serverHandshakeState) sendSessionTicket() error {
certificates: hs.certsFromClient,
handshakeHash: hs.finishedHash.server.Sum(nil),
}
+
+ if !hs.hello.ticketSupported || hs.c.config.Bugs.SkipNewSessionTicket {
+ if c.config.ServerSessionCache != nil && len(hs.hello.sessionId) != 0 {
+ c.config.ServerSessionCache.Put(string(hs.hello.sessionId), &state)
+ }
+ return nil
+ }
+
+ m := new(newSessionTicketMsg)
+
+ var err error
m.ticket, err = c.encryptTicket(&state)
if err != nil {
return err
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index cf1b1f98..1ee63622 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -148,10 +148,14 @@ type testCase struct {
// which attempts to resume the first session.
resumeSession bool
// resumeConfig, if not nil, points to a Config to be used on
- // resumption. SessionTicketKey and ClientSessionCache are copied from
- // the initial connection's config. If nil, the initial connection's
- // config is used.
+ // resumption. Unless newSessionsOnResume is set,
+ // SessionTicketKey, ServerSessionCache, and
+ // ClientSessionCache are copied from the initial connection's
+ // config. If nil, the initial connection's config is used.
resumeConfig *Config
+ // newSessionsOnResume, if true, will cause resumeConfig to
+ // use a different session resumption context.
+ newSessionsOnResume bool
// sendPrefix sends a prefix on the socket before actually performing a
// handshake.
sendPrefix string
@@ -787,6 +791,7 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
config := test.config
config.ClientSessionCache = NewLRUClientSessionCache(1)
+ config.ServerSessionCache = NewLRUServerSessionCache(1)
if test.testType == clientTest {
if len(config.Certificates) == 0 {
config.Certificates = []Certificate{getRSACertificate()}
@@ -814,8 +819,11 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error {
if len(resumeConfig.Certificates) == 0 {
resumeConfig.Certificates = []Certificate{getRSACertificate()}
}
- resumeConfig.SessionTicketKey = config.SessionTicketKey
- resumeConfig.ClientSessionCache = config.ClientSessionCache
+ if !test.newSessionsOnResume {
+ resumeConfig.SessionTicketKey = config.SessionTicketKey
+ resumeConfig.ClientSessionCache = config.ClientSessionCache
+ resumeConfig.ServerSessionCache = config.ServerSessionCache
+ }
} else {
resumeConfig = config
}
@@ -957,11 +965,6 @@ func addCipherSuiteTests() {
continue
}
- // Go's TLS implementation only implements session
- // resumption with tickets, so SSLv3 cannot resume
- // sessions.
- resumeSession := ver.version != VersionSSL30
-
testCases = append(testCases, testCase{
testType: clientTest,
name: ver.name + "-" + suite.name + "-client",
@@ -974,7 +977,7 @@ func addCipherSuiteTests() {
PreSharedKeyIdentity: pskIdentity,
},
flags: flags,
- resumeSession: resumeSession,
+ resumeSession: true,
})
testCases = append(testCases, testCase{
@@ -991,7 +994,7 @@ func addCipherSuiteTests() {
certFile: certFile,
keyFile: keyFile,
flags: flags,
- resumeSession: resumeSession,
+ resumeSession: true,
})
// TODO(davidben): Fix DTLS 1.2 support and test that.
@@ -1009,7 +1012,7 @@ func addCipherSuiteTests() {
PreSharedKeyIdentity: pskIdentity,
},
flags: flags,
- resumeSession: resumeSession,
+ resumeSession: true,
})
testCases = append(testCases, testCase{
testType: serverTest,
@@ -1026,7 +1029,7 @@ func addCipherSuiteTests() {
certFile: certFile,
keyFile: keyFile,
flags: flags,
- resumeSession: resumeSession,
+ resumeSession: true,
})
}
}
@@ -1265,7 +1268,8 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
maxHandshakeRecordLength = 1
}
- // Basic handshake, with resumption. Client and server.
+ // Basic handshake, with resumption. Client and server,
+ // session ID and session ticket.
testCases = append(testCases, testCase{
protocol: protocol,
name: "Basic-Client" + suffix,
@@ -1291,6 +1295,18 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
})
testCases = append(testCases, testCase{
protocol: protocol,
+ name: "Basic-Client-NoTicket" + suffix,
+ config: Config{
+ SessionTicketsDisabled: true,
+ Bugs: ProtocolBugs{
+ MaxHandshakeRecordLength: maxHandshakeRecordLength,
+ },
+ },
+ flags: flags,
+ resumeSession: true,
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
testType: serverTest,
name: "Basic-Server" + suffix,
config: Config{
@@ -1301,6 +1317,19 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol)
flags: flags,
resumeSession: true,
})
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "Basic-Server-NoTickets" + suffix,
+ config: Config{
+ SessionTicketsDisabled: true,
+ Bugs: ProtocolBugs{
+ MaxHandshakeRecordLength: maxHandshakeRecordLength,
+ },
+ },
+ flags: flags,
+ resumeSession: true,
+ })
// TLS client auth.
testCases = append(testCases, testCase{
@@ -1854,15 +1883,7 @@ func addExtensionTests() {
func addResumptionVersionTests() {
// TODO(davidben): Once DTLS 1.2 is working, test that as well.
for _, sessionVers := range tlsVersions {
- // TODO(davidben): SSLv3 is omitted here because runner does not
- // support resumption with session IDs.
- if sessionVers.version == VersionSSL30 {
- continue
- }
for _, resumeVers := range tlsVersions {
- if resumeVers.version == VersionSSL30 {
- continue
- }
suffix := "-" + sessionVers.name + "-" + resumeVers.name
testCases = append(testCases, testCase{
@@ -1896,10 +1917,10 @@ func addResumptionVersionTests() {
},
expectedVersion: sessionVers.version,
resumeConfig: &Config{
- MaxVersion: resumeVers.version,
- CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
- SessionTicketsDisabled: true,
+ MaxVersion: resumeVers.version,
+ CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
},
+ newSessionsOnResume: true,
expectedResumeVersion: resumeVers.version,
})