1#[cfg(feature = "wasm")]
7extern crate alloc;
8#[cfg(feature = "wasm")]
9use alloc::{
10 string::{
11 String,
12 ToString,
13 },
14 vec::Vec,
15};
16
17#[cfg(feature = "wasm")]
18use serde_json;
20#[cfg(feature = "wasm")]
21use wasm_bindgen::prelude::*;
22
23use crate::api::{
24 Algorithm,
25 AlgorithmCategory,
26 CryptoProvider,
27};
28use crate::providers::LibQCryptoProvider;
30use crate::security::SecurityValidator;
31
32#[cfg_attr(feature = "wasm", wasm_bindgen)]
40pub struct WasmProviderManager {
41 provider: LibQCryptoProvider,
42 security_validator: SecurityValidator,
43}
44
45impl WasmProviderManager {
46 pub fn new() -> WasmProviderManager {
48 WasmProviderManager {
49 provider: LibQCryptoProvider::new()
50 .unwrap_or_else(|_| LibQCryptoProvider::new().unwrap()),
51 security_validator: SecurityValidator::new()
52 .unwrap_or_else(|_| SecurityValidator::new().unwrap()),
53 }
54 }
55}
56
57impl Default for WasmProviderManager {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63impl WasmProviderManager {
64 pub fn get_provider_info(&self) -> String {
66 #[cfg(feature = "wasm")]
67 {
68 serde_json::json!({
69 "name": "lib-Q Crypto Provider",
70 "version": crate::VERSION,
71 "description": "Post-Quantum Cryptography Provider",
72 "features": {
73 "kem": true,
74 "signature": true,
75 "hash": true,
76 "aead": true,
77 "security_hardened": true,
78 "post_quantum": true
79 },
80 "security_levels": [128, 192, 256],
81 "algorithms": {
82 "kem": self.get_kem_algorithms(),
83 "signature": self.get_signature_algorithms(),
84 "hash": self.get_hash_algorithms(),
85 "aead": self.get_aead_algorithms()
86 }
87 })
88 .to_string()
89 }
90 #[cfg(not(feature = "wasm"))]
91 {
92 "{}".to_string()
93 }
94 }
95
96 pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
98 let algorithm = match self.parse_algorithm(algorithm) {
100 Ok(alg) => alg,
101 Err(_) => return false,
102 };
103
104 match algorithm.category() {
106 AlgorithmCategory::Kem => self.provider.kem().is_some(),
107 AlgorithmCategory::Signature => self.provider.signature().is_some(),
108 AlgorithmCategory::Hash => self.provider.hash().is_some(),
109 AlgorithmCategory::Aead => self.provider.aead().is_some(),
110 AlgorithmCategory::PrivacyProtocol => false,
111 }
112 }
113
114 pub fn get_algorithm_info(&self, algorithm: &str) -> Result<JsValue, JsValue> {
116 let algorithm = self.parse_algorithm(algorithm)?;
117
118 #[cfg(feature = "wasm")]
119 {
120 let info = Self::algorithm_info_json(algorithm);
121
122 match serde_wasm_bindgen::to_value(&info) {
123 Ok(value) => Ok(value),
124 Err(_) => Err(JsValue::from_str("Serialization error")),
125 }
126 }
127 #[cfg(not(feature = "wasm"))]
128 {
129 Err(JsValue::from_str("WASM feature not enabled"))
130 }
131 }
132
133 fn algorithm_info_json(algorithm: Algorithm) -> serde_json::Value {
143 let constants = crate::security::SecurityConstants::new();
144
145 let public_key = constants.get_expected_key_size(algorithm, false).ok();
146 let secret_key = constants.get_expected_key_size(algorithm, true).ok();
147 let signature = constants.get_expected_signature_size(algorithm).ok();
148 let ciphertext = constants.get_expected_ciphertext_size(algorithm).ok();
149
150 let max_message_size = if algorithm.category() == AlgorithmCategory::Aead {
153 Some(constants.max_aead_message_size())
154 } else {
155 None
156 };
157
158 serde_json::json!({
159 "name": algorithm.to_string(),
160 "category": algorithm.category().to_string(),
161 "security_level": serde_json::Value::Null,
165 "key_sizes": {
166 "public_key": public_key,
167 "secret_key": secret_key,
168 "signature": signature,
169 "ciphertext": ciphertext
170 },
171 "message_limits": {
172 "max_size": max_message_size
173 },
174 "features": {
175 "kem": algorithm.category() == AlgorithmCategory::Kem,
176 "signature": algorithm.category() == AlgorithmCategory::Signature,
177 "hash": algorithm.category() == AlgorithmCategory::Hash,
178 "aead": algorithm.category() == AlgorithmCategory::Aead,
179 "privacy_protocol": algorithm.category() == AlgorithmCategory::PrivacyProtocol
180 }
181 })
182 }
183
184 pub fn get_all_algorithms(&self) -> String {
186 #[cfg(feature = "wasm")]
187 {
188 let algorithms = serde_json::json!({
189 "kem": self.get_kem_algorithms(),
190 "signature": self.get_signature_algorithms(),
191 "hash": self.get_hash_algorithms(),
192 "aead": self.get_aead_algorithms()
193 });
194 algorithms.to_string()
195 }
196 #[cfg(not(feature = "wasm"))]
197 {
198 "{}".to_string()
199 }
200 }
201
202 pub fn get_kem_algorithms(&self) -> Vec<String> {
204 #[allow(unused_mut)] let mut algorithms = alloc::vec![
206 "ml-kem-512".to_string(),
207 "ml-kem-768".to_string(),
208 "ml-kem-1024".to_string(),
209 ];
210
211 algorithms
212 }
213
214 pub fn get_signature_algorithms(&self) -> Vec<String> {
216 crate::wasm::conversions::WASM_SIGNATURE_ALGORITHM_IDS
217 .iter()
218 .map(|s| (*s).to_string())
219 .collect()
220 }
221
222 pub fn get_hash_algorithms(&self) -> Vec<String> {
224 alloc::vec![
225 "sha3-224".to_string(),
226 "sha3-256".to_string(),
227 "sha3-384".to_string(),
228 "sha3-512".to_string(),
229 "shake128".to_string(),
230 "shake256".to_string(),
231 ]
232 }
233
234 pub fn get_aead_algorithms(&self) -> Vec<String> {
236 let algorithms = alloc::vec!["saturnin".to_string(), "shake256-aead".to_string(),];
237
238 algorithms
239 }
240
241 pub fn validate_algorithm_params(
243 &self,
244 algorithm: &str,
245 key_size: Option<usize>,
246 message_size: Option<usize>,
247 nonce_size: Option<usize>,
248 ) -> Result<bool, JsValue> {
249 let algorithm = self.parse_algorithm(algorithm)?;
250
251 if let Some(size) = key_size {
253 if size == 0 {
254 return Err(JsValue::from_str("Invalid algorithm key: empty key"));
255 }
256 let test_key = (0..size).map(|_| 0u8).collect::<Vec<u8>>();
258 self.security_validator
259 .validate_key_size(algorithm, &test_key, true)
260 .map_err(crate::wasm::error::error_to_js_value)?;
261 }
262
263 if let Some(size) = message_size {
264 if size == 0 {
265 return Err(JsValue::from_str("Invalid message size: empty data"));
266 }
267 let test_message = (0..size).map(|_| 0u8).collect::<Vec<u8>>();
269 if algorithm.supports_category(AlgorithmCategory::Aead) {
270 self.security_validator
271 .validate_aead_message(&test_message)
272 .map_err(crate::wasm::error::error_to_js_value)?;
273 } else {
274 self.security_validator
275 .validate_hash_input(&test_message)
276 .map_err(crate::wasm::error::error_to_js_value)?;
277 }
278 }
279
280 if let Some(size) = nonce_size {
281 if size == 0 {
282 return Err(JsValue::from_str("Invalid nonce size: empty nonce"));
283 }
284 let test_nonce = (0..size).map(|_| 0u8).collect::<Vec<u8>>();
286 self.security_validator
287 .validate_nonce(&test_nonce)
288 .map_err(crate::wasm::error::error_to_js_value)?;
289 }
290
291 Ok(true)
292 }
293
294 pub fn get_security_recommendations(&self) -> String {
296 #[cfg(feature = "wasm")]
297 {
298 serde_json::json!({
299 "general": {
300 "use_authenticated_encryption": true,
301 "validate_all_inputs": true,
302 "use_secure_random": true,
303 "protect_secret_keys": true,
304 "rotate_keys_regularly": true
305 },
306 "kem": {
307 "recommended_algorithms": ["ml-kem-768", "ml-kem-1024"],
308 "key_rotation": "Every 90 days",
309 "security_level": "Minimum 192-bit"
310 },
311 "signature": {
312 "recommended_algorithms": ["ml-dsa-65", "ml-dsa-87"],
313 "key_rotation": "Every 90 days",
314 "security_level": "Minimum 192-bit"
315 },
316 "hash": {
317 "recommended_algorithms": ["sha3-256", "sha3-384"],
318 "security_level": "Minimum 256-bit"
319 },
320 "aead": {
321 "recommended_algorithms": ["saturnin", "shake256-aead"],
322 "nonce_requirements": "Unique per key",
323 "security_level": "Minimum 256-bit"
324 }
325 })
326 .to_string()
327 }
328 #[cfg(not(feature = "wasm"))]
329 {
330 "{}".to_string()
331 }
332 }
333
334 pub fn get_performance_benchmarks(&self) -> String {
336 #[cfg(feature = "wasm")]
337 {
338 serde_json::json!({
339 "note": "Performance benchmarks are environment-dependent",
340 "recommendations": {
341 "kem": {
342 "fastest": "ml-kem-512",
343 "most_secure": "ml-kem-1024",
344 "balanced": "ml-kem-768"
345 },
346 "signature": {
347 "fastest": "ml-dsa-44",
348 "most_secure": "ml-dsa-87",
349 "balanced": "ml-dsa-65"
350 },
351 "hash": {
352 "fastest": "sha3-224",
353 "most_secure": "sha3-512",
354 "balanced": "sha3-256"
355 }
356 }
357 })
358 .to_string()
359 }
360 #[cfg(not(feature = "wasm"))]
361 {
362 "{}".to_string()
363 }
364 }
365
366 fn parse_algorithm(&self, algorithm: &str) -> Result<Algorithm, crate::error::Error> {
368 crate::wasm::error::parse_algorithm_wasm(algorithm).map_err(|_| {
369 crate::error::Error::InvalidAlgorithm {
370 algorithm: "Invalid algorithm name",
371 }
372 })
373 }
374}
375
376#[cfg_attr(feature = "wasm", wasm_bindgen)]
383pub struct WasmProviderFactory;
384
385#[cfg_attr(feature = "wasm", wasm_bindgen)]
386impl WasmProviderFactory {
387 #[cfg_attr(feature = "wasm", wasm_bindgen)]
389 pub fn create_provider_manager() -> WasmProviderManager {
390 WasmProviderManager::new()
391 }
392
393 #[cfg_attr(feature = "wasm", wasm_bindgen)]
395 pub fn create_provider_manager_with_config(
396 config: &str,
397 ) -> Result<WasmProviderManager, JsValue> {
398 #[cfg(feature = "wasm")]
399 {
400 let _config: serde_json::Value = match serde_json::from_str(config) {
402 Ok(config) => config,
403 Err(_) => {
404 return Err(JsValue::from_str("Configuration parsing error"));
405 }
406 };
407
408 Ok(WasmProviderManager::new())
411 }
412 #[cfg(not(feature = "wasm"))]
413 {
414 Err(JsValue::from_str("WASM feature not enabled"))
415 }
416 }
417
418 #[cfg_attr(feature = "wasm", wasm_bindgen)]
420 pub fn get_available_providers() -> String {
421 #[cfg(feature = "wasm")]
422 {
423 serde_json::json!({
424 "providers": [
425 {
426 "name": "lib-q-crypto",
427 "description": "Default lib-Q cryptographic provider",
428 "features": ["kem", "signature", "hash", "aead"],
429 "security_levels": [128, 192, 256]
430 }
431 ]
432 })
433 .to_string()
434 }
435 #[cfg(not(feature = "wasm"))]
436 {
437 "{}".to_string()
438 }
439 }
440
441 #[cfg_attr(feature = "wasm", wasm_bindgen)]
443 pub fn validate_provider_config(config: &str) -> Result<bool, JsValue> {
444 #[cfg(feature = "wasm")]
445 {
446 let _config: serde_json::Value = match serde_json::from_str(config) {
447 Ok(config) => config,
448 Err(_) => {
449 return Err(JsValue::from_str("Configuration parsing error"));
450 }
451 };
452
453 Ok(true)
455 }
456 #[cfg(not(feature = "wasm"))]
457 {
458 Err(JsValue::from_str("WASM feature not enabled"))
459 }
460 }
461}
462
463#[cfg(test)]
464mod tests {
465 use super::*;
466
467 #[test]
474 fn test_algorithm_info_reports_real_distinct_key_sizes() {
475 let kem_info = WasmProviderManager::algorithm_info_json(Algorithm::MlKem512);
476 let sig_info = WasmProviderManager::algorithm_info_json(Algorithm::MlDsa87);
477
478 let kem_pk = kem_info["key_sizes"]["public_key"].as_u64().unwrap();
479 let sig_pk = sig_info["key_sizes"]["public_key"].as_u64().unwrap();
480
481 assert_ne!(
483 kem_pk, sig_pk,
484 "ML-KEM-512 and ML-DSA-87 must not report the same public key size"
485 );
486
487 let constants = crate::security::SecurityConstants::new();
489 assert_eq!(
490 kem_pk as usize,
491 constants
492 .get_expected_key_size(Algorithm::MlKem512, false)
493 .unwrap()
494 );
495 assert_eq!(
496 sig_pk as usize,
497 constants
498 .get_expected_key_size(Algorithm::MlDsa87, false)
499 .unwrap()
500 );
501
502 let hash_info = WasmProviderManager::algorithm_info_json(Algorithm::Sha3_256);
504 assert!(hash_info["key_sizes"]["public_key"].is_null());
505 assert!(hash_info["key_sizes"]["secret_key"].is_null());
506 assert!(hash_info["key_sizes"]["signature"].is_null());
507
508 assert!(kem_info["security_level"].is_null());
510 assert!(sig_info["security_level"].is_null());
511 }
512
513 #[test]
514 #[cfg(target_arch = "wasm32")]
515 fn test_wasm_provider_manager_creation() {
516 let manager = WasmProviderManager::new();
517 let info = manager.get_provider_info();
518 assert!(info.contains("lib-Q") || info == "{}");
519 }
520
521 #[test]
522 #[cfg(target_arch = "wasm32")]
523 fn test_wasm_provider_factory() {
524 let manager = WasmProviderFactory::create_provider_manager();
525 assert!(manager.is_algorithm_supported("sha3-256"));
526 }
527
528 #[test]
529 #[cfg(target_arch = "wasm32")]
530 fn test_algorithm_support() {
531 let manager = WasmProviderManager::new();
532 assert!(manager.is_algorithm_supported("sha3-256"));
533 assert!(!manager.is_algorithm_supported("invalid-algorithm"));
534 }
535
536 #[test]
537 #[cfg(target_arch = "wasm32")]
538 fn test_algorithm_info() {
539 let manager = WasmProviderManager::new();
540 let info = manager.get_algorithm_info("sha3-256");
541 assert!(info.is_ok());
542 }
543
544 #[test]
545 #[cfg(target_arch = "wasm32")]
546 fn test_security_recommendations() {
547 let manager = WasmProviderManager::new();
548 let recommendations = manager.get_security_recommendations();
549 assert!(
550 recommendations.contains("use_authenticated_encryption") || recommendations == "{}"
551 );
552 }
553}