import (
"crypto/ecdsa"
+ "crypto/ed25519"
"crypto/rsa"
"crypto/x509/pkix"
"encoding/asn1"
// 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 {
}
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
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)
}
"crypto"
"crypto/dsa"
"crypto/ecdsa"
+ "crypto/ed25519"
"crypto/elliptic"
"crypto/rsa"
_ "crypto/sha1"
// 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 {
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")
}
SHA256WithRSAPSS
SHA384WithRSAPSS
SHA512WithRSAPSS
+ PureEd25519
)
func (algo SignatureAlgorithm) isRSAPSS() bool {
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 {
//
// 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}
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}
{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
}
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) {
// 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 {
return DSA
case oid.Equal(oidPublicKeyECDSA):
return ECDSA
+ case oid.Equal(oidPublicKeyEd25519):
+ return Ed25519
}
return UnknownPublicKeyAlgorithm
}
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 {
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
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
}
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
}
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 {
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
}
//
// 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
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,
}
var signature []byte
- signature, err = key.Sign(rand, digest, signerOpts)
+ signature, err = key.Sign(rand, signed, signerOpts)
if err != nil {
return
}
//
// 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) {
}
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
}
"bytes"
"crypto/dsa"
"crypto/ecdsa"
+ "crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
}
}
-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-----
-----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"
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{}
{"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}
}
}
+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
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{}
{"ECDSA-256", ecdsa256Priv, ECDSAWithSHA1},
{"ECDSA-384", ecdsa384Priv, ECDSAWithSHA1},
{"ECDSA-521", ecdsa521Priv, ECDSAWithSHA1},
+ {"Ed25519", ed25519Priv, PureEd25519},
}
for _, test := range tests {