vrl/stdlib/
random_bytes.rs

1use crate::compiler::prelude::*;
2use rand::{RngCore, thread_rng};
3
4const MAX_LENGTH: i64 = 1024 * 64;
5const LENGTH_TOO_LARGE_ERR: &str = "Length is too large. Maximum is 64k";
6const LENGTH_TOO_SMALL_ERR: &str = "Length cannot be negative";
7
8fn random_bytes(length: Value) -> Resolved {
9    let mut output = vec![0_u8; get_length(length)?];
10
11    // ThreadRng is a cryptographically secure generator
12    thread_rng().fill_bytes(&mut output);
13
14    Ok(Value::Bytes(Bytes::from(output)))
15}
16
17#[derive(Clone, Copy, Debug)]
18pub struct RandomBytes;
19
20impl Function for RandomBytes {
21    fn identifier(&self) -> &'static str {
22        "random_bytes"
23    }
24
25    fn usage(&self) -> &'static str {
26        "A cryptographically secure random number generator. Returns a string value containing the number of random bytes requested."
27    }
28
29    fn category(&self) -> &'static str {
30        Category::Random.as_ref()
31    }
32
33    fn internal_failure_reasons(&self) -> &'static [&'static str] {
34        &[
35            "`length` is negative.",
36            "`length` is larger than the maximum value (64k).",
37        ]
38    }
39
40    fn return_kind(&self) -> u16 {
41        kind::BYTES
42    }
43
44    fn parameters(&self) -> &'static [Parameter] {
45        const PARAMETERS: &[Parameter] = &[Parameter::required(
46            "length",
47            kind::INTEGER,
48            "The number of bytes to generate. Must not be larger than 64k.",
49        )];
50        PARAMETERS
51    }
52
53    fn examples(&self) -> &'static [Example] {
54        &[
55            example! {
56                title: "Generate random base 64 encoded bytes",
57                source: "encode_base64(random_bytes(16))",
58                result: Ok("LNu0BBgUbh7XAlXbjSOomQ=="),
59                deterministic: false,
60            },
61            example! {
62                title: "Generate 16 random bytes",
63                source: "length(random_bytes(16))",
64                result: Ok("16"),
65            },
66        ]
67    }
68
69    fn compile(
70        &self,
71        state: &state::TypeState,
72        _ctx: &mut FunctionCompileContext,
73        arguments: ArgumentList,
74    ) -> Compiled {
75        let length = arguments.required("length");
76
77        if let Some(literal) = length.resolve_constant(state) {
78            // check if length is valid
79            let _: usize =
80                get_length(literal.clone()).map_err(|err| function::Error::InvalidArgument {
81                    keyword: "length",
82                    value: literal,
83                    error: err,
84                })?;
85        }
86
87        Ok(RandomBytesFn { length }.as_expr())
88    }
89}
90
91fn get_length(value: Value) -> std::result::Result<usize, &'static str> {
92    let length = value.try_integer().expect("length must be an integer");
93    if length < 0 {
94        return Err(LENGTH_TOO_SMALL_ERR);
95    }
96    if length > MAX_LENGTH {
97        return Err(LENGTH_TOO_LARGE_ERR);
98    }
99    // TODO consider removal options
100    #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
101    Ok(length as usize)
102}
103
104#[derive(Debug, Clone)]
105struct RandomBytesFn {
106    length: Box<dyn Expression>,
107}
108
109impl FunctionExpression for RandomBytesFn {
110    fn resolve(&self, ctx: &mut Context) -> Resolved {
111        let length = self.length.resolve(ctx)?;
112        random_bytes(length)
113    }
114
115    fn type_def(&self, state: &state::TypeState) -> TypeDef {
116        match self.length.resolve_constant(state) {
117            None => TypeDef::bytes().fallible(),
118            Some(value) => {
119                if get_length(value).is_ok() {
120                    TypeDef::bytes()
121                } else {
122                    TypeDef::bytes().fallible()
123                }
124            }
125        }
126    }
127}