Support Ed25519 keys in BoGo.

These will be used to test the C implementation.

BUG=187

Change-Id: If397eaa51885c8140a63c5f731ce58a8ad6949aa
Reviewed-on: https://boringssl-review.googlesource.com/14452
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 95dcbd0..e11e8c7 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -184,6 +184,7 @@
 	signatureECDSAWithP256AndSHA256,
 	signatureRSAPKCS1WithSHA1,
 	signatureECDSAWithSHA1,
+	signatureEd25519,
 }
 
 // SRTP protection profiles (See RFC 5764, section 4.1.2)
diff --git a/ssl/test/runner/ed25519/ed25519.go b/ssl/test/runner/ed25519/ed25519.go
index e9d9873..dfef0ff 100644
--- a/ssl/test/runner/ed25519/ed25519.go
+++ b/ssl/test/runner/ed25519/ed25519.go
@@ -66,18 +66,31 @@
 		rand = cryptorand.Reader
 	}
 
-	privateKey = make([]byte, PrivateKeySize)
-	publicKey = make([]byte, PublicKeySize)
-	_, err = io.ReadFull(rand, privateKey[:32])
+	var seed [32]byte
+	_, err = io.ReadFull(rand, seed[:])
 	if err != nil {
 		return nil, nil, err
 	}
 
-	digest := sha512.Sum512(privateKey[:32])
+	publicKey, privateKey = NewKeyPairFromSeed(seed[:])
+	return publicKey, privateKey, nil
+}
+
+// NewKeyPairFromSeed calculates a public and private key from a 32-byte
+// Ed25519 seed.
+func NewKeyPairFromSeed(seed []byte) (publicKey PublicKey, privateKey PrivateKey) {
+	if len(seed) != 32 {
+		panic("Invalid seed length.")
+	}
+
+	digest := sha512.Sum512(seed)
 	digest[0] &= 248
 	digest[31] &= 127
 	digest[31] |= 64
 
+	privateKey = make([]byte, PrivateKeySize)
+	publicKey = make([]byte, PublicKeySize)
+
 	var A edwards25519.ExtendedGroupElement
 	var hBytes [32]byte
 	copy(hBytes[:], digest[:])
@@ -85,10 +98,11 @@
 	var publicKeyBytes [32]byte
 	A.ToBytes(&publicKeyBytes)
 
+	copy(privateKey, seed[:])
 	copy(privateKey[32:], publicKeyBytes[:])
 	copy(publicKey, publicKeyBytes[:])
 
-	return publicKey, privateKey, nil
+	return publicKey, privateKey
 }
 
 // Sign signs the message with privateKey and returns a signature. It will
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index d73722c..c531a28 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -19,6 +19,8 @@
 	"net"
 	"strconv"
 	"time"
+
+	"./ed25519"
 )
 
 type clientHandshakeState struct {
@@ -820,7 +822,7 @@
 
 		c.peerSignatureAlgorithm = certVerifyMsg.signatureAlgorithm
 		input := hs.finishedHash.certificateVerifyInput(serverCertificateVerifyContextTLS13)
-		err = verifyMessage(c.vers, leaf.PublicKey, c.config, certVerifyMsg.signatureAlgorithm, input, certVerifyMsg.signature)
+		err = verifyMessage(c.vers, getCertificatePublicKey(leaf), c.config, certVerifyMsg.signatureAlgorithm, input, certVerifyMsg.signature)
 		if err != nil {
 			return err
 		}
@@ -1216,12 +1218,13 @@
 		}
 	}
 
-	switch certs[0].PublicKey.(type) {
-	case *rsa.PublicKey, *ecdsa.PublicKey:
+	publicKey := getCertificatePublicKey(certs[0])
+	switch publicKey.(type) {
+	case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
 		break
 	default:
 		c.sendAlert(alertUnsupportedCertificate)
-		return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey)
+		return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", publicKey)
 	}
 
 	c.peerCertificates = certs
@@ -1695,6 +1698,7 @@
 				switch {
 				case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA:
 				case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA:
+				case ecdsaAvail && isEd25519Certificate(x509Cert):
 				default:
 					continue findCert
 				}
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index ef144c3..8dc0446 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -17,6 +17,8 @@
 	"io"
 	"math/big"
 	"time"
+
+	"./ed25519"
 )
 
 // serverHandshakeState contains details of a server handshake in progress.
@@ -1095,6 +1097,9 @@
 	hs.ellipticOk = supportedCurve && supportedPointFormat
 
 	_, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey)
+	// Ed25519 also uses ECDSA certificates.
+	_, ed25519Ok := hs.cert.PrivateKey.(ed25519.PrivateKey)
+	hs.ecdsaOk = hs.ecdsaOk || ed25519Ok
 
 	// For test purposes, check that the peer never offers a session when
 	// renegotiating.
@@ -1859,13 +1864,13 @@
 	}
 
 	if len(certs) > 0 {
-		var pub crypto.PublicKey
-		switch key := certs[0].PublicKey.(type) {
-		case *ecdsa.PublicKey, *rsa.PublicKey:
-			pub = key
+		pub := getCertificatePublicKey(certs[0])
+		switch pub.(type) {
+		case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:
+			break
 		default:
 			c.sendAlert(alertUnsupportedCertificate)
-			return nil, fmt.Errorf("tls: client's certificate contains an unsupported public key of type %T", certs[0].PublicKey)
+			return nil, fmt.Errorf("tls: client's certificate contains an unsupported public key of type %T", pub)
 		}
 		c.peerCertificates = certs
 		return pub, nil
diff --git a/ssl/test/runner/key_agreement.go b/ssl/test/runner/key_agreement.go
index 2a3e924..3936d88 100644
--- a/ssl/test/runner/key_agreement.go
+++ b/ssl/test/runner/key_agreement.go
@@ -17,6 +17,7 @@
 	"math/big"
 
 	"./curve25519"
+	"./ed25519"
 )
 
 type keyType int
@@ -378,14 +379,16 @@
 
 func (ka *signedKeyAgreement) verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, params []byte, sig []byte) error {
 	// The peer's key must match the cipher type.
+	publicKey := getCertificatePublicKey(cert)
 	switch ka.keyType {
 	case keyTypeECDSA:
-		_, ok := cert.PublicKey.(*ecdsa.PublicKey)
-		if !ok {
-			return errors.New("tls: ECDHE ECDSA requires a ECDSA server public key")
+		_, edsaOk := publicKey.(*ecdsa.PublicKey)
+		_, ed25519Ok := publicKey.(ed25519.PublicKey)
+		if !edsaOk && !ed25519Ok {
+			return errors.New("tls: ECDHE ECDSA requires a ECDSA or Ed25519 server public key")
 		}
 	case keyTypeRSA:
-		_, ok := cert.PublicKey.(*rsa.PublicKey)
+		_, ok := publicKey.(*rsa.PublicKey)
 		if !ok {
 			return errors.New("tls: ECDHE RSA requires a RSA server public key")
 		}
@@ -419,7 +422,7 @@
 	}
 	sig = sig[2:]
 
-	return verifyMessage(ka.version, cert.PublicKey, config, sigAlg, msg, sig)
+	return verifyMessage(ka.version, publicKey, config, sigAlg, msg, sig)
 }
 
 // ecdheKeyAgreement implements a TLS key agreement where the server
diff --git a/ssl/test/runner/sign.go b/ssl/test/runner/sign.go
index 5f56ff9..0ede1c9 100644
--- a/ssl/test/runner/sign.go
+++ b/ssl/test/runner/sign.go
@@ -17,6 +17,8 @@
 	"errors"
 	"fmt"
 	"math/big"
+
+	"./ed25519"
 )
 
 type signer interface {
@@ -242,6 +244,35 @@
 	return rsa.VerifyPSS(rsaKey, r.hash, h.Sum(nil), sig, &pssOptions)
 }
 
+type ed25519Signer struct{}
+
+func (e *ed25519Signer) supportsKey(key crypto.PrivateKey) bool {
+	_, ok := key.(ed25519.PrivateKey)
+	return ok
+}
+
+func (e *ed25519Signer) signMessage(key crypto.PrivateKey, config *Config, msg []byte) ([]byte, error) {
+	privKey, ok := key.(ed25519.PrivateKey)
+	if !ok {
+		return nil, errors.New("invalid key type for Ed25519")
+	}
+
+	return ed25519.Sign(privKey, msg), nil
+}
+
+func (e *ed25519Signer) verifyMessage(key crypto.PublicKey, msg, sig []byte) error {
+	pubKey, ok := key.(ed25519.PublicKey)
+	if !ok {
+		return errors.New("invalid key type for Ed25519")
+	}
+
+	if !ed25519.Verify(pubKey, msg, sig) {
+		return errors.New("invalid Ed25519 signature")
+	}
+
+	return nil
+}
+
 func getSigner(version uint16, key interface{}, config *Config, sigAlg signatureAlgorithm) (signer, error) {
 	// TLS 1.1 and below use legacy signature algorithms.
 	if version < VersionTLS12 {
@@ -291,6 +322,8 @@
 		return &rsaPSSSigner{crypto.SHA384}, nil
 	case signatureRSAPSSWithSHA512:
 		return &rsaPSSSigner{crypto.SHA512}, nil
+	case signatureEd25519:
+		return &ed25519Signer{}, nil
 	}
 
 	return nil, fmt.Errorf("unsupported signature algorithm %04x", sigAlg)
diff --git a/ssl/test/runner/tls.go b/ssl/test/runner/tls.go
index 24f9b1e..225794b 100644
--- a/ssl/test/runner/tls.go
+++ b/ssl/test/runner/tls.go
@@ -6,6 +6,7 @@
 package runner
 
 import (
+	"bytes"
 	"crypto"
 	"crypto/ecdsa"
 	"crypto/rsa"
@@ -16,6 +17,8 @@
 	"net"
 	"strings"
 	"time"
+
+	"./ed25519"
 )
 
 // Server returns a new TLS server side connection
@@ -226,7 +229,7 @@
 		return
 	}
 
-	switch pub := x509Cert.PublicKey.(type) {
+	switch pub := getCertificatePublicKey(x509Cert).(type) {
 	case *rsa.PublicKey:
 		priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
 		if !ok {
@@ -248,6 +251,16 @@
 			err = errors.New("crypto/tls: private key does not match public key")
 			return
 		}
+	case ed25519.PublicKey:
+		priv, ok := cert.PrivateKey.(ed25519.PrivateKey)
+		if !ok {
+			err = errors.New("crypto/tls: private key type does not match public key type")
+			return
+		}
+		if !bytes.Equal(priv[32:], pub) {
+			err = errors.New("crypto/tls: private key does not match public key")
+			return
+		}
 	default:
 		err = errors.New("crypto/tls: unknown public key algorithm")
 		return
@@ -256,6 +269,27 @@
 	return
 }
 
+var ed25519SPKIPrefix = []byte{0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00}
+
+func isEd25519Certificate(cert *x509.Certificate) bool {
+	return bytes.HasPrefix(cert.RawSubjectPublicKeyInfo, ed25519SPKIPrefix) && len(cert.RawSubjectPublicKeyInfo) == len(ed25519SPKIPrefix)+32
+}
+
+func getCertificatePublicKey(cert *x509.Certificate) crypto.PublicKey {
+	if cert.PublicKey != nil {
+		return cert.PublicKey
+	}
+
+	if isEd25519Certificate(cert) {
+		return ed25519.PublicKey(cert.RawSubjectPublicKeyInfo[len(ed25519SPKIPrefix):])
+	}
+
+	return nil
+}
+
+var ed25519PKCS8Prefix = []byte{0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70,
+	0x04, 0x22, 0x04, 0x20}
+
 // 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.
@@ -275,5 +309,11 @@
 		return key, nil
 	}
 
+	if bytes.HasPrefix(der, ed25519PKCS8Prefix) && len(der) == len(ed25519PKCS8Prefix)+32 {
+		seed := der[len(ed25519PKCS8Prefix):]
+		_, key := ed25519.NewKeyPairFromSeed(seed)
+		return key, nil
+	}
+
 	return nil, errors.New("crypto/tls: failed to parse private key")
 }