github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/ca/keyutils/keyutils.go (about)

     1  // Package keyutils serves as a utility to parse, encrypt and decrypt
     2  // PKCS#1 and PKCS#8 private keys based on current FIPS mode status,
     3  // supporting only EC type keys. It always allows PKCS#8 private keys
     4  // and disallow PKCS#1 private keys in FIPS-mode.
     5  package keyutils
     6  
     7  import (
     8  	"crypto"
     9  	cryptorand "crypto/rand"
    10  	"crypto/x509"
    11  	"encoding/pem"
    12  	"errors"
    13  
    14  	"github.com/cloudflare/cfssl/helpers"
    15  	"github.com/docker/swarmkit/ca/pkcs8"
    16  )
    17  
    18  // Formatter provides an interface for converting keys to the right format, and encrypting and decrypting keys
    19  type Formatter interface {
    20  	ParsePrivateKeyPEMWithPassword(pemBytes, password []byte) (crypto.Signer, error)
    21  	DecryptPEMBlock(block *pem.Block, password []byte) ([]byte, error)
    22  	EncryptPEMBlock(data, password []byte) (*pem.Block, error)
    23  }
    24  
    25  // ErrFIPSUnsupportedKeyFormat is returned when encryption/decryption operations are attempted on a PKCS1 key
    26  // when FIPS mode is enabled.
    27  var ErrFIPSUnsupportedKeyFormat = errors.New("unsupported key format due to FIPS compliance")
    28  
    29  // Default is the default key util, where FIPS is not required
    30  var Default Formatter = &utils{fips: false}
    31  
    32  // FIPS is the key utility which enforces FIPS compliance
    33  var FIPS Formatter = &utils{fips: true}
    34  
    35  type utils struct {
    36  	fips bool
    37  }
    38  
    39  // IsPKCS8 returns true if the provided der bytes is encrypted/unencrypted PKCS#8 key
    40  func IsPKCS8(derBytes []byte) bool {
    41  	if _, err := x509.ParsePKCS8PrivateKey(derBytes); err == nil {
    42  		return true
    43  	}
    44  
    45  	return pkcs8.IsEncryptedPEMBlock(&pem.Block{
    46  		Type:    "PRIVATE KEY",
    47  		Headers: nil,
    48  		Bytes:   derBytes,
    49  	})
    50  }
    51  
    52  // IsEncryptedPEMBlock checks if a PKCS#1 or PKCS#8 PEM-block is encrypted or not
    53  func IsEncryptedPEMBlock(block *pem.Block) bool {
    54  	return pkcs8.IsEncryptedPEMBlock(block) || x509.IsEncryptedPEMBlock(block)
    55  }
    56  
    57  // ParsePrivateKeyPEMWithPassword parses an encrypted or a decrypted PKCS#1 or PKCS#8 PEM to crypto.Signer.
    58  // It returns an error in FIPS mode if PKCS#1 PEM bytes are passed.
    59  func (u *utils) ParsePrivateKeyPEMWithPassword(pemBytes, password []byte) (crypto.Signer, error) {
    60  	block, _ := pem.Decode(pemBytes)
    61  	if block == nil {
    62  		return nil, errors.New("Could not parse PEM")
    63  	}
    64  
    65  	if IsPKCS8(block.Bytes) {
    66  		return pkcs8.ParsePrivateKeyPEMWithPassword(pemBytes, password)
    67  	} else if u.fips {
    68  		return nil, ErrFIPSUnsupportedKeyFormat
    69  	}
    70  
    71  	return helpers.ParsePrivateKeyPEMWithPassword(pemBytes, password)
    72  }
    73  
    74  // DecryptPEMBlock requires PKCS#1 or PKCS#8 PEM Block and password to decrypt and return unencrypted der []byte
    75  // It returns an error in FIPS mode when PKCS#1 PEM Block is passed.
    76  func (u *utils) DecryptPEMBlock(block *pem.Block, password []byte) ([]byte, error) {
    77  	if IsPKCS8(block.Bytes) {
    78  		return pkcs8.DecryptPEMBlock(block, password)
    79  	} else if u.fips {
    80  		return nil, ErrFIPSUnsupportedKeyFormat
    81  	}
    82  
    83  	return x509.DecryptPEMBlock(block, password)
    84  }
    85  
    86  // EncryptPEMBlock takes DER-format bytes and password to return an encrypted PKCS#1 or PKCS#8 PEM-block
    87  // It returns an error in FIPS mode when PKCS#1 PEM bytes are passed.
    88  func (u *utils) EncryptPEMBlock(data, password []byte) (*pem.Block, error) {
    89  	if IsPKCS8(data) {
    90  		return pkcs8.EncryptPEMBlock(data, password)
    91  	} else if u.fips {
    92  		return nil, ErrFIPSUnsupportedKeyFormat
    93  	}
    94  
    95  	cipherType := x509.PEMCipherAES256
    96  	return x509.EncryptPEMBlock(cryptorand.Reader,
    97  		"EC PRIVATE KEY",
    98  		data,
    99  		password,
   100  		cipherType)
   101  }