Support resumption in DTLS 1.3.
Bug: 42290594
Change-Id: Ic618dfa59d312979e613b0fb231dff4ec467195e
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/72087
Commit-Queue: David Benjamin <davidben@google.com>
Auto-Submit: Nick Harper <nharper@chromium.org>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index 827fa18..3f0ffa7 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -856,8 +856,11 @@
// We may have a pre-1.3 session if SendBothTickets is set.
if session.vers < VersionTLS13 {
version = VersionTLS13
+ if c.isDTLS {
+ version = VersionDTLS125Experimental
+ }
}
- generatePSKBinders(version, hello, session, nil, nil, c.config)
+ generatePSKBinders(version, c.isDTLS, hello, session, nil, nil, c.config)
}
if c.config.Bugs.SendClientHelloWithFixes != nil {
@@ -1573,7 +1576,7 @@
hello.raw = nil
if len(hello.pskIdentities) > 0 {
- generatePSKBinders(c.wireVersion, hello, hs.session, firstHelloBytes, helloRetryRequest.marshal(), c.config)
+ generatePSKBinders(c.wireVersion, c.isDTLS, hello, hs.session, firstHelloBytes, helloRetryRequest.marshal(), c.config)
}
if outerHello != nil {
@@ -2406,7 +2409,7 @@
copy(b[len(b)-len(xb):], xb)
}
-func generatePSKBinders(version uint16, hello *clientHelloMsg, session *ClientSessionState, firstClientHello, helloRetryRequest []byte, config *Config) {
+func generatePSKBinders(version uint16, isDTLS bool, hello *clientHelloMsg, session *ClientSessionState, firstClientHello, helloRetryRequest []byte, config *Config) {
maybeCorruptBinder := !config.Bugs.OnlyCorruptSecondPSKBinder || len(firstClientHello) > 0
binderLen := session.cipherSuite.hash().Size()
numBinders := 1
@@ -2438,7 +2441,7 @@
helloBytes := hello.marshal()
binderSize := len(hello.pskBinders)*(binderLen+1) + 2
truncatedHello := helloBytes[:len(helloBytes)-binderSize]
- binder := computePSKBinder(session.secret, version, resumptionPSKBinderLabel, session.cipherSuite, firstClientHello, helloRetryRequest, truncatedHello)
+ binder := computePSKBinder(session.secret, version, isDTLS, resumptionPSKBinderLabel, session.cipherSuite, firstClientHello, helloRetryRequest, truncatedHello)
if maybeCorruptBinder {
if config.Bugs.SendShortPSKBinder {
binder = binder[:binderLen]
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 2c210eb..7ed4745 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -665,7 +665,7 @@
if !replacedPSKIdentities {
binderToVerify := hs.clientHello.pskBinders[i]
- if err := verifyPSKBinder(c.wireVersion, hs.clientHello, sessionState, binderToVerify, []byte{}, []byte{}); err != nil {
+ if err := verifyPSKBinder(c.wireVersion, c.isDTLS, hs.clientHello, sessionState, binderToVerify, []byte{}, []byte{}); err != nil {
return err
}
}
@@ -893,7 +893,7 @@
}
if found {
binderToVerify := newClientHello.pskBinders[pskIndex]
- if err := verifyPSKBinder(c.wireVersion, newClientHello, hs.sessionState, binderToVerify, hs.clientHello.marshal(), helloRetryRequest.marshal()); err != nil {
+ if err := verifyPSKBinder(c.wireVersion, c.isDTLS, newClientHello, hs.sessionState, binderToVerify, hs.clientHello.marshal(), helloRetryRequest.marshal()); err != nil {
return err
}
} else if !config.Bugs.AcceptAnySession {
@@ -1425,8 +1425,7 @@
// TODO(davidben): Allow configuring the number of tickets sent for
// testing.
- // TODO(nharper): Add support for post-handshake messages in DTLS 1.3.
- if !c.config.SessionTicketsDisabled && foundKEMode && !c.isDTLS {
+ if !c.config.SessionTicketsDisabled && foundKEMode {
ticketCount := 2
for i := 0; i < ticketCount; i++ {
c.SendNewSessionTicket([]byte{byte(i)})
@@ -2404,7 +2403,7 @@
return val&0x0f0f == 0x0a0a && val&0xff == val>>8
}
-func verifyPSKBinder(version uint16, clientHello *clientHelloMsg, sessionState *sessionState, binderToVerify, firstClientHello, helloRetryRequest []byte) error {
+func verifyPSKBinder(version uint16, isDTLS bool, clientHello *clientHelloMsg, sessionState *sessionState, binderToVerify, firstClientHello, helloRetryRequest []byte) error {
binderLen := 2
for _, binder := range clientHello.pskBinders {
binderLen += 1 + len(binder)
@@ -2417,7 +2416,7 @@
return errors.New("tls: Unknown cipher suite for PSK in session")
}
- binder := computePSKBinder(sessionState.secret, version, resumptionPSKBinderLabel, pskCipherSuite, firstClientHello, helloRetryRequest, truncatedHello)
+ binder := computePSKBinder(sessionState.secret, version, isDTLS, resumptionPSKBinderLabel, pskCipherSuite, firstClientHello, helloRetryRequest, truncatedHello)
if !bytes.Equal(binder, binderToVerify) {
return errors.New("tls: PSK binder does not verify")
}
diff --git a/ssl/test/runner/prf.go b/ssl/test/runner/prf.go
index 0324c87..5d56257 100644
--- a/ssl/test/runner/prf.go
+++ b/ssl/test/runner/prf.go
@@ -466,8 +466,8 @@
return hkdfExpandLabel(hash, secret, applicationTrafficLabel, nil, hash.Size(), isDTLS)
}
-func computePSKBinder(psk []byte, version uint16, label []byte, cipherSuite *cipherSuite, clientHello, helloRetryRequest, truncatedHello []byte) []byte {
- finishedHash := newFinishedHash(version, false, cipherSuite)
+func computePSKBinder(psk []byte, version uint16, isDTLS bool, label []byte, cipherSuite *cipherSuite, clientHello, helloRetryRequest, truncatedHello []byte) []byte {
+ finishedHash := newFinishedHash(version, isDTLS, cipherSuite)
finishedHash.addEntropy(psk)
binderKey := finishedHash.deriveSecret(label)
finishedHash.Write(clientHello)
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 72937af..ecfc61e 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -5644,7 +5644,8 @@
}, flags...),
resumeSession: true,
})
- if vers.version >= VersionTLS13 {
+ // TODO(crbug.com/42290594): Support 0-RTT in DTLS 1.3.
+ if vers.version >= VersionTLS13 && config.protocol != dtls {
tests = append(tests, testCase{
testType: testType,
name: "EarlyData-RejectTicket-Client-Reverify" + suffix,
@@ -7901,255 +7902,258 @@
})
// Test that ALPS is carried over on 0-RTT.
- for _, empty := range []bool{false, true} {
- maybeEmpty := ""
- runnerSettings := "runner"
- shimSettings := "shim"
- if empty {
- maybeEmpty = "Empty-"
- runnerSettings = ""
- shimSettings = ""
- }
+ // TODO(crbug.com/42290594): Support 0-RTT in DTLS 1.3.
+ if protocol != dtls {
+ for _, empty := range []bool{false, true} {
+ maybeEmpty := ""
+ runnerSettings := "runner"
+ shimSettings := "shim"
+ if empty {
+ maybeEmpty = "Empty-"
+ runnerSettings = ""
+ shimSettings = ""
+ }
- expectations = connectionExpectations{
- peerApplicationSettingsOld: []byte(shimSettings),
- }
- if alpsCodePoint == ALPSUseCodepointNew {
expectations = connectionExpectations{
- peerApplicationSettings: []byte(shimSettings),
+ peerApplicationSettingsOld: []byte(shimSettings),
}
- }
- testCases = append(testCases, testCase{
- protocol: protocol,
- testType: clientTest,
- name: fmt.Sprintf("ALPS-EarlyData-Client-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
- skipQUICALPNConfig: true,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
- ALPSUseNewCodepoint: alpsCodePoint,
- },
- resumeSession: true,
- earlyData: true,
- flags: append([]string{
- "-advertise-alpn", "\x05proto",
- "-expect-alpn", "proto",
- "-application-settings", "proto," + shimSettings,
- "-expect-peer-application-settings", runnerSettings,
- }, alpsFlags...),
- expectations: expectations,
- })
- testCases = append(testCases, testCase{
- protocol: protocol,
- testType: serverTest,
- name: fmt.Sprintf("ALPS-EarlyData-Server-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
- skipQUICALPNConfig: true,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
- ALPSUseNewCodepoint: alpsCodePoint,
- },
- resumeSession: true,
- earlyData: true,
- flags: append([]string{
- "-select-alpn", "proto",
- "-application-settings", "proto," + shimSettings,
- "-expect-peer-application-settings", runnerSettings,
- }, alpsFlags...),
- expectations: expectations,
- })
-
- // Sending application settings in 0-RTT handshakes is forbidden.
- testCases = append(testCases, testCase{
- protocol: protocol,
- testType: clientTest,
- name: fmt.Sprintf("ALPS-EarlyData-SendApplicationSettingsWithEarlyData-Client-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
- skipQUICALPNConfig: true,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
- Bugs: ProtocolBugs{
- SendApplicationSettingsWithEarlyData: true,
- },
- ALPSUseNewCodepoint: alpsCodePoint,
- },
- resumeSession: true,
- earlyData: true,
- flags: append([]string{
- "-advertise-alpn", "\x05proto",
- "-expect-alpn", "proto",
- "-application-settings", "proto," + shimSettings,
- "-expect-peer-application-settings", runnerSettings,
- }, alpsFlags...),
- expectations: expectations,
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
- expectedLocalError: "remote error: illegal parameter",
- })
- testCases = append(testCases, testCase{
- protocol: protocol,
- testType: serverTest,
- name: fmt.Sprintf("ALPS-EarlyData-SendApplicationSettingsWithEarlyData-Server-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
- skipQUICALPNConfig: true,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
- Bugs: ProtocolBugs{
- SendApplicationSettingsWithEarlyData: true,
- },
- ALPSUseNewCodepoint: alpsCodePoint,
- },
- resumeSession: true,
- earlyData: true,
- flags: append([]string{
- "-select-alpn", "proto",
- "-application-settings", "proto," + shimSettings,
- "-expect-peer-application-settings", runnerSettings,
- }, alpsFlags...),
- expectations: expectations,
- shouldFail: true,
- expectedError: ":UNEXPECTED_MESSAGE:",
- expectedLocalError: "remote error: unexpected message",
- })
- }
-
- // Test that the client and server each decline early data if local
- // ALPS preferences has changed for the current connection.
- alpsMismatchTests := []struct {
- name string
- initialSettings, resumeSettings []byte
- }{
- {"DifferentValues", []byte("settings1"), []byte("settings2")},
- {"OnOff", []byte("settings"), nil},
- {"OffOn", nil, []byte("settings")},
- // The empty settings value should not be mistaken for ALPS not
- // being negotiated.
- {"OnEmpty", []byte("settings"), []byte{}},
- {"EmptyOn", []byte{}, []byte("settings")},
- {"EmptyOff", []byte{}, nil},
- {"OffEmpty", nil, []byte{}},
- }
- for _, test := range alpsMismatchTests {
- flags := []string{"-on-resume-expect-early-data-reason", "alps_mismatch"}
- flags = append(flags, alpsFlags...)
- if test.initialSettings != nil {
- flags = append(flags, "-on-initial-application-settings", "proto,"+string(test.initialSettings))
- flags = append(flags, "-on-initial-expect-peer-application-settings", "runner")
- }
- if test.resumeSettings != nil {
- flags = append(flags, "-on-resume-application-settings", "proto,"+string(test.resumeSettings))
- flags = append(flags, "-on-resume-expect-peer-application-settings", "runner")
- }
-
- expectations = connectionExpectations{
- peerApplicationSettingsOld: test.initialSettings,
- }
- resumeExpectations = &connectionExpectations{
- peerApplicationSettingsOld: test.resumeSettings,
- }
- if alpsCodePoint == ALPSUseCodepointNew {
- expectations = connectionExpectations{
- peerApplicationSettings: test.initialSettings,
+ if alpsCodePoint == ALPSUseCodepointNew {
+ expectations = connectionExpectations{
+ peerApplicationSettings: []byte(shimSettings),
+ }
}
- resumeExpectations = &connectionExpectations{
- peerApplicationSettings: test.resumeSettings,
- }
- }
- // The client should not offer early data if the session is
- // inconsistent with the new configuration. Note that if
- // the session did not negotiate ALPS (test.initialSettings
- // is nil), the client always offers early data.
- if test.initialSettings != nil {
testCases = append(testCases, testCase{
protocol: protocol,
testType: clientTest,
- name: fmt.Sprintf("ALPS-EarlyData-Mismatch-%s-Client-%s-%s", test.name, alpsCodePoint, suffix),
+ name: fmt.Sprintf("ALPS-EarlyData-Client-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
- MaxEarlyDataSize: 16384,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
+ ALPSUseNewCodepoint: alpsCodePoint,
+ },
+ resumeSession: true,
+ earlyData: true,
+ flags: append([]string{
+ "-advertise-alpn", "\x05proto",
+ "-expect-alpn", "proto",
+ "-application-settings", "proto," + shimSettings,
+ "-expect-peer-application-settings", runnerSettings,
+ }, alpsFlags...),
+ expectations: expectations,
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: fmt.Sprintf("ALPS-EarlyData-Server-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
+ ALPSUseNewCodepoint: alpsCodePoint,
+ },
+ resumeSession: true,
+ earlyData: true,
+ flags: append([]string{
+ "-select-alpn", "proto",
+ "-application-settings", "proto," + shimSettings,
+ "-expect-peer-application-settings", runnerSettings,
+ }, alpsFlags...),
+ expectations: expectations,
+ })
+
+ // Sending application settings in 0-RTT handshakes is forbidden.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: fmt.Sprintf("ALPS-EarlyData-SendApplicationSettingsWithEarlyData-Client-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
+ Bugs: ProtocolBugs{
+ SendApplicationSettingsWithEarlyData: true,
+ },
+ ALPSUseNewCodepoint: alpsCodePoint,
+ },
+ resumeSession: true,
+ earlyData: true,
+ flags: append([]string{
+ "-advertise-alpn", "\x05proto",
+ "-expect-alpn", "proto",
+ "-application-settings", "proto," + shimSettings,
+ "-expect-peer-application-settings", runnerSettings,
+ }, alpsFlags...),
+ expectations: expectations,
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
+ expectedLocalError: "remote error: illegal parameter",
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: fmt.Sprintf("ALPS-EarlyData-SendApplicationSettingsWithEarlyData-Server-%s-%s-%s", alpsCodePoint, maybeEmpty, suffix),
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte(runnerSettings)},
+ Bugs: ProtocolBugs{
+ SendApplicationSettingsWithEarlyData: true,
+ },
+ ALPSUseNewCodepoint: alpsCodePoint,
+ },
+ resumeSession: true,
+ earlyData: true,
+ flags: append([]string{
+ "-select-alpn", "proto",
+ "-application-settings", "proto," + shimSettings,
+ "-expect-peer-application-settings", runnerSettings,
+ }, alpsFlags...),
+ expectations: expectations,
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ expectedLocalError: "remote error: unexpected message",
+ })
+ }
+
+ // Test that the client and server each decline early data if local
+ // ALPS preferences has changed for the current connection.
+ alpsMismatchTests := []struct {
+ name string
+ initialSettings, resumeSettings []byte
+ }{
+ {"DifferentValues", []byte("settings1"), []byte("settings2")},
+ {"OnOff", []byte("settings"), nil},
+ {"OffOn", nil, []byte("settings")},
+ // The empty settings value should not be mistaken for ALPS not
+ // being negotiated.
+ {"OnEmpty", []byte("settings"), []byte{}},
+ {"EmptyOn", []byte{}, []byte("settings")},
+ {"EmptyOff", []byte{}, nil},
+ {"OffEmpty", nil, []byte{}},
+ }
+ for _, test := range alpsMismatchTests {
+ flags := []string{"-on-resume-expect-early-data-reason", "alps_mismatch"}
+ flags = append(flags, alpsFlags...)
+ if test.initialSettings != nil {
+ flags = append(flags, "-on-initial-application-settings", "proto,"+string(test.initialSettings))
+ flags = append(flags, "-on-initial-expect-peer-application-settings", "runner")
+ }
+ if test.resumeSettings != nil {
+ flags = append(flags, "-on-resume-application-settings", "proto,"+string(test.resumeSettings))
+ flags = append(flags, "-on-resume-expect-peer-application-settings", "runner")
+ }
+
+ expectations = connectionExpectations{
+ peerApplicationSettingsOld: test.initialSettings,
+ }
+ resumeExpectations = &connectionExpectations{
+ peerApplicationSettingsOld: test.resumeSettings,
+ }
+ if alpsCodePoint == ALPSUseCodepointNew {
+ expectations = connectionExpectations{
+ peerApplicationSettings: test.initialSettings,
+ }
+ resumeExpectations = &connectionExpectations{
+ peerApplicationSettings: test.resumeSettings,
+ }
+ }
+ // The client should not offer early data if the session is
+ // inconsistent with the new configuration. Note that if
+ // the session did not negotiate ALPS (test.initialSettings
+ // is nil), the client always offers early data.
+ if test.initialSettings != nil {
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: fmt.Sprintf("ALPS-EarlyData-Mismatch-%s-Client-%s-%s", test.name, alpsCodePoint, suffix),
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ MaxEarlyDataSize: 16384,
+ NextProtos: []string{"proto"},
+ ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
+ ALPSUseNewCodepoint: alpsCodePoint,
+ },
+ resumeSession: true,
+ flags: append([]string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-expect-no-offer-early-data",
+ "-advertise-alpn", "\x05proto",
+ "-expect-alpn", "proto",
+ }, flags...),
+ expectations: expectations,
+ resumeExpectations: resumeExpectations,
+ })
+ }
+
+ // The server should reject early data if the session is
+ // inconsistent with the new selection.
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: fmt.Sprintf("ALPS-EarlyData-Mismatch-%s-Server-%s-%s", test.name, alpsCodePoint, suffix),
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
NextProtos: []string{"proto"},
ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
ALPSUseNewCodepoint: alpsCodePoint,
},
- resumeSession: true,
+ resumeSession: true,
+ earlyData: true,
+ expectEarlyDataRejected: true,
flags: append([]string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-no-offer-early-data",
- "-advertise-alpn", "\x05proto",
- "-expect-alpn", "proto",
+ "-select-alpn", "proto",
}, flags...),
expectations: expectations,
resumeExpectations: resumeExpectations,
})
}
- // The server should reject early data if the session is
- // inconsistent with the new selection.
+ // Test that 0-RTT continues working when the shim configures
+ // ALPS but the peer does not.
testCases = append(testCases, testCase{
protocol: protocol,
- testType: serverTest,
- name: fmt.Sprintf("ALPS-EarlyData-Mismatch-%s-Server-%s-%s", test.name, alpsCodePoint, suffix),
+ testType: clientTest,
+ name: fmt.Sprintf("ALPS-EarlyData-Client-ServerDecline-%s-%s", alpsCodePoint, suffix),
skipQUICALPNConfig: true,
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"proto"},
- ApplicationSettings: map[string][]byte{"proto": []byte("runner")},
ALPSUseNewCodepoint: alpsCodePoint,
},
- resumeSession: true,
- earlyData: true,
- expectEarlyDataRejected: true,
+ resumeSession: true,
+ earlyData: true,
+ flags: append([]string{
+ "-advertise-alpn", "\x05proto",
+ "-expect-alpn", "proto",
+ "-application-settings", "proto,shim",
+ }, alpsFlags...),
+ })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: fmt.Sprintf("ALPS-EarlyData-Server-ClientNoOffe-%s-%s", alpsCodePoint, suffix),
+ skipQUICALPNConfig: true,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"proto"},
+ ALPSUseNewCodepoint: alpsCodePoint,
+ },
+ resumeSession: true,
+ earlyData: true,
flags: append([]string{
"-select-alpn", "proto",
- }, flags...),
- expectations: expectations,
- resumeExpectations: resumeExpectations,
+ "-application-settings", "proto,shim",
+ }, alpsFlags...),
})
}
-
- // Test that 0-RTT continues working when the shim configures
- // ALPS but the peer does not.
- testCases = append(testCases, testCase{
- protocol: protocol,
- testType: clientTest,
- name: fmt.Sprintf("ALPS-EarlyData-Client-ServerDecline-%s-%s", alpsCodePoint, suffix),
- skipQUICALPNConfig: true,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ALPSUseNewCodepoint: alpsCodePoint,
- },
- resumeSession: true,
- earlyData: true,
- flags: append([]string{
- "-advertise-alpn", "\x05proto",
- "-expect-alpn", "proto",
- "-application-settings", "proto,shim",
- }, alpsFlags...),
- })
- testCases = append(testCases, testCase{
- protocol: protocol,
- testType: serverTest,
- name: fmt.Sprintf("ALPS-EarlyData-Server-ClientNoOffe-%s-%s", alpsCodePoint, suffix),
- skipQUICALPNConfig: true,
- config: Config{
- MaxVersion: ver.version,
- NextProtos: []string{"proto"},
- ALPSUseNewCodepoint: alpsCodePoint,
- },
- resumeSession: true,
- earlyData: true,
- flags: append([]string{
- "-select-alpn", "proto",
- "-application-settings", "proto,shim",
- }, alpsFlags...),
- })
}
} else {
// Test the client rejects the ALPS extension if the server
@@ -8779,6 +8783,10 @@
if ech && ver.version < VersionTLS13 {
continue
}
+ // TODO(crbug.com/42290594): This test is broken when run with DTLS 1.3 and ECH.
+ if protocol == dtls && ver.version >= VersionTLS13 && ech {
+ continue
+ }
test := testCase{
protocol: protocol,
@@ -9090,6 +9098,13 @@
suffix := "-" + sessionVers.name + "-" + resumeVers.name
suffix += "-" + protocol.String()
+ // TODO(crbug.com/42290594): Attempting to resume a DTLS 1.3 session
+ // when the new connection is an older version is currently broken.
+ // The current implementation recomputes the PSK binder value for the
+ // second ClientHello (sent in response to HelloVerifyRequest), but
+ // the runner expects all extension values to stay byte-for-byte the
+ // same (a possible interpretation of RFC 6347 section 4.2.1).
+ isBadDTLSResumption := protocol == dtls && sessionVers.version == VersionTLS13 && resumeVers.version < VersionTLS13
if sessionVers.version == resumeVers.version {
testCases = append(testCases, testCase{
protocol: protocol,
@@ -9108,7 +9123,7 @@
version: resumeVers.version,
},
})
- } else {
+ } else if !isBadDTLSResumption {
testCases = append(testCases, testCase{
protocol: protocol,
name: "Resume-Client-Mismatch" + suffix,
@@ -9133,25 +9148,27 @@
})
}
- testCases = append(testCases, testCase{
- protocol: protocol,
- name: "Resume-Client-NoResume" + suffix,
- resumeSession: true,
- config: Config{
- MaxVersion: sessionVers.version,
- },
- expectations: connectionExpectations{
- version: sessionVers.version,
- },
- resumeConfig: &Config{
- MaxVersion: resumeVers.version,
- },
- newSessionsOnResume: true,
- expectResumeRejected: true,
- resumeExpectations: &connectionExpectations{
- version: resumeVers.version,
- },
- })
+ if !isBadDTLSResumption {
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ name: "Resume-Client-NoResume" + suffix,
+ resumeSession: true,
+ config: Config{
+ MaxVersion: sessionVers.version,
+ },
+ expectations: connectionExpectations{
+ version: sessionVers.version,
+ },
+ resumeConfig: &Config{
+ MaxVersion: resumeVers.version,
+ },
+ newSessionsOnResume: true,
+ expectResumeRejected: true,
+ resumeExpectations: &connectionExpectations{
+ version: resumeVers.version,
+ },
+ })
+ }
testCases = append(testCases, testCase{
protocol: protocol,
@@ -20948,27 +20965,6 @@
}
}
-// TODO(crbug.com/boringssl/715): Once our DTLS 1.3 implementation supports
-// resumption, remove this filter.
-func filterTests() {
- tests := make([]testCase, 0, len(testCases))
- isDTLS13ResumptionTest := func(test testCase) bool {
- if !test.resumeSession {
- return false
- }
- if test.protocol != dtls {
- return false
- }
- return test.config.MaxVersion == VersionTLS13 || test.config.MinVersion == VersionTLS13 || test.expectations.version == VersionTLS13
- }
- for _, test := range testCases {
- if !isDTLS13ResumptionTest(test) {
- tests = append(tests, test)
- }
- }
- testCases = tests
-}
-
func main() {
flag.Parse()
var err error
@@ -21054,7 +21050,6 @@
testCases = append(testCases, toAppend...)
checkTests()
- filterTests()
dispatcher, err := newShimDispatcher()
if err != nil {
diff --git a/ssl/tls13_both.cc b/ssl/tls13_both.cc
index 67c7b42..676b423 100644
--- a/ssl/tls13_both.cc
+++ b/ssl/tls13_both.cc
@@ -664,11 +664,15 @@
}
bool tls13_post_handshake(SSL *ssl, const SSLMessage &msg) {
- if (SSL_is_dtls(ssl)) {
- // TODO(crbug.com/42290594): Process post-handshake messages in DTLS 1.3.
- return true;
+ if (msg.type == SSL3_MT_NEW_SESSION_TICKET && !ssl->server) {
+ return tls13_process_new_session_ticket(ssl, msg);
}
+
if (msg.type == SSL3_MT_KEY_UPDATE) {
+ if (SSL_is_dtls(ssl)) {
+ // TODO(crbug.com/42290594): Process post-handshake messages in DTLS 1.3.
+ return true;
+ }
ssl->s3->key_update_count++;
if (ssl->quic_method != nullptr ||
ssl->s3->key_update_count > kMaxKeyUpdates) {
@@ -682,10 +686,6 @@
ssl->s3->key_update_count = 0;
- if (msg.type == SSL3_MT_NEW_SESSION_TICKET && !ssl->server) {
- return tls13_process_new_session_ticket(ssl, msg);
- }
-
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
return false;
diff --git a/ssl/tls13_enc.cc b/ssl/tls13_enc.cc
index 486908a..db59f71 100644
--- a/ssl/tls13_enc.cc
+++ b/ssl/tls13_enc.cc
@@ -558,11 +558,29 @@
uint8_t context[EVP_MAX_MD_SIZE];
unsigned context_len;
ScopedEVP_MD_CTX ctx;
- if (!transcript.CopyToHashContext(ctx.get(), digest) ||
- !EVP_DigestUpdate(ctx.get(), truncated.data(),
- truncated.size()) ||
- !EVP_DigestFinal_ex(ctx.get(), context, &context_len)) {
- return false;
+ if (!is_dtls) {
+ if (!transcript.CopyToHashContext(ctx.get(), digest) ||
+ !EVP_DigestUpdate(ctx.get(), truncated.data(), truncated.size()) ||
+ !EVP_DigestFinal_ex(ctx.get(), context, &context_len)) {
+ return false;
+ }
+ } else {
+ // In DTLS 1.3, the transcript hash is computed over only the TLS 1.3
+ // handshake messages (i.e. only type and length in the header), not the
+ // full DTLSHandshake messages that are in |truncated|. This code pulls
+ // the header and body out of the truncated ClientHello and writes those
+ // to the hash context so the correct binder value is computed.
+ if (truncated.size() < DTLS1_HM_HEADER_LENGTH) {
+ return false;
+ }
+ auto header = truncated.subspan(0, 4);
+ auto body = truncated.subspan(12);
+ if (!transcript.CopyToHashContext(ctx.get(), digest) ||
+ !EVP_DigestUpdate(ctx.get(), header.data(), header.size()) ||
+ !EVP_DigestUpdate(ctx.get(), body.data(), body.size()) ||
+ !EVP_DigestFinal_ex(ctx.get(), context, &context_len)) {
+ return false;
+ }
}
if (!tls13_verify_data(out, out_len, digest, session->ssl_version, binder_key,
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index d3cb492..65232f0 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -1275,7 +1275,8 @@
// the case of a small server write buffer. Consumers which don't write data
// to the client will need to do a zero-byte write if they wish to flush the
// tickets.
- if (hs->ssl->quic_method != nullptr && sent_tickets) {
+ if ((hs->ssl->quic_method != nullptr || SSL_is_dtls(hs->ssl)) &&
+ sent_tickets) {
return ssl_hs_flush;
}
return ssl_hs_ok;