Skip to content

Commit

Permalink
Add support for ed25519 (#11780)
Browse files Browse the repository at this point in the history
* update azure instructions

Update instructions in regards to azure AD Authentication and OIDC

* Initial pass of ed25519

* Fix typos on marshal function

* test wip

* typo

* fix tests

* missef changelog

* fix mismatch between signature and algo

* added test coverage for ed25519

* remove pkcs1 since does not exist for ed25519

* add ed25519 support to getsigner

* pull request feedback

Signed-off-by: Anner J. Bonilla <abonilla@hoyosintegrity.com>

* typo on key

Signed-off-by: Anner J. Bonilla <abonilla@hoyosintegrity.com>

* cast mistake

Signed-off-by: Anner J. Bonilla <abonilla@hoyosintegrity.com>

Co-authored-by: Jim Kalafut <jkalafut@hashicorp.com>
  • Loading branch information
annerajb and Jim Kalafut committed Oct 5, 2021
1 parent 0a022d1 commit 30fd91c
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 24 deletions.
53 changes: 52 additions & 1 deletion builtin/logical/pki/backend_test.go
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
Expand Down Expand Up @@ -211,6 +212,8 @@ func TestBackend_Roles(t *testing.T) {
{"RSACSR", &rsaCAKey, &rsaCACert, true},
{"EC", &ecCAKey, &ecCACert, false},
{"ECCSR", &ecCAKey, &ecCACert, true},
{"ED", &edCAKey, &edCACert, false},
{"EDCSR", &edCAKey, &edCACert, true},
}

for _, tc := range cases {
Expand Down Expand Up @@ -309,6 +312,13 @@ func checkCertsAndPrivateKey(keyType string, key crypto.Signer, usage x509.KeyUs
if err != nil {
return nil, fmt.Errorf("error parsing EC key: %s", err)
}
case "ed25519":
parsedCertBundle.PrivateKeyType = certutil.Ed25519PrivateKey
parsedCertBundle.PrivateKey = key
parsedCertBundle.PrivateKeyBytes, err = x509.MarshalPKCS8PrivateKey(key.(ed25519.PrivateKey))
if err != nil {
return nil, fmt.Errorf("error parsing Ed25519 key: %s", err)
}
}
}

Expand All @@ -324,6 +334,8 @@ func checkCertsAndPrivateKey(keyType string, key crypto.Signer, usage x509.KeyUs
}

switch {
case parsedCertBundle.PrivateKeyType == certutil.Ed25519PrivateKey && keyType != "ed25519":
fallthrough
case parsedCertBundle.PrivateKeyType == certutil.RSAPrivateKey && keyType != "rsa":
fallthrough
case parsedCertBundle.PrivateKeyType == certutil.ECPrivateKey && keyType != "ec":
Expand Down Expand Up @@ -707,7 +719,7 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep {

generatedRSAKeys := map[int]crypto.Signer{}
generatedECKeys := map[int]crypto.Signer{}

generatedEdKeys := map[int]crypto.Signer{}
/*
// For the number of tests being run, a seed of 1 has been tested
// to hit all of the various values below. However, for normal
Expand Down Expand Up @@ -1017,6 +1029,13 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep {
generatedECKeys[keyBits] = privKey
}

case "ed25519":
privKey, ok = generatedEdKeys[keyBits]
if !ok {
_, privKey, _ = ed25519.GenerateKey(rand.Reader)
generatedEdKeys[keyBits] = privKey
}

default:
panic("invalid key type: " + keyType)
}
Expand Down Expand Up @@ -3095,6 +3114,36 @@ func setCerts() {
Bytes: caBytes,
}
rsaCACert = strings.TrimSpace(string(pem.EncodeToMemory(caCertPEMBlock)))

_, edk, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
marshaledKey, err = x509.MarshalPKCS8PrivateKey(edk)
if err != nil {
panic(err)
}
keyPEMBlock = &pem.Block{
Type: "PRIVATE KEY",
Bytes: marshaledKey,
}
edCAKey = strings.TrimSpace(string(pem.EncodeToMemory(keyPEMBlock)))
if err != nil {
panic(err)
}
subjKeyID, err = certutil.GetSubjKeyID(edk)
if err != nil {
panic(err)
}
caBytes, err = x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, edk.Public(), edk)
if err != nil {
panic(err)
}
caCertPEMBlock = &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
}
edCACert = strings.TrimSpace(string(pem.EncodeToMemory(caCertPEMBlock)))
}

func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) {
Expand Down Expand Up @@ -3281,4 +3330,6 @@ var (
rsaCACert string
ecCAKey string
ecCACert string
edCAKey string
edCACert string
)
66 changes: 64 additions & 2 deletions builtin/logical/pki/ca_test.go
Expand Up @@ -3,6 +3,7 @@ package pki
import (
"context"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
Expand Down Expand Up @@ -49,7 +50,7 @@ func TestBackend_CA_Steps(t *testing.T) {
client := cluster.Cores[0].Client

// Set RSA/EC CA certificates
var rsaCAKey, rsaCACert, ecCAKey, ecCACert string
var rsaCAKey, rsaCACert, ecCAKey, ecCACert, edCAKey, edCACert string
{
cak, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
Expand Down Expand Up @@ -119,10 +120,40 @@ func TestBackend_CA_Steps(t *testing.T) {
Bytes: caBytes,
}
rsaCACert = strings.TrimSpace(string(pem.EncodeToMemory(caCertPEMBlock)))

_, edk, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
marshaledKey, err = x509.MarshalPKCS8PrivateKey(edk)
if err != nil {
panic(err)
}
keyPEMBlock = &pem.Block{
Type: "PRIVATE KEY",
Bytes: marshaledKey,
}
edCAKey = strings.TrimSpace(string(pem.EncodeToMemory(keyPEMBlock)))
if err != nil {
panic(err)
}
_, err = certutil.GetSubjKeyID(edk)
if err != nil {
panic(err)
}
caBytes, err = x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, edk.Public(), edk)
if err != nil {
panic(err)
}
caCertPEMBlock = &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
}
edCACert = strings.TrimSpace(string(pem.EncodeToMemory(caCertPEMBlock)))
}

// Setup backends
var rsaRoot, rsaInt, ecRoot, ecInt *backend
var rsaRoot, rsaInt, ecRoot, ecInt, edRoot, edInt *backend
{
if err := client.Sys().Mount("rsaroot", &api.MountInput{
Type: "pki",
Expand Down Expand Up @@ -167,6 +198,28 @@ func TestBackend_CA_Steps(t *testing.T) {
t.Fatal(err)
}
ecInt = b

if err := client.Sys().Mount("ed25519root", &api.MountInput{
Type: "pki",
Config: api.MountConfigInput{
DefaultLeaseTTL: "16h",
MaxLeaseTTL: "60h",
},
}); err != nil {
t.Fatal(err)
}
edRoot = b

if err := client.Sys().Mount("ed25519int", &api.MountInput{
Type: "pki",
Config: api.MountConfigInput{
DefaultLeaseTTL: "16h",
MaxLeaseTTL: "60h",
},
}); err != nil {
t.Fatal(err)
}
edInt = b
}

t.Run("teststeps", func(t *testing.T) {
Expand All @@ -188,6 +241,15 @@ func TestBackend_CA_Steps(t *testing.T) {
subClient.SetToken(client.Token())
runSteps(t, ecRoot, ecInt, subClient, "ecroot/", "ecint/", ecCACert, ecCAKey)
})
t.Run("ed25519", func(t *testing.T) {
t.Parallel()
subClient, err := client.Clone()
if err != nil {
t.Fatal(err)
}
subClient.SetToken(client.Token())
runSteps(t, edRoot, edInt, subClient, "ed25519root/", "ed25519int/", edCACert, edCAKey)
})
})
}

Expand Down
13 changes: 13 additions & 0 deletions builtin/logical/pki/cert_util.go
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
Expand Down Expand Up @@ -597,6 +598,18 @@ func signCert(b *backend,
pubKey.Params().BitSize)}
}

case "ed25519":
// Verify that the key matches the role type
if csr.PublicKeyAlgorithm != x509.PublicKeyAlgorithm(x509.Ed25519) {
return nil, errutil.UserError{Err: fmt.Sprintf(
"role requires keys of type %s",
data.role.KeyType)}
}
_, ok := csr.PublicKey.(ed25519.PublicKey)
if !ok {
return nil, errutil.UserError{Err: "could not parse CSR's public key"}
}

case "any":
// We only care about running RSA < 2048 bit checks, so if not RSA
// break out
Expand Down
4 changes: 2 additions & 2 deletions builtin/logical/pki/fields.go
Expand Up @@ -270,8 +270,8 @@ Set to 384 for SHA384 and 512 for SHA512.
Type: framework.TypeString,
Default: "rsa",
Description: `The type of key to use; defaults to RSA. "rsa"
and "ec" are the only valid values.`,
AllowedValues: []interface{}{"rsa", "ec"},
"ec" and "ed25519" are the only valid values.`,
AllowedValues: []interface{}{"rsa", "ec", "ed25519"},
DisplayAttrs: &framework.DisplayAttributes{
Value: "rsa",
},
Expand Down
4 changes: 2 additions & 2 deletions builtin/logical/pki/path_roles.go
Expand Up @@ -193,8 +193,8 @@ protection use. Defaults to false.`,
Type: framework.TypeString,
Default: "rsa",
Description: `The type of key to use; defaults to RSA. "rsa"
and "ec" are the only valid values.`,
AllowedValues: []interface{}{"rsa", "ec"},
"ec" and "ed25519" are the only valid values.`,
AllowedValues: []interface{}{"rsa", "ec", "ed25519"},
},

"key_bits": {
Expand Down
3 changes: 3 additions & 0 deletions changelog/11780.txt
@@ -0,0 +1,3 @@
```release-note:feature
pki: Support ed25519 as a key for the pki backend
```

0 comments on commit 30fd91c

Please sign in to comment.