From 207c1ffa4ee0cf06f33f500a1728272b8812a930 Mon Sep 17 00:00:00 2001 From: Tero Saarni Date: Fri, 18 Oct 2024 20:44:36 +0300 Subject: [PATCH] Added support for generating Ed25519 certs (#66) --- README.md | 4 ++-- certificate.go | 11 ++++++++++- certificate_test.go | 4 ++++ internal/manifest/manifest.go | 2 ++ internal/manifest/manifest_test.go | 10 ++++++++++ internal/manifest/testdata/certs-test-all-fields.yaml | 3 +++ 6 files changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 20b2949..a10fcf4 100644 --- a/README.md +++ b/README.md @@ -154,8 +154,8 @@ Writing state: certs.state | --- | ----------- | -------- | | subject | Distinguished name for the certificate. `subject` is the only mandatory field and it must be unique. | `CN=Joe` | | sans | List of values for x509 Subject Alternative Name extension. | `DNS:www.example.com`, `IP:1.2.3.4`, `URI:https://www.example.com` | -| key_type | Certificate key algorithm. Default value is `EC` (elliptic curve). | `EC` or `RSA` | -| key_size | The key length in bits. Default value is 256 if `key_size` is not defined. | For key_type EC: `256`, `384`, `521`. For key_type RSA: `1024`, `2048`, `4096` | +| key_type | Certificate key algorithm. Default value is `EC` (elliptic curve). | `EC`, `RSA` or `ED25519` | +| key_size | The key length in bits. Default value is 256 if `key_size` is not defined. | For key_type EC: `256`, `384`, `521`. For key_type RSA: `1024`, `2048`, `4096`. For key_type ED25519: `256`. | | expires | Certificate NotAfter field is calculated by adding duration defined in `expires` to current time. Default value is 8760h (one year) if `expires` is not defined. `not_after` takes precedence over `expires`. | `1s`, `10m`, `1h` | | key_usages | List of values for x509 key usage extension. If `key_usages` is not defined, `CertSign` and `CRLSign` are set for CA certificates, `KeyEncipherment` and `DigitalSignature` are set for end-entity certificates. | `DigitalSignature`, `ContentCommitment`, `KeyEncipherment`, `DataEncipherment`, `KeyAgreement`, `CertSign`, `CRLSign`, `EncipherOnly`, `DecipherOnly` | | ext_key_usages | List of values for x509 extended key usage extension. Not set by default. | `Any`, `ServerAuth`, `ClientAuth`, `CodeSigning`, `EmailProtection`, `IPSECEndSystem`, `IPSECTunnel`, `IPSECUser`. `TimeStamping`, `OCSPSigning`, `MicrosoftServerGatedCrypto`, `NetscapeServerGatedCrypto`, `MicrosoftCommercialCodeSigning`, `MicrosoftKernelCodeSigning` | diff --git a/certificate.go b/certificate.go index 57d3e3d..03b1863 100644 --- a/certificate.go +++ b/certificate.go @@ -18,6 +18,7 @@ import ( "bytes" "crypto" "crypto/ecdsa" + "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" @@ -54,7 +55,7 @@ type Certificate struct { // KeySize defines the key length in bits. // Default value is 256 (EC) or 2048 (RSA) if KeySize is undefined (when value is 0). - // Examples: For key_type EC: 256, 384, 521. For key_type RSA: 1024, 2048, 4096. + // Examples: For key_type EC: 256, 384, 521. For key_type RSA: 1024, 2048, 4096. For key_type ED25519: 256. KeySize int `json:"key_size"` // Expires automatically defines certificate's NotAfter field by adding duration defined in Expires to the current time. @@ -106,6 +107,7 @@ type KeyType uint const ( KeyTypeEC = iota KeyTypeRSA + KeyTypeEd25519 ) // TLSCertificate returns the Certificate as tls.Certificate. @@ -221,6 +223,8 @@ func (c *Certificate) defaults() error { c.KeySize = 256 } else if c.KeyType == KeyTypeRSA { c.KeySize = 2048 + } else if c.KeyType == KeyTypeEd25519 { + c.KeySize = 256 } } @@ -300,6 +304,11 @@ func (c *Certificate) Generate() error { key, err = ecdsa.GenerateKey(curve, rand.Reader) } else if c.KeyType == KeyTypeRSA { key, err = rsa.GenerateKey(rand.Reader, c.KeySize) + } else if c.KeyType == KeyTypeEd25519 { + if c.KeySize != 256 { + return fmt.Errorf("invalid Ed25519 key size: %d (valid: 256)", c.KeySize) + } + _, key, err = ed25519.GenerateKey(rand.Reader) } if err != nil { return err diff --git a/certificate_test.go b/certificate_test.go index 3d5eac8..88cda8e 100644 --- a/certificate_test.go +++ b/certificate_test.go @@ -208,6 +208,10 @@ func TestInvalidKeySize(t *testing.T) { input = Certificate{Subject: "CN=Joe", KeyType: KeyTypeRSA, KeySize: 1} _, err = input.X509Certificate() assert.NotNil(t, err) + + input = Certificate{Subject: "CN=Joe", KeyType: KeyTypeEd25519, KeySize: 1} + _, err = input.X509Certificate() + assert.NotNil(t, err) } func TestPEM(t *testing.T) { diff --git a/internal/manifest/manifest.go b/internal/manifest/manifest.go index 7f3bc61..8c5a1a5 100644 --- a/internal/manifest/manifest.go +++ b/internal/manifest/manifest.go @@ -219,6 +219,8 @@ func (m *Manifest) processCertificate(c *CertificateManifest) error { c.KeyType = api.KeyTypeEC case "RSA": c.KeyType = api.KeyTypeRSA + case "ED25519": + c.KeyType = api.KeyTypeEd25519 default: return fmt.Errorf("key_type contains invalid value: %s", c.KeyTypeAsString) } diff --git a/internal/manifest/manifest_test.go b/internal/manifest/manifest_test.go index 99b71d1..30d5d9c 100644 --- a/internal/manifest/manifest_test.go +++ b/internal/manifest/manifest_test.go @@ -239,6 +239,16 @@ func TestParsingAllCertificateFields(t *testing.T) { assert.Empty(t, got.IPAddresses) assert.Equal(t, big.NewInt(123), got.SerialNumber) + + // Check fields Ee25519 end-entity cert. + tlsCert, err = tls.LoadX509KeyPair(path.Join(dir, "ed25519-cert.pem"), path.Join(dir, "ed25519-cert-key.pem")) + assert.Nil(t, err) + got, err = x509.ParseCertificate(tlsCert.Certificate[0]) + assert.Nil(t, err) + + assert.Equal(t, "ed25519-cert", got.Issuer.CommonName) + assert.Equal(t, "ed25519-cert", got.Subject.CommonName) + assert.Equal(t, x509.Ed25519, got.PublicKeyAlgorithm) } func TestRevocation(t *testing.T) { diff --git a/internal/manifest/testdata/certs-test-all-fields.yaml b/internal/manifest/testdata/certs-test-all-fields.yaml index 031a89d..3a339de 100644 --- a/internal/manifest/testdata/certs-test-all-fields.yaml +++ b/internal/manifest/testdata/certs-test-all-fields.yaml @@ -42,3 +42,6 @@ key_size: 256 not_before: 2020-01-01T09:00:00Z expires: 1h serial: 123 +--- +subject: cn=ed25519-cert +key_type: ED25519