Skip to main content

lib_q_core/wasm/
contexts.rs

1//! WASM-compatible context wrappers
2//!
3//! This module provides WASM-compatible wrappers for all cryptographic contexts,
4//! integrating with the new modular architecture and security validation system.
5
6#[cfg(feature = "wasm")]
7extern crate alloc;
8#[cfg(feature = "wasm")]
9use alloc::{
10    boxed::Box,
11    format,
12    string::{
13        String,
14        ToString,
15    },
16    vec::Vec,
17};
18
19#[cfg(feature = "wasm")]
20use js_sys::Uint8Array;
21#[cfg(feature = "wasm")]
22use serde_json;
23#[cfg(feature = "wasm")]
24use serde_wasm_bindgen;
25#[cfg(feature = "wasm")]
26use wasm_bindgen::prelude::*;
27
28use crate::api::{
29    Algorithm,
30    AlgorithmCategory,
31    CryptoProvider,
32};
33use crate::contexts::{
34    AeadContext,
35    HashContext,
36    KemContext,
37    SignatureContext,
38};
39// use crate::error::Result;
40use crate::providers::LibQCryptoProvider;
41use crate::security::SecurityValidator;
42use crate::traits::{
43    AeadKey,
44    Nonce,
45};
46// Import secure error handling
47use crate::wasm::conversions::WASM_SIGNATURE_ALGORITHM_IDS;
48use crate::wasm::error::{
49    convert_result,
50    error_to_js_value,
51    parse_algorithm_wasm,
52    // secure_serialize,
53};
54
55/// WASM-compatible KEM context wrapper
56///
57/// This wrapper provides JavaScript-compatible bindings for KEM operations:
58/// - Integrates with the new modular architecture
59/// - Includes security validation
60/// - Provides consistent error handling
61/// - Supports all KEM algorithms
62#[cfg_attr(feature = "wasm", wasm_bindgen)]
63pub struct WasmKemContext {
64    inner: KemContext,
65    security_validator: SecurityValidator,
66}
67
68impl WasmKemContext {
69    /// Create a new WASM KEM context with default provider
70    pub fn new() -> WasmKemContext {
71        WasmKemContext {
72            inner: KemContext::with_default_provider(),
73            security_validator: SecurityValidator::new()
74                .unwrap_or_else(|_| SecurityValidator::new().unwrap()),
75        }
76    }
77
78    /// Create a new WASM KEM context with custom provider
79    pub fn with_provider(provider: &WasmCryptoProvider) -> WasmKemContext {
80        WasmKemContext {
81            inner: KemContext::with_provider(Box::new(provider.inner.clone())),
82            security_validator: SecurityValidator::new()
83                .unwrap_or_else(|_| SecurityValidator::new().unwrap()),
84        }
85    }
86}
87
88impl Default for WasmKemContext {
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94impl WasmKemContext {
95    /// Generate a keypair for the specified algorithm
96    ///
97    /// This method provides secure key generation with:
98    /// - Algorithm validation
99    /// - Security level verification
100    /// - Secure random generation
101    /// - Proper error handling
102    pub fn generate_keypair(
103        &mut self,
104        algorithm: &str,
105        randomness: Option<Uint8Array>,
106    ) -> Result<JsValue, JsValue> {
107        // Parse and validate algorithm
108        let algorithm = self
109            .parse_kem_algorithm(algorithm)
110            .map_err(error_to_js_value)?;
111
112        // Validate security level
113        convert_result(
114            self.security_validator
115                .validate_algorithm_category(algorithm, algorithm.category()),
116        )?;
117
118        // Convert randomness if provided
119        let randomness_vec = randomness.map(|rand| rand.to_vec());
120        let randomness_bytes = randomness_vec.as_deref();
121
122        // Generate keypair
123        let keypair = self
124            .inner
125            .generate_keypair(algorithm, randomness_bytes)
126            .map_err(error_to_js_value)?;
127
128        // Return as JavaScript object
129        #[cfg(feature = "wasm")]
130        {
131            let result = serde_json::json!({
132                "public_key": keypair.public_key.data,
133                "secret_key": keypair.secret_key.data,
134                "algorithm": algorithm.to_string(),
135                "security_level": 256 // Placeholder
136            });
137
138            serde_wasm_bindgen::to_value(&result)
139                .map_err(|e| JsValue::from_str(&format!("Serialization error: {:?}", e)))
140        }
141        #[cfg(not(feature = "wasm"))]
142        {
143            Err(JsValue::from_str("WASM feature not enabled"))
144        }
145    }
146
147    /// Encapsulate a shared secret using the given public key
148    ///
149    /// This method provides secure encapsulation with:
150    /// - Public key validation
151    /// - Algorithm verification
152    /// - Security level checking
153    /// - Proper error handling
154    pub fn encapsulate(
155        &self,
156        algorithm: &str,
157        public_key_data: &Uint8Array,
158        randomness: Option<Uint8Array>,
159    ) -> Result<JsValue, JsValue> {
160        // Parse and validate algorithm
161        let algorithm = self
162            .parse_kem_algorithm(algorithm)
163            .map_err(error_to_js_value)?;
164
165        // Validate public key size (simplified)
166        if public_key_data.length() == 0 {
167            return Err(JsValue::from_str("Invalid KEM public key: empty key"));
168        }
169
170        // Convert randomness if provided
171        let randomness_vec = randomness.map(|rand| rand.to_vec());
172        let randomness_bytes = randomness_vec.as_deref();
173
174        // Create public key using proper constructor
175        let public_key = crate::traits::KemPublicKey::new(public_key_data.to_vec());
176
177        // Encapsulate
178        let (ciphertext, shared_secret) = self
179            .inner
180            .encapsulate(algorithm, &public_key, randomness_bytes)
181            .map_err(error_to_js_value)?;
182
183        // Return as JavaScript object
184        #[cfg(feature = "wasm")]
185        {
186            let result = serde_json::json!({
187                "ciphertext": ciphertext,
188                "shared_secret": shared_secret,
189                "algorithm": algorithm.to_string(),
190                "security_level": 256 // Placeholder
191            });
192
193            serde_wasm_bindgen::to_value(&result)
194                .map_err(|e| JsValue::from_str(&format!("Serialization error: {:?}", e)))
195        }
196        #[cfg(not(feature = "wasm"))]
197        {
198            Err(JsValue::from_str("WASM feature not enabled"))
199        }
200    }
201
202    /// Decapsulate a shared secret using the given secret key and ciphertext
203    ///
204    /// This method provides secure decapsulation with:
205    /// - Secret key validation
206    /// - Ciphertext verification
207    /// - Algorithm checking
208    /// - Proper error handling
209    pub fn decapsulate(
210        &self,
211        algorithm: &str,
212        secret_key_data: &Uint8Array,
213        ciphertext: &Uint8Array,
214    ) -> Result<Vec<u8>, JsValue> {
215        // Parse and validate algorithm
216        let algorithm = self
217            .parse_kem_algorithm(algorithm)
218            .map_err(error_to_js_value)?;
219
220        // Validate algorithm category
221        self.security_validator
222            .validate_algorithm_category(algorithm, AlgorithmCategory::Kem)
223            .map_err(error_to_js_value)?;
224
225        // Validate secret key size
226        if secret_key_data.length() == 0 {
227            return Err(JsValue::from_str("Invalid KEM secret key: empty key"));
228        }
229
230        // Validate ciphertext size
231        if ciphertext.length() == 0 {
232            return Err(JsValue::from_str("Invalid message size: empty data"));
233        }
234
235        // Create secret key using proper constructor
236        let secret_key = crate::traits::KemSecretKey::new(secret_key_data.to_vec());
237
238        // Validate secret key
239        self.security_validator
240            .validate_secret_key(algorithm, secret_key.as_bytes())
241            .map_err(error_to_js_value)?;
242
243        // Validate ciphertext
244        self.security_validator
245            .validate_ciphertext(algorithm, &ciphertext.to_vec())
246            .map_err(error_to_js_value)?;
247
248        // Decapsulate
249        let shared_secret = self
250            .inner
251            .decapsulate(algorithm, &secret_key, &ciphertext.to_vec())
252            .map_err(error_to_js_value)?;
253
254        Ok(shared_secret)
255    }
256
257    /// Get the security level of the context
258    pub fn security_level(&self) -> u32 {
259        // Return the highest security level supported by the context
260        256 // This would be determined by the provider
261    }
262
263    /// Check if an algorithm is supported
264    pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
265        self.parse_kem_algorithm(algorithm).is_ok()
266    }
267
268    /// Get supported algorithms
269    pub fn supported_algorithms(&self) -> String {
270        #[allow(unused_mut)] // mut needed when feature flags are enabled
271        let mut algorithms = alloc::vec!["ml-kem-512", "ml-kem-768", "ml-kem-1024"];
272        #[cfg(feature = "wasm")]
273        {
274            serde_json::to_string(&algorithms).unwrap_or_else(|_| "[]".to_string())
275        }
276        #[cfg(not(feature = "wasm"))]
277        {
278            "[]".to_string()
279        }
280    }
281
282    /// Parse KEM algorithm from string
283    fn parse_kem_algorithm(&self, algorithm: &str) -> Result<Algorithm, crate::error::Error> {
284        parse_algorithm_wasm(algorithm).map_err(|_| crate::error::Error::InvalidAlgorithm {
285            algorithm: "Invalid algorithm name",
286        })
287    }
288}
289
290/// WASM-compatible Signature context wrapper
291///
292/// This wrapper provides JavaScript-compatible bindings for signature operations:
293/// - Integrates with the new modular architecture
294/// - Includes security validation
295/// - Provides consistent error handling
296/// - Supports all signature algorithms
297#[cfg_attr(feature = "wasm", wasm_bindgen)]
298pub struct WasmSignatureContext {
299    inner: SignatureContext,
300    security_validator: SecurityValidator,
301}
302
303impl WasmSignatureContext {
304    /// Create a new WASM Signature context with default provider
305    ///
306    /// Prefer [`Self::from_signature_context`] when building from the `lib-q` crate so that
307    /// real signature implementations (e.g. `lib-q-sig`) are wired in instead of the core stub.
308    pub fn new() -> WasmSignatureContext {
309        WasmSignatureContext {
310            inner: SignatureContext::with_default_provider(),
311            security_validator: SecurityValidator::new()
312                .unwrap_or_else(|_| SecurityValidator::new().unwrap()),
313        }
314    }
315
316    /// Wrap a Rust [`SignatureContext`] (for example one built with
317    /// `SignatureContext::with_provider(Box::new(lib_q_sig::LibQSignatureProvider::new()?))`).
318    pub fn from_signature_context(inner: SignatureContext) -> WasmSignatureContext {
319        WasmSignatureContext {
320            inner,
321            security_validator: SecurityValidator::new()
322                .unwrap_or_else(|_| SecurityValidator::new().unwrap()),
323        }
324    }
325}
326
327impl Default for WasmSignatureContext {
328    fn default() -> Self {
329        Self::new()
330    }
331}
332
333impl WasmSignatureContext {
334    /// Create a new WASM Signature context with custom provider
335    pub fn with_provider(provider: &WasmCryptoProvider) -> WasmSignatureContext {
336        WasmSignatureContext {
337            inner: SignatureContext::with_provider(Box::new(provider.inner.clone())),
338            security_validator: SecurityValidator::new()
339                .unwrap_or_else(|_| SecurityValidator::new().unwrap()),
340        }
341    }
342
343    /// Parse signature algorithm from string
344    fn parse_signature_algorithm(&self, algorithm: &str) -> Result<Algorithm, crate::error::Error> {
345        parse_algorithm_wasm(algorithm).map_err(|_| crate::error::Error::InvalidAlgorithm {
346            algorithm: "Invalid algorithm name",
347        })
348    }
349}
350
351#[cfg_attr(feature = "wasm", wasm_bindgen)]
352impl WasmSignatureContext {
353    /// Generate a keypair for the specified algorithm
354    pub fn generate_keypair(
355        &mut self,
356        algorithm: &str,
357        randomness: Option<Uint8Array>,
358    ) -> Result<JsValue, JsValue> {
359        // Parse and validate algorithm
360        let algorithm = self
361            .parse_signature_algorithm(algorithm)
362            .map_err(error_to_js_value)?;
363
364        // Validate security level
365        convert_result(
366            self.security_validator
367                .validate_algorithm_category(algorithm, algorithm.category()),
368        )?;
369
370        // Convert randomness if provided
371        let randomness_vec = randomness.map(|rand| rand.to_vec());
372        let randomness_bytes = randomness_vec.as_deref();
373
374        // Generate keypair
375        let keypair = self
376            .inner
377            .generate_keypair(algorithm, randomness_bytes)
378            .map_err(error_to_js_value)?;
379
380        // Return as JavaScript object
381        #[cfg(feature = "wasm")]
382        {
383            let result = serde_json::json!({
384                "public_key": keypair.public_key.data,
385                "secret_key": keypair.secret_key.data,
386                "algorithm": algorithm.to_string(),
387                "security_level": 256 // Placeholder
388            });
389
390            serde_wasm_bindgen::to_value(&result)
391                .map_err(|e| JsValue::from_str(&format!("Serialization error: {:?}", e)))
392        }
393        #[cfg(not(feature = "wasm"))]
394        {
395            Err(JsValue::from_str("WASM feature not enabled"))
396        }
397    }
398
399    /// Sign a message using the given secret key
400    pub fn sign(
401        &self,
402        algorithm: &str,
403        secret_key_data: &Uint8Array,
404        message: &Uint8Array,
405        randomness: Option<Uint8Array>,
406    ) -> Result<Vec<u8>, JsValue> {
407        // Parse and validate algorithm
408        let algorithm = self
409            .parse_signature_algorithm(algorithm)
410            .map_err(error_to_js_value)?;
411
412        // Validate secret key size (simplified)
413        if secret_key_data.length() == 0 {
414            return Err(JsValue::from_str("Invalid signature secret key: empty key"));
415        }
416
417        // Validate message size (simplified)
418        if message.length() == 0 {
419            return Err(JsValue::from_str("Invalid message size: empty data"));
420        }
421
422        // Convert randomness if provided
423        let randomness_vec = randomness.map(|rand| rand.to_vec());
424        let randomness_bytes = randomness_vec.as_deref();
425
426        // Create secret key using proper constructor
427        let secret_key = crate::traits::SigSecretKey::new(secret_key_data.to_vec());
428
429        // Sign
430        let signature = self
431            .inner
432            .sign(algorithm, &secret_key, &message.to_vec(), randomness_bytes)
433            .map_err(error_to_js_value)?;
434        Ok(signature)
435    }
436
437    /// Verify a signature using the given public key
438    pub fn verify(
439        &self,
440        algorithm: &str,
441        public_key_data: &Uint8Array,
442        message: &Uint8Array,
443        signature: &Uint8Array,
444    ) -> Result<bool, JsValue> {
445        // Parse and validate algorithm
446        let algorithm = self
447            .parse_signature_algorithm(algorithm)
448            .map_err(error_to_js_value)?;
449
450        // Validate public key size (simplified)
451        if public_key_data.length() == 0 {
452            return Err(JsValue::from_str("Invalid signature public key: empty key"));
453        }
454
455        // Validate message size (simplified)
456        if message.length() == 0 {
457            return Err(JsValue::from_str("Invalid message size: empty data"));
458        }
459
460        // Validate signature size (simplified)
461        if signature.length() == 0 {
462            return Err(JsValue::from_str("Invalid signature: empty data"));
463        }
464
465        // Create public key using proper constructor
466        let public_key = crate::traits::SigPublicKey::new(public_key_data.to_vec());
467
468        // Verify
469        let is_valid = self
470            .inner
471            .verify(
472                algorithm,
473                &public_key,
474                &message.to_vec(),
475                &signature.to_vec(),
476            )
477            .map_err(error_to_js_value)?;
478        Ok(is_valid)
479    }
480
481    /// Sign a message under a signing context (FIPS-204 / FIPS-205 domain separation)
482    ///
483    /// The resulting signature verifies only under the same `context` bytes — see
484    /// [`Self::verify_with_context`]. An empty `context` matches [`Self::sign`].
485    pub fn sign_with_context(
486        &self,
487        algorithm: &str,
488        secret_key_data: &Uint8Array,
489        message: &Uint8Array,
490        context: &Uint8Array,
491        randomness: Option<Uint8Array>,
492    ) -> Result<Vec<u8>, JsValue> {
493        let algorithm = self
494            .parse_signature_algorithm(algorithm)
495            .map_err(error_to_js_value)?;
496
497        if secret_key_data.length() == 0 {
498            return Err(JsValue::from_str("Invalid signature secret key: empty key"));
499        }
500
501        if message.length() == 0 {
502            return Err(JsValue::from_str("Invalid message size: empty data"));
503        }
504
505        let randomness_vec = randomness.map(|rand| rand.to_vec());
506        let randomness_bytes = randomness_vec.as_deref();
507
508        let secret_key = crate::traits::SigSecretKey::new(secret_key_data.to_vec());
509
510        let signature = self
511            .inner
512            .sign_with_context(
513                algorithm,
514                &secret_key,
515                &message.to_vec(),
516                &context.to_vec(),
517                randomness_bytes,
518            )
519            .map_err(error_to_js_value)?;
520        Ok(signature)
521    }
522
523    /// Verify a signature under a signing context (FIPS-204 / FIPS-205 domain separation)
524    ///
525    /// Returns `false` unless `context` matches the context the signature was produced under.
526    /// An empty `context` matches [`Self::verify`].
527    pub fn verify_with_context(
528        &self,
529        algorithm: &str,
530        public_key_data: &Uint8Array,
531        message: &Uint8Array,
532        context: &Uint8Array,
533        signature: &Uint8Array,
534    ) -> Result<bool, JsValue> {
535        let algorithm = self
536            .parse_signature_algorithm(algorithm)
537            .map_err(error_to_js_value)?;
538
539        if public_key_data.length() == 0 {
540            return Err(JsValue::from_str("Invalid signature public key: empty key"));
541        }
542
543        if message.length() == 0 {
544            return Err(JsValue::from_str("Invalid message size: empty data"));
545        }
546
547        if signature.length() == 0 {
548            return Err(JsValue::from_str("Invalid signature: empty data"));
549        }
550
551        let public_key = crate::traits::SigPublicKey::new(public_key_data.to_vec());
552
553        let is_valid = self
554            .inner
555            .verify_with_context(
556                algorithm,
557                &public_key,
558                &message.to_vec(),
559                &context.to_vec(),
560                &signature.to_vec(),
561            )
562            .map_err(error_to_js_value)?;
563        Ok(is_valid)
564    }
565
566    /// Get the security level of the context
567    pub fn security_level(&self) -> u32 {
568        256 // This would be determined by the provider
569    }
570
571    /// Check if an algorithm is supported
572    pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
573        self.parse_signature_algorithm(algorithm).is_ok()
574    }
575
576    /// Get supported algorithms
577    pub fn supported_algorithms(&self) -> String {
578        #[cfg(feature = "wasm")]
579        {
580            serde_json::to_string(&WASM_SIGNATURE_ALGORITHM_IDS)
581                .unwrap_or_else(|_| "[]".to_string())
582        }
583        #[cfg(not(feature = "wasm"))]
584        {
585            "[]".to_string()
586        }
587    }
588}
589
590/// WASM-compatible Hash context wrapper
591///
592/// This wrapper provides JavaScript-compatible bindings for hash operations:
593/// - Integrates with the new modular architecture
594/// - Includes security validation
595/// - Provides consistent error handling
596/// - Supports all hash algorithms
597#[cfg_attr(feature = "wasm", wasm_bindgen)]
598pub struct WasmHashContext {
599    inner: HashContext,
600    security_validator: SecurityValidator,
601}
602
603impl WasmHashContext {
604    /// Create a new WASM Hash context with default provider
605    pub fn new() -> WasmHashContext {
606        WasmHashContext {
607            inner: HashContext::with_default_provider(),
608            security_validator: SecurityValidator::new()
609                .unwrap_or_else(|_| SecurityValidator::new().unwrap()),
610        }
611    }
612
613    /// Wrap a Rust [`HashContext`] that already has a hash-capable provider (for example from
614    /// `lib-q-hash::LibQHashProvider` in the umbrella crate).
615    pub fn from_hash_context(inner: HashContext) -> WasmHashContext {
616        WasmHashContext {
617            inner,
618            security_validator: SecurityValidator::new()
619                .unwrap_or_else(|_| SecurityValidator::new().unwrap()),
620        }
621    }
622}
623
624impl Default for WasmHashContext {
625    fn default() -> Self {
626        Self::new()
627    }
628}
629
630impl WasmHashContext {
631    /// Create a new WASM Hash context with custom provider
632    pub fn with_provider(provider: &WasmCryptoProvider) -> WasmHashContext {
633        WasmHashContext {
634            inner: HashContext::with_provider(Box::new(provider.inner.clone())),
635            security_validator: SecurityValidator::new()
636                .unwrap_or_else(|_| SecurityValidator::new().unwrap()),
637        }
638    }
639
640    /// Hash data using the specified algorithm
641    pub fn hash(&mut self, algorithm: &str, data: &Uint8Array) -> Result<JsValue, JsValue> {
642        // Parse and validate algorithm
643        let algorithm = self
644            .parse_hash_algorithm(algorithm)
645            .map_err(error_to_js_value)?;
646
647        // Validate algorithm category
648        self.security_validator
649            .validate_algorithm_category(algorithm, AlgorithmCategory::Hash)
650            .map_err(error_to_js_value)?;
651
652        // Validate data using security validator
653        self.security_validator
654            .validate_hash_input(&data.to_vec())
655            .map_err(error_to_js_value)?;
656
657        // Hash
658        let hash = self
659            .inner
660            .hash(algorithm, &data.to_vec())
661            .map_err(error_to_js_value)?;
662
663        // Return as JavaScript object
664        #[cfg(feature = "wasm")]
665        {
666            let result = serde_json::json!({
667                "hash": hash,
668                "algorithm": algorithm.to_string(),
669                "security_level": 256 // Placeholder
670            });
671
672            match serde_wasm_bindgen::to_value(&result) {
673                Ok(value) => Ok(value),
674                Err(e) => Err(JsValue::from_str(&format!("Serialization error: {:?}", e))),
675            }
676        }
677        #[cfg(not(feature = "wasm"))]
678        {
679            Err(JsValue::from_str("WASM feature not enabled"))
680        }
681    }
682
683    /// Get the security level of the context
684    pub fn security_level(&self) -> u32 {
685        256 // This would be determined by the provider
686    }
687
688    /// Check if an algorithm is supported
689    pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
690        self.parse_hash_algorithm(algorithm).is_ok()
691    }
692
693    /// Get supported algorithms
694    pub fn supported_algorithms(&self) -> String {
695        let algorithms = alloc::vec![
696            "sha3-224",
697            "sha3-256",
698            "sha3-384",
699            "sha3-512",
700            "shake128",
701            "shake256",
702            "sha-224",
703            "sha-256",
704            "sha-384",
705            "sha-512",
706            "sha-512/224",
707            "sha-512/256",
708            "cshake128",
709            "cshake256",
710            "keccak-224",
711            "keccak-256",
712            "keccak-384",
713            "keccak-512",
714            "kangarootwelve",
715            "turboshake128",
716            "turboshake256",
717            "kmac128",
718            "kmac256",
719            "tuplehash128",
720            "tuplehash256",
721            "parallelhash128",
722            "parallelhash256",
723        ];
724        #[cfg(feature = "wasm")]
725        {
726            serde_json::to_string(&algorithms).unwrap_or_else(|_| "[]".to_string())
727        }
728        #[cfg(not(feature = "wasm"))]
729        {
730            "[]".to_string()
731        }
732    }
733
734    /// Parse hash algorithm from string
735    fn parse_hash_algorithm(&self, algorithm: &str) -> Result<Algorithm, crate::error::Error> {
736        parse_algorithm_wasm(algorithm).map_err(|_| crate::error::Error::InvalidAlgorithm {
737            algorithm: "Invalid algorithm name",
738        })
739    }
740}
741
742/// WASM-compatible AEAD context wrapper
743///
744/// This wrapper provides JavaScript-compatible bindings for AEAD operations:
745/// - Integrates with the new modular architecture
746/// - Includes security validation
747/// - Provides consistent error handling
748/// - Supports all AEAD algorithms
749#[cfg_attr(feature = "wasm", wasm_bindgen)]
750pub struct WasmAeadContext {
751    inner: AeadContext,
752    security_validator: SecurityValidator,
753}
754
755impl WasmAeadContext {
756    /// Create a WASM AEAD context with **no** crypto provider configured.
757    ///
758    /// For AEAD backed by `lib-q-aead`, use the `lib-q` crate's `wasm::create_aead_context`, or
759    /// [`Self::from_aead_context`] / [`Self::with_provider`].
760    pub fn new() -> WasmAeadContext {
761        WasmAeadContext {
762            inner: AeadContext::new(),
763            security_validator: SecurityValidator::new()
764                .unwrap_or_else(|_| SecurityValidator::new().unwrap()),
765        }
766    }
767
768    /// Wrap a Rust [`AeadContext`] (for example one built with `AeadContext::with_aead_operations`).
769    pub fn from_aead_context(inner: AeadContext) -> WasmAeadContext {
770        WasmAeadContext {
771            inner,
772            security_validator: SecurityValidator::new()
773                .unwrap_or_else(|_| SecurityValidator::new().unwrap()),
774        }
775    }
776}
777
778impl Default for WasmAeadContext {
779    fn default() -> Self {
780        Self::new()
781    }
782}
783
784impl WasmAeadContext {
785    /// Create a new WASM AEAD context with custom provider
786    pub fn with_provider(provider: &WasmCryptoProvider) -> WasmAeadContext {
787        WasmAeadContext {
788            inner: AeadContext::with_provider(Box::new(provider.inner.clone())),
789            security_validator: SecurityValidator::new()
790                .unwrap_or_else(|_| SecurityValidator::new().unwrap()),
791        }
792    }
793
794    /// Encrypt data using the specified algorithm
795    pub fn encrypt(
796        &mut self,
797        algorithm: &str,
798        key: &Uint8Array,
799        nonce: &Uint8Array,
800        plaintext: &Uint8Array,
801        aad: Option<Uint8Array>,
802    ) -> Result<Vec<u8>, JsValue> {
803        // Parse and validate algorithm
804        let algorithm = self
805            .parse_aead_algorithm(algorithm)
806            .map_err(error_to_js_value)?;
807
808        // Validate algorithm category
809        self.security_validator
810            .validate_algorithm_category(algorithm, AlgorithmCategory::Aead)
811            .map_err(error_to_js_value)?;
812
813        // Validate key using security validator
814        self.security_validator
815            .validate_key_size(algorithm, &key.to_vec(), true)
816            .map_err(error_to_js_value)?;
817
818        // Validate nonce using security validator
819        self.security_validator
820            .validate_nonce(&nonce.to_vec())
821            .map_err(error_to_js_value)?;
822
823        // Validate plaintext using security validator
824        self.security_validator
825            .validate_aead_message(&plaintext.to_vec())
826            .map_err(error_to_js_value)?;
827
828        // Convert AAD if provided and validate
829        let aad_bytes = aad.map(|aad_data| aad_data.to_vec());
830        if let Some(ref aad_data) = aad_bytes {
831            self.security_validator
832                .validate_aead_message(aad_data)
833                .map_err(error_to_js_value)?;
834        }
835
836        // Encrypt
837        let aead_key = AeadKey::new(key.to_vec());
838        let nonce_obj = Nonce::new(nonce.to_vec());
839        let ciphertext = self
840            .inner
841            .encrypt(
842                algorithm,
843                &aead_key,
844                &nonce_obj,
845                &plaintext.to_vec(),
846                aad_bytes.as_deref(),
847            )
848            .map_err(error_to_js_value)?;
849        Ok(ciphertext)
850    }
851
852    /// Decrypt data using the specified algorithm.
853    ///
854    /// This WASM binding stays on **Layer A** ([`crate::traits::Aead`] / [`crate::api::AeadOperations`]):
855    /// only `Result`-style success versus error is exposed. Semantic decrypt
856    /// ([`crate::AeadDecryptSemantic`]) is not wired here to avoid silent ABI changes; use Rust
857    /// types directly when Layer B is required.
858    pub fn decrypt(
859        &self,
860        algorithm: &str,
861        key: &Uint8Array,
862        nonce: &Uint8Array,
863        ciphertext: &Uint8Array,
864        aad: Option<Uint8Array>,
865    ) -> Result<Vec<u8>, JsValue> {
866        // Parse and validate algorithm
867        let algorithm = self
868            .parse_aead_algorithm(algorithm)
869            .map_err(error_to_js_value)?;
870
871        // Validate algorithm category
872        self.security_validator
873            .validate_algorithm_category(algorithm, AlgorithmCategory::Aead)
874            .map_err(error_to_js_value)?;
875
876        // Validate key using security validator
877        self.security_validator
878            .validate_key_size(algorithm, &key.to_vec(), true)
879            .map_err(error_to_js_value)?;
880
881        // Validate nonce using security validator
882        self.security_validator
883            .validate_nonce(&nonce.to_vec())
884            .map_err(error_to_js_value)?;
885
886        // Validate ciphertext using security validator
887        self.security_validator
888            .validate_ciphertext(algorithm, &ciphertext.to_vec())
889            .map_err(error_to_js_value)?;
890
891        // Convert AAD if provided and validate
892        let aad_bytes = aad.map(|aad_data| aad_data.to_vec());
893        if let Some(ref aad_data) = aad_bytes {
894            self.security_validator
895                .validate_aead_message(aad_data)
896                .map_err(error_to_js_value)?;
897        }
898
899        // Decrypt
900        let aead_key = AeadKey::new(key.to_vec());
901        let nonce_obj = Nonce::new(nonce.to_vec());
902        let plaintext = self
903            .inner
904            .decrypt(
905                algorithm,
906                &aead_key,
907                &nonce_obj,
908                &ciphertext.to_vec(),
909                aad_bytes.as_deref(),
910            )
911            .map_err(error_to_js_value)?;
912        Ok(plaintext)
913    }
914
915    /// Get the security level of the context
916    pub fn security_level(&self) -> u32 {
917        256 // This would be determined by the provider
918    }
919
920    /// Check if an algorithm is supported
921    pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
922        self.parse_aead_algorithm(algorithm).is_ok()
923    }
924
925    /// Get supported algorithms
926    pub fn supported_algorithms(&self) -> String {
927        let algorithms = alloc::vec!["saturnin", "shake256-aead"];
928        #[cfg(feature = "wasm")]
929        {
930            serde_json::to_string(&algorithms).unwrap_or_else(|_| "[]".to_string())
931        }
932        #[cfg(not(feature = "wasm"))]
933        {
934            "[]".to_string()
935        }
936    }
937
938    /// Parse AEAD algorithm from string
939    fn parse_aead_algorithm(&self, algorithm: &str) -> Result<Algorithm, crate::error::Error> {
940        parse_algorithm_wasm(algorithm).map_err(|_| crate::error::Error::InvalidAlgorithm {
941            algorithm: "Invalid algorithm name",
942        })
943    }
944}
945
946/// WASM-compatible CryptoProvider wrapper
947///
948/// This wrapper provides JavaScript-compatible bindings for the crypto provider:
949/// - Integrates with the new modular architecture
950/// - Provides consistent error handling
951/// - Supports all cryptographic operations
952#[cfg_attr(feature = "wasm", wasm_bindgen)]
953pub struct WasmCryptoProvider {
954    inner: LibQCryptoProvider,
955}
956
957impl WasmCryptoProvider {
958    /// Create a new WASM CryptoProvider
959    pub fn new() -> WasmCryptoProvider {
960        WasmCryptoProvider {
961            inner: LibQCryptoProvider::new().unwrap_or_else(|_| LibQCryptoProvider::new().unwrap()),
962        }
963    }
964}
965
966impl Default for WasmCryptoProvider {
967    fn default() -> Self {
968        Self::new()
969    }
970}
971
972impl WasmCryptoProvider {
973    /// Get the provider information
974    pub fn info(&self) -> String {
975        #[cfg(feature = "wasm")]
976        {
977            serde_json::json!({
978                "name": "lib-Q Crypto Provider",
979                "version": crate::VERSION,
980                "features": {
981                    "kem": true,
982                    "signature": true,
983                    "hash": true,
984                    "aead": true,
985                    "security_hardened": true
986                }
987            })
988            .to_string()
989        }
990        #[cfg(not(feature = "wasm"))]
991        {
992            "{}".to_string()
993        }
994    }
995
996    /// Check if an algorithm is supported
997    ///
998    /// An algorithm is supported only if its name parses to a known [`Algorithm`]
999    /// AND the wrapped provider actually exposes an implementation for that
1000    /// algorithm's category (mirrors `WasmProviderManager::is_algorithm_supported`).
1001    pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
1002        // Reuse the same string->Algorithm mapping `parse_algorithm_wasm` uses, without going
1003        // through its JsValue-producing error path (JsValue construction aborts outside a real
1004        // wasm32 runtime, which is why this deliberately calls the underlying conversion instead).
1005        if algorithm.len() > 64 || algorithm.chars().any(|c| c.is_control()) {
1006            return false;
1007        }
1008        let algorithm =
1009            match crate::wasm::conversions::WasmConversions::string_to_algorithm(algorithm) {
1010                Ok(alg) => alg,
1011                Err(_) => return false,
1012            };
1013
1014        match algorithm.category() {
1015            AlgorithmCategory::Kem => self.inner.kem().is_some(),
1016            AlgorithmCategory::Signature => self.inner.signature().is_some(),
1017            AlgorithmCategory::Hash => self.inner.hash().is_some(),
1018            AlgorithmCategory::Aead => self.inner.aead().is_some(),
1019            AlgorithmCategory::PrivacyProtocol => false,
1020        }
1021    }
1022
1023    /// Get supported algorithms by category
1024    pub fn supported_algorithms(&self) -> String {
1025        #[cfg(feature = "wasm")]
1026        {
1027            #[allow(unused_mut)] // mut needed when feature flags are enabled
1028            let mut kem_algorithms = alloc::vec!["ml-kem-512", "ml-kem-768", "ml-kem-1024"];
1029            let algorithms = serde_json::json!({
1030                "kem": kem_algorithms,
1031                "signature": WASM_SIGNATURE_ALGORITHM_IDS,
1032                "hash": [
1033                    "sha3-224", "sha3-256", "sha3-384", "sha3-512", "shake128", "shake256",
1034                    "sha-224", "sha-256", "sha-384", "sha-512", "sha-512/224", "sha-512/256",
1035                    "cshake128", "cshake256", "keccak-224", "keccak-256", "keccak-384", "keccak-512",
1036                    "kangarootwelve", "turboshake128", "turboshake256", "kmac128", "kmac256",
1037                    "tuplehash128", "tuplehash256", "parallelhash128", "parallelhash256",
1038                ],
1039                "aead": ["saturnin", "shake256-aead"]
1040            });
1041            algorithms.to_string()
1042        }
1043        #[cfg(not(feature = "wasm"))]
1044        {
1045            "{}".to_string()
1046        }
1047    }
1048}
1049
1050#[cfg(test)]
1051mod tests {
1052    use super::*;
1053
1054    #[test]
1055    fn test_wasm_kem_context_creation() {
1056        let context = WasmKemContext::new();
1057        assert_eq!(context.security_level(), 256);
1058    }
1059
1060    #[test]
1061    fn test_wasm_signature_context_creation() {
1062        let context = WasmSignatureContext::new();
1063        assert_eq!(context.security_level(), 256);
1064    }
1065
1066    #[test]
1067    fn test_wasm_hash_context_creation() {
1068        let context = WasmHashContext::new();
1069        assert_eq!(context.security_level(), 256);
1070    }
1071
1072    #[test]
1073    fn test_wasm_aead_context_creation() {
1074        let context = WasmAeadContext::new();
1075        assert_eq!(context.security_level(), 256);
1076    }
1077
1078    #[test]
1079    fn test_wasm_crypto_provider_creation() {
1080        let provider = WasmCryptoProvider::new();
1081        let info = provider.info();
1082        assert!(info.contains("lib-Q") || info == "{}");
1083    }
1084
1085    #[test]
1086    fn test_wasm_crypto_provider_rejects_unknown_algorithm() {
1087        let provider = WasmCryptoProvider::new();
1088        assert!(!provider.is_algorithm_supported("not-a-real-algorithm"));
1089        assert!(!provider.is_algorithm_supported(""));
1090    }
1091
1092    #[test]
1093    fn test_wasm_crypto_provider_accepts_known_algorithm() {
1094        let provider = WasmCryptoProvider::new();
1095        assert!(provider.is_algorithm_supported("sha3-256"));
1096    }
1097
1098    #[test]
1099    #[cfg(target_arch = "wasm32")]
1100    fn test_wasm_kem_context_operations() {
1101        let mut context = WasmKemContext::new();
1102
1103        // Test that operations return proper NotImplemented errors
1104        let result = context.generate_keypair("ml-kem-512", None);
1105        assert!(result.is_err());
1106        if let Err(error) = result {
1107            let error_str = error.as_string().unwrap_or_default();
1108            assert!(error_str.contains("NotImplemented") || error_str.contains("WASM"));
1109        }
1110    }
1111
1112    #[test]
1113    #[cfg(target_arch = "wasm32")]
1114    fn test_wasm_signature_context_operations() {
1115        let mut context = WasmSignatureContext::new();
1116
1117        // Test that operations return proper NotImplemented errors
1118        let result = context.generate_keypair("ml-dsa-65", None);
1119        assert!(result.is_err());
1120        if let Err(error) = result {
1121            let error_str = error.as_string().unwrap_or_default();
1122            assert!(error_str.contains("NotImplemented") || error_str.contains("WASM"));
1123        }
1124    }
1125
1126    #[test]
1127    #[cfg(target_arch = "wasm32")]
1128    fn test_wasm_hash_context_operations() {
1129        let mut context = WasmHashContext::new();
1130
1131        // Test that operations return proper NotImplemented errors
1132        let data = Uint8Array::new_with_length(10);
1133        let result = context.hash("sha3-256", &data);
1134        assert!(result.is_err());
1135        if let Err(error) = result {
1136            let error_str = error.as_string().unwrap_or_default();
1137            assert!(error_str.contains("NotImplemented") || error_str.contains("WASM"));
1138        }
1139    }
1140
1141    #[test]
1142    #[cfg(target_arch = "wasm32")]
1143    fn test_wasm_aead_context_operations() {
1144        let mut context = WasmAeadContext::new();
1145
1146        // Test that operations return proper NotImplemented errors
1147        let key = Uint8Array::new_with_length(32);
1148        let nonce = Uint8Array::new_with_length(16);
1149        let plaintext = Uint8Array::new_with_length(10);
1150
1151        let result = context.encrypt("saturnin", &key, &nonce, &plaintext, None);
1152        assert!(result.is_err());
1153        if let Err(error) = result {
1154            let error_str = error.as_string().unwrap_or_default();
1155            assert!(
1156                error_str.contains("Provider not configured") ||
1157                    error_str.contains("NotImplemented") ||
1158                    error_str.contains("WASM")
1159            );
1160        }
1161    }
1162}