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

Restrict ECDSA/NIST P-Curve hash function sizes for cert signing #12872

Merged
merged 7 commits into from
Nov 12, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 0 additions & 1 deletion builtin/logical/pki/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,6 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep {
KeyType: "rsa",
KeyBits: 2048,
RequireCN: true,
SignatureBits: 256,
}
issueVals := certutil.IssueData{}
ret := []logicaltest.TestStep{}
Expand Down
7 changes: 1 addition & 6 deletions builtin/logical/pki/ca_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,9 @@ func (b *backend) getGenerationParams(
return
}

if err := certutil.ValidateKeyTypeLength(role.KeyType, role.KeyBits); err != nil {
if err := certutil.ValidateKeyTypeSignatureLength(role.KeyType, role.KeyBits, &role.SignatureBits); err != nil {
errorResp = logical.ErrorResponse(err.Error())
}

if err := certutil.ValidateSignatureLength(role.SignatureBits); err != nil {
errorResp = logical.ErrorResponse(err.Error())
return
}

return
}
10 changes: 5 additions & 5 deletions builtin/logical/pki/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,13 @@ the key_type.`,

fields["signature_bits"] = &framework.FieldSchema{
Type: framework.TypeInt,
Default: 256,
Default: 0,
Description: `The number of bits to use in the signature
algorithm. Defaults to 256 for SHA256.
Set to 384 for SHA384 and 512 for SHA512.
`,
algorithm; accepts 256 for SHA-2-256, 384 for SHA-2-384, and 512 for
SHA-2-512. Defaults to 0 to automatically detect based on key length
(SHA-2-256 for RSA keys, and matching the curve size for NIST P-Curves).`,
DisplayAttrs: &framework.DisplayAttributes{
Value: 256,
Value: 0,
},
}

Expand Down
13 changes: 5 additions & 8 deletions builtin/logical/pki/path_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,11 @@ the key_type.`,

"signature_bits": &framework.FieldSchema{
Type: framework.TypeInt,
Default: 256,
Default: 0,
Description: `The number of bits to use in the signature
algorithm. Defaults to 256 for SHA256.
Set to 384 for SHA384 and 512 for SHA512.`,
algorithm; accepts 256 for SHA-2-256, 384 for SHA-2-384, and 512 for
SHA-2-512. Defaults to 0 to automatically detect based on key length
(SHA-2-256 for RSA keys, and matching the curve size for NIST P-Curves).`,
},

"key_usage": &framework.FieldSchema{
Expand Down Expand Up @@ -624,11 +625,7 @@ func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data
), nil
}

if err := certutil.ValidateKeyTypeLength(entry.KeyType, entry.KeyBits); err != nil {
return logical.ErrorResponse(err.Error()), nil
}

if err := certutil.ValidateSignatureLength(entry.SignatureBits); err != nil {
if err := certutil.ValidateKeyTypeSignatureLength(entry.KeyType, entry.KeyBits, &entry.SignatureBits); err != nil {
return logical.ErrorResponse(err.Error()), nil
}

Expand Down
3 changes: 3 additions & 0 deletions changelog/12872.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
secrets/pki: Fixes around NIST P-curve signature hash length, default value for signature_bits changed to 0.
```
64 changes: 55 additions & 9 deletions sdk/helper/certutil/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ import (
cbasn1 "golang.org/x/crypto/cryptobyte/asn1"
)

// Mapping of NIST P-Curve's key length to expected signature bits.
var expectedNISTPCurveHashBits = map[int]int{
224: 256,
256: 256,
384: 384,
521: 512,
}

// GetHexFormatted returns the byte buffer formatted in hex with
// the specified separator between bytes.
func GetHexFormatted(buf []byte, sep string) string {
Expand Down Expand Up @@ -525,13 +533,55 @@ func StringToOid(in string) (asn1.ObjectIdentifier, error) {
return asn1.ObjectIdentifier(ret), nil
}

func ValidateSignatureLength(keyBits int) error {
switch keyBits {
// Validates that the combination of keyType, keyBits, and hashBits are
// valid together; replaces individual calls to ValidateSignatureLength and
// ValidateKeyTypeLength.
func ValidateKeyTypeSignatureLength(keyType string, keyBits int, hashBits *int) error {
if err := ValidateKeyTypeLength(keyType, keyBits); err != nil {
return err
}

if keyType == "ec" {
// 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
// length corresponding to curve length. Note that ed25519 does not
// the "ec" key type.
expectedHashBits := expectedNISTPCurveHashBits[keyBits]

if expectedHashBits != *hashBits && *hashBits != 0 {
return fmt.Errorf("unsupported signature hash algorithm length (%d) for NIST P-%d", *hashBits, keyBits)
} else if *hashBits == 0 {
*hashBits = expectedHashBits
}
} else if keyType == "rsa" && *hashBits == 0 {
// To match previous behavior (and ignoring recommendations of hash
// size to match RSA key sizes), default to SHA-2-256.
*hashBits = 256
} /* else if keyType == "ed25519" {
// No-op; ed25519 and ed448 internally specify their own hash and
// we do not need to select one. Double hashing isn't supported in
// certificate signing.
} */

// 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
// were allowed to update it.
if err := ValidateSignatureLength(*hashBits); err != nil || *hashBits == 0 {
return err
}

return nil
}

// Validates that the length of the hash (in bits) used in the signature
// calculation is a known, approved value.
func ValidateSignatureLength(hashBits int) error {
switch hashBits {
case 256:
case 384:
case 512:
default:
return fmt.Errorf("unsupported signature algorithm: %d", keyBits)
return fmt.Errorf("unsupported hash signature algorithm: %d", hashBits)
}
return nil
}
Expand All @@ -548,12 +598,8 @@ func ValidateKeyTypeLength(keyType string, keyBits int) error {
return fmt.Errorf("unsupported bit length for RSA key: %d", keyBits)
}
case "ec":
switch keyBits {
case 224:
case 256:
case 384:
case 521:
default:
_, present := expectedNISTPCurveHashBits[keyBits]
if !present {
return fmt.Errorf("unsupported bit length for EC key: %d", keyBits)
}
case "any", "ed25519":
Expand Down