| // 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 | |
| import ( | |
| "fmt" | |
| "strconv" | |
| ) | |
| var testCurves = []struct { | |
| name string | |
| id CurveID | |
| }{ | |
| {"P-256", CurveP256}, | |
| {"P-384", CurveP384}, | |
| {"P-521", CurveP521}, | |
| {"X25519", CurveX25519}, | |
| {"X25519MLKEM768", CurveX25519MLKEM768}, | |
| {"MLKEM1024", CurveMLKEM1024}, | |
| } | |
| const bogusCurve = 0x1234 | |
| const curveEqualPreferenceWithNextFlag = 0x01 | |
| func isPqGroup(r CurveID) bool { | |
| return isMLKEMGroup(r) | |
| } | |
| func isMLKEMGroup(r CurveID) bool { | |
| return r == CurveX25519MLKEM768 || r == CurveMLKEM1024 | |
| } | |
| func isECDHGroup(r CurveID) bool { | |
| return r == CurveP256 || r == CurveP384 || r == CurveP521 | |
| } | |
| func isX25519Group(r CurveID) bool { | |
| return r == CurveX25519 || r == CurveX25519MLKEM768 | |
| } | |
| func addCurveTests() { | |
| // A set of cipher suites that ensures some curve-using mode is used. | |
| // Without this, servers may fall back to RSA key exchange. | |
| ecdheCiphers := []uint16{ | |
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, | |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| TLS_AES_256_GCM_SHA384, | |
| } | |
| // Not all curves are enabled by default, so these tests explicitly enable | |
| // the curve under test in the shim. | |
| for _, curve := range testCurves { | |
| for _, ver := range tlsVersions { | |
| if isPqGroup(curve.id) && ver.version < VersionTLS13 { | |
| continue | |
| } | |
| for _, testType := range []testType{clientTest, serverTest} { | |
| suffix := fmt.Sprintf("%s-%s-%s", testType, curve.name, ver.name) | |
| testCases = append(testCases, testCase{ | |
| testType: testType, | |
| name: "CurveTest-" + suffix, | |
| config: Config{ | |
| MaxVersion: ver.version, | |
| CipherSuites: ecdheCiphers, | |
| CurvePreferences: []CurveID{curve.id}, | |
| }, | |
| flags: append( | |
| []string{"-expect-curve-id", strconv.Itoa(int(curve.id))}, | |
| flagInts("-curves", []CurveID{curve.id})..., | |
| ), | |
| expectations: connectionExpectations{ | |
| curveID: curve.id, | |
| }, | |
| }) | |
| badKeyShareLocalError := "remote error: illegal parameter" | |
| if testType == clientTest && ver.version >= VersionTLS13 { | |
| // If the shim is a TLS 1.3 client and the runner sends a bad | |
| // key share, the runner never reads the client's cleartext | |
| // alert because the runner has already started encrypting by | |
| // the time the client sees it. | |
| badKeyShareLocalError = "local error: bad record MAC" | |
| } | |
| testCases = append(testCases, testCase{ | |
| testType: testType, | |
| name: "CurveTest-Invalid-TruncateKeyShare-" + suffix, | |
| config: Config{ | |
| MaxVersion: ver.version, | |
| CipherSuites: ecdheCiphers, | |
| CurvePreferences: []CurveID{curve.id}, | |
| Bugs: ProtocolBugs{ | |
| TruncateKeyShare: true, | |
| }, | |
| }, | |
| flags: flagInts("-curves", []CurveID{curve.id}), | |
| shouldFail: true, | |
| expectedError: ":BAD_ECPOINT:", | |
| expectedLocalError: badKeyShareLocalError, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: testType, | |
| name: "CurveTest-Invalid-PadKeyShare-" + suffix, | |
| config: Config{ | |
| MaxVersion: ver.version, | |
| CipherSuites: ecdheCiphers, | |
| CurvePreferences: []CurveID{curve.id}, | |
| Bugs: ProtocolBugs{ | |
| PadKeyShare: true, | |
| }, | |
| }, | |
| flags: flagInts("-curves", []CurveID{curve.id}), | |
| shouldFail: true, | |
| expectedError: ":BAD_ECPOINT:", | |
| expectedLocalError: badKeyShareLocalError, | |
| }) | |
| if isECDHGroup(curve.id) { | |
| testCases = append(testCases, testCase{ | |
| testType: testType, | |
| name: "CurveTest-Invalid-Compressed-" + suffix, | |
| config: Config{ | |
| MaxVersion: ver.version, | |
| CipherSuites: ecdheCiphers, | |
| CurvePreferences: []CurveID{curve.id}, | |
| Bugs: ProtocolBugs{ | |
| SendCompressedCoordinates: true, | |
| }, | |
| }, | |
| flags: flagInts("-curves", []CurveID{curve.id}), | |
| shouldFail: true, | |
| expectedError: ":BAD_ECPOINT:", | |
| expectedLocalError: badKeyShareLocalError, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: testType, | |
| name: "CurveTest-Invalid-NotOnCurve-" + suffix, | |
| config: Config{ | |
| MaxVersion: ver.version, | |
| CipherSuites: ecdheCiphers, | |
| CurvePreferences: []CurveID{curve.id}, | |
| Bugs: ProtocolBugs{ | |
| ECDHPointNotOnCurve: true, | |
| }, | |
| }, | |
| flags: flagInts("-curves", []CurveID{curve.id}), | |
| shouldFail: true, | |
| expectedError: ":BAD_ECPOINT:", | |
| expectedLocalError: badKeyShareLocalError, | |
| }) | |
| } | |
| if isX25519Group(curve.id) { | |
| // Implementations should mask off the high order bit in X25519. | |
| testCases = append(testCases, testCase{ | |
| testType: testType, | |
| name: "CurveTest-SetX25519HighBit-" + suffix, | |
| config: Config{ | |
| MaxVersion: ver.version, | |
| CipherSuites: ecdheCiphers, | |
| CurvePreferences: []CurveID{curve.id}, | |
| Bugs: ProtocolBugs{ | |
| SetX25519HighBit: true, | |
| }, | |
| }, | |
| flags: flagInts("-curves", []CurveID{curve.id}), | |
| expectations: connectionExpectations{ | |
| curveID: curve.id, | |
| }, | |
| }) | |
| // Implementations should reject low order points. | |
| testCases = append(testCases, testCase{ | |
| testType: testType, | |
| name: "CurveTest-Invalid-LowOrderX25519Point-" + suffix, | |
| config: Config{ | |
| MaxVersion: ver.version, | |
| CipherSuites: ecdheCiphers, | |
| CurvePreferences: []CurveID{curve.id}, | |
| Bugs: ProtocolBugs{ | |
| LowOrderX25519Point: true, | |
| }, | |
| }, | |
| flags: flagInts("-curves", []CurveID{curve.id}), | |
| shouldFail: true, | |
| expectedError: ":BAD_ECPOINT:", | |
| expectedLocalError: badKeyShareLocalError, | |
| }) | |
| } | |
| if isMLKEMGroup(curve.id) && testType == serverTest { | |
| testCases = append(testCases, testCase{ | |
| testType: testType, | |
| name: "CurveTest-Invalid-MLKEMEncapKeyNotReduced-" + suffix, | |
| config: Config{ | |
| MaxVersion: ver.version, | |
| CipherSuites: ecdheCiphers, | |
| CurvePreferences: []CurveID{curve.id}, | |
| Bugs: ProtocolBugs{ | |
| MLKEMEncapKeyNotReduced: true, | |
| }, | |
| }, | |
| flags: flagInts("-curves", []CurveID{curve.id}), | |
| shouldFail: true, | |
| expectedError: ":BAD_ECPOINT:", | |
| expectedLocalError: badKeyShareLocalError, | |
| }) | |
| } | |
| } | |
| } | |
| } | |
| // The server must be tolerant to bogus curves. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "UnknownCurve", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| CurvePreferences: []CurveID{bogusCurve, CurveP256}, | |
| }, | |
| }) | |
| // The server must be tolerant to bogus curves. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "UnknownCurve-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| CurvePreferences: []CurveID{bogusCurve, CurveP256}, | |
| }, | |
| }) | |
| // The server must not consider ECDHE ciphers when there are no | |
| // supported curves. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "NoSupportedCurves", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| Bugs: ProtocolBugs{ | |
| NoSupportedCurves: true, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":NO_SHARED_CIPHER:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "NoSupportedCurves-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| NoSupportedCurves: true, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":NO_SHARED_GROUP:", | |
| expectedLocalError: "remote error: missing extension", | |
| }) | |
| // The server must fall back to another cipher when there are no | |
| // supported curves. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "NoCommonCurves", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{ | |
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, | |
| TLS_RSA_WITH_AES_128_GCM_SHA256, | |
| }, | |
| CurvePreferences: []CurveID{21 /* P-224 */}, | |
| }, | |
| expectations: connectionExpectations{ | |
| cipher: TLS_RSA_WITH_AES_128_GCM_SHA256, | |
| }, | |
| }) | |
| // The client must reject bogus curves and disabled curves. | |
| testCases = append(testCases, testCase{ | |
| name: "BadECDHECurve", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendCurve: bogusCurve, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":WRONG_CURVE:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "BadECDHECurve-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendCurve: bogusCurve, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":WRONG_CURVE:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "UnsupportedCurve", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| CurvePreferences: []CurveID{CurveP256}, | |
| Bugs: ProtocolBugs{ | |
| IgnorePeerCurvePreferences: true, | |
| }, | |
| }, | |
| flags: []string{"-curves", strconv.Itoa(int(CurveP384))}, | |
| shouldFail: true, | |
| expectedError: ":WRONG_CURVE:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| // TODO(davidben): Add a TLS 1.3 version where | |
| // HelloRetryRequest requests an unsupported curve. | |
| name: "UnsupportedCurve-ServerHello-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| CurvePreferences: []CurveID{CurveP384}, | |
| Bugs: ProtocolBugs{ | |
| SendCurve: CurveP256, | |
| }, | |
| }, | |
| flags: []string{"-curves", strconv.Itoa(int(CurveP384))}, | |
| shouldFail: true, | |
| expectedError: ":WRONG_CURVE:", | |
| }) | |
| // The previous curve ID should be reported on TLS 1.2 resumption. | |
| testCases = append(testCases, testCase{ | |
| name: "CurveID-Resume-Client", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| CurvePreferences: []CurveID{CurveX25519}, | |
| }, | |
| flags: []string{"-expect-curve-id", strconv.Itoa(int(CurveX25519))}, | |
| resumeSession: true, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CurveID-Resume-Server", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| CurvePreferences: []CurveID{CurveX25519}, | |
| }, | |
| flags: []string{"-expect-curve-id", strconv.Itoa(int(CurveX25519))}, | |
| resumeSession: true, | |
| }) | |
| // TLS 1.3 allows resuming at a different curve. If this happens, the new | |
| // one should be reported. | |
| testCases = append(testCases, testCase{ | |
| name: "CurveID-Resume-Client-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| CurvePreferences: []CurveID{CurveX25519}, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: VersionTLS13, | |
| CurvePreferences: []CurveID{CurveP256}, | |
| }, | |
| flags: []string{ | |
| "-on-initial-expect-curve-id", strconv.Itoa(int(CurveX25519)), | |
| "-on-resume-expect-curve-id", strconv.Itoa(int(CurveP256)), | |
| }, | |
| resumeSession: true, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CurveID-Resume-Server-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| CurvePreferences: []CurveID{CurveX25519}, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: VersionTLS13, | |
| CurvePreferences: []CurveID{CurveP256}, | |
| }, | |
| flags: []string{ | |
| "-on-initial-expect-curve-id", strconv.Itoa(int(CurveX25519)), | |
| "-on-resume-expect-curve-id", strconv.Itoa(int(CurveP256)), | |
| }, | |
| resumeSession: true, | |
| }) | |
| // Server-sent point formats are legal in TLS 1.2, but not in TLS 1.3. | |
| testCases = append(testCases, testCase{ | |
| name: "PointFormat-ServerHello-TLS12", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| SendSupportedPointFormats: []byte{pointFormatUncompressed}, | |
| }, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "PointFormat-EncryptedExtensions-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendSupportedPointFormats: []byte{pointFormatUncompressed}, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":ERROR_PARSING_EXTENSION:", | |
| }) | |
| // Server-sent supported groups/curves are legal in TLS 1.3. They are | |
| // illegal in TLS 1.2, but some servers send them anyway, so we must | |
| // tolerate them. | |
| testCases = append(testCases, testCase{ | |
| name: "SupportedCurves-ServerHello-TLS12", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| SendServerSupportedCurves: true, | |
| }, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: "SupportedCurves-EncryptedExtensions-TLS13", | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| SendServerSupportedCurves: true, | |
| }, | |
| }, | |
| }) | |
| // Test that we tolerate unknown point formats, as long as | |
| // pointFormatUncompressed is present. Limit ciphers to ECDHE ciphers to | |
| // check they are still functional. | |
| testCases = append(testCases, testCase{ | |
| name: "PointFormat-Client-Tolerance", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| SendSupportedPointFormats: []byte{42, pointFormatUncompressed, 99, pointFormatCompressedPrime}, | |
| }, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "PointFormat-Server-Tolerance", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendSupportedPointFormats: []byte{42, pointFormatUncompressed, 99, pointFormatCompressedPrime}, | |
| }, | |
| }, | |
| }) | |
| // Test TLS 1.2 does not require the point format extension to be | |
| // present. | |
| testCases = append(testCases, testCase{ | |
| name: "PointFormat-Client-Missing", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendSupportedPointFormats: []byte{}, | |
| }, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "PointFormat-Server-Missing", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendSupportedPointFormats: []byte{}, | |
| }, | |
| }, | |
| }) | |
| // If the point format extension is present, uncompressed points must be | |
| // offered. BoringSSL requires this whether or not ECDHE is used. | |
| testCases = append(testCases, testCase{ | |
| name: "PointFormat-Client-MissingUncompressed", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| SendSupportedPointFormats: []byte{pointFormatCompressedPrime}, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":ERROR_PARSING_EXTENSION:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "PointFormat-Server-MissingUncompressed", | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| SendSupportedPointFormats: []byte{pointFormatCompressedPrime}, | |
| }, | |
| }, | |
| shouldFail: true, | |
| expectedError: ":ERROR_PARSING_EXTENSION:", | |
| }) | |
| // Post-quantum groups require TLS 1.3. | |
| for _, curve := range testCurves { | |
| if !isPqGroup(curve.id) { | |
| continue | |
| } | |
| // Post-quantum groups should not be offered by a TLS 1.2 client. | |
| testCases = append(testCases, testCase{ | |
| name: "TLS12ClientShouldNotOffer-" + curve.name, | |
| config: Config{ | |
| Bugs: ProtocolBugs{ | |
| FailIfPostQuantumOffered: true, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-max-version", strconv.Itoa(VersionTLS12), | |
| "-curves", strconv.Itoa(int(curve.id)), | |
| "-curves", strconv.Itoa(int(CurveX25519)), | |
| }, | |
| }) | |
| // Post-quantum groups should not be selected by a TLS 1.2 server. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "TLS12ServerShouldNotSelect-" + curve.name, | |
| flags: []string{ | |
| "-max-version", strconv.Itoa(VersionTLS12), | |
| "-curves", strconv.Itoa(int(curve.id)), | |
| "-curves", strconv.Itoa(int(CurveX25519)), | |
| }, | |
| expectations: connectionExpectations{ | |
| curveID: CurveX25519, | |
| }, | |
| }) | |
| // If a TLS 1.2 server selects a post-quantum group anyway, the client | |
| // should not accept it. | |
| testCases = append(testCases, testCase{ | |
| name: "ClientShouldNotAllowInTLS12-" + curve.name, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Bugs: ProtocolBugs{ | |
| SendCurve: curve.id, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-curves", strconv.Itoa(int(curve.id)), | |
| "-curves", strconv.Itoa(int(CurveX25519)), | |
| }, | |
| shouldFail: true, | |
| expectedError: ":WRONG_CURVE:", | |
| expectedLocalError: "remote error: illegal parameter", | |
| }) | |
| } | |
| // ML-KEM should be offered by default as a client. | |
| testCases = append(testCases, testCase{ | |
| name: "PostQuantumEnabledByDefaultInClients", | |
| config: Config{ | |
| MinVersion: VersionTLS13, | |
| CurvePreferences: []CurveID{CurveX25519MLKEM768}, | |
| Bugs: ProtocolBugs{}, | |
| }, | |
| }) | |
| for _, curve := range testCurves { | |
| if !isMLKEMGroup(curve.id) { | |
| continue | |
| } | |
| // If ML-KEM is offered, both X25519 and ML-KEM should have a key-share. | |
| testCases = append(testCases, testCase{ | |
| name: "NotJustMLKEMKeyShare-" + curve.name, | |
| config: Config{ | |
| MinVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| ExpectedKeyShares: []CurveID{curve.id, CurveX25519}, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-curves", strconv.Itoa(int(curve.id)), | |
| "-curves", strconv.Itoa(int(CurveX25519)), | |
| "-expect-curve-id", strconv.Itoa(int(curve.id)), | |
| }, | |
| }) | |
| // ... and the other way around | |
| testCases = append(testCases, testCase{ | |
| name: "MLKEMKeyShareIncludedSecond-" + curve.name, | |
| config: Config{ | |
| MinVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| ExpectedKeyShares: []CurveID{CurveX25519, curve.id}, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-curves", strconv.Itoa(int(CurveX25519)), | |
| "-curves", strconv.Itoa(int(curve.id)), | |
| "-expect-curve-id", strconv.Itoa(int(CurveX25519)), | |
| }, | |
| }) | |
| // ... and even if there's another curve in the middle because it's the | |
| // first classical and first post-quantum "curves" that get key shares | |
| // included. | |
| testCases = append(testCases, testCase{ | |
| name: "MLKEMKeyShareIncludedThird-" + curve.name, | |
| config: Config{ | |
| MinVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| ExpectedKeyShares: []CurveID{CurveX25519, curve.id}, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-curves", strconv.Itoa(int(CurveX25519)), | |
| "-curves", strconv.Itoa(int(CurveP256)), | |
| "-curves", strconv.Itoa(int(curve.id)), | |
| "-expect-curve-id", strconv.Itoa(int(CurveX25519)), | |
| }, | |
| }) | |
| // If ML-KEM is the only configured curve, the key share is sent. | |
| testCases = append(testCases, testCase{ | |
| name: "JustConfiguringMLKEMWorks-" + curve.name, | |
| config: Config{ | |
| MinVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| ExpectedKeyShares: []CurveID{curve.id}, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-curves", strconv.Itoa(int(curve.id)), | |
| "-expect-curve-id", strconv.Itoa(int(curve.id)), | |
| }, | |
| }) | |
| // If multiple PQ groups are configured, only the preferred one's | |
| // key share should be sent. | |
| otherMLKEM := CurveMLKEM1024 | |
| if curve.id == CurveMLKEM1024 { | |
| otherMLKEM = CurveX25519MLKEM768 | |
| } | |
| testCases = append(testCases, testCase{ | |
| name: "MultiplePQGroups-" + curve.name, | |
| config: Config{ | |
| MinVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| ExpectedKeyShares: []CurveID{curve.id}, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-curves", strconv.Itoa(int(curve.id)), | |
| "-curves", strconv.Itoa(int(otherMLKEM)), | |
| "-expect-curve-id", strconv.Itoa(int(curve.id)), | |
| }, | |
| }) | |
| } | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "PostQuantumEnabledByDefaultForAServer", | |
| config: Config{ | |
| MinVersion: VersionTLS13, | |
| CurvePreferences: []CurveID{CurveX25519MLKEM768}, | |
| DefaultCurves: []CurveID{CurveX25519MLKEM768}, | |
| }, | |
| flags: []string{ | |
| "-server-preference", | |
| "-expect-curve-id", strconv.Itoa(int(CurveX25519MLKEM768)), | |
| }, | |
| }) | |
| // If two ML-KEMs are configured, only the preferred one's | |
| // key share should be sent. | |
| testCases = append(testCases, testCase{ | |
| name: "TwoMLKEMs", | |
| config: Config{ | |
| MinVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| ExpectedKeyShares: []CurveID{CurveMLKEM1024}, | |
| }, | |
| }, | |
| flags: []string{ | |
| "-curves", strconv.Itoa(int(CurveMLKEM1024)), | |
| "-curves", strconv.Itoa(int(CurveX25519MLKEM768)), | |
| "-expect-curve-id", strconv.Itoa(int(CurveMLKEM1024)), | |
| }, | |
| }) | |
| // In TLS 1.2, the curve list is also used to signal ECDSA curves. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CheckECDSACurve-TLS12", | |
| config: Config{ | |
| MinVersion: VersionTLS12, | |
| MaxVersion: VersionTLS12, | |
| CurvePreferences: []CurveID{CurveP384}, | |
| }, | |
| shimCertificate: &ecdsaP256Certificate, | |
| shouldFail: true, | |
| expectedError: ":WRONG_CURVE:", | |
| }) | |
| // If the ECDSA certificate is ineligible due to a curve mismatch, the | |
| // server may still consider a PSK cipher suite. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CheckECDSACurve-PSK-TLS12", | |
| config: Config{ | |
| MinVersion: VersionTLS12, | |
| MaxVersion: VersionTLS12, | |
| CipherSuites: []uint16{ | |
| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, | |
| TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, | |
| }, | |
| CurvePreferences: []CurveID{CurveP384}, | |
| PreSharedKey: []byte("12345"), | |
| PreSharedKeyIdentity: "luggage combo", | |
| }, | |
| shimCertificate: &ecdsaP256Certificate, | |
| expectations: connectionExpectations{ | |
| cipher: TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, | |
| }, | |
| flags: []string{ | |
| "-psk", "12345", | |
| "-psk-identity", "luggage combo", | |
| }, | |
| }) | |
| // In TLS 1.3, the curve list only controls ECDH. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CheckECDSACurve-NotApplicable-TLS13", | |
| config: Config{ | |
| MinVersion: VersionTLS13, | |
| MaxVersion: VersionTLS13, | |
| CurvePreferences: []CurveID{CurveP384}, | |
| }, | |
| shimCertificate: &ecdsaP256Certificate, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| name: "CurveTest-Server-EqualPreference-TLS13", | |
| config: Config{ | |
| MinVersion: VersionTLS13, | |
| MaxVersion: VersionTLS13, | |
| CurvePreferences: []CurveID{CurveX25519, CurveMLKEM1024, CurveX25519MLKEM768}, | |
| }, | |
| flags: append(append( | |
| []string{ | |
| // Set the -cipher flag to force SSL_OP_CIPHER_SERVER_PREFERENCE. | |
| "-cipher", "ALL:3DES", | |
| "-expect-curve-id", strconv.Itoa(int(CurveMLKEM1024))}, | |
| flagInts("-curves", []CurveID{CurveX25519MLKEM768, CurveMLKEM1024, CurveX25519})...), | |
| flagInts("-curves-flags", []int{curveEqualPreferenceWithNextFlag, 0, 0})..., | |
| ), | |
| expectations: connectionExpectations{ | |
| curveID: CurveMLKEM1024, | |
| }, | |
| }) | |
| } |