Skip to main content

lib_q_core/wasm/
utils.rs

1//! WASM utility functions
2//!
3//! This module provides utility functions specifically designed for WASM environments,
4//! including secure random generation, data validation, and helper functions.
5
6#[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/// Generate cryptographically secure random bytes for WASM
30///
31/// This function provides secure random generation in WASM environments:
32/// - Uses the browser's crypto.getRandomValues() API
33/// - Validates input size to prevent DoS attacks
34/// - Returns Uint8Array for direct JavaScript consumption
35#[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; // 1MB limit
43    if length > MAX_RANDOM_SIZE {
44        return Err(format!(
45            "Length {} exceeds maximum {}",
46            length, MAX_RANDOM_SIZE
47        ));
48    }
49
50    // Generate secure random bytes using getrandom (which uses crypto.getRandomValues() in WASM)
51    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    // Convert to Uint8Array for JavaScript consumption
56    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/// Validate input data for WASM operations
65///
66/// This function provides comprehensive input validation:
67/// - Checks for null/undefined values
68/// - Validates data size limits
69/// - Ensures data is not empty when required
70#[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    // Check minimum size
80    if let Some(min) = min_size &&
81        length < min
82    {
83        return Err(format!("Length {} is less than minimum {}", length, min));
84    }
85
86    // Check maximum size
87    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/// Convert bytes to hexadecimal string
97///
98/// This function provides secure hex encoding for WASM:
99/// - Uses constant-time operations where possible
100/// - Handles large data efficiently
101/// - Returns JavaScript string
102#[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/// Convert hexadecimal string to bytes
117///
118/// This function provides secure hex decoding for WASM:
119/// - Validates hex string format
120/// - Handles errors gracefully
121/// - Returns Uint8Array
122#[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/// Get library information for WASM
144///
145/// This function provides library metadata for JavaScript consumption
146#[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/// Check if a feature is available
163///
164/// This function allows JavaScript to check feature availability
165#[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/// Get supported algorithms by category
182///
183/// This function provides algorithm information for JavaScript
184#[cfg(feature = "wasm")]
185#[wasm_bindgen]
186pub fn get_supported_algorithms() -> String {
187    // use crate::api::AlgorithmCategory;
188
189    let mut algorithms = BTreeMap::new();
190
191    // KEM algorithms
192    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    // Signature algorithms (single source: `conversions::WASM_SIGNATURE_ALGORITHM_IDS`)
197    algorithms.insert("signature", WASM_SIGNATURE_ALGORITHM_IDS.to_vec());
198
199    // Hash algorithms
200    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    // AEAD algorithms
206    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        // Test that feature checking works
220        assert!(is_feature_available("wasm"));
221        // Other features depend on actual feature flags
222    }
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}