Skip to content

Commit

Permalink
add support for generating ed25519 keys and certs
Browse files Browse the repository at this point in the history
  • Loading branch information
izolight authored and claucece committed Apr 16, 2020
1 parent 6b49bea commit 9a17a34
Show file tree
Hide file tree
Showing 16 changed files with 244 additions and 3 deletions.
7 changes: 7 additions & 0 deletions bundler/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package bundler
import (
"bytes"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"github.com/cloudflare/cfssl/helpers/derhelpers"
"time"

"github.com/cloudflare/cfssl/helpers"
Expand Down Expand Up @@ -104,6 +106,8 @@ func (b *Bundle) MarshalJSON() ([]byte, error) {
switch b.Cert.PublicKeyAlgorithm {
case x509.ECDSA:
keyType = fmt.Sprintf("%d-bit ECDSA", keyLength)
case x509.Ed25519:
keyType = "ED25519"
case x509.RSA:
keyType = fmt.Sprintf("%d-bit RSA", keyLength)
case x509.DSA:
Expand All @@ -119,6 +123,9 @@ func (b *Bundle) MarshalJSON() ([]byte, error) {
case *ecdsa.PrivateKey:
keyBytes, _ = x509.MarshalECPrivateKey(key)
keyString = PemBlockToString(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes})
case ed25519.PrivateKey:
keyBytes, _ = derhelpers.MarshalEd25519PrivateKey(key)
keyString = PemBlockToString(&pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes})
case fmt.Stringer:
keyString = key.String()
}
Expand Down
6 changes: 6 additions & 0 deletions bundler/bundler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
Expand Down Expand Up @@ -581,6 +582,10 @@ func (b *Bundler) Bundle(certs []*x509.Certificate, key crypto.Signer, flavor Bu
if cert.PublicKey.(*rsa.PublicKey).N.Cmp(rsaPublicKey.N) != 0 {
return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
}
case cert.PublicKeyAlgorithm == x509.Ed25519:
if _, ok := key.Public().(ed25519.PublicKey); !ok {
return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
}
case cert.PublicKeyAlgorithm == x509.ECDSA:
var ecdsaPublicKey *ecdsa.PublicKey
if ecdsaPublicKey, ok = key.Public().(*ecdsa.PublicKey); !ok {
Expand All @@ -596,6 +601,7 @@ func (b *Bundler) Bundle(certs []*x509.Certificate, key crypto.Signer, flavor Bu
switch {
case cert.PublicKeyAlgorithm == x509.RSA:
case cert.PublicKeyAlgorithm == x509.ECDSA:
case cert.PublicKeyAlgorithm == x509.Ed25519:
default:
return nil, errors.New(errors.PrivateKeyError, errors.NotRSAOrECC)
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/multirootca/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"errors"
"flag"
Expand All @@ -24,7 +25,7 @@ import (
func parseSigner(root *config.Root) (signer.Signer, error) {
privateKey := root.PrivateKey
switch priv := privateKey.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey:
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
s, err := local.NewSigner(priv, root.Certificate, signer.DefaultSigAlgo(priv), nil)
if err != nil {
return nil, err
Expand Down
24 changes: 24 additions & 0 deletions csr/csr.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package csr
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
Expand All @@ -12,6 +13,8 @@ import (
"encoding/asn1"
"encoding/pem"
"errors"
"github.com/cloudflare/cfssl/helpers/derhelpers"
"io"
"net"
"net/mail"
"net/url"
Expand Down Expand Up @@ -72,6 +75,15 @@ func (kr *KeyRequest) Generate() (crypto.PrivateKey, error) {
return nil, errors.New("RSA key size too large")
}
return rsa.GenerateKey(rand.Reader, kr.Size())
case "ed25519":
if kr.Size() != 256 && kr.Size() != 0 {
return nil, errors.New("ED25519 keys are always 256Bit")
}
seed := make([]byte, ed25519.SeedSize)
if _, err := io.ReadFull(rand.Reader, seed); err != nil {
return nil, err
}
return ed25519.NewKeyFromSeed(seed), nil
case "ecdsa":
var curve elliptic.Curve
switch kr.Size() {
Expand Down Expand Up @@ -105,6 +117,8 @@ func (kr *KeyRequest) SigAlgo() x509.SignatureAlgorithm {
default:
return x509.SHA1WithRSA
}
case "ed25519":
return x509.PureEd25519
case "ecdsa":
switch kr.Size() {
case curveP521:
Expand Down Expand Up @@ -205,6 +219,16 @@ func ParseRequest(req *CertificateRequest) (csr, key []byte, err error) {
Bytes: key,
}
key = pem.EncodeToMemory(&block)
case ed25519.PrivateKey:
key, err = derhelpers.MarshalEd25519PrivateKey(priv)
if err != nil {

}
block := pem.Block{
Type: "PRIVATE KEY",
Bytes: key,
}
key = pem.EncodeToMemory(&block)
case *ecdsa.PrivateKey:
key, err = x509.MarshalECPrivateKey(priv)
if err != nil {
Expand Down
57 changes: 57 additions & 0 deletions csr/csr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package csr
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
Expand Down Expand Up @@ -44,6 +45,10 @@ func TestKeyRequest(t *testing.T) {
if kr.Algo() != "ecdsa" {
t.Fatal("ECDSA key generated, but expected", kr.Algo())
}
case ed25519.PrivateKey:
if kr.Algo() != "ed25519" {
t.Fatal("ED25519 key generated, but expected", kr.Algo())
}
}
}

Expand Down Expand Up @@ -310,6 +315,23 @@ func TestECGeneration(t *testing.T) {
}
}

func TestED25519Generation(t *testing.T) {
for _, sz := range []int{256, 0} {
kr := &KeyRequest{"ed25519", sz}
priv, err := kr.Generate()
if err != nil {
t.Fatalf("%v", err)
}
_, ok := priv.(ed25519.PrivateKey)
if !ok {
t.Fatal("Expected ed25519 key")
}
if sa := kr.SigAlgo(); sa == x509.UnknownSignatureAlgorithm {
t.Fatal("Invalid signature algorithm!")
}
}
}

func TestRSAKeyGeneration(t *testing.T) {
var rsakey *rsa.PrivateKey

Expand Down Expand Up @@ -356,6 +378,14 @@ func TestBadKeyRequest(t *testing.T) {
t.Fatal("The wrong signature algorithm was returned from SigAlgo!")
}

kr.A = "ed25519"
if _, err := kr.Generate(); err == nil {
t.Fatal("Key generation should fail with invalid key size")
} else if sa := kr.SigAlgo(); sa != x509.PureEd25519 {
t.Fatal("The wrong signature algorithm was returned from SigAlgo!")
}


kr = &KeyRequest{"tobig", 9216}

kr.A = "rsa"
Expand Down Expand Up @@ -403,6 +433,10 @@ func TestDefaultKeyRequest(t *testing.T) {
if DefaultKeyRequest.Algo() != "ecdsa" {
t.Fatal("Invalid default key request.")
}
case "PRIVATE KEY":
if DefaultKeyRequest.Algo() != "ed25519" {
t.Fatal("Invalid default key request.")
}
}
}

Expand All @@ -429,6 +463,29 @@ func TestRSACertRequest(t *testing.T) {
}
}

// TestED25519CertRequest validates parsing a certificate request with an
// ED25519 key.
func TestED25519CertRequest(t *testing.T) {
var req = &CertificateRequest{
Names: []Name{
{
C: "US",
ST: "California",
L: "San Francisco",
O: "CloudFlare",
OU: "Systems Engineering",
},
},
CN: "cloudflare.com",
Hosts: []string{"cloudflare.com", "www.cloudflare.com", "jdoe@example.com", "https://www.cloudflare.com"},
KeyRequest: &KeyRequest{"ed25519", 256},
}
_, _, err := ParseRequest(req)
if err != nil {
t.Fatalf("%v", err)
}
}

// TestBadCertRequest checks for failure conditions of ParseRequest.
func TestBadCertRequest(t *testing.T) {
var req = &CertificateRequest{
Expand Down
9 changes: 9 additions & 0 deletions helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rsa"
"crypto/tls"
Expand Down Expand Up @@ -62,6 +63,8 @@ func KeyLength(key interface{}) int {
return ecdsaKey.Curve.Params().BitSize
} else if rsaKey, ok := key.(*rsa.PublicKey); ok {
return rsaKey.N.BitLen()
} else if _, ok := key.(ed25519.PublicKey); ok {
return ed25519.PublicKeySize
}

return 0
Expand Down Expand Up @@ -146,6 +149,8 @@ func SignatureString(alg x509.SignatureAlgorithm) string {
return "ECDSAWithSHA384"
case x509.ECDSAWithSHA512:
return "ECDSAWithSHA512"
case x509.PureEd25519:
return "ED25519"
default:
return "Unknown Signature"
}
Expand Down Expand Up @@ -179,6 +184,8 @@ func HashAlgoString(alg x509.SignatureAlgorithm) string {
return "SHA384"
case x509.ECDSAWithSHA512:
return "SHA512"
case x509.PureEd25519:
return "ED25519"
default:
return "Unknown Hash Algorithm"
}
Expand Down Expand Up @@ -460,6 +467,8 @@ func SignerAlgo(priv crypto.Signer) x509.SignatureAlgorithm {
default:
return x509.SHA1WithRSA
}
case ed25519.PublicKey:
return x509.PureEd25519
case *ecdsa.PublicKey:
switch pub.Curve {
case elliptic.P521():
Expand Down
18 changes: 17 additions & 1 deletion helpers/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package helpers
import (
"bytes"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
Expand Down Expand Up @@ -103,10 +104,19 @@ func TestKeyLength(t *testing.T) {
rsaIn, _ := rsaPriv.Public().(*rsa.PublicKey)
expRsa := rsaIn.N.BitLen()
outRsa := KeyLength(rsaIn)

if expRsa != outRsa {
t.Fatal("KeyLength malfunctioning on rsa input")
}

//test the ed25519 branch
_, ed25519priv, _ := ed25519.GenerateKey(rand.Reader)
ed25519In, _ := ed25519priv.Public().(ed25519.PublicKey)
expEd25519 := len(ed25519In)
outEd25519 := KeyLength(ed25519In)
if expEd25519 != outEd25519 {
t.Fatal("KeyLength malfunctioning on ed25519 input")
}

}

func TestExpiryTime(t *testing.T) {
Expand Down Expand Up @@ -220,6 +230,9 @@ func TestHashAlgoString(t *testing.T) {
if HashAlgoString(x509.ECDSAWithSHA512) != "SHA512" {
t.Fatal("standin")
}
if HashAlgoString(x509.PureEd25519) != "ED25519" {
t.Fatal("standin")
}
if HashAlgoString(math.MaxInt32) != "Unknown Hash Algorithm" {
t.Fatal("standin")
}
Expand Down Expand Up @@ -262,6 +275,9 @@ func TestSignatureString(t *testing.T) {
if SignatureString(x509.ECDSAWithSHA512) != "ECDSAWithSHA512" {
t.Fatal("Signature String functioning improperly")
}
if SignatureString(x509.PureEd25519) != "ED25519" {
t.Fatal("Signature String functioning improperly")
}
if SignatureString(math.MaxInt32) != "Unknown Signature" {
t.Fatal("Signature String functioning improperly")
}
Expand Down
5 changes: 5 additions & 0 deletions initca/initca.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package initca
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
Expand Down Expand Up @@ -197,6 +198,10 @@ func RenewFromSigner(ca *x509.Certificate, priv crypto.Signer) ([]byte, error) {
if ca.PublicKey.(*rsa.PublicKey).N.Cmp(rsaPublicKey.N) != 0 {
return nil, cferr.New(cferr.PrivateKeyError, cferr.KeyMismatch)
}
case ca.PublicKeyAlgorithm == x509.Ed25519:
if _, ok := priv.Public().(ed25519.PublicKey); !ok {
return nil, cferr.New(cferr.PrivateKeyError, cferr.KeyMismatch)
}
case ca.PublicKeyAlgorithm == x509.ECDSA:
var ecdsaPublicKey *ecdsa.PublicKey
var ok bool
Expand Down
Loading

0 comments on commit 9a17a34

Please sign in to comment.