1#[cfg(feature = "wasm")]
7use js_sys::Uint8Array;
8#[cfg(feature = "wasm")]
9use serde_json;
10#[cfg(feature = "wasm")]
11use wasm_bindgen::prelude::*;
12
13#[cfg(feature = "wasm")]
14extern crate alloc;
15#[cfg(feature = "wasm")]
16use alloc::{
17 collections::BTreeMap,
18 format,
19 string::{
20 String,
21 ToString,
22 },
23 vec::Vec,
24};
25
26#[cfg(feature = "wasm")]
27use crate::wasm::conversions::WASM_SIGNATURE_ALGORITHM_IDS;
28
29#[cfg(feature = "wasm")]
36#[wasm_bindgen]
37pub fn random_bytes(length: usize) -> Result<Uint8Array, String> {
38 if length == 0 {
39 return Err("Invalid length: 0".to_string());
40 }
41
42 const MAX_RANDOM_SIZE: usize = 1024 * 1024; if length > MAX_RANDOM_SIZE {
44 return Err(format!(
45 "Length {} exceeds maximum {}",
46 length, MAX_RANDOM_SIZE
47 ));
48 }
49
50 let mut bytes = alloc::vec![0u8; length];
52 getrandom::fill(&mut bytes)
53 .map_err(|e| format!("Failed to generate secure random bytes: {}", e))?;
54
55 let array = Uint8Array::new_with_length(length as u32);
57 for (i, &byte) in bytes.iter().enumerate() {
58 array.set_index(i as u32, byte);
59 }
60
61 Ok(array)
62}
63
64#[cfg(feature = "wasm")]
71#[wasm_bindgen]
72pub fn validate_input(
73 data: &Uint8Array,
74 min_size: Option<usize>,
75 max_size: Option<usize>,
76) -> Result<bool, String> {
77 let length = data.length() as usize;
78
79 if let Some(min) = min_size &&
81 length < min
82 {
83 return Err(format!("Length {} is less than minimum {}", length, min));
84 }
85
86 if let Some(max) = max_size &&
88 length > max
89 {
90 return Err(format!("Length {} exceeds maximum {}", length, max));
91 }
92
93 Ok(true)
94}
95
96#[cfg(feature = "wasm")]
103#[wasm_bindgen]
104pub fn bytes_to_hex(data: &Uint8Array) -> String {
105 let length = data.length() as usize;
106 let mut hex = String::with_capacity(length * 2);
107
108 for i in 0..length {
109 let byte = data.get_index(i as u32);
110 hex.push_str(&format!("{:02x}", byte));
111 }
112
113 hex
114}
115
116#[cfg(feature = "wasm")]
123#[wasm_bindgen]
124pub fn hex_to_bytes(hex: &str) -> Result<Uint8Array, String> {
125 if !hex.len().is_multiple_of(2) {
126 return Err("Invalid hex string length".to_string());
127 }
128
129 let length = hex.len() / 2;
130 let array = Uint8Array::new_with_length(length as u32);
131
132 for (i, chunk) in hex.as_bytes().chunks(2).enumerate() {
133 let hex_str = core::str::from_utf8(chunk).map_err(|_| "Invalid hex character")?;
134
135 let byte = u8::from_str_radix(hex_str, 16).map_err(|_| "Invalid hex character")?;
136
137 array.set_index(i as u32, byte);
138 }
139
140 Ok(array)
141}
142
143#[cfg(feature = "wasm")]
147#[wasm_bindgen]
148pub fn get_library_info() -> String {
149 serde_json::json!({
150 "name": "lib-Q",
151 "version": crate::VERSION,
152 "description": "Post-Quantum Cryptography Library",
153 "features": {
154 "wasm": true,
155 "security_hardened": true,
156 "post_quantum": true
157 }
158 })
159 .to_string()
160}
161
162#[cfg(feature = "wasm")]
166#[wasm_bindgen]
167pub fn is_feature_available(feature: &str) -> bool {
168 #[allow(clippy::match_like_matches_macro)]
169 match feature {
170 "ml-kem" => cfg!(feature = "ml-kem"),
171 "ml-dsa" => cfg!(feature = "ml-dsa"),
172 "fn-dsa" => cfg!(feature = "fn-dsa"),
173 "slh-dsa" => cfg!(feature = "slh-dsa"),
174 "saturnin" => cfg!(feature = "saturnin"),
175 "hash" => cfg!(feature = "hash"),
176 "wasm" => true,
177 _ => false,
178 }
179}
180
181#[cfg(feature = "wasm")]
185#[wasm_bindgen]
186pub fn get_supported_algorithms() -> String {
187 let mut algorithms = BTreeMap::new();
190
191 let mut kem_algorithms = Vec::new();
193 kem_algorithms.extend(&["ml-kem-512", "ml-kem-768", "ml-kem-1024"]);
194 algorithms.insert("kem", kem_algorithms);
195
196 algorithms.insert("signature", WASM_SIGNATURE_ALGORITHM_IDS.to_vec());
198
199 let hash_algorithms = alloc::vec![
201 "sha3-224", "sha3-256", "sha3-384", "sha3-512", "shake128", "shake256",
202 ];
203 algorithms.insert("hash", hash_algorithms);
204
205 let mut aead_algorithms = Vec::new();
207 aead_algorithms.extend(&["saturnin", "shake256-aead"]);
208 algorithms.insert("aead", aead_algorithms);
209
210 serde_json::to_string(&algorithms).unwrap_or_else(|_| "{}".to_string())
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 #[test]
218 fn test_feature_availability() {
219 assert!(is_feature_available("wasm"));
221 }
223
224 #[test]
225 fn test_library_info() {
226 let info = get_library_info();
227 assert!(info.contains("lib-Q"));
228 assert!(info.contains("version"));
229 }
230
231 #[test]
232 fn test_supported_algorithms() {
233 let algorithms = get_supported_algorithms();
234 assert!(algorithms.contains("hash"));
235 assert!(algorithms.contains("sha3-256"));
236 }
237}