Skip to content

Commit

Permalink
Disable encrypted private key support when in FIPS mode (#278)
Browse files Browse the repository at this point in the history
  • Loading branch information
michel-laterman authored Feb 12, 2025
1 parent 1aa0d48 commit 977d131
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 51 deletions.
33 changes: 33 additions & 0 deletions transport/tlscommon/decrypt_block_fips.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

//go:build requirefips

package tlscommon

import (
"encoding/pem"
"errors"
"fmt"
)

func decryptPKCS1Key(block pem.Block, passphrase []byte) (pem.Block, error) {
return block, fmt.Errorf("encrypted private keys are unsupported in FIPS mode: %w", errors.ErrUnsupported)
}
func decryptPKCS8Key(block pem.Block, passphrase []byte) (pem.Block, error) {
return block, fmt.Errorf("encrypted private keys are unsupported in FIPS mode: %w", errors.ErrUnsupported)
}
79 changes: 79 additions & 0 deletions transport/tlscommon/decrypt_block_nofips.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

//go:build !requirefips

package tlscommon

import (
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"

"github.com/elastic/pkcs8"
)

func decryptPKCS1Key(block pem.Block, passphrase []byte) (pem.Block, error) {
if len(passphrase) == 0 {
return block, errors.New("no passphrase available")
}

// Note, decrypting pem might succeed even with wrong password, but
// only noise will be stored in buffer in this case.
buffer, err := x509.DecryptPEMBlock(&block, passphrase) //nolint: staticcheck // deprecated, we have to get rid of it
if err != nil {
return block, fmt.Errorf("failed to decrypt pem: %w", err)
}

// DEK-Info contains encryption info. Remove header to mark block as
// unencrypted.
delete(block.Headers, "DEK-Info")
block.Bytes = buffer

return block, nil
}

func decryptPKCS8Key(block pem.Block, passphrase []byte) (pem.Block, error) {
if len(passphrase) == 0 {
return block, errors.New("no passphrase available")
}

key, err := pkcs8.ParsePKCS8PrivateKey(block.Bytes, passphrase)
if err != nil {
return block, fmt.Errorf("failed to parse key: %w", err)
}

switch key.(type) {
case *rsa.PrivateKey:
block.Type = "RSA PRIVATE KEY"
case *ecdsa.PrivateKey:
block.Type = "ECDSA PRIVATE KEY"
default:
return block, fmt.Errorf("unknown key type %T", key)
}

buffer, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return block, fmt.Errorf("failed to marshal decrypted private key: %w", err)
}
block.Bytes = buffer

return block, nil
}
51 changes: 0 additions & 51 deletions transport/tlscommon/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ package tlscommon

import (
"bytes"
"crypto/ecdsa"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
Expand All @@ -31,7 +29,6 @@ import (
"strings"

"github.com/elastic/elastic-agent-libs/logp"
"github.com/elastic/pkcs8"
)

const logSelector = "tls"
Expand Down Expand Up @@ -150,54 +147,6 @@ func ReadPEMFile(log *logp.Logger, s, passphrase string) ([]byte, error) {
return buffer.Bytes(), nil
}

func decryptPKCS1Key(block pem.Block, passphrase []byte) (pem.Block, error) {
if len(passphrase) == 0 {
return block, errors.New("no passphrase available")
}

// Note, decrypting pem might succeed even with wrong password, but
// only noise will be stored in buffer in this case.
buffer, err := x509.DecryptPEMBlock(&block, passphrase) //nolint: staticcheck // deprecated, we have to get rid of it
if err != nil {
return block, fmt.Errorf("failed to decrypt pem: %w", err)
}

// DEK-Info contains encryption info. Remove header to mark block as
// unencrypted.
delete(block.Headers, "DEK-Info")
block.Bytes = buffer

return block, nil
}

func decryptPKCS8Key(block pem.Block, passphrase []byte) (pem.Block, error) {
if len(passphrase) == 0 {
return block, errors.New("no passphrase available")
}

key, err := pkcs8.ParsePKCS8PrivateKey(block.Bytes, passphrase)
if err != nil {
return block, fmt.Errorf("failed to parse key: %w", err)
}

switch key.(type) {
case *rsa.PrivateKey:
block.Type = "RSA PRIVATE KEY"
case *ecdsa.PrivateKey:
block.Type = "ECDSA PRIVATE KEY"
default:
return block, fmt.Errorf("unknown key type %T", key)
}

buffer, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return block, fmt.Errorf("failed to marshal decrypted private key: %w", err)
}
block.Bytes = buffer

return block, nil
}

// LoadCertificateAuthorities read the slice of CAcert and return a Certpool.
func LoadCertificateAuthorities(CAs []string) (*x509.CertPool, []error) {
errors := []error{}
Expand Down

0 comments on commit 977d131

Please sign in to comment.