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

pki: When a role sets key_type to any ignore key_bits value when signing a csr #16246

Merged
merged 4 commits into from
Jul 8, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
34 changes: 34 additions & 0 deletions builtin/logical/pki/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2486,6 +2486,40 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) {
}
}

func TestBackend_ConsulSignLeafWithLegacyRole(t *testing.T) {
// create the backend
b, s := createBackendWithStorage(t)

// generate root
data, err := CBWrite(b, s, "root/generate/internal", map[string]interface{}{
"ttl": "40h",
"common_name": "myvault.com",
})
require.NoError(t, err, "failed generating internal root cert")
rootCaPem := data.Data["certificate"].(string)

// Create a signing role like Consul did with the default args prior to Vault 1.10
_, err = CBWrite(b, s, "roles/test", map[string]interface{}{
"allow_any_name": true,
"allowed_serial_numbers": []string{"MySerialNumber"},
"key_type": "any",
"key_bits": "2048",
"signature_bits": "256",
})
require.NoError(t, err, "failed creating legacy role")

_, csrPem := generateTestCsr(t, certutil.ECPrivateKey, 256)
data, err = CBWrite(b, s, "sign/test", map[string]interface{}{
"csr": csrPem,
})
require.NoError(t, err, "failed signing csr")
certAsPem := data.Data["certificate"].(string)

signedCert := parseCert(t, certAsPem)
rootCert := parseCert(t, rootCaPem)
requireSignedBy(t, signedCert, rootCert.PublicKey)
}

func TestBackend_SignSelfIssued(t *testing.T) {
// create the backend
b, storage := createBackendWithStorage(t)
Expand Down
24 changes: 13 additions & 11 deletions builtin/logical/pki/cert_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -836,22 +836,24 @@ func signCert(b *backend,
// We update the value of KeyBits and SignatureBits here (from the
// role), using the specified key type. This allows us to convert
// the default value (0) for SignatureBits and KeyBits to a
// meaningful value. In the event KeyBits takes a zero value, we also
// update that to a new value.
// meaningful value.
//
// This is mandatory because on some roles, with KeyType any, we'll
// set a default SignatureBits to 0, but this will need to be updated
// in order to behave correctly during signing.
roleBitsWasZero := data.role.KeyBits == 0
if data.role.KeyBits, data.role.SignatureBits, err = certutil.ValidateDefaultOrValueKeyTypeSignatureLength(actualKeyType, data.role.KeyBits, data.role.SignatureBits); err != nil {
// We ignore the role's original KeyBits value if the KeyType is any
// as legacy (pre-1.10) roles had default values that made sense only
// for RSA keys (2048) and the older code paths ignored the role value
cipherboy marked this conversation as resolved.
Show resolved Hide resolved
// set for KeyBits when KeyType was set to any. This also enforces the
// docs saying when key_type any we only enforce our specified minimums
cipherboy marked this conversation as resolved.
Show resolved Hide resolved
// for signing operations
if data.role.KeyBits, data.role.SignatureBits, err = certutil.ValidateDefaultOrValueKeyTypeSignatureLength(
actualKeyType, 0, data.role.SignatureBits); err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf("unknown internal error updating default values: %v", err)}
}

// We're using the KeyBits field as a minimum value, and P-224 is safe
// We're using the KeyBits field as a minimum value below, and P-224 is safe
// and a previously allowed value. However, the above call defaults
// to P-256 as that's a saner default than P-224 (w.r.t. generation).
// So, override our fake Role value if it was previously zero.
if actualKeyType == "ec" && roleBitsWasZero {
// to P-256 as that's a saner default than P-224 (w.r.t. generation), so
// override it here to allow 224 as the smallest size we permit.
if actualKeyType == "ec" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't quite look right? If a role's Keybits was 256, and we were using P-256 why would we change that without testing it was 0 (or any)? Also comment no longer looks right.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed all this code is a tad confusing. The api docs specify the following

signing existing CSRs, any can be specified to allow keys of either type and with any bit size (subject to >1024 bits for RSA keys).

So we are within the any block so we are resetting the minimum size of key_bits no matter what the role had as a value to the lowest values we feel comfortable supporting for EC keys like we do for RSA keys.

I'll tweak the comment a bit dropping the "if it was previously zero" that I missed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slack conversation:

Kit Haines
4:46 PM
I’m no longer sure if we are referring to “any” as keyBits or keyType
4:47
if we have set keyType “any” and keyBits “256" would we want to update that on the role?
4:47
or is that role not saved?

Steve Clark
4:47 PM
ha, yeah okay. When keyType == ‘any’ basically we just enforce mins for key bits no matter what the role says
4:48
That role that we update in the code you are seeing it thrown away
4:49
Alex had a different fix which might be clearer, I’m not sure. He was going to change the getRole function to always return a value of 0 for KeyBits if the role had a KeyType == ‘any’

Kit Haines
4:50 PM
^ I think I’d fine that clearer, but given we are throwing away the changes, I guess it doesn’t actually matter

data.role.KeyBits = 224
}
}
Expand Down
2 changes: 1 addition & 1 deletion builtin/logical/pki/path_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ func (b *backend) getRole(ctx context.Context, s logical.Storage, n string) (*ro
modified = true
}

// Ensure the role is valida fter updating.
// Ensure the role is valid after updating.
_, err = validateRole(b, &result, ctx, s)
if err != nil {
return nil, err
Expand Down
3 changes: 3 additions & 0 deletions changelog/16246.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
secret/pki: Do not fail validation with a legacy key_bits default value when signing CSRs
cipherboy marked this conversation as resolved.
Show resolved Hide resolved
```
5 changes: 3 additions & 2 deletions website/content/api-docs/secret/pki.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2338,7 +2338,7 @@ request is denied.
generated private keys and the type of key expected for submitted CSRs.
Currently, `rsa`, `ec`, and `ed25519` are supported, or when signing
existing CSRs, `any` can be specified to allow keys of either type
and with any bit size (subject to >1024 bits for RSA keys).
and with any bit size (subject to >1024 bits for RSA keys or >= 224 for EC keys).
cipherboy marked this conversation as resolved.
Show resolved Hide resolved

~> **Note**: In FIPS 140-2 mode, the following algorithms are not certified
and thus should not be used: `ed25519`.
Expand All @@ -2347,7 +2347,8 @@ request is denied.
generated keys. Allowed values are 0 (universal default); with
`key_type=rsa`, allowed values are: 2048 (default), 3072, or
4096; with `key_type=ec`, allowed values are: 224, 256 (default),
384, or 521; ignored with `key_type=ed25519`.
384, or 521; ignored with `key_type=ed25519` or in signing operations
when `key_type=any`.

- `signature_bits` `(int: 0)` - Specifies the number of bits to use in
the signature algorithm; accepts 256 for SHA-2-256, 384 for SHA-2-384,
Expand Down