Add universal default key_bits value for PKI endpoints (#13080)
* Allow universal default for key_bits This allows the key_bits field to take a universal default value, 0, which, depending on key_type, gets adjusted appropriately into a specific default value (rsa->2048, ec->256, ignored under ed25519). Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Handle universal default key size in certutil Also move RSA < 2048 error message into certutil directly, instead of in ca_util/path_roles. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add missing RSA key sizes to pki/backend_test.go Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Switch to returning updated values When determining the default, don't pass in pointer types, but instead return the newly updated value. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add changelog entry Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Re-add fix for ed25519 from #13254 Ed25519 internally specifies a hash length; by changing the default from 256 to 0, we fail validation in ValidateSignatureLength(...) unless we specify the key algorithm. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This commit is contained in:
parent
9674a75a4d
commit
31ff2be589
|
@ -1183,21 +1183,21 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep {
|
||||||
}
|
}
|
||||||
|
|
||||||
getRandCsr := func(keyType string, errorOk bool, csrTemplate *x509.CertificateRequest) csrPlan {
|
getRandCsr := func(keyType string, errorOk bool, csrTemplate *x509.CertificateRequest) csrPlan {
|
||||||
rsaKeyBits := []int{2048, 4096}
|
rsaKeyBits := []int{2048, 3072, 4096}
|
||||||
ecKeyBits := []int{224, 256, 384, 521}
|
ecKeyBits := []int{224, 256, 384, 521}
|
||||||
plan := csrPlan{errorOk: errorOk}
|
plan := csrPlan{errorOk: errorOk}
|
||||||
|
|
||||||
var testBitSize int
|
var testBitSize int
|
||||||
switch keyType {
|
switch keyType {
|
||||||
case "rsa":
|
case "rsa":
|
||||||
plan.roleKeyBits = rsaKeyBits[mathRand.Int()%2]
|
plan.roleKeyBits = rsaKeyBits[mathRand.Int()%len(rsaKeyBits)]
|
||||||
testBitSize = plan.roleKeyBits
|
testBitSize = plan.roleKeyBits
|
||||||
|
|
||||||
// If we don't expect an error already, randomly choose a
|
// If we don't expect an error already, randomly choose a
|
||||||
// key size and expect an error if it's less than the role
|
// key size and expect an error if it's less than the role
|
||||||
// setting
|
// setting
|
||||||
if !keybitSizeRandOff && !errorOk {
|
if !keybitSizeRandOff && !errorOk {
|
||||||
testBitSize = rsaKeyBits[mathRand.Int()%2]
|
testBitSize = rsaKeyBits[mathRand.Int()%len(rsaKeyBits)]
|
||||||
}
|
}
|
||||||
|
|
||||||
if testBitSize < plan.roleKeyBits {
|
if testBitSize < plan.roleKeyBits {
|
||||||
|
@ -1205,14 +1205,14 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep {
|
||||||
}
|
}
|
||||||
|
|
||||||
case "ec":
|
case "ec":
|
||||||
plan.roleKeyBits = ecKeyBits[mathRand.Int()%4]
|
plan.roleKeyBits = ecKeyBits[mathRand.Int()%len(ecKeyBits)]
|
||||||
testBitSize = plan.roleKeyBits
|
testBitSize = plan.roleKeyBits
|
||||||
|
|
||||||
// If we don't expect an error already, randomly choose a
|
// If we don't expect an error already, randomly choose a
|
||||||
// key size and expect an error if it's less than the role
|
// key size and expect an error if it's less than the role
|
||||||
// setting
|
// setting
|
||||||
if !keybitSizeRandOff && !errorOk {
|
if !keybitSizeRandOff && !errorOk {
|
||||||
testBitSize = ecKeyBits[mathRand.Int()%4]
|
testBitSize = ecKeyBits[mathRand.Int()%len(ecKeyBits)]
|
||||||
}
|
}
|
||||||
|
|
||||||
if testBitSize < plan.roleKeyBits {
|
if testBitSize < plan.roleKeyBits {
|
||||||
|
|
|
@ -50,12 +50,8 @@ func (b *backend) getGenerationParams(
|
||||||
PostalCode: data.Get("postal_code").([]string),
|
PostalCode: data.Get("postal_code").([]string),
|
||||||
}
|
}
|
||||||
|
|
||||||
if role.KeyType == "rsa" && role.KeyBits < 2048 {
|
var err error
|
||||||
errorResp = logical.ErrorResponse("RSA keys < 2048 bits are unsafe and not supported")
|
if role.KeyBits, role.SignatureBits, err = certutil.ValidateDefaultOrValueKeyTypeSignatureLength(role.KeyType, role.KeyBits, role.SignatureBits); err != nil {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := certutil.ValidateKeyTypeSignatureLength(role.KeyType, role.KeyBits, &role.SignatureBits); err != nil {
|
|
||||||
errorResp = logical.ErrorResponse(err.Error())
|
errorResp = logical.ErrorResponse(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -250,12 +250,13 @@ the private key!`,
|
||||||
|
|
||||||
fields["key_bits"] = &framework.FieldSchema{
|
fields["key_bits"] = &framework.FieldSchema{
|
||||||
Type: framework.TypeInt,
|
Type: framework.TypeInt,
|
||||||
Default: 2048,
|
Default: 0,
|
||||||
Description: `The number of bits to use. You will almost
|
Description: `The number of bits to use. Allowed values are
|
||||||
certainly want to change this if you adjust
|
0 (universal default); with rsa key_type: 2048 (default), 3072, or
|
||||||
the key_type.`,
|
4096; with ec key_type: 224, 256 (default), 384, or 521; ignored with
|
||||||
|
ed25519.`,
|
||||||
DisplayAttrs: &framework.DisplayAttributes{
|
DisplayAttrs: &framework.DisplayAttributes{
|
||||||
Value: 2048,
|
Value: 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -199,10 +199,11 @@ protection use. Defaults to false.`,
|
||||||
|
|
||||||
"key_bits": {
|
"key_bits": {
|
||||||
Type: framework.TypeInt,
|
Type: framework.TypeInt,
|
||||||
Default: 2048,
|
Default: 0,
|
||||||
Description: `The number of bits to use. You will almost
|
Description: `The number of bits to use. Allowed values are
|
||||||
certainly want to change this if you adjust
|
0 (universal default); with rsa key_type: 2048 (default), 3072, or
|
||||||
the key_type.`,
|
4096; with ec key_type: 224, 256 (default), 384, or 521; ignored with
|
||||||
|
ed25519.`,
|
||||||
},
|
},
|
||||||
|
|
||||||
"signature_bits": &framework.FieldSchema{
|
"signature_bits": &framework.FieldSchema{
|
||||||
|
@ -615,17 +616,13 @@ func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data
|
||||||
*entry.GenerateLease = data.Get("generate_lease").(bool)
|
*entry.GenerateLease = data.Get("generate_lease").(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
if entry.KeyType == "rsa" && entry.KeyBits < 2048 {
|
|
||||||
return logical.ErrorResponse("RSA keys < 2048 bits are unsafe and not supported"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry.MaxTTL > 0 && entry.TTL > entry.MaxTTL {
|
if entry.MaxTTL > 0 && entry.TTL > entry.MaxTTL {
|
||||||
return logical.ErrorResponse(
|
return logical.ErrorResponse(
|
||||||
`"ttl" value must be less than "max_ttl" value`,
|
`"ttl" value must be less than "max_ttl" value`,
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := certutil.ValidateKeyTypeSignatureLength(entry.KeyType, entry.KeyBits, &entry.SignatureBits); err != nil {
|
if entry.KeyBits, entry.SignatureBits, err = certutil.ValidateDefaultOrValueKeyTypeSignatureLength(entry.KeyType, entry.KeyBits, entry.SignatureBits); err != nil {
|
||||||
return logical.ErrorResponse(err.Error()), nil
|
return logical.ErrorResponse(err.Error()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
3
changelog/13080.txt
Normal file
3
changelog/13080.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bug
|
||||||
|
secrets/pki: Default value for key_bits changed to 0, enabling key_type=ec key generation with default value
|
||||||
|
```
|
|
@ -33,6 +33,14 @@ import (
|
||||||
cbasn1 "golang.org/x/crypto/cryptobyte/asn1"
|
cbasn1 "golang.org/x/crypto/cryptobyte/asn1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const rsaMinimumSecureKeySize = 2048
|
||||||
|
|
||||||
|
// Mapping of key types to default key lengths
|
||||||
|
var defaultAlgorithmKeyBits = map[string]int {
|
||||||
|
"rsa": 2048,
|
||||||
|
"ec": 256,
|
||||||
|
}
|
||||||
|
|
||||||
// Mapping of NIST P-Curve's key length to expected signature bits.
|
// Mapping of NIST P-Curve's key length to expected signature bits.
|
||||||
var expectedNISTPCurveHashBits = map[int]int{
|
var expectedNISTPCurveHashBits = map[int]int{
|
||||||
224: 256,
|
224: 256,
|
||||||
|
@ -533,14 +541,27 @@ func StringToOid(in string) (asn1.ObjectIdentifier, error) {
|
||||||
return asn1.ObjectIdentifier(ret), nil
|
return asn1.ObjectIdentifier(ret), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validates that the combination of keyType, keyBits, and hashBits are
|
// Returns default key bits for the specified key type, or the present value
|
||||||
// valid together; replaces individual calls to ValidateSignatureLength and
|
// if keyBits is non-zero.
|
||||||
// ValidateKeyTypeLength.
|
func DefaultOrValueKeyBits(keyType string, keyBits int) (int, error) {
|
||||||
func ValidateKeyTypeSignatureLength(keyType string, keyBits int, hashBits *int) error {
|
if keyBits == 0 {
|
||||||
if err := ValidateKeyTypeLength(keyType, keyBits); err != nil {
|
newValue, present := defaultAlgorithmKeyBits[keyType]
|
||||||
return err
|
if present {
|
||||||
|
keyBits = newValue
|
||||||
|
} /* else {
|
||||||
|
// We cannot return an error here as ed25519 (and potentially ed448
|
||||||
|
// in the future) aren't in defaultAlgorithmKeyBits -- the value of
|
||||||
|
// the keyBits parameter is ignored under that algorithm.
|
||||||
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return keyBits, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns default signature hash bit length for the specified key type and
|
||||||
|
// bits, or the present value if hashBits is non-zero. Returns an error under
|
||||||
|
// certain internal circumstances.
|
||||||
|
func DefaultOrValueHashBits(keyType string, keyBits int, hashBits int) (int, error) {
|
||||||
if keyType == "ec" {
|
if keyType == "ec" {
|
||||||
// To comply with BSI recommendations Section 4.2 and Mozilla root
|
// To comply with BSI recommendations Section 4.2 and Mozilla root
|
||||||
// store policy section 5.1.2, enforce that NIST P-curves use a hash
|
// store policy section 5.1.2, enforce that NIST P-curves use a hash
|
||||||
|
@ -548,35 +569,72 @@ func ValidateKeyTypeSignatureLength(keyType string, keyBits int, hashBits *int)
|
||||||
// the "ec" key type.
|
// the "ec" key type.
|
||||||
expectedHashBits := expectedNISTPCurveHashBits[keyBits]
|
expectedHashBits := expectedNISTPCurveHashBits[keyBits]
|
||||||
|
|
||||||
if expectedHashBits != *hashBits && *hashBits != 0 {
|
if expectedHashBits != hashBits && hashBits != 0 {
|
||||||
return fmt.Errorf("unsupported signature hash algorithm length (%d) for NIST P-%d", *hashBits, keyBits)
|
return hashBits, fmt.Errorf("unsupported signature hash algorithm length (%d) for NIST P-%d", hashBits, keyBits)
|
||||||
} else if *hashBits == 0 {
|
} else if hashBits == 0 {
|
||||||
*hashBits = expectedHashBits
|
hashBits = expectedHashBits
|
||||||
}
|
}
|
||||||
} else if keyType == "rsa" && *hashBits == 0 {
|
} else if keyType == "rsa" && hashBits == 0 {
|
||||||
// To match previous behavior (and ignoring recommendations of hash
|
// To match previous behavior (and ignoring NIST's recommendations for
|
||||||
// size to match RSA key sizes), default to SHA-2-256.
|
// hash size to align with RSA key sizes), default to SHA-2-256.
|
||||||
*hashBits = 256
|
hashBits = 256
|
||||||
} else if keyType == "ed25519" {
|
} else if keyType == "ed25519" || keyType == "ed448" {
|
||||||
// No-op; ed25519 and ed448 internally specify their own hash and
|
// No-op; ed25519 and ed448 internally specify their own hash and
|
||||||
// we do not need to select one. Double hashing isn't supported in
|
// we do not need to select one. Double hashing isn't supported in
|
||||||
// certificate signing.
|
// certificate signing and we must
|
||||||
return nil
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashBits, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates that the combination of keyType, keyBits, and hashBits are
|
||||||
|
// valid together; replaces individual calls to ValidateSignatureLength and
|
||||||
|
// ValidateKeyTypeLength. Also updates the value of keyBits and hashBits on
|
||||||
|
// return.
|
||||||
|
func ValidateDefaultOrValueKeyTypeSignatureLength(keyType string, keyBits int, hashBits int) (int, int, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if keyBits, err = DefaultOrValueKeyBits(keyType, keyBits); err != nil {
|
||||||
|
return keyBits, hashBits, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ValidateKeyTypeLength(keyType, keyBits); err != nil {
|
||||||
|
return keyBits, hashBits, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if hashBits, err = DefaultOrValueHashBits(keyType, keyBits, hashBits); err != nil {
|
||||||
|
return keyBits, hashBits, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that this check must come after we've selected a value for
|
// Note that this check must come after we've selected a value for
|
||||||
// hashBits above, in the event it was left as the default, but we
|
// hashBits above, in the event it was left as the default, but we
|
||||||
// were allowed to update it.
|
// were allowed to update it.
|
||||||
if err := ValidateSignatureLength(*hashBits); err != nil || *hashBits == 0 {
|
if err = ValidateSignatureLength(keyType, hashBits); err != nil {
|
||||||
return err
|
return keyBits, hashBits, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return keyBits, hashBits, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validates that the length of the hash (in bits) used in the signature
|
// Validates that the length of the hash (in bits) used in the signature
|
||||||
// calculation is a known, approved value.
|
// calculation is a known, approved value.
|
||||||
func ValidateSignatureLength(hashBits int) error {
|
func ValidateSignatureLength(keyType string, hashBits int) error {
|
||||||
|
if keyType == "ed25519" || keyType == "ed448" {
|
||||||
|
// ed25519 and ed448 include built-in hashing and is not externally
|
||||||
|
// configurable. There are three modes for each of these schemes:
|
||||||
|
//
|
||||||
|
// 1. Built-in hash (default, used in TLS, x509).
|
||||||
|
// 2. Double hash (notably used in some block-chain implementations,
|
||||||
|
// but largely regarded as a specialized use case with security
|
||||||
|
// concerns).
|
||||||
|
// 3. No hash (bring your own hash function, less commonly used).
|
||||||
|
//
|
||||||
|
// In all cases, we won't have a hash algorithm to validate here, so
|
||||||
|
// return nil.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
switch hashBits {
|
switch hashBits {
|
||||||
case 256:
|
case 256:
|
||||||
case 384:
|
case 384:
|
||||||
|
@ -584,12 +642,17 @@ func ValidateSignatureLength(hashBits int) error {
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported hash signature algorithm: %d", hashBits)
|
return fmt.Errorf("unsupported hash signature algorithm: %d", hashBits)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateKeyTypeLength(keyType string, keyBits int) error {
|
func ValidateKeyTypeLength(keyType string, keyBits int) error {
|
||||||
switch keyType {
|
switch keyType {
|
||||||
case "rsa":
|
case "rsa":
|
||||||
|
if keyBits < rsaMinimumSecureKeySize {
|
||||||
|
return fmt.Errorf("RSA keys < %d bits are unsafe and not supported: got %d", rsaMinimumSecureKeySize, keyBits)
|
||||||
|
}
|
||||||
|
|
||||||
switch keyBits {
|
switch keyBits {
|
||||||
case 2048:
|
case 2048:
|
||||||
case 3072:
|
case 3072:
|
||||||
|
|
Loading…
Reference in a new issue