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")
}