Change SignatureAndHashAlgorithm to SignatureScheme in Go.

TLS 1.3 defines a new SignatureScheme uint16 enum that is backwards
compatible on the wire with TLS1.2's SignatureAndHashAlgorithm. This
change updates the go testing code to use a single signatureAlgorithm
enum (instead of 2 separate signature and hash enums) in preparation for
TLS 1.3. It also unifies all the signing around this new scheme,
effectively backporting the change to TLS 1.2.

For now, it does not distinguish signature algorithms between 1.2 and
1.3 (RSA-PSS instead of RSA-PKCS1, ECDSA must match curve types). When
the C code is ready make a similar change, the Go code will be updated
to match.

[Originally written by nharper, tweaked significantly by davidben.]

Change-Id: If9a315c4670755089ac061e4ec254ef3457a00de
Reviewed-on: https://boringssl-review.googlesource.com/8450
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 8516977..54157ee 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -1128,12 +1128,12 @@
     }
   }
 
-  if (config->expect_server_key_exchange_hash != 0 &&
-      config->expect_server_key_exchange_hash !=
-          SSL_get_server_key_exchange_hash(ssl)) {
-    fprintf(stderr, "ServerKeyExchange hash was %d, wanted %d.\n",
-            SSL_get_server_key_exchange_hash(ssl),
-            config->expect_server_key_exchange_hash);
+  if (config->expect_peer_signature_algorithm != 0 &&
+      config->expect_peer_signature_algorithm !=
+          SSL_get_peer_signature_algorithm(ssl)) {
+    fprintf(stderr, "Peer signature algorithm was %04x, wanted %04x.\n",
+            SSL_get_peer_signature_algorithm(ssl),
+            config->expect_peer_signature_algorithm);
     return false;
   }
 
diff --git a/ssl/test/runner/cipher_suites.go b/ssl/test/runner/cipher_suites.go
index 8c5be3d..ad2a638 100644
--- a/ssl/test/runner/cipher_suites.go
+++ b/ssl/test/runner/cipher_suites.go
@@ -35,6 +35,10 @@
 	// ServerKeyExchange message.
 	processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error
 	generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error)
+
+	// peerSignatureAlgorithm returns the signature algorithm used by the
+	// peer, or zero if not applicable.
+	peerSignatureAlgorithm() signatureAlgorithm
 }
 
 const (
@@ -406,7 +410,7 @@
 func ecdheECDSAKA(version uint16) keyAgreement {
 	return &ecdheKeyAgreement{
 		auth: &signedKeyAgreement{
-			sigType: signatureECDSA,
+			keyType: keyTypeECDSA,
 			version: version,
 		},
 	}
@@ -415,7 +419,7 @@
 func cecpq1ECDSAKA(version uint16) keyAgreement {
 	return &cecpq1KeyAgreement{
 		auth: &signedKeyAgreement{
-			sigType: signatureECDSA,
+			keyType: keyTypeECDSA,
 			version: version,
 		},
 	}
@@ -424,7 +428,7 @@
 func ecdheRSAKA(version uint16) keyAgreement {
 	return &ecdheKeyAgreement{
 		auth: &signedKeyAgreement{
-			sigType: signatureRSA,
+			keyType: keyTypeRSA,
 			version: version,
 		},
 	}
@@ -433,7 +437,7 @@
 func cecpq1RSAKA(version uint16) keyAgreement {
 	return &cecpq1KeyAgreement{
 		auth: &signedKeyAgreement{
-			sigType: signatureRSA,
+			keyType: keyTypeRSA,
 			version: version,
 		},
 	}
@@ -442,7 +446,7 @@
 func dheRSAKA(version uint16) keyAgreement {
 	return &dheKeyAgreement{
 		auth: &signedKeyAgreement{
-			sigType: signatureRSA,
+			keyType: keyTypeRSA,
 			version: version,
 		},
 	}
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index c9593ff..56a7c49 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -132,43 +132,51 @@
 	// Rest of these are reserved by the TLS spec
 )
 
-// Hash functions for TLS 1.2 (See RFC 5246, section A.4.1)
-const (
-	hashMD5    uint8 = 1
-	hashSHA1   uint8 = 2
-	hashSHA224 uint8 = 3
-	hashSHA256 uint8 = 4
-	hashSHA384 uint8 = 5
-	hashSHA512 uint8 = 6
-)
+// signatureAlgorithm corresponds to a SignatureScheme value from TLS 1.3. Note
+// that TLS 1.3 names the production 'SignatureScheme' to avoid colliding with
+// TLS 1.2's SignatureAlgorithm but otherwise refers to them as 'signature
+// algorithms' throughout. We match the latter.
+type signatureAlgorithm uint16
 
-// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1)
 const (
-	signatureRSA   uint8 = 1
-	signatureECDSA uint8 = 3
-)
+	// RSASSA-PKCS1-v1_5 algorithms
+	signatureRSAPKCS1WithMD5    signatureAlgorithm = 0x0101
+	signatureRSAPKCS1WithSHA1   signatureAlgorithm = 0x0201
+	signatureRSAPKCS1WithSHA256 signatureAlgorithm = 0x0401
+	signatureRSAPKCS1WithSHA384 signatureAlgorithm = 0x0501
+	signatureRSAPKCS1WithSHA512 signatureAlgorithm = 0x0601
 
-// signatureAndHash mirrors the TLS 1.2, SignatureAndHashAlgorithm struct. See
-// RFC 5246, section A.4.1.
-type signatureAndHash struct {
-	signature, hash uint8
-}
+	// ECDSA algorithms
+	signatureECDSAWithSHA1          signatureAlgorithm = 0x0203
+	signatureECDSAWithP256AndSHA256 signatureAlgorithm = 0x0403
+	signatureECDSAWithP384AndSHA384 signatureAlgorithm = 0x0503
+	signatureECDSAWithP521AndSHA512 signatureAlgorithm = 0x0603
+
+	// RSASSA-PSS algorithms
+	signatureRSAPSSWithSHA256 signatureAlgorithm = 0x0700
+	signatureRSAPSSWithSHA384 signatureAlgorithm = 0x0701
+	signatureRSAPSSWithSHA512 signatureAlgorithm = 0x0702
+
+	// EdDSA algorithms
+	signatureEd25519 signatureAlgorithm = 0x0703
+	signatureEd448   signatureAlgorithm = 0x0704
+)
 
 // supportedSKXSignatureAlgorithms contains the signature and hash algorithms
 // that the code advertises as supported in a TLS 1.2 ClientHello.
-var supportedSKXSignatureAlgorithms = []signatureAndHash{
-	{signatureRSA, hashSHA256},
-	{signatureECDSA, hashSHA256},
-	{signatureRSA, hashSHA1},
-	{signatureECDSA, hashSHA1},
+var supportedSKXSignatureAlgorithms = []signatureAlgorithm{
+	signatureRSAPKCS1WithSHA256,
+	signatureECDSAWithP256AndSHA256,
+	signatureRSAPKCS1WithSHA1,
+	signatureECDSAWithSHA1,
 }
 
-// supportedClientCertSignatureAlgorithms contains the signature and hash
+// supportedPeerSignatureAlgorithms contains the signature and hash
 // algorithms that the code advertises as supported in a TLS 1.2
 // CertificateRequest.
-var supportedClientCertSignatureAlgorithms = []signatureAndHash{
-	{signatureRSA, hashSHA256},
-	{signatureECDSA, hashSHA256},
+var supportedPeerSignatureAlgorithms = []signatureAlgorithm{
+	signatureRSAPKCS1WithSHA256,
+	signatureECDSAWithP256AndSHA256,
 }
 
 // SRTP protection profiles (See RFC 5764, section 4.1.2)
@@ -193,7 +201,7 @@
 	SRTPProtectionProfile      uint16                // the negotiated DTLS-SRTP protection profile
 	TLSUnique                  []byte                // the tls-unique channel binding
 	SCTList                    []byte                // signed certificate timestamp list
-	ClientCertSignatureHash    uint8                 // TLS id of the hash used by the client to sign the handshake
+	PeerSignatureAlgorithm     signatureAlgorithm    // algorithm used by the peer in the handshake
 }
 
 // ClientAuthType declares the policy the server will follow for
@@ -378,10 +386,10 @@
 	// protection profiles to offer in DTLS-SRTP.
 	SRTPProtectionProfiles []uint16
 
-	// SignatureAndHashes, if not nil, overrides the default set of
+	// SignatureAlgorithms, if not nil, overrides the default set of
 	// supported signature and hash algorithms to advertise in
 	// CertificateRequest.
-	SignatureAndHashes []signatureAndHash
+	SignatureAlgorithms []signatureAlgorithm
 
 	// Bugs specifies optional misbehaviour to be used for testing other
 	// implementations.
@@ -640,13 +648,13 @@
 	// server sends in the ServerHello instead of the negotiated one.
 	SendSRTPProtectionProfile uint16
 
-	// NoSignatureAndHashes, if true, causes the client to omit the
+	// NoSignatureAlgorithms, if true, causes the client to omit the
 	// signature and hashes extension.
 	//
 	// For a server, it will cause an empty list to be sent in the
 	// CertificateRequest message. None the less, the configured set will
 	// still be enforced.
-	NoSignatureAndHashes bool
+	NoSignatureAlgorithms bool
 
 	// NoSupportedCurves, if true, causes the client to omit the
 	// supported_curves extension.
@@ -980,16 +988,16 @@
 	return &c.Certificates[0]
 }
 
-func (c *Config) signatureAndHashesForServer() []signatureAndHash {
-	if c != nil && c.SignatureAndHashes != nil {
-		return c.SignatureAndHashes
+func (c *Config) signatureAlgorithmsForServer() []signatureAlgorithm {
+	if c != nil && c.SignatureAlgorithms != nil {
+		return c.SignatureAlgorithms
 	}
-	return supportedClientCertSignatureAlgorithms
+	return supportedPeerSignatureAlgorithms
 }
 
-func (c *Config) signatureAndHashesForClient() []signatureAndHash {
-	if c != nil && c.SignatureAndHashes != nil {
-		return c.SignatureAndHashes
+func (c *Config) signatureAlgorithmsForClient() []signatureAlgorithm {
+	if c != nil && c.SignatureAlgorithms != nil {
+		return c.SignatureAlgorithms
 	}
 	return supportedSKXSignatureAlgorithms
 }
@@ -1206,9 +1214,9 @@
 	return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted)
 }
 
-func isSupportedSignatureAndHash(sigHash signatureAndHash, sigHashes []signatureAndHash) bool {
-	for _, s := range sigHashes {
-		if s == sigHash {
+func isSupportedSignatureAlgorithm(sigAlg signatureAlgorithm, sigAlgs []signatureAlgorithm) bool {
+	for _, s := range sigAlgs {
+		if s == sigAlg {
 			return true
 		}
 	}
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index e8f6857..8244a4c 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -50,11 +50,9 @@
 	// firstFinished contains the first Finished hash sent during the
 	// handshake. This is the "tls-unique" channel binding value.
 	firstFinished [12]byte
-	// clientCertSignatureHash contains the TLS hash id for the hash that
-	// was used by the client to sign the handshake with a client
-	// certificate. This is only set by a server and is zero if no client
-	// certificates were used.
-	clientCertSignatureHash uint8
+	// peerSignatureAlgorithm contains the signature algorithm that was used
+	// by the peer in the handshake, or zero if not applicable.
+	peerSignatureAlgorithm signatureAlgorithm
 
 	clientRandom, serverRandom [32]byte
 	masterSecret               [48]byte
@@ -1089,7 +1087,7 @@
 		m = new(certificateMsg)
 	case typeCertificateRequest:
 		m = &certificateRequestMsg{
-			hasSignatureAndHash: c.vers >= VersionTLS12,
+			hasSignatureAlgorithm: c.vers >= VersionTLS12,
 		}
 	case typeCertificateStatus:
 		m = new(certificateStatusMsg)
@@ -1101,7 +1099,7 @@
 		m = new(clientKeyExchangeMsg)
 	case typeCertificateVerify:
 		m = &certificateVerifyMsg{
-			hasSignatureAndHash: c.vers >= VersionTLS12,
+			hasSignatureAlgorithm: c.vers >= VersionTLS12,
 		}
 	case typeNextProtocol:
 		m = new(nextProtoMsg)
@@ -1436,7 +1434,7 @@
 		state.SRTPProtectionProfile = c.srtpProtectionProfile
 		state.TLSUnique = c.firstFinished[:]
 		state.SCTList = c.sctList
-		state.ClientCertSignatureHash = c.clientCertSignatureHash
+		state.PeerSignatureAlgorithm = c.peerSignatureAlgorithm
 	}
 
 	return state
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index 5123f55..62154b8 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -6,12 +6,12 @@
 
 import (
 	"bytes"
+	"crypto"
 	"crypto/ecdsa"
 	"crypto/elliptic"
 	"crypto/rsa"
 	"crypto/subtle"
 	"crypto/x509"
-	"encoding/asn1"
 	"errors"
 	"fmt"
 	"io"
@@ -141,8 +141,8 @@
 		return errors.New("tls: short read from Rand: " + err.Error())
 	}
 
-	if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes {
-		hello.signatureAndHashes = c.config.signatureAndHashesForClient()
+	if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAlgorithms {
+		hello.signatureAlgorithms = c.config.signatureAlgorithmsForClient()
 	}
 
 	var session *ClientSessionState
@@ -474,6 +474,8 @@
 			return err
 		}
 
+		c.peerSignatureAlgorithm = keyAgreement.peerSignatureAlgorithm()
+
 		msg, err = c.readHandshake()
 		if err != nil {
 			return err
@@ -605,56 +607,50 @@
 	}
 
 	if chainToSend != nil {
-		var signed []byte
 		certVerify := &certificateVerifyMsg{
-			hasSignatureAndHash: c.vers >= VersionTLS12,
+			hasSignatureAlgorithm: c.vers >= VersionTLS12,
 		}
 
 		// Determine the hash to sign.
-		var signatureType uint8
-		switch c.config.Certificates[0].PrivateKey.(type) {
-		case *ecdsa.PrivateKey:
-			signatureType = signatureECDSA
-		case *rsa.PrivateKey:
-			signatureType = signatureRSA
-		default:
-			c.sendAlert(alertInternalError)
-			return errors.New("unknown private key type")
-		}
+		privKey := c.config.Certificates[0].PrivateKey
 		if c.config.Bugs.IgnorePeerSignatureAlgorithmPreferences {
-			certReq.signatureAndHashes = c.config.signatureAndHashesForClient()
-		}
-		certVerify.signatureAndHash, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.signatureAndHashes, c.config.signatureAndHashesForClient(), signatureType)
-		if err != nil {
-			c.sendAlert(alertInternalError)
-			return err
-		}
-		digest, hashFunc, err := hs.finishedHash.hashForClientCertificate(certVerify.signatureAndHash, hs.masterSecret)
-		if err != nil {
-			c.sendAlert(alertInternalError)
-			return err
-		}
-		if c.config.Bugs.InvalidCertVerifySignature {
-			digest[0] ^= 0x80
+			certReq.signatureAlgorithms = c.config.signatureAlgorithmsForClient()
 		}
 
-		switch key := c.config.Certificates[0].PrivateKey.(type) {
-		case *ecdsa.PrivateKey:
-			var r, s *big.Int
-			r, s, err = ecdsa.Sign(c.config.rand(), key, digest)
-			if err == nil {
-				signed, err = asn1.Marshal(ecdsaSignature{r, s})
+		if certVerify.hasSignatureAlgorithm {
+			certVerify.signatureAlgorithm, err = selectSignatureAlgorithm(c.vers, privKey, certReq.signatureAlgorithms, c.config.signatureAlgorithmsForClient())
+			if err != nil {
+				c.sendAlert(alertInternalError)
+				return err
 			}
-		case *rsa.PrivateKey:
-			signed, err = rsa.SignPKCS1v15(c.config.rand(), key, hashFunc, digest)
-		default:
-			err = errors.New("unknown private key type")
+		}
+
+		if c.vers > VersionSSL30 {
+			msg := hs.finishedHash.buffer
+			if c.config.Bugs.InvalidCertVerifySignature {
+				msg = make([]byte, len(hs.finishedHash.buffer))
+				copy(msg, hs.finishedHash.buffer)
+				msg[0] ^= 0x80
+			}
+			certVerify.signature, err = signMessage(c.vers, privKey, c.config, certVerify.signatureAlgorithm, msg)
+		} else {
+			// SSL 3.0's client certificate construction is
+			// incompatible with signatureAlgorithm.
+			rsaKey, ok := privKey.(*rsa.PrivateKey)
+			if !ok {
+				err = errors.New("unsupported signature type for client certificate")
+			} else {
+				digest := hs.finishedHash.hashForClientCertificateSSL3(hs.masterSecret)
+				if c.config.Bugs.InvalidCertVerifySignature {
+					digest[0] ^= 0x80
+				}
+				certVerify.signature, err = rsa.SignPKCS1v15(c.config.rand(), rsaKey, crypto.MD5SHA1, digest)
+			}
 		}
 		if err != nil {
 			c.sendAlert(alertInternalError)
 			return errors.New("tls: failed to sign handshake with client certificate: " + err.Error())
 		}
-		certVerify.signature = signed
 
 		hs.writeClientHash(certVerify.marshal())
 		c.writeRecord(recordTypeHandshake, certVerify.marshal())
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index 092f51e..9637e9a 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -22,7 +22,7 @@
 	supportedPoints         []uint8
 	ticketSupported         bool
 	sessionTicket           []uint8
-	signatureAndHashes      []signatureAndHash
+	signatureAlgorithms     []signatureAlgorithm
 	secureRenegotiation     []byte
 	alpnProtocols           []string
 	duplicateExtension      bool
@@ -56,7 +56,7 @@
 		bytes.Equal(m.supportedPoints, m1.supportedPoints) &&
 		m.ticketSupported == m1.ticketSupported &&
 		bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
-		eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes) &&
+		eqSignatureAlgorithms(m.signatureAlgorithms, m1.signatureAlgorithms) &&
 		bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) &&
 		(m.secureRenegotiation == nil) == (m1.secureRenegotiation == nil) &&
 		eqStrings(m.alpnProtocols, m1.alpnProtocols) &&
@@ -104,8 +104,8 @@
 		extensionsLength += len(m.sessionTicket)
 		numExtensions++
 	}
-	if len(m.signatureAndHashes) > 0 {
-		extensionsLength += 2 + 2*len(m.signatureAndHashes)
+	if len(m.signatureAlgorithms) > 0 {
+		extensionsLength += 2 + 2*len(m.signatureAlgorithms)
 		numExtensions++
 	}
 	if m.secureRenegotiation != nil {
@@ -281,11 +281,11 @@
 		copy(z, m.sessionTicket)
 		z = z[len(m.sessionTicket):]
 	}
-	if len(m.signatureAndHashes) > 0 {
+	if len(m.signatureAlgorithms) > 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)
+		l := 2 + 2*len(m.signatureAlgorithms)
 		z[2] = byte(l >> 8)
 		z[3] = byte(l)
 		z = z[4:]
@@ -294,9 +294,9 @@
 		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
+		for _, sigAlg := range m.signatureAlgorithms {
+			z[0] = byte(sigAlg >> 8)
+			z[1] = byte(sigAlg)
 			z = z[2:]
 		}
 	}
@@ -454,7 +454,7 @@
 	m.ocspStapling = false
 	m.ticketSupported = false
 	m.sessionTicket = nil
-	m.signatureAndHashes = nil
+	m.signatureAlgorithms = nil
 	m.alpnProtocols = nil
 	m.extendedMasterSecret = false
 	m.customExtension = ""
@@ -556,10 +556,9 @@
 			}
 			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]
+			m.signatureAlgorithms = make([]signatureAlgorithm, n)
+			for i := range m.signatureAlgorithms {
+				m.signatureAlgorithms[i] = signatureAlgorithm(d[0])<<8 | signatureAlgorithm(d[1])
 				d = d[2:]
 			}
 		case extensionRenegotiationInfo:
@@ -1296,13 +1295,13 @@
 
 type certificateRequestMsg struct {
 	raw []byte
-	// hasSignatureAndHash indicates whether this message includes a list
+	// hasSignatureAlgorithm indicates whether this message includes a list
 	// of signature and hash functions. This change was introduced with TLS
 	// 1.2.
-	hasSignatureAndHash bool
+	hasSignatureAlgorithm bool
 
 	certificateTypes       []byte
-	signatureAndHashes     []signatureAndHash
+	signatureAlgorithms    []signatureAlgorithm
 	certificateAuthorities [][]byte
 }
 
@@ -1319,8 +1318,8 @@
 	}
 	length += casLength
 
-	if m.hasSignatureAndHash {
-		length += 2 + 2*len(m.signatureAndHashes)
+	if m.hasSignatureAlgorithm {
+		length += 2 + 2*len(m.signatureAlgorithms)
 	}
 
 	x = make([]byte, 4+length)
@@ -1334,14 +1333,14 @@
 	copy(x[5:], m.certificateTypes)
 	y := x[5+len(m.certificateTypes):]
 
-	if m.hasSignatureAndHash {
-		n := len(m.signatureAndHashes) * 2
+	if m.hasSignatureAlgorithm {
+		n := len(m.signatureAlgorithms) * 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
+		for _, sigAlg := range m.signatureAlgorithms {
+			y[0] = byte(sigAlg >> 8)
+			y[1] = byte(sigAlg)
 			y = y[2:]
 		}
 	}
@@ -1386,23 +1385,22 @@
 
 	data = data[numCertTypes:]
 
-	if m.hasSignatureAndHash {
+	if m.hasSignatureAlgorithm {
 		if len(data) < 2 {
 			return false
 		}
-		sigAndHashLen := uint16(data[0])<<8 | uint16(data[1])
+		sigAlgsLen := uint16(data[0])<<8 | uint16(data[1])
 		data = data[2:]
-		if sigAndHashLen&1 != 0 {
+		if sigAlgsLen&1 != 0 {
 			return false
 		}
-		if len(data) < int(sigAndHashLen) {
+		if len(data) < int(sigAlgsLen) {
 			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]
+		numSigAlgs := sigAlgsLen / 2
+		m.signatureAlgorithms = make([]signatureAlgorithm, numSigAlgs)
+		for i := range m.signatureAlgorithms {
+			m.signatureAlgorithms[i] = signatureAlgorithm(data[0])<<8 | signatureAlgorithm(data[1])
 			data = data[2:]
 		}
 	}
@@ -1442,10 +1440,10 @@
 }
 
 type certificateVerifyMsg struct {
-	raw                 []byte
-	hasSignatureAndHash bool
-	signatureAndHash    signatureAndHash
-	signature           []byte
+	raw                   []byte
+	hasSignatureAlgorithm bool
+	signatureAlgorithm    signatureAlgorithm
+	signature             []byte
 }
 
 func (m *certificateVerifyMsg) marshal() (x []byte) {
@@ -1456,7 +1454,7 @@
 	// See http://tools.ietf.org/html/rfc4346#section-7.4.8
 	siglength := len(m.signature)
 	length := 2 + siglength
-	if m.hasSignatureAndHash {
+	if m.hasSignatureAlgorithm {
 		length += 2
 	}
 	x = make([]byte, 4+length)
@@ -1465,9 +1463,9 @@
 	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
+	if m.hasSignatureAlgorithm {
+		y[0] = byte(m.signatureAlgorithm >> 8)
+		y[1] = byte(m.signatureAlgorithm)
 		y = y[2:]
 	}
 	y[0] = uint8(siglength >> 8)
@@ -1492,9 +1490,8 @@
 	}
 
 	data = data[4:]
-	if m.hasSignatureAndHash {
-		m.signatureAndHash.hash = data[0]
-		m.signatureAndHash.signature = data[1]
+	if m.hasSignatureAlgorithm {
+		m.signatureAlgorithm = signatureAlgorithm(data[0])<<8 | signatureAlgorithm(data[1])
 		data = data[2:]
 	}
 
@@ -1745,13 +1742,13 @@
 	return true
 }
 
-func eqSignatureAndHashes(x, y []signatureAndHash) bool {
+func eqSignatureAlgorithms(x, y []signatureAlgorithm) 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 {
+		if v != v2 {
 			return false
 		}
 	}
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 7d462b5..7847acb 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -12,7 +12,6 @@
 	"crypto/rsa"
 	"crypto/subtle"
 	"crypto/x509"
-	"encoding/asn1"
 	"errors"
 	"fmt"
 	"io"
@@ -196,11 +195,11 @@
 	c.clientVersion = hs.clientHello.vers
 
 	// Reject < 1.2 ClientHellos with signature_algorithms.
-	if c.clientVersion < VersionTLS12 && len(hs.clientHello.signatureAndHashes) > 0 {
+	if c.clientVersion < VersionTLS12 && len(hs.clientHello.signatureAlgorithms) > 0 {
 		return false, fmt.Errorf("tls: client included signature_algorithms before TLS 1.2")
 	}
 	if config.Bugs.IgnorePeerSignatureAlgorithmPreferences {
-		hs.clientHello.signatureAndHashes = config.signatureAndHashesForServer()
+		hs.clientHello.signatureAlgorithms = config.signatureAlgorithmsForServer()
 	}
 
 	// Check the client cipher list is consistent with the version.
@@ -592,9 +591,9 @@
 			}
 		}
 		if c.vers >= VersionTLS12 {
-			certReq.hasSignatureAndHash = true
-			if !config.Bugs.NoSignatureAndHashes {
-				certReq.signatureAndHashes = config.signatureAndHashesForServer()
+			certReq.hasSignatureAlgorithm = true
+			if !config.Bugs.NoSignatureAlgorithms {
+				certReq.signatureAlgorithms = config.signatureAlgorithmsForServer()
 			}
 		}
 
@@ -707,60 +706,27 @@
 		}
 
 		// Determine the signature type.
-		var signatureAndHash signatureAndHash
-		if certVerify.hasSignatureAndHash {
-			signatureAndHash = certVerify.signatureAndHash
-			if !isSupportedSignatureAndHash(signatureAndHash, config.signatureAndHashesForServer()) {
-				return errors.New("tls: unsupported hash function for client certificate")
+		var sigAlg signatureAlgorithm
+		if certVerify.hasSignatureAlgorithm {
+			sigAlg = certVerify.signatureAlgorithm
+			if !isSupportedSignatureAlgorithm(sigAlg, config.signatureAlgorithmsForServer()) {
+				return errors.New("tls: unsupported signature algorithm for client certificate")
 			}
-			c.clientCertSignatureHash = signatureAndHash.hash
-		} else {
-			// Before TLS 1.2 the signature algorithm was implicit
-			// from the key type, and only one hash per signature
-			// algorithm was possible. Leave the hash as zero.
-			switch pub.(type) {
-			case *ecdsa.PublicKey:
-				signatureAndHash.signature = signatureECDSA
-			case *rsa.PublicKey:
-				signatureAndHash.signature = signatureRSA
-			}
+			c.peerSignatureAlgorithm = sigAlg
 		}
 
-		switch key := pub.(type) {
-		case *ecdsa.PublicKey:
-			if signatureAndHash.signature != signatureECDSA {
-				err = errors.New("tls: bad signature type for client's ECDSA certificate")
-				break
+		if c.vers > VersionSSL30 {
+			err = verifyMessage(c.vers, pub, sigAlg, hs.finishedHash.buffer, certVerify.signature)
+		} else {
+			// SSL 3.0's client certificate construction is
+			// incompatible with signatureAlgorithm.
+			rsaPub, ok := pub.(*rsa.PublicKey)
+			if !ok {
+				err = errors.New("unsupported key type for client certificate")
+			} else {
+				digest := hs.finishedHash.hashForClientCertificateSSL3(hs.masterSecret)
+				err = rsa.VerifyPKCS1v15(rsaPub, crypto.MD5SHA1, digest, certVerify.signature)
 			}
-			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
-			}
-			var digest []byte
-			digest, _, err = hs.finishedHash.hashForClientCertificate(signatureAndHash, hs.masterSecret)
-			if err != nil {
-				break
-			}
-			if !ecdsa.Verify(key, digest, ecdsaSig.R, ecdsaSig.S) {
-				err = errors.New("ECDSA verification failure")
-				break
-			}
-		case *rsa.PublicKey:
-			if signatureAndHash.signature != signatureRSA {
-				err = errors.New("tls: bad signature type for client's RSA certificate")
-				break
-			}
-			var digest []byte
-			var hashFunc crypto.Hash
-			digest, hashFunc, err = hs.finishedHash.hashForClientCertificate(signatureAndHash, hs.masterSecret)
-			if err != nil {
-				break
-			}
-			err = rsa.VerifyPKCS1v15(key, hashFunc, digest, certVerify.signature)
 		}
 		if err != nil {
 			c.sendAlert(alertBadCertificate)
diff --git a/ssl/test/runner/key_agreement.go b/ssl/test/runner/key_agreement.go
index 5e5d976..cb02c53 100644
--- a/ssl/test/runner/key_agreement.go
+++ b/ssl/test/runner/key_agreement.go
@@ -5,16 +5,12 @@
 package runner
 
 import (
-	"crypto"
 	"crypto/ecdsa"
 	"crypto/elliptic"
-	"crypto/md5"
 	"crypto/rand"
 	"crypto/rsa"
-	"crypto/sha1"
 	"crypto/subtle"
 	"crypto/x509"
-	"encoding/asn1"
 	"errors"
 	"fmt"
 	"io"
@@ -24,6 +20,13 @@
 	"./newhope"
 )
 
+type keyType int
+
+const (
+	keyTypeRSA keyType = iota + 1
+	keyTypeECDSA
+)
+
 var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
 var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message")
 
@@ -59,37 +62,30 @@
 	serverRSAParams = append(serverRSAParams, byte(len(exponent)>>8), byte(len(exponent)))
 	serverRSAParams = append(serverRSAParams, exponent...)
 
-	var tls12HashId uint8
+	var sigAlg signatureAlgorithm
 	if ka.version >= VersionTLS12 {
-		if tls12HashId, err = pickTLS12HashForSignature(signatureRSA, clientHello.signatureAndHashes, config.signatureAndHashesForServer()); err != nil {
+		sigAlg, err = selectSignatureAlgorithm(ka.version, cert.PrivateKey, clientHello.signatureAlgorithms, config.signatureAlgorithmsForServer())
+		if err != nil {
 			return nil, err
 		}
 	}
 
-	digest, hashFunc, err := hashForServerKeyExchange(signatureRSA, tls12HashId, ka.version, clientHello.random, hello.random, serverRSAParams)
-	if err != nil {
-		return nil, err
-	}
-	privKey, ok := cert.PrivateKey.(*rsa.PrivateKey)
-	if !ok {
-		return nil, errors.New("RSA ephemeral key requires an RSA server private key")
-	}
-	sig, err := rsa.SignPKCS1v15(config.rand(), privKey, hashFunc, digest)
+	sig, err := signMessage(ka.version, cert.PrivateKey, config, sigAlg, serverRSAParams)
 	if err != nil {
 		return nil, errors.New("failed to sign RSA parameters: " + err.Error())
 	}
 
 	skx := new(serverKeyExchangeMsg)
-	sigAndHashLen := 0
+	sigAlgsLen := 0
 	if ka.version >= VersionTLS12 {
-		sigAndHashLen = 2
+		sigAlgsLen = 2
 	}
-	skx.key = make([]byte, len(serverRSAParams)+sigAndHashLen+2+len(sig))
+	skx.key = make([]byte, len(serverRSAParams)+sigAlgsLen+2+len(sig))
 	copy(skx.key, serverRSAParams)
 	k := skx.key[len(serverRSAParams):]
 	if ka.version >= VersionTLS12 {
-		k[0] = tls12HashId
-		k[1] = signatureRSA
+		k[0] = byte(sigAlg >> 8)
+		k[1] = byte(sigAlg)
 		k = k[2:]
 	}
 	k[0] = byte(len(sig) >> 8)
@@ -185,70 +181,8 @@
 	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
-}
-
-// 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 {
-		hash, err := lookupTLSHash(hashFunc)
-		if err != nil {
-			return nil, 0, err
-		}
-		h := hash.New()
-		for _, slice := range slices {
-			h.Write(slice)
-		}
-		return h.Sum(nil), hash, nil
-	}
-	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, clientList, serverList []signatureAndHash) (uint8, error) {
-	if len(clientList) == 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 clientList {
-		if sigAndHash.signature != sigType {
-			continue
-		}
-		if isSupportedSignatureAndHash(sigAndHash, serverList) {
-			return sigAndHash.hash, nil
-		}
-	}
-
-	return 0, errors.New("tls: client doesn't support any common hash functions")
+func (ka *rsaKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm {
+	return 0
 }
 
 // A ecdhCurve is an instance of ECDH-style key agreement for TLS.
@@ -448,91 +382,53 @@
 	return nil
 }
 
-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")
-	}
-}
-
 // signedKeyAgreement signs the ServerKeyExchange parameters with the
 // server's private key.
 type signedKeyAgreement struct {
-	version uint16
-	sigType uint8
+	keyType                keyType
+	version                uint16
+	peerSignatureAlgorithm signatureAlgorithm
 }
 
 func (ka *signedKeyAgreement) signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) {
-	var tls12HashId uint8
+	// The message to be signed is prepended by the randoms.
+	var msg []byte
+	msg = append(msg, clientHello.random...)
+	msg = append(msg, hello.random...)
+	msg = append(msg, params...)
+
+	if config.Bugs.InvalidSKXSignature {
+		msg[0] ^= 0x80
+	}
+
+	var sigAlg signatureAlgorithm
 	var err error
 	if ka.version >= VersionTLS12 {
-		if tls12HashId, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes, config.signatureAndHashesForServer()); err != nil {
+		sigAlg, err = selectSignatureAlgorithm(ka.version, cert.PrivateKey, clientHello.signatureAlgorithms, config.signatureAlgorithmsForServer())
+		if err != nil {
 			return nil, err
 		}
 	}
 
-	digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, hello.random, params)
+	sig, err := signMessage(ka.version, cert.PrivateKey, config, sigAlg, msg)
 	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)
 	if config.Bugs.UnauthenticatedECDH {
 		skx.key = params
 	} else {
-		sigAndHashLen := 0
+		sigAlgsLen := 0
 		if ka.version >= VersionTLS12 {
-			sigAndHashLen = 2
+			sigAlgsLen = 2
 		}
-		skx.key = make([]byte, len(params)+sigAndHashLen+2+len(sig))
+		skx.key = make([]byte, len(params)+sigAlgsLen+2+len(sig))
 		copy(skx.key, params)
 		k := skx.key[len(params):]
 		if ka.version >= VersionTLS12 {
-			k[0] = tls12HashId
-			k[1] = ka.sigType
+			k[0] = byte(sigAlg >> 8)
+			k[1] = byte(sigAlg)
 			k = k[2:]
 		}
 		k[0] = byte(len(sig) >> 8)
@@ -544,26 +440,44 @@
 }
 
 func (ka *signedKeyAgreement) verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, params []byte, sig []byte) error {
-	if len(sig) < 2 {
-		return errServerKeyExchange
+	// The peer's key must match the cipher type.
+	switch ka.keyType {
+	case keyTypeECDSA:
+		_, ok := cert.PublicKey.(*ecdsa.PublicKey)
+		if !ok {
+			return errors.New("tls: ECDHE ECDSA requires a ECDSA server public key")
+		}
+	case keyTypeRSA:
+		_, ok := cert.PublicKey.(*rsa.PublicKey)
+		if !ok {
+			return errors.New("tls: ECDHE RSA requires a RSA server public key")
+		}
+	default:
+		return errors.New("tls: unknown key type")
 	}
 
-	var tls12HashId uint8
+	// The message to be signed is prepended by the randoms.
+	var msg []byte
+	msg = append(msg, clientHello.random...)
+	msg = append(msg, serverHello.random...)
+	msg = append(msg, params...)
+
+	var sigAlg signatureAlgorithm
 	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
 		}
-
-		if !isSupportedSignatureAndHash(signatureAndHash{ka.sigType, tls12HashId}, config.signatureAndHashesForClient()) {
-			return errors.New("tls: unsupported hash function for ServerKeyExchange")
+		sigAlg = signatureAlgorithm(sig[0])<<8 | signatureAlgorithm(sig[1])
+		sig = sig[2:]
+		if !isSupportedSignatureAlgorithm(sigAlg, config.signatureAlgorithmsForClient()) {
+			return errors.New("tls: unsupported signature algorithm for ServerKeyExchange")
 		}
+		// Stash the signature algorithm to be extracted by the handshake.
+		ka.peerSignatureAlgorithm = sigAlg
+	}
+
+	if len(sig) < 2 {
+		return errServerKeyExchange
 	}
 	sigLen := int(sig[0])<<8 | int(sig[1])
 	if sigLen+2 != len(sig) {
@@ -571,39 +485,7 @@
 	}
 	sig = sig[2:]
 
-	digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, serverHello.random, params)
-	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
+	return verifyMessage(ka.version, cert.PublicKey, sigAlg, msg, sig)
 }
 
 // ecdheRSAKeyAgreement implements a TLS key agreement where the server
@@ -716,6 +598,13 @@
 	return preMasterSecret, ckx, nil
 }
 
+func (ka *ecdheKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm {
+	if auth, ok := ka.auth.(*signedKeyAgreement); ok {
+		return auth.peerSignatureAlgorithm
+	}
+	return 0
+}
+
 // cecpq1RSAKeyAgreement is like an ecdheKeyAgreement, but using the cecpq1Curve
 // pseudo-curve, and without any parameters (e.g. curve name) other than the
 // keys being exchanged. The signature may either be ECDSA or RSA.
@@ -807,6 +696,13 @@
 	return preMasterSecret, ckx, nil
 }
 
+func (ka *cecpq1KeyAgreement) peerSignatureAlgorithm() signatureAlgorithm {
+	if auth, ok := ka.auth.(*signedKeyAgreement); ok {
+		return auth.peerSignatureAlgorithm
+	}
+	return 0
+}
+
 // dheRSAKeyAgreement implements a TLS key agreement where the server generates
 // an ephemeral Diffie-Hellman public/private key pair and signs it. The
 // pre-master secret is then calculated using Diffie-Hellman.
@@ -941,6 +837,13 @@
 	return preMasterSecret, ckx, nil
 }
 
+func (ka *dheKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm {
+	if auth, ok := ka.auth.(*signedKeyAgreement); ok {
+		return auth.peerSignatureAlgorithm
+	}
+	return 0
+}
+
 // nilKeyAgreement is a fake key agreement used to implement the plain PSK key
 // exchange.
 type nilKeyAgreement struct{}
@@ -976,6 +879,10 @@
 	return nil, &clientKeyExchangeMsg{}, nil
 }
 
+func (ka *nilKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm {
+	return 0
+}
+
 // makePSKPremaster formats a PSK pre-master secret based on otherSecret from
 // the base key exchange and psk.
 func makePSKPremaster(otherSecret, psk []byte) []byte {
@@ -1101,3 +1008,7 @@
 	}
 	return makePSKPremaster(otherSecret, config.PreSharedKey), ckx, nil
 }
+
+func (ka *pskKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm {
+	return 0
+}
diff --git a/ssl/test/runner/prf.go b/ssl/test/runner/prf.go
index 1bfe84c..a91a319 100644
--- a/ssl/test/runner/prf.go
+++ b/ssl/test/runner/prf.go
@@ -5,13 +5,11 @@
 package runner
 
 import (
-	"crypto"
 	"crypto/hmac"
 	"crypto/md5"
 	"crypto/sha1"
 	"crypto/sha256"
 	"crypto/sha512"
-	"errors"
 	"hash"
 )
 
@@ -189,27 +187,6 @@
 	return
 }
 
-// lookupTLSHash looks up the corresponding crypto.Hash for a given
-// TLS hash identifier.
-func lookupTLSHash(hash uint8) (crypto.Hash, error) {
-	switch hash {
-	case hashMD5:
-		return crypto.MD5, nil
-	case hashSHA1:
-		return crypto.SHA1, nil
-	case hashSHA224:
-		return crypto.SHA224, nil
-	case hashSHA256:
-		return crypto.SHA256, nil
-	case hashSHA384:
-		return crypto.SHA384, nil
-	case hashSHA512:
-		return crypto.SHA512, nil
-	default:
-		return 0, errors.New("tls: unsupported hash algorithm")
-	}
-}
-
 func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash {
 	if version >= VersionTLS12 {
 		newHash := sha256.New
@@ -325,50 +302,14 @@
 	return out
 }
 
-// selectClientCertSignatureAlgorithm returns a signatureAndHash to sign a
-// client's CertificateVerify with, or an error if none can be found.
-func (h finishedHash) selectClientCertSignatureAlgorithm(serverList, clientList []signatureAndHash, sigType uint8) (signatureAndHash, error) {
-	if h.version < VersionTLS12 {
-		// Nothing to negotiate before TLS 1.2.
-		return signatureAndHash{signature: sigType}, nil
-	}
-
-	for _, v := range serverList {
-		if v.signature == sigType && isSupportedSignatureAndHash(v, clientList) {
-			return v, nil
-		}
-	}
-	return signatureAndHash{}, errors.New("tls: no supported signature algorithm found for signing client certificate")
-}
-
-// hashForClientCertificate returns a digest, hash function, and TLS 1.2 hash
-// id suitable for signing by a TLS client certificate.
-func (h finishedHash) hashForClientCertificate(signatureAndHash signatureAndHash, masterSecret []byte) ([]byte, crypto.Hash, error) {
-	if h.version == VersionSSL30 {
-		if signatureAndHash.signature != signatureRSA {
-			return nil, 0, errors.New("tls: unsupported signature type for client certificate")
-		}
-
-		md5Hash := md5.New()
-		md5Hash.Write(h.buffer)
-		sha1Hash := sha1.New()
-		sha1Hash.Write(h.buffer)
-		return finishedSum30(md5Hash, sha1Hash, masterSecret, nil), crypto.MD5SHA1, nil
-	}
-	if h.version >= VersionTLS12 {
-		hashAlg, err := lookupTLSHash(signatureAndHash.hash)
-		if err != nil {
-			return nil, 0, err
-		}
-		hash := hashAlg.New()
-		hash.Write(h.buffer)
-		return hash.Sum(nil), hashAlg, nil
-	}
-	if signatureAndHash.signature == signatureECDSA {
-		return h.server.Sum(nil), crypto.SHA1, nil
-	}
-
-	return h.Sum(), crypto.MD5SHA1, nil
+// hashForClientCertificateSSL3 returns the hash to be signed for client
+// certificates in SSL 3.0.
+func (h finishedHash) hashForClientCertificateSSL3(masterSecret []byte) []byte {
+	md5Hash := md5.New()
+	md5Hash.Write(h.buffer)
+	sha1Hash := sha1.New()
+	sha1Hash.Write(h.buffer)
+	return finishedSum30(md5Hash, sha1Hash, masterSecret, nil)
 }
 
 // hashForChannelID returns the hash to be signed for TLS Channel
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 0ede7bb..41b886c 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -143,6 +143,46 @@
 	npn  = 2
 )
 
+type testCert int
+
+const (
+	testCertRSA testCert = iota
+	testCertECDSA
+)
+
+func getRunnerCertificate(t testCert) Certificate {
+	switch t {
+	case testCertRSA:
+		return getRSACertificate()
+	case testCertECDSA:
+		return getECDSACertificate()
+	default:
+		panic("Unknown test certificate")
+	}
+}
+
+func getShimCertificate(t testCert) string {
+	switch t {
+	case testCertRSA:
+		return rsaCertificateFile
+	case testCertECDSA:
+		return ecdsaCertificateFile
+	default:
+		panic("Unknown test certificate")
+	}
+}
+
+func getShimKey(t testCert) string {
+	switch t {
+	case testCertRSA:
+		return rsaKeyFile
+	case testCertECDSA:
+		return ecdsaKeyFile
+	default:
+		panic("Unknown test certificate")
+	}
+}
+
 type testCase struct {
 	testType      testType
 	protocol      protocol
@@ -181,10 +221,9 @@
 	expectedOCSPResponse []uint8
 	// expectedSCTList, if not nil, is the expected SCT list to be received.
 	expectedSCTList []uint8
-	// expectedClientCertSignatureHash, if not zero, is the TLS id of the
-	// hash function that the client should have used when signing the
-	// handshake with a client certificate.
-	expectedClientCertSignatureHash uint8
+	// expectedPeerSignatureAlgorithm, if not zero, is the signature
+	// algorithm that the peer should have used in the handshake.
+	expectedPeerSignatureAlgorithm signatureAlgorithm
 	// messageLen is the length, in bytes, of the test message that will be
 	// sent.
 	messageLen int
@@ -447,8 +486,8 @@
 		return fmt.Errorf("SCT list mismatch")
 	}
 
-	if expected := test.expectedClientCertSignatureHash; expected != 0 && expected != connState.ClientCertSignatureHash {
-		return fmt.Errorf("expected client to sign handshake with hash %d, but got %d", expected, connState.ClientCertSignatureHash)
+	if expected := test.expectedPeerSignatureAlgorithm; expected != 0 && expected != connState.PeerSignatureAlgorithm {
+		return fmt.Errorf("expected peer to use signature algorithm %04x, but got %04x", expected, connState.PeerSignatureAlgorithm)
 	}
 
 	if test.exportKeyingMaterial > 0 {
@@ -646,10 +685,6 @@
 		panic("expectResumeRejected without resumeSession in " + test.name)
 	}
 
-	if test.testType != clientTest && test.expectedClientCertSignatureHash != 0 {
-		panic("expectedClientCertSignatureHash non-zero with serverTest in " + test.name)
-	}
-
 	listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IP{127, 0, 0, 1}})
 	if err != nil {
 		panic(err)
@@ -4510,78 +4545,123 @@
 	})
 }
 
-var testHashes = []struct {
+var testSignatureAlgorithms = []struct {
 	name string
-	id   uint8
+	id   signatureAlgorithm
+	cert testCert
 }{
-	{"SHA1", hashSHA1},
-	{"SHA256", hashSHA256},
-	{"SHA384", hashSHA384},
-	{"SHA512", hashSHA512},
+	{"RSA-PKCS1-SHA1", signatureRSAPKCS1WithSHA1, testCertRSA},
+	{"RSA-PKCS1-SHA256", signatureRSAPKCS1WithSHA256, testCertRSA},
+	{"RSA-PKCS1-SHA384", signatureRSAPKCS1WithSHA384, testCertRSA},
+	{"RSA-PKCS1-SHA512", signatureRSAPKCS1WithSHA512, testCertRSA},
+	{"ECDSA-SHA1", signatureECDSAWithSHA1, testCertECDSA},
+	// TODO(davidben): These signature algorithms are paired with a curve in
+	// TLS 1.3. Test that, in TLS 1.3, the curves must match and, in TLS
+	// 1.2, mismatches are tolerated.
+	{"ECDSA-SHA256", signatureECDSAWithP256AndSHA256, testCertECDSA},
+	{"ECDSA-SHA384", signatureECDSAWithP384AndSHA384, testCertECDSA},
+	{"ECDSA-SHA512", signatureECDSAWithP521AndSHA512, testCertECDSA},
 }
 
-func addSigningHashTests() {
-	// Make sure each hash works. Include some fake hashes in the list and
-	// ensure they're ignored.
-	for _, hash := range testHashes {
+const fakeSigAlg1 signatureAlgorithm = 0x2a01
+const fakeSigAlg2 signatureAlgorithm = 0xff01
+
+func addSignatureAlgorithmTests() {
+	// Make sure each signature algorithm works. Include some fake values in
+	// the list and ensure they're ignored.
+	for _, alg := range testSignatureAlgorithms {
 		testCases = append(testCases, testCase{
-			name: "SigningHash-ClientAuth-" + hash.name,
+			name: "SigningHash-ClientAuth-Sign-" + alg.name,
 			config: Config{
-				ClientAuth: RequireAnyClientCert,
-				SignatureAndHashes: []signatureAndHash{
-					{signatureRSA, 42},
-					{signatureRSA, hash.id},
-					{signatureRSA, 255},
+				// SignatureAlgorithms is shared, so we must
+				// configure a matching server certificate too.
+				Certificates: []Certificate{getRunnerCertificate(alg.cert)},
+				ClientAuth:   RequireAnyClientCert,
+				SignatureAlgorithms: []signatureAlgorithm{
+					fakeSigAlg1,
+					alg.id,
+					fakeSigAlg2,
 				},
 			},
 			flags: []string{
-				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
-				"-key-file", path.Join(*resourceDir, rsaKeyFile),
+				"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
+				"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
+			},
+			expectedPeerSignatureAlgorithm: alg.id,
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "SigningHash-ClientAuth-Verify-" + alg.name,
+			config: Config{
+				Certificates: []Certificate{getRunnerCertificate(alg.cert)},
+				SignatureAlgorithms: []signatureAlgorithm{
+					alg.id,
+				},
+			},
+			flags: []string{
+				"-require-any-client-certificate",
+				"-expect-peer-signature-algorithm", strconv.Itoa(int(alg.id)),
+				// SignatureAlgorithms is shared, so we must
+				// configure a matching server certificate too.
+				"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
+				"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
 			},
 		})
 
 		testCases = append(testCases, testCase{
 			testType: serverTest,
-			name:     "SigningHash-ServerKeyExchange-Sign-" + hash.name,
+			name:     "SigningHash-ServerKeyExchange-Sign-" + alg.name,
 			config: Config{
-				CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
-				SignatureAndHashes: []signatureAndHash{
-					{signatureRSA, 42},
-					{signatureRSA, hash.id},
-					{signatureRSA, 255},
+				CipherSuites: []uint16{
+					TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+					TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+				},
+				SignatureAlgorithms: []signatureAlgorithm{
+					fakeSigAlg1,
+					alg.id,
+					fakeSigAlg2,
 				},
 			},
+			flags: []string{
+				"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
+				"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
+			},
+			expectedPeerSignatureAlgorithm: alg.id,
 		})
 
 		testCases = append(testCases, testCase{
-			name: "SigningHash-ServerKeyExchange-Verify-" + hash.name,
+			name: "SigningHash-ServerKeyExchange-Verify-" + alg.name,
 			config: Config{
-				CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
-				SignatureAndHashes: []signatureAndHash{
-					{signatureRSA, 42},
-					{signatureRSA, hash.id},
-					{signatureRSA, 255},
+				Certificates: []Certificate{getRunnerCertificate(alg.cert)},
+				CipherSuites: []uint16{
+					TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+					TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+				},
+				SignatureAlgorithms: []signatureAlgorithm{
+					alg.id,
 				},
 			},
-			flags: []string{"-expect-server-key-exchange-hash", strconv.Itoa(int(hash.id))},
+			flags: []string{"-expect-peer-signature-algorithm", strconv.Itoa(int(alg.id))},
 		})
 	}
 
-	// Test that hash resolution takes the signature type into account.
+	// Test that algorithm selection takes the key type into account.
 	testCases = append(testCases, testCase{
 		name: "SigningHash-ClientAuth-SignatureType",
 		config: Config{
 			ClientAuth: RequireAnyClientCert,
-			SignatureAndHashes: []signatureAndHash{
-				{signatureECDSA, hashSHA512},
-				{signatureRSA, hashSHA384},
-				{signatureECDSA, hashSHA1},
+			SignatureAlgorithms: []signatureAlgorithm{
+				signatureECDSAWithP521AndSHA512,
+				signatureRSAPKCS1WithSHA384,
+				signatureECDSAWithSHA1,
 			},
 		},
 		flags: []string{
 			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
 			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 		},
+		expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA384,
 	})
 
 	testCases = append(testCases, testCase{
@@ -4589,12 +4669,13 @@
 		name:     "SigningHash-ServerKeyExchange-SignatureType",
 		config: Config{
 			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
-			SignatureAndHashes: []signatureAndHash{
-				{signatureECDSA, hashSHA512},
-				{signatureRSA, hashSHA384},
-				{signatureECDSA, hashSHA1},
+			SignatureAlgorithms: []signatureAlgorithm{
+				signatureECDSAWithP521AndSHA512,
+				signatureRSAPKCS1WithSHA384,
+				signatureECDSAWithSHA1,
 			},
 		},
+		expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA384,
 	})
 
 	// Test that, if the list is missing, the peer falls back to SHA-1.
@@ -4602,11 +4683,11 @@
 		name: "SigningHash-ClientAuth-Fallback",
 		config: Config{
 			ClientAuth: RequireAnyClientCert,
-			SignatureAndHashes: []signatureAndHash{
-				{signatureRSA, hashSHA1},
+			SignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPKCS1WithSHA1,
 			},
 			Bugs: ProtocolBugs{
-				NoSignatureAndHashes: true,
+				NoSignatureAlgorithms: true,
 			},
 		},
 		flags: []string{
@@ -4620,11 +4701,11 @@
 		name:     "SigningHash-ServerKeyExchange-Fallback",
 		config: Config{
 			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
-			SignatureAndHashes: []signatureAndHash{
-				{signatureRSA, hashSHA1},
+			SignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPKCS1WithSHA1,
 			},
 			Bugs: ProtocolBugs{
-				NoSignatureAndHashes: true,
+				NoSignatureAlgorithms: true,
 			},
 		},
 	})
@@ -4636,13 +4717,13 @@
 		name:     "SigningHash-ClientAuth-Enforced",
 		config: Config{
 			Certificates: []Certificate{rsaCertificate},
-			SignatureAndHashes: []signatureAndHash{
-				{signatureRSA, hashMD5},
+			SignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPKCS1WithMD5,
 				// Advertise SHA-1 so the handshake will
 				// proceed, but the shim's preferences will be
 				// ignored in CertificateVerify generation, so
 				// MD5 will be chosen.
-				{signatureRSA, hashSHA1},
+				signatureRSAPKCS1WithSHA1,
 			},
 			Bugs: ProtocolBugs{
 				IgnorePeerSignatureAlgorithmPreferences: true,
@@ -4657,8 +4738,8 @@
 		name: "SigningHash-ServerKeyExchange-Enforced",
 		config: Config{
 			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
-			SignatureAndHashes: []signatureAndHash{
-				{signatureRSA, hashMD5},
+			SignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPKCS1WithMD5,
 			},
 			Bugs: ProtocolBugs{
 				IgnorePeerSignatureAlgorithmPreferences: true,
@@ -4674,65 +4755,65 @@
 		name: "Agree-Digest-Fallback",
 		config: Config{
 			ClientAuth: RequireAnyClientCert,
-			SignatureAndHashes: []signatureAndHash{
-				{signatureRSA, hashSHA512},
-				{signatureRSA, hashSHA1},
+			SignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPKCS1WithSHA512,
+				signatureRSAPKCS1WithSHA1,
 			},
 		},
 		flags: []string{
 			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
 			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 		},
-		digestPrefs:                     "SHA256",
-		expectedClientCertSignatureHash: hashSHA1,
+		digestPrefs:                    "SHA256",
+		expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA1,
 	})
 	testCases = append(testCases, testCase{
 		name: "Agree-Digest-SHA256",
 		config: Config{
 			ClientAuth: RequireAnyClientCert,
-			SignatureAndHashes: []signatureAndHash{
-				{signatureRSA, hashSHA1},
-				{signatureRSA, hashSHA256},
+			SignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPKCS1WithSHA1,
+				signatureRSAPKCS1WithSHA256,
 			},
 		},
 		flags: []string{
 			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
 			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 		},
-		digestPrefs:                     "SHA256,SHA1",
-		expectedClientCertSignatureHash: hashSHA256,
+		digestPrefs:                    "SHA256,SHA1",
+		expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA256,
 	})
 	testCases = append(testCases, testCase{
 		name: "Agree-Digest-SHA1",
 		config: Config{
 			ClientAuth: RequireAnyClientCert,
-			SignatureAndHashes: []signatureAndHash{
-				{signatureRSA, hashSHA1},
+			SignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPKCS1WithSHA1,
 			},
 		},
 		flags: []string{
 			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
 			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 		},
-		digestPrefs:                     "SHA512,SHA256,SHA1",
-		expectedClientCertSignatureHash: hashSHA1,
+		digestPrefs:                    "SHA512,SHA256,SHA1",
+		expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA1,
 	})
 	testCases = append(testCases, testCase{
 		name: "Agree-Digest-Default",
 		config: Config{
 			ClientAuth: RequireAnyClientCert,
-			SignatureAndHashes: []signatureAndHash{
-				{signatureRSA, hashSHA256},
-				{signatureECDSA, hashSHA256},
-				{signatureRSA, hashSHA1},
-				{signatureECDSA, hashSHA1},
+			SignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPKCS1WithSHA256,
+				signatureECDSAWithP256AndSHA256,
+				signatureRSAPKCS1WithSHA1,
+				signatureECDSAWithSHA1,
 			},
 		},
 		flags: []string{
 			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
 			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 		},
-		expectedClientCertSignatureHash: hashSHA256,
+		expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA256,
 	})
 }
 
@@ -5439,7 +5520,7 @@
 	addExtendedMasterSecretTests()
 	addRenegotiationTests()
 	addDTLSReplayTests()
-	addSigningHashTests()
+	addSignatureAlgorithmTests()
 	addDTLSRetransmitTests()
 	addExportKeyingMaterialTests()
 	addTLSUniqueTests()
diff --git a/ssl/test/runner/sign.go b/ssl/test/runner/sign.go
new file mode 100644
index 0000000..cf2d91d
--- /dev/null
+++ b/ssl/test/runner/sign.go
@@ -0,0 +1,219 @@
+// Copyright 2016 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 runner
+
+import (
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/md5"
+	"crypto/rsa"
+	"crypto/sha1"
+	_ "crypto/sha256"
+	_ "crypto/sha512"
+	"encoding/asn1"
+	"errors"
+	"fmt"
+	"math/big"
+)
+
+type signer interface {
+	supportsKey(key crypto.PrivateKey) bool
+	signMessage(key crypto.PrivateKey, config *Config, msg []byte) ([]byte, error)
+	verifyMessage(key crypto.PublicKey, msg, sig []byte) error
+}
+
+func selectSignatureAlgorithm(version uint16, key crypto.PrivateKey, peerSigAlgs, ourSigAlgs []signatureAlgorithm) (signatureAlgorithm, error) {
+	// 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
+	if len(peerSigAlgs) == 0 {
+		peerSigAlgs = []signatureAlgorithm{signatureRSAPKCS1WithSHA1, signatureECDSAWithSHA1}
+	}
+
+	for _, sigAlg := range ourSigAlgs {
+		if !isSupportedSignatureAlgorithm(sigAlg, peerSigAlgs) {
+			continue
+		}
+
+		signer, err := getSigner(version, key, sigAlg)
+		if err != nil {
+			continue
+		}
+
+		if signer.supportsKey(key) {
+			return sigAlg, nil
+		}
+	}
+	return 0, errors.New("tls: no common signature algorithms")
+}
+
+func signMessage(version uint16, key crypto.PrivateKey, config *Config, sigAlg signatureAlgorithm, msg []byte) ([]byte, error) {
+	signer, err := getSigner(version, key, sigAlg)
+	if err != nil {
+		return nil, err
+	}
+
+	return signer.signMessage(key, config, msg)
+}
+
+func verifyMessage(version uint16, key crypto.PublicKey, sigAlg signatureAlgorithm, msg, sig []byte) error {
+	signer, err := getSigner(version, key, sigAlg)
+	if err != nil {
+		return err
+	}
+
+	return signer.verifyMessage(key, msg, sig)
+}
+
+type rsaPKCS1Signer struct {
+	hash crypto.Hash
+}
+
+func (r *rsaPKCS1Signer) computeHash(msg []byte) []byte {
+	if r.hash == crypto.MD5SHA1 {
+		// crypto.MD5SHA1 is not a real hash function.
+		hashMD5 := md5.New()
+		hashMD5.Write(msg)
+		hashSHA1 := sha1.New()
+		hashSHA1.Write(msg)
+		return hashSHA1.Sum(hashMD5.Sum(nil))
+	}
+
+	h := r.hash.New()
+	h.Write(msg)
+	return h.Sum(nil)
+}
+
+func (r *rsaPKCS1Signer) supportsKey(key crypto.PrivateKey) bool {
+	_, ok := key.(*rsa.PrivateKey)
+	return ok
+}
+
+func (r *rsaPKCS1Signer) signMessage(key crypto.PrivateKey, config *Config, msg []byte) ([]byte, error) {
+	rsaKey, ok := key.(*rsa.PrivateKey)
+	if !ok {
+		return nil, errors.New("invalid key type for RSA-PKCS1")
+	}
+
+	return rsa.SignPKCS1v15(config.rand(), rsaKey, r.hash, r.computeHash(msg))
+}
+
+func (r *rsaPKCS1Signer) verifyMessage(key crypto.PublicKey, msg, sig []byte) error {
+	rsaKey, ok := key.(*rsa.PublicKey)
+	if !ok {
+		return errors.New("invalid key type for RSA-PKCS1")
+	}
+
+	return rsa.VerifyPKCS1v15(rsaKey, r.hash, r.computeHash(msg), sig)
+}
+
+type ecdsaSigner struct {
+	// TODO(davidben): In TLS 1.3, ECDSA signatures must match curves as
+	// well. Pass in a curve to enforce in 1.3 alone.
+	hash crypto.Hash
+}
+
+func (e *ecdsaSigner) supportsKey(key crypto.PrivateKey) bool {
+	_, ok := key.(*ecdsa.PrivateKey)
+	return ok
+}
+
+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 (e *ecdsaSigner) signMessage(key crypto.PrivateKey, config *Config, msg []byte) ([]byte, error) {
+	ecdsaKey, ok := key.(*ecdsa.PrivateKey)
+	if !ok {
+		return nil, errors.New("invalid key type for ECDSA")
+	}
+
+	h := e.hash.New()
+	h.Write(msg)
+	digest := h.Sum(nil)
+
+	r, s, err := ecdsa.Sign(config.rand(), ecdsaKey, digest)
+	if err != nil {
+		return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
+	}
+	order := ecdsaKey.Curve.Params().N
+	r = maybeCorruptECDSAValue(r, config.Bugs.BadECDSAR, order)
+	s = maybeCorruptECDSAValue(s, config.Bugs.BadECDSAS, order)
+	return asn1.Marshal(ecdsaSignature{r, s})
+}
+
+func (e *ecdsaSigner) verifyMessage(key crypto.PublicKey, msg, sig []byte) error {
+	ecdsaKey, ok := key.(*ecdsa.PublicKey)
+	if !ok {
+		return errors.New("invalid key type for ECDSA")
+	}
+
+	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")
+	}
+
+	h := e.hash.New()
+	h.Write(msg)
+	if !ecdsa.Verify(ecdsaKey, h.Sum(nil), ecdsaSig.R, ecdsaSig.S) {
+		return errors.New("ECDSA verification failure")
+	}
+	return nil
+}
+
+func getSigner(version uint16, key interface{}, sigAlg signatureAlgorithm) (signer, error) {
+	// TLS 1.1 and below use legacy signature algorithms.
+	if version < VersionTLS12 {
+		switch key.(type) {
+		case *rsa.PrivateKey, *rsa.PublicKey:
+			return &rsaPKCS1Signer{crypto.MD5SHA1}, nil
+		case *ecdsa.PrivateKey, *ecdsa.PublicKey:
+			return &ecdsaSigner{crypto.SHA1}, nil
+		default:
+			return nil, errors.New("unknown key type")
+		}
+	}
+
+	// TODO(davidben): Implement RSA-PSS for TLS 1.3.
+	switch sigAlg {
+	case signatureRSAPKCS1WithMD5:
+		return &rsaPKCS1Signer{crypto.MD5}, nil
+	case signatureRSAPKCS1WithSHA1:
+		return &rsaPKCS1Signer{crypto.SHA1}, nil
+	case signatureRSAPKCS1WithSHA256:
+		return &rsaPKCS1Signer{crypto.SHA256}, nil
+	case signatureRSAPKCS1WithSHA384:
+		return &rsaPKCS1Signer{crypto.SHA384}, nil
+	case signatureRSAPKCS1WithSHA512:
+		return &rsaPKCS1Signer{crypto.SHA512}, nil
+	case signatureECDSAWithSHA1:
+		return &ecdsaSigner{crypto.SHA1}, nil
+	case signatureECDSAWithP256AndSHA256:
+		return &ecdsaSigner{crypto.SHA256}, nil
+	case signatureECDSAWithP384AndSHA384:
+		return &ecdsaSigner{crypto.SHA384}, nil
+	case signatureECDSAWithP521AndSHA512:
+		return &ecdsaSigner{crypto.SHA512}, nil
+	default:
+		return nil, fmt.Errorf("unsupported signature algorithm %04x", sigAlg)
+	}
+}
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index cf3541d..32b91e9 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -145,8 +145,8 @@
   { "-mtu", &TestConfig::mtu },
   { "-export-keying-material", &TestConfig::export_keying_material },
   { "-expect-total-renegotiations", &TestConfig::expect_total_renegotiations },
-  { "-expect-server-key-exchange-hash",
-    &TestConfig::expect_server_key_exchange_hash },
+  { "-expect-peer-signature-algorithm",
+    &TestConfig::expect_peer_signature_algorithm },
   { "-expect-curve-id", &TestConfig::expect_curve_id },
   { "-expect-dhe-group-size", &TestConfig::expect_dhe_group_size },
   { "-initial-timeout-duration-ms", &TestConfig::initial_timeout_duration_ms },
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index b554f14..a3e9ad1 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -99,7 +99,7 @@
   bool renegotiate_freely = false;
   bool renegotiate_ignore = false;
   bool disable_npn = false;
-  int expect_server_key_exchange_hash = 0;
+  int expect_peer_signature_algorithm = 0;
   bool p384_only = false;
   bool enable_all_curves = false;
   bool use_sparse_dh_prime = false;