blob: 0950bb0ac4533f6961732fb18bb5078da3fab238 [file] [log] [blame]
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
import (
"bytes"
"context"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/binary"
"encoding/pem"
"errors"
"fmt"
"io"
"math/big"
"net"
"os"
"os/exec"
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
"testing"
"time"
)
// Note: see comment in handshake_test.go for details of how the reference
// tests work.
// opensslInputEvent enumerates possible inputs that can be sent to an `openssl
// s_client` process.
type opensslInputEvent int
const (
// opensslRenegotiate causes OpenSSL to request a renegotiation of the
// connection.
opensslRenegotiate opensslInputEvent = iota
// opensslSendBanner causes OpenSSL to send the contents of
// opensslSentinel on the connection.
opensslSendSentinel
// opensslKeyUpdate causes OpenSSL to send a key update message to the
// client and request one back.
opensslKeyUpdate
)
const opensslSentinel = "SENTINEL\n"
type opensslInput chan opensslInputEvent
func (i opensslInput) Read(buf []byte) (n int, err error) {
for event := range i {
switch event {
case opensslRenegotiate:
return copy(buf, []byte("R\n")), nil
case opensslKeyUpdate:
return copy(buf, []byte("K\n")), nil
case opensslSendSentinel:
return copy(buf, []byte(opensslSentinel)), nil
default:
panic("unknown event")
}
}
return 0, io.EOF
}
// opensslOutputSink is an io.Writer that receives the stdout and stderr from an
// `openssl` process and sends a value to handshakeComplete or readKeyUpdate
// when certain messages are seen.
type opensslOutputSink struct {
handshakeComplete chan struct{}
readKeyUpdate chan struct{}
all []byte
line []byte
}
func newOpensslOutputSink() *opensslOutputSink {
return &opensslOutputSink{make(chan struct{}), make(chan struct{}), nil, nil}
}
// opensslEndOfHandshake is a message that the “openssl s_server” tool will
// print when a handshake completes if run with “-state”.
const opensslEndOfHandshake = "SSL_accept:SSLv3/TLS write finished"
// opensslReadKeyUpdate is a message that the “openssl s_server” tool will
// print when a KeyUpdate message is received if run with “-state”.
const opensslReadKeyUpdate = "SSL_accept:TLSv1.3 read client key update"
func (o *opensslOutputSink) Write(data []byte) (n int, err error) {
o.line = append(o.line, data...)
o.all = append(o.all, data...)
for {
line, next, ok := bytes.Cut(o.line, []byte("\n"))
if !ok {
break
}
if bytes.Equal([]byte(opensslEndOfHandshake), line) {
o.handshakeComplete <- struct{}{}
}
if bytes.Equal([]byte(opensslReadKeyUpdate), line) {
o.readKeyUpdate <- struct{}{}
}
o.line = next
}
return len(data), nil
}
func (o *opensslOutputSink) String() string {
return string(o.all)
}
// clientTest represents a test of the TLS client handshake against a reference
// implementation.
type clientTest struct {
// name is a freeform string identifying the test and the file in which
// the expected results will be stored.
name string
// args, if not empty, contains a series of arguments for the
// command to run for the reference server.
args []string
// config, if not nil, contains a custom Config to use for this test.
config *Config
// cert, if not empty, contains a DER-encoded certificate for the
// reference server.
cert []byte
// key, if not nil, contains either a *rsa.PrivateKey, ed25519.PrivateKey or
// *ecdsa.PrivateKey which is the private key for the reference server.
key any
// extensions, if not nil, contains a list of extension data to be returned
// from the ServerHello. The data should be in standard TLS format with
// a 2-byte uint16 type, 2-byte data length, followed by the extension data.
extensions [][]byte
// validate, if not nil, is a function that will be called with the
// ConnectionState of the resulting connection. It returns a non-nil
// error if the ConnectionState is unacceptable.
validate func(ConnectionState) error
// numRenegotiations is the number of times that the connection will be
// renegotiated.
numRenegotiations int
// renegotiationExpectedToFail, if not zero, is the number of the
// renegotiation attempt that is expected to fail.
renegotiationExpectedToFail int
// checkRenegotiationError, if not nil, is called with any error
// arising from renegotiation. It can map expected errors to nil to
// ignore them.
checkRenegotiationError func(renegotiationNum int, err error) error
// sendKeyUpdate will cause the server to send a KeyUpdate message.
sendKeyUpdate bool
}
var serverCommand = []string{"openssl", "s_server", "-no_ticket", "-num_tickets", "0"}
// connFromCommand starts the reference server process, connects to it and
// returns a recordingConn for the connection. The stdin return value is an
// opensslInput for the stdin of the child process. It must be closed before
// Waiting for child.
func (test *clientTest) connFromCommand() (conn *recordingConn, child *exec.Cmd, stdin opensslInput, stdout *opensslOutputSink, err error) {
cert := testRSACertificate
if len(test.cert) > 0 {
cert = test.cert
}
certPath := tempFile(string(cert))
defer os.Remove(certPath)
var key any = testRSAPrivateKey
if test.key != nil {
key = test.key
}
derBytes, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
panic(err)
}
var pemOut bytes.Buffer
pem.Encode(&pemOut, &pem.Block{Type: "PRIVATE KEY", Bytes: derBytes})
keyPath := tempFile(pemOut.String())
defer os.Remove(keyPath)
var command []string
command = append(command, serverCommand...)
command = append(command, test.args...)
command = append(command, "-cert", certPath, "-certform", "DER", "-key", keyPath)
// serverPort contains the port that OpenSSL will listen on. OpenSSL
// can't take "0" as an argument here so we have to pick a number and
// hope that it's not in use on the machine. Since this only occurs
// when -update is given and thus when there's a human watching the
// test, this isn't too bad.
const serverPort = 24323
command = append(command, "-accept", strconv.Itoa(serverPort))
if len(test.extensions) > 0 {
var serverInfo bytes.Buffer
for _, ext := range test.extensions {
pem.Encode(&serverInfo, &pem.Block{
Type: fmt.Sprintf("SERVERINFO FOR EXTENSION %d", binary.BigEndian.Uint16(ext)),
Bytes: ext,
})
}
serverInfoPath := tempFile(serverInfo.String())
defer os.Remove(serverInfoPath)
command = append(command, "-serverinfo", serverInfoPath)
}
if test.numRenegotiations > 0 || test.sendKeyUpdate {
found := false
for _, flag := range command[1:] {
if flag == "-state" {
found = true
break
}
}
if !found {
panic("-state flag missing to OpenSSL, you need this if testing renegotiation or KeyUpdate")
}
}
cmd := exec.Command(command[0], command[1:]...)
stdin = opensslInput(make(chan opensslInputEvent))
cmd.Stdin = stdin
out := newOpensslOutputSink()
cmd.Stdout = out
cmd.Stderr = out
if err := cmd.Start(); err != nil {
return nil, nil, nil, nil, err
}
// OpenSSL does print an "ACCEPT" banner, but it does so *before*
// opening the listening socket, so we can't use that to wait until it
// has started listening. Thus we are forced to poll until we get a
// connection.
var tcpConn net.Conn
for i := uint(0); i < 5; i++ {
tcpConn, err = net.DialTCP("tcp", nil, &net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: serverPort,
})
if err == nil {
break
}
time.Sleep((1 << i) * 5 * time.Millisecond)
}
if err != nil {
close(stdin)
cmd.Process.Kill()
err = fmt.Errorf("error connecting to the OpenSSL server: %v (%v)\n\n%s", err, cmd.Wait(), out)
return nil, nil, nil, nil, err
}
record := &recordingConn{
Conn: tcpConn,
}
return record, cmd, stdin, out, nil
}
func (test *clientTest) dataPath() string {
return filepath.Join("testdata", "Client-"+test.name)
}
func (test *clientTest) loadData() (flows [][]byte, err error) {
in, err := os.Open(test.dataPath())
if err != nil {
return nil, err
}
defer in.Close()
return parseTestData(in)
}
func (test *clientTest) run(t *testing.T, write bool) {
var clientConn, serverConn net.Conn
var recordingConn *recordingConn
var childProcess *exec.Cmd
var stdin opensslInput
var stdout *opensslOutputSink
if write {
var err error
recordingConn, childProcess, stdin, stdout, err = test.connFromCommand()
if err != nil {
t.Fatalf("Failed to start subcommand: %s", err)
}
clientConn = recordingConn
defer func() {
if t.Failed() {
t.Logf("OpenSSL output:\n\n%s", stdout.all)
}
}()
} else {
clientConn, serverConn = localPipe(t)
}
doneChan := make(chan bool)
defer func() {
clientConn.Close()
<-doneChan
}()
go func() {
defer close(doneChan)
config := test.config
if config == nil {
config = testConfig
}
client := Client(clientConn, config)
defer client.Close()
if _, err := client.Write([]byte("hello\n")); err != nil {
t.Errorf("Client.Write failed: %s", err)
return
}
for i := 1; i <= test.numRenegotiations; i++ {
// The initial handshake will generate a
// handshakeComplete signal which needs to be quashed.
if i == 1 && write {
<-stdout.handshakeComplete
}
// OpenSSL will try to interleave application data and
// a renegotiation if we send both concurrently.
// Therefore: ask OpensSSL to start a renegotiation, run
// a goroutine to call client.Read and thus process the
// renegotiation request, watch for OpenSSL's stdout to
// indicate that the handshake is complete and,
// finally, have OpenSSL write something to cause
// client.Read to complete.
if write {
stdin <- opensslRenegotiate
}
signalChan := make(chan struct{})
go func() {
defer close(signalChan)
buf := make([]byte, 256)
n, err := client.Read(buf)
if test.checkRenegotiationError != nil {
newErr := test.checkRenegotiationError(i, err)
if err != nil && newErr == nil {
return
}
err = newErr
}
if err != nil {
t.Errorf("Client.Read failed after renegotiation #%d: %s", i, err)
return
}
buf = buf[:n]
if !bytes.Equal([]byte(opensslSentinel), buf) {
t.Errorf("Client.Read returned %q, but wanted %q", string(buf), opensslSentinel)
}
if expected := i + 1; client.handshakes != expected {
t.Errorf("client should have recorded %d handshakes, but believes that %d have occurred", expected, client.handshakes)
}
}()
if write && test.renegotiationExpectedToFail != i {
<-stdout.handshakeComplete
stdin <- opensslSendSentinel
}
<-signalChan
}
if test.sendKeyUpdate {
if write {
<-stdout.handshakeComplete
stdin <- opensslKeyUpdate
}
doneRead := make(chan struct{})
go func() {
defer close(doneRead)
buf := make([]byte, 256)
n, err := client.Read(buf)
if err != nil {
t.Errorf("Client.Read failed after KeyUpdate: %s", err)
return
}
buf = buf[:n]
if !bytes.Equal([]byte(opensslSentinel), buf) {
t.Errorf("Client.Read returned %q, but wanted %q", string(buf), opensslSentinel)
}
}()
if write {
// There's no real reason to wait for the client KeyUpdate to
// send data with the new server keys, except that s_server
// drops writes if they are sent at the wrong time.
<-stdout.readKeyUpdate
stdin <- opensslSendSentinel
}
<-doneRead
if _, err := client.Write([]byte("hello again\n")); err != nil {
t.Errorf("Client.Write failed: %s", err)
return
}
}
if test.validate != nil {
if err := test.validate(client.ConnectionState()); err != nil {
t.Errorf("validate callback returned error: %s", err)
}
}
// If the server sent us an alert after our last flight, give it a
// chance to arrive.
if write && test.renegotiationExpectedToFail == 0 {
if err := peekError(client); err != nil {
t.Errorf("final Read returned an error: %s", err)
}
}
}()
if !write {
flows, err := test.loadData()
if err != nil {
t.Fatalf("%s: failed to load data from %s: %v", test.name, test.dataPath(), err)
}
for i, b := range flows {
if i%2 == 1 {
if *fast {
serverConn.SetWriteDeadline(time.Now().Add(1 * time.Second))
} else {
serverConn.SetWriteDeadline(time.Now().Add(1 * time.Minute))
}
serverConn.Write(b)
continue
}
bb := make([]byte, len(b))
if *fast {
serverConn.SetReadDeadline(time.Now().Add(1 * time.Second))
} else {
serverConn.SetReadDeadline(time.Now().Add(1 * time.Minute))
}
_, err := io.ReadFull(serverConn, bb)
if err != nil {
t.Fatalf("%s, flow %d: %s", test.name, i+1, err)
}
if !bytes.Equal(b, bb) {
t.Fatalf("%s, flow %d: mismatch on read: got:%x want:%x", test.name, i+1, bb, b)
}
}
}
<-doneChan
if !write {
serverConn.Close()
}
if write {
path := test.dataPath()
out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
t.Fatalf("Failed to create output file: %s", err)
}
defer out.Close()
recordingConn.Close()
close(stdin)
childProcess.Process.Kill()
childProcess.Wait()
if len(recordingConn.flows) < 3 {
t.Fatalf("Client connection didn't work")
}
recordingConn.WriteTo(out)
t.Logf("Wrote %s\n", path)
}
}
// peekError does a read with a short timeout to check if the next read would
// cause an error, for example if there is an alert waiting on the wire.
func peekError(conn net.Conn) error {
conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
if n, err := conn.Read(make([]byte, 1)); n != 0 {
return errors.New("unexpectedly read data")
} else if err != nil {
if netErr, ok := err.(net.Error); !ok || !netErr.Timeout() {
return err
}
}
return nil
}
func runClientTestForVersion(t *testing.T, template *clientTest, version, option string) {
// Make a deep copy of the template before going parallel.
test := *template
if template.config != nil {
test.config = template.config.Clone()
}
test.name = version + "-" + test.name
test.args = append([]string{option}, test.args...)
runTestAndUpdateIfNeeded(t, version, test.run, false)
}
func runClientTestTLS10(t *testing.T, template *clientTest) {
runClientTestForVersion(t, template, "TLSv10", "-tls1")
}
func runClientTestTLS11(t *testing.T, template *clientTest) {
runClientTestForVersion(t, template, "TLSv11", "-tls1_1")
}
func runClientTestTLS12(t *testing.T, template *clientTest) {
runClientTestForVersion(t, template, "TLSv12", "-tls1_2")
}
func runClientTestTLS13(t *testing.T, template *clientTest) {
runClientTestForVersion(t, template, "TLSv13", "-tls1_3")
}
func TestHandshakeClientRSARC4(t *testing.T) {
test := &clientTest{
name: "RSA-RC4",
args: []string{"-cipher", "RC4-SHA"},
}
runClientTestTLS10(t, test)
runClientTestTLS11(t, test)
runClientTestTLS12(t, test)
}
func TestHandshakeClientRSAAES128GCM(t *testing.T) {
test := &clientTest{
name: "AES128-GCM-SHA256",
args: []string{"-cipher", "AES128-GCM-SHA256"},
}
runClientTestTLS12(t, test)
}
func TestHandshakeClientRSAAES256GCM(t *testing.T) {
test := &clientTest{
name: "AES256-GCM-SHA384",
args: []string{"-cipher", "AES256-GCM-SHA384"},
}
runClientTestTLS12(t, test)
}
func TestHandshakeClientECDHERSAAES(t *testing.T) {
test := &clientTest{
name: "ECDHE-RSA-AES",
args: []string{"-cipher", "ECDHE-RSA-AES128-SHA"},
}
runClientTestTLS10(t, test)
runClientTestTLS11(t, test)
runClientTestTLS12(t, test)
}
func TestHandshakeClientECDHEECDSAAES(t *testing.T) {
test := &clientTest{
name: "ECDHE-ECDSA-AES",
args: []string{"-cipher", "ECDHE-ECDSA-AES128-SHA"},
cert: testECDSACertificate,
key: testECDSAPrivateKey,
}
runClientTestTLS10(t, test)
runClientTestTLS11(t, test)
runClientTestTLS12(t, test)
}
func TestHandshakeClientECDHEECDSAAESGCM(t *testing.T) {
test := &clientTest{
name: "ECDHE-ECDSA-AES-GCM",
args: []string{"-cipher", "ECDHE-ECDSA-AES128-GCM-SHA256"},
cert: testECDSACertificate,
key: testECDSAPrivateKey,
}
runClientTestTLS12(t, test)
}
func TestHandshakeClientAES256GCMSHA384(t *testing.T) {
test := &clientTest{
name: "ECDHE-ECDSA-AES256-GCM-SHA384",
args: []string{"-cipher", "ECDHE-ECDSA-AES256-GCM-SHA384"},
cert: testECDSACertificate,
key: testECDSAPrivateKey,
}
runClientTestTLS12(t, test)
}
func TestHandshakeClientAES128CBCSHA256(t *testing.T) {
test := &clientTest{
name: "AES128-SHA256",
args: []string{"-cipher", "AES128-SHA256"},
}
runClientTestTLS12(t, test)
}
func TestHandshakeClientECDHERSAAES128CBCSHA256(t *testing.T) {
test := &clientTest{
name: "ECDHE-RSA-AES128-SHA256",
args: []string{"-cipher", "ECDHE-RSA-AES128-SHA256"},
}
runClientTestTLS12(t, test)
}
func TestHandshakeClientECDHEECDSAAES128CBCSHA256(t *testing.T) {
test := &clientTest{
name: "ECDHE-ECDSA-AES128-SHA256",
args: []string{"-cipher", "ECDHE-ECDSA-AES128-SHA256"},
cert: testECDSACertificate,
key: testECDSAPrivateKey,
}
runClientTestTLS12(t, test)
}
func TestHandshakeClientX25519(t *testing.T) {
config := testConfig.Clone()
config.CurvePreferences = []CurveID{X25519}
test := &clientTest{
name: "X25519-ECDHE",
args: []string{"-cipher", "ECDHE-RSA-AES128-GCM-SHA256", "-curves", "X25519"},
config: config,
}
runClientTestTLS12(t, test)
runClientTestTLS13(t, test)
}
func TestHandshakeClientP256(t *testing.T) {
config := testConfig.Clone()
config.CurvePreferences = []CurveID{CurveP256}
test := &clientTest{
name: "P256-ECDHE",
args: []string{"-cipher", "ECDHE-RSA-AES128-GCM-SHA256", "-curves", "P-256"},
config: config,
}
runClientTestTLS12(t, test)
runClientTestTLS13(t, test)
}
func TestHandshakeClientHelloRetryRequest(t *testing.T) {
config := testConfig.Clone()
config.CurvePreferences = []CurveID{X25519, CurveP256}
test := &clientTest{
name: "HelloRetryRequest",
args: []string{"-cipher", "ECDHE-RSA-AES128-GCM-SHA256", "-curves", "P-256"},
config: config,
}
runClientTestTLS13(t, test)
}
func TestHandshakeClientECDHERSAChaCha20(t *testing.T) {
config := testConfig.Clone()
config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305}
test := &clientTest{
name: "ECDHE-RSA-CHACHA20-POLY1305",
args: []string{"-cipher", "ECDHE-RSA-CHACHA20-POLY1305"},
config: config,
}
runClientTestTLS12(t, test)
}
func TestHandshakeClientECDHEECDSAChaCha20(t *testing.T) {
config := testConfig.Clone()
config.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305}
test := &clientTest{
name: "ECDHE-ECDSA-CHACHA20-POLY1305",
args: []string{"-cipher", "ECDHE-ECDSA-CHACHA20-POLY1305"},
config: config,
cert: testECDSACertificate,
key: testECDSAPrivateKey,
}
runClientTestTLS12(t, test)
}
func TestHandshakeClientAES128SHA256(t *testing.T) {
test := &clientTest{
name: "AES128-SHA256",
args: []string{"-ciphersuites", "TLS_AES_128_GCM_SHA256"},
}
runClientTestTLS13(t, test)
}
func TestHandshakeClientAES256SHA384(t *testing.T) {
test := &clientTest{
name: "AES256-SHA384",
args: []string{"-ciphersuites", "TLS_AES_256_GCM_SHA384"},
}
runClientTestTLS13(t, test)
}
func TestHandshakeClientCHACHA20SHA256(t *testing.T) {
test := &clientTest{
name: "CHACHA20-SHA256",
args: []string{"-ciphersuites", "TLS_CHACHA20_POLY1305_SHA256"},
}
runClientTestTLS13(t, test)
}
func TestHandshakeClientECDSATLS13(t *testing.T) {
test := &clientTest{
name: "ECDSA",
cert: testECDSACertificate,
key: testECDSAPrivateKey,
}
runClientTestTLS13(t, test)
}
func TestHandshakeClientEd25519(t *testing.T) {
test := &clientTest{
name: "Ed25519",
cert: testEd25519Certificate,
key: testEd25519PrivateKey,
}
runClientTestTLS12(t, test)
runClientTestTLS13(t, test)
config := testConfig.Clone()
cert, _ := X509KeyPair([]byte(clientEd25519CertificatePEM), []byte(clientEd25519KeyPEM))
config.Certificates = []Certificate{cert}
test = &clientTest{
name: "ClientCert-Ed25519",
args: []string{"-Verify", "1"},
config: config,
}
runClientTestTLS12(t, test)
runClientTestTLS13(t, test)
}
func TestHandshakeClientCertRSA(t *testing.T) {
config := testConfig.Clone()
cert, _ := X509KeyPair([]byte(clientCertificatePEM), []byte(clientKeyPEM))
config.Certificates = []Certificate{cert}
test := &clientTest{
name: "ClientCert-RSA-RSA",
args: []string{"-cipher", "AES128", "-Verify", "1"},
config: config,
}
runClientTestTLS10(t, test)
runClientTestTLS12(t, test)
test = &clientTest{
name: "ClientCert-RSA-ECDSA",
args: []string{"-cipher", "ECDHE-ECDSA-AES128-SHA", "-Verify", "1"},
config: config,
cert: testECDSACertificate,
key: testECDSAPrivateKey,
}
runClientTestTLS10(t, test)
runClientTestTLS12(t, test)
runClientTestTLS13(t, test)
test = &clientTest{
name: "ClientCert-RSA-AES256-GCM-SHA384",
args: []string{"-cipher", "ECDHE-RSA-AES256-GCM-SHA384", "-Verify", "1"},
config: config,
cert: testRSACertificate,
key: testRSAPrivateKey,
}
runClientTestTLS12(t, test)
}
func TestHandshakeClientCertECDSA(t *testing.T) {
config := testConfig.Clone()
cert, _ := X509KeyPair([]byte(clientECDSACertificatePEM), []byte(clientECDSAKeyPEM))
config.Certificates = []Certificate{cert}
test := &clientTest{
name: "ClientCert-ECDSA-RSA",
args: []string{"-cipher", "AES128", "-Verify", "1"},
config: config,
}
runClientTestTLS10(t, test)
runClientTestTLS12(t, test)
runClientTestTLS13(t, test)
test = &clientTest{
name: "ClientCert-ECDSA-ECDSA",
args: []string{"-cipher", "ECDHE-ECDSA-AES128-SHA", "-Verify", "1"},
config: config,
cert: testECDSACertificate,
key: testECDSAPrivateKey,
}
runClientTestTLS10(t, test)
runClientTestTLS12(t, test)
}
// TestHandshakeClientCertRSAPSS tests rsa_pss_rsae_sha256 signatures from both
// client and server certificates. It also serves from both sides a certificate
// signed itself with RSA-PSS, mostly to check that crypto/x509 chain validation
// works.
func TestHandshakeClientCertRSAPSS(t *testing.T) {
cert, err := x509.ParseCertificate(testRSAPSSCertificate)
if err != nil {
panic(err)
}
rootCAs := x509.NewCertPool()
rootCAs.AddCert(cert)
config := testConfig.Clone()
// Use GetClientCertificate to bypass the client certificate selection logic.
config.GetClientCertificate = func(*CertificateRequestInfo) (*Certificate, error) {
return &Certificate{
Certificate: [][]byte{testRSAPSSCertificate},
PrivateKey: testRSAPrivateKey,
}, nil
}
config.RootCAs = rootCAs
test := &clientTest{
name: "ClientCert-RSA-RSAPSS",
args: []string{"-cipher", "AES128", "-Verify", "1", "-client_sigalgs",
"rsa_pss_rsae_sha256", "-sigalgs", "rsa_pss_rsae_sha256"},
config: config,
cert: testRSAPSSCertificate,
key: testRSAPrivateKey,
}
runClientTestTLS12(t, test)
runClientTestTLS13(t, test)
}
func TestHandshakeClientCertRSAPKCS1v15(t *testing.T) {
config := testConfig.Clone()
cert, _ := X509KeyPair([]byte(clientCertificatePEM), []byte(clientKeyPEM))
config.Certificates = []Certificate{cert}
test := &clientTest{
name: "ClientCert-RSA-RSAPKCS1v15",
args: []string{"-cipher", "AES128", "-Verify", "1", "-client_sigalgs",
"rsa_pkcs1_sha256", "-sigalgs", "rsa_pkcs1_sha256"},
config: config,
}
runClientTestTLS12(t, test)
}
func TestClientKeyUpdate(t *testing.T) {
test := &clientTest{
name: "KeyUpdate",
args: []string{"-state"},
sendKeyUpdate: true,
}
runClientTestTLS13(t, test)
}
func TestResumption(t *testing.T) {
t.Run("TLSv12", func(t *testing.T) { testResumption(t, VersionTLS12) })
t.Run("TLSv13", func(t *testing.T) { testResumption(t, VersionTLS13) })
}
func testResumption(t *testing.T, version uint16) {
if testing.Short() {
t.Skip("skipping in -short mode")
}
serverConfig := &Config{
MaxVersion: version,
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
Certificates: testConfig.Certificates,
}
issuer, err := x509.ParseCertificate(testRSACertificateIssuer)
if err != nil {
panic(err)
}
rootCAs := x509.NewCertPool()
rootCAs.AddCert(issuer)
clientConfig := &Config{
MaxVersion: version,
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
ClientSessionCache: NewLRUClientSessionCache(32),
RootCAs: rootCAs,
ServerName: "example.golang",
}
testResumeState := func(test string, didResume bool) {
_, hs, err := testHandshake(t, clientConfig, serverConfig)
if err != nil {
t.Fatalf("%s: handshake failed: %s", test, err)
}
if hs.DidResume != didResume {
t.Fatalf("%s resumed: %v, expected: %v", test, hs.DidResume, didResume)
}
if didResume && (hs.PeerCertificates == nil || hs.VerifiedChains == nil) {
t.Fatalf("expected non-nil certificates after resumption. Got peerCertificates: %#v, verifiedCertificates: %#v", hs.PeerCertificates, hs.VerifiedChains)
}
if got, want := hs.ServerName, clientConfig.ServerName; got != want {
t.Errorf("%s: server name %s, want %s", test, got, want)
}
}
getTicket := func() []byte {
return clientConfig.ClientSessionCache.(*lruSessionCache).q.Front().Value.(*lruSessionCacheEntry).state.sessionTicket
}
deleteTicket := func() {
ticketKey := clientConfig.ClientSessionCache.(*lruSessionCache).q.Front().Value.(*lruSessionCacheEntry).sessionKey
clientConfig.ClientSessionCache.Put(ticketKey, nil)
}
corruptTicket := func() {
clientConfig.ClientSessionCache.(*lruSessionCache).q.Front().Value.(*lruSessionCacheEntry).state.masterSecret[0] ^= 0xff
}
randomKey := func() [32]byte {
var k [32]byte
if _, err := io.ReadFull(serverConfig.rand(), k[:]); err != nil {
t.Fatalf("Failed to read new SessionTicketKey: %s", err)
}
return k
}
testResumeState("Handshake", false)
ticket := getTicket()
testResumeState("Resume", true)
if !bytes.Equal(ticket, getTicket()) && version != VersionTLS13 {
t.Fatal("first ticket doesn't match ticket after resumption")
}
if bytes.Equal(ticket, getTicket()) && version == VersionTLS13 {
t.Fatal("ticket didn't change after resumption")
}
// An old session ticket can resume, but the server will provide a ticket encrypted with a fresh key.
serverConfig.Time = func() time.Time { return time.Now().Add(24*time.Hour + time.Minute) }
testResumeState("ResumeWithOldTicket", true)
if bytes.Equal(ticket[:ticketKeyNameLen], getTicket()[:ticketKeyNameLen]) {
t.Fatal("old first ticket matches the fresh one")
}
// Now the session tickey key is expired, so a full handshake should occur.
serverConfig.Time = func() time.Time { return time.Now().Add(24*8*time.Hour + time.Minute) }
testResumeState("ResumeWithExpiredTicket", false)
if bytes.Equal(ticket, getTicket()) {
t.Fatal("expired first ticket matches the fresh one")
}
serverConfig.Time = func() time.Time { return time.Now() } // reset the time back
key1 := randomKey()
serverConfig.SetSessionTicketKeys([][32]byte{key1})
testResumeState("InvalidSessionTicketKey", false)
testResumeState("ResumeAfterInvalidSessionTicketKey", true)
key2 := randomKey()
serverConfig.SetSessionTicketKeys([][32]byte{key2, key1})
ticket = getTicket()
testResumeState("KeyChange", true)
if bytes.Equal(ticket, getTicket()) {
t.Fatal("new ticket wasn't included while resuming")
}
testResumeState("KeyChangeFinish", true)
// Age the session ticket a bit, but not yet expired.
serverConfig.Time = func() time.Time { return time.Now().Add(24*time.Hour + time.Minute) }
testResumeState("OldSessionTicket", true)
ticket = getTicket()
// Expire the session ticket, which would force a full handshake.
serverConfig.Time = func() time.Time { return time.Now().Add(24*8*time.Hour + time.Minute) }
testResumeState("ExpiredSessionTicket", false)
if bytes.Equal(ticket, getTicket()) {
t.Fatal("new ticket wasn't provided after old ticket expired")
}
// Age the session ticket a bit at a time, but don't expire it.
d := 0 * time.Hour
for i := 0; i < 13; i++ {
d += 12 * time.Hour
serverConfig.Time = func() time.Time { return time.Now().Add(d) }
testResumeState("OldSessionTicket", true)
}
// Expire it (now a little more than 7 days) and make sure a full
// handshake occurs for TLS 1.2. Resumption should still occur for
// TLS 1.3 since the client should be using a fresh ticket sent over
// by the server.
d += 12 * time.Hour
serverConfig.Time = func() time.Time { return time.Now().Add(d) }
if version == VersionTLS13 {
testResumeState("ExpiredSessionTicket", true)
} else {
testResumeState("ExpiredSessionTicket", false)
}
if bytes.Equal(ticket, getTicket()) {
t.Fatal("new ticket wasn't provided after old ticket expired")
}
// Reset serverConfig to ensure that calling SetSessionTicketKeys
// before the serverConfig is used works.
serverConfig = &Config{
MaxVersion: version,
CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
Certificates: testConfig.Certificates,
}
serverConfig.SetSessionTicketKeys([][32]byte{key2})
testResumeState("FreshConfig", true)
// In TLS 1.3, cross-cipher suite resumption is allowed as long as the KDF
// hash matches. Also, Config.CipherSuites does not apply to TLS 1.3.
if version != VersionTLS13 {
clientConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
testResumeState("DifferentCipherSuite", false)
testResumeState("DifferentCipherSuiteRecovers", true)
}
deleteTicket()
testResumeState("WithoutSessionTicket", false)
// Session resumption should work when using client certificates
deleteTicket()
serverConfig.ClientCAs = rootCAs
serverConfig.ClientAuth = RequireAndVerifyClientCert
clientConfig.Certificates = serverConfig.Certificates
testResumeState("InitialHandshake", false)
testResumeState("WithClientCertificates", true)
serverConfig.ClientAuth = NoClientCert
// Tickets should be removed from the session cache on TLS handshake
// failure, and the client should recover from a corrupted PSK
testResumeState("FetchTicketToCorrupt", false)
corruptTicket()
_, _, err = testHandshake(t, clientConfig, serverConfig)
if err == nil {
t.Fatalf("handshake did not fail with a corrupted client secret")
}
testResumeState("AfterHandshakeFailure", false)
clientConfig.ClientSessionCache = nil
testResumeState("WithoutSessionCache", false)
}
func TestLRUClientSessionCache(t *testing.T) {
// Initialize cache of capacity 4.
cache := NewLRUClientSessionCache(4)
cs := make([]ClientSessionState, 6)
keys := []string{"0", "1", "2", "3", "4", "5", "6"}
// Add 4 entries to the cache and look them up.
for i := 0; i < 4; i++ {
cache.Put(keys[i], &cs[i])
}
for i := 0; i < 4; i++ {
if s, ok := cache.Get(keys[i]); !ok || s != &cs[i] {
t.Fatalf("session cache failed lookup for added key: %s", keys[i])
}
}
// Add 2 more entries to the cache. First 2 should be evicted.
for i := 4; i < 6; i++ {
cache.Put(keys[i], &cs[i])
}
for i := 0; i < 2; i++ {
if s, ok := cache.Get(keys[i]); ok || s != nil {
t.Fatalf("session cache should have evicted key: %s", keys[i])
}
}
// Touch entry 2. LRU should evict 3 next.
cache.Get(keys[2])
cache.Put(keys[0], &cs[0])
if s, ok := cache.Get(keys[3]); ok || s != nil {
t.Fatalf("session cache should have evicted key 3")
}
// Update entry 0 in place.
cache.Put(keys[0], &cs[3])
if s, ok := cache.Get(keys[0]); !ok || s != &cs[3] {
t.Fatalf("session cache failed update for key 0")
}
// Calling Put with a nil entry deletes the key.
cache.Put(keys[0], nil)
if _, ok := cache.Get(keys[0]); ok {
t.Fatalf("session cache failed to delete key 0")
}
// Delete entry 2. LRU should keep 4 and 5
cache.Put(keys[2], nil)
if _, ok := cache.Get(keys[2]); ok {
t.Fatalf("session cache failed to delete key 4")
}
for i := 4; i < 6; i++ {
if s, ok := cache.Get(keys[i]); !ok || s != &cs[i] {
t.Fatalf("session cache should not have deleted key: %s", keys[i])
}
}
}
func TestKeyLogTLS12(t *testing.T) {
var serverBuf, clientBuf bytes.Buffer
clientConfig := testConfig.Clone()
clientConfig.KeyLogWriter = &clientBuf
clientConfig.MaxVersion = VersionTLS12
serverConfig := testConfig.Clone()
serverConfig.KeyLogWriter = &serverBuf
serverConfig.MaxVersion = VersionTLS12
c, s := localPipe(t)
done := make(chan bool)
go func() {
defer close(done)
if err := Server(s, serverConfig).Handshake(); err != nil {
t.Errorf("server: %s", err)
return
}
s.Close()
}()
if err := Client(c, clientConfig).Handshake(); err != nil {
t.Fatalf("client: %s", err)
}
c.Close()
<-done
checkKeylogLine := func(side, loggedLine string) {
if len(loggedLine) == 0 {
t.Fatalf("%s: no keylog line was produced", side)
}
const expectedLen = 13 /* "CLIENT_RANDOM" */ +
1 /* space */ +
32*2 /* hex client nonce */ +
1 /* space */ +
48*2 /* hex master secret */ +
1 /* new line */
if len(loggedLine) != expectedLen {
t.Fatalf("%s: keylog line has incorrect length (want %d, got %d): %q", side, expectedLen, len(loggedLine), loggedLine)
}
if !strings.HasPrefix(loggedLine, "CLIENT_RANDOM "+strings.Repeat("0", 64)+" ") {
t.Fatalf("%s: keylog line has incorrect structure or nonce: %q", side, loggedLine)
}
}
checkKeylogLine("client", clientBuf.String())
checkKeylogLine("server", serverBuf.String())
}
func TestKeyLogTLS13(t *testing.T) {
var serverBuf, clientBuf bytes.Buffer
clientConfig := testConfig.Clone()
clientConfig.KeyLogWriter = &clientBuf
serverConfig := testConfig.Clone()
serverConfig.KeyLogWriter = &serverBuf
c, s := localPipe(t)
done := make(chan bool)
go func() {
defer close(done)
if err := Server(s, serverConfig).Handshake(); err != nil {
t.Errorf("server: %s", err)
return
}
s.Close()
}()
if err := Client(c, clientConfig).Handshake(); err != nil {
t.Fatalf("client: %s", err)
}
c.Close()
<-done
checkKeylogLines := func(side, loggedLines string) {
loggedLines = strings.TrimSpace(loggedLines)
lines := strings.Split(loggedLines, "\n")
if len(lines) != 4 {
t.Errorf("Expected the %s to log 4 lines, got %d", side, len(lines))
}
}
checkKeylogLines("client", clientBuf.String())
checkKeylogLines("server", serverBuf.String())
}
func TestHandshakeClientALPNMatch(t *testing.T) {
config := testConfig.Clone()
config.NextProtos = []string{"proto2", "proto1"}
test := &clientTest{
name: "ALPN",
// Note that this needs OpenSSL 1.0.2 because that is the first
// version that supports the -alpn flag.
args: []string{"-alpn", "proto1,proto2"},
config: config,
validate: func(state ConnectionState) error {
// The server's preferences should override the client.
if state.NegotiatedProtocol != "proto1" {
return fmt.Errorf("Got protocol %q, wanted proto1", state.NegotiatedProtocol)
}
return nil
},
}
runClientTestTLS12(t, test)
runClientTestTLS13(t, test)
}
func TestServerSelectingUnconfiguredApplicationProtocol(t *testing.T) {
// This checks that the server can't select an application protocol that the
// client didn't offer.
c, s := localPipe(t)
errChan := make(chan error, 1)
go func() {
client := Client(c, &Config{
ServerName: "foo",
CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
NextProtos: []string{"http", "something-else"},
})
errChan <- client.Handshake()
}()
var header [5]byte
if _, err := io.ReadFull(s, header[:]); err != nil {
t.Fatal(err)
}
recordLen := int(header[3])<<8 | int(header[4])
record := make([]byte, recordLen)
if _, err := io.ReadFull(s, record); err != nil {
t.Fatal(err)
}
serverHello := &serverHelloMsg{
vers: VersionTLS12,
random: make([]byte, 32),
cipherSuite: TLS_RSA_WITH_AES_128_GCM_SHA256,
alpnProtocol: "how-about-this",
}
serverHelloBytes := serverHello.marshal()
s.Write([]byte{
byte(recordTypeHandshake),
byte(VersionTLS12 >> 8),
byte(VersionTLS12 & 0xff),
byte(len(serverHelloBytes) >> 8),
byte(len(serverHelloBytes)),
})
s.Write(serverHelloBytes)
s.Close()
if err := <-errChan; !strings.Contains(err.Error(), "server selected unadvertised ALPN protocol") {
t.Fatalf("Expected error about unconfigured cipher suite but got %q", err)
}
}
// sctsBase64 contains data from `openssl s_client -serverinfo 18 -connect ritter.vg:443`
const sctsBase64 = "ABIBaQFnAHUApLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BAAAAFHl5nuFgAABAMARjBEAiAcS4JdlW5nW9sElUv2zvQyPoZ6ejKrGGB03gjaBZFMLwIgc1Qbbn+hsH0RvObzhS+XZhr3iuQQJY8S9G85D9KeGPAAdgBo9pj4H2SCvjqM7rkoHUz8cVFdZ5PURNEKZ6y7T0/7xAAAAUeX4bVwAAAEAwBHMEUCIDIhFDgG2HIuADBkGuLobU5a4dlCHoJLliWJ1SYT05z6AiEAjxIoZFFPRNWMGGIjskOTMwXzQ1Wh2e7NxXE1kd1J0QsAdgDuS723dc5guuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAUhcZIqHAAAEAwBHMEUCICmJ1rBT09LpkbzxtUC+Hi7nXLR0J+2PmwLp+sJMuqK+AiEAr0NkUnEVKVhAkccIFpYDqHOlZaBsuEhWWrYpg2RtKp0="
func TestHandshakClientSCTs(t *testing.T) {
config := testConfig.Clone()
scts, err := base64.StdEncoding.DecodeString(sctsBase64)
if err != nil {
t.Fatal(err)
}
// Note that this needs OpenSSL 1.0.2 because that is the first
// version that supports the -serverinfo flag.
test := &clientTest{
name: "SCT",
config: config,
extensions: [][]byte{scts},
validate: func(state ConnectionState) error {
expectedSCTs := [][]byte{
scts[8:125],
scts[127:245],
scts[247:],
}
if n := len(state.SignedCertificateTimestamps); n != len(expectedSCTs) {
return fmt.Errorf("Got %d scts, wanted %d", n, len(expectedSCTs))
}
for i, expected := range expectedSCTs {
if sct := state.SignedCertificateTimestamps[i]; !bytes.Equal(sct, expected) {
return fmt.Errorf("SCT #%d contained %x, expected %x", i, sct, expected)
}
}
return nil
},
}
runClientTestTLS12(t, test)
// TLS 1.3 moved SCTs to the Certificate extensions and -serverinfo only
// supports ServerHello extensions.
}
func TestRenegotiationRejected(t *testing.T) {
config := testConfig.Clone()
test := &clientTest{
name: "RenegotiationRejected",
args: []string{"-state"},
config: config,
numRenegotiations: 1,
renegotiationExpectedToFail: 1,
checkRenegotiationError: func(renegotiationNum int, err error) error {
if err == nil {
return errors.New("expected error from renegotiation but got nil")
}
if !strings.Contains(err.Error(), "no renegotiation") {
return fmt.Errorf("expected renegotiation to be rejected but got %q", err)
}
return nil
},
}
runClientTestTLS12(t, test)
}
func TestRenegotiateOnce(t *testing.T) {
config := testConfig.Clone()
config.Renegotiation = RenegotiateOnceAsClient
test := &clientTest{
name: "RenegotiateOnce",
args: []string{"-state"},
config: config,
numRenegotiations: 1,
}
runClientTestTLS12(t, test)
}
func TestRenegotiateTwice(t *testing.T) {
config := testConfig.Clone()
config.Renegotiation = RenegotiateFreelyAsClient
test := &clientTest{
name: "RenegotiateTwice",
args: []string{"-state"},
config: config,
numRenegotiations: 2,
}
runClientTestTLS12(t, test)
}
func TestRenegotiateTwiceRejected(t *testing.T) {
config := testConfig.Clone()
config.Renegotiation = RenegotiateOnceAsClient
test := &clientTest{
name: "RenegotiateTwiceRejected",
args: []string{"-state"},
config: config,
numRenegotiations: 2,
renegotiationExpectedToFail: 2,
checkRenegotiationError: func(renegotiationNum int, err error) error {
if renegotiationNum == 1 {
return err
}
if err == nil {
return errors.New("expected error from renegotiation but got nil")
}
if !strings.Contains(err.Error(), "no renegotiation") {
return fmt.Errorf("expected renegotiation to be rejected but got %q", err)
}
return nil
},
}
runClientTestTLS12(t, test)
}
func TestHandshakeClientExportKeyingMaterial(t *testing.T) {
test := &clientTest{
name: "ExportKeyingMaterial",
config: testConfig.Clone(),
validate: func(state ConnectionState) error {
if km, err := state.ExportKeyingMaterial("test", nil, 42); err != nil {
return fmt.Errorf("ExportKeyingMaterial failed: %v", err)
} else if len(km) != 42 {
return fmt.Errorf("Got %d bytes from ExportKeyingMaterial, wanted %d", len(km), 42)
}
return nil
},
}
runClientTestTLS10(t, test)
runClientTestTLS12(t, test)
runClientTestTLS13(t, test)
}
var hostnameInSNITests = []struct {
in, out string
}{
// Opaque string
{"", ""},
{"localhost", "localhost"},
{"foo, bar, baz and qux", "foo, bar, baz and qux"},
// DNS hostname
{"golang.org", "golang.org"},
{"golang.org.", "golang.org"},
// Literal IPv4 address
{"1.2.3.4", ""},
// Literal IPv6 address
{"::1", ""},
{"::1%lo0", ""}, // with zone identifier
{"[::1]", ""}, // as per RFC 5952 we allow the [] style as IPv6 literal
{"[::1%lo0]", ""},
}
func TestHostnameInSNI(t *testing.T) {
for _, tt := range hostnameInSNITests {
c, s := localPipe(t)
go func(host string) {
Client(c, &Config{ServerName: host, InsecureSkipVerify: true}).Handshake()
}(tt.in)
var header [5]byte
if _, err := io.ReadFull(s, header[:]); err != nil {
t.Fatal(err)
}
recordLen := int(header[3])<<8 | int(header[4])
record := make([]byte, recordLen)
if _, err := io.ReadFull(s, record[:]); err != nil {
t.Fatal(err)
}
c.Close()
s.Close()
var m clientHelloMsg
if !m.unmarshal(record) {
t.Errorf("unmarshaling ClientHello for %q failed", tt.in)
continue
}
if tt.in != tt.out && m.serverName == tt.in {
t.Errorf("prohibited %q found in ClientHello: %x", tt.in, record)
}
if m.serverName != tt.out {
t.Errorf("expected %q not found in ClientHello: %x", tt.out, record)
}
}
}
func TestServerSelectingUnconfiguredCipherSuite(t *testing.T) {
// This checks that the server can't select a cipher suite that the
// client didn't offer. See #13174.
c, s := localPipe(t)
errChan := make(chan error, 1)
go func() {
client := Client(c, &Config{
ServerName: "foo",
CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
})
errChan <- client.Handshake()
}()
var header [5]byte
if _, err := io.ReadFull(s, header[:]); err != nil {
t.Fatal(err)
}
recordLen := int(header[3])<<8 | int(header[4])
record := make([]byte, recordLen)
if _, err := io.ReadFull(s, record); err != nil {
t.Fatal(err)
}
// Create a ServerHello that selects a different cipher suite than the
// sole one that the client offered.
serverHello := &serverHelloMsg{
vers: VersionTLS12,
random: make([]byte, 32),
cipherSuite: TLS_RSA_WITH_AES_256_GCM_SHA384,
}
serverHelloBytes := serverHello.marshal()
s.Write([]byte{
byte(recordTypeHandshake),
byte(VersionTLS12 >> 8),
byte(VersionTLS12 & 0xff),
byte(len(serverHelloBytes) >> 8),
byte(len(serverHelloBytes)),
})
s.Write(serverHelloBytes)
s.Close()
if err := <-errChan; !strings.Contains(err.Error(), "unconfigured cipher") {
t.Fatalf("Expected error about unconfigured cipher suite but got %q", err)
}
}
func TestVerifyConnection(t *testing.T) {
t.Run("TLSv12", func(t *testing.T) { testVerifyConnection(t, VersionTLS12) })
t.Run("TLSv13", func(t *testing.T) { testVerifyConnection(t, VersionTLS13) })
}
func testVerifyConnection(t *testing.T, version uint16) {
checkFields := func(c ConnectionState, called *int, errorType string) error {
if c.Version != version {
return fmt.Errorf("%s: got Version %v, want %v", errorType, c.Version, version)
}
if c.HandshakeComplete {
return fmt.Errorf("%s: got HandshakeComplete, want false", errorType)
}
if c.ServerName != "example.golang" {
return fmt.Errorf("%s: got ServerName %s, want %s", errorType, c.ServerName, "example.golang")
}
if c.NegotiatedProtocol != "protocol1" {
return fmt.Errorf("%s: got NegotiatedProtocol %s, want %s", errorType, c.NegotiatedProtocol, "protocol1")
}
if c.CipherSuite == 0 {
return fmt.Errorf("%s: got CipherSuite 0, want non-zero", errorType)
}
wantDidResume := false
if *called == 2 { // if this is the second time, then it should be a resumption
wantDidResume = true
}
if c.DidResume != wantDidResume {
return fmt.Errorf("%s: got DidResume %t, want %t", errorType, c.DidResume, wantDidResume)
}
return nil
}
tests := []struct {
name string
configureServer func(*Config, *int)
configureClient func(*Config, *int)
}{
{
name: "RequireAndVerifyClientCert",
configureServer: func(config *Config, called *int) {
config.ClientAuth = RequireAndVerifyClientCert
config.VerifyConnection = func(c ConnectionState) error {
*called++
if l := len(c.PeerCertificates); l != 1 {
return fmt.Errorf("server: got len(PeerCertificates) = %d, wanted 1", l)
}
if len(c.VerifiedChains) == 0 {
return fmt.Errorf("server: got len(VerifiedChains) = 0, wanted non-zero")
}
return checkFields(c, called, "server")
}
},
configureClient: func(config *Config, called *int) {
config.VerifyConnection = func(c ConnectionState) error {
*called++
if l := len(c.PeerCertificates); l != 1 {
return fmt.Errorf("client: got len(PeerCertificates) = %d, wanted 1", l)
}
if len(c.VerifiedChains) == 0 {
return fmt.Errorf("client: got len(VerifiedChains) = 0, wanted non-zero")
}
if c.DidResume {
return nil
// The SCTs and OCSP Response are dropped on resumption.
// See http://golang.org/issue/39075.
}
if len(c.OCSPResponse) == 0 {
return fmt.Errorf("client: got len(OCSPResponse) = 0, wanted non-zero")
}
if len(c.SignedCertificateTimestamps) == 0 {
return fmt.Errorf("client: got len(SignedCertificateTimestamps) = 0, wanted non-zero")
}
return checkFields(c, called, "client")
}
},
},
{
name: "InsecureSkipVerify",
configureServer: func(config *Config, called *int) {
config.ClientAuth = RequireAnyClientCert
config.InsecureSkipVerify = true
config.VerifyConnection = func(c ConnectionState) error {
*called++
if l := len(c.PeerCertificates); l != 1 {
return fmt.Errorf("server: got len(PeerCertificates) = %d, wanted 1", l)
}
if c.VerifiedChains != nil {
return fmt.Errorf("server: got Verified Chains %v, want nil", c.VerifiedChains)
}
return checkFields(c, called, "server")
}
},
configureClient: func(config *Config, called *int) {
config.InsecureSkipVerify = true
config.VerifyConnection = func(c ConnectionState) error {
*called++
if l := len(c.PeerCertificates); l != 1 {
return fmt.Errorf("client: got len(PeerCertificates) = %d, wanted 1", l)
}
if c.VerifiedChains != nil {
return fmt.Errorf("server: got Verified Chains %v, want nil", c.VerifiedChains)
}
if c.DidResume {
return nil
// The SCTs and OCSP Response are dropped on resumption.
// See http://golang.org/issue/39075.
}
if len(c.OCSPResponse) == 0 {
return fmt.Errorf("client: got len(OCSPResponse) = 0, wanted non-zero")
}
if len(c.SignedCertificateTimestamps) == 0 {
return fmt.Errorf("client: got len(SignedCertificateTimestamps) = 0, wanted non-zero")
}
return checkFields(c, called, "client")
}
},
},
{
name: "NoClientCert",
configureServer: func(config *Config, called *int) {
config.ClientAuth = NoClientCert
config.VerifyConnection = func(c ConnectionState) error {
*called++
return checkFields(c, called, "server")
}
},
configureClient: func(config *Config, called *int) {
config.VerifyConnection = func(c ConnectionState) error {
*called++
return checkFields(c, called, "client")
}
},
},
{
name: "RequestClientCert",
configureServer: func(config *Config, called *int) {
config.ClientAuth = RequestClientCert
config.VerifyConnection = func(c ConnectionState) error {
*called++
return checkFields(c, called, "server")
}
},
configureClient: func(config *Config, called *int) {
config.Certificates = nil // clear the client cert
config.VerifyConnection = func(c ConnectionState) error {
*called++
if l := len(c.PeerCertificates); l != 1 {
return fmt.Errorf("client: got len(PeerCertificates) = %d, wanted 1", l)
}
if len(c.VerifiedChains) == 0 {
return fmt.Errorf("client: got len(VerifiedChains) = 0, wanted non-zero")
}
if c.DidResume {
return nil
// The SCTs and OCSP Response are dropped on resumption.
// See http://golang.org/issue/39075.
}
if len(c.OCSPResponse) == 0 {
return fmt.Errorf("client: got len(OCSPResponse) = 0, wanted non-zero")
}
if len(c.SignedCertificateTimestamps) == 0 {
return fmt.Errorf("client: got len(SignedCertificateTimestamps) = 0, wanted non-zero")
}
return checkFields(c, called, "client")
}
},
},
}
for _, test := range tests {
issuer, err := x509.ParseCertificate(testRSACertificateIssuer)
if err != nil {
panic(err)
}
rootCAs := x509.NewCertPool()
rootCAs.AddCert(issuer)
var serverCalled, clientCalled int
serverConfig := &Config{
MaxVersion: version,
Certificates: []Certificate{testConfig.Certificates[0]},
ClientCAs: rootCAs,
NextProtos: []string{"protocol1"},
}
serverConfig.Certificates[0].SignedCertificateTimestamps = [][]byte{[]byte("dummy sct 1"), []byte("dummy sct 2")}
serverConfig.Certificates[0].OCSPStaple = []byte("dummy ocsp")
test.configureServer(serverConfig, &serverCalled)
clientConfig := &Config{
MaxVersion: version,
ClientSessionCache: NewLRUClientSessionCache(32),
RootCAs: rootCAs,
ServerName: "example.golang",
Certificates: []Certificate{testConfig.Certificates[0]},
NextProtos: []string{"protocol1"},
}
test.configureClient(clientConfig, &clientCalled)
testHandshakeState := func(name string, didResume bool) {
_, hs, err := testHandshake(t, clientConfig, serverConfig)
if err != nil {
t.Fatalf("%s: handshake failed: %s", name, err)
}
if hs.DidResume != didResume {
t.Errorf("%s: resumed: %v, expected: %v", name, hs.DidResume, didResume)
}
wantCalled := 1
if didResume {
wantCalled = 2 // resumption would mean this is the second time it was called in this test
}
if clientCalled != wantCalled {
t.Errorf("%s: expected client VerifyConnection called %d times, did %d times", name, wantCalled, clientCalled)
}
if serverCalled != wantCalled {
t.Errorf("%s: expected server VerifyConnection called %d times, did %d times", name, wantCalled, serverCalled)
}
}
testHandshakeState(fmt.Sprintf("%s-FullHandshake", test.name), false)
testHandshakeState(fmt.Sprintf("%s-Resumption", test.name), true)
}
}
func TestVerifyPeerCertificate(t *testing.T) {
t.Run("TLSv12", func(t *testing.T) { testVerifyPeerCertificate(t, VersionTLS12) })
t.Run("TLSv13", func(t *testing.T) { testVerifyPeerCertificate(t, VersionTLS13) })
}
func testVerifyPeerCertificate(t *testing.T, version uint16) {
issuer, err := x509.ParseCertificate(testRSACertificateIssuer)
if err != nil {
panic(err)
}
rootCAs := x509.NewCertPool()
rootCAs.AddCert(issuer)
now := func() time.Time { return time.Unix(1476984729, 0) }
sentinelErr := errors.New("TestVerifyPeerCertificate")
verifyPeerCertificateCallback := func(called *bool, rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
if l := len(rawCerts); l != 1 {
return fmt.Errorf("got len(rawCerts) = %d, wanted 1", l)
}
if len(validatedChains) == 0 {
return errors.New("got len(validatedChains) = 0, wanted non-zero")
}
*called = true
return nil
}
verifyConnectionCallback := func(called *bool, isClient bool, c ConnectionState) error {
if l := len(c.PeerCertificates); l != 1 {
return fmt.Errorf("got len(PeerCertificates) = %d, wanted 1", l)
}
if len(c.VerifiedChains) == 0 {
return fmt.Errorf("got len(VerifiedChains) = 0, wanted non-zero")
}
if isClient && len(c.OCSPResponse) == 0 {
return fmt.Errorf("got len(OCSPResponse) = 0, wanted non-zero")
}
*called = true
return nil
}
tests := []struct {
configureServer func(*Config, *bool)
configureClient func(*Config, *bool)
validate func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error)
}{
{
configureServer: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyPeerCertificate = func(rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
return verifyPeerCertificateCallback(called, rawCerts, validatedChains)
}
},
configureClient: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyPeerCertificate = func(rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
return verifyPeerCertificateCallback(called, rawCerts, validatedChains)
}
},
validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
if clientErr != nil {
t.Errorf("test[%d]: client handshake failed: %v", testNo, clientErr)
}
if serverErr != nil {
t.Errorf("test[%d]: server handshake failed: %v", testNo, serverErr)
}
if !clientCalled {
t.Errorf("test[%d]: client did not call callback", testNo)
}
if !serverCalled {
t.Errorf("test[%d]: server did not call callback", testNo)
}
},
},
{
configureServer: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyPeerCertificate = func(rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
return sentinelErr
}
},
configureClient: func(config *Config, called *bool) {
config.VerifyPeerCertificate = nil
},
validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
if serverErr != sentinelErr {
t.Errorf("#%d: got server error %v, wanted sentinelErr", testNo, serverErr)
}
},
},
{
configureServer: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
},
configureClient: func(config *Config, called *bool) {
config.VerifyPeerCertificate = func(rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
return sentinelErr
}
},
validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
if clientErr != sentinelErr {
t.Errorf("#%d: got client error %v, wanted sentinelErr", testNo, clientErr)
}
},
},
{
configureServer: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
},
configureClient: func(config *Config, called *bool) {
config.InsecureSkipVerify = true
config.VerifyPeerCertificate = func(rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
if l := len(rawCerts); l != 1 {
return fmt.Errorf("got len(rawCerts) = %d, wanted 1", l)
}
// With InsecureSkipVerify set, this
// callback should still be called but
// validatedChains must be empty.
if l := len(validatedChains); l != 0 {
return fmt.Errorf("got len(validatedChains) = %d, wanted zero", l)
}
*called = true
return nil
}
},
validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
if clientErr != nil {
t.Errorf("test[%d]: client handshake failed: %v", testNo, clientErr)
}
if serverErr != nil {
t.Errorf("test[%d]: server handshake failed: %v", testNo, serverErr)
}
if !clientCalled {
t.Errorf("test[%d]: client did not call callback", testNo)
}
},
},
{
configureServer: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyConnection = func(c ConnectionState) error {
return verifyConnectionCallback(called, false, c)
}
},
configureClient: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyConnection = func(c ConnectionState) error {
return verifyConnectionCallback(called, true, c)
}
},
validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
if clientErr != nil {
t.Errorf("test[%d]: client handshake failed: %v", testNo, clientErr)
}
if serverErr != nil {
t.Errorf("test[%d]: server handshake failed: %v", testNo, serverErr)
}
if !clientCalled {
t.Errorf("test[%d]: client did not call callback", testNo)
}
if !serverCalled {
t.Errorf("test[%d]: server did not call callback", testNo)
}
},
},
{
configureServer: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyConnection = func(c ConnectionState) error {
return sentinelErr
}
},
configureClient: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyConnection = nil
},
validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
if serverErr != sentinelErr {
t.Errorf("#%d: got server error %v, wanted sentinelErr", testNo, serverErr)
}
},
},
{
configureServer: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyConnection = nil
},
configureClient: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyConnection = func(c ConnectionState) error {
return sentinelErr
}
},
validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
if clientErr != sentinelErr {
t.Errorf("#%d: got client error %v, wanted sentinelErr", testNo, clientErr)
}
},
},
{
configureServer: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyPeerCertificate = func(rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
return verifyPeerCertificateCallback(called, rawCerts, validatedChains)
}
config.VerifyConnection = func(c ConnectionState) error {
return sentinelErr
}
},
configureClient: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyPeerCertificate = nil
config.VerifyConnection = nil
},
validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
if serverErr != sentinelErr {
t.Errorf("#%d: got server error %v, wanted sentinelErr", testNo, serverErr)
}
if !serverCalled {
t.Errorf("test[%d]: server did not call callback", testNo)
}
},
},
{
configureServer: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyPeerCertificate = nil
config.VerifyConnection = nil
},
configureClient: func(config *Config, called *bool) {
config.InsecureSkipVerify = false
config.VerifyPeerCertificate = func(rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
return verifyPeerCertificateCallback(called, rawCerts, validatedChains)
}
config.VerifyConnection = func(c ConnectionState) error {
return sentinelErr
}
},
validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
if clientErr != sentinelErr {
t.Errorf("#%d: got client error %v, wanted sentinelErr", testNo, clientErr)
}
if !clientCalled {
t.Errorf("test[%d]: client did not call callback", testNo)
}
},
},
}
for i, test := range tests {
c, s := localPipe(t)
done := make(chan error)
var clientCalled, serverCalled bool
go func() {
config := testConfig.Clone()
config.ServerName = "example.golang"
config.ClientAuth = RequireAndVerifyClientCert
config.ClientCAs = rootCAs
config.Time = now
config.MaxVersion = version
config.Certificates = make([]Certificate, 1)
config.Certificates[0].Certificate = [][]byte{testRSACertificate}
config.Certificates[0].PrivateKey = testRSAPrivateKey
config.Certificates[0].SignedCertificateTimestamps = [][]byte{[]byte("dummy sct 1"), []byte("dummy sct 2")}
config.Certificates[0].OCSPStaple = []byte("dummy ocsp")
test.configureServer(config, &serverCalled)
err = Server(s, config).Handshake()
s.Close()
done <- err
}()
config := testConfig.Clone()
config.ServerName = "example.golang"
config.RootCAs = rootCAs
config.Time = now
config.MaxVersion = version
test.configureClient(config, &clientCalled)
clientErr := Client(c, config).Handshake()
c.Close()
serverErr := <-done
test.validate(t, i, clientCalled, serverCalled, clientErr, serverErr)
}
}
// brokenConn wraps a net.Conn and causes all Writes after a certain number to
// fail with brokenConnErr.
type brokenConn struct {
net.Conn
// breakAfter is the number of successful writes that will be allowed
// before all subsequent writes fail.
breakAfter int
// numWrites is the number of writes that have been done.
numWrites int
}
// brokenConnErr is the error that brokenConn returns once exhausted.
var brokenConnErr = errors.New("too many writes to brokenConn")
func (b *brokenConn) Write(data []byte) (int, error) {
if b.numWrites >= b.breakAfter {
return 0, brokenConnErr
}
b.numWrites++
return b.Conn.Write(data)
}
func TestFailedWrite(t *testing.T) {
// Test that a write error during the handshake is returned.
for _, breakAfter := range []int{0, 1} {
c, s := localPipe(t)
done := make(chan bool)
go func() {
Server(s, testConfig).Handshake()
s.Close()
done <- true
}()
brokenC := &brokenConn{Conn: c, breakAfter: breakAfter}
err := Client(brokenC, testConfig).Handshake()
if err != brokenConnErr {
t.Errorf("#%d: expected error from brokenConn but got %q", breakAfter, err)
}
brokenC.Close()
<-done
}
}
// writeCountingConn wraps a net.Conn and counts the number of Write calls.
type writeCountingConn struct {
net.Conn
// numWrites is the number of writes that have been done.
numWrites int
}
func (wcc *writeCountingConn) Write(data []byte) (int, error) {
wcc.numWrites++
return wcc.Conn.Write(data)
}
func TestBuffering(t *testing.T) {
t.Run("TLSv12", func(t *testing.T) { testBuffering(t, VersionTLS12) })
t.Run("TLSv13", func(t *testing.T) { testBuffering(t, VersionTLS13) })
}
func testBuffering(t *testing.T, version uint16) {
c, s := localPipe(t)
done := make(chan bool)
clientWCC := &writeCountingConn{Conn: c}
serverWCC := &writeCountingConn{Conn: s}
go func() {
config := testConfig.Clone()
config.MaxVersion = version
Server(serverWCC, config).Handshake()
serverWCC.Close()
done <- true
}()
err := Client(clientWCC, testConfig).Handshake()
if err != nil {
t.Fatal(err)
}
clientWCC.Close()
<-done
var expectedClient, expectedServer int
if version == VersionTLS13 {
expectedClient = 2
expectedServer = 1
} else {
expectedClient = 2
expectedServer = 2
}
if n := clientWCC.numWrites; n != expectedClient {
t.Errorf("expected client handshake to complete with %d writes, but saw %d", expectedClient, n)
}
if n := serverWCC.numWrites; n != expectedServer {
t.Errorf("expected server handshake to complete with %d writes, but saw %d", expectedServer, n)
}
}
func TestAlertFlushing(t *testing.T) {
c, s := localPipe(t)
done := make(chan bool)
clientWCC := &writeCountingConn{Conn: c}
serverWCC := &writeCountingConn{Conn: s}
serverConfig := testConfig.Clone()
// Cause a signature-time error
brokenKey := rsa.PrivateKey{PublicKey: testRSAPrivateKey.PublicKey}
brokenKey.D = big.NewInt(42)
serverConfig.Certificates = []Certificate{{
Certificate: [][]byte{testRSACertificate},
PrivateKey: &brokenKey,
}}
go func() {
Server(serverWCC, serverConfig).Handshake()
serverWCC.Close()
done <- true
}()
err := Client(clientWCC, testConfig).Handshake()
if err == nil {
t.Fatal("client unexpectedly returned no error")
}
const expectedError = "remote error: tls: internal error"
if e := err.Error(); !strings.Contains(e, expectedError) {
t.Fatalf("expected to find %q in error but error was %q", expectedError, e)
}
clientWCC.Close()
<-done
if n := serverWCC.numWrites; n != 1 {
t.Errorf("expected server handshake to complete with one write, but saw %d", n)
}
}
func TestHandshakeRace(t *testing.T) {
if testing.Short() {
t.Skip("skipping in -short mode")
}
t.Parallel()
// This test races a Read and Write to try and complete a handshake in
// order to provide some evidence that there are no races or deadlocks
// in the handshake locking.
for i := 0; i < 32; i++ {
c, s := localPipe(t)
go func() {
server := Server(s, testConfig)
if err := server.Handshake(); err != nil {
panic(err)
}
var request [1]byte
if n, err := server.Read(request[:]); err != nil || n != 1 {
panic(err)
}
server.Write(request[:])
server.Close()
}()
startWrite := make(chan struct{})
startRead := make(chan struct{})
readDone := make(chan struct{}, 1)
client := Client(c, testConfig)
go func() {
<-startWrite
var request [1]byte
client.Write(request[:])
}()
go func() {
<-startRead
var reply [1]byte
if _, err := io.ReadFull(client, reply[:]); err != nil {
panic(err)
}
c.Close()
readDone <- struct{}{}
}()
if i&1 == 1 {
startWrite <- struct{}{}
startRead <- struct{}{}
} else {
startRead <- struct{}{}
startWrite <- struct{}{}
}
<-readDone
}
}
var getClientCertificateTests = []struct {
setup func(*Config, *Config)
expectedClientError string
verify func(*testing.T, int, *ConnectionState)
}{
{
func(clientConfig, serverConfig *Config) {
// Returning a Certificate with no certificate data
// should result in an empty message being sent to the
// server.
serverConfig.ClientCAs = nil
clientConfig.GetClientCertificate = func(cri *CertificateRequestInfo) (*Certificate, error) {
if len(cri.SignatureSchemes) == 0 {
panic("empty SignatureSchemes")
}
if len(cri.AcceptableCAs) != 0 {
panic("AcceptableCAs should have been empty")
}
return new(Certificate), nil
}
},
"",
func(t *testing.T, testNum int, cs *ConnectionState) {
if l := len(cs.PeerCertificates); l != 0 {
t.Errorf("#%d: expected no certificates but got %d", testNum, l)
}
},
},
{
func(clientConfig, serverConfig *Config) {
// With TLS 1.1, the SignatureSchemes should be
// synthesised from the supported certificate types.
clientConfig.MaxVersion = VersionTLS11
clientConfig.GetClientCertificate = func(cri *CertificateRequestInfo) (*Certificate, error) {
if len(cri.SignatureSchemes) == 0 {
panic("empty SignatureSchemes")
}
return new(Certificate), nil
}
},
"",
func(t *testing.T, testNum int, cs *ConnectionState) {
if l := len(cs.PeerCertificates); l != 0 {
t.Errorf("#%d: expected no certificates but got %d", testNum, l)
}
},
},
{
func(clientConfig, serverConfig *Config) {
// Returning an error should abort the handshake with
// that error.
clientConfig.GetClientCertificate = func(cri *CertificateRequestInfo) (*Certificate, error) {
return nil, errors.New("GetClientCertificate")
}
},
"GetClientCertificate",
func(t *testing.T, testNum int, cs *ConnectionState) {
},
},
{
func(clientConfig, serverConfig *Config) {
clientConfig.GetClientCertificate = func(cri *CertificateRequestInfo) (*Certificate, error) {
if len(cri.AcceptableCAs) == 0 {
panic("empty AcceptableCAs")
}
cert := &Certificate{
Certificate: [][]byte{testRSACertificate},
PrivateKey: testRSAPrivateKey,
}
return cert, nil
}
},
"",
func(t *testing.T, testNum int, cs *ConnectionState) {
if len(cs.VerifiedChains) == 0 {
t.Errorf("#%d: expected some verified chains, but found none", testNum)
}
},
},
}
func TestGetClientCertificate(t *testing.T) {
t.Run("TLSv12", func(t *testing.T) { testGetClientCertificate(t, VersionTLS12) })
t.Run("TLSv13", func(t *testing.T) { testGetClientCertificate(t, VersionTLS13) })
}
func testGetClientCertificate(t *testing.T, version uint16) {
issuer, err := x509.ParseCertificate(testRSACertificateIssuer)
if err != nil {
panic(err)
}
for i, test := range getClientCertificateTests {
serverConfig := testConfig.Clone()
serverConfig.ClientAuth = VerifyClientCertIfGiven
serverConfig.RootCAs = x509.NewCertPool()
serverConfig.RootCAs.AddCert(issuer)
serverConfig.ClientCAs = serverConfig.RootCAs
serverConfig.Time = func() time.Time { return time.Unix(1476984729, 0) }
serverConfig.MaxVersion = version
clientConfig := testConfig.Clone()
clientConfig.MaxVersion = version
test.setup(clientConfig, serverConfig)
type serverResult struct {
cs ConnectionState
err error
}
c, s := localPipe(t)
done := make(chan serverResult)
go func() {
defer s.Close()
server := Server(s, serverConfig)
err := server.Handshake()
var cs ConnectionState
if err == nil {
cs = server.ConnectionState()
}
done <- serverResult{cs, err}
}()
clientErr := Client(c, clientConfig).Handshake()
c.Close()
result := <-done
if clientErr != nil {
if len(test.expectedClientError) == 0 {
t.Errorf("#%d: client error: %v", i, clientErr)
} else if got := clientErr.Error(); got != test.expectedClientError {
t.Errorf("#%d: expected client error %q, but got %q", i, test.expectedClientError, got)
} else {
test.verify(t, i, &result.cs)
}
} else if len(test.expectedClientError) > 0 {
t.Errorf("#%d: expected client error %q, but got no error", i, test.expectedClientError)
} else if err := result.err; err != nil {
t.Errorf("#%d: server error: %v", i, err)
} else {
test.verify(t, i, &result.cs)
}
}
}
func TestRSAPSSKeyError(t *testing.T) {
// crypto/tls does not support the rsa_pss_pss_* SignatureSchemes. If support for
// public keys with OID RSASSA-PSS is added to crypto/x509, they will be misused with
// the rsa_pss_rsae_* SignatureSchemes. Assert that RSASSA-PSS certificates don't
// parse, or that they don't carry *rsa.PublicKey keys.
b, _ := pem.Decode([]byte(`
-----BEGIN CERTIFICATE-----
MIIDZTCCAhygAwIBAgIUCF2x0FyTgZG0CC9QTDjGWkB5vgEwPgYJKoZIhvcNAQEK
MDGgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogQC
AgDeMBIxEDAOBgNVBAMMB1JTQS1QU1MwHhcNMTgwNjI3MjI0NDM2WhcNMTgwNzI3
MjI0NDM2WjASMRAwDgYDVQQDDAdSU0EtUFNTMIIBIDALBgkqhkiG9w0BAQoDggEP
ADCCAQoCggEBANxDm0f76JdI06YzsjB3AmmjIYkwUEGxePlafmIASFjDZl/elD0Z
/a7xLX468b0qGxLS5al7XCcEprSdsDR6DF5L520+pCbpfLyPOjuOvGmk9KzVX4x5
b05YXYuXdsQ0Kjxcx2i3jjCday6scIhMJVgBZxTEyMj1thPQM14SHzKCd/m6HmCL
QmswpH2yMAAcBRWzRpp/vdH5DeOJEB3aelq7094no731mrLUCHRiZ1htq8BDB3ou
czwqgwspbqZ4dnMXl2MvfySQ5wJUxQwILbiuAKO2lVVPUbFXHE9pgtznNoPvKwQT
JNcX8ee8WIZc2SEGzofjk3NpjR+2ADB2u3sCAwEAAaNTMFEwHQYDVR0OBBYEFNEz
AdyJ2f+fU+vSCS6QzohnOnprMB8GA1UdIwQYMBaAFNEzAdyJ2f+fU+vSCS6Qzohn
OnprMA8GA1UdEwEB/wQFMAMBAf8wPgYJKoZIhvcNAQEKMDGgDTALBglghkgBZQME
AgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogQCAgDeA4IBAQCjEdrR5aab
sZmCwrMeKidXgfkmWvfuLDE+TCbaqDZp7BMWcMQXT9O0UoUT5kqgKj2ARm2pEW0Z
H3Z1vj3bbds72qcDIJXp+l0fekyLGeCrX/CbgnMZXEP7+/+P416p34ChR1Wz4dU1
KD3gdsUuTKKeMUog3plxlxQDhRQmiL25ygH1LmjLd6dtIt0GVRGr8lj3euVeprqZ
bZ3Uq5eLfsn8oPgfC57gpO6yiN+UURRTlK3bgYvLh4VWB3XXk9UaQZ7Mq1tpXjoD
HYFybkWzibkZp4WRo+Fa28rirH+/wHt0vfeN7UCceURZEx4JaxIIfe4ku7uDRhJi
RwBA9Xk1KBNF
-----END CERTIFICATE-----`))
if b == nil {
t.Fatal("Failed to decode certificate")
}
cert, err := x509.ParseCertificate(b.Bytes)
if err != nil {
return
}
if _, ok := cert.PublicKey.(*rsa.PublicKey); ok {
t.Error("A RSASSA-PSS certificate was parsed like a PKCS#1 v1.5 one, and it will be mistakenly used with rsa_pss_rsae_* signature algorithms")
}
}
func TestCloseClientConnectionOnIdleServer(t *testing.T) {
clientConn, serverConn := localPipe(t)
client := Client(clientConn, testConfig.Clone())
go func() {
var b [1]byte
serverConn.Read(b[:])
client.Close()
}()
client.SetWriteDeadline(time.Now().Add(time.Minute))
err := client.Handshake()
if err != nil {
if err, ok := err.(net.Error); ok && err.Timeout() {
t.Errorf("Expected a closed network connection error but got '%s'", err.Error())
}
} else {
t.Errorf("Error expected, but no error returned")
}
}
func testDowngradeCanary(t *testing.T, clientVersion, serverVersion uint16) error {
defer func() { testingOnlyForceDowngradeCanary = false }()
testingOnlyForceDowngradeCanary = true
clientConfig := testConfig.Clone()
clientConfig.MaxVersion = clientVersion
serverConfig := testConfig.Clone()
serverConfig.MaxVersion = serverVersion
_, _, err := testHandshake(t, clientConfig, serverConfig)
return err
}
func TestDowngradeCanary(t *testing.T) {
if err := testDowngradeCanary(t, VersionTLS13, VersionTLS12); err == nil {
t.Errorf("downgrade from TLS 1.3 to TLS 1.2 was not detected")
}
if testing.Short() {
t.Skip("skipping the rest of the checks in short mode")
}
if err := testDowngradeCanary(t, VersionTLS13, VersionTLS11); err == nil {
t.Errorf("downgrade from TLS 1.3 to TLS 1.1 was not detected")
}
if err := testDowngradeCanary(t, VersionTLS13, VersionTLS10); err == nil {
t.Errorf("downgrade from TLS 1.3 to TLS 1.0 was not detected")
}
if err := testDowngradeCanary(t, VersionTLS12, VersionTLS11); err == nil {
t.Errorf("downgrade from TLS 1.2 to TLS 1.1 was not detected")
}
if err := testDowngradeCanary(t, VersionTLS12, VersionTLS10); err == nil {
t.Errorf("downgrade from TLS 1.2 to TLS 1.0 was not detected")
}
if err := testDowngradeCanary(t, VersionTLS13, VersionTLS13); err != nil {
t.Errorf("server unexpectedly sent downgrade canary for TLS 1.3")
}
if err := testDowngradeCanary(t, VersionTLS12, VersionTLS12); err != nil {
t.Errorf("client didn't ignore expected TLS 1.2 canary")
}
if err := testDowngradeCanary(t, VersionTLS11, VersionTLS11); err != nil {
t.Errorf("client unexpectedly reacted to a canary in TLS 1.1")
}
if err := testDowngradeCanary(t, VersionTLS10, VersionTLS10); err != nil {
t.Errorf("client unexpectedly reacted to a canary in TLS 1.0")
}
}
func TestResumptionKeepsOCSPAndSCT(t *testing.T) {
t.Run("TLSv12", func(t *testing.T) { testResumptionKeepsOCSPAndSCT(t, VersionTLS12) })
t.Run("TLSv13", func(t *testing.T) { testResumptionKeepsOCSPAndSCT(t, VersionTLS13) })
}
func testResumptionKeepsOCSPAndSCT(t *testing.T, ver uint16) {
issuer, err := x509.ParseCertificate(testRSACertificateIssuer)
if err != nil {
t.Fatalf("failed to parse test issuer")
}
roots := x509.NewCertPool()
roots.AddCert(issuer)
clientConfig := &Config{
MaxVersion: ver,
ClientSessionCache: NewLRUClientSessionCache(32),
ServerName: "example.golang",
RootCAs: roots,
}
serverConfig := testConfig.Clone()
serverConfig.MaxVersion = ver
serverConfig.Certificates[0].OCSPStaple = []byte{1, 2, 3}
serverConfig.Certificates[0].SignedCertificateTimestamps = [][]byte{{4, 5, 6}}
_, ccs, err := testHandshake(t, clientConfig, serverConfig)
if err != nil {
t.Fatalf("handshake failed: %s", err)
}
// after a new session we expect to see OCSPResponse and
// SignedCertificateTimestamps populated as usual
if !bytes.Equal(ccs.OCSPResponse, serverConfig.Certificates[0].OCSPStaple) {
t.Errorf("client ConnectionState contained unexpected OCSPResponse: wanted %v, got %v",
serverConfig.Certificates[0].OCSPStaple, ccs.OCSPResponse)
}
if !reflect.DeepEqual(ccs.SignedCertificateTimestamps, serverConfig.Certificates[0].SignedCertificateTimestamps) {
t.Errorf("client ConnectionState contained unexpected SignedCertificateTimestamps: wanted %v, got %v",
serverConfig.Certificates[0].SignedCertificateTimestamps, ccs.SignedCertificateTimestamps)
}
// if the server doesn't send any SCTs, repopulate the old SCTs
oldSCTs := serverConfig.Certificates[0].SignedCertificateTimestamps
serverConfig.Certificates[0].SignedCertificateTimestamps = nil
_, ccs, err = testHandshake(t, clientConfig, serverConfig)
if err != nil {
t.Fatalf("handshake failed: %s", err)
}
if !ccs.DidResume {
t.Fatalf("expected session to be resumed")
}
// after a resumed session we also expect to see OCSPResponse
// and SignedCertificateTimestamps populated
if !bytes.Equal(ccs.OCSPResponse, serverConfig.Certificates[0].OCSPStaple) {
t.Errorf("client ConnectionState contained unexpected OCSPResponse after resumption: wanted %v, got %v",
serverConfig.Certificates[0].OCSPStaple, ccs.OCSPResponse)
}
if !reflect.DeepEqual(ccs.SignedCertificateTimestamps, oldSCTs) {
t.Errorf("client ConnectionState contained unexpected SignedCertificateTimestamps after resumption: wanted %v, got %v",
oldSCTs, ccs.SignedCertificateTimestamps)
}
// Only test overriding the SCTs for TLS 1.2, since in 1.3
// the server won't send the message containing them
if ver == VersionTLS13 {
return
}
// if the server changes the SCTs it sends, they should override the saved SCTs
serverConfig.Certificates[0].SignedCertificateTimestamps = [][]byte{{7, 8, 9}}
_, ccs, err = testHandshake(t, clientConfig, serverConfig)
if err != nil {
t.Fatalf("handshake failed: %s", err)
}
if !ccs.DidResume {
t.Fatalf("expected session to be resumed")
}
if !reflect.DeepEqual(ccs.SignedCertificateTimestamps, serverConfig.Certificates[0].SignedCertificateTimestamps) {
t.Errorf("client ConnectionState contained unexpected SignedCertificateTimestamps after resumption: wanted %v, got %v",
serverConfig.Certificates[0].SignedCertificateTimestamps, ccs.SignedCertificateTimestamps)
}
}
// TestClientHandshakeContextCancellation tests that cancelling
// the context given to the client side conn.HandshakeContext
// interrupts the in-progress handshake.
func TestClientHandshakeContextCancellation(t *testing.T) {
c, s := localPipe(t)
ctx, cancel := context.WithCancel(context.Background())
unblockServer := make(chan struct{})
defer close(unblockServer)
go func() {
cancel()
<-unblockServer
_ = s.Close()
}()
cli := Client(c, testConfig)
// Initiates client side handshake, which will block until the client hello is read
// by the server, unless the cancellation works.
err := cli.HandshakeContext(ctx)
if err == nil {
t.Fatal("Client handshake did not error when the context was canceled")
}
if err != context.Canceled {
t.Errorf("Unexpected client handshake error: %v", err)
}
if runtime.GOARCH == "wasm" {
t.Skip("conn.Close does not error as expected when called multiple times on WASM")
}
err = cli.Close()
if err == nil {
t.Error("Client connection was not closed when the context was canceled")
}
}