119 lines
3.0 KiB
Go
119 lines
3.0 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package random
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
|
|
"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
|
|
|
|
func HandleRandomAPI(d *framework.FieldData, additionalSource io.Reader) (*logical.Response, error) {
|
|
bytes := 0
|
|
// Parsing is convoluted here, but allows operators to ACL both source and byte count
|
|
maybeUrlBytes := d.Raw["urlbytes"]
|
|
maybeSource := d.Raw["source"]
|
|
source := "platform"
|
|
var err error
|
|
if maybeSource == "" {
|
|
bytes = d.Get("bytes").(int)
|
|
} else if maybeUrlBytes == "" && isValidSource(maybeSource.(string)) {
|
|
source = maybeSource.(string)
|
|
bytes = d.Get("bytes").(int)
|
|
} else if maybeUrlBytes == "" {
|
|
bytes, err = strconv.Atoi(maybeSource.(string))
|
|
if err != nil {
|
|
return logical.ErrorResponse(fmt.Sprintf("error parsing url-set byte count: %s", err)), nil
|
|
}
|
|
} else {
|
|
source = maybeSource.(string)
|
|
bytes, err = strconv.Atoi(maybeUrlBytes.(string))
|
|
if err != nil {
|
|
return logical.ErrorResponse(fmt.Sprintf("error parsing url-set byte count: %s", err)), nil
|
|
}
|
|
}
|
|
format := d.Get("format").(string)
|
|
|
|
if bytes < 1 {
|
|
return logical.ErrorResponse(`"bytes" cannot be less than 1`), nil
|
|
}
|
|
|
|
if bytes > APIMaxBytes {
|
|
return logical.ErrorResponse(`"bytes" should be less than %d`, APIMaxBytes), nil
|
|
}
|
|
|
|
switch format {
|
|
case "hex":
|
|
case "base64":
|
|
default:
|
|
return logical.ErrorResponse("unsupported encoding format %q; must be \"hex\" or \"base64\"", format), nil
|
|
}
|
|
|
|
var randBytes []byte
|
|
var warning string
|
|
switch source {
|
|
case "", "platform":
|
|
randBytes, err = uuid.GenerateRandomBytes(bytes)
|
|
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
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var retStr string
|
|
switch format {
|
|
case "hex":
|
|
retStr = hex.EncodeToString(randBytes)
|
|
case "base64":
|
|
retStr = base64.StdEncoding.EncodeToString(randBytes)
|
|
}
|
|
|
|
// Generate the response
|
|
resp := &logical.Response{
|
|
Data: map[string]interface{}{
|
|
"random_bytes": retStr,
|
|
},
|
|
}
|
|
if warning != "" {
|
|
resp.Warnings = []string{warning}
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func isValidSource(s string) bool {
|
|
switch s {
|
|
case "", "platform", "seal", "all":
|
|
return true
|
|
}
|
|
return false
|
|
}
|