| // 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", |
| }, |
| }) |
| |
| // 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", |
| }, |
| }) |
| } |