| // 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" | |
| ) | |
| func canBeShimCertificate(c *Credential) bool { | |
| // Some options can only be set with the credentials API. | |
| return c.Type == CredentialTypeX509 && !c.MustMatchIssuer && c.Properties.Empty() | |
| } | |
| func addCertificateSelectionTests() { | |
| // Combinatorially test each selection criteria at different versions, | |
| // protocols, and with the matching certificate before and after the | |
| // mismatching one. | |
| type certSelectTest struct { | |
| name string | |
| testType testType | |
| minVersion uint16 | |
| maxVersion uint16 | |
| config Config | |
| match *Credential | |
| mismatch *Credential | |
| flags []string | |
| expectedError string | |
| } | |
| certSelectTests := []certSelectTest{ | |
| // TLS 1.0 through TLS 1.2 servers should incorporate TLS cipher suites | |
| // into certificate selection. | |
| { | |
| name: "Server-CipherSuite-ECDHE_ECDSA", | |
| testType: serverTest, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| CipherSuites: []uint16{ | |
| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, | |
| }, | |
| }, | |
| match: &ecdsaP256Certificate, | |
| mismatch: &rsaCertificate, | |
| expectedError: ":NO_SHARED_CIPHER:", | |
| }, | |
| { | |
| name: "Server-CipherSuite-ECDHE_RSA", | |
| testType: serverTest, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| CipherSuites: []uint16{ | |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| }, | |
| }, | |
| match: &rsaCertificate, | |
| mismatch: &ecdsaP256Certificate, | |
| expectedError: ":NO_SHARED_CIPHER:", | |
| }, | |
| { | |
| name: "Server-CipherSuite-RSA", | |
| testType: serverTest, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| CipherSuites: []uint16{ | |
| TLS_RSA_WITH_AES_128_CBC_SHA, | |
| }, | |
| }, | |
| match: &rsaCertificate, | |
| mismatch: &ecdsaP256Certificate, | |
| expectedError: ":NO_SHARED_CIPHER:", | |
| }, | |
| // Ed25519 counts as ECDSA for purposes of cipher suite matching. | |
| { | |
| name: "Server-CipherSuite-ECDHE_ECDSA-Ed25519", | |
| testType: serverTest, | |
| minVersion: VersionTLS12, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| CipherSuites: []uint16{ | |
| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, | |
| }, | |
| }, | |
| match: &ed25519Certificate, | |
| mismatch: &rsaCertificate, | |
| expectedError: ":NO_SHARED_CIPHER:", | |
| }, | |
| { | |
| name: "Server-CipherSuite-ECDHE_RSA-Ed25519", | |
| testType: serverTest, | |
| minVersion: VersionTLS12, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| CipherSuites: []uint16{ | |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| }, | |
| }, | |
| match: &rsaCertificate, | |
| mismatch: &ed25519Certificate, | |
| expectedError: ":NO_SHARED_CIPHER:", | |
| }, | |
| // If there is no ECDHE curve match, ECDHE cipher suites are | |
| // disqualified in TLS 1.2 and below. This, in turn, impacts the | |
| // available cipher suites for each credential. | |
| { | |
| name: "Server-CipherSuite-NoECDHE", | |
| testType: serverTest, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| CurvePreferences: []CurveID{CurveP256}, | |
| }, | |
| flags: []string{"-curves", strconv.Itoa(int(CurveX25519))}, | |
| match: &rsaCertificate, | |
| mismatch: &ecdsaP256Certificate, | |
| expectedError: ":NO_SHARED_CIPHER:", | |
| }, | |
| // If the client offered a cipher that would allow a certificate, but it | |
| // wasn't one of the ones we configured, the certificate should be | |
| // skipped in favor of another one. | |
| { | |
| name: "Server-CipherSuite-Prefs", | |
| testType: serverTest, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| CipherSuites: []uint16{ | |
| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, | |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| }, | |
| }, | |
| flags: []string{"-cipher", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"}, | |
| match: &rsaCertificate, | |
| mismatch: &ecdsaP256Certificate, | |
| expectedError: ":NO_SHARED_CIPHER:", | |
| }, | |
| // TLS 1.0 through 1.2 servers should incorporate the curve list into | |
| // ECDSA certificate selection. | |
| { | |
| name: "Server-Curve", | |
| testType: serverTest, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| CurvePreferences: []CurveID{CurveP256}, | |
| }, | |
| match: &ecdsaP256Certificate, | |
| mismatch: &ecdsaP384Certificate, | |
| expectedError: ":WRONG_CURVE:", | |
| }, | |
| // TLS 1.3 servers ignore the curve list. ECDSA certificate selection is | |
| // solely determined by the signature algorithm list. | |
| { | |
| name: "Server-IgnoreCurve", | |
| testType: serverTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| CurvePreferences: []CurveID{CurveP256}, | |
| }, | |
| match: &ecdsaP384Certificate, | |
| }, | |
| // TLS 1.2 servers also ignore the curve list for Ed25519. The signature | |
| // algorithm list is sufficient for Ed25519. | |
| { | |
| name: "Server-IgnoreCurveEd25519", | |
| testType: serverTest, | |
| minVersion: VersionTLS12, | |
| config: Config{ | |
| CurvePreferences: []CurveID{CurveP256}, | |
| }, | |
| match: &ed25519Certificate, | |
| }, | |
| // Without signature algorithm negotiation, Ed25519 is not usable in TLS | |
| // 1.1 and below. | |
| { | |
| name: "Server-NoEd25519", | |
| testType: serverTest, | |
| maxVersion: VersionTLS11, | |
| match: &rsaCertificate, | |
| mismatch: &ed25519Certificate, | |
| }, | |
| // TLS 1.2 and up should incorporate the signature algorithm list into | |
| // certificate selection. | |
| { | |
| name: "Server-SignatureAlgorithm", | |
| testType: serverTest, | |
| minVersion: VersionTLS12, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| CipherSuites: []uint16{ | |
| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, | |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| }, | |
| }, | |
| match: &ecdsaP256Certificate, | |
| mismatch: &rsaCertificate, | |
| expectedError: ":NO_SHARED_CIPHER:", | |
| }, | |
| { | |
| name: "Server-SignatureAlgorithm", | |
| testType: serverTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| }, | |
| match: &ecdsaP256Certificate, | |
| mismatch: &rsaCertificate, | |
| expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", | |
| }, | |
| // TLS 1.2's use of the signature algorithm list only disables the | |
| // signing-based algorithms. If an RSA key exchange cipher suite is | |
| // eligible, that is fine. (This is not a realistic configuration, | |
| // however. No one would configure RSA before ECDSA.) | |
| { | |
| name: "Server-SignatureAlgorithmImpactsECDHEOnly", | |
| testType: serverTest, | |
| minVersion: VersionTLS12, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| CipherSuites: []uint16{ | |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| TLS_RSA_WITH_AES_128_CBC_SHA, | |
| }, | |
| }, | |
| match: &rsaCertificate, | |
| }, | |
| // TLS 1.3's use of the signature algorithm looks at the ECDSA curve | |
| // embedded in the signature algorithm. | |
| { | |
| name: "Server-SignatureAlgorithmECDSACurve", | |
| testType: serverTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| }, | |
| match: &ecdsaP256Certificate, | |
| mismatch: &ecdsaP384Certificate, | |
| expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", | |
| }, | |
| // TLS 1.2's use does not. | |
| { | |
| name: "Server-SignatureAlgorithmECDSACurve", | |
| testType: serverTest, | |
| minVersion: VersionTLS12, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| }, | |
| match: &ecdsaP384Certificate, | |
| }, | |
| // TLS 1.0 and 1.1 do not look at the signature algorithm. | |
| { | |
| name: "Server-IgnoreSignatureAlgorithm", | |
| testType: serverTest, | |
| maxVersion: VersionTLS11, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| }, | |
| match: &rsaCertificate, | |
| }, | |
| // Signature algorithm matches take preferences on the keys into | |
| // consideration. | |
| { | |
| name: "Server-SignatureAlgorithmKeyPrefs", | |
| testType: serverTest, | |
| minVersion: VersionTLS12, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureRSAPSSWithSHA256}, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| }, | |
| match: rsaChainCertificate.WithSignatureAlgorithms(signatureRSAPSSWithSHA256), | |
| mismatch: rsaCertificate.WithSignatureAlgorithms(signatureRSAPSSWithSHA384), | |
| expectedError: ":NO_SHARED_CIPHER:", | |
| }, | |
| { | |
| name: "Server-SignatureAlgorithmKeyPrefs", | |
| testType: serverTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureRSAPSSWithSHA256}, | |
| }, | |
| match: rsaChainCertificate.WithSignatureAlgorithms(signatureRSAPSSWithSHA256), | |
| mismatch: rsaCertificate.WithSignatureAlgorithms(signatureRSAPSSWithSHA384), | |
| expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", | |
| }, | |
| // The default server_certificate_type is X.509, which should match X.509 | |
| // certificates and delegated credentials, but not raw public key | |
| // credentials. | |
| { | |
| name: "Server-CertificateType-Default-MatchesX509OverRawPublicKey", | |
| testType: serverTest, | |
| match: &ecdsaP256Certificate, | |
| mismatch: &rpkEcdsaP256, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| { | |
| name: "Server-CertificateType-Default-MatchesDelegatedCredentialOverRawPublicKey", | |
| testType: serverTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| DelegatedCredentialAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| }, | |
| match: p256DC, | |
| mismatch: &rpkEcdsaP256, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| // If server_certificate_type is explicitly negotiated to permit only raw | |
| // public keys, X.509 certificates and delegated credentials should not | |
| // match. | |
| { | |
| name: "Server-CertificateType-MatchesRawPublicKeyOverX509", | |
| testType: serverTest, | |
| config: Config{ | |
| Bugs: ProtocolBugs{ | |
| SendServerCertificateTypes: []CertificateType{certTypeRawPublicKey}, | |
| }, | |
| }, | |
| match: &rpkEcdsaP256, | |
| mismatch: &ecdsaP256Certificate, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| { | |
| name: "Server-CertificateType-MatchesRawPublicKeyOverDelegatedCredential", | |
| testType: serverTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| DelegatedCredentialAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendServerCertificateTypes: []CertificateType{certTypeRawPublicKey}, | |
| }, | |
| }, | |
| match: &rpkEcdsaP256, | |
| mismatch: p256DC, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| // If server_certificate_type contains only an unknown value, no credentials | |
| // should match. | |
| { | |
| name: "Server-CertificateType-Unknown-RawPublicKey", | |
| testType: serverTest, | |
| config: Config{ | |
| Bugs: ProtocolBugs{ | |
| SendServerCertificateTypes: []CertificateType{certTypeBogus}, | |
| }, | |
| }, | |
| mismatch: &rpkEcdsaP256, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| { | |
| name: "Server-CertificateType-Unknown-X509", | |
| testType: serverTest, | |
| config: Config{ | |
| Bugs: ProtocolBugs{ | |
| SendServerCertificateTypes: []CertificateType{certTypeBogus}, | |
| }, | |
| }, | |
| mismatch: &ecdsaP256Certificate, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| { | |
| name: "Server-CertificateType-Unknown-DelegatedCredential", | |
| testType: serverTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| Bugs: ProtocolBugs{ | |
| SendServerCertificateTypes: []CertificateType{certTypeBogus}, | |
| }, | |
| }, | |
| mismatch: p256DC, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| // Raw Public Key credentials are subject to selection based on | |
| // algorithm-based criteria for each TLS version. | |
| { | |
| name: "Server-RawPublicKey-SignatureAlgorithm", | |
| testType: serverTest, | |
| minVersion: VersionTLS12, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| CipherSuites: []uint16{ | |
| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, | |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| }, | |
| Bugs: ProtocolBugs{ | |
| SendServerCertificateTypes: []CertificateType{certTypeRawPublicKey}, | |
| }, | |
| }, | |
| match: &rpkEcdsaP256, | |
| mismatch: &rpkRsa, | |
| expectedError: ":NO_SHARED_CIPHER:", | |
| }, | |
| { | |
| name: "Server-RawPublicKey-SignatureAlgorithm", | |
| testType: serverTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendServerCertificateTypes: []CertificateType{certTypeRawPublicKey}, | |
| }, | |
| }, | |
| match: &rpkEcdsaP256, | |
| mismatch: &rpkRsa, | |
| expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", | |
| }, | |
| { | |
| name: "Server-RawPublicKey-SignatureAlgorithmECDSACurve", | |
| testType: serverTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendServerCertificateTypes: []CertificateType{certTypeRawPublicKey}, | |
| }, | |
| }, | |
| match: &rpkEcdsaP256, | |
| mismatch: &rpkEcdsaP384, | |
| expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", | |
| }, | |
| { | |
| name: "Server-RawPublicKey-SignatureAlgorithmKeyPrefs", | |
| testType: serverTest, | |
| minVersion: VersionTLS12, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureRSAPSSWithSHA256}, | |
| CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendServerCertificateTypes: []CertificateType{certTypeRawPublicKey}, | |
| }, | |
| }, | |
| match: rpkRsa.WithSignatureAlgorithms(signatureRSAPSSWithSHA256), | |
| mismatch: rpkRsa.WithSignatureAlgorithms(signatureRSAPSSWithSHA384), | |
| expectedError: ":NO_SHARED_CIPHER:", | |
| }, | |
| { | |
| name: "Server-RawPublicKey-SignatureAlgorithmKeyPrefs", | |
| testType: serverTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureRSAPSSWithSHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendServerCertificateTypes: []CertificateType{certTypeRawPublicKey}, | |
| }, | |
| }, | |
| match: rpkRsa.WithSignatureAlgorithms(signatureRSAPSSWithSHA256), | |
| mismatch: rpkRsa.WithSignatureAlgorithms(signatureRSAPSSWithSHA384), | |
| expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", | |
| }, | |
| // TLS 1.2 clients and below check the certificate against the old | |
| // client certificate types field. | |
| { | |
| name: "Client-ClientCertificateTypes-RSA", | |
| testType: clientTest, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| ClientCertificateTypes: []uint8{CertTypeRSASign}, | |
| }, | |
| match: &rsaCertificate, | |
| mismatch: &ecdsaP256Certificate, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| { | |
| name: "Client-ClientCertificateTypes-ECDSA", | |
| testType: clientTest, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| ClientCertificateTypes: []uint8{CertTypeECDSASign}, | |
| }, | |
| match: &ecdsaP256Certificate, | |
| mismatch: &rsaCertificate, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| // Ed25519 is considered ECDSA for purposes of client certificate types. | |
| { | |
| name: "Client-ClientCertificateTypes-RSA-Ed25519", | |
| testType: clientTest, | |
| minVersion: VersionTLS12, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| ClientCertificateTypes: []uint8{CertTypeRSASign}, | |
| }, | |
| match: &rsaCertificate, | |
| mismatch: &ed25519Certificate, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| { | |
| name: "Client-ClientCertificateTypes-ECDSA-Ed25519", | |
| testType: clientTest, | |
| minVersion: VersionTLS12, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| ClientCertificateTypes: []uint8{CertTypeECDSASign}, | |
| }, | |
| match: &ed25519Certificate, | |
| mismatch: &rsaCertificate, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| // TLS 1.2 and up should incorporate the signature algorithm list into | |
| // certificate selection. (There is no signature algorithm list to look | |
| // at in TLS 1.0 and 1.1.) | |
| { | |
| name: "Client-SignatureAlgorithm", | |
| testType: clientTest, | |
| minVersion: VersionTLS12, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| }, | |
| match: &ecdsaP256Certificate, | |
| mismatch: &rsaCertificate, | |
| expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", | |
| }, | |
| // TLS 1.3's use of the signature algorithm looks at the ECDSA curve | |
| // embedded in the signature algorithm. | |
| { | |
| name: "Client-SignatureAlgorithmECDSACurve", | |
| testType: clientTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| }, | |
| match: &ecdsaP256Certificate, | |
| mismatch: &ecdsaP384Certificate, | |
| expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", | |
| }, | |
| // TLS 1.2's use does not. It is not possible to determine what ECDSA | |
| // curves are allowed by the server. | |
| { | |
| name: "Client-SignatureAlgorithmECDSACurve", | |
| testType: clientTest, | |
| minVersion: VersionTLS12, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| }, | |
| match: &ecdsaP384Certificate, | |
| }, | |
| // Signature algorithm matches take preferences on the keys into | |
| // consideration. | |
| { | |
| name: "Client-SignatureAlgorithmKeyPrefs", | |
| testType: clientTest, | |
| minVersion: VersionTLS12, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureRSAPSSWithSHA256}, | |
| }, | |
| match: rsaChainCertificate.WithSignatureAlgorithms(signatureRSAPSSWithSHA256), | |
| mismatch: rsaCertificate.WithSignatureAlgorithms(signatureRSAPSSWithSHA384), | |
| expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", | |
| }, | |
| // The default client_certificate_type is X.509, which should match X.509 | |
| // certificates but not raw public key credentials. | |
| { | |
| name: "Client-CertificateType-Default-MatchesX509OverRawPublicKey", | |
| testType: clientTest, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| }, | |
| flags: flagInts("-available-client-cert-types", []CertificateType{certTypeX509, certTypeRawPublicKey}), | |
| match: &ecdsaP256Certificate, | |
| mismatch: &rpkEcdsaP256, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| // If client_certificate_type is explicitly negotiated to permit only raw | |
| // public keys, X.509 certificates should not match. | |
| { | |
| name: "Client-CertificateType-MatchesRawPublicKeyOverX509", | |
| testType: clientTest, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| Bugs: ProtocolBugs{ | |
| SendClientCertificateTypes: []CertificateType{certTypeRawPublicKey}, | |
| }, | |
| }, | |
| flags: flagInts("-available-client-cert-types", []CertificateType{certTypeX509, certTypeRawPublicKey}), | |
| match: &rpkEcdsaP256, | |
| mismatch: &ecdsaP256Certificate, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| // Raw Public Key credentials are subject to selection based on | |
| // algorithm-based criteria for each TLS version. | |
| { | |
| name: "Client-RawPublicKey-ClientCertificateType", | |
| testType: clientTest, | |
| minVersion: VersionTLS12, | |
| maxVersion: VersionTLS12, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| ClientCertificateTypes: []uint8{CertTypeECDSASign}, | |
| Bugs: ProtocolBugs{ | |
| SendClientCertificateTypes: []CertificateType{certTypeRawPublicKey}, | |
| }, | |
| }, | |
| match: &rpkEcdsaP256, | |
| mismatch: &rpkRsa, | |
| expectedError: ":UNKNOWN_CERTIFICATE_TYPE:", | |
| }, | |
| { | |
| name: "Client-RawPublicKey-SignatureAlgorithm", | |
| testType: clientTest, | |
| minVersion: VersionTLS12, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendClientCertificateTypes: []CertificateType{certTypeRawPublicKey}, | |
| }, | |
| }, | |
| match: &rpkEcdsaP256, | |
| mismatch: &rpkRsa, | |
| expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", | |
| }, | |
| { | |
| name: "Client-RawPublicKey-SignatureAlgorithmECDSACurve", | |
| testType: clientTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureECDSAWithP256AndSHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendClientCertificateTypes: []CertificateType{certTypeRawPublicKey}, | |
| }, | |
| }, | |
| match: &rpkEcdsaP256, | |
| mismatch: &rpkEcdsaP384, | |
| expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", | |
| }, | |
| { | |
| name: "Client-RawPublicKey-SignatureAlgorithmKeyPrefs", | |
| testType: clientTest, | |
| minVersion: VersionTLS12, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureRSAPSSWithSHA256}, | |
| Bugs: ProtocolBugs{ | |
| SendClientCertificateTypes: []CertificateType{certTypeRawPublicKey}, | |
| }, | |
| }, | |
| match: rpkRsa.WithSignatureAlgorithms(signatureRSAPSSWithSHA256), | |
| mismatch: rpkRsa.WithSignatureAlgorithms(signatureRSAPSSWithSHA384), | |
| expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", | |
| }, | |
| // By default, certificate selection does not take issuers | |
| // into account. | |
| { | |
| name: "Client-DontCheckIssuer", | |
| testType: clientTest, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| ClientCAs: makeCertPoolFromRoots(&rsaChainCertificate, &ecdsaP384Certificate), | |
| }, | |
| match: &ecdsaP256Certificate, | |
| }, | |
| { | |
| name: "Server-DontCheckIssuer", | |
| testType: serverTest, | |
| config: Config{ | |
| RootCAs: makeCertPoolFromRoots(&rsaChainCertificate, &ecdsaP384Certificate), | |
| SendRootCAs: true, | |
| }, | |
| match: &ecdsaP256Certificate, | |
| }, | |
| // If requested, certificate selection will match against the | |
| // requested issuers. | |
| { | |
| name: "Client-CheckIssuer", | |
| testType: clientTest, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| ClientCAs: makeCertPoolFromRoots(&rsaChainCertificate, &ecdsaP384Certificate), | |
| }, | |
| match: rsaChainCertificate.WithMustMatchIssuer(true), | |
| mismatch: ecdsaP256Certificate.WithMustMatchIssuer(true), | |
| expectedError: ":NO_MATCHING_ISSUER:", | |
| }, | |
| { | |
| name: "Server-CheckIssuer", | |
| testType: serverTest, | |
| config: Config{ | |
| RootCAs: makeCertPoolFromRoots(&rsaChainCertificate, &ecdsaP384Certificate), | |
| SendRootCAs: true, | |
| }, | |
| match: rsaChainCertificate.WithMustMatchIssuer(true), | |
| mismatch: ecdsaP256Certificate.WithMustMatchIssuer(true), | |
| expectedError: ":NO_MATCHING_ISSUER:", | |
| }, | |
| // Trust anchor IDs can also be used to match issuers. | |
| // TODO(crbug.com/398275713): Implement this for client certificates. | |
| { | |
| name: "Server-CheckIssuer-TrustAnchorIDs", | |
| testType: serverTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| RequestTrustAnchors: [][]byte{{1, 1, 1}}, | |
| }, | |
| match: rsaChainCertificate.WithTrustAnchorID([]byte{1, 1, 1}), | |
| mismatch: ecdsaP256Certificate.WithTrustAnchorID([]byte{2, 2, 2}), | |
| expectedError: ":NO_MATCHING_ISSUER:", | |
| }, | |
| // When an issuer-gated credential fails, a normal credential may be | |
| // selected instead. | |
| { | |
| name: "Client-CheckIssuerFallback", | |
| testType: clientTest, | |
| config: Config{ | |
| ClientAuth: RequestClientCert, | |
| ClientCAs: makeCertPoolFromRoots(&ecdsaP384Certificate), | |
| }, | |
| match: &rsaChainCertificate, | |
| mismatch: ecdsaP256Certificate.WithMustMatchIssuer(true), | |
| expectedError: ":NO_MATCHING_ISSUER:", | |
| }, | |
| { | |
| name: "Server-CheckIssuerFallback", | |
| testType: serverTest, | |
| config: Config{ | |
| RootCAs: makeCertPoolFromRoots(&ecdsaP384Certificate), | |
| SendRootCAs: true, | |
| }, | |
| match: &rsaChainCertificate, | |
| mismatch: ecdsaP256Certificate.WithMustMatchIssuer(true), | |
| expectedError: ":NO_MATCHING_ISSUER:", | |
| }, | |
| { | |
| name: "Server-CheckIssuerFallback-TrustAnchorIDs", | |
| testType: serverTest, | |
| minVersion: VersionTLS13, | |
| config: Config{ | |
| RequestTrustAnchors: [][]byte{{1, 1, 1}}, | |
| }, | |
| match: &rsaChainCertificate, | |
| mismatch: ecdsaP256Certificate.WithTrustAnchorID([]byte{2, 2, 2}), | |
| expectedError: ":NO_MATCHING_ISSUER:", | |
| }, | |
| } | |
| for _, protocol := range []protocol{tls, dtls} { | |
| for _, vers := range allVersions(protocol) { | |
| suffix := fmt.Sprintf("%s-%s", protocol, vers) | |
| // Test that the credential list is interpreted in preference order, | |
| // with the default credential, if any, at the end. | |
| testCases = append(testCases, testCase{ | |
| name: fmt.Sprintf("CertificateSelection-Client-PreferenceOrder-%s", suffix), | |
| testType: clientTest, | |
| protocol: protocol, | |
| config: Config{ | |
| MinVersion: vers.version, | |
| MaxVersion: vers.version, | |
| ClientAuth: RequestClientCert, | |
| }, | |
| shimCredentials: []*Credential{&ecdsaP256Certificate, &ecdsaP384Certificate}, | |
| shimCertificate: &rsaCertificate, | |
| flags: []string{"-expect-selected-credential", "0"}, | |
| expectations: connectionExpectations{peerCertificate: &ecdsaP256Certificate}, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: fmt.Sprintf("CertificateSelection-Server-PreferenceOrder-%s", suffix), | |
| testType: serverTest, | |
| protocol: protocol, | |
| config: Config{ | |
| MinVersion: vers.version, | |
| MaxVersion: vers.version, | |
| }, | |
| shimCredentials: []*Credential{&ecdsaP256Certificate, &ecdsaP384Certificate}, | |
| shimCertificate: &rsaCertificate, | |
| flags: []string{"-expect-selected-credential", "0"}, | |
| expectations: connectionExpectations{peerCertificate: &ecdsaP256Certificate}, | |
| }) | |
| // Test that the selected credential contributes the certificate chain, OCSP response, | |
| // and SCT list. | |
| testCases = append(testCases, testCase{ | |
| name: fmt.Sprintf("CertificateSelection-Server-OCSP-SCT-%s", suffix), | |
| testType: serverTest, | |
| protocol: protocol, | |
| config: Config{ | |
| MinVersion: vers.version, | |
| MaxVersion: vers.version, | |
| // Configure enough options so that, at all TLS versions, only an RSA | |
| // certificate will be accepted. | |
| CipherSuites: []uint16{ | |
| TLS_AES_128_GCM_SHA256, | |
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, | |
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| }, | |
| VerifySignatureAlgorithms: []signatureAlgorithm{signatureRSAPSSWithSHA256}, | |
| }, | |
| shimCredentials: []*Credential{ | |
| ecdsaP256Certificate.WithOCSP(testOCSPResponse2).WithSCTList(testSCTList2), | |
| rsaChainCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList), | |
| }, | |
| shimCertificate: ecdsaP384Certificate.WithOCSP(testOCSPResponse2).WithSCTList(testSCTList2), | |
| flags: []string{"-expect-selected-credential", "1"}, | |
| expectations: connectionExpectations{ | |
| peerCertificate: rsaChainCertificate.WithOCSP(testOCSPResponse).WithSCTList(testSCTList), | |
| }, | |
| }) | |
| // Test that the credentials API works asynchronously. This tests both deferring the | |
| // configuration to the certificate callback, and using a custom, async private key. | |
| testCases = append(testCases, testCase{ | |
| name: fmt.Sprintf("CertificateSelection-Client-Async-%s", suffix), | |
| testType: clientTest, | |
| protocol: protocol, | |
| config: Config{ | |
| MinVersion: vers.version, | |
| MaxVersion: vers.version, | |
| ClientAuth: RequestClientCert, | |
| }, | |
| shimCredentials: []*Credential{&ecdsaP256Certificate}, | |
| shimCertificate: &rsaCertificate, | |
| flags: []string{"-async", "-expect-selected-credential", "0"}, | |
| expectations: connectionExpectations{peerCertificate: &ecdsaP256Certificate}, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: fmt.Sprintf("CertificateSelection-Server-Async-%s", suffix), | |
| testType: serverTest, | |
| protocol: protocol, | |
| config: Config{ | |
| MinVersion: vers.version, | |
| MaxVersion: vers.version, | |
| }, | |
| shimCredentials: []*Credential{&ecdsaP256Certificate}, | |
| shimCertificate: &rsaCertificate, | |
| flags: []string{"-async", "-expect-selected-credential", "0"}, | |
| expectations: connectionExpectations{peerCertificate: &ecdsaP256Certificate}, | |
| }) | |
| for _, test := range certSelectTests { | |
| if test.minVersion != 0 && vers.version < test.minVersion { | |
| continue | |
| } | |
| if test.maxVersion != 0 && vers.version > test.maxVersion { | |
| continue | |
| } | |
| config := test.config | |
| config.MinVersion = vers.version | |
| config.MaxVersion = vers.version | |
| // If the mismatch field is omitted, this is a positive test, | |
| // just to confirm that the selection logic does not block a | |
| // particular certificate. | |
| if test.mismatch == nil { | |
| testCases = append(testCases, testCase{ | |
| name: fmt.Sprintf("CertificateSelection-%s-%s", test.name, suffix), | |
| protocol: protocol, | |
| testType: test.testType, | |
| config: config, | |
| shimCredentials: []*Credential{test.match}, | |
| flags: append([]string{"-expect-selected-credential", "0"}, test.flags...), | |
| expectations: connectionExpectations{peerCertificate: test.match}, | |
| }) | |
| continue | |
| } | |
| if test.match != nil { | |
| testCases = append(testCases, testCase{ | |
| name: fmt.Sprintf("CertificateSelection-%s-MatchFirst-%s", test.name, suffix), | |
| protocol: protocol, | |
| testType: test.testType, | |
| config: config, | |
| shimCredentials: []*Credential{test.match, test.mismatch}, | |
| flags: append([]string{"-expect-selected-credential", "0"}, test.flags...), | |
| expectations: connectionExpectations{peerCertificate: test.match}, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| name: fmt.Sprintf("CertificateSelection-%s-MatchSecond-%s", test.name, suffix), | |
| protocol: protocol, | |
| testType: test.testType, | |
| config: config, | |
| shimCredentials: []*Credential{test.mismatch, test.match}, | |
| flags: append([]string{"-expect-selected-credential", "1"}, test.flags...), | |
| expectations: connectionExpectations{peerCertificate: test.match}, | |
| }) | |
| if canBeShimCertificate(test.match) { | |
| testCases = append(testCases, testCase{ | |
| name: fmt.Sprintf("CertificateSelection-%s-MatchDefault-%s", test.name, suffix), | |
| protocol: protocol, | |
| testType: test.testType, | |
| config: config, | |
| shimCredentials: []*Credential{test.mismatch}, | |
| shimCertificate: test.match, | |
| flags: append([]string{"-expect-selected-credential", "-1"}, test.flags...), | |
| expectations: connectionExpectations{peerCertificate: test.match}, | |
| }) | |
| } | |
| } | |
| testCases = append(testCases, testCase{ | |
| name: fmt.Sprintf("CertificateSelection-%s-MatchNone-%s", test.name, suffix), | |
| protocol: protocol, | |
| testType: test.testType, | |
| config: config, | |
| shimCredentials: []*Credential{test.mismatch, test.mismatch, test.mismatch}, | |
| flags: test.flags, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: handshake failure", | |
| expectedError: test.expectedError, | |
| }) | |
| } | |
| } | |
| } | |
| } |