diff --git a/crypto.go b/crypto.go index 7ae3991c..ca5b316e 100644 --- a/crypto.go +++ b/crypto.go @@ -60,6 +60,14 @@ func htpasswd(username string, password string) string { return fmt.Sprintf("%s:%s", username, hash) } +func randBytes(count int) (string, error) { + buf := make([]byte, count) + if _, err := rand.Read(buf); err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(buf), nil +} + // uuidv4 provides a safe and secure UUID v4 implementation func uuidv4() string { return uuid.New().String() diff --git a/crypto_test.go b/crypto_test.go index c9032412..047231ac 100644 --- a/crypto_test.go +++ b/crypto_test.go @@ -141,6 +141,31 @@ func TestGenPrivateKey(t *testing.T) { } } +func TestRandBytes(t *testing.T) { + tpl := `{{randBytes 12}}` + out, err := runRaw(tpl, nil) + if err != nil { + t.Error(err) + } + + bytes, err := base64.StdEncoding.DecodeString(out) + if err != nil { + t.Error(err) + } + if len(bytes) != 12 { + t.Error("Expected 12 base64-encoded bytes") + } + + out2, err := runRaw(tpl, nil) + if err != nil { + t.Error(err) + } + + if out == out2 { + t.Error("Expected subsequent randBytes to be different") + } +} + func TestUUIDGeneration(t *testing.T) { tpl := `{{uuidv4}}` out, err := runRaw(tpl, nil) diff --git a/docs/crypto.md b/docs/crypto.md index 3808f918..9784fa15 100644 --- a/docs/crypto.md +++ b/docs/crypto.md @@ -39,6 +39,16 @@ htpasswd "myUser" "myPassword" Note that it is insecure to store the password directly in the template. +## randBytes + +The `randBytes` function accepts a count `N` and generates a cryptographically +secure (uses ```crypto/rand```) random sequence of `N` bytes. The sequence is +returned as a base64 encoded string. + +``` +randBytes 24 +``` + ## derivePassword The `derivePassword` function can be used to derive a specific password based on diff --git a/functions.go b/functions.go index c16e9c3e..ce9effa8 100644 --- a/functions.go +++ b/functions.go @@ -80,6 +80,7 @@ var nonhermeticFunctions = []string{ "randAlpha", "randAscii", "randNumeric", + "randBytes", "uuidv4", // OS @@ -308,6 +309,7 @@ var genericMap = map[string]interface{}{ "genSignedCert": generateSignedCertificate, "encryptAES": encryptAES, "decryptAES": decryptAES, + "randBytes": randBytes, // UUIDs: "uuidv4": uuidv4,