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

Address a panic when exporting RSA public keys in transit #24054

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 21 additions & 7 deletions builtin/logical/transit/path_export.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package transit

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/x509"
Expand Down Expand Up @@ -296,18 +297,31 @@ func encodeRSAPublicKey(key *keysutil.KeyEntry) (string, error) {
return "", errors.New("nil KeyEntry provided")
}

blockType := "RSA PUBLIC KEY"
derBytes, err := x509.MarshalPKIXPublicKey(key.RSAPublicKey)
if err != nil {
return "", err
var publicKey crypto.PublicKey
publicKey = key.RSAPublicKey
if key.RSAKey != nil {
// Prefer the private key if it exists
publicKey = key.RSAKey.Public()
}

pemBlock := pem.Block{
Type: blockType,
if publicKey == nil {
return "", errors.New("requested to encode an RSA public key with no RSA key present")
}

// Encode the RSA public key in PEM format to return over the API
derBytes, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return "", fmt.Errorf("error marshaling RSA public key: %w", err)
}
pemBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: derBytes,
}
pemBytes := pem.EncodeToMemory(pemBlock)
if pemBytes == nil || len(pemBytes) == 0 {
return "", fmt.Errorf("failed to PEM-encode RSA public key")
}

pemBytes := pem.EncodeToMemory(&pemBlock)
return string(pemBytes), nil
}

Expand Down
109 changes: 108 additions & 1 deletion builtin/logical/transit/path_export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,58 @@ import (
"github.com/stretchr/testify/require"
)

func TestTransit_Export_Unknown_ExportType(t *testing.T) {
t.Parallel()

b, storage := createBackendWithSysView(t)
keyType := "ed25519"
req := &logical.Request{
Storage: storage,
Operation: logical.UpdateOperation,
Path: "keys/foo",
Data: map[string]interface{}{
"exportable": true,
"type": keyType,
},
}
_, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatalf("failed creating key %s: %v", keyType, err)
}

req = &logical.Request{
Storage: storage,
Operation: logical.ReadOperation,
Path: "export/bad-export-type/foo",
}
rsp, err := b.HandleRequest(context.Background(), req)
if err == nil {
t.Fatalf("did not error on bad export type got: %v", rsp)
}
if rsp == nil || !rsp.IsError() {
t.Fatalf("response did not contain an error on bad export type got: %v", rsp)
}
if !strings.Contains(rsp.Error().Error(), "invalid export type") {
t.Fatalf("failed with unexpected error: %v", err)
}
}

func TestTransit_Export_KeyVersion_ExportsCorrectVersion(t *testing.T) {
t.Parallel()

verifyExportsCorrectVersion(t, "encryption-key", "aes128-gcm96")
verifyExportsCorrectVersion(t, "encryption-key", "aes256-gcm96")
verifyExportsCorrectVersion(t, "encryption-key", "chacha20-poly1305")
verifyExportsCorrectVersion(t, "encryption-key", "rsa-2048")
verifyExportsCorrectVersion(t, "encryption-key", "rsa-3072")
verifyExportsCorrectVersion(t, "encryption-key", "rsa-4096")
verifyExportsCorrectVersion(t, "signing-key", "ecdsa-p256")
verifyExportsCorrectVersion(t, "signing-key", "ecdsa-p384")
verifyExportsCorrectVersion(t, "signing-key", "ecdsa-p521")
verifyExportsCorrectVersion(t, "signing-key", "ed25519")
verifyExportsCorrectVersion(t, "signing-key", "rsa-2048")
verifyExportsCorrectVersion(t, "signing-key", "rsa-3072")
verifyExportsCorrectVersion(t, "signing-key", "rsa-4096")
verifyExportsCorrectVersion(t, "hmac-key", "aes128-gcm96")
verifyExportsCorrectVersion(t, "hmac-key", "aes256-gcm96")
verifyExportsCorrectVersion(t, "hmac-key", "chacha20-poly1305")
Expand All @@ -40,6 +84,13 @@ func TestTransit_Export_KeyVersion_ExportsCorrectVersion(t *testing.T) {
verifyExportsCorrectVersion(t, "hmac-key", "ecdsa-p521")
verifyExportsCorrectVersion(t, "hmac-key", "ed25519")
verifyExportsCorrectVersion(t, "hmac-key", "hmac")
verifyExportsCorrectVersion(t, "public-key", "rsa-2048")
verifyExportsCorrectVersion(t, "public-key", "rsa-3072")
verifyExportsCorrectVersion(t, "public-key", "rsa-4096")
verifyExportsCorrectVersion(t, "public-key", "ecdsa-p256")
verifyExportsCorrectVersion(t, "public-key", "ecdsa-p384")
verifyExportsCorrectVersion(t, "public-key", "ecdsa-p521")
verifyExportsCorrectVersion(t, "public-key", "ed25519")
}

func verifyExportsCorrectVersion(t *testing.T, exportType, keyType string) {
Expand Down Expand Up @@ -136,6 +187,8 @@ func verifyExportsCorrectVersion(t *testing.T, exportType, keyType string) {
}

func TestTransit_Export_ValidVersionsOnly(t *testing.T) {
t.Parallel()

b, storage := createBackendWithSysView(t)

// First create a key, v1
Expand Down Expand Up @@ -236,6 +289,8 @@ func TestTransit_Export_ValidVersionsOnly(t *testing.T) {
}

func TestTransit_Export_KeysNotMarkedExportable_ReturnsError(t *testing.T) {
t.Parallel()

b, storage := createBackendWithSysView(t)

req := &logical.Request{
Expand Down Expand Up @@ -266,6 +321,8 @@ func TestTransit_Export_KeysNotMarkedExportable_ReturnsError(t *testing.T) {
}

func TestTransit_Export_SigningDoesNotSupportSigning_ReturnsError(t *testing.T) {
t.Parallel()

b, storage := createBackendWithSysView(t)

req := &logical.Request{
Expand Down Expand Up @@ -294,6 +351,8 @@ func TestTransit_Export_SigningDoesNotSupportSigning_ReturnsError(t *testing.T)
}

func TestTransit_Export_EncryptionDoesNotSupportEncryption_ReturnsError(t *testing.T) {
t.Parallel()

testTransit_Export_EncryptionDoesNotSupportEncryption_ReturnsError(t, "ecdsa-p256")
testTransit_Export_EncryptionDoesNotSupportEncryption_ReturnsError(t, "ecdsa-p384")
testTransit_Export_EncryptionDoesNotSupportEncryption_ReturnsError(t, "ecdsa-p521")
Expand Down Expand Up @@ -324,11 +383,55 @@ func testTransit_Export_EncryptionDoesNotSupportEncryption_ReturnsError(t *testi
}
_, err = b.HandleRequest(context.Background(), req)
if err == nil {
t.Fatal("Key does not support encryption but was exported without error.")
t.Fatalf("Key %s does not support encryption but was exported without error.", keyType)
}
}

func TestTransit_Export_PublicKeyDoesNotSupportEncryption_ReturnsError(t *testing.T) {
t.Parallel()

testTransit_Export_PublicKeyNotSupported_ReturnsError(t, "chacha20-poly1305")
testTransit_Export_PublicKeyNotSupported_ReturnsError(t, "aes128-gcm96")
testTransit_Export_PublicKeyNotSupported_ReturnsError(t, "aes256-gcm96")
testTransit_Export_PublicKeyNotSupported_ReturnsError(t, "hmac")
}

func testTransit_Export_PublicKeyNotSupported_ReturnsError(t *testing.T, keyType string) {
b, storage := createBackendWithSysView(t)

req := &logical.Request{
Storage: storage,
Operation: logical.UpdateOperation,
Path: "keys/foo",
Data: map[string]interface{}{
"type": keyType,
},
}
if keyType == "hmac" {
req.Data["key_size"] = 32
}
_, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatalf("failed creating key %s: %v", keyType, err)
}

req = &logical.Request{
Storage: storage,
Operation: logical.ReadOperation,
Path: "export/public-key/foo",
}
_, err = b.HandleRequest(context.Background(), req)
if err == nil {
t.Fatalf("Key %s does not support public key exporting but was exported without error.", keyType)
}
if !strings.Contains(err.Error(), fmt.Sprintf("unknown key type %s for export type public-key", keyType)) {
t.Fatalf("unexpected error value for key type: %s: %v", keyType, err)
}
}

func TestTransit_Export_KeysDoesNotExist_ReturnsNotFound(t *testing.T) {
t.Parallel()

b, storage := createBackendWithSysView(t)

req := &logical.Request{
Expand All @@ -344,6 +447,8 @@ func TestTransit_Export_KeysDoesNotExist_ReturnsNotFound(t *testing.T) {
}

func TestTransit_Export_EncryptionKey_DoesNotExportHMACKey(t *testing.T) {
t.Parallel()

b, storage := createBackendWithSysView(t)

req := &logical.Request{
Expand Down Expand Up @@ -394,6 +499,8 @@ func TestTransit_Export_EncryptionKey_DoesNotExportHMACKey(t *testing.T) {
}

func TestTransit_Export_CertificateChain(t *testing.T) {
t.Parallel()

generateKeys(t)

// Create Cluster
Expand Down
24 changes: 3 additions & 21 deletions builtin/logical/transit/path_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ package transit

import (
"context"
"crypto"
"crypto/elliptic"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
Expand Down Expand Up @@ -429,27 +427,11 @@ func (b *backend) formatKeyPolicy(p *keysutil.Policy, context []byte) (*logical.
key.Name = "rsa-4096"
}

var publicKey crypto.PublicKey
publicKey = v.RSAPublicKey
if !v.IsPrivateKeyMissing() {
publicKey = v.RSAKey.Public()
}

// Encode the RSA public key in PEM format to return over the
// API
derBytes, err := x509.MarshalPKIXPublicKey(publicKey)
pubKey, err := encodeRSAPublicKey(&v)
if err != nil {
return nil, fmt.Errorf("error marshaling RSA public key: %w", err)
}
pemBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: derBytes,
}
pemBytes := pem.EncodeToMemory(pemBlock)
if pemBytes == nil || len(pemBytes) == 0 {
return nil, fmt.Errorf("failed to PEM-encode RSA public key")
return nil, err
}
key.PublicKey = string(pemBytes)
key.PublicKey = pubKey
}

retKeys[k] = structs.New(key).Map()
Expand Down
3 changes: 3 additions & 0 deletions changelog/24054.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
secrets/transit: Fix a panic when attempting to export a public RSA key
```
Loading