blob: d00cf8678bc790d5d65d7fe45612e91bcaddc3c4 [file]
// 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,
}
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"},
})
// 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: ptrTo(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: ptrTo(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"},
})
}
// 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",
})
}
}