mirror of
https://github.com/hashicorp/vault.git
synced 2026-02-03 20:40:45 -05:00
* In the random APIs, add a 'prng' param that causes a DRBG seeded from the selected source(s) to be the source of the returned bytes * fixes, unit test next * unit tests * changelog * memory ramifications * switch to using a string called drbg * Update helper/random/random_api.go * wrong changelog --------- Co-authored-by: Scott Miller <smiller@hashicorp.com> Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
This commit is contained in:
parent
375a59c4cd
commit
a728a665e1
5 changed files with 105 additions and 25 deletions
|
|
@ -44,6 +44,12 @@ func (b *backend) pathRandom() *framework.Path {
|
|||
Default: "platform",
|
||||
Description: `Which system to source random data from, ether "platform", "seal", or "all".`,
|
||||
},
|
||||
"drbg": {
|
||||
Type: framework.TypeString,
|
||||
Default: "",
|
||||
Description: "If set, seed a secure DRBG from the source and use it to generate the bytes. This can be more performant when using the seal source." +
|
||||
" Possible values are unset (don't use a DRBG), \"auto\" and \"hmacdrbg\" which are equivalent.",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
|
|
|
|||
6
changelog/_12119.txt
Normal file
6
changelog/_12119.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
```release-note:improvement
|
||||
secrets/transit, core: Boost the limit of random bytes retrievable via random byte
|
||||
APIs. And add the option to get PRNG random bytes seeded by random sources.
|
||||
Note that requests for large numbers of bytes will increase Vault memory
|
||||
usage accordingly.
|
||||
```
|
||||
|
|
@ -11,13 +11,17 @@ import (
|
|||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/hashicorp/go-hmac-drbg/hmacdrbg"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/sdk/framework"
|
||||
"github.com/hashicorp/vault/sdk/helper/xor"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
||||
const APIMaxBytes = 128 * 1024
|
||||
const (
|
||||
APIMaxBytes = 10 * 1024 * 1024
|
||||
SealMaxBytes = 128 * 1024
|
||||
)
|
||||
|
||||
func HandleRandomAPI(d *framework.FieldData, additionalSource io.Reader) (*logical.Response, error) {
|
||||
bytes := 0
|
||||
|
|
@ -43,6 +47,16 @@ func HandleRandomAPI(d *framework.FieldData, additionalSource io.Reader) (*logic
|
|||
return logical.ErrorResponse(fmt.Sprintf("error parsing url-set byte count: %s", err)), nil
|
||||
}
|
||||
}
|
||||
maybeDrbg, ok := d.GetOk("drbg")
|
||||
var drbg string
|
||||
if ok {
|
||||
drbg = maybeDrbg.(string)
|
||||
switch drbg {
|
||||
case "", "auto", "hmacdrbg":
|
||||
default:
|
||||
return logical.ErrorResponse("invalid setting for drbg, must absent, auto, or hmacdrbg"), nil
|
||||
}
|
||||
}
|
||||
format := d.Get("format").(string)
|
||||
|
||||
if bytes < 1 {
|
||||
|
|
@ -50,7 +64,20 @@ func HandleRandomAPI(d *framework.FieldData, additionalSource io.Reader) (*logic
|
|||
}
|
||||
|
||||
if bytes > APIMaxBytes {
|
||||
return logical.ErrorResponse(`"bytes" should be less than %d`, APIMaxBytes), nil
|
||||
return logical.ErrorResponse(`"bytes" must be less than %d`, APIMaxBytes), nil
|
||||
} else if (source == "seal" || source == "all") && bytes > SealMaxBytes && len(drbg) == 0 {
|
||||
return logical.ErrorResponse("bytes cannot be more than %d if sourced from seal. Use drbg mode instead", SealMaxBytes), nil
|
||||
}
|
||||
|
||||
var warning string
|
||||
switch source {
|
||||
case "seal":
|
||||
if rand.Reader == additionalSource {
|
||||
warning = "no seal/entropy augmentation available, using platform entropy source"
|
||||
}
|
||||
case "platform", "all", "":
|
||||
default:
|
||||
return logical.ErrorResponse("unsupported entropy source %q; must be \"platform\" or \"seal\", or \"all\"", source), nil
|
||||
}
|
||||
|
||||
switch format {
|
||||
|
|
@ -61,33 +88,30 @@ func HandleRandomAPI(d *framework.FieldData, additionalSource io.Reader) (*logic
|
|||
}
|
||||
|
||||
var randBytes []byte
|
||||
var warning string
|
||||
switch source {
|
||||
case "", "platform":
|
||||
randBytes, err = uuid.GenerateRandomBytes(bytes)
|
||||
if len(drbg) > 0 {
|
||||
// right now only one value for drbg besides unset is possible, so we assume it was validated
|
||||
// above
|
||||
|
||||
// Seed the drbg from source, but use it to generate byes
|
||||
var seed []byte
|
||||
// HMACDRBG wants a seed at least 3x the security level
|
||||
seed, err = getEntropy(source, (256*3)/8, additionalSource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "seal":
|
||||
if rand.Reader == additionalSource {
|
||||
warning = "no seal/entropy augmentation available, using platform entropy source"
|
||||
}
|
||||
randBytes, err = uuid.GenerateRandomBytesWithReader(bytes, additionalSource)
|
||||
case "all":
|
||||
randBytes, err = uuid.GenerateRandomBytes(bytes)
|
||||
if err == nil && rand.Reader != additionalSource {
|
||||
var sealBytes []byte
|
||||
sealBytes, err = uuid.GenerateRandomBytesWithReader(bytes, additionalSource)
|
||||
if err == nil {
|
||||
randBytes, err = xor.XORBytes(sealBytes, randBytes)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return logical.ErrorResponse("unsupported entropy source %q; must be \"platform\" or \"seal\", or \"all\"", source), nil
|
||||
}
|
||||
drbg := hmacdrbg.NewHmacDrbg(256, seed, []byte("api-random-with-hmac-drbg"))
|
||||
reader := hmacdrbg.NewHmacDrbgReader(drbg)
|
||||
randBytes = make([]byte, bytes)
|
||||
_, err = io.ReadFull(reader, randBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
randBytes, err = getEntropy(source, bytes, additionalSource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var retStr string
|
||||
switch format {
|
||||
|
|
@ -109,6 +133,32 @@ func HandleRandomAPI(d *framework.FieldData, additionalSource io.Reader) (*logic
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
func getEntropy(source string, bytes int, additionalSource io.Reader) ([]byte, error) {
|
||||
var randBytes []byte
|
||||
var err error
|
||||
switch source {
|
||||
case "", "platform":
|
||||
randBytes, err = uuid.GenerateRandomBytes(bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "seal":
|
||||
randBytes, err = uuid.GenerateRandomBytesWithReader(bytes, additionalSource)
|
||||
case "all":
|
||||
randBytes, err = uuid.GenerateRandomBytes(bytes)
|
||||
if err == nil && rand.Reader != additionalSource {
|
||||
var sealBytes []byte
|
||||
sealBytes, err = uuid.GenerateRandomBytesWithReader(bytes, additionalSource)
|
||||
if err == nil {
|
||||
randBytes, err = xor.XORBytes(sealBytes, randBytes)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported entropy source %q; must be \"platform\" or \"seal\", or \"all\"", source)
|
||||
}
|
||||
return randBytes, err
|
||||
}
|
||||
|
||||
func isValidSource(s string) bool {
|
||||
switch s {
|
||||
case "", "platform", "seal", "all":
|
||||
|
|
|
|||
|
|
@ -2586,6 +2586,12 @@ func (b *SystemBackend) toolsPaths() []*framework.Path {
|
|||
Default: "platform",
|
||||
Description: `Which system to source random data from, ether "platform", "seal", or "all".`,
|
||||
},
|
||||
"drbg": {
|
||||
Type: framework.TypeString,
|
||||
Default: "",
|
||||
Description: "If set, seed a secure DRBG from the source and use it to generate the bytes. This can be more performant when using the seal source." +
|
||||
" Possible values are unset (don't use a DRBG), \"auto\" and \"hmacdrbg\" which are equivalent.",
|
||||
},
|
||||
},
|
||||
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
|
|
|
|||
|
|
@ -4377,7 +4377,7 @@ func TestSystemBackend_ToolsRandom(t *testing.T) {
|
|||
}
|
||||
if errExpected {
|
||||
if !resp.IsError() {
|
||||
t.Fatalf("bad: got error response: %#v", *resp)
|
||||
t.Fatalf("bad: got no error response: %#v", *resp)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
|
|
@ -4435,6 +4435,13 @@ func TestSystemBackend_ToolsRandom(t *testing.T) {
|
|||
req.Data["format"] = "hex"
|
||||
doRequest(req, false, "hex", 24)
|
||||
|
||||
// Test PRNG with large output
|
||||
req.Path = "tools/random/1024768"
|
||||
req.Data["format"] = "hex"
|
||||
req.Data["drbg"] = "hmacdrbg"
|
||||
doRequest(req, false, "hex", 1024768)
|
||||
delete(req.Data, "drbg")
|
||||
|
||||
// Test bad input/format
|
||||
req.Path = "tools/random"
|
||||
req.Data["format"] = "base92"
|
||||
|
|
@ -4445,7 +4452,12 @@ func TestSystemBackend_ToolsRandom(t *testing.T) {
|
|||
doRequest(req, true, "", 0)
|
||||
|
||||
req.Data["format"] = "hex"
|
||||
req.Data["bytes"] = maxBytes + 1
|
||||
req.Data["bytes"] = random.APIMaxBytes + 1
|
||||
doRequest(req, true, "", 0)
|
||||
|
||||
req.Path = "tools/random/seal"
|
||||
req.Data["format"] = "hex"
|
||||
req.Data["bytes"] = random.SealMaxBytes + 1
|
||||
doRequest(req, true, "", 0)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue