utls/handshake_client_test.go
Daniel McCarney 4cb059fbbf crypto/tls: test with FIPS 140-3 TLS mode
For tests that are interested in testing the difference between TLS in
FIPS 140-3 required mode or otherwise two new helpers are introduced,
runWithFIPSEnabled and runWithFIPSDisabled. They take care of forcing
the correct TLS FIPS 140-3 state regardless of the overal GODEBUG=fips
state, and restoring it afterwards.

For the tests that use features or test data not appropriate for
TLS in FIPS 140-3 required mode we add skips. For some tests we can make
them appropriate for both TLS FIPS 140-3 required or not by tweaking some
parameters that weren't important to the subject under test, but would
otherwise preclude TLS FIPS 140-3 required mode (e.g. because they used
TLS 1.0 when the test could use TLS 1.2 instead). For others, switching
test certificates to a RSA 2048 hierarchy is sufficient. We avoid
regenerating the existing RSA 1024 certs as 2048 since it would
invalidate recorded static flow data.

Tests that rely on static message flows (primarily the client and server
handshake) tests are skipped due to FIPS mode being non-deterministic
and inappropriate for this style of testing.

Change-Id: I311f3828dac890bb3ff8ebda6ed73d50f0797110
Reviewed-on: https://go-review.googlesource.com/c/go/+/629736
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
2024-11-22 00:00:26 +00:00

2924 lines
89 KiB
Go

// 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/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls/internal/fips140tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"internal/byteorder"
"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", byteorder.BEUint16(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 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 {
flows, err := test.loadData()
if err != nil {
t.Fatalf("failed to load data from %s: %v", test.dataPath(), err)
}
clientConn = &replayingConn{t: t, flows: flows, reading: false}
}
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 {
client.Close()
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,
validate: func(cs ConnectionState) error {
if !cs.testingOnlyDidHRR {
return errors.New("expected HelloRetryRequest")
}
return nil
},
}
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")
}
// Note: using RSA 2048 test certificates because they are compatible with FIPS mode.
testCertificates := []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}}
serverConfig := &Config{
MaxVersion: version,
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
Certificates: testCertificates,
}
issuer, err := x509.ParseCertificate(testRSA2048CertificateIssuer)
if err != nil {
panic(err)
}
rootCAs := x509.NewCertPool()
rootCAs.AddCert(issuer)
clientConfig := &Config{
MaxVersion: version,
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
ClientSessionCache: NewLRUClientSessionCache(32),
RootCAs: rootCAs,
ServerName: "example.golang",
}
testResumeState := func(test string, didResume bool) {
t.Helper()
_, 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.session.ticket
}
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.session.secret[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()) {
t.Fatal("ticket didn't change after resumption")
}
// An old session ticket is replaced with a ticket encrypted with a fresh key.
ticket = getTicket()
serverConfig.Time = func() time.Time { return time.Now().Add(24*time.Hour + time.Minute) }
testResumeState("ResumeWithOldTicket", true)
if bytes.Equal(ticket, getTicket()) {
t.Fatal("old first ticket matches the fresh one")
}
// Once the session master secret is expired, a full handshake should occur.
ticket = getTicket()
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
serverConfig.Time = func() time.Time { return time.Now().Add(d) }
deleteTicket()
testResumeState("GetFreshSessionTicket", false)
for i := 0; i < 13; i++ {
d += 12 * time.Hour
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
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_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
Certificates: testCertificates,
}
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_AES_256_GCM_SHA384}
testResumeState("DifferentCipherSuite", false)
testResumeState("DifferentCipherSuiteRecovers", true)
}
deleteTicket()
testResumeState("WithoutSessionTicket", false)
// In TLS 1.3, HelloRetryRequest is sent after incorrect key share.
// See https://www.rfc-editor.org/rfc/rfc8446#page-14.
if version == VersionTLS13 {
deleteTicket()
serverConfig = &Config{
// Use a different curve than the client to force a HelloRetryRequest.
CurvePreferences: []CurveID{CurveP521, CurveP384, CurveP256},
MaxVersion: version,
Certificates: testCertificates,
}
testResumeState("InitialHandshake", false)
testResumeState("WithHelloRetryRequest", true)
// Reset serverConfig back.
serverConfig = &Config{
MaxVersion: version,
CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
Certificates: testCertificates,
}
}
// 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)
clientConfig.ClientSessionCache = &serializingClientCache{t: t}
testResumeState("BeforeSerializingCache", false)
testResumeState("WithSerializingCache", true)
}
type serializingClientCache struct {
t *testing.T
ticket, state []byte
}
func (c *serializingClientCache) Get(sessionKey string) (session *ClientSessionState, ok bool) {
if c.ticket == nil {
return nil, false
}
state, err := ParseSessionState(c.state)
if err != nil {
c.t.Error(err)
return nil, false
}
cs, err := NewResumptionState(c.ticket, state)
if err != nil {
c.t.Error(err)
return nil, false
}
return cs, true
}
func (c *serializingClientCache) Put(sessionKey string, cs *ClientSessionState) {
if cs == nil {
c.ticket, c.state = nil, nil
return
}
ticket, state, err := cs.ResumptionState()
if err != nil {
c.t.Error(err)
return
}
stateBytes, err := state.Bytes()
if err != nil {
c.t.Error(err)
return
}
c.ticket, c.state = ticket, stateBytes
}
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_ECDHE_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_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
alpnProtocol: "how-about-this",
}
serverHelloBytes := mustMarshal(t, serverHello)
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 ALPN protocol 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 := mustMarshal(t, serverHello)
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 {
// Note: using RSA 2048 test certificates because they are compatible with FIPS mode.
testCertificates := []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}}
issuer, err := x509.ParseCertificate(testRSA2048CertificateIssuer)
if err != nil {
panic(err)
}
rootCAs := x509.NewCertPool()
rootCAs.AddCert(issuer)
var serverCalled, clientCalled int
serverConfig := &Config{
MaxVersion: version,
Certificates: testCertificates,
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: testCertificates,
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) {
// Note: using RSA 2048 test certificates because they are compatible with FIPS mode.
issuer, err := x509.ParseCertificate(testRSA2048CertificateIssuer)
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{testRSA2048Certificate}
config.Certificates[0].PrivateKey = testRSA2048PrivateKey
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.Certificates = []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}}
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{testRSA2048Certificate},
PrivateKey: testRSA2048PrivateKey,
}
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) {
// Note: using RSA 2048 test certificates because they are compatible with FIPS mode.
issuer, err := x509.ParseCertificate(testRSA2048CertificateIssuer)
if err != nil {
panic(err)
}
for i, test := range getClientCertificateTests {
serverConfig := testConfig.Clone()
serverConfig.Certificates = []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}}
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.Certificates = []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}}
clientConfig.MaxVersion = version
test.setup(clientConfig, serverConfig)
// TLS 1.1 isn't available for FIPS required
if fips140tls.Required() && clientConfig.MaxVersion == VersionTLS11 {
t.Logf("skipping test %d for FIPS mode", i)
continue
}
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 !fips140tls.Required() {
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")
}
} else {
t.Logf("skiping TLS 1.1 and TLS 1.0 downgrade canary checks in FIPS mode")
}
}
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) {
// Note: using RSA 2048 test certificates because they are compatible with FIPS mode.
issuer, err := x509.ParseCertificate(testRSA2048CertificateIssuer)
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.Certificates = []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}}
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 canceling
// 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")
}
}
// TestTLS13OnlyClientHelloCipherSuite tests that when a client states that
// it only supports TLS 1.3, it correctly advertises only TLS 1.3 ciphers.
func TestTLS13OnlyClientHelloCipherSuite(t *testing.T) {
tls13Tests := []struct {
name string
ciphers []uint16
}{
{
name: "nil",
ciphers: nil,
},
{
name: "empty",
ciphers: []uint16{},
},
{
name: "some TLS 1.2 cipher",
ciphers: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
},
{
name: "some TLS 1.3 cipher",
ciphers: []uint16{TLS_AES_128_GCM_SHA256},
},
{
name: "some TLS 1.2 and 1.3 ciphers",
ciphers: []uint16{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_AES_256_GCM_SHA384},
},
}
for _, tt := range tls13Tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
testTLS13OnlyClientHelloCipherSuite(t, tt.ciphers)
})
}
}
func testTLS13OnlyClientHelloCipherSuite(t *testing.T, ciphers []uint16) {
serverConfig := &Config{
Certificates: testConfig.Certificates,
GetConfigForClient: func(chi *ClientHelloInfo) (*Config, error) {
expectedCiphersuites := defaultCipherSuitesTLS13NoAES
if fips140tls.Required() {
expectedCiphersuites = defaultCipherSuitesTLS13FIPS
}
if len(chi.CipherSuites) != len(expectedCiphersuites) {
t.Errorf("only TLS 1.3 suites should be advertised, got=%x", chi.CipherSuites)
} else {
for i := range expectedCiphersuites {
if want, got := expectedCiphersuites[i], chi.CipherSuites[i]; want != got {
t.Errorf("cipher at index %d does not match, want=%x, got=%x", i, want, got)
}
}
}
return nil, nil
},
}
clientConfig := &Config{
MinVersion: VersionTLS13, // client only supports TLS 1.3
CipherSuites: ciphers,
InsecureSkipVerify: true,
}
if _, _, err := testHandshake(t, clientConfig, serverConfig); err != nil {
t.Fatalf("handshake failed: %s", err)
}
}
// discardConn wraps a net.Conn but discards all writes, but reports that they happened.
type discardConn struct {
net.Conn
}
func (dc *discardConn) Write(data []byte) (int, error) {
return len(data), nil
}
// largeRSAKeyCertPEM contains a 8193 bit RSA key
const largeRSAKeyCertPEM = `-----BEGIN CERTIFICATE-----
MIIInjCCBIWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDEwd0ZXN0
aW5nMB4XDTIzMDYwNzIxMjMzNloXDTIzMDYwNzIzMjMzNlowEjEQMA4GA1UEAxMH
dGVzdGluZzCCBCIwDQYJKoZIhvcNAQEBBQADggQPADCCBAoCggQBAWdHsf6Rh2Ca
n2SQwn4t4OQrOjbLLdGE1pM6TBKKrHUFy62uEL8atNjlcfXIsa4aEu3xNGiqxqur
ZectlkZbm0FkaaQ1Wr9oikDY3KfjuaXdPdO/XC/h8AKNxlDOylyXwUSK/CuYb+1j
gy8yF5QFvVfwW/xwTlHmhUeSkVSQPosfQ6yXNNsmMzkd+ZPWLrfq4R+wiNtwYGu0
WSBcI/M9o8/vrNLnIppoiBJJ13j9CR1ToEAzOFh9wwRWLY10oZhoh1ONN1KQURx4
qedzvvP2DSjZbUccdvl2rBGvZpzfOiFdm1FCnxB0c72Cqx+GTHXBFf8bsa7KHky9
sNO1GUanbq17WoDNgwbY6H51bfShqv0CErxatwWox3we4EcAmFHPVTCYL1oWVMGo
a3Eth91NZj+b/nGhF9lhHKGzXSv9brmLLkfvM1jA6XhNhA7BQ5Vz67lj2j3XfXdh
t/BU5pBXbL4Ut4mIhT1YnKXAjX2/LF5RHQTE8Vwkx5JAEKZyUEGOReD/B+7GOrLp
HduMT9vZAc5aR2k9I8qq1zBAzsL69lyQNAPaDYd1BIAjUety9gAYaSQffCgAgpRO
Gt+DYvxS+7AT/yEd5h74MU2AH7KrAkbXOtlwupiGwhMVTstncDJWXMJqbBhyHPF8
3UmZH0hbL4PYmzSj9LDWQQXI2tv6vrCpfts3Cqhqxz9vRpgY7t1Wu6l/r+KxYYz3
1pcGpPvRmPh0DJm7cPTiXqPnZcPt+ulSaSdlxmd19OnvG5awp0fXhxryZVwuiT8G
VDkhyARrxYrdjlINsZJZbQjO0t8ketXAELJOnbFXXzeCOosyOHkLwsqOO96AVJA8
45ZVL5m95ClGy0RSrjVIkXsxTAMVG6SPAqKwk6vmTdRGuSPS4rhgckPVDHmccmuq
dfnT2YkX+wB2/M3oCgU+s30fAHGkbGZ0pCdNbFYFZLiH0iiMbTDl/0L/z7IdK0nH
GLHVE7apPraKC6xl6rPWsD2iSfrmtIPQa0+rqbIVvKP5JdfJ8J4alI+OxFw/znQe
V0/Rez0j22Fe119LZFFSXhRv+ZSvcq20xDwh00mzcumPWpYuCVPozA18yIhC9tNn
ALHndz0tDseIdy9vC71jQWy9iwri3ueN0DekMMF8JGzI1Z6BAFzgyAx3DkHtwHg7
B7qD0jPG5hJ5+yt323fYgJsuEAYoZ8/jzZ01pkX8bt+UsVN0DGnSGsI2ktnIIk3J
l+8krjmUy6EaW79nITwoOqaeHOIp8m3UkjEcoKOYrzHRKqRy+A09rY+m/cAQaafW
4xp0Zv7qZPLwnu0jsqB4jD8Ll9yPB02ndsoV6U5PeHzTkVhPml19jKUAwFfs7TJg
kXy+/xFhYVUCAwEAATANBgkqhkiG9w0BAQsFAAOCBAIAAQnZY77pMNeypfpba2WK
aDasT7dk2JqP0eukJCVPTN24Zca+xJNPdzuBATm/8SdZK9lddIbjSnWRsKvTnO2r
/rYdlPf3jM5uuJtb8+Uwwe1s+gszelGS9G/lzzq+ehWicRIq2PFcs8o3iQMfENiv
qILJ+xjcrvms5ZPDNahWkfRx3KCg8Q+/at2n5p7XYjMPYiLKHnDC+RE2b1qT20IZ
FhuK/fTWLmKbfYFNNga6GC4qcaZJ7x0pbm4SDTYp0tkhzcHzwKhidfNB5J2vNz6l
Ur6wiYwamFTLqcOwWo7rdvI+sSn05WQBv0QZlzFX+OAu0l7WQ7yU+noOxBhjvHds
14+r9qcQZg2q9kG+evopYZqYXRUNNlZKo9MRBXhfrISulFAc5lRFQIXMXnglvAu+
Ipz2gomEAOcOPNNVldhKAU94GAMJd/KfN0ZP7gX3YvPzuYU6XDhag5RTohXLm18w
5AF+ES3DOQ6ixu3DTf0D+6qrDuK+prdX8ivcdTQVNOQ+MIZeGSc6NWWOTaMGJ3lg
aZIxJUGdo6E7GBGiC1YTjgFKFbHzek1LRTh/LX3vbSudxwaG0HQxwsU9T4DWiMqa
Fkf2KteLEUA6HrR+0XlAZrhwoqAmrJ+8lCFX3V0gE9lpENfVHlFXDGyx10DpTB28
DdjnY3F7EPWNzwf9P3oNT69CKW3Bk6VVr3ROOJtDxVu1ioWo3TaXltQ0VOnap2Pu
sa5wfrpfwBDuAS9JCDg4ttNp2nW3F7tgXC6xPqw5pvGwUppEw9XNrqV8TZrxduuv
rQ3NyZ7KSzIpmFlD3UwV/fGfz3UQmHS6Ng1evrUID9DjfYNfRqSGIGjDfxGtYD+j
Z1gLJZuhjJpNtwBkKRtlNtrCWCJK2hidK/foxwD7kwAPo2I9FjpltxCRywZUs07X
KwXTfBR9v6ij1LV6K58hFS+8ezZyZ05CeVBFkMQdclTOSfuPxlMkQOtjp8QWDj+F
j/MYziT5KBkHvcbrjdRtUJIAi4N7zCsPZtjik918AK1WBNRVqPbrgq/XSEXMfuvs
6JbfK0B76vdBDRtJFC1JsvnIrGbUztxXzyQwFLaR/AjVJqpVlysLWzPKWVX6/+SJ
u1NQOl2E8P6ycyBsuGnO89p0S4F8cMRcI2X1XQsZ7/q0NBrOMaEp5T3SrWo9GiQ3
o2SBdbs3Y6MBPBtTu977Z/0RO63J3M5i2tjUiDfrFy7+VRLKr7qQ7JibohyB8QaR
9tedgjn2f+of7PnP/PEl1cCphUZeHM7QKUMPT8dbqwmKtlYY43EHXcvNOT5IBk3X
9lwJoZk/B2i+ZMRNSP34ztAwtxmasPt6RAWGQpWCn9qmttAHAnMfDqe7F7jVR6rS
u58=
-----END CERTIFICATE-----`
func TestHandshakeRSATooBig(t *testing.T) {
testCert, _ := pem.Decode([]byte(largeRSAKeyCertPEM))
c := &Conn{conn: &discardConn{}, config: testConfig.Clone()}
expectedErr := "tls: server sent certificate containing RSA key larger than 8192 bits"
err := c.verifyServerCertificate([][]byte{testCert.Bytes})
if err == nil || err.Error() != expectedErr {
t.Errorf("Conn.verifyServerCertificate unexpected error: want %q, got %q", expectedErr, err)
}
expectedErr = "tls: client sent certificate containing RSA key larger than 8192 bits"
err = c.processCertsFromClient(Certificate{Certificate: [][]byte{testCert.Bytes}})
if err == nil || err.Error() != expectedErr {
t.Errorf("Conn.processCertsFromClient unexpected error: want %q, got %q", expectedErr, err)
}
}
func TestTLS13ECHRejectionCallbacks(t *testing.T) {
k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "test"},
DNSNames: []string{"example.golang"},
NotBefore: testConfig.Time().Add(-time.Hour),
NotAfter: testConfig.Time().Add(time.Hour),
}
certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, k.Public(), k)
if err != nil {
t.Fatal(err)
}
cert, err := x509.ParseCertificate(certDER)
if err != nil {
t.Fatal(err)
}
clientConfig, serverConfig := testConfig.Clone(), testConfig.Clone()
serverConfig.Certificates = []Certificate{
{
Certificate: [][]byte{certDER},
PrivateKey: k,
},
}
serverConfig.MinVersion = VersionTLS13
clientConfig.RootCAs = x509.NewCertPool()
clientConfig.RootCAs.AddCert(cert)
clientConfig.MinVersion = VersionTLS13
clientConfig.EncryptedClientHelloConfigList, _ = hex.DecodeString("0041fe0d003d0100200020204bed0a11fc0dde595a9b78d966b0011128eb83f65d3c91c1cc5ac786cd246f000400010001ff0e6578616d706c652e676f6c616e670000")
clientConfig.ServerName = "example.golang"
for _, tc := range []struct {
name string
expectedErr string
verifyConnection func(ConnectionState) error
verifyPeerCertificate func([][]byte, [][]*x509.Certificate) error
encryptedClientHelloRejectionVerify func(ConnectionState) error
}{
{
name: "no callbacks",
expectedErr: "tls: server rejected ECH",
},
{
name: "EncryptedClientHelloRejectionVerify, no err",
encryptedClientHelloRejectionVerify: func(ConnectionState) error {
return nil
},
expectedErr: "tls: server rejected ECH",
},
{
name: "EncryptedClientHelloRejectionVerify, err",
encryptedClientHelloRejectionVerify: func(ConnectionState) error {
return errors.New("callback err")
},
// testHandshake returns the server side error, so we just need to
// check alertBadCertificate was sent
expectedErr: "callback err",
},
{
name: "VerifyConnection, err",
verifyConnection: func(ConnectionState) error {
return errors.New("callback err")
},
expectedErr: "tls: server rejected ECH",
},
{
name: "VerifyPeerCertificate, err",
verifyPeerCertificate: func([][]byte, [][]*x509.Certificate) error {
return errors.New("callback err")
},
expectedErr: "tls: server rejected ECH",
},
} {
t.Run(tc.name, func(t *testing.T) {
c, s := localPipe(t)
done := make(chan error)
go func() {
serverErr := Server(s, serverConfig).Handshake()
s.Close()
done <- serverErr
}()
cConfig := clientConfig.Clone()
cConfig.VerifyConnection = tc.verifyConnection
cConfig.VerifyPeerCertificate = tc.verifyPeerCertificate
cConfig.EncryptedClientHelloRejectionVerify = tc.encryptedClientHelloRejectionVerify
clientErr := Client(c, cConfig).Handshake()
c.Close()
if tc.expectedErr == "" && clientErr != nil {
t.Fatalf("unexpected err: %s", clientErr)
} else if clientErr != nil && tc.expectedErr != clientErr.Error() {
t.Fatalf("unexpected err: got %q, want %q", clientErr, tc.expectedErr)
}
})
}
}
func TestECHTLS12Server(t *testing.T) {
clientConfig, serverConfig := testConfig.Clone(), testConfig.Clone()
serverConfig.MaxVersion = VersionTLS12
clientConfig.MinVersion = 0
clientConfig.EncryptedClientHelloConfigList, _ = hex.DecodeString("0041fe0d003d0100200020204bed0a11fc0dde595a9b78d966b0011128eb83f65d3c91c1cc5ac786cd246f000400010001ff0e6578616d706c652e676f6c616e670000")
expectedErr := "server: tls: client offered only unsupported versions: [304]\nclient: remote error: tls: protocol version not supported"
_, _, err := testHandshake(t, clientConfig, serverConfig)
if err == nil || err.Error() != expectedErr {
t.Fatalf("unexpected handshake error: got %q, want %q", err, expectedErr)
}
}