| // Copyright 2026 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 ( | |
| "crypto" | |
| "fmt" | |
| "slices" | |
| ) | |
| func hashToString(hash crypto.Hash) string { | |
| switch hash { | |
| case crypto.SHA256: | |
| return "SHA256" | |
| case crypto.SHA384: | |
| return "SHA384" | |
| default: | |
| panic(fmt.Sprintf("unknown hash %d", hash)) | |
| } | |
| } | |
| func addPSKTests() { | |
| pskSHA256Credential := Credential{ | |
| Type: CredentialTypePreSharedKey, | |
| PreSharedKey: slices.Repeat([]byte{'A', 'B', 'C', 'D'}, 8), | |
| PSKIdentity: []byte("psk1"), | |
| PSKContext: []byte("context1"), | |
| PSKHash: crypto.SHA256, | |
| } | |
| pskSHA384Credential := Credential{ | |
| Type: CredentialTypePreSharedKey, | |
| PreSharedKey: slices.Repeat([]byte{'E', 'F', 'G', 'H'}, 12), | |
| PSKIdentity: []byte("psk2"), | |
| PSKContext: []byte("context2"), | |
| PSKHash: crypto.SHA384, | |
| } | |
| pskSHA256Credential2 := Credential{ | |
| Type: CredentialTypePreSharedKey, | |
| PreSharedKey: slices.Repeat([]byte{'I', 'J', 'K', 'L'}, 8), | |
| PSKIdentity: []byte("psk3"), | |
| PSKContext: []byte("context3"), | |
| PSKHash: crypto.SHA256, | |
| } | |
| hashToPSK := func(hash crypto.Hash) *Credential { | |
| switch hash { | |
| case crypto.SHA256: | |
| return &pskSHA256Credential | |
| case crypto.SHA384: | |
| return &pskSHA384Credential | |
| default: | |
| panic(fmt.Sprintf("unknown hash %d", hash)) | |
| } | |
| } | |
| hashToCipher := func(hash crypto.Hash) uint16 { | |
| switch hash { | |
| case crypto.SHA256: | |
| return TLS_AES_128_GCM_SHA256 | |
| case crypto.SHA384: | |
| return TLS_AES_256_GCM_SHA384 | |
| default: | |
| panic(fmt.Sprintf("unknown hash %d", hash)) | |
| } | |
| } | |
| for _, protocol := range []protocol{tls, dtls, quic} { | |
| // Test that SHA-256 and SHA-384 PSKs can be used with SHA-256 and | |
| // SHA-384 ciphers. | |
| for _, pskHash := range []crypto.Hash{crypto.SHA256, crypto.SHA384} { | |
| psk := hashToPSK(pskHash) | |
| for _, cipherHash := range []crypto.Hash{crypto.SHA256, crypto.SHA384} { | |
| cipher := hashToCipher(cipherHash) | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-%s-%s-%s", hashToString(pskHash), hashToString(cipherHash), protocol), | |
| config: Config{ | |
| Credential: psk, | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{cipher}, | |
| }, | |
| shimCredentials: []*Credential{psk}, | |
| // Also test that the resulting session can be reused. | |
| resumeSession: true, | |
| // Override the default behavior of expecting a peer certificate on | |
| // resumption connections. | |
| flags: []string{"-expect-no-peer-cert"}, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-%s-%s-%s", hashToString(pskHash), hashToString(cipherHash), protocol), | |
| config: Config{ | |
| Credential: psk, | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{cipher}, | |
| }, | |
| shimCredentials: []*Credential{psk}, | |
| expectations: connectionExpectations{ | |
| selectedPSK: psk, | |
| }, | |
| // Also test that the resulting session can be reused. | |
| resumeSession: true, | |
| resumeExpectations: &connectionExpectations{}, | |
| }) | |
| // Test with HelloRetryRequest to ensure the client computes | |
| // the second ClientHello's binder correctly, and also accounts | |
| // for the PSK list getting smaller once the cipher is known. | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-HRR-%s-%s-%s", hashToString(pskHash), hashToString(cipherHash), protocol), | |
| config: Config{ | |
| Credential: psk, | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{cipher}, | |
| Bugs: ProtocolBugs{ | |
| SendHelloRetryRequestCookie: []byte("cookie"), | |
| }, | |
| }, | |
| shimCredentials: []*Credential{psk}, | |
| // Also test that the resulting session can be reused. | |
| resumeSession: true, | |
| // Override the default behavior of expecting a peer certificate on | |
| // resumption connections. | |
| flags: []string{"-expect-no-peer-cert"}, | |
| }) | |
| } | |
| } | |
| // If the client is configured to offer multiple PSKs, it should accept | |
| // either from the server. | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-AcceptFirst-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential, &pskSHA384Credential}, | |
| flags: []string{"-expect-selected-credential", "0"}, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-AcceptSecond-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA384Credential, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential, &pskSHA384Credential}, | |
| flags: []string{"-expect-selected-credential", "1"}, | |
| }) | |
| // If the client is configured (on the second connection) with both PSKs and | |
| // a session, the PSK is still usable. | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-DeclineSession-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| }, | |
| resumeConfig: &Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| SessionTicketsDisabled: true, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| resumeSession: true, | |
| expectResumeRejected: true, | |
| // The runner will not provision a ticket on the second connection. | |
| flags: []string{"-on-resume-expect-no-session"}, | |
| }) | |
| // The client should reject out-of-bounds PSK indices. | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-OutOfBoundsIndex-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| Bugs: ProtocolBugs{ | |
| // The shim will import two PSKs from the credential, so | |
| // only indices 0 and 1 are valid, | |
| AlwaysSelectPSKIdentity: new(uint16(2)), | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedError: ":PSK_IDENTITY_NOT_FOUND:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-OutOfBoundsIndex-HRR-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| Bugs: ProtocolBugs{ | |
| SendHelloRetryRequestCookie: []byte("cookie"), | |
| // The shim will import two PSKs from the credential, but | |
| // then prune them in the second ClientHello, so only index | |
| // 0 is valid. | |
| AlwaysSelectPSKIdentity: new(uint16(1)), | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedError: ":PSK_IDENTITY_NOT_FOUND:", | |
| }) | |
| // The client should reject psk_ke connections. We require psk_dhe_ke. | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-NoKeyShare-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| Bugs: ProtocolBugs{ | |
| NegotiatePSKResumption: true, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedError: ":MISSING_KEY_SHARE:", | |
| }) | |
| // By default, if the client configures PSKs, it should reject server | |
| // responses that do use certificates, including TLS 1.2. | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-PSKRequired-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &rsaCertificate, | |
| Bugs: ProtocolBugs{ | |
| // Ignore the client's lack of signature_algorithms. | |
| IgnorePeerSignatureAlgorithmPreferences: true, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedError: ":MISSING_EXTENSION:", | |
| // The shim sends an alert, but alerts immediately after TLS 1.3 | |
| // ServerHello have an encryption mismatch. | |
| }) | |
| if protocol != quic { | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-PSKRequired-TLS12-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Credential: &rsaCertificate, | |
| Bugs: ProtocolBugs{ | |
| // Ignore the client's lack of signature_algorithms. | |
| IgnorePeerSignatureAlgorithmPreferences: true, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedError: ":UNSUPPORTED_PROTOCOL:", | |
| expectedLocalError: "remote error: protocol version not supported", | |
| }) | |
| } | |
| // The client can be configured to accept certificates or PSKs. In this | |
| // case, even TLS 1.2 certificates are acceptable. | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-PSKOrCert-PSK-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| flags: []string{"-verify-peer"}, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-PSKOrCert-Cert-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &rsaCertificate, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| flags: []string{"-verify-peer"}, | |
| }) | |
| if protocol != quic { | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-PSKOrCert-Cert-TLS12-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Credential: &rsaCertificate, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| flags: []string{"-verify-peer"}, | |
| }) | |
| } | |
| // When a client is configured with PSKs or certificates, it can even send | |
| // client certificates, configured from the credential list. | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-PSKOrCert-CertRequest-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &rsaCertificate, | |
| ClientAuth: RequireAnyClientCert, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential, &rsaCertificate}, | |
| flags: []string{"-verify-peer"}, | |
| }) | |
| if protocol != quic { | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-PSKOrCert-CertRequest-TLS12-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Credential: &rsaCertificate, | |
| ClientAuth: RequireAnyClientCert, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential, &rsaCertificate}, | |
| flags: []string{"-verify-peer"}, | |
| }) | |
| } | |
| // When a client is configured with PSKs or certificates, the server picks certificates, | |
| // and the server sends CertificateRequests, it must be possible for the client to | |
| // proceed without sending any client certificate, even if the credential list has a | |
| // PSK credential. | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-PSKOrCert-CertRequest-NoClientCert-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &rsaCertificate, | |
| ClientAuth: RequestClientCert, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| flags: []string{"-verify-peer"}, | |
| }) | |
| if protocol != quic { | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-PSKOrCert-CertRequest-NoClientCert-TLS12-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS12, | |
| Credential: &rsaCertificate, | |
| ClientAuth: RequestClientCert, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| flags: []string{"-verify-peer"}, | |
| }) | |
| } | |
| // The client should reject CertificateRequest messages on PSK connections. | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-UnexpectedCertificateRequest-%s", protocol), | |
| testType: clientTest, | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| ClientAuth: RequireAnyClientCert, | |
| Bugs: ProtocolBugs{ | |
| AlwaysSendCertificateRequest: true, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedError: ":UNEXPECTED_MESSAGE:", | |
| expectedLocalError: "remote error: unexpected message", | |
| }) | |
| // The client should reject Certificate messages on PSK connections. | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Client-UnexpectedCertificate-%s", protocol), | |
| config: Config{ | |
| Credential: &pskSHA256Credential, | |
| MaxVersion: VersionTLS13, | |
| Bugs: ProtocolBugs{ | |
| AlwaysSendCertificate: true, | |
| UseCertificateCredential: &rsaCertificate, | |
| // Ignore the client's lack of signature_algorithms. | |
| IgnorePeerSignatureAlgorithmPreferences: true, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedError: ":UNEXPECTED_MESSAGE:", | |
| expectedLocalError: "remote error: unexpected message", | |
| }) | |
| // If a server is configured to request client certificates, it should | |
| // still not do so when negotiating a PSK. | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-DoNotRequestClientCertificate-%s", protocol), | |
| testType: serverTest, | |
| config: Config{ | |
| Credential: &pskSHA256Credential, | |
| MaxVersion: VersionTLS13, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| flags: []string{"-require-any-client-certificate"}, | |
| }) | |
| // The server should notice if the second binder is wrong. | |
| for _, secondBinder := range []bool{false, true} { | |
| binderStr := "FirstBinder" | |
| var defaultCurves []CurveID | |
| if secondBinder { | |
| binderStr = "SecondBinder" | |
| // Force a HelloRetryRequest by predicting an empty curve list. | |
| defaultCurves = []CurveID{} | |
| } | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| testType: serverTest, | |
| name: fmt.Sprintf("PSK-Server-BinderWrongLength-%s-%s", binderStr, protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| DefaultCurves: defaultCurves, | |
| Bugs: ProtocolBugs{ | |
| SendShortPSKBinder: true, | |
| OnlyCorruptSecondPSKBinder: secondBinder, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: error decrypting message", | |
| expectedError: ":DIGEST_CHECK_FAILED:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| testType: serverTest, | |
| name: fmt.Sprintf("PSK-Server-NoPSKBinder-%s-%s", binderStr, protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| DefaultCurves: defaultCurves, | |
| Bugs: ProtocolBugs{ | |
| SendNoPSKBinder: true, | |
| OnlyCorruptSecondPSKBinder: secondBinder, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: error decoding message", | |
| expectedError: ":DECODE_ERROR:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| testType: serverTest, | |
| name: fmt.Sprintf("PSK-Server-ExtraPSKBinder-%s-%s", binderStr, protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| DefaultCurves: defaultCurves, | |
| Bugs: ProtocolBugs{ | |
| SendExtraPSKBinder: true, | |
| OnlyCorruptSecondPSKBinder: secondBinder, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: illegal parameter", | |
| expectedError: ":PSK_IDENTITY_BINDER_COUNT_MISMATCH:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| testType: serverTest, | |
| name: fmt.Sprintf("PSK-Server-ExtraIdentityNoBinder-%s-%s", binderStr, protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| DefaultCurves: defaultCurves, | |
| Bugs: ProtocolBugs{ | |
| ExtraPSKIdentity: true, | |
| OnlyCorruptSecondPSKBinder: secondBinder, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: illegal parameter", | |
| expectedError: ":PSK_IDENTITY_BINDER_COUNT_MISMATCH:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| testType: serverTest, | |
| name: fmt.Sprintf("PSK-Server-InvalidPSKBinder-%s-%s", binderStr, protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| DefaultCurves: defaultCurves, | |
| Bugs: ProtocolBugs{ | |
| SendInvalidPSKBinder: true, | |
| OnlyCorruptSecondPSKBinder: secondBinder, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: error decrypting message", | |
| expectedError: ":DIGEST_CHECK_FAILED:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| testType: serverTest, | |
| name: fmt.Sprintf("PSK-Server-PSKBinderFirstExtension-%s-%s", binderStr, protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| DefaultCurves: defaultCurves, | |
| Bugs: ProtocolBugs{ | |
| PSKBinderFirst: true, | |
| OnlyCorruptSecondPSKBinder: secondBinder, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: illegal parameter", | |
| expectedError: ":PRE_SHARED_KEY_MUST_BE_LAST:", | |
| }) | |
| } | |
| // The server can defer configuring PSKs to either the early callback | |
| // or the SSL_CTX_set_cert_cb callback. (-async causes the shim to defer | |
| // installing credentials. -use-early-callback controls which callback | |
| // installs it.) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-CertCallback-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| flags: []string{"-async", "-expect-selected-credential", "0"}, | |
| expectations: connectionExpectations{ | |
| selectedPSK: &pskSHA256Credential, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-EarlyCallback-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| flags: []string{"-async", "-use-early-callback", "-expect-selected-credential", "0"}, | |
| expectations: connectionExpectations{ | |
| selectedPSK: &pskSHA256Credential, | |
| }, | |
| }) | |
| // If a server is configured with multiple PSKs, it selects the | |
| // first common one. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-ConsiderMultiplePSKs-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| PSKCredentials: []*Credential{&pskSHA256Credential, &pskSHA384Credential}, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential2, &pskSHA384Credential}, | |
| flags: []string{"-expect-selected-credential", "1"}, | |
| expectations: connectionExpectations{ | |
| selectedPSK: &pskSHA384Credential, | |
| }, | |
| }) | |
| // The client and server have no PSKs in common. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-NoCommonPSKs-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| PSKCredentials: []*Credential{&pskSHA256Credential, &pskSHA384Credential}, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential2}, | |
| shouldFail: true, | |
| expectedError: ":PSK_IDENTITY_NOT_FOUND:", | |
| expectedLocalError: "remote error: handshake failure", | |
| }) | |
| // If the server sends HelloRetryRequest, the client may filter its PSK list | |
| // based on the selected cipher. The server must send its PSK index based | |
| // on the second list, not the first. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-HRR-UpdateIndex-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| PSKCredentials: []*Credential{&pskSHA256Credential, &pskSHA384Credential}, | |
| DefaultCurves: []CurveID{}, // Trigger HRR | |
| Bugs: ProtocolBugs{ | |
| OmitPSKsOnSecondClientHello: 1, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA384Credential}, | |
| flags: []string{"-expect-selected-credential", "0"}, | |
| expectations: connectionExpectations{ | |
| selectedPSK: &pskSHA384Credential, | |
| }, | |
| }) | |
| // If the PSK is missing from the second ClientHello, the server should | |
| // reject the connection. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-HRR-PSKMissing-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| PSKCredentials: []*Credential{&pskSHA256Credential, &pskSHA384Credential}, | |
| DefaultCurves: []CurveID{}, // Trigger HRR | |
| Bugs: ProtocolBugs{ | |
| OmitPSKsOnSecondClientHello: 2, // Delete both SHA-256 and SHA-384 variants. | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: illegal parameter", | |
| expectedError: ":PSK_IDENTITY_NOT_FOUND:", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| protocol: protocol, | |
| testType: serverTest, | |
| name: fmt.Sprintf("PSK-Server-OmitAllPSKsOnSecondClientHello-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| Credential: &pskSHA256Credential, | |
| DefaultCurves: []CurveID{}, // Trigger HRR | |
| Bugs: ProtocolBugs{ | |
| OmitAllPSKsOnSecondClientHello: true, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedLocalError: "remote error: missing extension", | |
| expectedError: ":MISSING_EXTENSION:", | |
| }) | |
| // The imported PSK must match exactly, or it is not in common. | |
| extraBytes := pskSHA256Credential | |
| extraBytes.AppendToImportedPSKIdentity = []byte("extra") | |
| wrongHash := pskSHA256Credential | |
| wrongHash.ImportTargetPSKHashes = []crypto.Hash{0} | |
| wrongProtocol := pskSHA256Credential | |
| wrongProtocol.ImportTargetPSKProtocol = 0x1234 | |
| wrongProtocol2 := pskSHA256Credential | |
| wrongProtocol2.ImportTargetPSKProtocol = VersionDTLS13 | |
| wrongContext := pskSHA256Credential | |
| wrongContext.PSKContext = []byte("wrong context") | |
| if protocol == dtls { | |
| wrongProtocol2.ImportTargetPSKProtocol = VersionTLS13 | |
| } | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-IdentityDoesNotMatch-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| PSKCredentials: []*Credential{ | |
| &extraBytes, | |
| &wrongHash, | |
| &wrongProtocol, | |
| &wrongProtocol2, | |
| &wrongContext, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedError: ":PSK_IDENTITY_NOT_FOUND:", | |
| expectedLocalError: "remote error: handshake failure", | |
| }) | |
| // If multiple PSKs match, the server's order is used. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-ServerPreferenceOrder-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| PSKCredentials: []*Credential{&pskSHA384Credential, &pskSHA256Credential}, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential, &pskSHA384Credential}, | |
| flags: []string{"-expect-selected-credential", "0"}, | |
| expectations: connectionExpectations{ | |
| selectedPSK: &pskSHA256Credential, | |
| }, | |
| }) | |
| // Servers can be configured with both PSKs and certificates, | |
| // in which case they evaluate based on their preference order. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-PSKOrCert-PSK-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| PSKCredentials: []*Credential{&pskSHA256Credential}, | |
| }, | |
| shimCredentials: []*Credential{ | |
| &pskSHA256Credential, | |
| &rsaCertificate, | |
| }, | |
| // The ClientHello works for both, but the shim should | |
| // pick the PSK. | |
| flags: []string{"-expect-selected-credential", "0"}, | |
| expectations: connectionExpectations{ | |
| selectedPSK: &pskSHA256Credential, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-PSKOrCert-Cert-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| PSKCredentials: []*Credential{&pskSHA384Credential}, // Wrong PSK | |
| }, | |
| shimCredentials: []*Credential{ | |
| &pskSHA256Credential, | |
| &rsaCertificate, | |
| }, | |
| // The ClientHello is not good for the PSK, so the shim | |
| // should pick the certificate. | |
| flags: []string{"-expect-selected-credential", "1"}, | |
| expectations: connectionExpectations{ | |
| peerCertificate: &rsaCertificate, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-CertOrPSK-Cert-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| PSKCredentials: []*Credential{&pskSHA256Credential}, | |
| }, | |
| shimCredentials: []*Credential{ | |
| &rsaCertificate, | |
| &pskSHA256Credential, | |
| }, | |
| // The ClientHello works for both, but the shim should | |
| // pick the certificate. | |
| flags: []string{"-expect-selected-credential", "0"}, | |
| expectations: connectionExpectations{ | |
| peerCertificate: &rsaCertificate, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-CertOrPSK-PSK-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| PSKCredentials: []*Credential{&pskSHA256Credential}, | |
| VerifySignatureAlgorithms: []signatureAlgorithm{}, // No common algs | |
| }, | |
| shimCredentials: []*Credential{ | |
| &rsaCertificate, | |
| &pskSHA256Credential, | |
| }, | |
| // The ClientHello is not good for the certificate, so the | |
| // shim should pick the PSK. | |
| flags: []string{"-expect-selected-credential", "1"}, | |
| expectations: connectionExpectations{ | |
| selectedPSK: &pskSHA256Credential, | |
| }, | |
| }) | |
| // Clients should import PSKs for each of their supported ciphers. | |
| // If one does not, the server should skip any PSKs that do not | |
| // work with the chosen cipher. | |
| importSHA384Only := pskSHA256Credential | |
| importSHA384Only.ImportTargetPSKHashes = []crypto.Hash{crypto.SHA384} | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-PartialImport-Match-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{TLS_AES_256_GCM_SHA384}, | |
| PSKCredentials: []*Credential{&importSHA384Only}, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| flags: []string{"-expect-selected-credential", "0"}, | |
| expectations: connectionExpectations{ | |
| selectedPSK: &importSHA384Only, | |
| }, | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-PartialImport-NoMatch-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, | |
| PSKCredentials: []*Credential{&importSHA384Only}, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedError: ":PSK_IDENTITY_NOT_FOUND:", | |
| expectedLocalError: "remote error: handshake failure", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-PartialImport-NoMatch-Fallback-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| CipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, | |
| PSKCredentials: []*Credential{&importSHA384Only}, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential, &rsaCertificate}, | |
| flags: []string{"-expect-selected-credential", "1"}, | |
| expectations: connectionExpectations{ | |
| peerCertificate: &rsaCertificate, | |
| }, | |
| }) | |
| // We only implement psk_dhe_ke. If the client does not offer it, PSKs | |
| // are not eligible. | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-MissingPSKMode-NoMatch-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| PSKCredentials: []*Credential{&pskSHA256Credential}, | |
| Bugs: ProtocolBugs{ | |
| SendPSKKeyExchangeModes: []byte{0x1a}, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential}, | |
| shouldFail: true, | |
| expectedError: ":NO_SUPPORTED_PSK_MODE:", | |
| expectedLocalError: "remote error: handshake failure", | |
| }) | |
| testCases = append(testCases, testCase{ | |
| testType: serverTest, | |
| protocol: protocol, | |
| name: fmt.Sprintf("PSK-Server-MissingPSKMode-NoMatch-Fallback-%s", protocol), | |
| config: Config{ | |
| MaxVersion: VersionTLS13, | |
| PSKCredentials: []*Credential{&pskSHA256Credential}, | |
| Bugs: ProtocolBugs{ | |
| SendPSKKeyExchangeModes: []byte{0x1a}, | |
| }, | |
| }, | |
| shimCredentials: []*Credential{&pskSHA256Credential, &rsaCertificate}, | |
| flags: []string{"-expect-selected-credential", "1"}, | |
| expectations: connectionExpectations{ | |
| peerCertificate: &rsaCertificate, | |
| }, | |
| }) | |
| } | |
| } |