Updating NewSessionTicket message and updating PSK to Draft 15.
BUG=77
Change-Id: Id8c45e98c4c22cdd437cbba1e9375239e123b261
Reviewed-on: https://boringssl-review.googlesource.com/10763
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 175160a..c4d7940 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -3715,7 +3715,6 @@
uint32_t tlsext_tick_lifetime_hint; /* Session lifetime hint in seconds */
- uint32_t ticket_flags;
uint32_t ticket_age_add;
/* extended_master_secret is true if the master secret in this session was
diff --git a/ssl/internal.h b/ssl/internal.h
index 9a78a46..4719acb 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1272,10 +1272,12 @@
extern const SSL3_ENC_METHOD TLSv1_enc_data;
extern const SSL3_ENC_METHOD SSLv3_enc_data;
-/* From draft-ietf-tls-tls13-14, used in determining ticket validity. */
-#define SSL_TICKET_ALLOW_EARLY_DATA 1
-#define SSL_TICKET_ALLOW_DHE_RESUMPTION 2
-#define SSL_TICKET_ALLOW_PSK_RESUMPTION 4
+/* From draft-ietf-tls-tls13-15, used in determining PSK modes. */
+#define SSL_PSK_KE 0x0
+#define SSL_PSK_DHE_KE 0x1
+
+#define SSL_PSK_AUTH 0x0
+#define SSL_PSK_SIGN_AUTH 0x1
CERT *ssl_cert_new(void);
CERT *ssl_cert_dup(CERT *cert);
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index ba3b10e..eeea826 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -121,7 +121,6 @@
* extendedMasterSecret [17] BOOLEAN OPTIONAL,
* keyExchangeInfo [18] INTEGER OPTIONAL,
* certChain [19] SEQUENCE OF Certificate OPTIONAL,
- * ticketFlags [20] INTEGER OPTIONAL,
* ticketAgeAdd [21] OCTET STRING OPTIONAL,
* }
*
@@ -131,7 +130,9 @@
* keyArg [0] IMPLICIT OCTET STRING OPTIONAL,
* pskIdentityHint [7] OCTET STRING OPTIONAL,
* compressionMethod [11] OCTET STRING OPTIONAL,
- * srpUsername [12] OCTET STRING OPTIONAL, */
+ * srpUsername [12] OCTET STRING OPTIONAL,
+ * ticketFlags [20] INTEGER OPTIONAL,
+ */
static const unsigned kVersion = 1;
@@ -167,8 +168,6 @@
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 18;
static const int kCertChainTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 19;
-static const int kTicketFlagsTag =
- CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 20;
static const int kTicketAgeAddTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 21;
@@ -347,14 +346,6 @@
}
}
- if (in->ticket_flags > 0) {
- if (!CBB_add_asn1(&session, &child, kTicketFlagsTag) ||
- !CBB_add_asn1_uint64(&child, in->ticket_flags)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- goto err;
- }
- }
-
if (in->ticket_age_add_valid) {
if (!CBB_add_asn1(&session, &child, kTicketAgeAddTag) ||
!CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
@@ -687,9 +678,7 @@
CBS age_add;
int age_add_present;
- if (!SSL_SESSION_parse_u32(&session, &ret->ticket_flags,
- kTicketFlagsTag, 0) ||
- !CBS_get_optional_asn1_octet_string(&session, &age_add, &age_add_present,
+ if (!CBS_get_optional_asn1_octet_string(&session, &age_add, &age_add_present,
kTicketAgeAddTag) ||
(age_add_present &&
!CBS_get_u32(&age_add, &ret->ticket_age_add)) ||
diff --git a/ssl/ssl_session.c b/ssl/ssl_session.c
index c081476..c2396b1 100644
--- a/ssl/ssl_session.c
+++ b/ssl/ssl_session.c
@@ -258,7 +258,6 @@
new_session->original_handshake_hash_len =
session->original_handshake_hash_len;
new_session->tlsext_tick_lifetime_hint = session->tlsext_tick_lifetime_hint;
- new_session->ticket_flags = session->ticket_flags;
new_session->ticket_age_add = session->ticket_age_add;
new_session->extended_master_secret = session->extended_master_secret;
}
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 048b629..0da212b 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -2019,7 +2019,7 @@
/* Pre Shared Key
*
- * https://tools.ietf.org/html/draft-ietf-tls-tls13-14 */
+ * https://tools.ietf.org/html/draft-ietf-tls-tls13-15 */
static int ext_pre_shared_key_add_clienthello(SSL *ssl, CBB *out) {
uint16_t min_version, max_version;
@@ -2035,12 +2035,16 @@
return 1;
}
- CBB contents, identities, identity;
+ CBB contents, identity, ke_modes, auth_modes, ticket;
if (!CBB_add_u16(out, TLSEXT_TYPE_pre_shared_key) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
- !CBB_add_u16_length_prefixed(&contents, &identities) ||
- !CBB_add_u16_length_prefixed(&identities, &identity) ||
- !CBB_add_bytes(&identity, ssl->session->tlsext_tick,
+ !CBB_add_u16_length_prefixed(&contents, &identity) ||
+ !CBB_add_u8_length_prefixed(&identity, &ke_modes) ||
+ !CBB_add_u8(&ke_modes, SSL_PSK_DHE_KE) ||
+ !CBB_add_u8_length_prefixed(&identity, &auth_modes) ||
+ !CBB_add_u8(&auth_modes, SSL_PSK_AUTH) ||
+ !CBB_add_u16_length_prefixed(&identity, &ticket) ||
+ !CBB_add_bytes(&ticket, ssl->session->tlsext_tick,
ssl->session->tlsext_ticklen)) {
return 0;
}
@@ -2069,19 +2073,30 @@
SSL_SESSION **out_session,
uint8_t *out_alert,
CBS *contents) {
- CBS identities, identity;
- if (!CBS_get_u16_length_prefixed(contents, &identities) ||
- !CBS_get_u16_length_prefixed(&identities, &identity) ||
- CBS_len(contents) != 0) {
+ /* We only process the first PSK identity since we don't support pure PSK. */
+ CBS identity, ke_modes, auth_modes, ticket;
+ if (!CBS_get_u16_length_prefixed(contents, &identity) ||
+ !CBS_get_u8_length_prefixed(&identity, &ke_modes) ||
+ !CBS_get_u8_length_prefixed(&identity, &auth_modes) ||
+ !CBS_get_u16_length_prefixed(&identity, &ticket) ||
+ CBS_len(&identity) != 0) {
*out_alert = SSL_AD_DECODE_ERROR;
return 0;
}
+ /* We only support tickets with PSK_DHE_KE and PSK_AUTH. */
+ if (memchr(CBS_data(&ke_modes), SSL_PSK_DHE_KE, CBS_len(&ke_modes)) == NULL ||
+ memchr(CBS_data(&auth_modes), SSL_PSK_AUTH, CBS_len(&auth_modes)) ==
+ NULL) {
+ *out_session = NULL;
+ return 1;
+ }
+
/* TLS 1.3 session tickets are renewed separately as part of the
* NewSessionTicket. */
int renew;
- return tls_process_ticket(ssl, out_session, &renew, CBS_data(&identity),
- CBS_len(&identity), NULL, 0);
+ return tls_process_ticket(ssl, out_session, &renew, CBS_data(&ticket),
+ CBS_len(&ticket), NULL, 0);
}
int ssl_ext_pre_shared_key_add_serverhello(SSL *ssl, CBB *out) {
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 1d16a7a..599ce02 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -189,11 +189,16 @@
SRTP_AES128_CM_HMAC_SHA1_32 = 0x0002
)
-// TicketFlags values (see draft-ietf-tls-tls13-14, section 4.4.1)
+// PskKeyExchangeMode values (see draft-ietf-tls-tls13-15)
const (
- ticketAllowEarlyData = 1
- ticketAllowDHEResumption = 2
- ticketAllowPSKResumption = 4
+ pskKEMode = 0
+ pskDHEKEMode = 1
+)
+
+// PskAuthenticationMode values (see draft-ietf-tls-tls13-15)
+const (
+ pskAuthMode = 0
+ pskSignAuthMode = 1
)
// ConnectionState records basic TLS details about the connection.
@@ -243,8 +248,6 @@
ocspResponse []byte
ticketCreationTime time.Time
ticketExpiration time.Time
- ticketFlags uint32
- ticketAgeAdd uint32
}
// ClientSessionCache is a cache of ClientSessionState objects that can be used
@@ -914,6 +917,13 @@
// session ticket.
SendEmptySessionTicket bool
+ // SnedPSKKeyExchangeModes, if present, determines the PSK key exchange modes
+ // to send.
+ SendPSKKeyExchangeModes []byte
+
+ // SendPSKAuthModes, if present, determines the PSK auth modes to send.
+ SendPSKAuthModes []byte
+
// FailIfSessionOffered, if true, causes the server to fail any
// connections where the client offers a non-empty session ID or session
// ticket.
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index 1fd0200..84e1eb8 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -1402,6 +1402,23 @@
return nil
}
+ var foundKE, foundAuth bool
+ for _, mode := range newSessionTicket.keModes {
+ if mode == pskDHEKEMode {
+ foundKE = true
+ }
+ }
+ for _, mode := range newSessionTicket.authModes {
+ if mode == pskAuthMode {
+ foundAuth = true
+ }
+ }
+
+ // Ignore the ticket if the server preferences do not match a mode we implement.
+ if !foundKE || !foundAuth {
+ return nil
+ }
+
session := &ClientSessionState{
sessionTicket: newSessionTicket.ticket,
vers: c.vers,
@@ -1412,8 +1429,6 @@
ocspResponse: c.ocspResponse,
ticketCreationTime: c.config.time(),
ticketExpiration: c.config.time().Add(time.Duration(newSessionTicket.ticketLifetime) * time.Second),
- ticketFlags: newSessionTicket.ticketFlags,
- ticketAgeAdd: newSessionTicket.ticketAgeAdd,
}
cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
@@ -1693,17 +1708,19 @@
peerCertificatesRaw = append(peerCertificatesRaw, cert.Raw)
}
- var ageAdd uint32
- if err := binary.Read(c.config.rand(), binary.LittleEndian, &ageAdd); err != nil {
- return err
- }
-
// TODO(davidben): Allow configuring these values.
m := &newSessionTicketMsg{
version: c.vers,
ticketLifetime: uint32(24 * time.Hour / time.Second),
- ticketFlags: ticketAllowDHEResumption | ticketAllowPSKResumption,
- ticketAgeAdd: ageAdd,
+ keModes: []byte{pskDHEKEMode},
+ authModes: []byte{pskAuthMode},
+ }
+
+ if len(c.config.Bugs.SendPSKKeyExchangeModes) != 0 {
+ m.keModes = c.config.Bugs.SendPSKKeyExchangeModes
+ }
+ if len(c.config.Bugs.SendPSKAuthModes) != 0 {
+ m.authModes = c.config.Bugs.SendPSKAuthModes
}
state := sessionState{
@@ -1713,8 +1730,6 @@
certificates: peerCertificatesRaw,
ticketCreationTime: c.config.time(),
ticketExpiration: c.config.time().Add(time.Duration(m.ticketLifetime) * time.Second),
- ticketFlags: m.ticketFlags,
- ticketAgeAdd: ageAdd,
}
if !c.config.Bugs.SendEmptySessionTicket {
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index c658e72..f03e169 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -261,10 +261,20 @@
if session.vers >= VersionTLS13 || c.config.Bugs.SendBothTickets {
// TODO(nharper): Support sending more
// than one PSK identity.
- if session.ticketFlags&ticketAllowDHEResumption != 0 || c.config.Bugs.SendBothTickets {
- hello.pskIdentities = [][]uint8{ticket}
- hello.cipherSuites = append(hello.cipherSuites, ecdhePSKSuite(session.cipherSuite))
+ psk := pskIdentity{
+ keModes: []byte{pskDHEKEMode},
+ authModes: []byte{pskAuthMode},
+ ticket: ticket,
}
+ if len(c.config.Bugs.SendPSKKeyExchangeModes) != 0 {
+ psk.keModes = c.config.Bugs.SendPSKKeyExchangeModes
+ }
+ if len(c.config.Bugs.SendPSKAuthModes) != 0 {
+ psk.authModes = c.config.Bugs.SendPSKAuthModes
+ }
+
+ hello.pskIdentities = []pskIdentity{psk}
+ hello.cipherSuites = append(hello.cipherSuites, ecdhePSKSuite(session.cipherSuite))
}
if session.vers < VersionTLS13 || c.config.Bugs.SendBothTickets {
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index 0b45a4d..e7a1235 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -124,6 +124,12 @@
keyExchange []byte
}
+type pskIdentity struct {
+ keModes []byte
+ authModes []byte
+ ticket []uint8
+}
+
type clientHelloMsg struct {
raw []byte
isDTLS bool
@@ -143,7 +149,7 @@
hasKeyShares bool
keyShares []keyShareEntry
trailingKeyShareData bool
- pskIdentities [][]uint8
+ pskIdentities []pskIdentity
hasEarlyData bool
earlyDataContext []byte
ticketSupported bool
@@ -185,7 +191,7 @@
m.hasKeyShares == m1.hasKeyShares &&
eqKeyShareEntryLists(m.keyShares, m1.keyShares) &&
m.trailingKeyShareData == m1.trailingKeyShareData &&
- eqByteSlices(m.pskIdentities, m1.pskIdentities) &&
+ eqPSKIdentityLists(m.pskIdentities, m1.pskIdentities) &&
m.hasEarlyData == m1.hasEarlyData &&
bytes.Equal(m.earlyDataContext, m1.earlyDataContext) &&
m.ticketSupported == m1.ticketSupported &&
@@ -316,8 +322,9 @@
pskIdentities := pskExtension.addU16LengthPrefixed()
for _, psk := range m.pskIdentities {
- pskIdentity := pskIdentities.addU16LengthPrefixed()
- pskIdentity.addBytes(psk)
+ pskIdentities.addU8LengthPrefixed().addBytes(psk.keModes)
+ pskIdentities.addU8LengthPrefixed().addBytes(psk.authModes)
+ pskIdentities.addU16LengthPrefixed().addBytes(psk.ticket)
}
}
if m.hasEarlyData {
@@ -612,15 +619,39 @@
}
d := data[2:length]
for len(d) > 0 {
+ var psk pskIdentity
+
+ if len(d) < 1 {
+ return false
+ }
+ keModesLen := int(d[0])
+ d = d[1:]
+ if len(d) < keModesLen {
+ return false
+ }
+ psk.keModes = d[:keModesLen]
+ d = d[keModesLen:]
+
+ if len(d) < 1 {
+ return false
+ }
+ authModesLen := int(d[0])
+ d = d[1:]
+ if len(d) < authModesLen {
+ return false
+ }
+ psk.authModes = d[:authModesLen]
+ d = d[authModesLen:]
if len(d) < 2 {
return false
}
pskLen := int(d[0])<<8 | int(d[1])
d = d[2:]
+
if len(d) < pskLen {
return false
}
- psk := d[:pskLen]
+ psk.ticket = d[:pskLen]
m.pskIdentities = append(m.pskIdentities, psk)
d = d[pskLen:]
}
@@ -1781,8 +1812,8 @@
raw []byte
version uint16
ticketLifetime uint32
- ticketFlags uint32
- ticketAgeAdd uint32
+ keModes []byte
+ authModes []byte
ticket []byte
}
@@ -1797,16 +1828,20 @@
body := ticketMsg.addU24LengthPrefixed()
body.addU32(m.ticketLifetime)
if m.version >= VersionTLS13 {
- body.addU32(m.ticketFlags)
- body.addU32(m.ticketAgeAdd)
+ body.addU8LengthPrefixed().addBytes(m.keModes)
+ body.addU8LengthPrefixed().addBytes(m.authModes)
+ }
+
+ ticket := body.addU16LengthPrefixed()
+ ticket.addBytes(m.ticket)
+
+ if m.version >= VersionTLS13 {
// Send no extensions.
//
// TODO(davidben): Add an option to send a custom extension to
// test we correctly ignore unknown ones.
body.addU16(0)
}
- ticket := body.addU16LengthPrefixed()
- ticket.addBytes(m.ticket)
m.raw = ticketMsg.finish()
return m.raw
@@ -1822,31 +1857,58 @@
data = data[8:]
if m.version >= VersionTLS13 {
- if len(data) < 10 {
+ if len(data) < 1 {
return false
}
- m.ticketFlags = uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
- m.ticketAgeAdd = uint32(data[4])<<24 | uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
- extsLength := int(data[8])<<8 + int(data[9])
- data = data[10:]
- if len(data) < extsLength {
+ keModesLength := int(data[0])
+ if len(data)-1 < keModesLength {
return false
}
- data = data[extsLength:]
+ m.keModes = data[1 : 1+keModesLength]
+ data = data[1+keModesLength:]
+
+ if len(data) < 1 {
+ return false
+ }
+ authModesLength := int(data[0])
+ if len(data)-1 < authModesLength {
+ return false
+ }
+ m.authModes = data[1 : 1+authModesLength]
+ data = data[1+authModesLength:]
}
if len(data) < 2 {
return false
}
ticketLen := int(data[0])<<8 + int(data[1])
- if len(data)-2 != ticketLen {
+ data = data[2:]
+ if len(data) < ticketLen {
return false
}
+
if m.version >= VersionTLS13 && ticketLen == 0 {
return false
}
- m.ticket = data[2:]
+ m.ticket = data[:ticketLen]
+ data = data[ticketLen:]
+
+ if m.version >= VersionTLS13 {
+ if len(data) < 2 {
+ return false
+ }
+ extsLength := int(data[0])<<8 + int(data[1])
+ data = data[2:]
+ if len(data) < extsLength {
+ return false
+ }
+ data = data[extsLength:]
+ }
+
+ if len(data) > 0 {
+ return false
+ }
return true
}
@@ -2071,3 +2133,16 @@
return true
}
+
+func eqPSKIdentityLists(x, y []pskIdentity) bool {
+ if len(x) != len(y) {
+ return false
+ }
+ for i, v := range x {
+ if !bytes.Equal(y[i].keModes, v.keModes) || !bytes.Equal(y[i].authModes, v.authModes) || !bytes.Equal(y[i].ticket, v.ticket) {
+ return false
+ }
+ }
+ return true
+
+}
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index a09dc27..934e052 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -400,10 +400,34 @@
pskIdentities := hs.clientHello.pskIdentities
if len(pskIdentities) == 0 && len(hs.clientHello.sessionTicket) > 0 && c.config.Bugs.AcceptAnySession {
- pskIdentities = [][]uint8{hs.clientHello.sessionTicket}
+ psk := pskIdentity{
+ keModes: []byte{pskDHEKEMode},
+ authModes: []byte{pskAuthMode},
+ ticket: hs.clientHello.sessionTicket,
+ }
+ pskIdentities = []pskIdentity{psk}
}
for i, pskIdentity := range pskIdentities {
- sessionState, ok := c.decryptTicket(pskIdentity)
+ foundKE := false
+ foundAuth := false
+
+ for _, keMode := range pskIdentity.keModes {
+ if keMode == pskDHEKEMode {
+ foundKE = true
+ }
+ }
+
+ for _, authMode := range pskIdentity.authModes {
+ if authMode == pskAuthMode {
+ foundAuth = true
+ }
+ }
+
+ if !foundKE || !foundAuth {
+ continue
+ }
+
+ sessionState, ok := c.decryptTicket(pskIdentity.ticket)
if !ok {
continue
}
@@ -411,9 +435,6 @@
if sessionState.vers != c.vers && c.config.Bugs.AcceptAnySession {
continue
}
- if sessionState.ticketFlags&ticketAllowDHEResumption == 0 {
- continue
- }
if sessionState.ticketExpiration.Before(c.config.time()) {
continue
}
@@ -1090,7 +1111,7 @@
ticket := hs.clientHello.sessionTicket
if len(ticket) == 0 && len(hs.clientHello.pskIdentities) > 0 && c.config.Bugs.AcceptAnySession {
- ticket = hs.clientHello.pskIdentities[0]
+ ticket = hs.clientHello.pskIdentities[0].ticket
}
if len(ticket) > 0 {
if c.config.SessionTicketsDisabled {
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 17cc507..f536ceb 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -2118,19 +2118,6 @@
expectMessageDropped: true,
},
{
- // In TLS 1.2 and below, empty NewSessionTicket messages
- // mean the server changed its mind on sending a ticket.
- name: "SendEmptySessionTicket",
- config: Config{
- MaxVersion: VersionTLS12,
- Bugs: ProtocolBugs{
- SendEmptySessionTicket: true,
- FailIfSessionOffered: true,
- },
- },
- flags: []string{"-expect-no-session"},
- },
- {
name: "BadHelloRequest-1",
renegotiate: 1,
config: Config{
@@ -7572,6 +7559,105 @@
})
}
+func addSessionTicketTests() {
+ testCases = append(testCases, testCase{
+ // In TLS 1.2 and below, empty NewSessionTicket messages
+ // mean the server changed its mind on sending a ticket.
+ name: "SendEmptySessionTicket",
+ config: Config{
+ MaxVersion: VersionTLS12,
+ Bugs: ProtocolBugs{
+ SendEmptySessionTicket: true,
+ },
+ },
+ flags: []string{"-expect-no-session"},
+ })
+
+ // Test that the server ignores unknown PSK modes.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-SendUnknownModeSessionTicket-Server",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendPSKKeyExchangeModes: []byte{0x1a, pskDHEKEMode, 0x2a},
+ SendPSKAuthModes: []byte{0x1a, pskAuthMode, 0x2a},
+ },
+ },
+ resumeSession: true,
+ expectedResumeVersion: VersionTLS13,
+ })
+
+ // Test that the server declines sessions with no matching key exchange mode.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-SendBadKEModeSessionTicket-Server",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendPSKKeyExchangeModes: []byte{0x1a},
+ },
+ },
+ resumeSession: true,
+ expectResumeRejected: true,
+ })
+
+ // Test that the server declines sessions with no matching auth mode.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-SendBadAuthModeSessionTicket-Server",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendPSKAuthModes: []byte{0x1a},
+ },
+ },
+ resumeSession: true,
+ expectResumeRejected: true,
+ })
+
+ // Test that the client ignores unknown PSK modes.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "TLS13-SendUnknownModeSessionTicket-Client",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendPSKKeyExchangeModes: []byte{0x1a, pskDHEKEMode, 0x2a},
+ SendPSKAuthModes: []byte{0x1a, pskAuthMode, 0x2a},
+ },
+ },
+ resumeSession: true,
+ expectedResumeVersion: VersionTLS13,
+ })
+
+ // Test that the client ignores tickets with no matching key exchange mode.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "TLS13-SendBadKEModeSessionTicket-Client",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendPSKKeyExchangeModes: []byte{0x1a},
+ },
+ },
+ flags: []string{"-expect-no-session"},
+ })
+
+ // Test that the client ignores tickets with no matching auth mode.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "TLS13-SendBadAuthModeSessionTicket-Client",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendPSKAuthModes: []byte{0x1a},
+ },
+ },
+ flags: []string{"-expect-no-session"},
+ })
+}
+
func addChangeCipherSpecTests() {
// Test missing ChangeCipherSpecs.
testCases = append(testCases, testCase{
@@ -8642,6 +8728,7 @@
addCurveTests()
addCECPQ1Tests()
addDHEGroupSizeTests()
+ addSessionTicketTests()
addTLS13RecordTests()
addAllStateMachineCoverageTests()
addChangeCipherSpecTests()
diff --git a/ssl/tls13_client.c b/ssl/tls13_client.c
index ee73f73..aa80b69 100644
--- a/ssl/tls13_client.c
+++ b/ssl/tls13_client.c
@@ -645,14 +645,16 @@
return 0;
}
- CBS cbs, extensions, ticket;
+ CBS cbs, ke_modes, auth_modes, ticket, extensions;
CBS_init(&cbs, ssl->init_msg, ssl->init_num);
if (!CBS_get_u32(&cbs, &session->tlsext_tick_lifetime_hint) ||
- !CBS_get_u32(&cbs, &session->ticket_flags) ||
- !CBS_get_u32(&cbs, &session->ticket_age_add) ||
- !CBS_get_u16_length_prefixed(&cbs, &extensions) ||
+ !CBS_get_u8_length_prefixed(&cbs, &ke_modes) ||
+ CBS_len(&ke_modes) == 0 ||
+ !CBS_get_u8_length_prefixed(&cbs, &auth_modes) ||
+ CBS_len(&auth_modes) == 0 ||
!CBS_get_u16_length_prefixed(&cbs, &ticket) ||
!CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) ||
+ !CBS_get_u16_length_prefixed(&cbs, &extensions) ||
CBS_len(&cbs) != 0) {
SSL_SESSION_free(session);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -660,10 +662,13 @@
return 0;
}
- session->ticket_age_add_valid = 1;
session->not_resumable = 0;
- if (ssl->ctx->new_session_cb != NULL &&
+ /* Ignore the ticket unless the server preferences are compatible with us. */
+ if (memchr(CBS_data(&ke_modes), SSL_PSK_DHE_KE, CBS_len(&ke_modes)) != NULL &&
+ memchr(CBS_data(&auth_modes), SSL_PSK_AUTH, CBS_len(&auth_modes)) !=
+ NULL &&
+ ssl->ctx->new_session_cb != NULL &&
ssl->ctx->new_session_cb(ssl, session)) {
/* |new_session_cb|'s return value signals that it took ownership. */
return 1;
diff --git a/ssl/tls13_server.c b/ssl/tls13_server.c
index 53e5363..9e146e6 100644
--- a/ssl/tls13_server.c
+++ b/ssl/tls13_server.c
@@ -141,10 +141,8 @@
uint16_t resumption_cipher;
if (session != NULL &&
- /* We currently only support ECDHE-PSK resumption. */
- ((session->ticket_flags & SSL_TICKET_ALLOW_DHE_RESUMPTION) == 0 ||
- /* Only resume if the session's version matches. */
- session->ssl_version != ssl->version ||
+ /* Only resume if the session's version matches. */
+ (session->ssl_version != ssl->version ||
!ssl_cipher_get_ecdhe_psk_cipher(session->cipher, &resumption_cipher) ||
!ssl_client_cipher_list_contains_cipher(&client_hello,
resumption_cipher))) {
@@ -556,22 +554,21 @@
SSL_HANDSHAKE *hs) {
SSL_SESSION *session = ssl->s3->new_session;
session->tlsext_tick_lifetime_hint = session->timeout;
- session->ticket_flags = SSL_TICKET_ALLOW_DHE_RESUMPTION;
- if (!RAND_bytes((uint8_t *)&session->ticket_age_add,
- sizeof(session->ticket_age_add))) {
- return 0;
- }
- session->ticket_age_add_valid = 1;
- CBB cbb, body, ticket;
+ /* TODO(svaldez): Add support for sending 0RTT through TicketEarlyDataInfo
+ * extension. */
+
+ CBB cbb, body, ke_modes, auth_modes, ticket;
if (!ssl->method->init_message(ssl, &cbb, &body,
SSL3_MT_NEW_SESSION_TICKET) ||
!CBB_add_u32(&body, session->tlsext_tick_lifetime_hint) ||
- !CBB_add_u32(&body, session->ticket_flags) ||
- !CBB_add_u32(&body, session->ticket_age_add) ||
- !CBB_add_u16(&body, 0 /* no ticket extensions */) ||
+ !CBB_add_u8_length_prefixed(&body, &ke_modes) ||
+ !CBB_add_u8(&ke_modes, SSL_PSK_DHE_KE) ||
+ !CBB_add_u8_length_prefixed(&body, &auth_modes) ||
+ !CBB_add_u8(&auth_modes, SSL_PSK_AUTH) ||
!CBB_add_u16_length_prefixed(&body, &ticket) ||
!ssl_encrypt_ticket(ssl, &ticket, session) ||
+ !CBB_add_u16(&body, 0 /* no ticket extensions */) ||
!ssl->method->finish_message(ssl, &cbb)) {
CBB_cleanup(&cbb);
return ssl_hs_error;