Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@

## [Unreleased](https://github.com/hashicorp/vault-benchmark/tree/HEAD)

### Features

- Add benchmark for PKI secrets engine for generating keys [\#274](https://github.com/hashicorp/vault-benchmark/pull/274)

### Improvements

- Consolidate identity_group_read into identity as a workload option [\#268](https://github.com/hashicorp/vault-benchmark/pull/268)
- Update benchmarks for PKI issue and PKI sign to support ML-DSA [\#274](https://github.com/hashicorp/vault-benchmark/pull/274)

## [v0.4.1](https://github.com/hashicorp/vault-benchmark/tree/v0.4.1) (Dec 29, 2025)

Expand Down
156 changes: 156 additions & 0 deletions benchmarktests/target_secret_pki_generate_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright IBM Corp. 2022, 2026
// SPDX-License-Identifier: MPL-2.0

package benchmarktests

import (
"encoding/json"
"flag"
"fmt"
"net/http"
"path/filepath"
"time"

"github.com/hashicorp/go-hclog"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/vault/api"
vegeta "github.com/tsenart/vegeta/v12/lib"
)

const (
PKIKeyGenerationTestType = "pki_generate_key"
PKIKeyGenerationTestMethod = "POST"
)

func init() {
TestList[PKIKeyGenerationTestType] = func() BenchmarkBuilder { return &PKIGenerateKey{} }
}

type PKIGenerateKey struct {
pathPrefix string
body []byte
header http.Header
config *PKIGenerateKeyConfig
logger hclog.Logger
mountPath string
}

// PKIGenerateKeyConfig is the top-level HCL config block for this benchmark.
type PKIGenerateKeyConfig struct {
SetupDelay string `hcl:"setup_delay,optional"`
GenerateKeyConfig *PKIGenerateKeyBodyConfig `hcl:"generate_key,block"`
}

// PKIGenerateKeyBodyConfig maps to POST /v1/<mount>/keys/generate/
type PKIGenerateKeyBodyConfig struct {
Type string `hcl:"type,optional"`
KeyType string `hcl:"key_type,optional"`
KeyBits int `hcl:"key_bits,optional"`
KeyName string `hcl:"key_name,optional"`
ManagedKeyName string `hcl:"managed_key_name,optional"`
ManagedKeyID string `hcl:"managed_key_id,optional"`
ParameterSet string `hcl:"parameter_set,optional"`
}

func (p *PKIGenerateKey) ParseConfig(body hcl.Body) error {
testConfig := &struct {
Config *PKIGenerateKeyConfig `hcl:"config,block"`
}{
Config: &PKIGenerateKeyConfig{
SetupDelay: "1s",
GenerateKeyConfig: &PKIGenerateKeyBodyConfig{
Type: "internal",
KeyType: "rsa",
KeyBits: 2048,
},
},
}

diags := gohcl.DecodeBody(body, nil, testConfig)
if diags.HasErrors() {
return fmt.Errorf("error decoding to struct: %v", diags)
}
p.config = testConfig.Config
return nil
}

func (p *PKIGenerateKey) Setup(client *api.Client, mountName string, topLevelConfig *TopLevelTargetConfig) (BenchmarkBuilder, error) {
p.logger = targetLogger.Named(PKIKeyGenerationTestType)

mountPath, err := resolveMountPath(mountName, topLevelConfig.RandomMounts)
if err != nil {
return nil, err
}
p.logger = p.logger.Named(mountPath)

p.logger.Trace(mountLogMessage("secrets", "pki", mountPath))
err = client.Sys().Mount(mountPath, &api.MountInput{
Type: "pki",
Config: api.MountConfigInput{
MaxLeaseTTL: "87600h",
},
})
if err != nil {
return nil, fmt.Errorf("error mounting pki secrets engine: %v", err)
}
p.mountPath = mountPath

// Brief delay to let the PKI mount finish initialising before the first
// write — mirrors the pattern used by pki_issue and pki_sign.
delay, err := time.ParseDuration(p.config.SetupDelay)
if err != nil {
return nil, fmt.Errorf("error parsing setup_delay: %v", err)
}
time.Sleep(delay)

p.logger.Trace(parsingConfigLogMessage("generate key"))
bodyData, err := structToMap(p.config.GenerateKeyConfig)
if err != nil {
return nil, fmt.Errorf("error parsing generate_key config: %v", err)
}
// "type" is the URL path segment, not a body field.
delete(bodyData, "type")

bodyBytes, err := json.Marshal(bodyData)
if err != nil {
return nil, fmt.Errorf("error marshaling generate_key config: %v", err)
}

keyGenType := p.config.GenerateKeyConfig.Type
if keyGenType == "" {
keyGenType = "internal"
}

return &PKIGenerateKey{
// POST /v1/<mount>/keys/generate/<type> where <type> is "internal" or "exported"
pathPrefix: "/v1/" + filepath.Join(mountPath, "keys", "generate", keyGenType),
header: generateHeader(client),
body: bodyBytes,
config: p.config,
logger: p.logger,
mountPath: p.mountPath,
}, nil
}

func (p *PKIGenerateKey) Target(client *api.Client) vegeta.Target {
return vegeta.Target{
Method: PKIKeyGenerationTestMethod,
URL: client.Address() + p.pathPrefix,
Body: p.body,
Header: p.header,
}
}

func (p *PKIGenerateKey) Cleanup(client *api.Client) error {
return cleanupMount(p.logger, client, "/v1/"+p.mountPath)
}

func (p *PKIGenerateKey) GetTargetInfo() TargetInfo {
return TargetInfo{
method: PKIKeyGenerationTestMethod,
pathPrefix: p.pathPrefix,
}
}

func (p *PKIGenerateKey) Flags(fs *flag.FlagSet) {}
3 changes: 3 additions & 0 deletions benchmarktests/target_secret_pki_issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ type PKIIssueRootConfig struct {
PrivateKeyFormat string `hcl:"private_key_format,optional"`
KeyType string `hcl:"key_type,optional"`
KeyBits int `hcl:"key_bits,optional"`
ParameterSet string `hcl:"parameter_set,optional"`
MaxPathLength int `hcl:"max_path_length,optional"`
ExcludeCNFromSANS bool `hcl:"exclude_cn_from_sans,optional"`
PermittedDNSDomains string `hcl:"permitted_dns_domains,optional"`
Expand Down Expand Up @@ -120,6 +121,7 @@ type PKIIssueIntCSRConfig struct {
PrivateKeyFormat string `hcl:"private_key_format,optional"`
KeyType string `hcl:"key_type,optional"`
KeyBits int `hcl:"key_bits,optional"`
ParameterSet string `hcl:"parameter_set,optional"`
KeyName string `hcl:"key_name,optional"`
KeyRef string `hcl:"key_ref,optional"`
SignatureBits int `hcl:"signature_bits,optional"`
Expand Down Expand Up @@ -197,6 +199,7 @@ type PKIIssueRoleConfig struct {
EmailProtectionFlag bool `hcl:"email_protection_flag,optional"`
KeyType string `hcl:"key_type,optional"`
KeyBits int `hcl:"key_bits,optional"`
ParameterSet string `hcl:"parameter_set,optional"`
SignatureBits int `hcl:"signature_bits,optional"`
UsePSS bool `hcl:"use_pss,optional"`
KeyUsage []string `hcl:"key_usage,optional"`
Expand Down
3 changes: 3 additions & 0 deletions benchmarktests/target_secret_pki_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ type pkiSignRootConfig struct {
PrivateKeyFormat string `hcl:"private_key_format,optional"`
KeyType string `hcl:"key_type,optional"`
KeyBits int `hcl:"key_bits,optional"`
ParameterSet string `hcl:"parameter_set,optional"`
MaxPathLength int `hcl:"max_path_length,optional"`
ExcludeCNFromSANS bool `hcl:"exclude_cn_from_sans,optional"`
PermittedDNSDomains string `hcl:"permitted_dns_domains,optional"`
Expand Down Expand Up @@ -126,6 +127,7 @@ type pkiSignIntCSRConfig struct {
PrivateKeyFormat string `hcl:"private_key_format,optional"`
KeyType string `hcl:"key_type,optional"`
KeyBits int `hcl:"key_bits,optional"`
ParameterSet string `hcl:"parameter_set,optional"`
KeyName string `hcl:"key_name,optional"`
KeyRef string `hcl:"key_ref,optional"`
SignatureBits int `hcl:"signature_bits,optional"`
Expand Down Expand Up @@ -203,6 +205,7 @@ type pkiSignRoleConfig struct {
EmailProtectionFlag bool `hcl:"email_protection_flag,optional"`
KeyType string `hcl:"key_type,optional"`
KeyBits int `hcl:"key_bits,optional"`
ParameterSet string `hcl:"parameter_set,optional"`
SignatureBits int `hcl:"signature_bits,optional"`
UsePSS bool `hcl:"use_pss,optional"`
KeyUsage []string `hcl:"key_usage,optional"`
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ Enable Vault debug logging](tests/auth-radius.md)
- [MySQL Secret Benchmark `mysql_secret`](tests/secret-mysql.md)
- [Nomad Secrets Engine Benchmark](tests/secret-nomad.md)
- [Oracle Database Secrets Engine Benchmark (`oracle_secret`)](tests/secret-oracle.md)
- [PKI Generate Key Benchmark (`pki_generate_key`)](tests/secret-pki-generate-key.md)
- [PKI Secret Configuration Options](tests/secret-pki-issue.md)
- [PKI Sign Secret Configuration Options](tests/secret-pki-sign.md)
- [Postgresql Secrets Engine Benchmark `postgresql_secret`](tests/secret-postgresql.md)
Expand Down
70 changes: 70 additions & 0 deletions docs/tests/secret-pki-generate-key.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# PKI Generate Key Benchmark (`pki_generate_key`)

This benchmark tests the performance of PKI key generation operations.

## Test Parameters

### Generate Key Config `generate_key`

- `type` `(string: "internal")` - Specifies the type of key to generate. If
`internal`, the private key is generated inside Vault and cannot be retrieved
later. If `exported`, the private key will be returned in the response.

- `key_type` `(string: "rsa")` - Specifies the desired key type; must be `rsa`,
`ec`, `ed25519`, or `ml-dsa`.

- `key_bits` `(int: 0)` - Specifies the number of bits to use for the generated
key. 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` and
`key_type=ml-dsa`.

- `parameter_set` `(string: "")` - An ML-DSA key parameter set. Required when
`key_type=ml-dsa`. Allowed values are `"44"`, `"65"`, and `"87"`.

- `key_name` `(string: "")` - Optionally specifies a name for the generated key.
The global ref `default` may not be used as a name.

#### Managed Keys Parameters

See [Managed Keys](https://developer.hashicorp.com/vault/api-docs/secret/pki#managed-keys)
for additional details. One of the following parameters must be set when
`type=kms`.

- `managed_key_name` `(string: "")` - The managed key's configured name.

- `managed_key_id` `(string: "")` - The managed key's UUID.

## Example HCL Configuration

```hcl
test "pki_generate_key" "pki_generate_key_test1" {
weight = 100
config {
setup_delay = "2s"

generate_key {
type = "internal"
key_type = "rsa"
key_bits = 2048
}
}
}
```

### ML-DSA Example

```hcl
test "pki_generate_key" "pki_generate_key_mldsa" {
weight = 100
config {
setup_delay = "2s"

generate_key {
type = "internal"
key_type = "ml-dsa"
parameter_set = "44"
}
}
}
```
15 changes: 12 additions & 3 deletions docs/tests/secret-pki-issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ This benchmark tests the performance of PKI issue operations.
field if `format=pem_bundle` parameter is specified.

- `key_type` `(string: "rsa")` - Specifies the desired key type; must be `rsa`, `ed25519`
or `ec`.
`ec`, or `ml-dsa`.

~> **Note**: In FIPS 140-2 mode, the following algorithms are not certified
and thus should not be used: `ed25519`.
Expand All @@ -79,6 +79,9 @@ This benchmark tests the performance of PKI issue operations.
4096; with `key_type=ec`, allowed values are: 224, 256 (default),
384, or 521; ignored with `key_type=ed25519`.

- `parameter_set` `(string: "")` - An ML-DSA key parameter set. Required when
`key_type=ml-dsa`. Allowed values are `"44"`, `"65"`, and `"87"`.

- `max_path_length` `(int: -1)` - Specifies the maximum path length to encode in
the generated certificate. `-1` means no limit. Unless the signing certificate
has a maximum path length set, in which case the path length is set to one
Expand Down Expand Up @@ -195,7 +198,7 @@ See [Managed Keys](https://developer.hashicorp.com/vault/api-docs/secret/pki#man
field if `format=pem_bundle` parameter is specified.

- `key_type` `(string: "rsa")` - Specifies the desired key type; must be `rsa`, `ed25519`
or `ec`. Not suitable for `type=existing` requests.
`ec` or `ml-dsa`. Not suitable for `type=existing` requests.

~> **Note**: In FIPS 140-2 mode, the following algorithms are not certified
and thus should not be used: `ed25519`.
Expand All @@ -210,6 +213,9 @@ See [Managed Keys](https://developer.hashicorp.com/vault/api-docs/secret/pki#man
384, or 521; ignored with `key_type=ed25519`. Not suitable for
`type=existing` requests.

- `parameter_set` `(string: "")` - An ML-DSA key parameter set. Required when
`key_type=ml-dsa`. Allowed values are `"44"`, `"65"`, and `"87"`.

- `key_name` `(string: "")` - When a new key is created with this request,
optionally specifies the name for this. The global ref `default` may not
be used as a name.
Expand Down Expand Up @@ -566,7 +572,7 @@ See [Managed Keys](https://developer.hashicorp.com/vault/api-docs/secret/pki#man

- `key_type` `(string: "rsa")` - Specifies the type of key to generate for
generated private keys and the type of key expected for submitted CSRs.
Currently, `rsa`, `ec`, and `ed25519` are supported, or when signing
Currently, `rsa`, `ec`, `ed25519`, or `ml-dsa` are supported, or when signing
existing CSRs, `any` can be specified to allow keys of either type
and with any bit size (subject to >=2048 bits for RSA keys or >= 224 for EC keys).
When `any` is used, this role cannot generate certificates and can only
Expand All @@ -582,6 +588,9 @@ See [Managed Keys](https://developer.hashicorp.com/vault/api-docs/secret/pki#man
384, or 521; ignored with `key_type=ed25519` or in signing operations
when `key_type=any`.

- `parameter_set` `(string: "")` - An ML-DSA key parameter set. Required when
`key_type=ml-dsa`. Allowed values are `"44"`, `"65"`, and `"87"`.

- `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,
and 512 for SHA-2-512. Defaults to 0 to automatically detect based
Expand Down
Loading
Loading