| // Copyright 2025 The BoringSSL Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package runner | |
| func addRenegotiationTests() { | |
| // Servers cannot renegotiate. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Renegotiate-Server-Forbidden", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| }, | |
| renegotiate: 1, | |
| shouldFail: true, | |
| expectedError: ":NO_RENEGOTIATION:", | |
| expectedLocalError: "remote error: no renegotiation", | |
| }) | |
| // The server shouldn't echo the renegotiation extension unless | |
| // requested by the client. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Renegotiate-Server-NoExt", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| NoRenegotiationInfo: true, | |
| RequireRenegotiationInfo: true, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedLocalError: "renegotiation extension missing", | |
| }) | |
| // The renegotiation SCSV should be sufficient for the server to echo | |
| // the extension. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "Renegotiate-Server-NoExt-SCSV", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| NoRenegotiationInfo: true, | |
| SendRenegotiationSCSV: true, | |
| RequireRenegotiationInfo: true, | |
| }, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| FailIfResumeOnRenego: true, | |
| }, | |
| }, | |
| renegotiate: 1, | |
| // Test renegotiation after both an initial and resumption | |
| // handshake. | |
| resumeSession: true, | |
| flags: []string{ | |
| "-renegotiate-freely", | |
| "-expect-total-renegotiations", "1", | |
| "-expect-secure-renegotiation", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-TLS12", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| FailIfResumeOnRenego: true, | |
| }, | |
| }, | |
| renegotiate: 1, | |
| // Test renegotiation after both an initial and resumption | |
| // handshake. | |
| resumeSession: true, | |
| flags: []string{ | |
| "-renegotiate-freely", | |
| "-expect-total-renegotiations", "1", | |
| "-expect-secure-renegotiation", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-EmptyExt", | |
| renegotiate: 1, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| EmptyRenegotiationInfo: true, | |
| }, | |
| }, | |
| flags: []string{"-renegotiate-freely"}, | |
| shouldFail: true, | |
| expectedError: ":RENEGOTIATION_MISMATCH:", | |
| expectedLocalError: "handshake failure", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-BadExt", | |
| renegotiate: 1, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| BadRenegotiationInfo: true, | |
| }, | |
| }, | |
| flags: []string{"-renegotiate-freely"}, | |
| shouldFail: true, | |
| expectedError: ":RENEGOTIATION_MISMATCH:", | |
| expectedLocalError: "handshake failure", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-BadExt2", | |
| renegotiate: 1, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| BadRenegotiationInfoEnd: true, | |
| }, | |
| }, | |
| flags: []string{"-renegotiate-freely"}, | |
| shouldFail: true, | |
| expectedError: ":RENEGOTIATION_MISMATCH:", | |
| expectedLocalError: "handshake failure", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-Downgrade", | |
| renegotiate: 1, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| NoRenegotiationInfoAfterInitial: true, | |
| }, | |
| }, | |
| flags: []string{"-renegotiate-freely"}, | |
| shouldFail: true, | |
| expectedError: ":RENEGOTIATION_MISMATCH:", | |
| expectedLocalError: "handshake failure", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-Upgrade", | |
| renegotiate: 1, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| NoRenegotiationInfoInInitial: true, | |
| }, | |
| }, | |
| flags: []string{"-renegotiate-freely"}, | |
| shouldFail: true, | |
| expectedError: ":RENEGOTIATION_MISMATCH:", | |
| expectedLocalError: "handshake failure", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-NoExt-Allowed", | |
| renegotiate: 1, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| NoRenegotiationInfo: true, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-renegotiate-freely", | |
| "-expect-total-renegotiations", "1", | |
| "-expect-no-secure-renegotiation", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-NoExt-Forbidden", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| NoRenegotiationInfo: true, | |
| }, | |
| }, | |
| flags: []string{"-no-legacy-server-connect"}, | |
| shouldFail: true, | |
| expectedError: ":UNSAFE_LEGACY_RENEGOTIATION_DISABLED:", | |
| }) | |
| // Missing extension is OK (and expected!) in TLS 1.3, even if the | |
| // extension is otherwise enforced. TLS 1.3 has no renegotiation. | |
| testCases = append(testCases, testCase{ | |
| name: "NoLegacyServerConnect-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| }, | |
| flags: []string{"-no-legacy-server-connect"}, | |
| }) | |
| // Test that the server may switch ciphers on renegotiation without | |
| // problems. | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-SwitchCiphers", | |
| renegotiate: 1, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, | |
| }, | |
| renegotiateCiphers: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| flags: []string{ | |
| "-renegotiate-freely", | |
| "-expect-total-renegotiations", "1", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-SwitchCiphers2", | |
| renegotiate: 1, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| }, | |
| renegotiateCiphers: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, | |
| flags: []string{ | |
| "-renegotiate-freely", | |
| "-expect-total-renegotiations", "1", | |
| }, | |
| }) | |
| // Test that the server may not switch versions on renegotiation. | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-SwitchVersion", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| // Pick a cipher which exists at both versions. | |
| CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, | |
| Bugs: ProtocolBugs{ | |
| NegotiateVersionOnRenego: VersionTLS11, | |
| // Avoid failing early at the record layer. | |
| SendRecordVersion: VersionTLS12, | |
| }, | |
| }, | |
| renegotiate: 1, | |
| flags: []string{ | |
| "-renegotiate-freely", | |
| "-expect-total-renegotiations", "1", | |
| }, | |
| shouldFail: true, | |
| expectedError: ":WRONG_SSL_VERSION:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-SameClientVersion", | |
| renegotiate: 1, | |
| config: Config{ | |
| MaxVersion: VersionTLS10, | |
| Bugs: ProtocolBugs{ | |
| RequireSameRenegoClientVersion: true, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-renegotiate-freely", | |
| "-expect-total-renegotiations", "1", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-FalseStart", | |
| renegotiate: 1, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| NextProtos: []string{"foo"}, | |
| }, | |
| flags: []string{ | |
| "-false-start", | |
| "-select-next-proto", "foo", | |
| "-renegotiate-freely", | |
| "-expect-total-renegotiations", "1", | |
| }, | |
| shimWritesFirst: true, | |
| }) | |
| // Client-side renegotiation controls. | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-Forbidden-1", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| }, | |
| renegotiate: 1, | |
| shouldFail: true, | |
| expectedError: ":NO_RENEGOTIATION:", | |
| expectedLocalError: "remote error: no renegotiation", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-Once-1", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| }, | |
| renegotiate: 1, | |
| flags: []string{ | |
| "-renegotiate-once", | |
| "-expect-total-renegotiations", "1", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-Freely-1", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| }, | |
| renegotiate: 1, | |
| flags: []string{ | |
| "-renegotiate-freely", | |
| "-expect-total-renegotiations", "1", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-Once-2", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| }, | |
| renegotiate: 2, | |
| flags: []string{"-renegotiate-once"}, | |
| shouldFail: true, | |
| expectedError: ":NO_RENEGOTIATION:", | |
| expectedLocalError: "remote error: no renegotiation", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-Freely-2", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| }, | |
| renegotiate: 2, | |
| flags: []string{ | |
| "-renegotiate-freely", | |
| "-expect-total-renegotiations", "2", | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-NoIgnore", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| SendHelloRequestBeforeEveryAppDataRecord: true, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":NO_RENEGOTIATION:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-Ignore", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| SendHelloRequestBeforeEveryAppDataRecord: true, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-renegotiate-ignore", | |
| "-expect-total-renegotiations", "0", | |
| }, | |
| }) | |
| // Renegotiation may be enabled and then disabled immediately after the | |
| // handshake. | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-ForbidAfterHandshake", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| }, | |
| renegotiate: 1, | |
| flags: []string{"-forbid-renegotiation-after-handshake"}, | |
| shouldFail: true, | |
| expectedError: ":NO_RENEGOTIATION:", | |
| expectedLocalError: "remote error: no renegotiation", | |
| }) | |
| // Renegotiation is not allowed when there is an unfinished write. | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-UnfinishedWrite", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| }, | |
| renegotiate: 1, | |
| readWithUnfinishedWrite: true, | |
| flags: []string{ | |
| "-async", | |
| "-renegotiate-freely", | |
| }, | |
| shouldFail: true, | |
| expectedError: ":NO_RENEGOTIATION:", | |
| // We do not successfully send the no_renegotiation alert in | |
| // this case. https://crbug.com/boringssl/130 | |
| }) | |
| // We reject stray HelloRequests during the handshake in TLS 1.2. | |
| testCases = append(testCases, testCase{ | |
| name: "StrayHelloRequest", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| SendHelloRequestBeforeEveryHandshakeMessage: true, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":UNEXPECTED_MESSAGE:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "StrayHelloRequest-Packed", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| PackHandshakeFlight: true, | |
| SendHelloRequestBeforeEveryHandshakeMessage: true, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":UNEXPECTED_MESSAGE:", | |
| }) | |
| // Test that HelloRequest is rejected if it comes in the same record as the | |
| // server Finished. | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-Packed", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| PackHandshakeFlight: true, | |
| PackHelloRequestWithFinished: true, | |
| }, | |
| }, | |
| renegotiate: 1, | |
| flags: []string{"-renegotiate-freely"}, | |
| shouldFail: true, | |
| expectedError: ":EXCESS_HANDSHAKE_DATA:", | |
| expectedLocalError: "remote error: unexpected message", | |
| }) | |
| // Renegotiation is forbidden in TLS 1.3. | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiate-Client-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendHelloRequestBeforeEveryAppDataRecord: true, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-renegotiate-freely", | |
| }, | |
| shouldFail: true, | |
| expectedError: ":UNEXPECTED_MESSAGE:", | |
| }) | |
| // Stray HelloRequests during the handshake are forbidden in TLS 1.3. | |
| testCases = append(testCases, testCase{ | |
| name: "StrayHelloRequest-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendHelloRequestBeforeEveryHandshakeMessage: true, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":UNEXPECTED_MESSAGE:", | |
| }) | |
| // The renegotiation_info extension is not sent in TLS 1.3, but TLS 1.3 | |
| // always reads as supporting it, regardless of whether it was | |
| // negotiated. | |
| testCases = append(testCases, testCase{ | |
| name: "AlwaysReportRenegotiationInfo-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| NoRenegotiationInfo: true, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-expect-secure-renegotiation", | |
| }, | |
| }) | |
| // Certificates may not change on renegotiation. | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiation-CertificateChange", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Credential: &rsaCertificate, | |
| Bugs: ProtocolBugs{ | |
| RenegotiationCertificate: &rsaChainCertificate, | |
| }, | |
| }, | |
| renegotiate: 1, | |
| flags: []string{"-renegotiate-freely"}, | |
| shouldFail: true, | |
| expectedError: ":SERVER_CERT_CHANGED:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "Renegotiation-CertificateChange-2", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Credential: &rsaCertificate, | |
| Bugs: ProtocolBugs{ | |
| RenegotiationCertificate: &rsa1024Certificate, | |
| }, | |
| }, | |
| renegotiate: 1, | |
| flags: []string{"-renegotiate-freely"}, | |
| shouldFail: true, | |
| expectedError: ":SERVER_CERT_CHANGED:", | |
| }) | |
| // We do not negotiate ALPN after the initial handshake. This is | |
| // error-prone and only risks bugs in consumers. | |
| testCases = append(testCases, testCase{ | |
| testType: clientTest, | |
| name: "Renegotiation-ForbidALPN", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| // Forcibly negotiate ALPN on both initial and | |
| // renegotiation handshakes. The test stack will | |
| // internally check the client does not offer | |
| // it. | |
| SendALPN: "foo", | |
| }, | |
| }, | |
| flags: []string{ | |
| "-advertise-alpn", "\x03foo\x03bar\x03baz", | |
| "-expect-alpn", "foo", | |
| "-renegotiate-freely", | |
| }, | |
| renegotiate: 1, | |
| shouldFail: true, | |
| expectedError: ":UNEXPECTED_EXTENSION:", | |
| }) | |
| // The server may send different stapled OCSP responses or SCT lists on | |
| // renegotiation, but BoringSSL ignores this and reports the old values. | |
| // Also test that non-fatal verify results are preserved. | |
| testCases = append(testCases, testCase{ | |
| testType: clientTest, | |
| name: "Renegotiation-ChangeAuthProperties", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Credential: rsaCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList), | |
| Bugs: ProtocolBugs{ | |
| SendOCSPResponseOnRenegotiation: testOCSPResponse2, | |
| SendSCTListOnRenegotiation: testSCTList2, | |
| }, | |
| }, | |
| renegotiate: 1, | |
| flags: []string{ | |
| "-renegotiate-freely", | |
| "-expect-total-renegotiations", "1", | |
| "-enable-ocsp-stapling", | |
| "-expect-ocsp-response", | |
| base64FlagValue(testOCSPResponse), | |
| "-enable-signed-cert-timestamps", | |
| "-expect-signed-cert-timestamps", | |
| base64FlagValue(testSCTList), | |
| "-verify-fail", | |
| "-expect-verify-result", | |
| }, | |
| }) | |
| // We do not support renegotiation in DTLS, even if enabled. Currently, | |
| // BoringSSL treats this as an unexpected record. | |
| testCases = append(testCases, testCase{ | |
| protocol: dtls, | |
| testType: clientTest, | |
| name: "Renegotiate-DTLS-Client-Forbidden", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| }, | |
| renegotiate: 1, | |
| flags: []string{"-renegotiate-freely"}, | |
| shouldFail: true, | |
| expectedError: ":UNEXPECTED_RECORD:", | |
| expectedLocalError: "remote error: unexpected message", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| protocol: dtls, | |
| testType: serverTest, | |
| name: "Renegotiate-DTLS-Server-Forbidden", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| }, | |
| renegotiate: 1, | |
| flags: []string{"-renegotiate-freely"}, | |
| shouldFail: true, | |
| expectedError: ":UNEXPECTED_RECORD:", | |
| expectedLocalError: "remote error: unexpected message", | |
| }) | |
| } |