]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/x509: add support for Ed25519 certificates and keys
authorFilippo Valsorda <filippo@golang.org>
Mon, 6 May 2019 18:06:47 +0000 (14:06 -0400)
committerFilippo Valsorda <filippo@golang.org>
Wed, 15 May 2019 18:51:12 +0000 (18:51 +0000)
Based on RFC 8410.

Updates #25355

Change-Id: If7abb7eeb0ede10a9bb3d2004f2116e587c6207a
Reviewed-on: https://go-review.googlesource.com/c/go/+/175478
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
src/crypto/x509/pkcs8.go
src/crypto/x509/pkcs8_test.go
src/crypto/x509/x509.go
src/crypto/x509/x509_test.go
src/go/build/deps_test.go

index fa8d4082288ee3fe97e6b63252983e5297b61269..fa1847e5e7f6373f6d3534415da2f87c5ed1edc2 100644 (file)
@@ -6,6 +6,7 @@ package x509
 
 import (
        "crypto/ecdsa"
+       "crypto/ed25519"
        "crypto/rsa"
        "crypto/x509/pkix"
        "encoding/asn1"
@@ -23,8 +24,9 @@ type pkcs8 struct {
        // optional attributes omitted.
 }
 
-// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key.
-// See RFC 5208.
+// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. It returns a
+// *rsa.PrivateKey, a *ecdsa.PrivateKey, or a ed25519.PrivateKey. More types
+// might be supported in future versions. See RFC 5208 and RFC 8410.
 func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
        var privKey pkcs8
        if _, err := asn1.Unmarshal(der, &privKey); err != nil {
@@ -56,16 +58,28 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
                }
                return key, nil
 
+       case privKey.Algo.Algorithm.Equal(oidPublicKeyEd25519):
+               if l := len(privKey.Algo.Parameters.FullBytes); l != 0 {
+                       return nil, errors.New("x509: invalid Ed25519 private key parameters")
+               }
+               var curvePrivateKey []byte
+               if _, err := asn1.Unmarshal(privKey.PrivateKey, &curvePrivateKey); err != nil {
+                       return nil, fmt.Errorf("x509: invalid Ed25519 private key: %v", err)
+               }
+               if l := len(curvePrivateKey); l != ed25519.SeedSize {
+                       return nil, fmt.Errorf("x509: invalid Ed25519 private key length: %d", l)
+               }
+               return ed25519.NewKeyFromSeed(curvePrivateKey), nil
+
        default:
                return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
        }
 }
 
 // MarshalPKCS8PrivateKey converts a private key to PKCS#8 encoded form.
-// The following key types are supported: *rsa.PrivateKey, *ecdsa.PrivateKey.
-// Unsupported key types result in an error.
-//
-// See RFC 5208.
+// The following key types are currently supported: *rsa.PrivateKey,
+// *ecdsa.PrivateKey and ed25519.PrivateKey. Unsupported key types result in an
+// error. See RFC 5208 and RFC 8410.
 func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error) {
        var privKey pkcs8
 
@@ -99,6 +113,16 @@ func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error) {
                        return nil, errors.New("x509: failed to marshal EC private key while building PKCS#8: " + err.Error())
                }
 
+       case ed25519.PrivateKey:
+               privKey.Algo = pkix.AlgorithmIdentifier{
+                       Algorithm: oidPublicKeyEd25519,
+               }
+               curvePrivateKey, err := asn1.Marshal(k.Seed())
+               if err != nil {
+                       return nil, fmt.Errorf("x509: failed to marshal private key: %v", err)
+               }
+               privKey.PrivateKey = curvePrivateKey
+
        default:
                return nil, fmt.Errorf("x509: unknown key type while marshaling PKCS#8: %T", key)
        }
index 9e890c386e5d10ab330e7a75fc93296221e4d22d..cb7ee4c1627f3b021704a09feaa8f98418ed7ce7 100644 (file)
@@ -7,6 +7,7 @@ package x509
 import (
        "bytes"
        "crypto/ecdsa"
+       "crypto/ed25519"
        "crypto/elliptic"
        "crypto/rsa"
        "encoding/hex"
@@ -40,6 +41,9 @@ var pkcs8P384PrivateKeyHex = `3081b6020100301006072a8648ce3d020106052b8104002204
 // expected and the Go test will fail to recreate it exactly.
 var pkcs8P521PrivateKeyHex = `3081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044200cfe0b87113a205cf291bb9a8cd1a74ac6c7b2ebb8199aaa9a5010d8b8012276fa3c22ac913369fa61beec2a3b8b4516bc049bde4fb3b745ac11b56ab23ac52e361a1818903818600040138f75acdd03fbafa4f047a8e4b272ba9d555c667962b76f6f232911a5786a0964e5edea6bd21a6f8725720958de049c6e3e6661c1c91b227cebee916c0319ed6ca003db0a3206d372229baf9dd25d868bf81140a518114803ce40c1855074d68c4e9dab9e65efba7064c703b400f1767f217dac82715ac1f6d88c74baf47a7971de4ea`
 
+// From RFC 8410, Section 7.
+var pkcs8Ed25519PrivateKeyHex = `302e020100300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842`
+
 func TestPKCS8(t *testing.T) {
        tests := []struct {
                name    string
@@ -76,6 +80,11 @@ func TestPKCS8(t *testing.T) {
                        keyType: reflect.TypeOf(&ecdsa.PrivateKey{}),
                        curve:   elliptic.P521(),
                },
+               {
+                       name:    "Ed25519 private key",
+                       keyHex:  pkcs8Ed25519PrivateKeyHex,
+                       keyType: reflect.TypeOf(ed25519.PrivateKey{}),
+               },
        }
 
        for _, test := range tests {
index 4aca7ca40b13b969f523966876802ca36c1968c1..16c0526196d8603ee8aefdd2c80c994572dce98f 100644 (file)
@@ -14,6 +14,7 @@ import (
        "crypto"
        "crypto/dsa"
        "crypto/ecdsa"
+       "crypto/ed25519"
        "crypto/elliptic"
        "crypto/rsa"
        _ "crypto/sha1"
@@ -50,7 +51,7 @@ type pkixPublicKey struct {
 // types result in an error.
 //
 // On success, pub will be of type *rsa.PublicKey, *dsa.PublicKey,
-// or *ecdsa.PublicKey.
+// *ecdsa.PublicKey, or ed25519.PublicKey.
 func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) {
        var pki publicKeyInfo
        if rest, err := asn1.Unmarshal(derBytes, &pki); err != nil {
@@ -95,6 +96,9 @@ func marshalPublicKey(pub interface{}) (publicKeyBytes []byte, publicKeyAlgorith
                        return
                }
                publicKeyAlgorithm.Parameters.FullBytes = paramBytes
+       case ed25519.PublicKey:
+               publicKeyBytes = pub
+               publicKeyAlgorithm.Algorithm = oidPublicKeyEd25519
        default:
                return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: only RSA and ECDSA public keys supported")
        }
@@ -191,6 +195,7 @@ const (
        SHA256WithRSAPSS
        SHA384WithRSAPSS
        SHA512WithRSAPSS
+       PureEd25519
 )
 
 func (algo SignatureAlgorithm) isRSAPSS() bool {
@@ -218,12 +223,14 @@ const (
        RSA
        DSA
        ECDSA
+       Ed25519
 )
 
 var publicKeyAlgoName = [...]string{
-       RSA:   "RSA",
-       DSA:   "DSA",
-       ECDSA: "ECDSA",
+       RSA:     "RSA",
+       DSA:     "DSA",
+       ECDSA:   "ECDSA",
+       Ed25519: "Ed25519",
 }
 
 func (algo PublicKeyAlgorithm) String() string {
@@ -282,6 +289,11 @@ func (algo PublicKeyAlgorithm) String() string {
 //
 // ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
 //    us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 }
+//
+//
+// RFC 8410 3 Curve25519 and Curve448 Algorithm Identifiers
+//
+// id-Ed25519   OBJECT IDENTIFIER ::= { 1 3 101 112 }
 
 var (
        oidSignatureMD2WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
@@ -297,6 +309,7 @@ var (
        oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
        oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
        oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
+       oidSignatureEd25519         = asn1.ObjectIdentifier{1, 3, 101, 112}
 
        oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
        oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
@@ -333,6 +346,7 @@ var signatureAlgorithmDetails = []struct {
        {ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, ECDSA, crypto.SHA256},
        {ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, ECDSA, crypto.SHA384},
        {ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, ECDSA, crypto.SHA512},
+       {PureEd25519, "Ed25519", oidSignatureEd25519, Ed25519, crypto.Hash(0) /* no pre-hashing */},
 }
 
 // pssParameters reflects the parameters in an AlgorithmIdentifier that
@@ -393,6 +407,14 @@ func rsaPSSParameters(hashFunc crypto.Hash) asn1.RawValue {
 }
 
 func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) SignatureAlgorithm {
+       if ai.Algorithm.Equal(oidSignatureEd25519) {
+               // RFC 8410, Section 3
+               // > For all of the OIDs, the parameters MUST be absent.
+               if len(ai.Parameters.FullBytes) != 0 {
+                       return UnknownSignatureAlgorithm
+               }
+       }
+
        if !ai.Algorithm.Equal(oidSignatureRSAPSS) {
                for _, details := range signatureAlgorithmDetails {
                        if ai.Algorithm.Equal(details.oid) {
@@ -455,9 +477,10 @@ func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) SignatureAlgorithm
 // id-ecPublicKey OBJECT IDENTIFIER ::= {
 //       iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
 var (
-       oidPublicKeyRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
-       oidPublicKeyDSA   = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
-       oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
+       oidPublicKeyRSA     = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
+       oidPublicKeyDSA     = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
+       oidPublicKeyECDSA   = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
+       oidPublicKeyEd25519 = oidSignatureEd25519
 )
 
 func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm {
@@ -468,6 +491,8 @@ func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm
                return DSA
        case oid.Equal(oidPublicKeyECDSA):
                return ECDSA
+       case oid.Equal(oidPublicKeyEd25519):
+               return Ed25519
        }
        return UnknownPublicKeyAlgorithm
 }
@@ -874,28 +899,29 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
 
        switch hashType {
        case crypto.Hash(0):
-               return ErrUnsupportedAlgorithm
+               if pubKeyAlgo != Ed25519 {
+                       return ErrUnsupportedAlgorithm
+               }
        case crypto.MD5:
                return InsecureAlgorithmError(algo)
+       default:
+               if !hashType.Available() {
+                       return ErrUnsupportedAlgorithm
+               }
+               h := hashType.New()
+               h.Write(signed)
+               signed = h.Sum(nil)
        }
 
-       if !hashType.Available() {
-               return ErrUnsupportedAlgorithm
-       }
-       h := hashType.New()
-
-       h.Write(signed)
-       digest := h.Sum(nil)
-
        switch pub := publicKey.(type) {
        case *rsa.PublicKey:
                if pubKeyAlgo != RSA {
                        return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub)
                }
                if algo.isRSAPSS() {
-                       return rsa.VerifyPSS(pub, hashType, digest, signature, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash})
+                       return rsa.VerifyPSS(pub, hashType, signed, signature, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash})
                } else {
-                       return rsa.VerifyPKCS1v15(pub, hashType, digest, signature)
+                       return rsa.VerifyPKCS1v15(pub, hashType, signed, signature)
                }
        case *dsa.PublicKey:
                if pubKeyAlgo != DSA {
@@ -910,7 +936,7 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
                if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 {
                        return errors.New("x509: DSA signature contained zero or negative values")
                }
-               if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) {
+               if !dsa.Verify(pub, signed, dsaSig.R, dsaSig.S) {
                        return errors.New("x509: DSA verification failure")
                }
                return
@@ -927,10 +953,18 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
                if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
                        return errors.New("x509: ECDSA signature contained zero or negative values")
                }
-               if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) {
+               if !ecdsa.Verify(pub, signed, ecdsaSig.R, ecdsaSig.S) {
                        return errors.New("x509: ECDSA verification failure")
                }
                return
+       case ed25519.PublicKey:
+               if pubKeyAlgo != Ed25519 {
+                       return signaturePublicKeyAlgoMismatchError(pubKeyAlgo, pub)
+               }
+               if !ed25519.Verify(pub, signed, signature) {
+                       return errors.New("x509: Ed25519 verification failure")
+               }
+               return
        }
        return ErrUnsupportedAlgorithm
 }
@@ -1068,6 +1102,18 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
                        Y:     y,
                }
                return pub, nil
+       case Ed25519:
+               // RFC 8410, Section 3
+               // > For all of the OIDs, the parameters MUST be absent.
+               if len(keyData.Algorithm.Parameters.FullBytes) != 0 {
+                       return nil, errors.New("x509: Ed25519 key encoded with illegal parameters")
+               }
+               if len(asn1Data) != ed25519.PublicKeySize {
+                       return nil, errors.New("x509: wrong Ed25519 public key size")
+               }
+               pub := make([]byte, ed25519.PublicKeySize)
+               copy(pub, asn1Data)
+               return ed25519.PublicKey(pub), nil
        default:
                return nil, nil
        }
@@ -1989,8 +2035,12 @@ func signingParamsForPublicKey(pub interface{}, requestedSigAlgo SignatureAlgori
                        err = errors.New("x509: unknown elliptic curve")
                }
 
+       case ed25519.PublicKey:
+               pubType = Ed25519
+               sigAlgo.Algorithm = oidSignatureEd25519
+
        default:
-               err = errors.New("x509: only RSA and ECDSA keys supported")
+               err = errors.New("x509: only RSA, ECDSA and Ed25519 keys supported")
        }
 
        if err != nil {
@@ -2009,7 +2059,7 @@ func signingParamsForPublicKey(pub interface{}, requestedSigAlgo SignatureAlgori
                                return
                        }
                        sigAlgo.Algorithm, hashFunc = details.oid, details.hash
-                       if hashFunc == 0 {
+                       if hashFunc == 0 && pubType != Ed25519 {
                                err = errors.New("x509: cannot sign with hash function requested")
                                return
                        }
@@ -2073,8 +2123,9 @@ var emptyASN1Subject = []byte{0x30, 0}
 //
 // The returned slice is the certificate in DER encoding.
 //
-// All keys types that are implemented via crypto.Signer are supported (This
-// includes *rsa.PublicKey and *ecdsa.PublicKey.)
+// The currently supported key types are *rsa.PublicKey, *ecdsa.PublicKey and
+// ed25519.PublicKey. pub must be a supported key type, and priv must be a
+// crypto.Signer with a supported public key.
 //
 // The AuthorityKeyId will be taken from the SubjectKeyId of parent, if any,
 // unless the resulting certificate is self-signed. Otherwise the value from
@@ -2135,15 +2186,16 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
        if err != nil {
                return
        }
-
        c.Raw = tbsCertContents
 
-       h := hashFunc.New()
-       h.Write(tbsCertContents)
-       digest := h.Sum(nil)
+       signed := tbsCertContents
+       if hashFunc != 0 {
+               h := hashFunc.New()
+               h.Write(signed)
+               signed = h.Sum(nil)
+       }
 
-       var signerOpts crypto.SignerOpts
-       signerOpts = hashFunc
+       var signerOpts crypto.SignerOpts = hashFunc
        if template.SignatureAlgorithm != 0 && template.SignatureAlgorithm.isRSAPSS() {
                signerOpts = &rsa.PSSOptions{
                        SaltLength: rsa.PSSSaltLengthEqualsHash,
@@ -2152,7 +2204,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
        }
 
        var signature []byte
-       signature, err = key.Sign(rand, digest, signerOpts)
+       signature, err = key.Sign(rand, signed, signerOpts)
        if err != nil {
                return
        }
@@ -2403,8 +2455,9 @@ func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error)
 //
 // priv is the private key to sign the CSR with, and the corresponding public
 // key will be included in the CSR. It must implement crypto.Signer and its
-// Public() method must return a *rsa.PublicKey or a *ecdsa.PublicKey. (A
-// *rsa.PrivateKey or *ecdsa.PrivateKey satisfies this.)
+// Public() method must return a *rsa.PublicKey or a *ecdsa.PublicKey or a
+// ed25519.PublicKey. (A *rsa.PrivateKey, *ecdsa.PrivateKey or
+// ed25519.PrivateKey satisfies this.)
 //
 // The returned slice is the certificate request in DER encoding.
 func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv interface{}) (csr []byte, err error) {
@@ -2553,12 +2606,15 @@ func CreateCertificateRequest(rand io.Reader, template *CertificateRequest, priv
        }
        tbsCSR.Raw = tbsCSRContents
 
-       h := hashFunc.New()
-       h.Write(tbsCSRContents)
-       digest := h.Sum(nil)
+       signed := tbsCSRContents
+       if hashFunc != 0 {
+               h := hashFunc.New()
+               h.Write(signed)
+               signed = h.Sum(nil)
+       }
 
        var signature []byte
-       signature, err = key.Sign(rand, digest, hashFunc)
+       signature, err = key.Sign(rand, signed, hashFunc)
        if err != nil {
                return
        }
index f5851f1f11b79fe03dbb8e5e038e2a3fd6f05d83..7dadb77dfd778bec3697f1d20a9b4ffedb647750 100644 (file)
@@ -8,6 +8,7 @@ import (
        "bytes"
        "crypto/dsa"
        "crypto/ecdsa"
+       "crypto/ed25519"
        "crypto/elliptic"
        "crypto/rand"
        "crypto/rsa"
@@ -65,27 +66,39 @@ func TestPKCS1MismatchPublicKeyFormat(t *testing.T) {
        }
 }
 
-func TestParsePKIXPublicKey(t *testing.T) {
-       block, _ := pem.Decode([]byte(pemPublicKey))
+func testParsePKIXPublicKey(t *testing.T, pemBytes string) (pub interface{}) {
+       block, _ := pem.Decode([]byte(pemBytes))
        pub, err := ParsePKIXPublicKey(block.Bytes)
        if err != nil {
-               t.Errorf("Failed to parse RSA public key: %s", err)
-               return
-       }
-       rsaPub, ok := pub.(*rsa.PublicKey)
-       if !ok {
-               t.Errorf("Value returned from ParsePKIXPublicKey was not an RSA public key")
-               return
+               t.Fatalf("Failed to parse public key: %s", err)
        }
 
-       pubBytes2, err := MarshalPKIXPublicKey(rsaPub)
+       pubBytes2, err := MarshalPKIXPublicKey(pub)
        if err != nil {
-               t.Errorf("Failed to marshal RSA public key for the second time: %s", err)
+               t.Errorf("Failed to marshal public key for the second time: %s", err)
                return
        }
        if !bytes.Equal(pubBytes2, block.Bytes) {
                t.Errorf("Reserialization of public key didn't match. got %x, want %x", pubBytes2, block.Bytes)
        }
+       return
+}
+
+func TestParsePKIXPublicKey(t *testing.T) {
+       t.Run("RSA", func(t *testing.T) {
+               pub := testParsePKIXPublicKey(t, pemPublicKey)
+               _, ok := pub.(*rsa.PublicKey)
+               if !ok {
+                       t.Errorf("Value returned from ParsePKIXPublicKey was not an RSA public key")
+               }
+       })
+       t.Run("Ed25519", func(t *testing.T) {
+               pub := testParsePKIXPublicKey(t, pemEd25519Key)
+               _, ok := pub.(ed25519.PublicKey)
+               if !ok {
+                       t.Errorf("Value returned from ParsePKIXPublicKey was not an Ed25519 public key")
+               }
+       })
 }
 
 var pemPublicKey = `-----BEGIN PUBLIC KEY-----
@@ -117,6 +130,13 @@ wg/HcAJWY60xZTJDFN+Qfx8ZQvBEin6c2/h+zZi5IVY=
 -----END RSA PRIVATE KEY-----
 `
 
+// pemEd25519Key is the example from RFC 8410, Secrion 4.
+var pemEd25519Key = `
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
+-----END PUBLIC KEY-----
+`
+
 func TestPKIXMismatchPublicKeyFormat(t *testing.T) {
 
        const pkcs1PublicKey = "308201080282010100817cfed98bcaa2e2a57087451c7674e0c675686dc33ff1268b0c2a6ee0202dec710858ee1c31bdf5e7783582e8ca800be45f3275c6576adc35d98e26e95bb88ca5beb186f853b8745d88bc9102c5f38753bcda519fb05948d5c77ac429255ff8aaf27d9f45d1586e95e2e9ba8a7cb771b8a09dd8c8fed3f933fd9b439bc9f30c475953418ef25f71a2b6496f53d94d39ce850aa0cc75d445b5f5b4f4ee4db78ab197a9a8d8a852f44529a007ac0ac23d895928d60ba538b16b0b087a7f903ed29770e215019b77eaecc360f35f7ab11b6d735978795b2c4a74e5bdea4dc6594cd67ed752a108e666729a753ab36d6c4f606f8760f507e1765be8cd744007e629020103"
@@ -518,6 +538,11 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
                t.Fatalf("Failed to generate ECDSA key: %s", err)
        }
 
+       ed25519Pub, ed25519Priv, err := ed25519.GenerateKey(random)
+       if err != nil {
+               t.Fatalf("Failed to generate Ed25519 key: %s", err)
+       }
+
        tests := []struct {
                name      string
                pub, priv interface{}
@@ -531,6 +556,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
                {"RSAPSS/RSAPSS", &testPrivateKey.PublicKey, testPrivateKey, true, SHA256WithRSAPSS},
                {"ECDSA/RSAPSS", &ecdsaPriv.PublicKey, testPrivateKey, false, SHA256WithRSAPSS},
                {"RSAPSS/ECDSA", &testPrivateKey.PublicKey, ecdsaPriv, false, ECDSAWithSHA384},
+               {"Ed25519", ed25519Pub, ed25519Priv, true, PureEd25519},
        }
 
        testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth}
@@ -1017,6 +1043,76 @@ func TestRSAPSSSelfSigned(t *testing.T) {
        }
 }
 
+const ed25519Certificate = `
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            0c:83:d8:21:2b:82:cb:23:98:23:63:e2:f7:97:8a:43:5b:f3:bd:92
+        Signature Algorithm: ED25519
+        Issuer: CN = Ed25519 test certificate
+        Validity
+            Not Before: May  6 17:27:16 2019 GMT
+            Not After : Jun  5 17:27:16 2019 GMT
+        Subject: CN = Ed25519 test certificate
+        Subject Public Key Info:
+            Public Key Algorithm: ED25519
+                ED25519 Public-Key:
+                pub:
+                    36:29:c5:6c:0d:4f:14:6c:81:d0:ff:75:d3:6a:70:
+                    5f:69:cd:0f:4d:66:d5:da:98:7e:82:49:89:a3:8a:
+                    3c:fa
+        X509v3 extensions:
+            X509v3 Subject Key Identifier:
+                09:3B:3A:9D:4A:29:D8:95:FF:68:BE:7B:43:54:72:E0:AD:A2:E3:AE
+            X509v3 Authority Key Identifier:
+                keyid:09:3B:3A:9D:4A:29:D8:95:FF:68:BE:7B:43:54:72:E0:AD:A2:E3:AE
+
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: ED25519
+         53:a5:58:1c:2c:3b:2a:9e:ac:9d:4e:a5:1d:5f:5d:6d:a6:b5:
+         08:de:12:82:f3:97:20:ae:fa:d8:98:f4:1a:83:32:6b:91:f5:
+         24:1d:c4:20:7f:2c:e2:4d:da:13:3b:6d:54:1a:d2:a8:28:dc:
+         60:b9:d4:f4:78:4b:3c:1c:91:00
+-----BEGIN CERTIFICATE-----
+MIIBWzCCAQ2gAwIBAgIUDIPYISuCyyOYI2Pi95eKQ1vzvZIwBQYDK2VwMCMxITAf
+BgNVBAMMGEVkMjU1MTkgdGVzdCBjZXJ0aWZpY2F0ZTAeFw0xOTA1MDYxNzI3MTZa
+Fw0xOTA2MDUxNzI3MTZaMCMxITAfBgNVBAMMGEVkMjU1MTkgdGVzdCBjZXJ0aWZp
+Y2F0ZTAqMAUGAytlcAMhADYpxWwNTxRsgdD/ddNqcF9pzQ9NZtXamH6CSYmjijz6
+o1MwUTAdBgNVHQ4EFgQUCTs6nUop2JX/aL57Q1Ry4K2i464wHwYDVR0jBBgwFoAU
+CTs6nUop2JX/aL57Q1Ry4K2i464wDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQBT
+pVgcLDsqnqydTqUdX11tprUI3hKC85cgrvrYmPQagzJrkfUkHcQgfyziTdoTO21U
+GtKoKNxgudT0eEs8HJEA
+-----END CERTIFICATE-----`
+
+func TestEd25519SelfSigned(t *testing.T) {
+       der, _ := pem.Decode([]byte(ed25519Certificate))
+       if der == nil {
+               t.Fatalf("Failed to find PEM block")
+       }
+
+       cert, err := ParseCertificate(der.Bytes)
+       if err != nil {
+               t.Fatalf("Failed to parse: %s", err)
+       }
+
+       if cert.PublicKeyAlgorithm != Ed25519 {
+               t.Fatalf("Parsed key algorithm was not Ed25519")
+       }
+       parsedKey, ok := cert.PublicKey.(ed25519.PublicKey)
+       if !ok {
+               t.Fatalf("Parsed key was not an Ed25519 key: %s", err)
+       }
+       if len(parsedKey) != ed25519.PublicKeySize {
+               t.Fatalf("Invalid Ed25519 key")
+       }
+
+       if err = cert.CheckSignatureFrom(cert); err != nil {
+               t.Fatalf("Signature check failed: %s", err)
+       }
+}
+
 const pemCertificate = `-----BEGIN CERTIFICATE-----
 MIIDATCCAemgAwIBAgIRAKQkkrFx1T/dgB/Go/xBM5swDQYJKoZIhvcNAQELBQAw
 EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjA4MTcyMDM2MDdaFw0xNzA4MTcyMDM2
@@ -1176,6 +1272,11 @@ func TestCreateCertificateRequest(t *testing.T) {
                t.Fatalf("Failed to generate ECDSA key: %s", err)
        }
 
+       _, ed25519Priv, err := ed25519.GenerateKey(random)
+       if err != nil {
+               t.Fatalf("Failed to generate Ed25519 key: %s", err)
+       }
+
        tests := []struct {
                name    string
                priv    interface{}
@@ -1185,6 +1286,7 @@ func TestCreateCertificateRequest(t *testing.T) {
                {"ECDSA-256", ecdsa256Priv, ECDSAWithSHA1},
                {"ECDSA-384", ecdsa384Priv, ECDSAWithSHA1},
                {"ECDSA-521", ecdsa521Priv, ECDSAWithSHA1},
+               {"Ed25519", ed25519Priv, PureEd25519},
        }
 
        for _, test := range tests {
index 6d46ee82f0791c25e1f9a9422c75744b554b33f4..c1010c1534ce71a3861a2998574ad8429a4e54fe 100644 (file)
@@ -403,7 +403,7 @@ var pkgDeps = map[string][]string{
                "container/list", "crypto/x509", "encoding/pem", "net", "syscall",
        },
        "crypto/x509": {
-               "L4", "CRYPTO-MATH", "OS", "CGO",
+               "L4", "CRYPTO-MATH", "OS", "CGO", "crypto/ed25519",
                "crypto/x509/pkix", "encoding/pem", "encoding/hex", "net", "os/user", "syscall", "net/url",
                "golang.org/x/crypto/cryptobyte", "golang.org/x/crypto/cryptobyte/asn1",
        },