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