vrl/stdlib/
random_bytes.rs1use 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 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 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 #[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}