crypto/tls: persist the createdAt time when re-wrapping session tickets

Change-Id: I33fcde2d544943fb04c2599810cf7fb773aeba1f
Reviewed-on: https://go-review.googlesource.com/c/go/+/234483
Run-TryBot: Katie Hockman <katie@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/src/crypto/tls/handshake_client_test.go b/src/crypto/tls/handshake_client_test.go
index 313872c..de93e1b 100644
--- a/src/crypto/tls/handshake_client_test.go
+++ b/src/crypto/tls/handshake_client_test.go
@@ -980,7 +980,28 @@
 	if bytes.Equal(ticket, getTicket()) {
 		t.Fatal("new ticket wasn't provided after old ticket expired")
 	}
-	testResumeState("FreshSessionTicket", true)
+
+	// Age the session ticket a bit at a time, but don't expire it.
+	d := 0 * time.Hour
+	for i := 0; i < 13; i++ {
+		d += 12 * time.Hour
+		serverConfig.Time = func() time.Time { return time.Now().Add(d) }
+		testResumeState("OldSessionTicket", true)
+	}
+	// Expire it (now a little more than 7 days) and make sure a full
+	// handshake occurs for TLS 1.2. Resumption should still occur for
+	// TLS 1.3 since the client should be using a fresh ticket sent over
+	// by the server.
+	d += 12 * time.Hour
+	serverConfig.Time = func() time.Time { return time.Now().Add(d) }
+	if version == VersionTLS13 {
+		testResumeState("ExpiredSessionTicket", true)
+	} else {
+		testResumeState("ExpiredSessionTicket", false)
+	}
+	if bytes.Equal(ticket, getTicket()) {
+		t.Fatal("new ticket wasn't provided after old ticket expired")
+	}
 
 	// Reset serverConfig to ensure that calling SetSessionTicketKeys
 	// before the serverConfig is used works.
diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go
index 6aacfa1..57fba10 100644
--- a/src/crypto/tls/handshake_server.go
+++ b/src/crypto/tls/handshake_server.go
@@ -75,13 +75,8 @@
 		if err := hs.establishKeys(); err != nil {
 			return err
 		}
-		// ticketSupported is set in a resumption handshake if the
-		// ticket from the client was encrypted with an old session
-		// ticket key and thus a refreshed ticket should be sent.
-		if hs.hello.ticketSupported {
-			if err := hs.sendSessionTicket(); err != nil {
-				return err
-			}
+		if err := hs.sendSessionTicket(); err != nil {
+			return err
 		}
 		if err := hs.sendFinished(c.serverFinished[:]); err != nil {
 			return err
@@ -688,6 +683,9 @@
 }
 
 func (hs *serverHandshakeState) sendSessionTicket() error {
+	// ticketSupported is set in a resumption handshake if the
+	// ticket from the client was encrypted with an old session
+	// ticket key and thus a refreshed ticket should be sent.
 	if !hs.hello.ticketSupported {
 		return nil
 	}
@@ -695,6 +693,13 @@
 	c := hs.c
 	m := new(newSessionTicketMsg)
 
+	createdAt := uint64(c.config.time().Unix())
+	if hs.sessionState != nil {
+		// If this is re-wrapping an old key, then keep
+		// the original time it was created.
+		createdAt = hs.sessionState.createdAt
+	}
+
 	var certsFromClient [][]byte
 	for _, cert := range c.peerCertificates {
 		certsFromClient = append(certsFromClient, cert.Raw)
@@ -702,7 +707,7 @@
 	state := sessionState{
 		vers:         c.vers,
 		cipherSuite:  hs.suite.id,
-		createdAt:    uint64(c.config.time().Unix()),
+		createdAt:    createdAt,
 		masterSecret: hs.masterSecret,
 		certificates: certsFromClient,
 	}