1use bitwarden_error::bitwarden_error;
4use rand::{Rng, RngExt};
5use thiserror::Error;
6
7use crate::rng;
8
9#[bitwarden_error(flat)]
11#[derive(Debug, Error)]
12pub enum GenBytesError {
13 #[error("gen_bytes() is limited to 1KiB; requested {requested} bytes")]
15 TooManyBytes {
16 requested: u32,
18 },
19}
20
21#[bitwarden_error(flat)]
23#[derive(Debug, Error)]
24pub enum GenRangeError {
25 #[error("Invalid range: min ({min}) must not exceed max ({max})")]
27 InvalidRange {
28 min: u32,
30 max: u32,
32 },
33}
34
35#[derive(Default)]
37#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
38#[bitwarden_ffi::wasm_object]
39pub struct SdkRandomNumberClient;
40
41#[bitwarden_ffi::wasm_export]
42#[cfg_attr(feature = "uniffi", uniffi::export)]
43impl SdkRandomNumberClient {
44 #[cfg_attr(feature = "wasm", wasm_bindgen(constructor))]
46 #[cfg_attr(feature = "uniffi", uniffi::constructor)]
47 pub fn new() -> Self {
48 SdkRandomNumberClient
52 }
53
54 pub fn gen_bytes(&self, len: u32) -> Result<Vec<u8>, GenBytesError> {
59 if len > 1024 {
60 return Err(GenBytesError::TooManyBytes { requested: len });
61 }
62
63 let mut buf = vec![0u8; len as usize];
64 rng().fill_bytes(&mut buf);
65 Ok(buf)
66 }
67
68 pub fn gen_uuid(&self) -> String {
72 let mut bytes = [0u8; 16];
73 rng().fill_bytes(&mut bytes);
74 uuid::Builder::from_random_bytes(bytes)
75 .with_variant(uuid::Variant::RFC4122)
76 .with_version(uuid::Version::Random)
77 .into_uuid()
78 .to_string()
79 }
80
81 pub fn gen_range(&self, min: u32, max: u32) -> Result<u32, GenRangeError> {
85 if min > max {
86 return Err(GenRangeError::InvalidRange { min, max });
87 }
88 Ok(rng().random_range(min..=max))
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use uuid::Uuid;
95
96 use super::*;
97
98 #[test]
99 fn gen_bytes_returns_requested_length() {
100 let client = SdkRandomNumberClient::new();
101 assert_eq!(client.gen_bytes(0).unwrap().len(), 0);
102 assert_eq!(client.gen_bytes(1).unwrap().len(), 1);
103 assert_eq!(client.gen_bytes(32).unwrap().len(), 32);
104 assert_eq!(client.gen_bytes(1024).unwrap().len(), 1024);
106 }
107
108 #[test]
109 fn gen_bytes_is_random() {
110 let client = SdkRandomNumberClient::new();
111 assert_ne!(client.gen_bytes(32).unwrap(), client.gen_bytes(32).unwrap());
113 }
114
115 #[test]
116 fn gen_bytes_errors_above_1_kib() {
117 let err = SdkRandomNumberClient::new().gen_bytes(1025).unwrap_err();
118 assert!(matches!(
119 err,
120 GenBytesError::TooManyBytes { requested: 1025 }
121 ));
122 }
123
124 #[test]
125 fn gen_uuid_is_a_valid_random_v4_uuid() {
126 let client = SdkRandomNumberClient::new();
127 let uuid = Uuid::parse_str(&client.gen_uuid()).expect("a valid UUID string");
128 assert_eq!(uuid.get_version(), Some(uuid::Version::Random));
129 assert_eq!(uuid.get_variant(), uuid::Variant::RFC4122);
130 }
131
132 #[test]
133 fn gen_uuid_is_distinct() {
134 let client = SdkRandomNumberClient::new();
135 assert_ne!(client.gen_uuid(), client.gen_uuid());
136 }
137
138 #[test]
139 fn gen_range_stays_within_inclusive_bounds() {
140 let client = SdkRandomNumberClient::new();
141 for _ in 0..1000 {
142 let n = client.gen_range(10, 20).unwrap();
143 assert!((10..=20).contains(&n));
144 }
145 }
146
147 #[test]
148 fn gen_range_with_single_value_is_that_value() {
149 let client = SdkRandomNumberClient::new();
150 assert_eq!(client.gen_range(7, 7).unwrap(), 7);
151 }
152
153 #[test]
154 fn gen_range_errors_when_min_exceeds_max() {
155 let err = SdkRandomNumberClient::new().gen_range(20, 10).unwrap_err();
156 assert!(matches!(
157 err,
158 GenRangeError::InvalidRange { min: 20, max: 10 }
159 ));
160 }
161}