Based on Nekogram. Key additions: - Rebrand to FoxiGram (app name, APK name, applicationId com.foxigram.app) - Embedded Xray (VLESS+Reality) proxy client via JNI libxray.so - Bundled hidden one-tap proxies (LTE + WiFi), read-only in UI - Auto-restore proxy on restart, rebind to active network (LTE/WiFi) - Server credentials externalized to git-ignored XrayServers.java (+ template) - libxray Go source included; compiled .so, keystore, google-services.json ignored
546 lines
15 KiB
Go
546 lines
15 KiB
Go
// 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",
|
|
},
|
|
})
|
|
}
|