runner: Test that clients actually use renewed tickets. Some tests run three connections, resuming a renewed ticket. Particularly the way TLS 1.2 ticket renewal works, the client logic could accidentally report the old session up to the application. Our runner tests would not currently notice (though one of the tests in ssl_tests does). Make runner tests also check this by cycling ticket keys between connection attempts. This also makes newSessionsOnResume apply even if the test did not specify a resumeConfig. Change-Id: I95375c01adf6ad62de65ecf8aed3c286a0571875 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48131 Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index 645b964..767f8da 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go
@@ -1867,6 +1867,10 @@ // CompatModeWithQUIC, if true, enables TLS 1.3 compatibility mode // when running over QUIC. CompatModeWithQUIC bool + + // EncryptSessionTicketKey, if non-nil, is the ticket key to use when + // encrypting tickets. + EncryptSessionTicketKey *[32]byte } func (c *Config) serverInit() {
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index f967850..e07621d 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go
@@ -1360,19 +1360,35 @@ return err } + nextTicketKey := config.SessionTicketKey for i := 0; i < resumeCount; i++ { var resumeConfig Config if test.resumeConfig != nil { resumeConfig = *test.resumeConfig - if !test.newSessionsOnResume { - resumeConfig.SessionTicketKey = config.SessionTicketKey - resumeConfig.ClientSessionCache = config.ClientSessionCache - resumeConfig.ServerSessionCache = config.ServerSessionCache - } resumeConfig.Rand = config.Rand } else { resumeConfig = config } + + if test.newSessionsOnResume { + resumeConfig.ClientSessionCache = nil + resumeConfig.ServerSessionCache = nil + if _, err := resumeConfig.rand().Read(resumeConfig.SessionTicketKey[:]); err != nil { + return err + } + } else { + resumeConfig.ClientSessionCache = config.ClientSessionCache + resumeConfig.ServerSessionCache = config.ServerSessionCache + // Rotate the ticket keys between each connection, with each connection + // encrypting with next connection's keys. This ensures that we test + // the renewed sessions. + resumeConfig.SessionTicketKey = nextTicketKey + if _, err := resumeConfig.rand().Read(nextTicketKey[:]); err != nil { + return err + } + resumeConfig.Bugs.EncryptSessionTicketKey = &nextTicketKey + } + var connResume net.Conn connResume, err = shim.accept() if err != nil {
diff --git a/ssl/test/runner/ticket.go b/ssl/test/runner/ticket.go index 347edb5..46a6b35 100644 --- a/ssl/test/runner/ticket.go +++ b/ssl/test/runner/ticket.go
@@ -145,6 +145,11 @@ } func (c *Conn) encryptTicket(state *sessionState) ([]byte, error) { + key := c.config.SessionTicketKey[:] + if c.config.Bugs.EncryptSessionTicketKey != nil { + key = c.config.Bugs.EncryptSessionTicketKey[:] + } + serialized := state.marshal() encrypted := make([]byte, aes.BlockSize+len(serialized)+sha256.Size) iv := encrypted[:aes.BlockSize] @@ -153,13 +158,13 @@ if _, err := io.ReadFull(c.config.rand(), iv); err != nil { return nil, err } - block, err := aes.NewCipher(c.config.SessionTicketKey[:16]) + block, err := aes.NewCipher(key[:16]) if err != nil { return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error()) } cipher.NewCTR(block, iv).XORKeyStream(encrypted[aes.BlockSize:], serialized) - mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32]) + mac := hmac.New(sha256.New, key[16:32]) mac.Write(encrypted[:len(encrypted)-sha256.Size]) mac.Sum(macBytes[:0])