Inital import.

Initial fork from f2d678e6e89b6508147086610e985d4e8416e867 (1.0.2 beta).

(This change contains substantial changes from the original and
effectively starts a new history.)
diff --git a/ssl/test/runner/alert.go b/ssl/test/runner/alert.go
new file mode 100644
index 0000000..b48ab2a
--- /dev/null
+++ b/ssl/test/runner/alert.go
@@ -0,0 +1,77 @@
+// Copyright 2009 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 main
+
+import "strconv"
+
+type alert uint8
+
+const (
+	// alert level
+	alertLevelWarning = 1
+	alertLevelError   = 2
+)
+
+const (
+	alertCloseNotify            alert = 0
+	alertUnexpectedMessage      alert = 10
+	alertBadRecordMAC           alert = 20
+	alertDecryptionFailed       alert = 21
+	alertRecordOverflow         alert = 22
+	alertDecompressionFailure   alert = 30
+	alertHandshakeFailure       alert = 40
+	alertBadCertificate         alert = 42
+	alertUnsupportedCertificate alert = 43
+	alertCertificateRevoked     alert = 44
+	alertCertificateExpired     alert = 45
+	alertCertificateUnknown     alert = 46
+	alertIllegalParameter       alert = 47
+	alertUnknownCA              alert = 48
+	alertAccessDenied           alert = 49
+	alertDecodeError            alert = 50
+	alertDecryptError           alert = 51
+	alertProtocolVersion        alert = 70
+	alertInsufficientSecurity   alert = 71
+	alertInternalError          alert = 80
+	alertUserCanceled           alert = 90
+	alertNoRenegotiation        alert = 100
+)
+
+var alertText = map[alert]string{
+	alertCloseNotify:            "close notify",
+	alertUnexpectedMessage:      "unexpected message",
+	alertBadRecordMAC:           "bad record MAC",
+	alertDecryptionFailed:       "decryption failed",
+	alertRecordOverflow:         "record overflow",
+	alertDecompressionFailure:   "decompression failure",
+	alertHandshakeFailure:       "handshake failure",
+	alertBadCertificate:         "bad certificate",
+	alertUnsupportedCertificate: "unsupported certificate",
+	alertCertificateRevoked:     "revoked certificate",
+	alertCertificateExpired:     "expired certificate",
+	alertCertificateUnknown:     "unknown certificate",
+	alertIllegalParameter:       "illegal parameter",
+	alertUnknownCA:              "unknown certificate authority",
+	alertAccessDenied:           "access denied",
+	alertDecodeError:            "error decoding message",
+	alertDecryptError:           "error decrypting message",
+	alertProtocolVersion:        "protocol version not supported",
+	alertInsufficientSecurity:   "insufficient security level",
+	alertInternalError:          "internal error",
+	alertUserCanceled:           "user canceled",
+	alertNoRenegotiation:        "no renegotiation",
+}
+
+func (e alert) String() string {
+	s, ok := alertText[e]
+	if ok {
+		return s
+	}
+	return "alert(" + strconv.Itoa(int(e)) + ")"
+}
+
+func (e alert) Error() string {
+	return e.String()
+}
diff --git a/ssl/test/runner/cert.pem b/ssl/test/runner/cert.pem
new file mode 100644
index 0000000..4de4f49
--- /dev/null
+++ b/ssl/test/runner/cert.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICWDCCAcGgAwIBAgIJAPuwTC6rEJsMMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTQwNDIzMjA1MDQwWhcNMTcwNDIyMjA1MDQwWjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQDYK8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92kWdGMdAQhLci
+HnAjkXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiFKKAnHmUcrgfV
+W28tQ+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQABo1AwTjAdBgNV
+HQ4EFgQUi3XVrMsIvg4fZbf6Vr5sp3Xaha8wHwYDVR0jBBgwFoAUi3XVrMsIvg4f
+Zbf6Vr5sp3Xaha8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQA76Hht
+ldY9avcTGSwbwoiuIqv0jTL1fHFnzy3RHMLDh+Lpvolc5DSrSJHCP5WuK0eeJXhr
+T5oQpHL9z/cCDLAKCKRa4uV0fhEdOWBqyR9p8y5jJtye72t6CuFUV5iqcpF4BH4f
+j2VNHwsSrJwkD4QUGlUtH7vwnQmyCFxZMmWAJg==
+-----END CERTIFICATE-----
diff --git a/ssl/test/runner/cipher_suites.go b/ssl/test/runner/cipher_suites.go
new file mode 100644
index 0000000..11c8bdd
--- /dev/null
+++ b/ssl/test/runner/cipher_suites.go
@@ -0,0 +1,290 @@
+// 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 main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/des"
+	"crypto/hmac"
+	"crypto/md5"
+	"crypto/rc4"
+	"crypto/sha1"
+	"crypto/x509"
+	"hash"
+)
+
+// a keyAgreement implements the client and server side of a TLS key agreement
+// protocol by generating and processing key exchange messages.
+type keyAgreement interface {
+	// On the server side, the first two methods are called in order.
+
+	// In the case that the key agreement protocol doesn't use a
+	// ServerKeyExchange message, generateServerKeyExchange can return nil,
+	// nil.
+	generateServerKeyExchange(*Config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error)
+	processClientKeyExchange(*Config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error)
+
+	// On the client side, the next two methods are called in order.
+
+	// This method may not be called if the server doesn't send a
+	// ServerKeyExchange message.
+	processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error
+	generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error)
+}
+
+const (
+	// suiteECDH indicates that the cipher suite involves elliptic curve
+	// Diffie-Hellman. This means that it should only be selected when the
+	// client indicates that it supports ECC with a curve and point format
+	// that we're happy with.
+	suiteECDHE = 1 << iota
+	// suiteECDSA indicates that the cipher suite involves an ECDSA
+	// signature and therefore may only be selected when the server's
+	// certificate is ECDSA. If this is not set then the cipher suite is
+	// RSA based.
+	suiteECDSA
+	// suiteTLS12 indicates that the cipher suite should only be advertised
+	// and accepted when using TLS 1.2.
+	suiteTLS12
+	// suiteSHA384 indicates that the cipher suite uses SHA384 as the
+	// handshake hash.
+	suiteSHA384
+)
+
+// A cipherSuite is a specific combination of key agreement, cipher and MAC
+// function. All cipher suites currently assume RSA key agreement.
+type cipherSuite struct {
+	id uint16
+	// the lengths, in bytes, of the key material needed for each component.
+	keyLen int
+	macLen int
+	ivLen  int
+	ka     func(version uint16) keyAgreement
+	// flags is a bitmask of the suite* values, above.
+	flags  int
+	cipher func(key, iv []byte, isRead bool) interface{}
+	mac    func(version uint16, macKey []byte) macFunction
+	aead   func(key, fixedNonce []byte) cipher.AEAD
+}
+
+var cipherSuites = []*cipherSuite{
+	// Ciphersuite order is chosen so that ECDHE comes before plain RSA
+	// and RC4 comes before AES (because of the Lucky13 attack).
+	{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
+	{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
+	{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
+	{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
+	{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherRC4, macSHA1, nil},
+	{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
+	{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
+	{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
+	{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
+	{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
+	{TLS_RSA_WITH_RC4_128_MD5, 16, 16, 0, rsaKA, 0, cipherRC4, macMD5, nil},
+	{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
+	{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
+	{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
+	{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
+}
+
+func cipherRC4(key, iv []byte, isRead bool) interface{} {
+	cipher, _ := rc4.NewCipher(key)
+	return cipher
+}
+
+func cipher3DES(key, iv []byte, isRead bool) interface{} {
+	block, _ := des.NewTripleDESCipher(key)
+	if isRead {
+		return cipher.NewCBCDecrypter(block, iv)
+	}
+	return cipher.NewCBCEncrypter(block, iv)
+}
+
+func cipherAES(key, iv []byte, isRead bool) interface{} {
+	block, _ := aes.NewCipher(key)
+	if isRead {
+		return cipher.NewCBCDecrypter(block, iv)
+	}
+	return cipher.NewCBCEncrypter(block, iv)
+}
+
+// macSHA1 returns a macFunction for the given protocol version.
+func macSHA1(version uint16, key []byte) macFunction {
+	if version == VersionSSL30 {
+		mac := ssl30MAC{
+			h:   sha1.New(),
+			key: make([]byte, len(key)),
+		}
+		copy(mac.key, key)
+		return mac
+	}
+	return tls10MAC{hmac.New(sha1.New, key)}
+}
+
+func macMD5(version uint16, key []byte) macFunction {
+	if version == VersionSSL30 {
+		mac := ssl30MAC{
+			h:   md5.New(),
+			key: make([]byte, len(key)),
+		}
+		copy(mac.key, key)
+		return mac
+	}
+	return tls10MAC{hmac.New(md5.New, key)}
+}
+
+type macFunction interface {
+	Size() int
+	MAC(digestBuf, seq, header, data []byte) []byte
+}
+
+// fixedNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
+// each call.
+type fixedNonceAEAD struct {
+	// sealNonce and openNonce are buffers where the larger nonce will be
+	// constructed. Since a seal and open operation may be running
+	// concurrently, there is a separate buffer for each.
+	sealNonce, openNonce []byte
+	aead                 cipher.AEAD
+}
+
+func (f *fixedNonceAEAD) NonceSize() int { return 8 }
+func (f *fixedNonceAEAD) Overhead() int  { return f.aead.Overhead() }
+
+func (f *fixedNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
+	copy(f.sealNonce[len(f.sealNonce)-8:], nonce)
+	return f.aead.Seal(out, f.sealNonce, plaintext, additionalData)
+}
+
+func (f *fixedNonceAEAD) Open(out, nonce, plaintext, additionalData []byte) ([]byte, error) {
+	copy(f.openNonce[len(f.openNonce)-8:], nonce)
+	return f.aead.Open(out, f.openNonce, plaintext, additionalData)
+}
+
+func aeadAESGCM(key, fixedNonce []byte) cipher.AEAD {
+	aes, err := aes.NewCipher(key)
+	if err != nil {
+		panic(err)
+	}
+	aead, err := cipher.NewGCM(aes)
+	if err != nil {
+		panic(err)
+	}
+
+	nonce1, nonce2 := make([]byte, 12), make([]byte, 12)
+	copy(nonce1, fixedNonce)
+	copy(nonce2, fixedNonce)
+
+	return &fixedNonceAEAD{nonce1, nonce2, aead}
+}
+
+// ssl30MAC implements the SSLv3 MAC function, as defined in
+// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 5.2.3.1
+type ssl30MAC struct {
+	h   hash.Hash
+	key []byte
+}
+
+func (s ssl30MAC) Size() int {
+	return s.h.Size()
+}
+
+var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36}
+
+var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c}
+
+func (s ssl30MAC) MAC(digestBuf, seq, header, data []byte) []byte {
+	padLength := 48
+	if s.h.Size() == 20 {
+		padLength = 40
+	}
+
+	s.h.Reset()
+	s.h.Write(s.key)
+	s.h.Write(ssl30Pad1[:padLength])
+	s.h.Write(seq)
+	s.h.Write(header[:1])
+	s.h.Write(header[3:5])
+	s.h.Write(data)
+	digestBuf = s.h.Sum(digestBuf[:0])
+
+	s.h.Reset()
+	s.h.Write(s.key)
+	s.h.Write(ssl30Pad2[:padLength])
+	s.h.Write(digestBuf)
+	return s.h.Sum(digestBuf[:0])
+}
+
+// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, section 6.2.3.
+type tls10MAC struct {
+	h hash.Hash
+}
+
+func (s tls10MAC) Size() int {
+	return s.h.Size()
+}
+
+func (s tls10MAC) MAC(digestBuf, seq, header, data []byte) []byte {
+	s.h.Reset()
+	s.h.Write(seq)
+	s.h.Write(header)
+	s.h.Write(data)
+	return s.h.Sum(digestBuf[:0])
+}
+
+func rsaKA(version uint16) keyAgreement {
+	return rsaKeyAgreement{}
+}
+
+func ecdheECDSAKA(version uint16) keyAgreement {
+	return &ecdheKeyAgreement{
+		sigType: signatureECDSA,
+		version: version,
+	}
+}
+
+func ecdheRSAKA(version uint16) keyAgreement {
+	return &ecdheKeyAgreement{
+		sigType: signatureRSA,
+		version: version,
+	}
+}
+
+// mutualCipherSuite returns a cipherSuite given a list of supported
+// ciphersuites and the id requested by the peer.
+func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
+	for _, id := range have {
+		if id == want {
+			for _, suite := range cipherSuites {
+				if suite.id == want {
+					return suite
+				}
+			}
+			return nil
+		}
+	}
+	return nil
+}
+
+// A list of the possible cipher suite ids. Taken from
+// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
+const (
+	TLS_RSA_WITH_RC4_128_MD5                uint16 = 0x0004
+	TLS_RSA_WITH_RC4_128_SHA                uint16 = 0x0005
+	TLS_RSA_WITH_3DES_EDE_CBC_SHA           uint16 = 0x000a
+	TLS_RSA_WITH_AES_128_CBC_SHA            uint16 = 0x002f
+	TLS_RSA_WITH_AES_256_CBC_SHA            uint16 = 0x0035
+	TLS_ECDHE_ECDSA_WITH_RC4_128_SHA        uint16 = 0xc007
+	TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA    uint16 = 0xc009
+	TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA    uint16 = 0xc00a
+	TLS_ECDHE_RSA_WITH_RC4_128_SHA          uint16 = 0xc011
+	TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA     uint16 = 0xc012
+	TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA      uint16 = 0xc013
+	TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA      uint16 = 0xc014
+	TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256   uint16 = 0xc02f
+	TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
+	TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384   uint16 = 0xc030
+)
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
new file mode 100644
index 0000000..9812bde
--- /dev/null
+++ b/ssl/test/runner/common.go
@@ -0,0 +1,598 @@
+// Copyright 2009 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 main
+
+import (
+	"container/list"
+	"crypto"
+	"crypto/rand"
+	"crypto/x509"
+	"fmt"
+	"io"
+	"math/big"
+	"strings"
+	"sync"
+	"time"
+)
+
+const (
+	VersionSSL30 = 0x0300
+	VersionTLS10 = 0x0301
+	VersionTLS11 = 0x0302
+	VersionTLS12 = 0x0303
+)
+
+const (
+	maxPlaintext    = 16384        // maximum plaintext payload length
+	maxCiphertext   = 16384 + 2048 // maximum ciphertext payload length
+	recordHeaderLen = 5            // record header length
+	maxHandshake    = 65536        // maximum handshake we support (protocol max is 16 MB)
+
+	minVersion = VersionSSL30
+	maxVersion = VersionTLS12
+)
+
+// TLS record types.
+type recordType uint8
+
+const (
+	recordTypeChangeCipherSpec recordType = 20
+	recordTypeAlert            recordType = 21
+	recordTypeHandshake        recordType = 22
+	recordTypeApplicationData  recordType = 23
+)
+
+// TLS handshake message types.
+const (
+	typeClientHello        uint8 = 1
+	typeServerHello        uint8 = 2
+	typeNewSessionTicket   uint8 = 4
+	typeCertificate        uint8 = 11
+	typeServerKeyExchange  uint8 = 12
+	typeCertificateRequest uint8 = 13
+	typeServerHelloDone    uint8 = 14
+	typeCertificateVerify  uint8 = 15
+	typeClientKeyExchange  uint8 = 16
+	typeFinished           uint8 = 20
+	typeCertificateStatus  uint8 = 22
+	typeNextProtocol       uint8 = 67 // Not IANA assigned
+)
+
+// TLS compression types.
+const (
+	compressionNone uint8 = 0
+)
+
+// TLS extension numbers
+const (
+	extensionServerName          uint16 = 0
+	extensionStatusRequest       uint16 = 5
+	extensionSupportedCurves     uint16 = 10
+	extensionSupportedPoints     uint16 = 11
+	extensionSignatureAlgorithms uint16 = 13
+	extensionSessionTicket       uint16 = 35
+	extensionNextProtoNeg        uint16 = 13172 // not IANA assigned
+	extensionRenegotiationInfo   uint16 = 0xff01
+)
+
+// TLS signaling cipher suite values
+const (
+	scsvRenegotiation uint16 = 0x00ff
+)
+
+// CurveID is the type of a TLS identifier for an elliptic curve. See
+// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8
+type CurveID uint16
+
+const (
+	CurveP256 CurveID = 23
+	CurveP384 CurveID = 24
+	CurveP521 CurveID = 25
+)
+
+// TLS Elliptic Curve Point Formats
+// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9
+const (
+	pointFormatUncompressed uint8 = 0
+)
+
+// TLS CertificateStatusType (RFC 3546)
+const (
+	statusTypeOCSP uint8 = 1
+)
+
+// Certificate types (for certificateRequestMsg)
+const (
+	certTypeRSASign    = 1 // A certificate containing an RSA key
+	certTypeDSSSign    = 2 // A certificate containing a DSA key
+	certTypeRSAFixedDH = 3 // A certificate containing a static DH key
+	certTypeDSSFixedDH = 4 // A certificate containing a static DH key
+
+	// See RFC4492 sections 3 and 5.5.
+	certTypeECDSASign      = 64 // A certificate containing an ECDSA-capable public key, signed with ECDSA.
+	certTypeRSAFixedECDH   = 65 // A certificate containing an ECDH-capable public key, signed with RSA.
+	certTypeECDSAFixedECDH = 66 // A certificate containing an ECDH-capable public key, signed with ECDSA.
+
+	// Rest of these are reserved by the TLS spec
+)
+
+// Hash functions for TLS 1.2 (See RFC 5246, section A.4.1)
+const (
+	hashSHA1   uint8 = 2
+	hashSHA256 uint8 = 4
+)
+
+// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1)
+const (
+	signatureRSA   uint8 = 1
+	signatureECDSA uint8 = 3
+)
+
+// signatureAndHash mirrors the TLS 1.2, SignatureAndHashAlgorithm struct. See
+// RFC 5246, section A.4.1.
+type signatureAndHash struct {
+	hash, signature uint8
+}
+
+// supportedSKXSignatureAlgorithms contains the signature and hash algorithms
+// that the code advertises as supported in a TLS 1.2 ClientHello.
+var supportedSKXSignatureAlgorithms = []signatureAndHash{
+	{hashSHA256, signatureRSA},
+	{hashSHA256, signatureECDSA},
+	{hashSHA1, signatureRSA},
+	{hashSHA1, signatureECDSA},
+}
+
+// supportedClientCertSignatureAlgorithms contains the signature and hash
+// algorithms that the code advertises as supported in a TLS 1.2
+// CertificateRequest.
+var supportedClientCertSignatureAlgorithms = []signatureAndHash{
+	{hashSHA256, signatureRSA},
+	{hashSHA256, signatureECDSA},
+}
+
+// ConnectionState records basic TLS details about the connection.
+type ConnectionState struct {
+	Version                    uint16                // TLS version used by the connection (e.g. VersionTLS12)
+	HandshakeComplete          bool                  // TLS handshake is complete
+	DidResume                  bool                  // connection resumes a previous TLS connection
+	CipherSuite                uint16                // cipher suite in use (TLS_RSA_WITH_RC4_128_SHA, ...)
+	NegotiatedProtocol         string                // negotiated next protocol (from Config.NextProtos)
+	NegotiatedProtocolIsMutual bool                  // negotiated protocol was advertised by server
+	ServerName                 string                // server name requested by client, if any (server side only)
+	PeerCertificates           []*x509.Certificate   // certificate chain presented by remote peer
+	VerifiedChains             [][]*x509.Certificate // verified chains built from PeerCertificates
+}
+
+// ClientAuthType declares the policy the server will follow for
+// TLS Client Authentication.
+type ClientAuthType int
+
+const (
+	NoClientCert ClientAuthType = iota
+	RequestClientCert
+	RequireAnyClientCert
+	VerifyClientCertIfGiven
+	RequireAndVerifyClientCert
+)
+
+// ClientSessionState contains the state needed by clients to resume TLS
+// sessions.
+type ClientSessionState struct {
+	sessionTicket      []uint8             // Encrypted ticket used for session resumption with server
+	vers               uint16              // SSL/TLS version negotiated for the session
+	cipherSuite        uint16              // Ciphersuite negotiated for the session
+	masterSecret       []byte              // MasterSecret generated by client on a full handshake
+	serverCertificates []*x509.Certificate // Certificate chain presented by the server
+}
+
+// ClientSessionCache is a cache of ClientSessionState objects that can be used
+// by a client to resume a TLS session with a given server. ClientSessionCache
+// implementations should expect to be called concurrently from different
+// goroutines.
+type ClientSessionCache interface {
+	// Get searches for a ClientSessionState associated with the given key.
+	// On return, ok is true if one was found.
+	Get(sessionKey string) (session *ClientSessionState, ok bool)
+
+	// Put adds the ClientSessionState to the cache with the given key.
+	Put(sessionKey string, cs *ClientSessionState)
+}
+
+// A Config structure is used to configure a TLS client or server.
+// After one has been passed to a TLS function it must not be
+// modified. A Config may be reused; the tls package will also not
+// modify it.
+type Config struct {
+	// Rand provides the source of entropy for nonces and RSA blinding.
+	// If Rand is nil, TLS uses the cryptographic random reader in package
+	// crypto/rand.
+	// The Reader must be safe for use by multiple goroutines.
+	Rand io.Reader
+
+	// Time returns the current time as the number of seconds since the epoch.
+	// If Time is nil, TLS uses time.Now.
+	Time func() time.Time
+
+	// Certificates contains one or more certificate chains
+	// to present to the other side of the connection.
+	// Server configurations must include at least one certificate.
+	Certificates []Certificate
+
+	// NameToCertificate maps from a certificate name to an element of
+	// Certificates. Note that a certificate name can be of the form
+	// '*.example.com' and so doesn't have to be a domain name as such.
+	// See Config.BuildNameToCertificate
+	// The nil value causes the first element of Certificates to be used
+	// for all connections.
+	NameToCertificate map[string]*Certificate
+
+	// RootCAs defines the set of root certificate authorities
+	// that clients use when verifying server certificates.
+	// If RootCAs is nil, TLS uses the host's root CA set.
+	RootCAs *x509.CertPool
+
+	// NextProtos is a list of supported, application level protocols.
+	NextProtos []string
+
+	// ServerName is used to verify the hostname on the returned
+	// certificates unless InsecureSkipVerify is given. It is also included
+	// in the client's handshake to support virtual hosting.
+	ServerName string
+
+	// ClientAuth determines the server's policy for
+	// TLS Client Authentication. The default is NoClientCert.
+	ClientAuth ClientAuthType
+
+	// ClientCAs defines the set of root certificate authorities
+	// that servers use if required to verify a client certificate
+	// by the policy in ClientAuth.
+	ClientCAs *x509.CertPool
+
+	// InsecureSkipVerify controls whether a client verifies the
+	// server's certificate chain and host name.
+	// If InsecureSkipVerify is true, TLS accepts any certificate
+	// presented by the server and any host name in that certificate.
+	// In this mode, TLS is susceptible to man-in-the-middle attacks.
+	// This should be used only for testing.
+	InsecureSkipVerify bool
+
+	// CipherSuites is a list of supported cipher suites. If CipherSuites
+	// is nil, TLS uses a list of suites supported by the implementation.
+	CipherSuites []uint16
+
+	// PreferServerCipherSuites controls whether the server selects the
+	// client's most preferred ciphersuite, or the server's most preferred
+	// ciphersuite. If true then the server's preference, as expressed in
+	// the order of elements in CipherSuites, is used.
+	PreferServerCipherSuites bool
+
+	// SessionTicketsDisabled may be set to true to disable session ticket
+	// (resumption) support.
+	SessionTicketsDisabled bool
+
+	// SessionTicketKey is used by TLS servers to provide session
+	// resumption. See RFC 5077. If zero, it will be filled with
+	// random data before the first server handshake.
+	//
+	// If multiple servers are terminating connections for the same host
+	// they should all have the same SessionTicketKey. If the
+	// SessionTicketKey leaks, previously recorded and future TLS
+	// connections using that key are compromised.
+	SessionTicketKey [32]byte
+
+	// SessionCache is a cache of ClientSessionState entries for TLS session
+	// resumption.
+	ClientSessionCache ClientSessionCache
+
+	// MinVersion contains the minimum SSL/TLS version that is acceptable.
+	// If zero, then SSLv3 is taken as the minimum.
+	MinVersion uint16
+
+	// MaxVersion contains the maximum SSL/TLS version that is acceptable.
+	// If zero, then the maximum version supported by this package is used,
+	// which is currently TLS 1.2.
+	MaxVersion uint16
+
+	// CurvePreferences contains the elliptic curves that will be used in
+	// an ECDHE handshake, in preference order. If empty, the default will
+	// be used.
+	CurvePreferences []CurveID
+
+	// Bugs specifies optional misbehaviour to be used for testing other
+	// implementations.
+	Bugs ProtocolBugs
+
+	serverInitOnce sync.Once // guards calling (*Config).serverInit
+}
+
+type BadValue int
+
+const (
+	BadValueNone BadValue = iota
+	BadValueNegative
+	BadValueZero
+	BadValueLimit
+	BadValueLarge
+	NumBadValues
+)
+
+type ProtocolBugs struct {
+	// InvalidSKXSignature specifies that the signature in a
+	// ServerKeyExchange message should be invalid.
+	InvalidSKXSignature bool
+
+	// InvalidSKXCurve causes the curve ID in the ServerKeyExchange message
+	// to be wrong.
+	InvalidSKXCurve bool
+
+	// BadECDSAR controls ways in which the 'r' value of an ECDSA signature
+	// can be invalid.
+	BadECDSAR BadValue
+	BadECDSAS BadValue
+}
+
+func (c *Config) serverInit() {
+	if c.SessionTicketsDisabled {
+		return
+	}
+
+	// If the key has already been set then we have nothing to do.
+	for _, b := range c.SessionTicketKey {
+		if b != 0 {
+			return
+		}
+	}
+
+	if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil {
+		c.SessionTicketsDisabled = true
+	}
+}
+
+func (c *Config) rand() io.Reader {
+	r := c.Rand
+	if r == nil {
+		return rand.Reader
+	}
+	return r
+}
+
+func (c *Config) time() time.Time {
+	t := c.Time
+	if t == nil {
+		t = time.Now
+	}
+	return t()
+}
+
+func (c *Config) cipherSuites() []uint16 {
+	s := c.CipherSuites
+	if s == nil {
+		s = defaultCipherSuites()
+	}
+	return s
+}
+
+func (c *Config) minVersion() uint16 {
+	if c == nil || c.MinVersion == 0 {
+		return minVersion
+	}
+	return c.MinVersion
+}
+
+func (c *Config) maxVersion() uint16 {
+	if c == nil || c.MaxVersion == 0 {
+		return maxVersion
+	}
+	return c.MaxVersion
+}
+
+var defaultCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521}
+
+func (c *Config) curvePreferences() []CurveID {
+	if c == nil || len(c.CurvePreferences) == 0 {
+		return defaultCurvePreferences
+	}
+	return c.CurvePreferences
+}
+
+// mutualVersion returns the protocol version to use given the advertised
+// version of the peer.
+func (c *Config) mutualVersion(vers uint16) (uint16, bool) {
+	minVersion := c.minVersion()
+	maxVersion := c.maxVersion()
+
+	if vers < minVersion {
+		return 0, false
+	}
+	if vers > maxVersion {
+		vers = maxVersion
+	}
+	return vers, true
+}
+
+// getCertificateForName returns the best certificate for the given name,
+// defaulting to the first element of c.Certificates if there are no good
+// options.
+func (c *Config) getCertificateForName(name string) *Certificate {
+	if len(c.Certificates) == 1 || c.NameToCertificate == nil {
+		// There's only one choice, so no point doing any work.
+		return &c.Certificates[0]
+	}
+
+	name = strings.ToLower(name)
+	for len(name) > 0 && name[len(name)-1] == '.' {
+		name = name[:len(name)-1]
+	}
+
+	if cert, ok := c.NameToCertificate[name]; ok {
+		return cert
+	}
+
+	// try replacing labels in the name with wildcards until we get a
+	// match.
+	labels := strings.Split(name, ".")
+	for i := range labels {
+		labels[i] = "*"
+		candidate := strings.Join(labels, ".")
+		if cert, ok := c.NameToCertificate[candidate]; ok {
+			return cert
+		}
+	}
+
+	// If nothing matches, return the first certificate.
+	return &c.Certificates[0]
+}
+
+// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate
+// from the CommonName and SubjectAlternateName fields of each of the leaf
+// certificates.
+func (c *Config) BuildNameToCertificate() {
+	c.NameToCertificate = make(map[string]*Certificate)
+	for i := range c.Certificates {
+		cert := &c.Certificates[i]
+		x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
+		if err != nil {
+			continue
+		}
+		if len(x509Cert.Subject.CommonName) > 0 {
+			c.NameToCertificate[x509Cert.Subject.CommonName] = cert
+		}
+		for _, san := range x509Cert.DNSNames {
+			c.NameToCertificate[san] = cert
+		}
+	}
+}
+
+// A Certificate is a chain of one or more certificates, leaf first.
+type Certificate struct {
+	Certificate [][]byte
+	PrivateKey  crypto.PrivateKey // supported types: *rsa.PrivateKey, *ecdsa.PrivateKey
+	// OCSPStaple contains an optional OCSP response which will be served
+	// to clients that request it.
+	OCSPStaple []byte
+	// Leaf is the parsed form of the leaf certificate, which may be
+	// initialized using x509.ParseCertificate to reduce per-handshake
+	// processing for TLS clients doing client authentication. If nil, the
+	// leaf certificate will be parsed as needed.
+	Leaf *x509.Certificate
+}
+
+// A TLS record.
+type record struct {
+	contentType  recordType
+	major, minor uint8
+	payload      []byte
+}
+
+type handshakeMessage interface {
+	marshal() []byte
+	unmarshal([]byte) bool
+}
+
+// lruSessionCache is a ClientSessionCache implementation that uses an LRU
+// caching strategy.
+type lruSessionCache struct {
+	sync.Mutex
+
+	m        map[string]*list.Element
+	q        *list.List
+	capacity int
+}
+
+type lruSessionCacheEntry struct {
+	sessionKey string
+	state      *ClientSessionState
+}
+
+// NewLRUClientSessionCache returns a ClientSessionCache with the given
+// capacity that uses an LRU strategy. If capacity is < 1, a default capacity
+// is used instead.
+func NewLRUClientSessionCache(capacity int) ClientSessionCache {
+	const defaultSessionCacheCapacity = 64
+
+	if capacity < 1 {
+		capacity = defaultSessionCacheCapacity
+	}
+	return &lruSessionCache{
+		m:        make(map[string]*list.Element),
+		q:        list.New(),
+		capacity: capacity,
+	}
+}
+
+// Put adds the provided (sessionKey, cs) pair to the cache.
+func (c *lruSessionCache) Put(sessionKey string, cs *ClientSessionState) {
+	c.Lock()
+	defer c.Unlock()
+
+	if elem, ok := c.m[sessionKey]; ok {
+		entry := elem.Value.(*lruSessionCacheEntry)
+		entry.state = cs
+		c.q.MoveToFront(elem)
+		return
+	}
+
+	if c.q.Len() < c.capacity {
+		entry := &lruSessionCacheEntry{sessionKey, cs}
+		c.m[sessionKey] = c.q.PushFront(entry)
+		return
+	}
+
+	elem := c.q.Back()
+	entry := elem.Value.(*lruSessionCacheEntry)
+	delete(c.m, entry.sessionKey)
+	entry.sessionKey = sessionKey
+	entry.state = cs
+	c.q.MoveToFront(elem)
+	c.m[sessionKey] = elem
+}
+
+// Get returns the ClientSessionState value associated with a given key. It
+// returns (nil, false) if no value is found.
+func (c *lruSessionCache) Get(sessionKey string) (*ClientSessionState, bool) {
+	c.Lock()
+	defer c.Unlock()
+
+	if elem, ok := c.m[sessionKey]; ok {
+		c.q.MoveToFront(elem)
+		return elem.Value.(*lruSessionCacheEntry).state, true
+	}
+	return nil, false
+}
+
+// TODO(jsing): Make these available to both crypto/x509 and crypto/tls.
+type dsaSignature struct {
+	R, S *big.Int
+}
+
+type ecdsaSignature dsaSignature
+
+var emptyConfig Config
+
+func defaultConfig() *Config {
+	return &emptyConfig
+}
+
+var (
+	once                   sync.Once
+	varDefaultCipherSuites []uint16
+)
+
+func defaultCipherSuites() []uint16 {
+	once.Do(initDefaultCipherSuites)
+	return varDefaultCipherSuites
+}
+
+func initDefaultCipherSuites() {
+	varDefaultCipherSuites = make([]uint16, len(cipherSuites))
+	for i, suite := range cipherSuites {
+		varDefaultCipherSuites[i] = suite.id
+	}
+}
+
+func unexpectedMessageError(wanted, got interface{}) error {
+	return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted)
+}
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
new file mode 100644
index 0000000..d130895
--- /dev/null
+++ b/ssl/test/runner/conn.go
@@ -0,0 +1,1019 @@
+// 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.
+
+// TLS low level connection and record layer
+
+package main
+
+import (
+	"bytes"
+	"crypto/cipher"
+	"crypto/subtle"
+	"crypto/x509"
+	"errors"
+	"fmt"
+	"io"
+	"net"
+	"sync"
+	"time"
+)
+
+// A Conn represents a secured connection.
+// It implements the net.Conn interface.
+type Conn struct {
+	// constant
+	conn     net.Conn
+	isClient bool
+
+	// constant after handshake; protected by handshakeMutex
+	handshakeMutex    sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
+	handshakeErr      error      // error resulting from handshake
+	vers              uint16     // TLS version
+	haveVers          bool       // version has been negotiated
+	config            *Config    // configuration passed to constructor
+	handshakeComplete bool
+	didResume         bool // whether this connection was a session resumption
+	cipherSuite       uint16
+	ocspResponse      []byte // stapled OCSP response
+	peerCertificates  []*x509.Certificate
+	// verifiedChains contains the certificate chains that we built, as
+	// opposed to the ones presented by the server.
+	verifiedChains [][]*x509.Certificate
+	// serverName contains the server name indicated by the client, if any.
+	serverName string
+
+	clientProtocol         string
+	clientProtocolFallback bool
+
+	// input/output
+	in, out  halfConn     // in.Mutex < out.Mutex
+	rawInput *block       // raw input, right off the wire
+	input    *block       // application data waiting to be read
+	hand     bytes.Buffer // handshake data waiting to be read
+
+	tmp [16]byte
+}
+
+// Access to net.Conn methods.
+// Cannot just embed net.Conn because that would
+// export the struct field too.
+
+// LocalAddr returns the local network address.
+func (c *Conn) LocalAddr() net.Addr {
+	return c.conn.LocalAddr()
+}
+
+// RemoteAddr returns the remote network address.
+func (c *Conn) RemoteAddr() net.Addr {
+	return c.conn.RemoteAddr()
+}
+
+// SetDeadline sets the read and write deadlines associated with the connection.
+// A zero value for t means Read and Write will not time out.
+// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
+func (c *Conn) SetDeadline(t time.Time) error {
+	return c.conn.SetDeadline(t)
+}
+
+// SetReadDeadline sets the read deadline on the underlying connection.
+// A zero value for t means Read will not time out.
+func (c *Conn) SetReadDeadline(t time.Time) error {
+	return c.conn.SetReadDeadline(t)
+}
+
+// SetWriteDeadline sets the write deadline on the underlying conneciton.
+// A zero value for t means Write will not time out.
+// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
+func (c *Conn) SetWriteDeadline(t time.Time) error {
+	return c.conn.SetWriteDeadline(t)
+}
+
+// A halfConn represents one direction of the record layer
+// connection, either sending or receiving.
+type halfConn struct {
+	sync.Mutex
+
+	err     error       // first permanent error
+	version uint16      // protocol version
+	cipher  interface{} // cipher algorithm
+	mac     macFunction
+	seq     [8]byte // 64-bit sequence number
+	bfree   *block  // list of free blocks
+
+	nextCipher interface{} // next encryption state
+	nextMac    macFunction // next MAC algorithm
+
+	// used to save allocating a new buffer for each MAC.
+	inDigestBuf, outDigestBuf []byte
+}
+
+func (hc *halfConn) setErrorLocked(err error) error {
+	hc.err = err
+	return err
+}
+
+func (hc *halfConn) error() error {
+	hc.Lock()
+	err := hc.err
+	hc.Unlock()
+	return err
+}
+
+// prepareCipherSpec sets the encryption and MAC states
+// that a subsequent changeCipherSpec will use.
+func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction) {
+	hc.version = version
+	hc.nextCipher = cipher
+	hc.nextMac = mac
+}
+
+// changeCipherSpec changes the encryption and MAC states
+// to the ones previously passed to prepareCipherSpec.
+func (hc *halfConn) changeCipherSpec() error {
+	if hc.nextCipher == nil {
+		return alertInternalError
+	}
+	hc.cipher = hc.nextCipher
+	hc.mac = hc.nextMac
+	hc.nextCipher = nil
+	hc.nextMac = nil
+	for i := range hc.seq {
+		hc.seq[i] = 0
+	}
+	return nil
+}
+
+// incSeq increments the sequence number.
+func (hc *halfConn) incSeq() {
+	for i := 7; i >= 0; i-- {
+		hc.seq[i]++
+		if hc.seq[i] != 0 {
+			return
+		}
+	}
+
+	// Not allowed to let sequence number wrap.
+	// Instead, must renegotiate before it does.
+	// Not likely enough to bother.
+	panic("TLS: sequence number wraparound")
+}
+
+// resetSeq resets the sequence number to zero.
+func (hc *halfConn) resetSeq() {
+	for i := range hc.seq {
+		hc.seq[i] = 0
+	}
+}
+
+// removePadding returns an unpadded slice, in constant time, which is a prefix
+// of the input. It also returns a byte which is equal to 255 if the padding
+// was valid and 0 otherwise. See RFC 2246, section 6.2.3.2
+func removePadding(payload []byte) ([]byte, byte) {
+	if len(payload) < 1 {
+		return payload, 0
+	}
+
+	paddingLen := payload[len(payload)-1]
+	t := uint(len(payload)-1) - uint(paddingLen)
+	// if len(payload) >= (paddingLen - 1) then the MSB of t is zero
+	good := byte(int32(^t) >> 31)
+
+	toCheck := 255 // the maximum possible padding length
+	// The length of the padded data is public, so we can use an if here
+	if toCheck+1 > len(payload) {
+		toCheck = len(payload) - 1
+	}
+
+	for i := 0; i < toCheck; i++ {
+		t := uint(paddingLen) - uint(i)
+		// if i <= paddingLen then the MSB of t is zero
+		mask := byte(int32(^t) >> 31)
+		b := payload[len(payload)-1-i]
+		good &^= mask&paddingLen ^ mask&b
+	}
+
+	// We AND together the bits of good and replicate the result across
+	// all the bits.
+	good &= good << 4
+	good &= good << 2
+	good &= good << 1
+	good = uint8(int8(good) >> 7)
+
+	toRemove := good&paddingLen + 1
+	return payload[:len(payload)-int(toRemove)], good
+}
+
+// removePaddingSSL30 is a replacement for removePadding in the case that the
+// protocol version is SSLv3. In this version, the contents of the padding
+// are random and cannot be checked.
+func removePaddingSSL30(payload []byte) ([]byte, byte) {
+	if len(payload) < 1 {
+		return payload, 0
+	}
+
+	paddingLen := int(payload[len(payload)-1]) + 1
+	if paddingLen > len(payload) {
+		return payload, 0
+	}
+
+	return payload[:len(payload)-paddingLen], 255
+}
+
+func roundUp(a, b int) int {
+	return a + (b-a%b)%b
+}
+
+// cbcMode is an interface for block ciphers using cipher block chaining.
+type cbcMode interface {
+	cipher.BlockMode
+	SetIV([]byte)
+}
+
+// decrypt checks and strips the mac and decrypts the data in b. Returns a
+// success boolean, the number of bytes to skip from the start of the record in
+// order to get the application payload, and an optional alert value.
+func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) {
+	// pull out payload
+	payload := b.data[recordHeaderLen:]
+
+	macSize := 0
+	if hc.mac != nil {
+		macSize = hc.mac.Size()
+	}
+
+	paddingGood := byte(255)
+	explicitIVLen := 0
+
+	// decrypt
+	if hc.cipher != nil {
+		switch c := hc.cipher.(type) {
+		case cipher.Stream:
+			c.XORKeyStream(payload, payload)
+		case cipher.AEAD:
+			explicitIVLen = 8
+			if len(payload) < explicitIVLen {
+				return false, 0, alertBadRecordMAC
+			}
+			nonce := payload[:8]
+			payload = payload[8:]
+
+			var additionalData [13]byte
+			copy(additionalData[:], hc.seq[:])
+			copy(additionalData[8:], b.data[:3])
+			n := len(payload) - c.Overhead()
+			additionalData[11] = byte(n >> 8)
+			additionalData[12] = byte(n)
+			var err error
+			payload, err = c.Open(payload[:0], nonce, payload, additionalData[:])
+			if err != nil {
+				return false, 0, alertBadRecordMAC
+			}
+			b.resize(recordHeaderLen + explicitIVLen + len(payload))
+		case cbcMode:
+			blockSize := c.BlockSize()
+			if hc.version >= VersionTLS11 {
+				explicitIVLen = blockSize
+			}
+
+			if len(payload)%blockSize != 0 || len(payload) < roundUp(explicitIVLen+macSize+1, blockSize) {
+				return false, 0, alertBadRecordMAC
+			}
+
+			if explicitIVLen > 0 {
+				c.SetIV(payload[:explicitIVLen])
+				payload = payload[explicitIVLen:]
+			}
+			c.CryptBlocks(payload, payload)
+			if hc.version == VersionSSL30 {
+				payload, paddingGood = removePaddingSSL30(payload)
+			} else {
+				payload, paddingGood = removePadding(payload)
+			}
+			b.resize(recordHeaderLen + explicitIVLen + len(payload))
+
+			// note that we still have a timing side-channel in the
+			// MAC check, below. An attacker can align the record
+			// so that a correct padding will cause one less hash
+			// block to be calculated. Then they can iteratively
+			// decrypt a record by breaking each byte. See
+			// "Password Interception in a SSL/TLS Channel", Brice
+			// Canvel et al.
+			//
+			// However, our behavior matches OpenSSL, so we leak
+			// only as much as they do.
+		default:
+			panic("unknown cipher type")
+		}
+	}
+
+	// check, strip mac
+	if hc.mac != nil {
+		if len(payload) < macSize {
+			return false, 0, alertBadRecordMAC
+		}
+
+		// strip mac off payload, b.data
+		n := len(payload) - macSize
+		b.data[3] = byte(n >> 8)
+		b.data[4] = byte(n)
+		b.resize(recordHeaderLen + explicitIVLen + n)
+		remoteMAC := payload[n:]
+		localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], payload[:n])
+
+		if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 {
+			return false, 0, alertBadRecordMAC
+		}
+		hc.inDigestBuf = localMAC
+	}
+	hc.incSeq()
+
+	return true, recordHeaderLen + explicitIVLen, 0
+}
+
+// padToBlockSize calculates the needed padding block, if any, for a payload.
+// On exit, prefix aliases payload and extends to the end of the last full
+// block of payload. finalBlock is a fresh slice which contains the contents of
+// any suffix of payload as well as the needed padding to make finalBlock a
+// full block.
+func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) {
+	overrun := len(payload) % blockSize
+	paddingLen := blockSize - overrun
+	prefix = payload[:len(payload)-overrun]
+	finalBlock = make([]byte, blockSize)
+	copy(finalBlock, payload[len(payload)-overrun:])
+	for i := overrun; i < blockSize; i++ {
+		finalBlock[i] = byte(paddingLen - 1)
+	}
+	return
+}
+
+// encrypt encrypts and macs the data in b.
+func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
+	// mac
+	if hc.mac != nil {
+		mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:])
+
+		n := len(b.data)
+		b.resize(n + len(mac))
+		copy(b.data[n:], mac)
+		hc.outDigestBuf = mac
+	}
+
+	payload := b.data[recordHeaderLen:]
+
+	// encrypt
+	if hc.cipher != nil {
+		switch c := hc.cipher.(type) {
+		case cipher.Stream:
+			c.XORKeyStream(payload, payload)
+		case cipher.AEAD:
+			payloadLen := len(b.data) - recordHeaderLen - explicitIVLen
+			b.resize(len(b.data) + c.Overhead())
+			nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
+			payload := b.data[recordHeaderLen+explicitIVLen:]
+			payload = payload[:payloadLen]
+
+			var additionalData [13]byte
+			copy(additionalData[:], hc.seq[:])
+			copy(additionalData[8:], b.data[:3])
+			additionalData[11] = byte(payloadLen >> 8)
+			additionalData[12] = byte(payloadLen)
+
+			c.Seal(payload[:0], nonce, payload, additionalData[:])
+		case cbcMode:
+			blockSize := c.BlockSize()
+			if explicitIVLen > 0 {
+				c.SetIV(payload[:explicitIVLen])
+				payload = payload[explicitIVLen:]
+			}
+			prefix, finalBlock := padToBlockSize(payload, blockSize)
+			b.resize(recordHeaderLen + explicitIVLen + len(prefix) + len(finalBlock))
+			c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen:], prefix)
+			c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen+len(prefix):], finalBlock)
+		default:
+			panic("unknown cipher type")
+		}
+	}
+
+	// update length to include MAC and any block padding needed.
+	n := len(b.data) - recordHeaderLen
+	b.data[3] = byte(n >> 8)
+	b.data[4] = byte(n)
+	hc.incSeq()
+
+	return true, 0
+}
+
+// A block is a simple data buffer.
+type block struct {
+	data []byte
+	off  int // index for Read
+	link *block
+}
+
+// resize resizes block to be n bytes, growing if necessary.
+func (b *block) resize(n int) {
+	if n > cap(b.data) {
+		b.reserve(n)
+	}
+	b.data = b.data[0:n]
+}
+
+// reserve makes sure that block contains a capacity of at least n bytes.
+func (b *block) reserve(n int) {
+	if cap(b.data) >= n {
+		return
+	}
+	m := cap(b.data)
+	if m == 0 {
+		m = 1024
+	}
+	for m < n {
+		m *= 2
+	}
+	data := make([]byte, len(b.data), m)
+	copy(data, b.data)
+	b.data = data
+}
+
+// readFromUntil reads from r into b until b contains at least n bytes
+// or else returns an error.
+func (b *block) readFromUntil(r io.Reader, n int) error {
+	// quick case
+	if len(b.data) >= n {
+		return nil
+	}
+
+	// read until have enough.
+	b.reserve(n)
+	for {
+		m, err := r.Read(b.data[len(b.data):cap(b.data)])
+		b.data = b.data[0 : len(b.data)+m]
+		if len(b.data) >= n {
+			// TODO(bradfitz,agl): slightly suspicious
+			// that we're throwing away r.Read's err here.
+			break
+		}
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (b *block) Read(p []byte) (n int, err error) {
+	n = copy(p, b.data[b.off:])
+	b.off += n
+	return
+}
+
+// newBlock allocates a new block, from hc's free list if possible.
+func (hc *halfConn) newBlock() *block {
+	b := hc.bfree
+	if b == nil {
+		return new(block)
+	}
+	hc.bfree = b.link
+	b.link = nil
+	b.resize(0)
+	return b
+}
+
+// freeBlock returns a block to hc's free list.
+// The protocol is such that each side only has a block or two on
+// its free list at a time, so there's no need to worry about
+// trimming the list, etc.
+func (hc *halfConn) freeBlock(b *block) {
+	b.link = hc.bfree
+	hc.bfree = b
+}
+
+// splitBlock splits a block after the first n bytes,
+// returning a block with those n bytes and a
+// block with the remainder.  the latter may be nil.
+func (hc *halfConn) splitBlock(b *block, n int) (*block, *block) {
+	if len(b.data) <= n {
+		return b, nil
+	}
+	bb := hc.newBlock()
+	bb.resize(len(b.data) - n)
+	copy(bb.data, b.data[n:])
+	b.data = b.data[0:n]
+	return b, bb
+}
+
+// readRecord reads the next TLS record from the connection
+// and updates the record layer state.
+// c.in.Mutex <= L; c.input == nil.
+func (c *Conn) readRecord(want recordType) error {
+	// Caller must be in sync with connection:
+	// handshake data if handshake not yet completed,
+	// else application data.  (We don't support renegotiation.)
+	switch want {
+	default:
+		c.sendAlert(alertInternalError)
+		return c.in.setErrorLocked(errors.New("tls: unknown record type requested"))
+	case recordTypeHandshake, recordTypeChangeCipherSpec:
+		if c.handshakeComplete {
+			c.sendAlert(alertInternalError)
+			return c.in.setErrorLocked(errors.New("tls: handshake or ChangeCipherSpec requested after handshake complete"))
+		}
+	case recordTypeApplicationData:
+		if !c.handshakeComplete {
+			c.sendAlert(alertInternalError)
+			return c.in.setErrorLocked(errors.New("tls: application data record requested before handshake complete"))
+		}
+	}
+
+Again:
+	if c.rawInput == nil {
+		c.rawInput = c.in.newBlock()
+	}
+	b := c.rawInput
+
+	// Read header, payload.
+	if err := b.readFromUntil(c.conn, recordHeaderLen); err != nil {
+		// RFC suggests that EOF without an alertCloseNotify is
+		// an error, but popular web sites seem to do this,
+		// so we can't make it an error.
+		// if err == io.EOF {
+		// 	err = io.ErrUnexpectedEOF
+		// }
+		if e, ok := err.(net.Error); !ok || !e.Temporary() {
+			c.in.setErrorLocked(err)
+		}
+		return err
+	}
+	typ := recordType(b.data[0])
+
+	// No valid TLS record has a type of 0x80, however SSLv2 handshakes
+	// start with a uint16 length where the MSB is set and the first record
+	// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
+	// an SSLv2 client.
+	if want == recordTypeHandshake && typ == 0x80 {
+		c.sendAlert(alertProtocolVersion)
+		return c.in.setErrorLocked(errors.New("tls: unsupported SSLv2 handshake received"))
+	}
+
+	vers := uint16(b.data[1])<<8 | uint16(b.data[2])
+	n := int(b.data[3])<<8 | int(b.data[4])
+	if c.haveVers && vers != c.vers {
+		c.sendAlert(alertProtocolVersion)
+		return c.in.setErrorLocked(fmt.Errorf("tls: received record with version %x when expecting version %x", vers, c.vers))
+	}
+	if n > maxCiphertext {
+		c.sendAlert(alertRecordOverflow)
+		return c.in.setErrorLocked(fmt.Errorf("tls: oversized record received with length %d", n))
+	}
+	if !c.haveVers {
+		// First message, be extra suspicious:
+		// this might not be a TLS client.
+		// Bail out before reading a full 'body', if possible.
+		// The current max version is 3.1.
+		// If the version is >= 16.0, it's probably not real.
+		// Similarly, a clientHello message encodes in
+		// well under a kilobyte.  If the length is >= 12 kB,
+		// it's probably not real.
+		if (typ != recordTypeAlert && typ != want) || vers >= 0x1000 || n >= 0x3000 {
+			c.sendAlert(alertUnexpectedMessage)
+			return c.in.setErrorLocked(fmt.Errorf("tls: first record does not look like a TLS handshake"))
+		}
+	}
+	if err := b.readFromUntil(c.conn, recordHeaderLen+n); err != nil {
+		if err == io.EOF {
+			err = io.ErrUnexpectedEOF
+		}
+		if e, ok := err.(net.Error); !ok || !e.Temporary() {
+			c.in.setErrorLocked(err)
+		}
+		return err
+	}
+
+	// Process message.
+	b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n)
+	ok, off, err := c.in.decrypt(b)
+	if !ok {
+		c.in.setErrorLocked(c.sendAlert(err))
+	}
+	b.off = off
+	data := b.data[b.off:]
+	if len(data) > maxPlaintext {
+		err := c.sendAlert(alertRecordOverflow)
+		c.in.freeBlock(b)
+		return c.in.setErrorLocked(err)
+	}
+
+	switch typ {
+	default:
+		c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+
+	case recordTypeAlert:
+		if len(data) != 2 {
+			c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+			break
+		}
+		if alert(data[1]) == alertCloseNotify {
+			c.in.setErrorLocked(io.EOF)
+			break
+		}
+		switch data[0] {
+		case alertLevelWarning:
+			// drop on the floor
+			c.in.freeBlock(b)
+			goto Again
+		case alertLevelError:
+			c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])})
+		default:
+			c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+		}
+
+	case recordTypeChangeCipherSpec:
+		if typ != want || len(data) != 1 || data[0] != 1 {
+			c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+			break
+		}
+		err := c.in.changeCipherSpec()
+		if err != nil {
+			c.in.setErrorLocked(c.sendAlert(err.(alert)))
+		}
+
+	case recordTypeApplicationData:
+		if typ != want {
+			c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+			break
+		}
+		c.input = b
+		b = nil
+
+	case recordTypeHandshake:
+		// TODO(rsc): Should at least pick off connection close.
+		if typ != want {
+			return c.in.setErrorLocked(c.sendAlert(alertNoRenegotiation))
+		}
+		c.hand.Write(data)
+	}
+
+	if b != nil {
+		c.in.freeBlock(b)
+	}
+	return c.in.err
+}
+
+// sendAlert sends a TLS alert message.
+// c.out.Mutex <= L.
+func (c *Conn) sendAlertLocked(err alert) error {
+	switch err {
+	case alertNoRenegotiation, alertCloseNotify:
+		c.tmp[0] = alertLevelWarning
+	default:
+		c.tmp[0] = alertLevelError
+	}
+	c.tmp[1] = byte(err)
+	c.writeRecord(recordTypeAlert, c.tmp[0:2])
+	// closeNotify is a special case in that it isn't an error:
+	if err != alertCloseNotify {
+		return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
+	}
+	return nil
+}
+
+// sendAlert sends a TLS alert message.
+// L < c.out.Mutex.
+func (c *Conn) sendAlert(err alert) error {
+	c.out.Lock()
+	defer c.out.Unlock()
+	return c.sendAlertLocked(err)
+}
+
+// writeRecord writes a TLS record with the given type and payload
+// to the connection and updates the record layer state.
+// c.out.Mutex <= L.
+func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
+	b := c.out.newBlock()
+	for len(data) > 0 {
+		m := len(data)
+		if m > maxPlaintext {
+			m = maxPlaintext
+		}
+		explicitIVLen := 0
+		explicitIVIsSeq := false
+
+		var cbc cbcMode
+		if c.out.version >= VersionTLS11 {
+			var ok bool
+			if cbc, ok = c.out.cipher.(cbcMode); ok {
+				explicitIVLen = cbc.BlockSize()
+			}
+		}
+		if explicitIVLen == 0 {
+			if _, ok := c.out.cipher.(cipher.AEAD); ok {
+				explicitIVLen = 8
+				// The AES-GCM construction in TLS has an
+				// explicit nonce so that the nonce can be
+				// random. However, the nonce is only 8 bytes
+				// which is too small for a secure, random
+				// nonce. Therefore we use the sequence number
+				// as the nonce.
+				explicitIVIsSeq = true
+			}
+		}
+		b.resize(recordHeaderLen + explicitIVLen + m)
+		b.data[0] = byte(typ)
+		vers := c.vers
+		if vers == 0 {
+			// Some TLS servers fail if the record version is
+			// greater than TLS 1.0 for the initial ClientHello.
+			vers = VersionTLS10
+		}
+		b.data[1] = byte(vers >> 8)
+		b.data[2] = byte(vers)
+		b.data[3] = byte(m >> 8)
+		b.data[4] = byte(m)
+		if explicitIVLen > 0 {
+			explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
+			if explicitIVIsSeq {
+				copy(explicitIV, c.out.seq[:])
+			} else {
+				if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil {
+					break
+				}
+			}
+		}
+		copy(b.data[recordHeaderLen+explicitIVLen:], data)
+		c.out.encrypt(b, explicitIVLen)
+		_, err = c.conn.Write(b.data)
+		if err != nil {
+			break
+		}
+		n += m
+		data = data[m:]
+	}
+	c.out.freeBlock(b)
+
+	if typ == recordTypeChangeCipherSpec {
+		err = c.out.changeCipherSpec()
+		if err != nil {
+			// Cannot call sendAlert directly,
+			// because we already hold c.out.Mutex.
+			c.tmp[0] = alertLevelError
+			c.tmp[1] = byte(err.(alert))
+			c.writeRecord(recordTypeAlert, c.tmp[0:2])
+			return n, c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
+		}
+	}
+	return
+}
+
+// readHandshake reads the next handshake message from
+// the record layer.
+// c.in.Mutex < L; c.out.Mutex < L.
+func (c *Conn) readHandshake() (interface{}, error) {
+	for c.hand.Len() < 4 {
+		if err := c.in.err; err != nil {
+			return nil, err
+		}
+		if err := c.readRecord(recordTypeHandshake); err != nil {
+			return nil, err
+		}
+	}
+
+	data := c.hand.Bytes()
+	n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
+	if n > maxHandshake {
+		return nil, c.in.setErrorLocked(c.sendAlert(alertInternalError))
+	}
+	for c.hand.Len() < 4+n {
+		if err := c.in.err; err != nil {
+			return nil, err
+		}
+		if err := c.readRecord(recordTypeHandshake); err != nil {
+			return nil, err
+		}
+	}
+	data = c.hand.Next(4 + n)
+	var m handshakeMessage
+	switch data[0] {
+	case typeClientHello:
+		m = new(clientHelloMsg)
+	case typeServerHello:
+		m = new(serverHelloMsg)
+	case typeNewSessionTicket:
+		m = new(newSessionTicketMsg)
+	case typeCertificate:
+		m = new(certificateMsg)
+	case typeCertificateRequest:
+		m = &certificateRequestMsg{
+			hasSignatureAndHash: c.vers >= VersionTLS12,
+		}
+	case typeCertificateStatus:
+		m = new(certificateStatusMsg)
+	case typeServerKeyExchange:
+		m = new(serverKeyExchangeMsg)
+	case typeServerHelloDone:
+		m = new(serverHelloDoneMsg)
+	case typeClientKeyExchange:
+		m = new(clientKeyExchangeMsg)
+	case typeCertificateVerify:
+		m = &certificateVerifyMsg{
+			hasSignatureAndHash: c.vers >= VersionTLS12,
+		}
+	case typeNextProtocol:
+		m = new(nextProtoMsg)
+	case typeFinished:
+		m = new(finishedMsg)
+	default:
+		return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+	}
+
+	// The handshake message unmarshallers
+	// expect to be able to keep references to data,
+	// so pass in a fresh copy that won't be overwritten.
+	data = append([]byte(nil), data...)
+
+	if !m.unmarshal(data) {
+		return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+	}
+	return m, nil
+}
+
+// Write writes data to the connection.
+func (c *Conn) Write(b []byte) (int, error) {
+	if err := c.Handshake(); err != nil {
+		return 0, err
+	}
+
+	c.out.Lock()
+	defer c.out.Unlock()
+
+	if err := c.out.err; err != nil {
+		return 0, err
+	}
+
+	if !c.handshakeComplete {
+		return 0, alertInternalError
+	}
+
+	// SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
+	// attack when using block mode ciphers due to predictable IVs.
+	// This can be prevented by splitting each Application Data
+	// record into two records, effectively randomizing the IV.
+	//
+	// http://www.openssl.org/~bodo/tls-cbc.txt
+	// https://bugzilla.mozilla.org/show_bug.cgi?id=665814
+	// http://www.imperialviolet.org/2012/01/15/beastfollowup.html
+
+	var m int
+	if len(b) > 1 && c.vers <= VersionTLS10 {
+		if _, ok := c.out.cipher.(cipher.BlockMode); ok {
+			n, err := c.writeRecord(recordTypeApplicationData, b[:1])
+			if err != nil {
+				return n, c.out.setErrorLocked(err)
+			}
+			m, b = 1, b[1:]
+		}
+	}
+
+	n, err := c.writeRecord(recordTypeApplicationData, b)
+	return n + m, c.out.setErrorLocked(err)
+}
+
+// Read can be made to time out and return a net.Error with Timeout() == true
+// after a fixed time limit; see SetDeadline and SetReadDeadline.
+func (c *Conn) Read(b []byte) (n int, err error) {
+	if err = c.Handshake(); err != nil {
+		return
+	}
+
+	c.in.Lock()
+	defer c.in.Unlock()
+
+	// Some OpenSSL servers send empty records in order to randomize the
+	// CBC IV. So this loop ignores a limited number of empty records.
+	const maxConsecutiveEmptyRecords = 100
+	for emptyRecordCount := 0; emptyRecordCount <= maxConsecutiveEmptyRecords; emptyRecordCount++ {
+		for c.input == nil && c.in.err == nil {
+			if err := c.readRecord(recordTypeApplicationData); err != nil {
+				// Soft error, like EAGAIN
+				return 0, err
+			}
+		}
+		if err := c.in.err; err != nil {
+			return 0, err
+		}
+
+		n, err = c.input.Read(b)
+		if c.input.off >= len(c.input.data) {
+			c.in.freeBlock(c.input)
+			c.input = nil
+		}
+
+		// If a close-notify alert is waiting, read it so that
+		// we can return (n, EOF) instead of (n, nil), to signal
+		// to the HTTP response reading goroutine that the
+		// connection is now closed. This eliminates a race
+		// where the HTTP response reading goroutine would
+		// otherwise not observe the EOF until its next read,
+		// by which time a client goroutine might have already
+		// tried to reuse the HTTP connection for a new
+		// request.
+		// See https://codereview.appspot.com/76400046
+		// and http://golang.org/issue/3514
+		if ri := c.rawInput; ri != nil &&
+			n != 0 && err == nil &&
+			c.input == nil && len(ri.data) > 0 && recordType(ri.data[0]) == recordTypeAlert {
+			if recErr := c.readRecord(recordTypeApplicationData); recErr != nil {
+				err = recErr // will be io.EOF on closeNotify
+			}
+		}
+
+		if n != 0 || err != nil {
+			return n, err
+		}
+	}
+
+	return 0, io.ErrNoProgress
+}
+
+// Close closes the connection.
+func (c *Conn) Close() error {
+	var alertErr error
+
+	c.handshakeMutex.Lock()
+	defer c.handshakeMutex.Unlock()
+	if c.handshakeComplete {
+		alertErr = c.sendAlert(alertCloseNotify)
+	}
+
+	if err := c.conn.Close(); err != nil {
+		return err
+	}
+	return alertErr
+}
+
+// Handshake runs the client or server handshake
+// protocol if it has not yet been run.
+// Most uses of this package need not call Handshake
+// explicitly: the first Read or Write will call it automatically.
+func (c *Conn) Handshake() error {
+	c.handshakeMutex.Lock()
+	defer c.handshakeMutex.Unlock()
+	if err := c.handshakeErr; err != nil {
+		return err
+	}
+	if c.handshakeComplete {
+		return nil
+	}
+
+	if c.isClient {
+		c.handshakeErr = c.clientHandshake()
+	} else {
+		c.handshakeErr = c.serverHandshake()
+	}
+	return c.handshakeErr
+}
+
+// ConnectionState returns basic TLS details about the connection.
+func (c *Conn) ConnectionState() ConnectionState {
+	c.handshakeMutex.Lock()
+	defer c.handshakeMutex.Unlock()
+
+	var state ConnectionState
+	state.HandshakeComplete = c.handshakeComplete
+	if c.handshakeComplete {
+		state.Version = c.vers
+		state.NegotiatedProtocol = c.clientProtocol
+		state.DidResume = c.didResume
+		state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
+		state.CipherSuite = c.cipherSuite
+		state.PeerCertificates = c.peerCertificates
+		state.VerifiedChains = c.verifiedChains
+		state.ServerName = c.serverName
+	}
+
+	return state
+}
+
+// OCSPResponse returns the stapled OCSP response from the TLS server, if
+// any. (Only valid for client connections.)
+func (c *Conn) OCSPResponse() []byte {
+	c.handshakeMutex.Lock()
+	defer c.handshakeMutex.Unlock()
+
+	return c.ocspResponse
+}
+
+// VerifyHostname checks that the peer certificate chain is valid for
+// connecting to host.  If so, it returns nil; if not, it returns an error
+// describing the problem.
+func (c *Conn) VerifyHostname(host string) error {
+	c.handshakeMutex.Lock()
+	defer c.handshakeMutex.Unlock()
+	if !c.isClient {
+		return errors.New("tls: VerifyHostname called on TLS server connection")
+	}
+	if !c.handshakeComplete {
+		return errors.New("tls: handshake has not yet been performed")
+	}
+	return c.peerCertificates[0].VerifyHostname(host)
+}
diff --git a/ssl/test/runner/ecdsa_cert.pem b/ssl/test/runner/ecdsa_cert.pem
new file mode 100644
index 0000000..50bcbf5
--- /dev/null
+++ b/ssl/test/runner/ecdsa_cert.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBzzCCAXagAwIBAgIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC
+QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp
+dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ
+BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l
+dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni
+v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa
+HPUdfvGULUvPciLBo1AwTjAdBgNVHQ4EFgQUq4TSrKuV8IJOFngHVVdf5CaNgtEw
+HwYDVR0jBBgwFoAUq4TSrKuV8IJOFngHVVdf5CaNgtEwDAYDVR0TBAUwAwEB/zAJ
+BgcqhkjOPQQBA0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13E
+BwIgfB55FGohg/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=
+-----END CERTIFICATE-----
diff --git a/ssl/test/runner/ecdsa_key.pem b/ssl/test/runner/ecdsa_key.pem
new file mode 100644
index 0000000..b9116f0
--- /dev/null
+++ b/ssl/test/runner/ecdsa_key.pem
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIAcPCHJ61KBKnN1ZyU2JaHcItW/JXTB3DujRyc4Ki7RqoAoGCCqGSM49
+AwEHoUQDQgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY
++cPEox5W4nyDSNsWGhz1HX7xlC1Lz3IiwQ==
+-----END EC PRIVATE KEY-----
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
new file mode 100644
index 0000000..f335d03
--- /dev/null
+++ b/ssl/test/runner/handshake_client.go
@@ -0,0 +1,601 @@
+// Copyright 2009 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 main
+
+import (
+	"bytes"
+	"crypto/ecdsa"
+	"crypto/rsa"
+	"crypto/subtle"
+	"crypto/x509"
+	"encoding/asn1"
+	"errors"
+	"fmt"
+	"io"
+	"net"
+	"strconv"
+)
+
+type clientHandshakeState struct {
+	c            *Conn
+	serverHello  *serverHelloMsg
+	hello        *clientHelloMsg
+	suite        *cipherSuite
+	finishedHash finishedHash
+	masterSecret []byte
+	session      *ClientSessionState
+}
+
+func (c *Conn) clientHandshake() error {
+	if c.config == nil {
+		c.config = defaultConfig()
+	}
+
+	if len(c.config.ServerName) == 0 && !c.config.InsecureSkipVerify {
+		return errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
+	}
+
+	hello := &clientHelloMsg{
+		vers:                c.config.maxVersion(),
+		compressionMethods:  []uint8{compressionNone},
+		random:              make([]byte, 32),
+		ocspStapling:        true,
+		serverName:          c.config.ServerName,
+		supportedCurves:     c.config.curvePreferences(),
+		supportedPoints:     []uint8{pointFormatUncompressed},
+		nextProtoNeg:        len(c.config.NextProtos) > 0,
+		secureRenegotiation: true,
+	}
+
+	possibleCipherSuites := c.config.cipherSuites()
+	hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites))
+
+NextCipherSuite:
+	for _, suiteId := range possibleCipherSuites {
+		for _, suite := range cipherSuites {
+			if suite.id != suiteId {
+				continue
+			}
+			// Don't advertise TLS 1.2-only cipher suites unless
+			// we're attempting TLS 1.2.
+			if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 {
+				continue
+			}
+			hello.cipherSuites = append(hello.cipherSuites, suiteId)
+			continue NextCipherSuite
+		}
+	}
+
+	_, err := io.ReadFull(c.config.rand(), hello.random)
+	if err != nil {
+		c.sendAlert(alertInternalError)
+		return errors.New("tls: short read from Rand: " + err.Error())
+	}
+
+	if hello.vers >= VersionTLS12 {
+		hello.signatureAndHashes = supportedSKXSignatureAlgorithms
+	}
+
+	var session *ClientSessionState
+	var cacheKey string
+	sessionCache := c.config.ClientSessionCache
+	if c.config.SessionTicketsDisabled {
+		sessionCache = nil
+	}
+
+	if sessionCache != nil {
+		hello.ticketSupported = true
+
+		// Try to resume a previously negotiated TLS session, if
+		// available.
+		cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
+		candidateSession, ok := sessionCache.Get(cacheKey)
+		if ok {
+			// Check that the ciphersuite/version used for the
+			// previous session are still valid.
+			cipherSuiteOk := false
+			for _, id := range hello.cipherSuites {
+				if id == candidateSession.cipherSuite {
+					cipherSuiteOk = true
+					break
+				}
+			}
+
+			versOk := candidateSession.vers >= c.config.minVersion() &&
+				candidateSession.vers <= c.config.maxVersion()
+			if versOk && cipherSuiteOk {
+				session = candidateSession
+			}
+		}
+	}
+
+	if session != nil {
+		hello.sessionTicket = session.sessionTicket
+		// A random session ID is used to detect when the
+		// server accepted the ticket and is resuming a session
+		// (see RFC 5077).
+		hello.sessionId = make([]byte, 16)
+		if _, err := io.ReadFull(c.config.rand(), hello.sessionId); err != nil {
+			c.sendAlert(alertInternalError)
+			return errors.New("tls: short read from Rand: " + err.Error())
+		}
+	}
+
+	c.writeRecord(recordTypeHandshake, hello.marshal())
+
+	msg, err := c.readHandshake()
+	if err != nil {
+		return err
+	}
+	serverHello, ok := msg.(*serverHelloMsg)
+	if !ok {
+		c.sendAlert(alertUnexpectedMessage)
+		return unexpectedMessageError(serverHello, msg)
+	}
+
+	vers, ok := c.config.mutualVersion(serverHello.vers)
+	if !ok || vers < VersionTLS10 {
+		// TLS 1.0 is the minimum version supported as a client.
+		c.sendAlert(alertProtocolVersion)
+		return fmt.Errorf("tls: server selected unsupported protocol version %x", serverHello.vers)
+	}
+	c.vers = vers
+	c.haveVers = true
+
+	suite := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite)
+	if suite == nil {
+		c.sendAlert(alertHandshakeFailure)
+		return fmt.Errorf("tls: server selected an unsupported cipher suite")
+	}
+
+	hs := &clientHandshakeState{
+		c:            c,
+		serverHello:  serverHello,
+		hello:        hello,
+		suite:        suite,
+		finishedHash: newFinishedHash(c.vers, suite),
+		session:      session,
+	}
+
+	hs.finishedHash.Write(hs.hello.marshal())
+	hs.finishedHash.Write(hs.serverHello.marshal())
+
+	isResume, err := hs.processServerHello()
+	if err != nil {
+		return err
+	}
+
+	if isResume {
+		if err := hs.establishKeys(); err != nil {
+			return err
+		}
+		if err := hs.readSessionTicket(); err != nil {
+			return err
+		}
+		if err := hs.readFinished(); err != nil {
+			return err
+		}
+		if err := hs.sendFinished(); err != nil {
+			return err
+		}
+	} else {
+		if err := hs.doFullHandshake(); err != nil {
+			return err
+		}
+		if err := hs.establishKeys(); err != nil {
+			return err
+		}
+		if err := hs.sendFinished(); err != nil {
+			return err
+		}
+		if err := hs.readSessionTicket(); err != nil {
+			return err
+		}
+		if err := hs.readFinished(); err != nil {
+			return err
+		}
+	}
+
+	if sessionCache != nil && hs.session != nil && session != hs.session {
+		sessionCache.Put(cacheKey, hs.session)
+	}
+
+	c.didResume = isResume
+	c.handshakeComplete = true
+	c.cipherSuite = suite.id
+	return nil
+}
+
+func (hs *clientHandshakeState) doFullHandshake() error {
+	c := hs.c
+
+	msg, err := c.readHandshake()
+	if err != nil {
+		return err
+	}
+	certMsg, ok := msg.(*certificateMsg)
+	if !ok || len(certMsg.certificates) == 0 {
+		c.sendAlert(alertUnexpectedMessage)
+		return unexpectedMessageError(certMsg, msg)
+	}
+	hs.finishedHash.Write(certMsg.marshal())
+
+	certs := make([]*x509.Certificate, len(certMsg.certificates))
+	for i, asn1Data := range certMsg.certificates {
+		cert, err := x509.ParseCertificate(asn1Data)
+		if err != nil {
+			c.sendAlert(alertBadCertificate)
+			return errors.New("tls: failed to parse certificate from server: " + err.Error())
+		}
+		certs[i] = cert
+	}
+
+	if !c.config.InsecureSkipVerify {
+		opts := x509.VerifyOptions{
+			Roots:         c.config.RootCAs,
+			CurrentTime:   c.config.time(),
+			DNSName:       c.config.ServerName,
+			Intermediates: x509.NewCertPool(),
+		}
+
+		for i, cert := range certs {
+			if i == 0 {
+				continue
+			}
+			opts.Intermediates.AddCert(cert)
+		}
+		c.verifiedChains, err = certs[0].Verify(opts)
+		if err != nil {
+			c.sendAlert(alertBadCertificate)
+			return err
+		}
+	}
+
+	switch certs[0].PublicKey.(type) {
+	case *rsa.PublicKey, *ecdsa.PublicKey:
+		break
+	default:
+		c.sendAlert(alertUnsupportedCertificate)
+		return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey)
+	}
+
+	c.peerCertificates = certs
+
+	if hs.serverHello.ocspStapling {
+		msg, err = c.readHandshake()
+		if err != nil {
+			return err
+		}
+		cs, ok := msg.(*certificateStatusMsg)
+		if !ok {
+			c.sendAlert(alertUnexpectedMessage)
+			return unexpectedMessageError(cs, msg)
+		}
+		hs.finishedHash.Write(cs.marshal())
+
+		if cs.statusType == statusTypeOCSP {
+			c.ocspResponse = cs.response
+		}
+	}
+
+	msg, err = c.readHandshake()
+	if err != nil {
+		return err
+	}
+
+	keyAgreement := hs.suite.ka(c.vers)
+
+	skx, ok := msg.(*serverKeyExchangeMsg)
+	if ok {
+		hs.finishedHash.Write(skx.marshal())
+		err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, certs[0], skx)
+		if err != nil {
+			c.sendAlert(alertUnexpectedMessage)
+			return err
+		}
+
+		msg, err = c.readHandshake()
+		if err != nil {
+			return err
+		}
+	}
+
+	var chainToSend *Certificate
+	var certRequested bool
+	certReq, ok := msg.(*certificateRequestMsg)
+	if ok {
+		certRequested = true
+
+		// RFC 4346 on the certificateAuthorities field:
+		// A list of the distinguished names of acceptable certificate
+		// authorities. These distinguished names may specify a desired
+		// distinguished name for a root CA or for a subordinate CA;
+		// thus, this message can be used to describe both known roots
+		// and a desired authorization space. If the
+		// certificate_authorities list is empty then the client MAY
+		// send any certificate of the appropriate
+		// ClientCertificateType, unless there is some external
+		// arrangement to the contrary.
+
+		hs.finishedHash.Write(certReq.marshal())
+
+		var rsaAvail, ecdsaAvail bool
+		for _, certType := range certReq.certificateTypes {
+			switch certType {
+			case certTypeRSASign:
+				rsaAvail = true
+			case certTypeECDSASign:
+				ecdsaAvail = true
+			}
+		}
+
+		// We need to search our list of client certs for one
+		// where SignatureAlgorithm is RSA and the Issuer is in
+		// certReq.certificateAuthorities
+	findCert:
+		for i, chain := range c.config.Certificates {
+			if !rsaAvail && !ecdsaAvail {
+				continue
+			}
+
+			for j, cert := range chain.Certificate {
+				x509Cert := chain.Leaf
+				// parse the certificate if this isn't the leaf
+				// node, or if chain.Leaf was nil
+				if j != 0 || x509Cert == nil {
+					if x509Cert, err = x509.ParseCertificate(cert); err != nil {
+						c.sendAlert(alertInternalError)
+						return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error())
+					}
+				}
+
+				switch {
+				case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA:
+				case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA:
+				default:
+					continue findCert
+				}
+
+				if len(certReq.certificateAuthorities) == 0 {
+					// they gave us an empty list, so just take the
+					// first RSA cert from c.config.Certificates
+					chainToSend = &chain
+					break findCert
+				}
+
+				for _, ca := range certReq.certificateAuthorities {
+					if bytes.Equal(x509Cert.RawIssuer, ca) {
+						chainToSend = &chain
+						break findCert
+					}
+				}
+			}
+		}
+
+		msg, err = c.readHandshake()
+		if err != nil {
+			return err
+		}
+	}
+
+	shd, ok := msg.(*serverHelloDoneMsg)
+	if !ok {
+		c.sendAlert(alertUnexpectedMessage)
+		return unexpectedMessageError(shd, msg)
+	}
+	hs.finishedHash.Write(shd.marshal())
+
+	// If the server requested a certificate then we have to send a
+	// Certificate message, even if it's empty because we don't have a
+	// certificate to send.
+	if certRequested {
+		certMsg = new(certificateMsg)
+		if chainToSend != nil {
+			certMsg.certificates = chainToSend.Certificate
+		}
+		hs.finishedHash.Write(certMsg.marshal())
+		c.writeRecord(recordTypeHandshake, certMsg.marshal())
+	}
+
+	preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, certs[0])
+	if err != nil {
+		c.sendAlert(alertInternalError)
+		return err
+	}
+	if ckx != nil {
+		hs.finishedHash.Write(ckx.marshal())
+		c.writeRecord(recordTypeHandshake, ckx.marshal())
+	}
+
+	if chainToSend != nil {
+		var signed []byte
+		certVerify := &certificateVerifyMsg{
+			hasSignatureAndHash: c.vers >= VersionTLS12,
+		}
+
+		switch key := c.config.Certificates[0].PrivateKey.(type) {
+		case *ecdsa.PrivateKey:
+			digest, _, hashId := hs.finishedHash.hashForClientCertificate(signatureECDSA)
+			r, s, err := ecdsa.Sign(c.config.rand(), key, digest)
+			if err == nil {
+				signed, err = asn1.Marshal(ecdsaSignature{r, s})
+			}
+			certVerify.signatureAndHash.signature = signatureECDSA
+			certVerify.signatureAndHash.hash = hashId
+		case *rsa.PrivateKey:
+			digest, hashFunc, hashId := hs.finishedHash.hashForClientCertificate(signatureRSA)
+			signed, err = rsa.SignPKCS1v15(c.config.rand(), key, hashFunc, digest)
+			certVerify.signatureAndHash.signature = signatureRSA
+			certVerify.signatureAndHash.hash = hashId
+		default:
+			err = errors.New("unknown private key type")
+		}
+		if err != nil {
+			c.sendAlert(alertInternalError)
+			return errors.New("tls: failed to sign handshake with client certificate: " + err.Error())
+		}
+		certVerify.signature = signed
+
+		hs.finishedHash.Write(certVerify.marshal())
+		c.writeRecord(recordTypeHandshake, certVerify.marshal())
+	}
+
+	hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
+	return nil
+}
+
+func (hs *clientHandshakeState) establishKeys() error {
+	c := hs.c
+
+	clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
+		keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
+	var clientCipher, serverCipher interface{}
+	var clientHash, serverHash macFunction
+	if hs.suite.cipher != nil {
+		clientCipher = hs.suite.cipher(clientKey, clientIV, false /* not for reading */)
+		clientHash = hs.suite.mac(c.vers, clientMAC)
+		serverCipher = hs.suite.cipher(serverKey, serverIV, true /* for reading */)
+		serverHash = hs.suite.mac(c.vers, serverMAC)
+	} else {
+		clientCipher = hs.suite.aead(clientKey, clientIV)
+		serverCipher = hs.suite.aead(serverKey, serverIV)
+	}
+
+	c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
+	c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
+	return nil
+}
+
+func (hs *clientHandshakeState) serverResumedSession() bool {
+	// If the server responded with the same sessionId then it means the
+	// sessionTicket is being used to resume a TLS session.
+	return hs.session != nil && hs.hello.sessionId != nil &&
+		bytes.Equal(hs.serverHello.sessionId, hs.hello.sessionId)
+}
+
+func (hs *clientHandshakeState) processServerHello() (bool, error) {
+	c := hs.c
+
+	if hs.serverHello.compressionMethod != compressionNone {
+		c.sendAlert(alertUnexpectedMessage)
+		return false, errors.New("tls: server selected unsupported compression format")
+	}
+
+	if !hs.hello.nextProtoNeg && hs.serverHello.nextProtoNeg {
+		c.sendAlert(alertHandshakeFailure)
+		return false, errors.New("server advertised unrequested NPN extension")
+	}
+
+	if hs.serverResumedSession() {
+		// Restore masterSecret and peerCerts from previous state
+		hs.masterSecret = hs.session.masterSecret
+		c.peerCertificates = hs.session.serverCertificates
+		return true, nil
+	}
+	return false, nil
+}
+
+func (hs *clientHandshakeState) readFinished() error {
+	c := hs.c
+
+	c.readRecord(recordTypeChangeCipherSpec)
+	if err := c.in.error(); err != nil {
+		return err
+	}
+
+	msg, err := c.readHandshake()
+	if err != nil {
+		return err
+	}
+	serverFinished, ok := msg.(*finishedMsg)
+	if !ok {
+		c.sendAlert(alertUnexpectedMessage)
+		return unexpectedMessageError(serverFinished, msg)
+	}
+
+	verify := hs.finishedHash.serverSum(hs.masterSecret)
+	if len(verify) != len(serverFinished.verifyData) ||
+		subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 {
+		c.sendAlert(alertHandshakeFailure)
+		return errors.New("tls: server's Finished message was incorrect")
+	}
+	hs.finishedHash.Write(serverFinished.marshal())
+	return nil
+}
+
+func (hs *clientHandshakeState) readSessionTicket() error {
+	if !hs.serverHello.ticketSupported {
+		return nil
+	}
+
+	c := hs.c
+	msg, err := c.readHandshake()
+	if err != nil {
+		return err
+	}
+	sessionTicketMsg, ok := msg.(*newSessionTicketMsg)
+	if !ok {
+		c.sendAlert(alertUnexpectedMessage)
+		return unexpectedMessageError(sessionTicketMsg, msg)
+	}
+	hs.finishedHash.Write(sessionTicketMsg.marshal())
+
+	hs.session = &ClientSessionState{
+		sessionTicket:      sessionTicketMsg.ticket,
+		vers:               c.vers,
+		cipherSuite:        hs.suite.id,
+		masterSecret:       hs.masterSecret,
+		serverCertificates: c.peerCertificates,
+	}
+
+	return nil
+}
+
+func (hs *clientHandshakeState) sendFinished() error {
+	c := hs.c
+
+	c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+	if hs.serverHello.nextProtoNeg {
+		nextProto := new(nextProtoMsg)
+		proto, fallback := mutualProtocol(c.config.NextProtos, hs.serverHello.nextProtos)
+		nextProto.proto = proto
+		c.clientProtocol = proto
+		c.clientProtocolFallback = fallback
+
+		hs.finishedHash.Write(nextProto.marshal())
+		c.writeRecord(recordTypeHandshake, nextProto.marshal())
+	}
+
+	finished := new(finishedMsg)
+	finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
+	hs.finishedHash.Write(finished.marshal())
+	c.writeRecord(recordTypeHandshake, finished.marshal())
+	return nil
+}
+
+// clientSessionCacheKey returns a key used to cache sessionTickets that could
+// be used to resume previously negotiated TLS sessions with a server.
+func clientSessionCacheKey(serverAddr net.Addr, config *Config) string {
+	if len(config.ServerName) > 0 {
+		return config.ServerName
+	}
+	return serverAddr.String()
+}
+
+// mutualProtocol finds the mutual Next Protocol Negotiation protocol given the
+// set of client and server supported protocols. The set of client supported
+// protocols must not be empty. It returns the resulting protocol and flag
+// indicating if the fallback case was reached.
+func mutualProtocol(clientProtos, serverProtos []string) (string, bool) {
+	for _, s := range serverProtos {
+		for _, c := range clientProtos {
+			if s == c {
+				return s, false
+			}
+		}
+	}
+
+	return clientProtos[0], true
+}
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
new file mode 100644
index 0000000..e31f47b
--- /dev/null
+++ b/ssl/test/runner/handshake_messages.go
@@ -0,0 +1,1344 @@
+// Copyright 2009 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 main
+
+import "bytes"
+
+type clientHelloMsg struct {
+	raw                 []byte
+	vers                uint16
+	random              []byte
+	sessionId           []byte
+	cipherSuites        []uint16
+	compressionMethods  []uint8
+	nextProtoNeg        bool
+	serverName          string
+	ocspStapling        bool
+	supportedCurves     []CurveID
+	supportedPoints     []uint8
+	ticketSupported     bool
+	sessionTicket       []uint8
+	signatureAndHashes  []signatureAndHash
+	secureRenegotiation bool
+}
+
+func (m *clientHelloMsg) equal(i interface{}) bool {
+	m1, ok := i.(*clientHelloMsg)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal(m.raw, m1.raw) &&
+		m.vers == m1.vers &&
+		bytes.Equal(m.random, m1.random) &&
+		bytes.Equal(m.sessionId, m1.sessionId) &&
+		eqUint16s(m.cipherSuites, m1.cipherSuites) &&
+		bytes.Equal(m.compressionMethods, m1.compressionMethods) &&
+		m.nextProtoNeg == m1.nextProtoNeg &&
+		m.serverName == m1.serverName &&
+		m.ocspStapling == m1.ocspStapling &&
+		eqCurveIDs(m.supportedCurves, m1.supportedCurves) &&
+		bytes.Equal(m.supportedPoints, m1.supportedPoints) &&
+		m.ticketSupported == m1.ticketSupported &&
+		bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
+		eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes) &&
+		m.secureRenegotiation == m1.secureRenegotiation
+}
+
+func (m *clientHelloMsg) marshal() []byte {
+	if m.raw != nil {
+		return m.raw
+	}
+
+	length := 2 + 32 + 1 + len(m.sessionId) + 2 + len(m.cipherSuites)*2 + 1 + len(m.compressionMethods)
+	numExtensions := 0
+	extensionsLength := 0
+	if m.nextProtoNeg {
+		numExtensions++
+	}
+	if m.ocspStapling {
+		extensionsLength += 1 + 2 + 2
+		numExtensions++
+	}
+	if len(m.serverName) > 0 {
+		extensionsLength += 5 + len(m.serverName)
+		numExtensions++
+	}
+	if len(m.supportedCurves) > 0 {
+		extensionsLength += 2 + 2*len(m.supportedCurves)
+		numExtensions++
+	}
+	if len(m.supportedPoints) > 0 {
+		extensionsLength += 1 + len(m.supportedPoints)
+		numExtensions++
+	}
+	if m.ticketSupported {
+		extensionsLength += len(m.sessionTicket)
+		numExtensions++
+	}
+	if len(m.signatureAndHashes) > 0 {
+		extensionsLength += 2 + 2*len(m.signatureAndHashes)
+		numExtensions++
+	}
+	if m.secureRenegotiation {
+		extensionsLength += 1
+		numExtensions++
+	}
+	if numExtensions > 0 {
+		extensionsLength += 4 * numExtensions
+		length += 2 + extensionsLength
+	}
+
+	x := make([]byte, 4+length)
+	x[0] = typeClientHello
+	x[1] = uint8(length >> 16)
+	x[2] = uint8(length >> 8)
+	x[3] = uint8(length)
+	x[4] = uint8(m.vers >> 8)
+	x[5] = uint8(m.vers)
+	copy(x[6:38], m.random)
+	x[38] = uint8(len(m.sessionId))
+	copy(x[39:39+len(m.sessionId)], m.sessionId)
+	y := x[39+len(m.sessionId):]
+	y[0] = uint8(len(m.cipherSuites) >> 7)
+	y[1] = uint8(len(m.cipherSuites) << 1)
+	for i, suite := range m.cipherSuites {
+		y[2+i*2] = uint8(suite >> 8)
+		y[3+i*2] = uint8(suite)
+	}
+	z := y[2+len(m.cipherSuites)*2:]
+	z[0] = uint8(len(m.compressionMethods))
+	copy(z[1:], m.compressionMethods)
+
+	z = z[1+len(m.compressionMethods):]
+	if numExtensions > 0 {
+		z[0] = byte(extensionsLength >> 8)
+		z[1] = byte(extensionsLength)
+		z = z[2:]
+	}
+	if m.nextProtoNeg {
+		z[0] = byte(extensionNextProtoNeg >> 8)
+		z[1] = byte(extensionNextProtoNeg & 0xff)
+		// The length is always 0
+		z = z[4:]
+	}
+	if len(m.serverName) > 0 {
+		z[0] = byte(extensionServerName >> 8)
+		z[1] = byte(extensionServerName & 0xff)
+		l := len(m.serverName) + 5
+		z[2] = byte(l >> 8)
+		z[3] = byte(l)
+		z = z[4:]
+
+		// RFC 3546, section 3.1
+		//
+		// struct {
+		//     NameType name_type;
+		//     select (name_type) {
+		//         case host_name: HostName;
+		//     } name;
+		// } ServerName;
+		//
+		// enum {
+		//     host_name(0), (255)
+		// } NameType;
+		//
+		// opaque HostName<1..2^16-1>;
+		//
+		// struct {
+		//     ServerName server_name_list<1..2^16-1>
+		// } ServerNameList;
+
+		z[0] = byte((len(m.serverName) + 3) >> 8)
+		z[1] = byte(len(m.serverName) + 3)
+		z[3] = byte(len(m.serverName) >> 8)
+		z[4] = byte(len(m.serverName))
+		copy(z[5:], []byte(m.serverName))
+		z = z[l:]
+	}
+	if m.ocspStapling {
+		// RFC 4366, section 3.6
+		z[0] = byte(extensionStatusRequest >> 8)
+		z[1] = byte(extensionStatusRequest)
+		z[2] = 0
+		z[3] = 5
+		z[4] = 1 // OCSP type
+		// Two zero valued uint16s for the two lengths.
+		z = z[9:]
+	}
+	if len(m.supportedCurves) > 0 {
+		// http://tools.ietf.org/html/rfc4492#section-5.5.1
+		z[0] = byte(extensionSupportedCurves >> 8)
+		z[1] = byte(extensionSupportedCurves)
+		l := 2 + 2*len(m.supportedCurves)
+		z[2] = byte(l >> 8)
+		z[3] = byte(l)
+		l -= 2
+		z[4] = byte(l >> 8)
+		z[5] = byte(l)
+		z = z[6:]
+		for _, curve := range m.supportedCurves {
+			z[0] = byte(curve >> 8)
+			z[1] = byte(curve)
+			z = z[2:]
+		}
+	}
+	if len(m.supportedPoints) > 0 {
+		// http://tools.ietf.org/html/rfc4492#section-5.5.2
+		z[0] = byte(extensionSupportedPoints >> 8)
+		z[1] = byte(extensionSupportedPoints)
+		l := 1 + len(m.supportedPoints)
+		z[2] = byte(l >> 8)
+		z[3] = byte(l)
+		l--
+		z[4] = byte(l)
+		z = z[5:]
+		for _, pointFormat := range m.supportedPoints {
+			z[0] = byte(pointFormat)
+			z = z[1:]
+		}
+	}
+	if m.ticketSupported {
+		// http://tools.ietf.org/html/rfc5077#section-3.2
+		z[0] = byte(extensionSessionTicket >> 8)
+		z[1] = byte(extensionSessionTicket)
+		l := len(m.sessionTicket)
+		z[2] = byte(l >> 8)
+		z[3] = byte(l)
+		z = z[4:]
+		copy(z, m.sessionTicket)
+		z = z[len(m.sessionTicket):]
+	}
+	if len(m.signatureAndHashes) > 0 {
+		// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
+		z[0] = byte(extensionSignatureAlgorithms >> 8)
+		z[1] = byte(extensionSignatureAlgorithms)
+		l := 2 + 2*len(m.signatureAndHashes)
+		z[2] = byte(l >> 8)
+		z[3] = byte(l)
+		z = z[4:]
+
+		l -= 2
+		z[0] = byte(l >> 8)
+		z[1] = byte(l)
+		z = z[2:]
+		for _, sigAndHash := range m.signatureAndHashes {
+			z[0] = sigAndHash.hash
+			z[1] = sigAndHash.signature
+			z = z[2:]
+		}
+	}
+	if m.secureRenegotiation {
+		z[0] = byte(extensionRenegotiationInfo >> 8)
+		z[1] = byte(extensionRenegotiationInfo & 0xff)
+		z[2] = 0
+		z[3] = 1
+		z = z[5:]
+	}
+
+	m.raw = x
+
+	return x
+}
+
+func (m *clientHelloMsg) unmarshal(data []byte) bool {
+	if len(data) < 42 {
+		return false
+	}
+	m.raw = data
+	m.vers = uint16(data[4])<<8 | uint16(data[5])
+	m.random = data[6:38]
+	sessionIdLen := int(data[38])
+	if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
+		return false
+	}
+	m.sessionId = data[39 : 39+sessionIdLen]
+	data = data[39+sessionIdLen:]
+	if len(data) < 2 {
+		return false
+	}
+	// cipherSuiteLen is the number of bytes of cipher suite numbers. Since
+	// they are uint16s, the number must be even.
+	cipherSuiteLen := int(data[0])<<8 | int(data[1])
+	if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
+		return false
+	}
+	numCipherSuites := cipherSuiteLen / 2
+	m.cipherSuites = make([]uint16, numCipherSuites)
+	for i := 0; i < numCipherSuites; i++ {
+		m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i])
+		if m.cipherSuites[i] == scsvRenegotiation {
+			m.secureRenegotiation = true
+		}
+	}
+	data = data[2+cipherSuiteLen:]
+	if len(data) < 1 {
+		return false
+	}
+	compressionMethodsLen := int(data[0])
+	if len(data) < 1+compressionMethodsLen {
+		return false
+	}
+	m.compressionMethods = data[1 : 1+compressionMethodsLen]
+
+	data = data[1+compressionMethodsLen:]
+
+	m.nextProtoNeg = false
+	m.serverName = ""
+	m.ocspStapling = false
+	m.ticketSupported = false
+	m.sessionTicket = nil
+	m.signatureAndHashes = nil
+
+	if len(data) == 0 {
+		// ClientHello is optionally followed by extension data
+		return true
+	}
+	if len(data) < 2 {
+		return false
+	}
+
+	extensionsLength := int(data[0])<<8 | int(data[1])
+	data = data[2:]
+	if extensionsLength != len(data) {
+		return false
+	}
+
+	for len(data) != 0 {
+		if len(data) < 4 {
+			return false
+		}
+		extension := uint16(data[0])<<8 | uint16(data[1])
+		length := int(data[2])<<8 | int(data[3])
+		data = data[4:]
+		if len(data) < length {
+			return false
+		}
+
+		switch extension {
+		case extensionServerName:
+			if length < 2 {
+				return false
+			}
+			numNames := int(data[0])<<8 | int(data[1])
+			d := data[2:]
+			for i := 0; i < numNames; i++ {
+				if len(d) < 3 {
+					return false
+				}
+				nameType := d[0]
+				nameLen := int(d[1])<<8 | int(d[2])
+				d = d[3:]
+				if len(d) < nameLen {
+					return false
+				}
+				if nameType == 0 {
+					m.serverName = string(d[0:nameLen])
+					break
+				}
+				d = d[nameLen:]
+			}
+		case extensionNextProtoNeg:
+			if length > 0 {
+				return false
+			}
+			m.nextProtoNeg = true
+		case extensionStatusRequest:
+			m.ocspStapling = length > 0 && data[0] == statusTypeOCSP
+		case extensionSupportedCurves:
+			// http://tools.ietf.org/html/rfc4492#section-5.5.1
+			if length < 2 {
+				return false
+			}
+			l := int(data[0])<<8 | int(data[1])
+			if l%2 == 1 || length != l+2 {
+				return false
+			}
+			numCurves := l / 2
+			m.supportedCurves = make([]CurveID, numCurves)
+			d := data[2:]
+			for i := 0; i < numCurves; i++ {
+				m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1])
+				d = d[2:]
+			}
+		case extensionSupportedPoints:
+			// http://tools.ietf.org/html/rfc4492#section-5.5.2
+			if length < 1 {
+				return false
+			}
+			l := int(data[0])
+			if length != l+1 {
+				return false
+			}
+			m.supportedPoints = make([]uint8, l)
+			copy(m.supportedPoints, data[1:])
+		case extensionSessionTicket:
+			// http://tools.ietf.org/html/rfc5077#section-3.2
+			m.ticketSupported = true
+			m.sessionTicket = data[:length]
+		case extensionSignatureAlgorithms:
+			// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
+			if length < 2 || length&1 != 0 {
+				return false
+			}
+			l := int(data[0])<<8 | int(data[1])
+			if l != length-2 {
+				return false
+			}
+			n := l / 2
+			d := data[2:]
+			m.signatureAndHashes = make([]signatureAndHash, n)
+			for i := range m.signatureAndHashes {
+				m.signatureAndHashes[i].hash = d[0]
+				m.signatureAndHashes[i].signature = d[1]
+				d = d[2:]
+			}
+		case extensionRenegotiationInfo + 1:
+			if length != 1 || data[0] != 0 {
+				return false
+			}
+			m.secureRenegotiation = true
+		}
+		data = data[length:]
+	}
+
+	return true
+}
+
+type serverHelloMsg struct {
+	raw                 []byte
+	vers                uint16
+	random              []byte
+	sessionId           []byte
+	cipherSuite         uint16
+	compressionMethod   uint8
+	nextProtoNeg        bool
+	nextProtos          []string
+	ocspStapling        bool
+	ticketSupported     bool
+	secureRenegotiation bool
+}
+
+func (m *serverHelloMsg) equal(i interface{}) bool {
+	m1, ok := i.(*serverHelloMsg)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal(m.raw, m1.raw) &&
+		m.vers == m1.vers &&
+		bytes.Equal(m.random, m1.random) &&
+		bytes.Equal(m.sessionId, m1.sessionId) &&
+		m.cipherSuite == m1.cipherSuite &&
+		m.compressionMethod == m1.compressionMethod &&
+		m.nextProtoNeg == m1.nextProtoNeg &&
+		eqStrings(m.nextProtos, m1.nextProtos) &&
+		m.ocspStapling == m1.ocspStapling &&
+		m.ticketSupported == m1.ticketSupported &&
+		m.secureRenegotiation == m1.secureRenegotiation
+}
+
+func (m *serverHelloMsg) marshal() []byte {
+	if m.raw != nil {
+		return m.raw
+	}
+
+	length := 38 + len(m.sessionId)
+	numExtensions := 0
+	extensionsLength := 0
+
+	nextProtoLen := 0
+	if m.nextProtoNeg {
+		numExtensions++
+		for _, v := range m.nextProtos {
+			nextProtoLen += len(v)
+		}
+		nextProtoLen += len(m.nextProtos)
+		extensionsLength += nextProtoLen
+	}
+	if m.ocspStapling {
+		numExtensions++
+	}
+	if m.ticketSupported {
+		numExtensions++
+	}
+	if m.secureRenegotiation {
+		extensionsLength += 1
+		numExtensions++
+	}
+	if numExtensions > 0 {
+		extensionsLength += 4 * numExtensions
+		length += 2 + extensionsLength
+	}
+
+	x := make([]byte, 4+length)
+	x[0] = typeServerHello
+	x[1] = uint8(length >> 16)
+	x[2] = uint8(length >> 8)
+	x[3] = uint8(length)
+	x[4] = uint8(m.vers >> 8)
+	x[5] = uint8(m.vers)
+	copy(x[6:38], m.random)
+	x[38] = uint8(len(m.sessionId))
+	copy(x[39:39+len(m.sessionId)], m.sessionId)
+	z := x[39+len(m.sessionId):]
+	z[0] = uint8(m.cipherSuite >> 8)
+	z[1] = uint8(m.cipherSuite)
+	z[2] = uint8(m.compressionMethod)
+
+	z = z[3:]
+	if numExtensions > 0 {
+		z[0] = byte(extensionsLength >> 8)
+		z[1] = byte(extensionsLength)
+		z = z[2:]
+	}
+	if m.nextProtoNeg {
+		z[0] = byte(extensionNextProtoNeg >> 8)
+		z[1] = byte(extensionNextProtoNeg & 0xff)
+		z[2] = byte(nextProtoLen >> 8)
+		z[3] = byte(nextProtoLen)
+		z = z[4:]
+
+		for _, v := range m.nextProtos {
+			l := len(v)
+			if l > 255 {
+				l = 255
+			}
+			z[0] = byte(l)
+			copy(z[1:], []byte(v[0:l]))
+			z = z[1+l:]
+		}
+	}
+	if m.ocspStapling {
+		z[0] = byte(extensionStatusRequest >> 8)
+		z[1] = byte(extensionStatusRequest)
+		z = z[4:]
+	}
+	if m.ticketSupported {
+		z[0] = byte(extensionSessionTicket >> 8)
+		z[1] = byte(extensionSessionTicket)
+		z = z[4:]
+	}
+	if m.secureRenegotiation {
+		z[0] = byte(extensionRenegotiationInfo >> 8)
+		z[1] = byte(extensionRenegotiationInfo & 0xff)
+		z[2] = 0
+		z[3] = 1
+		z = z[5:]
+	}
+
+	m.raw = x
+
+	return x
+}
+
+func (m *serverHelloMsg) unmarshal(data []byte) bool {
+	if len(data) < 42 {
+		return false
+	}
+	m.raw = data
+	m.vers = uint16(data[4])<<8 | uint16(data[5])
+	m.random = data[6:38]
+	sessionIdLen := int(data[38])
+	if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
+		return false
+	}
+	m.sessionId = data[39 : 39+sessionIdLen]
+	data = data[39+sessionIdLen:]
+	if len(data) < 3 {
+		return false
+	}
+	m.cipherSuite = uint16(data[0])<<8 | uint16(data[1])
+	m.compressionMethod = data[2]
+	data = data[3:]
+
+	m.nextProtoNeg = false
+	m.nextProtos = nil
+	m.ocspStapling = false
+	m.ticketSupported = false
+
+	if len(data) == 0 {
+		// ServerHello is optionally followed by extension data
+		return true
+	}
+	if len(data) < 2 {
+		return false
+	}
+
+	extensionsLength := int(data[0])<<8 | int(data[1])
+	data = data[2:]
+	if len(data) != extensionsLength {
+		return false
+	}
+
+	for len(data) != 0 {
+		if len(data) < 4 {
+			return false
+		}
+		extension := uint16(data[0])<<8 | uint16(data[1])
+		length := int(data[2])<<8 | int(data[3])
+		data = data[4:]
+		if len(data) < length {
+			return false
+		}
+
+		switch extension {
+		case extensionNextProtoNeg:
+			m.nextProtoNeg = true
+			d := data[:length]
+			for len(d) > 0 {
+				l := int(d[0])
+				d = d[1:]
+				if l == 0 || l > len(d) {
+					return false
+				}
+				m.nextProtos = append(m.nextProtos, string(d[:l]))
+				d = d[l:]
+			}
+		case extensionStatusRequest:
+			if length > 0 {
+				return false
+			}
+			m.ocspStapling = true
+		case extensionSessionTicket:
+			if length > 0 {
+				return false
+			}
+			m.ticketSupported = true
+		case extensionRenegotiationInfo:
+			if length != 1 || data[0] != 0 {
+				return false
+			}
+			m.secureRenegotiation = true
+		}
+		data = data[length:]
+	}
+
+	return true
+}
+
+type certificateMsg struct {
+	raw          []byte
+	certificates [][]byte
+}
+
+func (m *certificateMsg) equal(i interface{}) bool {
+	m1, ok := i.(*certificateMsg)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal(m.raw, m1.raw) &&
+		eqByteSlices(m.certificates, m1.certificates)
+}
+
+func (m *certificateMsg) marshal() (x []byte) {
+	if m.raw != nil {
+		return m.raw
+	}
+
+	var i int
+	for _, slice := range m.certificates {
+		i += len(slice)
+	}
+
+	length := 3 + 3*len(m.certificates) + i
+	x = make([]byte, 4+length)
+	x[0] = typeCertificate
+	x[1] = uint8(length >> 16)
+	x[2] = uint8(length >> 8)
+	x[3] = uint8(length)
+
+	certificateOctets := length - 3
+	x[4] = uint8(certificateOctets >> 16)
+	x[5] = uint8(certificateOctets >> 8)
+	x[6] = uint8(certificateOctets)
+
+	y := x[7:]
+	for _, slice := range m.certificates {
+		y[0] = uint8(len(slice) >> 16)
+		y[1] = uint8(len(slice) >> 8)
+		y[2] = uint8(len(slice))
+		copy(y[3:], slice)
+		y = y[3+len(slice):]
+	}
+
+	m.raw = x
+	return
+}
+
+func (m *certificateMsg) unmarshal(data []byte) bool {
+	if len(data) < 7 {
+		return false
+	}
+
+	m.raw = data
+	certsLen := uint32(data[4])<<16 | uint32(data[5])<<8 | uint32(data[6])
+	if uint32(len(data)) != certsLen+7 {
+		return false
+	}
+
+	numCerts := 0
+	d := data[7:]
+	for certsLen > 0 {
+		if len(d) < 4 {
+			return false
+		}
+		certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
+		if uint32(len(d)) < 3+certLen {
+			return false
+		}
+		d = d[3+certLen:]
+		certsLen -= 3 + certLen
+		numCerts++
+	}
+
+	m.certificates = make([][]byte, numCerts)
+	d = data[7:]
+	for i := 0; i < numCerts; i++ {
+		certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
+		m.certificates[i] = d[3 : 3+certLen]
+		d = d[3+certLen:]
+	}
+
+	return true
+}
+
+type serverKeyExchangeMsg struct {
+	raw []byte
+	key []byte
+}
+
+func (m *serverKeyExchangeMsg) equal(i interface{}) bool {
+	m1, ok := i.(*serverKeyExchangeMsg)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal(m.raw, m1.raw) &&
+		bytes.Equal(m.key, m1.key)
+}
+
+func (m *serverKeyExchangeMsg) marshal() []byte {
+	if m.raw != nil {
+		return m.raw
+	}
+	length := len(m.key)
+	x := make([]byte, length+4)
+	x[0] = typeServerKeyExchange
+	x[1] = uint8(length >> 16)
+	x[2] = uint8(length >> 8)
+	x[3] = uint8(length)
+	copy(x[4:], m.key)
+
+	m.raw = x
+	return x
+}
+
+func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool {
+	m.raw = data
+	if len(data) < 4 {
+		return false
+	}
+	m.key = data[4:]
+	return true
+}
+
+type certificateStatusMsg struct {
+	raw        []byte
+	statusType uint8
+	response   []byte
+}
+
+func (m *certificateStatusMsg) equal(i interface{}) bool {
+	m1, ok := i.(*certificateStatusMsg)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal(m.raw, m1.raw) &&
+		m.statusType == m1.statusType &&
+		bytes.Equal(m.response, m1.response)
+}
+
+func (m *certificateStatusMsg) marshal() []byte {
+	if m.raw != nil {
+		return m.raw
+	}
+
+	var x []byte
+	if m.statusType == statusTypeOCSP {
+		x = make([]byte, 4+4+len(m.response))
+		x[0] = typeCertificateStatus
+		l := len(m.response) + 4
+		x[1] = byte(l >> 16)
+		x[2] = byte(l >> 8)
+		x[3] = byte(l)
+		x[4] = statusTypeOCSP
+
+		l -= 4
+		x[5] = byte(l >> 16)
+		x[6] = byte(l >> 8)
+		x[7] = byte(l)
+		copy(x[8:], m.response)
+	} else {
+		x = []byte{typeCertificateStatus, 0, 0, 1, m.statusType}
+	}
+
+	m.raw = x
+	return x
+}
+
+func (m *certificateStatusMsg) unmarshal(data []byte) bool {
+	m.raw = data
+	if len(data) < 5 {
+		return false
+	}
+	m.statusType = data[4]
+
+	m.response = nil
+	if m.statusType == statusTypeOCSP {
+		if len(data) < 8 {
+			return false
+		}
+		respLen := uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
+		if uint32(len(data)) != 4+4+respLen {
+			return false
+		}
+		m.response = data[8:]
+	}
+	return true
+}
+
+type serverHelloDoneMsg struct{}
+
+func (m *serverHelloDoneMsg) equal(i interface{}) bool {
+	_, ok := i.(*serverHelloDoneMsg)
+	return ok
+}
+
+func (m *serverHelloDoneMsg) marshal() []byte {
+	x := make([]byte, 4)
+	x[0] = typeServerHelloDone
+	return x
+}
+
+func (m *serverHelloDoneMsg) unmarshal(data []byte) bool {
+	return len(data) == 4
+}
+
+type clientKeyExchangeMsg struct {
+	raw        []byte
+	ciphertext []byte
+}
+
+func (m *clientKeyExchangeMsg) equal(i interface{}) bool {
+	m1, ok := i.(*clientKeyExchangeMsg)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal(m.raw, m1.raw) &&
+		bytes.Equal(m.ciphertext, m1.ciphertext)
+}
+
+func (m *clientKeyExchangeMsg) marshal() []byte {
+	if m.raw != nil {
+		return m.raw
+	}
+	length := len(m.ciphertext)
+	x := make([]byte, length+4)
+	x[0] = typeClientKeyExchange
+	x[1] = uint8(length >> 16)
+	x[2] = uint8(length >> 8)
+	x[3] = uint8(length)
+	copy(x[4:], m.ciphertext)
+
+	m.raw = x
+	return x
+}
+
+func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool {
+	m.raw = data
+	if len(data) < 4 {
+		return false
+	}
+	l := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
+	if l != len(data)-4 {
+		return false
+	}
+	m.ciphertext = data[4:]
+	return true
+}
+
+type finishedMsg struct {
+	raw        []byte
+	verifyData []byte
+}
+
+func (m *finishedMsg) equal(i interface{}) bool {
+	m1, ok := i.(*finishedMsg)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal(m.raw, m1.raw) &&
+		bytes.Equal(m.verifyData, m1.verifyData)
+}
+
+func (m *finishedMsg) marshal() (x []byte) {
+	if m.raw != nil {
+		return m.raw
+	}
+
+	x = make([]byte, 4+len(m.verifyData))
+	x[0] = typeFinished
+	x[3] = byte(len(m.verifyData))
+	copy(x[4:], m.verifyData)
+	m.raw = x
+	return
+}
+
+func (m *finishedMsg) unmarshal(data []byte) bool {
+	m.raw = data
+	if len(data) < 4 {
+		return false
+	}
+	m.verifyData = data[4:]
+	return true
+}
+
+type nextProtoMsg struct {
+	raw   []byte
+	proto string
+}
+
+func (m *nextProtoMsg) equal(i interface{}) bool {
+	m1, ok := i.(*nextProtoMsg)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal(m.raw, m1.raw) &&
+		m.proto == m1.proto
+}
+
+func (m *nextProtoMsg) marshal() []byte {
+	if m.raw != nil {
+		return m.raw
+	}
+	l := len(m.proto)
+	if l > 255 {
+		l = 255
+	}
+
+	padding := 32 - (l+2)%32
+	length := l + padding + 2
+	x := make([]byte, length+4)
+	x[0] = typeNextProtocol
+	x[1] = uint8(length >> 16)
+	x[2] = uint8(length >> 8)
+	x[3] = uint8(length)
+
+	y := x[4:]
+	y[0] = byte(l)
+	copy(y[1:], []byte(m.proto[0:l]))
+	y = y[1+l:]
+	y[0] = byte(padding)
+
+	m.raw = x
+
+	return x
+}
+
+func (m *nextProtoMsg) unmarshal(data []byte) bool {
+	m.raw = data
+
+	if len(data) < 5 {
+		return false
+	}
+	data = data[4:]
+	protoLen := int(data[0])
+	data = data[1:]
+	if len(data) < protoLen {
+		return false
+	}
+	m.proto = string(data[0:protoLen])
+	data = data[protoLen:]
+
+	if len(data) < 1 {
+		return false
+	}
+	paddingLen := int(data[0])
+	data = data[1:]
+	if len(data) != paddingLen {
+		return false
+	}
+
+	return true
+}
+
+type certificateRequestMsg struct {
+	raw []byte
+	// hasSignatureAndHash indicates whether this message includes a list
+	// of signature and hash functions. This change was introduced with TLS
+	// 1.2.
+	hasSignatureAndHash bool
+
+	certificateTypes       []byte
+	signatureAndHashes     []signatureAndHash
+	certificateAuthorities [][]byte
+}
+
+func (m *certificateRequestMsg) equal(i interface{}) bool {
+	m1, ok := i.(*certificateRequestMsg)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal(m.raw, m1.raw) &&
+		bytes.Equal(m.certificateTypes, m1.certificateTypes) &&
+		eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) &&
+		eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes)
+}
+
+func (m *certificateRequestMsg) marshal() (x []byte) {
+	if m.raw != nil {
+		return m.raw
+	}
+
+	// See http://tools.ietf.org/html/rfc4346#section-7.4.4
+	length := 1 + len(m.certificateTypes) + 2
+	casLength := 0
+	for _, ca := range m.certificateAuthorities {
+		casLength += 2 + len(ca)
+	}
+	length += casLength
+
+	if m.hasSignatureAndHash {
+		length += 2 + 2*len(m.signatureAndHashes)
+	}
+
+	x = make([]byte, 4+length)
+	x[0] = typeCertificateRequest
+	x[1] = uint8(length >> 16)
+	x[2] = uint8(length >> 8)
+	x[3] = uint8(length)
+
+	x[4] = uint8(len(m.certificateTypes))
+
+	copy(x[5:], m.certificateTypes)
+	y := x[5+len(m.certificateTypes):]
+
+	if m.hasSignatureAndHash {
+		n := len(m.signatureAndHashes) * 2
+		y[0] = uint8(n >> 8)
+		y[1] = uint8(n)
+		y = y[2:]
+		for _, sigAndHash := range m.signatureAndHashes {
+			y[0] = sigAndHash.hash
+			y[1] = sigAndHash.signature
+			y = y[2:]
+		}
+	}
+
+	y[0] = uint8(casLength >> 8)
+	y[1] = uint8(casLength)
+	y = y[2:]
+	for _, ca := range m.certificateAuthorities {
+		y[0] = uint8(len(ca) >> 8)
+		y[1] = uint8(len(ca))
+		y = y[2:]
+		copy(y, ca)
+		y = y[len(ca):]
+	}
+
+	m.raw = x
+	return
+}
+
+func (m *certificateRequestMsg) unmarshal(data []byte) bool {
+	m.raw = data
+
+	if len(data) < 5 {
+		return false
+	}
+
+	length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
+	if uint32(len(data))-4 != length {
+		return false
+	}
+
+	numCertTypes := int(data[4])
+	data = data[5:]
+	if numCertTypes == 0 || len(data) <= numCertTypes {
+		return false
+	}
+
+	m.certificateTypes = make([]byte, numCertTypes)
+	if copy(m.certificateTypes, data) != numCertTypes {
+		return false
+	}
+
+	data = data[numCertTypes:]
+
+	if m.hasSignatureAndHash {
+		if len(data) < 2 {
+			return false
+		}
+		sigAndHashLen := uint16(data[0])<<8 | uint16(data[1])
+		data = data[2:]
+		if sigAndHashLen&1 != 0 {
+			return false
+		}
+		if len(data) < int(sigAndHashLen) {
+			return false
+		}
+		numSigAndHash := sigAndHashLen / 2
+		m.signatureAndHashes = make([]signatureAndHash, numSigAndHash)
+		for i := range m.signatureAndHashes {
+			m.signatureAndHashes[i].hash = data[0]
+			m.signatureAndHashes[i].signature = data[1]
+			data = data[2:]
+		}
+	}
+
+	if len(data) < 2 {
+		return false
+	}
+	casLength := uint16(data[0])<<8 | uint16(data[1])
+	data = data[2:]
+	if len(data) < int(casLength) {
+		return false
+	}
+	cas := make([]byte, casLength)
+	copy(cas, data)
+	data = data[casLength:]
+
+	m.certificateAuthorities = nil
+	for len(cas) > 0 {
+		if len(cas) < 2 {
+			return false
+		}
+		caLen := uint16(cas[0])<<8 | uint16(cas[1])
+		cas = cas[2:]
+
+		if len(cas) < int(caLen) {
+			return false
+		}
+
+		m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen])
+		cas = cas[caLen:]
+	}
+	if len(data) > 0 {
+		return false
+	}
+
+	return true
+}
+
+type certificateVerifyMsg struct {
+	raw                 []byte
+	hasSignatureAndHash bool
+	signatureAndHash    signatureAndHash
+	signature           []byte
+}
+
+func (m *certificateVerifyMsg) equal(i interface{}) bool {
+	m1, ok := i.(*certificateVerifyMsg)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal(m.raw, m1.raw) &&
+		m.hasSignatureAndHash == m1.hasSignatureAndHash &&
+		m.signatureAndHash.hash == m1.signatureAndHash.hash &&
+		m.signatureAndHash.signature == m1.signatureAndHash.signature &&
+		bytes.Equal(m.signature, m1.signature)
+}
+
+func (m *certificateVerifyMsg) marshal() (x []byte) {
+	if m.raw != nil {
+		return m.raw
+	}
+
+	// See http://tools.ietf.org/html/rfc4346#section-7.4.8
+	siglength := len(m.signature)
+	length := 2 + siglength
+	if m.hasSignatureAndHash {
+		length += 2
+	}
+	x = make([]byte, 4+length)
+	x[0] = typeCertificateVerify
+	x[1] = uint8(length >> 16)
+	x[2] = uint8(length >> 8)
+	x[3] = uint8(length)
+	y := x[4:]
+	if m.hasSignatureAndHash {
+		y[0] = m.signatureAndHash.hash
+		y[1] = m.signatureAndHash.signature
+		y = y[2:]
+	}
+	y[0] = uint8(siglength >> 8)
+	y[1] = uint8(siglength)
+	copy(y[2:], m.signature)
+
+	m.raw = x
+
+	return
+}
+
+func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
+	m.raw = data
+
+	if len(data) < 6 {
+		return false
+	}
+
+	length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
+	if uint32(len(data))-4 != length {
+		return false
+	}
+
+	data = data[4:]
+	if m.hasSignatureAndHash {
+		m.signatureAndHash.hash = data[0]
+		m.signatureAndHash.signature = data[1]
+		data = data[2:]
+	}
+
+	if len(data) < 2 {
+		return false
+	}
+	siglength := int(data[0])<<8 + int(data[1])
+	data = data[2:]
+	if len(data) != siglength {
+		return false
+	}
+
+	m.signature = data
+
+	return true
+}
+
+type newSessionTicketMsg struct {
+	raw    []byte
+	ticket []byte
+}
+
+func (m *newSessionTicketMsg) equal(i interface{}) bool {
+	m1, ok := i.(*newSessionTicketMsg)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal(m.raw, m1.raw) &&
+		bytes.Equal(m.ticket, m1.ticket)
+}
+
+func (m *newSessionTicketMsg) marshal() (x []byte) {
+	if m.raw != nil {
+		return m.raw
+	}
+
+	// See http://tools.ietf.org/html/rfc5077#section-3.3
+	ticketLen := len(m.ticket)
+	length := 2 + 4 + ticketLen
+	x = make([]byte, 4+length)
+	x[0] = typeNewSessionTicket
+	x[1] = uint8(length >> 16)
+	x[2] = uint8(length >> 8)
+	x[3] = uint8(length)
+	x[8] = uint8(ticketLen >> 8)
+	x[9] = uint8(ticketLen)
+	copy(x[10:], m.ticket)
+
+	m.raw = x
+
+	return
+}
+
+func (m *newSessionTicketMsg) unmarshal(data []byte) bool {
+	m.raw = data
+
+	if len(data) < 10 {
+		return false
+	}
+
+	length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
+	if uint32(len(data))-4 != length {
+		return false
+	}
+
+	ticketLen := int(data[8])<<8 + int(data[9])
+	if len(data)-10 != ticketLen {
+		return false
+	}
+
+	m.ticket = data[10:]
+
+	return true
+}
+
+func eqUint16s(x, y []uint16) bool {
+	if len(x) != len(y) {
+		return false
+	}
+	for i, v := range x {
+		if y[i] != v {
+			return false
+		}
+	}
+	return true
+}
+
+func eqCurveIDs(x, y []CurveID) bool {
+	if len(x) != len(y) {
+		return false
+	}
+	for i, v := range x {
+		if y[i] != v {
+			return false
+		}
+	}
+	return true
+}
+
+func eqStrings(x, y []string) bool {
+	if len(x) != len(y) {
+		return false
+	}
+	for i, v := range x {
+		if y[i] != v {
+			return false
+		}
+	}
+	return true
+}
+
+func eqByteSlices(x, y [][]byte) bool {
+	if len(x) != len(y) {
+		return false
+	}
+	for i, v := range x {
+		if !bytes.Equal(v, y[i]) {
+			return false
+		}
+	}
+	return true
+}
+
+func eqSignatureAndHashes(x, y []signatureAndHash) bool {
+	if len(x) != len(y) {
+		return false
+	}
+	for i, v := range x {
+		v2 := y[i]
+		if v.hash != v2.hash || v.signature != v2.signature {
+			return false
+		}
+	}
+	return true
+}
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
new file mode 100644
index 0000000..7a44a94
--- /dev/null
+++ b/ssl/test/runner/handshake_server.go
@@ -0,0 +1,659 @@
+// Copyright 2009 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 main
+
+import (
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/rsa"
+	"crypto/subtle"
+	"crypto/x509"
+	"encoding/asn1"
+	"errors"
+	"fmt"
+	"io"
+)
+
+// serverHandshakeState contains details of a server handshake in progress.
+// It's discarded once the handshake has completed.
+type serverHandshakeState struct {
+	c               *Conn
+	clientHello     *clientHelloMsg
+	hello           *serverHelloMsg
+	suite           *cipherSuite
+	ellipticOk      bool
+	ecdsaOk         bool
+	sessionState    *sessionState
+	finishedHash    finishedHash
+	masterSecret    []byte
+	certsFromClient [][]byte
+	cert            *Certificate
+}
+
+// serverHandshake performs a TLS handshake as a server.
+func (c *Conn) serverHandshake() error {
+	config := c.config
+
+	// If this is the first server handshake, we generate a random key to
+	// encrypt the tickets with.
+	config.serverInitOnce.Do(config.serverInit)
+
+	hs := serverHandshakeState{
+		c: c,
+	}
+	isResume, err := hs.readClientHello()
+	if err != nil {
+		return err
+	}
+
+	// For an overview of TLS handshaking, see https://tools.ietf.org/html/rfc5246#section-7.3
+	if isResume {
+		// The client has included a session ticket and so we do an abbreviated handshake.
+		if err := hs.doResumeHandshake(); err != nil {
+			return err
+		}
+		if err := hs.establishKeys(); err != nil {
+			return err
+		}
+		if err := hs.sendFinished(); err != nil {
+			return err
+		}
+		if err := hs.readFinished(); err != nil {
+			return err
+		}
+		c.didResume = true
+	} else {
+		// The client didn't include a session ticket, or it wasn't
+		// valid so we do a full handshake.
+		if err := hs.doFullHandshake(); err != nil {
+			return err
+		}
+		if err := hs.establishKeys(); err != nil {
+			return err
+		}
+		if err := hs.readFinished(); err != nil {
+			return err
+		}
+		if err := hs.sendSessionTicket(); err != nil {
+			return err
+		}
+		if err := hs.sendFinished(); err != nil {
+			return err
+		}
+	}
+	c.handshakeComplete = true
+
+	return nil
+}
+
+// readClientHello reads a ClientHello message from the client and decides
+// whether we will perform session resumption.
+func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
+	config := hs.c.config
+	c := hs.c
+
+	msg, err := c.readHandshake()
+	if err != nil {
+		return false, err
+	}
+	var ok bool
+	hs.clientHello, ok = msg.(*clientHelloMsg)
+	if !ok {
+		c.sendAlert(alertUnexpectedMessage)
+		return false, unexpectedMessageError(hs.clientHello, msg)
+	}
+	c.vers, ok = config.mutualVersion(hs.clientHello.vers)
+	if !ok {
+		c.sendAlert(alertProtocolVersion)
+		return false, fmt.Errorf("tls: client offered an unsupported, maximum protocol version of %x", hs.clientHello.vers)
+	}
+	c.haveVers = true
+
+	hs.hello = new(serverHelloMsg)
+
+	supportedCurve := false
+	preferredCurves := config.curvePreferences()
+Curves:
+	for _, curve := range hs.clientHello.supportedCurves {
+		for _, supported := range preferredCurves {
+			if supported == curve {
+				supportedCurve = true
+				break Curves
+			}
+		}
+	}
+
+	supportedPointFormat := false
+	for _, pointFormat := range hs.clientHello.supportedPoints {
+		if pointFormat == pointFormatUncompressed {
+			supportedPointFormat = true
+			break
+		}
+	}
+	hs.ellipticOk = supportedCurve && supportedPointFormat
+
+	foundCompression := false
+	// We only support null compression, so check that the client offered it.
+	for _, compression := range hs.clientHello.compressionMethods {
+		if compression == compressionNone {
+			foundCompression = true
+			break
+		}
+	}
+
+	if !foundCompression {
+		c.sendAlert(alertHandshakeFailure)
+		return false, errors.New("tls: client does not support uncompressed connections")
+	}
+
+	hs.hello.vers = c.vers
+	hs.hello.random = make([]byte, 32)
+	_, err = io.ReadFull(config.rand(), hs.hello.random)
+	if err != nil {
+		c.sendAlert(alertInternalError)
+		return false, err
+	}
+	hs.hello.secureRenegotiation = hs.clientHello.secureRenegotiation
+	hs.hello.compressionMethod = compressionNone
+	if len(hs.clientHello.serverName) > 0 {
+		c.serverName = hs.clientHello.serverName
+	}
+	// Although sending an empty NPN extension is reasonable, Firefox has
+	// had a bug around this. Best to send nothing at all if
+	// config.NextProtos is empty. See
+	// https://code.google.com/p/go/issues/detail?id=5445.
+	if hs.clientHello.nextProtoNeg && len(config.NextProtos) > 0 {
+		hs.hello.nextProtoNeg = true
+		hs.hello.nextProtos = config.NextProtos
+	}
+
+	if len(config.Certificates) == 0 {
+		c.sendAlert(alertInternalError)
+		return false, errors.New("tls: no certificates configured")
+	}
+	hs.cert = &config.Certificates[0]
+	if len(hs.clientHello.serverName) > 0 {
+		hs.cert = config.getCertificateForName(hs.clientHello.serverName)
+	}
+
+	_, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey)
+
+	if hs.checkForResumption() {
+		return true, nil
+	}
+
+	var preferenceList, supportedList []uint16
+	if c.config.PreferServerCipherSuites {
+		preferenceList = c.config.cipherSuites()
+		supportedList = hs.clientHello.cipherSuites
+	} else {
+		preferenceList = hs.clientHello.cipherSuites
+		supportedList = c.config.cipherSuites()
+	}
+
+	for _, id := range preferenceList {
+		if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, hs.ellipticOk, hs.ecdsaOk); hs.suite != nil {
+			break
+		}
+	}
+
+	if hs.suite == nil {
+		c.sendAlert(alertHandshakeFailure)
+		return false, errors.New("tls: no cipher suite supported by both client and server")
+	}
+
+	return false, nil
+}
+
+// checkForResumption returns true if we should perform resumption on this connection.
+func (hs *serverHandshakeState) checkForResumption() bool {
+	c := hs.c
+
+	var ok bool
+	if hs.sessionState, ok = c.decryptTicket(hs.clientHello.sessionTicket); !ok {
+		return false
+	}
+
+	if hs.sessionState.vers > hs.clientHello.vers {
+		return false
+	}
+	if vers, ok := c.config.mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers {
+		return false
+	}
+
+	cipherSuiteOk := false
+	// Check that the client is still offering the ciphersuite in the session.
+	for _, id := range hs.clientHello.cipherSuites {
+		if id == hs.sessionState.cipherSuite {
+			cipherSuiteOk = true
+			break
+		}
+	}
+	if !cipherSuiteOk {
+		return false
+	}
+
+	// Check that we also support the ciphersuite from the session.
+	hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.sessionState.vers, hs.ellipticOk, hs.ecdsaOk)
+	if hs.suite == nil {
+		return false
+	}
+
+	sessionHasClientCerts := len(hs.sessionState.certificates) != 0
+	needClientCerts := c.config.ClientAuth == RequireAnyClientCert || c.config.ClientAuth == RequireAndVerifyClientCert
+	if needClientCerts && !sessionHasClientCerts {
+		return false
+	}
+	if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
+		return false
+	}
+
+	return true
+}
+
+func (hs *serverHandshakeState) doResumeHandshake() error {
+	c := hs.c
+
+	hs.hello.cipherSuite = hs.suite.id
+	// We echo the client's session ID in the ServerHello to let it know
+	// that we're doing a resumption.
+	hs.hello.sessionId = hs.clientHello.sessionId
+
+	hs.finishedHash = newFinishedHash(c.vers, hs.suite)
+	hs.finishedHash.Write(hs.clientHello.marshal())
+	hs.finishedHash.Write(hs.hello.marshal())
+
+	c.writeRecord(recordTypeHandshake, hs.hello.marshal())
+
+	if len(hs.sessionState.certificates) > 0 {
+		if _, err := hs.processCertsFromClient(hs.sessionState.certificates); err != nil {
+			return err
+		}
+	}
+
+	hs.masterSecret = hs.sessionState.masterSecret
+
+	return nil
+}
+
+func (hs *serverHandshakeState) doFullHandshake() error {
+	config := hs.c.config
+	c := hs.c
+
+	if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
+		hs.hello.ocspStapling = true
+	}
+
+	hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled
+	hs.hello.cipherSuite = hs.suite.id
+
+	hs.finishedHash = newFinishedHash(c.vers, hs.suite)
+	hs.finishedHash.Write(hs.clientHello.marshal())
+	hs.finishedHash.Write(hs.hello.marshal())
+
+	c.writeRecord(recordTypeHandshake, hs.hello.marshal())
+
+	certMsg := new(certificateMsg)
+	certMsg.certificates = hs.cert.Certificate
+	hs.finishedHash.Write(certMsg.marshal())
+	c.writeRecord(recordTypeHandshake, certMsg.marshal())
+
+	if hs.hello.ocspStapling {
+		certStatus := new(certificateStatusMsg)
+		certStatus.statusType = statusTypeOCSP
+		certStatus.response = hs.cert.OCSPStaple
+		hs.finishedHash.Write(certStatus.marshal())
+		c.writeRecord(recordTypeHandshake, certStatus.marshal())
+	}
+
+	keyAgreement := hs.suite.ka(c.vers)
+	skx, err := keyAgreement.generateServerKeyExchange(config, hs.cert, hs.clientHello, hs.hello)
+	if err != nil {
+		c.sendAlert(alertHandshakeFailure)
+		return err
+	}
+	if skx != nil {
+		hs.finishedHash.Write(skx.marshal())
+		c.writeRecord(recordTypeHandshake, skx.marshal())
+	}
+
+	if config.ClientAuth >= RequestClientCert {
+		// Request a client certificate
+		certReq := new(certificateRequestMsg)
+		certReq.certificateTypes = []byte{
+			byte(certTypeRSASign),
+			byte(certTypeECDSASign),
+		}
+		if c.vers >= VersionTLS12 {
+			certReq.hasSignatureAndHash = true
+			certReq.signatureAndHashes = supportedClientCertSignatureAlgorithms
+		}
+
+		// An empty list of certificateAuthorities signals to
+		// the client that it may send any certificate in response
+		// to our request. When we know the CAs we trust, then
+		// we can send them down, so that the client can choose
+		// an appropriate certificate to give to us.
+		if config.ClientCAs != nil {
+			certReq.certificateAuthorities = config.ClientCAs.Subjects()
+		}
+		hs.finishedHash.Write(certReq.marshal())
+		c.writeRecord(recordTypeHandshake, certReq.marshal())
+	}
+
+	helloDone := new(serverHelloDoneMsg)
+	hs.finishedHash.Write(helloDone.marshal())
+	c.writeRecord(recordTypeHandshake, helloDone.marshal())
+
+	var pub crypto.PublicKey // public key for client auth, if any
+
+	msg, err := c.readHandshake()
+	if err != nil {
+		return err
+	}
+
+	var ok bool
+	// If we requested a client certificate, then the client must send a
+	// certificate message, even if it's empty.
+	if config.ClientAuth >= RequestClientCert {
+		if certMsg, ok = msg.(*certificateMsg); !ok {
+			c.sendAlert(alertUnexpectedMessage)
+			return unexpectedMessageError(certMsg, msg)
+		}
+		hs.finishedHash.Write(certMsg.marshal())
+
+		if len(certMsg.certificates) == 0 {
+			// The client didn't actually send a certificate
+			switch config.ClientAuth {
+			case RequireAnyClientCert, RequireAndVerifyClientCert:
+				c.sendAlert(alertBadCertificate)
+				return errors.New("tls: client didn't provide a certificate")
+			}
+		}
+
+		pub, err = hs.processCertsFromClient(certMsg.certificates)
+		if err != nil {
+			return err
+		}
+
+		msg, err = c.readHandshake()
+		if err != nil {
+			return err
+		}
+	}
+
+	// Get client key exchange
+	ckx, ok := msg.(*clientKeyExchangeMsg)
+	if !ok {
+		c.sendAlert(alertUnexpectedMessage)
+		return unexpectedMessageError(ckx, msg)
+	}
+	hs.finishedHash.Write(ckx.marshal())
+
+	// If we received a client cert in response to our certificate request message,
+	// the client will send us a certificateVerifyMsg immediately after the
+	// clientKeyExchangeMsg.  This message is a digest of all preceding
+	// handshake-layer messages that is signed using the private key corresponding
+	// to the client's certificate. This allows us to verify that the client is in
+	// possession of the private key of the certificate.
+	if len(c.peerCertificates) > 0 {
+		msg, err = c.readHandshake()
+		if err != nil {
+			return err
+		}
+		certVerify, ok := msg.(*certificateVerifyMsg)
+		if !ok {
+			c.sendAlert(alertUnexpectedMessage)
+			return unexpectedMessageError(certVerify, msg)
+		}
+
+		switch key := pub.(type) {
+		case *ecdsa.PublicKey:
+			ecdsaSig := new(ecdsaSignature)
+			if _, err = asn1.Unmarshal(certVerify.signature, ecdsaSig); err != nil {
+				break
+			}
+			if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
+				err = errors.New("ECDSA signature contained zero or negative values")
+				break
+			}
+			digest, _, _ := hs.finishedHash.hashForClientCertificate(signatureECDSA)
+			if !ecdsa.Verify(key, digest, ecdsaSig.R, ecdsaSig.S) {
+				err = errors.New("ECDSA verification failure")
+				break
+			}
+		case *rsa.PublicKey:
+			digest, hashFunc, _ := hs.finishedHash.hashForClientCertificate(signatureRSA)
+			err = rsa.VerifyPKCS1v15(key, hashFunc, digest, certVerify.signature)
+		}
+		if err != nil {
+			c.sendAlert(alertBadCertificate)
+			return errors.New("could not validate signature of connection nonces: " + err.Error())
+		}
+
+		hs.finishedHash.Write(certVerify.marshal())
+	}
+
+	preMasterSecret, err := keyAgreement.processClientKeyExchange(config, hs.cert, ckx, c.vers)
+	if err != nil {
+		c.sendAlert(alertHandshakeFailure)
+		return err
+	}
+	hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
+
+	return nil
+}
+
+func (hs *serverHandshakeState) establishKeys() error {
+	c := hs.c
+
+	clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
+		keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
+
+	var clientCipher, serverCipher interface{}
+	var clientHash, serverHash macFunction
+
+	if hs.suite.aead == nil {
+		clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */)
+		clientHash = hs.suite.mac(c.vers, clientMAC)
+		serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
+		serverHash = hs.suite.mac(c.vers, serverMAC)
+	} else {
+		clientCipher = hs.suite.aead(clientKey, clientIV)
+		serverCipher = hs.suite.aead(serverKey, serverIV)
+	}
+
+	c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
+	c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
+
+	return nil
+}
+
+func (hs *serverHandshakeState) readFinished() error {
+	c := hs.c
+
+	c.readRecord(recordTypeChangeCipherSpec)
+	if err := c.in.error(); err != nil {
+		return err
+	}
+
+	if hs.hello.nextProtoNeg {
+		msg, err := c.readHandshake()
+		if err != nil {
+			return err
+		}
+		nextProto, ok := msg.(*nextProtoMsg)
+		if !ok {
+			c.sendAlert(alertUnexpectedMessage)
+			return unexpectedMessageError(nextProto, msg)
+		}
+		hs.finishedHash.Write(nextProto.marshal())
+		c.clientProtocol = nextProto.proto
+	}
+
+	msg, err := c.readHandshake()
+	if err != nil {
+		return err
+	}
+	clientFinished, ok := msg.(*finishedMsg)
+	if !ok {
+		c.sendAlert(alertUnexpectedMessage)
+		return unexpectedMessageError(clientFinished, msg)
+	}
+
+	verify := hs.finishedHash.clientSum(hs.masterSecret)
+	if len(verify) != len(clientFinished.verifyData) ||
+		subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
+		c.sendAlert(alertHandshakeFailure)
+		return errors.New("tls: client's Finished message is incorrect")
+	}
+
+	hs.finishedHash.Write(clientFinished.marshal())
+	return nil
+}
+
+func (hs *serverHandshakeState) sendSessionTicket() error {
+	if !hs.hello.ticketSupported {
+		return nil
+	}
+
+	c := hs.c
+	m := new(newSessionTicketMsg)
+
+	var err error
+	state := sessionState{
+		vers:         c.vers,
+		cipherSuite:  hs.suite.id,
+		masterSecret: hs.masterSecret,
+		certificates: hs.certsFromClient,
+	}
+	m.ticket, err = c.encryptTicket(&state)
+	if err != nil {
+		return err
+	}
+	m.ticket = make([]byte, 16105+62)
+	for i := range m.ticket {
+		m.ticket[i] = 'A'
+	}
+
+	hs.finishedHash.Write(m.marshal())
+	c.writeRecord(recordTypeHandshake, m.marshal())
+
+	return nil
+}
+
+func (hs *serverHandshakeState) sendFinished() error {
+	c := hs.c
+
+	c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+
+	finished := new(finishedMsg)
+	finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
+	hs.finishedHash.Write(finished.marshal())
+	c.writeRecord(recordTypeHandshake, finished.marshal())
+
+	c.cipherSuite = hs.suite.id
+
+	return nil
+}
+
+// processCertsFromClient takes a chain of client certificates either from a
+// Certificates message or from a sessionState and verifies them. It returns
+// the public key of the leaf certificate.
+func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (crypto.PublicKey, error) {
+	c := hs.c
+
+	hs.certsFromClient = certificates
+	certs := make([]*x509.Certificate, len(certificates))
+	var err error
+	for i, asn1Data := range certificates {
+		if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
+			c.sendAlert(alertBadCertificate)
+			return nil, errors.New("tls: failed to parse client certificate: " + err.Error())
+		}
+	}
+
+	if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
+		opts := x509.VerifyOptions{
+			Roots:         c.config.ClientCAs,
+			CurrentTime:   c.config.time(),
+			Intermediates: x509.NewCertPool(),
+			KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
+		}
+
+		for _, cert := range certs[1:] {
+			opts.Intermediates.AddCert(cert)
+		}
+
+		chains, err := certs[0].Verify(opts)
+		if err != nil {
+			c.sendAlert(alertBadCertificate)
+			return nil, errors.New("tls: failed to verify client's certificate: " + err.Error())
+		}
+
+		ok := false
+		for _, ku := range certs[0].ExtKeyUsage {
+			if ku == x509.ExtKeyUsageClientAuth {
+				ok = true
+				break
+			}
+		}
+		if !ok {
+			c.sendAlert(alertHandshakeFailure)
+			return nil, errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication")
+		}
+
+		c.verifiedChains = chains
+	}
+
+	if len(certs) > 0 {
+		var pub crypto.PublicKey
+		switch key := certs[0].PublicKey.(type) {
+		case *ecdsa.PublicKey, *rsa.PublicKey:
+			pub = key
+		default:
+			c.sendAlert(alertUnsupportedCertificate)
+			return nil, fmt.Errorf("tls: client's certificate contains an unsupported public key of type %T", certs[0].PublicKey)
+		}
+		c.peerCertificates = certs
+		return pub, nil
+	}
+
+	return nil, nil
+}
+
+// tryCipherSuite returns a cipherSuite with the given id if that cipher suite
+// is acceptable to use.
+func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16, ellipticOk, ecdsaOk bool) *cipherSuite {
+	for _, supported := range supportedCipherSuites {
+		if id == supported {
+			var candidate *cipherSuite
+
+			for _, s := range cipherSuites {
+				if s.id == id {
+					candidate = s
+					break
+				}
+			}
+			if candidate == nil {
+				continue
+			}
+			// Don't select a ciphersuite which we can't
+			// support for this client.
+			if (candidate.flags&suiteECDHE != 0) && !ellipticOk {
+				continue
+			}
+			if (candidate.flags&suiteECDSA != 0) != ecdsaOk {
+				continue
+			}
+			if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 {
+				continue
+			}
+			return candidate
+		}
+	}
+
+	return nil
+}
diff --git a/ssl/test/runner/key.pem b/ssl/test/runner/key.pem
new file mode 100644
index 0000000..e9107bf
--- /dev/null
+++ b/ssl/test/runner/key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDYK8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92
+kWdGMdAQhLciHnAjkXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiF
+KKAnHmUcrgfVW28tQ+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQAB
+AoGBAIBy09Fd4DOq/Ijp8HeKuCMKTHqTW1xGHshLQ6jwVV2vWZIn9aIgmDsvkjCe
+i6ssZvnbjVcwzSoByhjN8ZCf/i15HECWDFFh6gt0P5z0MnChwzZmvatV/FXCT0j+
+WmGNB/gkehKjGXLLcjTb6dRYVJSCZhVuOLLcbWIV10gggJQBAkEA8S8sGe4ezyyZ
+m4e9r95g6s43kPqtj5rewTsUxt+2n4eVodD+ZUlCULWVNAFLkYRTBCASlSrm9Xhj
+QpmWAHJUkQJBAOVzQdFUaewLtdOJoPCtpYoY1zd22eae8TQEmpGOR11L6kbxLQsk
+aMly/DOnOaa82tqAGTdqDEZgSNmCeKKknmECQAvpnY8GUOVAubGR6c+W90iBuQLj
+LtFp/9ihd2w/PoDwrHZaoUYVcT4VSfJQog/k7kjE4MYXYWL8eEKg3WTWQNECQQDk
+104Wi91Umd1PzF0ijd2jXOERJU1wEKe6XLkYYNHWQAe5l4J4MWj9OdxFXAxIuuR/
+tfDwbqkta4xcux67//khAkEAvvRXLHTaa6VFzTaiiO8SaFsHV3lQyXOtMrBpB5jd
+moZWgjHvB2W9Ckn7sDqsPB+U2tyX0joDdQEyuiMECDY8oQ==
+-----END RSA PRIVATE KEY-----
diff --git a/ssl/test/runner/key_agreement.go b/ssl/test/runner/key_agreement.go
new file mode 100644
index 0000000..991a91d
--- /dev/null
+++ b/ssl/test/runner/key_agreement.go
@@ -0,0 +1,436 @@
+// 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 main
+
+import (
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/md5"
+	"crypto/rsa"
+	"crypto/sha1"
+	"crypto/sha256"
+	"crypto/x509"
+	"encoding/asn1"
+	"errors"
+	"io"
+	"math/big"
+)
+
+var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
+var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message")
+
+// rsaKeyAgreement implements the standard TLS key agreement where the client
+// encrypts the pre-master secret to the server's public key.
+type rsaKeyAgreement struct{}
+
+func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+	return nil, nil
+}
+
+func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+	preMasterSecret := make([]byte, 48)
+	_, err := io.ReadFull(config.rand(), preMasterSecret[2:])
+	if err != nil {
+		return nil, err
+	}
+
+	if len(ckx.ciphertext) < 2 {
+		return nil, errClientKeyExchange
+	}
+
+	ciphertext := ckx.ciphertext
+	if version != VersionSSL30 {
+		ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
+		if ciphertextLen != len(ckx.ciphertext)-2 {
+			return nil, errClientKeyExchange
+		}
+		ciphertext = ckx.ciphertext[2:]
+	}
+
+	err = rsa.DecryptPKCS1v15SessionKey(config.rand(), cert.PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret)
+	if err != nil {
+		return nil, err
+	}
+	// We don't check the version number in the premaster secret.  For one,
+	// by checking it, we would leak information about the validity of the
+	// encrypted pre-master secret. Secondly, it provides only a small
+	// benefit against a downgrade attack and some implementations send the
+	// wrong version anyway. See the discussion at the end of section
+	// 7.4.7.1 of RFC 4346.
+	return preMasterSecret, nil
+}
+
+func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+	return errors.New("tls: unexpected ServerKeyExchange")
+}
+
+func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+	preMasterSecret := make([]byte, 48)
+	preMasterSecret[0] = byte(clientHello.vers >> 8)
+	preMasterSecret[1] = byte(clientHello.vers)
+	_, err := io.ReadFull(config.rand(), preMasterSecret[2:])
+	if err != nil {
+		return nil, nil, err
+	}
+
+	encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), preMasterSecret)
+	if err != nil {
+		return nil, nil, err
+	}
+	ckx := new(clientKeyExchangeMsg)
+	ckx.ciphertext = make([]byte, len(encrypted)+2)
+	ckx.ciphertext[0] = byte(len(encrypted) >> 8)
+	ckx.ciphertext[1] = byte(len(encrypted))
+	copy(ckx.ciphertext[2:], encrypted)
+	return preMasterSecret, ckx, nil
+}
+
+// sha1Hash calculates a SHA1 hash over the given byte slices.
+func sha1Hash(slices [][]byte) []byte {
+	hsha1 := sha1.New()
+	for _, slice := range slices {
+		hsha1.Write(slice)
+	}
+	return hsha1.Sum(nil)
+}
+
+// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the
+// concatenation of an MD5 and SHA1 hash.
+func md5SHA1Hash(slices [][]byte) []byte {
+	md5sha1 := make([]byte, md5.Size+sha1.Size)
+	hmd5 := md5.New()
+	for _, slice := range slices {
+		hmd5.Write(slice)
+	}
+	copy(md5sha1, hmd5.Sum(nil))
+	copy(md5sha1[md5.Size:], sha1Hash(slices))
+	return md5sha1
+}
+
+// sha256Hash implements TLS 1.2's hash function.
+func sha256Hash(slices [][]byte) []byte {
+	h := sha256.New()
+	for _, slice := range slices {
+		h.Write(slice)
+	}
+	return h.Sum(nil)
+}
+
+// hashForServerKeyExchange hashes the given slices and returns their digest
+// and the identifier of the hash function used. The hashFunc argument is only
+// used for >= TLS 1.2 and precisely identifies the hash function to use.
+func hashForServerKeyExchange(sigType, hashFunc uint8, version uint16, slices ...[]byte) ([]byte, crypto.Hash, error) {
+	if version >= VersionTLS12 {
+		switch hashFunc {
+		case hashSHA256:
+			return sha256Hash(slices), crypto.SHA256, nil
+		case hashSHA1:
+			return sha1Hash(slices), crypto.SHA1, nil
+		default:
+			return nil, crypto.Hash(0), errors.New("tls: unknown hash function used by peer")
+		}
+	}
+	if sigType == signatureECDSA {
+		return sha1Hash(slices), crypto.SHA1, nil
+	}
+	return md5SHA1Hash(slices), crypto.MD5SHA1, nil
+}
+
+// pickTLS12HashForSignature returns a TLS 1.2 hash identifier for signing a
+// ServerKeyExchange given the signature type being used and the client's
+// advertized list of supported signature and hash combinations.
+func pickTLS12HashForSignature(sigType uint8, clientSignatureAndHashes []signatureAndHash) (uint8, error) {
+	if len(clientSignatureAndHashes) == 0 {
+		// If the client didn't specify any signature_algorithms
+		// extension then we can assume that it supports SHA1. See
+		// http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
+		return hashSHA1, nil
+	}
+
+	for _, sigAndHash := range clientSignatureAndHashes {
+		if sigAndHash.signature != sigType {
+			continue
+		}
+		switch sigAndHash.hash {
+		case hashSHA1, hashSHA256:
+			return sigAndHash.hash, nil
+		}
+	}
+
+	return 0, errors.New("tls: client doesn't support any common hash functions")
+}
+
+func curveForCurveID(id CurveID) (elliptic.Curve, bool) {
+	switch id {
+	case CurveP256:
+		return elliptic.P256(), true
+	case CurveP384:
+		return elliptic.P384(), true
+	case CurveP521:
+		return elliptic.P521(), true
+	default:
+		return nil, false
+	}
+
+}
+
+// ecdheRSAKeyAgreement implements a TLS key agreement where the server
+// generates a ephemeral EC public/private key pair and signs it. The
+// pre-master secret is then calculated using ECDH. The signature may
+// either be ECDSA or RSA.
+type ecdheKeyAgreement struct {
+	version    uint16
+	sigType    uint8
+	privateKey []byte
+	curve      elliptic.Curve
+	x, y       *big.Int
+}
+
+func maybeCorruptECDSAValue(n *big.Int, typeOfCorruption BadValue, limit *big.Int) *big.Int {
+	switch typeOfCorruption {
+	case BadValueNone:
+		return n
+	case BadValueNegative:
+		return new(big.Int).Neg(n)
+	case BadValueZero:
+		return big.NewInt(0)
+	case BadValueLimit:
+		return limit
+	case BadValueLarge:
+		bad := new(big.Int).Set(limit)
+		return bad.Lsh(bad, 20)
+	default:
+		panic("unknown BadValue type")
+	}
+}
+
+func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+	var curveid CurveID
+	preferredCurves := config.curvePreferences()
+
+NextCandidate:
+	for _, candidate := range preferredCurves {
+		for _, c := range clientHello.supportedCurves {
+			if candidate == c {
+				curveid = c
+				break NextCandidate
+			}
+		}
+	}
+
+	if curveid == 0 {
+		return nil, errors.New("tls: no supported elliptic curves offered")
+	}
+
+	var ok bool
+	if ka.curve, ok = curveForCurveID(curveid); !ok {
+		return nil, errors.New("tls: preferredCurves includes unsupported curve")
+	}
+
+	var x, y *big.Int
+	var err error
+	ka.privateKey, x, y, err = elliptic.GenerateKey(ka.curve, config.rand())
+	if err != nil {
+		return nil, err
+	}
+	ecdhePublic := elliptic.Marshal(ka.curve, x, y)
+
+	// http://tools.ietf.org/html/rfc4492#section-5.4
+	serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic))
+	serverECDHParams[0] = 3 // named curve
+	serverECDHParams[1] = byte(curveid >> 8)
+	serverECDHParams[2] = byte(curveid)
+	if config.Bugs.InvalidSKXCurve {
+		serverECDHParams[2] ^= 0xff
+	}
+	serverECDHParams[3] = byte(len(ecdhePublic))
+	copy(serverECDHParams[4:], ecdhePublic)
+
+	var tls12HashId uint8
+	if ka.version >= VersionTLS12 {
+		if tls12HashId, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes); err != nil {
+			return nil, err
+		}
+	}
+
+	digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, hello.random, serverECDHParams)
+	if err != nil {
+		return nil, err
+	}
+
+	if config.Bugs.InvalidSKXSignature {
+		digest[0] ^= 0x80
+	}
+
+	var sig []byte
+	switch ka.sigType {
+	case signatureECDSA:
+		privKey, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
+		if !ok {
+			return nil, errors.New("ECDHE ECDSA requires an ECDSA server private key")
+		}
+		r, s, err := ecdsa.Sign(config.rand(), privKey, digest)
+		if err != nil {
+			return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
+		}
+		order := privKey.Curve.Params().N
+		r = maybeCorruptECDSAValue(r, config.Bugs.BadECDSAR, order)
+		s = maybeCorruptECDSAValue(s, config.Bugs.BadECDSAS, order)
+		sig, err = asn1.Marshal(ecdsaSignature{r, s})
+	case signatureRSA:
+		privKey, ok := cert.PrivateKey.(*rsa.PrivateKey)
+		if !ok {
+			return nil, errors.New("ECDHE RSA requires a RSA server private key")
+		}
+		sig, err = rsa.SignPKCS1v15(config.rand(), privKey, hashFunc, digest)
+		if err != nil {
+			return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
+		}
+	default:
+		return nil, errors.New("unknown ECDHE signature algorithm")
+	}
+
+	skx := new(serverKeyExchangeMsg)
+	sigAndHashLen := 0
+	if ka.version >= VersionTLS12 {
+		sigAndHashLen = 2
+	}
+	skx.key = make([]byte, len(serverECDHParams)+sigAndHashLen+2+len(sig))
+	copy(skx.key, serverECDHParams)
+	k := skx.key[len(serverECDHParams):]
+	if ka.version >= VersionTLS12 {
+		k[0] = tls12HashId
+		k[1] = ka.sigType
+		k = k[2:]
+	}
+	k[0] = byte(len(sig) >> 8)
+	k[1] = byte(len(sig))
+	copy(k[2:], sig)
+
+	return skx, nil
+}
+
+func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+	if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
+		return nil, errClientKeyExchange
+	}
+	x, y := elliptic.Unmarshal(ka.curve, ckx.ciphertext[1:])
+	if x == nil {
+		return nil, errClientKeyExchange
+	}
+	x, _ = ka.curve.ScalarMult(x, y, ka.privateKey)
+	preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3)
+	xBytes := x.Bytes()
+	copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
+
+	return preMasterSecret, nil
+}
+
+func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+	if len(skx.key) < 4 {
+		return errServerKeyExchange
+	}
+	if skx.key[0] != 3 { // named curve
+		return errors.New("tls: server selected unsupported curve")
+	}
+	curveid := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
+
+	var ok bool
+	if ka.curve, ok = curveForCurveID(curveid); !ok {
+		return errors.New("tls: server selected unsupported curve")
+	}
+
+	publicLen := int(skx.key[3])
+	if publicLen+4 > len(skx.key) {
+		return errServerKeyExchange
+	}
+	ka.x, ka.y = elliptic.Unmarshal(ka.curve, skx.key[4:4+publicLen])
+	if ka.x == nil {
+		return errServerKeyExchange
+	}
+	serverECDHParams := skx.key[:4+publicLen]
+
+	sig := skx.key[4+publicLen:]
+	if len(sig) < 2 {
+		return errServerKeyExchange
+	}
+
+	var tls12HashId uint8
+	if ka.version >= VersionTLS12 {
+		// handle SignatureAndHashAlgorithm
+		var sigAndHash []uint8
+		sigAndHash, sig = sig[:2], sig[2:]
+		if sigAndHash[1] != ka.sigType {
+			return errServerKeyExchange
+		}
+		tls12HashId = sigAndHash[0]
+		if len(sig) < 2 {
+			return errServerKeyExchange
+		}
+	}
+	sigLen := int(sig[0])<<8 | int(sig[1])
+	if sigLen+2 != len(sig) {
+		return errServerKeyExchange
+	}
+	sig = sig[2:]
+
+	digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, serverHello.random, serverECDHParams)
+	if err != nil {
+		return err
+	}
+	switch ka.sigType {
+	case signatureECDSA:
+		pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
+		if !ok {
+			return errors.New("ECDHE ECDSA requires a ECDSA server public key")
+		}
+		ecdsaSig := new(ecdsaSignature)
+		if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil {
+			return err
+		}
+		if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
+			return errors.New("ECDSA signature contained zero or negative values")
+		}
+		if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) {
+			return errors.New("ECDSA verification failure")
+		}
+	case signatureRSA:
+		pubKey, ok := cert.PublicKey.(*rsa.PublicKey)
+		if !ok {
+			return errors.New("ECDHE RSA requires a RSA server public key")
+		}
+		if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil {
+			return err
+		}
+	default:
+		return errors.New("unknown ECDHE signature algorithm")
+	}
+
+	return nil
+}
+
+func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+	if ka.curve == nil {
+		return nil, nil, errors.New("missing ServerKeyExchange message")
+	}
+	priv, mx, my, err := elliptic.GenerateKey(ka.curve, config.rand())
+	if err != nil {
+		return nil, nil, err
+	}
+	x, _ := ka.curve.ScalarMult(ka.x, ka.y, priv)
+	preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3)
+	xBytes := x.Bytes()
+	copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
+
+	serialized := elliptic.Marshal(ka.curve, mx, my)
+
+	ckx := new(clientKeyExchangeMsg)
+	ckx.ciphertext = make([]byte, 1+len(serialized))
+	ckx.ciphertext[0] = byte(len(serialized))
+	copy(ckx.ciphertext[1:], serialized)
+
+	return preMasterSecret, ckx, nil
+}
diff --git a/ssl/test/runner/prf.go b/ssl/test/runner/prf.go
new file mode 100644
index 0000000..acb9654
--- /dev/null
+++ b/ssl/test/runner/prf.go
@@ -0,0 +1,303 @@
+// Copyright 2009 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 main
+
+import (
+	"crypto"
+	"crypto/hmac"
+	"crypto/md5"
+	"crypto/sha1"
+	"crypto/sha256"
+	"crypto/sha512"
+	"hash"
+)
+
+// Split a premaster secret in two as specified in RFC 4346, section 5.
+func splitPreMasterSecret(secret []byte) (s1, s2 []byte) {
+	s1 = secret[0 : (len(secret)+1)/2]
+	s2 = secret[len(secret)/2:]
+	return
+}
+
+// pHash implements the P_hash function, as defined in RFC 4346, section 5.
+func pHash(result, secret, seed []byte, hash func() hash.Hash) {
+	h := hmac.New(hash, secret)
+	h.Write(seed)
+	a := h.Sum(nil)
+
+	j := 0
+	for j < len(result) {
+		h.Reset()
+		h.Write(a)
+		h.Write(seed)
+		b := h.Sum(nil)
+		todo := len(b)
+		if j+todo > len(result) {
+			todo = len(result) - j
+		}
+		copy(result[j:j+todo], b)
+		j += todo
+
+		h.Reset()
+		h.Write(a)
+		a = h.Sum(nil)
+	}
+}
+
+// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, section 5.
+func prf10(result, secret, label, seed []byte) {
+	hashSHA1 := sha1.New
+	hashMD5 := md5.New
+
+	labelAndSeed := make([]byte, len(label)+len(seed))
+	copy(labelAndSeed, label)
+	copy(labelAndSeed[len(label):], seed)
+
+	s1, s2 := splitPreMasterSecret(secret)
+	pHash(result, s1, labelAndSeed, hashMD5)
+	result2 := make([]byte, len(result))
+	pHash(result2, s2, labelAndSeed, hashSHA1)
+
+	for i, b := range result2 {
+		result[i] ^= b
+	}
+}
+
+// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, section 5.
+func prf12(hashFunc func() hash.Hash) func(result, secret, label, seed []byte) {
+	return func(result, secret, label, seed []byte) {
+		labelAndSeed := make([]byte, len(label)+len(seed))
+		copy(labelAndSeed, label)
+		copy(labelAndSeed[len(label):], seed)
+
+		pHash(result, secret, labelAndSeed, hashFunc)
+	}
+}
+
+// prf30 implements the SSL 3.0 pseudo-random function, as defined in
+// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6.
+func prf30(result, secret, label, seed []byte) {
+	hashSHA1 := sha1.New()
+	hashMD5 := md5.New()
+
+	done := 0
+	i := 0
+	// RFC5246 section 6.3 says that the largest PRF output needed is 128
+	// bytes. Since no more ciphersuites will be added to SSLv3, this will
+	// remain true. Each iteration gives us 16 bytes so 10 iterations will
+	// be sufficient.
+	var b [11]byte
+	for done < len(result) {
+		for j := 0; j <= i; j++ {
+			b[j] = 'A' + byte(i)
+		}
+
+		hashSHA1.Reset()
+		hashSHA1.Write(b[:i+1])
+		hashSHA1.Write(secret)
+		hashSHA1.Write(seed)
+		digest := hashSHA1.Sum(nil)
+
+		hashMD5.Reset()
+		hashMD5.Write(secret)
+		hashMD5.Write(digest)
+
+		done += copy(result[done:], hashMD5.Sum(nil))
+		i++
+	}
+}
+
+const (
+	tlsRandomLength      = 32 // Length of a random nonce in TLS 1.1.
+	masterSecretLength   = 48 // Length of a master secret in TLS 1.1.
+	finishedVerifyLength = 12 // Length of verify_data in a Finished message.
+)
+
+var masterSecretLabel = []byte("master secret")
+var keyExpansionLabel = []byte("key expansion")
+var clientFinishedLabel = []byte("client finished")
+var serverFinishedLabel = []byte("server finished")
+
+func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, label, seed []byte) {
+	switch version {
+	case VersionSSL30:
+		return prf30
+	case VersionTLS10, VersionTLS11:
+		return prf10
+	case VersionTLS12:
+		if suite.flags&suiteSHA384 != 0 {
+			return prf12(sha512.New384)
+		}
+		return prf12(sha256.New)
+	default:
+		panic("unknown version")
+	}
+}
+
+// masterFromPreMasterSecret generates the master secret from the pre-master
+// secret. See http://tools.ietf.org/html/rfc5246#section-8.1
+func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte {
+	var seed [tlsRandomLength * 2]byte
+	copy(seed[0:len(clientRandom)], clientRandom)
+	copy(seed[len(clientRandom):], serverRandom)
+	masterSecret := make([]byte, masterSecretLength)
+	prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed[0:])
+	return masterSecret
+}
+
+// keysFromMasterSecret generates the connection keys from the master
+// secret, given the lengths of the MAC key, cipher key and IV, as defined in
+// RFC 2246, section 6.3.
+func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
+	var seed [tlsRandomLength * 2]byte
+	copy(seed[0:len(clientRandom)], serverRandom)
+	copy(seed[len(serverRandom):], clientRandom)
+
+	n := 2*macLen + 2*keyLen + 2*ivLen
+	keyMaterial := make([]byte, n)
+	prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed[0:])
+	clientMAC = keyMaterial[:macLen]
+	keyMaterial = keyMaterial[macLen:]
+	serverMAC = keyMaterial[:macLen]
+	keyMaterial = keyMaterial[macLen:]
+	clientKey = keyMaterial[:keyLen]
+	keyMaterial = keyMaterial[keyLen:]
+	serverKey = keyMaterial[:keyLen]
+	keyMaterial = keyMaterial[keyLen:]
+	clientIV = keyMaterial[:ivLen]
+	keyMaterial = keyMaterial[ivLen:]
+	serverIV = keyMaterial[:ivLen]
+	return
+}
+
+func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash {
+	if version >= VersionTLS12 {
+		newHash := sha256.New
+		if cipherSuite.flags&suiteSHA384 != 0 {
+			newHash = sha512.New384
+		}
+
+		return finishedHash{newHash(), newHash(), nil, nil, version, prf12(newHash)}
+	}
+	return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), version, prf10}
+}
+
+// A finishedHash calculates the hash of a set of handshake messages suitable
+// for including in a Finished message.
+type finishedHash struct {
+	client hash.Hash
+	server hash.Hash
+
+	// Prior to TLS 1.2, an additional MD5 hash is required.
+	clientMD5 hash.Hash
+	serverMD5 hash.Hash
+
+	version uint16
+	prf     func(result, secret, label, seed []byte)
+}
+
+func (h finishedHash) Write(msg []byte) (n int, err error) {
+	h.client.Write(msg)
+	h.server.Write(msg)
+
+	if h.version < VersionTLS12 {
+		h.clientMD5.Write(msg)
+		h.serverMD5.Write(msg)
+	}
+	return len(msg), nil
+}
+
+// finishedSum30 calculates the contents of the verify_data member of a SSLv3
+// Finished message given the MD5 and SHA1 hashes of a set of handshake
+// messages.
+func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic [4]byte) []byte {
+	md5.Write(magic[:])
+	md5.Write(masterSecret)
+	md5.Write(ssl30Pad1[:])
+	md5Digest := md5.Sum(nil)
+
+	md5.Reset()
+	md5.Write(masterSecret)
+	md5.Write(ssl30Pad2[:])
+	md5.Write(md5Digest)
+	md5Digest = md5.Sum(nil)
+
+	sha1.Write(magic[:])
+	sha1.Write(masterSecret)
+	sha1.Write(ssl30Pad1[:40])
+	sha1Digest := sha1.Sum(nil)
+
+	sha1.Reset()
+	sha1.Write(masterSecret)
+	sha1.Write(ssl30Pad2[:40])
+	sha1.Write(sha1Digest)
+	sha1Digest = sha1.Sum(nil)
+
+	ret := make([]byte, len(md5Digest)+len(sha1Digest))
+	copy(ret, md5Digest)
+	copy(ret[len(md5Digest):], sha1Digest)
+	return ret
+}
+
+var ssl3ClientFinishedMagic = [4]byte{0x43, 0x4c, 0x4e, 0x54}
+var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52}
+
+// clientSum returns the contents of the verify_data member of a client's
+// Finished message.
+func (h finishedHash) clientSum(masterSecret []byte) []byte {
+	if h.version == VersionSSL30 {
+		return finishedSum30(h.clientMD5, h.client, masterSecret, ssl3ClientFinishedMagic)
+	}
+
+	out := make([]byte, finishedVerifyLength)
+	if h.version >= VersionTLS12 {
+		seed := h.client.Sum(nil)
+		h.prf(out, masterSecret, clientFinishedLabel, seed)
+	} else {
+		seed := make([]byte, 0, md5.Size+sha1.Size)
+		seed = h.clientMD5.Sum(seed)
+		seed = h.client.Sum(seed)
+		h.prf(out, masterSecret, clientFinishedLabel, seed)
+	}
+	return out
+}
+
+// serverSum returns the contents of the verify_data member of a server's
+// Finished message.
+func (h finishedHash) serverSum(masterSecret []byte) []byte {
+	if h.version == VersionSSL30 {
+		return finishedSum30(h.serverMD5, h.server, masterSecret, ssl3ServerFinishedMagic)
+	}
+
+	out := make([]byte, finishedVerifyLength)
+	if h.version >= VersionTLS12 {
+		seed := h.server.Sum(nil)
+		h.prf(out, masterSecret, serverFinishedLabel, seed)
+	} else {
+		seed := make([]byte, 0, md5.Size+sha1.Size)
+		seed = h.serverMD5.Sum(seed)
+		seed = h.server.Sum(seed)
+		h.prf(out, masterSecret, serverFinishedLabel, seed)
+	}
+	return out
+}
+
+// hashForClientCertificate returns a digest, hash function, and TLS 1.2 hash
+// id suitable for signing by a TLS client certificate.
+func (h finishedHash) hashForClientCertificate(sigType uint8) ([]byte, crypto.Hash, uint8) {
+	if h.version >= VersionTLS12 {
+		digest := h.server.Sum(nil)
+		return digest, crypto.SHA256, hashSHA256
+	}
+	if sigType == signatureECDSA {
+		digest := h.server.Sum(nil)
+		return digest, crypto.SHA1, hashSHA1
+	}
+
+	digest := make([]byte, 0, 36)
+	digest = h.serverMD5.Sum(digest)
+	digest = h.server.Sum(digest)
+	return digest, crypto.MD5SHA1, 0 /* not specified in TLS 1.2. */
+}
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
new file mode 100644
index 0000000..1edd00b
--- /dev/null
+++ b/ssl/test/runner/runner.go
@@ -0,0 +1,365 @@
+package main
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"io"
+	"net"
+	"os"
+	"os/exec"
+	"strings"
+	"sync"
+	"syscall"
+)
+
+var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
+
+var rsaCertificate, ecdsaCertificate Certificate
+
+func initCertificates() {
+	var err error
+	rsaCertificate, err = LoadX509KeyPair("cert.pem", "key.pem")
+	if err != nil {
+		panic(err)
+	}
+
+	ecdsaCertificate, err = LoadX509KeyPair("ecdsa_cert.pem", "ecdsa_key.pem")
+	if err != nil {
+		panic(err)
+	}
+}
+
+var certificateOnce sync.Once
+
+func getRSACertificate() Certificate {
+	certificateOnce.Do(initCertificates)
+	return rsaCertificate
+}
+
+func getECDSACertificate() Certificate {
+	certificateOnce.Do(initCertificates)
+	return ecdsaCertificate
+}
+
+type testCase struct {
+	name          string
+	config        Config
+	shouldFail    bool
+	expectedError string
+}
+
+var clientTests = []testCase{
+	{
+		name: "BadRSASignature",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				InvalidSKXSignature: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":BAD_SIGNATURE:",
+	},
+	{
+		name: "BadECDSASignature",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				InvalidSKXSignature: true,
+			},
+			Certificates: []Certificate{getECDSACertificate()},
+		},
+		shouldFail:    true,
+		expectedError: ":BAD_SIGNATURE:",
+	},
+	{
+		name: "BadECDSACurve",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				InvalidSKXCurve: true,
+			},
+			Certificates: []Certificate{getECDSACertificate()},
+		},
+		shouldFail:    true,
+		expectedError: ":WRONG_CURVE:",
+	},
+}
+
+var testMessage = []byte("testing")
+
+func doExchange(tlsConn *Conn) error {
+	if err := tlsConn.Handshake(); err != nil {
+		return err
+	}
+	tlsConn.Write(testMessage)
+
+	buf := make([]byte, len(testMessage))
+	_, err := io.ReadFull(tlsConn, buf)
+	if err != nil {
+		return err
+	}
+
+	for i, v := range buf {
+		if v != testMessage[i]^0xff {
+			return fmt.Errorf("bad reply contents at byte %d", i)
+		}
+	}
+
+	return nil
+}
+
+func valgrindOf(dbAttach bool, baseArgs ...string) *exec.Cmd {
+	args := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"}
+	if dbAttach {
+		args = append(args, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p")
+	}
+	args = append(args, baseArgs...)
+
+	return exec.Command("valgrind", args...)
+}
+
+func gdbOf(baseArgs ...string) *exec.Cmd {
+	args := []string{"-e", "gdb", "--args"}
+	args = append(args, baseArgs...)
+
+	return exec.Command("xterm", args...)
+}
+
+func runTest(test *testCase) error {
+	socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
+	if err != nil {
+		panic(err)
+	}
+
+	syscall.CloseOnExec(socks[0])
+	syscall.CloseOnExec(socks[1])
+	clientEnd := os.NewFile(uintptr(socks[0]), "client end")
+	connFile := os.NewFile(uintptr(socks[1]), "our end")
+	conn, err := net.FileConn(connFile)
+	connFile.Close()
+	if err != nil {
+		panic(err)
+	}
+
+	const shim_path = "../../../build/ssl/test/client_shim"
+	var client *exec.Cmd
+	if *useValgrind {
+		client = valgrindOf(false, shim_path)
+	} else {
+		client = exec.Command(shim_path)
+	}
+	//client := gdbOf(shim_path)
+	client.ExtraFiles = []*os.File{clientEnd}
+	client.Stdin = os.Stdin
+	var stdoutBuf, stderrBuf bytes.Buffer
+	client.Stdout = &stdoutBuf
+	client.Stderr = &stderrBuf
+
+	if err := client.Start(); err != nil {
+		panic(err)
+	}
+	clientEnd.Close()
+
+	config := test.config
+	if len(config.Certificates) == 0 {
+		config.Certificates = []Certificate{getRSACertificate()}
+	}
+
+	tlsConn := Server(conn, &config)
+	err = doExchange(tlsConn)
+
+	conn.Close()
+	childErr := client.Wait()
+
+	stdout := string(stdoutBuf.Bytes())
+	stderr := string(stderrBuf.Bytes())
+	failed := err != nil || childErr != nil
+	correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.expectedError)
+
+	if failed != test.shouldFail || failed && !correctFailure {
+		localError := "none"
+		childError := "none"
+		if err != nil {
+			localError = err.Error()
+		}
+		if childErr != nil {
+			childError = childErr.Error()
+		}
+
+		var msg string
+		switch {
+		case failed && !test.shouldFail:
+			msg = "unexpected failure"
+		case !failed && test.shouldFail:
+			msg = "unexpected success"
+		case failed && !correctFailure:
+			msg = "bad error (wanted '" + test.expectedError + "')"
+		default:
+			panic("internal error")
+		}
+
+		return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr)
+	}
+
+	if !*useValgrind && len(stderr) > 0 {
+		println(stderr)
+	}
+
+	return nil
+}
+
+var tlsVersions = []struct {
+	name    string
+	version uint16
+}{
+	{"SSL3", VersionSSL30},
+	{"TLS1", VersionTLS10},
+	{"TLS11", VersionTLS11},
+	{"TLS12", VersionTLS12},
+}
+
+var testCipherSuites = []struct {
+	name string
+	id   uint16
+}{
+	{"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
+	{"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
+	{"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
+	{"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+	{"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
+	{"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
+	{"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
+	{"ECDHE-RSA-3DES-SHA", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
+	{"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+	{"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
+	{"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+	{"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
+	{"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
+	{"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
+	{"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
+}
+
+func addCipherSuiteTests() {
+	for _, suite := range testCipherSuites {
+		var cert Certificate
+		if strings.Contains(suite.name, "ECDSA") {
+			cert = getECDSACertificate()
+		} else {
+			cert = getRSACertificate()
+		}
+
+		for _, ver := range tlsVersions {
+			if ver.version != VersionTLS12 && strings.HasSuffix(suite.name, "-GCM") {
+				continue
+			}
+
+			clientTests = append(clientTests, testCase{
+				name: ver.name + "-" + suite.name,
+				config: Config{
+					MinVersion:   ver.version,
+					MaxVersion:   ver.version,
+					CipherSuites: []uint16{suite.id},
+					Certificates: []Certificate{cert},
+				},
+			})
+		}
+	}
+}
+
+func addBadECDSASignatureTests() {
+	for badR := BadValue(1); badR < NumBadValues; badR++ {
+		for badS := BadValue(1); badS < NumBadValues; badS++ {
+			clientTests = append(clientTests, testCase{
+				name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS),
+				config: Config{
+					CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+					Certificates: []Certificate{getECDSACertificate()},
+					Bugs: ProtocolBugs{
+						BadECDSAR: badR,
+						BadECDSAS: badS,
+					},
+				},
+				shouldFail:    true,
+				expectedError: "SIGNATURE",
+			})
+		}
+	}
+}
+
+func worker(statusChan chan statusMsg, c chan *testCase, wg *sync.WaitGroup) {
+	defer wg.Done()
+
+	for test := range c {
+		statusChan <- statusMsg{test: test, started: true}
+		err := runTest(test)
+		statusChan <- statusMsg{test: test, err: err}
+	}
+}
+
+type statusMsg struct {
+	test    *testCase
+	started bool
+	err     error
+}
+
+func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) {
+	var started, done, failed, lineLen int
+	defer close(doneChan)
+
+	for msg := range statusChan {
+		if msg.started {
+			started++
+		} else {
+			done++
+		}
+
+		fmt.Printf("\x1b[%dD\x1b[K", lineLen)
+
+		if msg.err != nil {
+			fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err)
+			failed++
+		}
+		line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total)
+		lineLen = len(line)
+		os.Stdout.WriteString(line)
+	}
+}
+
+func main() {
+	var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests")
+
+	flag.Parse()
+
+	addCipherSuiteTests()
+	addBadECDSASignatureTests()
+
+	var wg sync.WaitGroup
+
+	const numWorkers = 64
+
+	statusChan := make(chan statusMsg, numWorkers)
+	testChan := make(chan *testCase, numWorkers)
+	doneChan := make(chan struct{})
+
+	go statusPrinter(doneChan, statusChan, len(clientTests))
+
+	for i := 0; i < numWorkers; i++ {
+		wg.Add(1)
+		go worker(statusChan, testChan, &wg)
+	}
+
+	for i := range clientTests {
+		if len(*flagTest) == 0 || *flagTest == clientTests[i].name {
+			testChan <- &clientTests[i]
+		}
+	}
+
+	close(testChan)
+	wg.Wait()
+	close(statusChan)
+	<-doneChan
+
+	fmt.Printf("\n")
+}
diff --git a/ssl/test/runner/ticket.go b/ssl/test/runner/ticket.go
new file mode 100644
index 0000000..70e91cf
--- /dev/null
+++ b/ssl/test/runner/ticket.go
@@ -0,0 +1,182 @@
+// Copyright 2012 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 main
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/hmac"
+	"crypto/sha256"
+	"crypto/subtle"
+	"errors"
+	"io"
+)
+
+// sessionState contains the information that is serialized into a session
+// ticket in order to later resume a connection.
+type sessionState struct {
+	vers         uint16
+	cipherSuite  uint16
+	masterSecret []byte
+	certificates [][]byte
+}
+
+func (s *sessionState) equal(i interface{}) bool {
+	s1, ok := i.(*sessionState)
+	if !ok {
+		return false
+	}
+
+	if s.vers != s1.vers ||
+		s.cipherSuite != s1.cipherSuite ||
+		!bytes.Equal(s.masterSecret, s1.masterSecret) {
+		return false
+	}
+
+	if len(s.certificates) != len(s1.certificates) {
+		return false
+	}
+
+	for i := range s.certificates {
+		if !bytes.Equal(s.certificates[i], s1.certificates[i]) {
+			return false
+		}
+	}
+
+	return true
+}
+
+func (s *sessionState) marshal() []byte {
+	length := 2 + 2 + 2 + len(s.masterSecret) + 2
+	for _, cert := range s.certificates {
+		length += 4 + len(cert)
+	}
+
+	ret := make([]byte, length)
+	x := ret
+	x[0] = byte(s.vers >> 8)
+	x[1] = byte(s.vers)
+	x[2] = byte(s.cipherSuite >> 8)
+	x[3] = byte(s.cipherSuite)
+	x[4] = byte(len(s.masterSecret) >> 8)
+	x[5] = byte(len(s.masterSecret))
+	x = x[6:]
+	copy(x, s.masterSecret)
+	x = x[len(s.masterSecret):]
+
+	x[0] = byte(len(s.certificates) >> 8)
+	x[1] = byte(len(s.certificates))
+	x = x[2:]
+
+	for _, cert := range s.certificates {
+		x[0] = byte(len(cert) >> 24)
+		x[1] = byte(len(cert) >> 16)
+		x[2] = byte(len(cert) >> 8)
+		x[3] = byte(len(cert))
+		copy(x[4:], cert)
+		x = x[4+len(cert):]
+	}
+
+	return ret
+}
+
+func (s *sessionState) unmarshal(data []byte) bool {
+	if len(data) < 8 {
+		return false
+	}
+
+	s.vers = uint16(data[0])<<8 | uint16(data[1])
+	s.cipherSuite = uint16(data[2])<<8 | uint16(data[3])
+	masterSecretLen := int(data[4])<<8 | int(data[5])
+	data = data[6:]
+	if len(data) < masterSecretLen {
+		return false
+	}
+
+	s.masterSecret = data[:masterSecretLen]
+	data = data[masterSecretLen:]
+
+	if len(data) < 2 {
+		return false
+	}
+
+	numCerts := int(data[0])<<8 | int(data[1])
+	data = data[2:]
+
+	s.certificates = make([][]byte, numCerts)
+	for i := range s.certificates {
+		if len(data) < 4 {
+			return false
+		}
+		certLen := int(data[0])<<24 | int(data[1])<<16 | int(data[2])<<8 | int(data[3])
+		data = data[4:]
+		if certLen < 0 {
+			return false
+		}
+		if len(data) < certLen {
+			return false
+		}
+		s.certificates[i] = data[:certLen]
+		data = data[certLen:]
+	}
+
+	if len(data) > 0 {
+		return false
+	}
+
+	return true
+}
+
+func (c *Conn) encryptTicket(state *sessionState) ([]byte, error) {
+	serialized := state.marshal()
+	encrypted := make([]byte, aes.BlockSize+len(serialized)+sha256.Size)
+	iv := encrypted[:aes.BlockSize]
+	macBytes := encrypted[len(encrypted)-sha256.Size:]
+
+	if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
+		return nil, err
+	}
+	block, err := aes.NewCipher(c.config.SessionTicketKey[:16])
+	if err != nil {
+		return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
+	}
+	cipher.NewCTR(block, iv).XORKeyStream(encrypted[aes.BlockSize:], serialized)
+
+	mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32])
+	mac.Write(encrypted[:len(encrypted)-sha256.Size])
+	mac.Sum(macBytes[:0])
+
+	return encrypted, nil
+}
+
+func (c *Conn) decryptTicket(encrypted []byte) (*sessionState, bool) {
+	if len(encrypted) < aes.BlockSize+sha256.Size {
+		return nil, false
+	}
+
+	iv := encrypted[:aes.BlockSize]
+	macBytes := encrypted[len(encrypted)-sha256.Size:]
+
+	mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32])
+	mac.Write(encrypted[:len(encrypted)-sha256.Size])
+	expected := mac.Sum(nil)
+
+	if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
+		return nil, false
+	}
+
+	block, err := aes.NewCipher(c.config.SessionTicketKey[:16])
+	if err != nil {
+		return nil, false
+	}
+	ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
+	plaintext := ciphertext
+	cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
+
+	state := new(sessionState)
+	ok := state.unmarshal(plaintext)
+	return state, ok
+}
diff --git a/ssl/test/runner/tls.go b/ssl/test/runner/tls.go
new file mode 100644
index 0000000..c062a41
--- /dev/null
+++ b/ssl/test/runner/tls.go
@@ -0,0 +1,275 @@
+// Copyright 2009 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 partially implements TLS 1.2, as specified in RFC 5246.
+package main
+
+import (
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/pem"
+	"errors"
+	"io/ioutil"
+	"net"
+	"strings"
+	"time"
+)
+
+// Server returns a new TLS server side connection
+// using conn as the underlying transport.
+// The configuration config must be non-nil and must have
+// at least one certificate.
+func Server(conn net.Conn, config *Config) *Conn {
+	return &Conn{conn: conn, config: config}
+}
+
+// Client returns a new TLS client side connection
+// using conn as the underlying transport.
+// The config cannot be nil: users must set either ServerHostname or
+// InsecureSkipVerify in the config.
+func Client(conn net.Conn, config *Config) *Conn {
+	return &Conn{conn: conn, config: config, isClient: true}
+}
+
+// A listener implements a network listener (net.Listener) for TLS connections.
+type listener struct {
+	net.Listener
+	config *Config
+}
+
+// Accept waits for and returns the next incoming TLS connection.
+// The returned connection c is a *tls.Conn.
+func (l *listener) Accept() (c net.Conn, err error) {
+	c, err = l.Listener.Accept()
+	if err != nil {
+		return
+	}
+	c = Server(c, l.config)
+	return
+}
+
+// NewListener creates a Listener which accepts connections from an inner
+// Listener and wraps each connection with Server.
+// The configuration config must be non-nil and must have
+// at least one certificate.
+func NewListener(inner net.Listener, config *Config) net.Listener {
+	l := new(listener)
+	l.Listener = inner
+	l.config = config
+	return l
+}
+
+// Listen creates a TLS listener accepting connections on the
+// given network address using net.Listen.
+// The configuration config must be non-nil and must have
+// at least one certificate.
+func Listen(network, laddr string, config *Config) (net.Listener, error) {
+	if config == nil || len(config.Certificates) == 0 {
+		return nil, errors.New("tls.Listen: no certificates in configuration")
+	}
+	l, err := net.Listen(network, laddr)
+	if err != nil {
+		return nil, err
+	}
+	return NewListener(l, config), nil
+}
+
+type timeoutError struct{}
+
+func (timeoutError) Error() string   { return "tls: DialWithDialer timed out" }
+func (timeoutError) Timeout() bool   { return true }
+func (timeoutError) Temporary() bool { return true }
+
+// DialWithDialer connects to the given network address using dialer.Dial and
+// then initiates a TLS handshake, returning the resulting TLS connection. Any
+// timeout or deadline given in the dialer apply to connection and TLS
+// handshake as a whole.
+//
+// DialWithDialer interprets a nil configuration as equivalent to the zero
+// configuration; see the documentation of Config for the defaults.
+func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
+	// We want the Timeout and Deadline values from dialer to cover the
+	// whole process: TCP connection and TLS handshake. This means that we
+	// also need to start our own timers now.
+	timeout := dialer.Timeout
+
+	if !dialer.Deadline.IsZero() {
+		deadlineTimeout := dialer.Deadline.Sub(time.Now())
+		if timeout == 0 || deadlineTimeout < timeout {
+			timeout = deadlineTimeout
+		}
+	}
+
+	var errChannel chan error
+
+	if timeout != 0 {
+		errChannel = make(chan error, 2)
+		time.AfterFunc(timeout, func() {
+			errChannel <- timeoutError{}
+		})
+	}
+
+	rawConn, err := dialer.Dial(network, addr)
+	if err != nil {
+		return nil, err
+	}
+
+	colonPos := strings.LastIndex(addr, ":")
+	if colonPos == -1 {
+		colonPos = len(addr)
+	}
+	hostname := addr[:colonPos]
+
+	if config == nil {
+		config = defaultConfig()
+	}
+	// If no ServerName is set, infer the ServerName
+	// from the hostname we're connecting to.
+	if config.ServerName == "" {
+		// Make a copy to avoid polluting argument or default.
+		c := *config
+		c.ServerName = hostname
+		config = &c
+	}
+
+	conn := Client(rawConn, config)
+
+	if timeout == 0 {
+		err = conn.Handshake()
+	} else {
+		go func() {
+			errChannel <- conn.Handshake()
+		}()
+
+		err = <-errChannel
+	}
+
+	if err != nil {
+		rawConn.Close()
+		return nil, err
+	}
+
+	return conn, nil
+}
+
+// Dial connects to the given network address using net.Dial
+// and then initiates a TLS handshake, returning the resulting
+// TLS connection.
+// Dial interprets a nil configuration as equivalent to
+// the zero configuration; see the documentation of Config
+// for the defaults.
+func Dial(network, addr string, config *Config) (*Conn, error) {
+	return DialWithDialer(new(net.Dialer), network, addr, config)
+}
+
+// LoadX509KeyPair reads and parses a public/private key pair from a pair of
+// files. The files must contain PEM encoded data.
+func LoadX509KeyPair(certFile, keyFile string) (cert Certificate, err error) {
+	certPEMBlock, err := ioutil.ReadFile(certFile)
+	if err != nil {
+		return
+	}
+	keyPEMBlock, err := ioutil.ReadFile(keyFile)
+	if err != nil {
+		return
+	}
+	return X509KeyPair(certPEMBlock, keyPEMBlock)
+}
+
+// X509KeyPair parses a public/private key pair from a pair of
+// PEM encoded data.
+func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error) {
+	var certDERBlock *pem.Block
+	for {
+		certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
+		if certDERBlock == nil {
+			break
+		}
+		if certDERBlock.Type == "CERTIFICATE" {
+			cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
+		}
+	}
+
+	if len(cert.Certificate) == 0 {
+		err = errors.New("crypto/tls: failed to parse certificate PEM data")
+		return
+	}
+
+	var keyDERBlock *pem.Block
+	for {
+		keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
+		if keyDERBlock == nil {
+			err = errors.New("crypto/tls: failed to parse key PEM data")
+			return
+		}
+		if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
+			break
+		}
+	}
+
+	cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
+	if err != nil {
+		return
+	}
+
+	// We don't need to parse the public key for TLS, but we so do anyway
+	// to check that it looks sane and matches the private key.
+	x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
+	if err != nil {
+		return
+	}
+
+	switch pub := x509Cert.PublicKey.(type) {
+	case *rsa.PublicKey:
+		priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
+		if !ok {
+			err = errors.New("crypto/tls: private key type does not match public key type")
+			return
+		}
+		if pub.N.Cmp(priv.N) != 0 {
+			err = errors.New("crypto/tls: private key does not match public key")
+			return
+		}
+	case *ecdsa.PublicKey:
+		priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
+		if !ok {
+			err = errors.New("crypto/tls: private key type does not match public key type")
+			return
+
+		}
+		if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
+			err = errors.New("crypto/tls: private key does not match public key")
+			return
+		}
+	default:
+		err = errors.New("crypto/tls: unknown public key algorithm")
+		return
+	}
+
+	return
+}
+
+// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
+// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
+// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
+func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
+	if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
+		return key, nil
+	}
+	if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
+		switch key := key.(type) {
+		case *rsa.PrivateKey, *ecdsa.PrivateKey:
+			return key, nil
+		default:
+			return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping")
+		}
+	}
+	if key, err := x509.ParseECPrivateKey(der); err == nil {
+		return key, nil
+	}
+
+	return nil, errors.New("crypto/tls: failed to parse private key")
+}