Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for generating ed25519 keys and certs #1061

Merged
merged 39 commits into from
Jun 12, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
e340753
add support for generating ed25519 keys and certs
izolight Nov 23, 2019
c639e67
add 5min ca testfiles
izolight Nov 24, 2019
e4cf3d8
only build for go1.13 as the ed25519 package is now part of std library
izolight Nov 24, 2019
bb83c8c
remove dependency on golang.org/x/crypto/ed25519
izolight Nov 24, 2019
9a17a34
add support for generating ed25519 keys and certs
izolight Nov 23, 2019
cf7fb43
add 5min ca testfiles
izolight Nov 24, 2019
79fa3b0
only build for go1.13 as the ed25519 package is now part of std library
izolight Nov 24, 2019
221d4ec
remove dependency on golang.org/x/crypto/ed25519
izolight Nov 24, 2019
626680c
Change string domain and format
claucece Apr 21, 2020
1d9e618
typo
izolight Apr 23, 2020
751bf89
compare key and cert
izolight Apr 23, 2020
6624bf1
update comment and error to indicate support for ed25519
izolight Apr 23, 2020
b43f9a2
update comments
izolight Apr 23, 2020
7f6b294
Change to use circl ed25519. This will fail due to issue cloudflare/c…
claucece Apr 30, 2020
b8f9375
Use circl only for signing
claucece May 1, 2020
f1bf794
Update vendor
claucece May 1, 2020
259b11c
Update vendor to mod
claucece May 1, 2020
35d039a
Remove go 1.12
claucece May 1, 2020
887b6fc
Only use c25519 for generation
claucece May 6, 2020
7dc5eca
Fix style
claucece May 6, 2020
4685a06
Use circl library for all ed25519 key generation
claucece May 12, 2020
b4a68f6
Run the tests with expired data
claucece May 13, 2020
7aff46b
Consistent naming
claucece May 14, 2020
2725cfc
Not using pointers
claucece May 16, 2020
ad00b62
Fix 5min files
claucece May 16, 2020
7d2779f
Use 25519 from stdlib
claucece May 23, 2020
acff4d8
This package is vendored
claucece May 23, 2020
21fb139
Re add error line and mod tidy
claucece Jul 22, 2020
bca3df9
Merge branch 'claucece-ed25519cert' into ed25519
izolight May 26, 2023
c1cfc4c
Merge remote-tracking branch 'origin' into ed25519
izolight May 26, 2023
58fc165
remove unneeded todo as per https://github.com/cloudflare/cfssl/pull/…
izolight May 26, 2023
fcceb3d
ignore key size for ed25519 as all keys are 256bit
izolight May 26, 2023
dfcdcd0
remove duplicate case from merge
izolight May 26, 2023
27e825e
add Ed25519 to error for supported keys
izolight May 26, 2023
ed80075
Update errors/error.go
izolight Jun 2, 2023
2b14670
remove duplicate switch case
izolight Jun 2, 2023
6fdd6b4
correctly remove size param from ed25519 instead of ecdsa
izolight Jun 2, 2023
0c7066a
remove unneeded test for ed25519 size
izolight Jun 2, 2023
ecc0574
print config instead of error if test fails
izolight Jun 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
izolight marked this conversation as resolved.
Show resolved Hide resolved
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})
izolight marked this conversation as resolved.
Show resolved Hide resolved
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)
}
izolight marked this conversation as resolved.
Show resolved Hide resolved
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 {
izolight marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -204,6 +218,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",
izolight marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -43,6 +44,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 @@ -277,6 +282,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 @@ -323,6 +345,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 @@ -370,6 +400,10 @@ func TestDefaultKeyRequest(t *testing.T) {
if DefaultKeyRequest.Algo() != "ecdsa" {
t.Fatal("Invalid default key request.")
}
case "PRIVATE KEY":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the type should change as well ;)

if DefaultKeyRequest.Algo() != "ed25519" {
t.Fatal("Invalid default key request.")
}
}
}

Expand All @@ -396,6 +430,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
izolight marked this conversation as resolved.
Show resolved Hide resolved
}

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 @@ -452,6 +459,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 @@ -102,10 +103,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 @@ -219,6 +229,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 @@ -261,6 +274,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:
izolight marked this conversation as resolved.
Show resolved Hide resolved
if _, ok := priv.Public().(ed25519.PublicKey); !ok {
return nil, cferr.New(cferr.PrivateKeyError, cferr.KeyMismatch)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not compare here as well the Public with the ca.Public?

case ca.PublicKeyAlgorithm == x509.ECDSA:
var ecdsaPublicKey *ecdsa.PublicKey
var ok bool
Expand Down
Loading