Skip to main content

lib_q_core/wasm/
secure_contexts.rs

1//! Secure WASM Contexts
2//!
3//! This module provides secure, production-ready WASM contexts that implement
4//! proper error handling, security validation, and consistent API design.
5//!
6//! These contexts provide a unified interface for post-quantum cryptographic
7//! operations in WebAssembly environments with comprehensive security validation
8//! and protection against common attack vectors.
9
10#[cfg(feature = "wasm")]
11extern crate alloc;
12#[cfg(feature = "wasm")]
13use alloc::boxed::Box;
14
15#[cfg(feature = "wasm")]
16use js_sys::Uint8Array;
17#[cfg(feature = "wasm")]
18use wasm_bindgen::prelude::*;
19
20use crate::api::{
21    // Algorithm,
22    AlgorithmCategory,
23};
24use crate::contexts::{
25    AeadContext,
26    HashContext,
27    KemContext,
28    SignatureContext,
29};
30// use crate::error::Result;
31use crate::providers::LibQCryptoProvider;
32use crate::security::SecurityValidator;
33use crate::traits::{
34    AeadKey,
35    Nonce,
36    // SigPublicKey,
37};
38use crate::wasm::conversions::WASM_SIGNATURE_ALGORITHM_IDS;
39use crate::wasm::error::{
40    convert_result,
41    // error_to_js_value,
42    parse_algorithm_wasm,
43    secure_serialize,
44};
45
46/// Secure WASM KEM Context
47///
48/// This context provides secure KEM operations with:
49/// - Consistent error handling using Result<T, JsValue>
50/// - Security validation for all inputs
51/// - Protection against timing attacks
52/// - Memory safety with automatic cleanup
53#[cfg_attr(feature = "wasm", wasm_bindgen)]
54pub struct SecureWasmKemContext {
55    inner: KemContext,
56    security_validator: SecurityValidator,
57}
58
59#[cfg_attr(feature = "wasm", wasm_bindgen)]
60impl SecureWasmKemContext {
61    /// Create a new secure WASM KEM context
62    #[cfg_attr(feature = "wasm", wasm_bindgen(constructor))]
63    pub fn new() -> Result<SecureWasmKemContext, JsValue> {
64        let provider = Box::new(LibQCryptoProvider::new()?);
65        let inner = KemContext::with_provider(provider);
66        let security_validator = SecurityValidator::new()?;
67
68        Ok(SecureWasmKemContext {
69            inner,
70            security_validator,
71        })
72    }
73
74    /// Generate a KEM keypair
75    pub fn generate_keypair(
76        &mut self,
77        algorithm: &str,
78        randomness: Option<Uint8Array>,
79    ) -> Result<JsValue, JsValue> {
80        // Parse and validate algorithm
81        let algorithm = parse_algorithm_wasm(algorithm)?;
82
83        // Validate algorithm category
84        match convert_result(
85            self.security_validator
86                .validate_algorithm_category(algorithm, AlgorithmCategory::Kem),
87        ) {
88            Ok(_) => {}
89            Err(error) => return Err(error),
90        }
91
92        // Convert randomness if provided
93        let randomness_bytes = randomness.map(|rand| rand.to_vec());
94
95        // Generate keypair
96        let keypair = convert_result(
97            self.inner
98                .generate_keypair(algorithm, randomness_bytes.as_deref()),
99        )?;
100
101        // Serialize and return
102        match secure_serialize(&keypair) {
103            Ok(value) => Ok(value),
104            Err(error) => Err(error),
105        }
106    }
107
108    /// Encapsulate a shared secret
109    pub fn encapsulate(
110        &self,
111        algorithm: &str,
112        public_key: &Uint8Array,
113        randomness: Option<Uint8Array>,
114    ) -> Result<JsValue, JsValue> {
115        // Parse and validate algorithm
116        let algorithm = parse_algorithm_wasm(algorithm)?;
117
118        // Validate algorithm category
119        match convert_result(
120            self.security_validator
121                .validate_algorithm_category(algorithm, AlgorithmCategory::Kem),
122        ) {
123            Ok(_) => {}
124            Err(error) => return Err(error),
125        }
126
127        // Convert inputs
128        let public_key_bytes = public_key.to_vec();
129        let randomness_bytes = randomness.map(|rand| rand.to_vec());
130
131        // Validate key size
132        match convert_result(self.security_validator.validate_key_size(
133            algorithm,
134            &public_key_bytes,
135            false,
136        )) {
137            Ok(_) => {}
138            Err(error) => return Err(error),
139        }
140
141        // Create proper key type
142        let public_key = crate::traits::KemPublicKey::new(public_key_bytes.to_vec());
143
144        // Encapsulate
145        let result = convert_result(self.inner.encapsulate(
146            algorithm,
147            &public_key,
148            randomness_bytes.as_deref(),
149        ))?;
150
151        // Serialize and return
152        match secure_serialize(&result) {
153            Ok(value) => Ok(value),
154            Err(error) => Err(error),
155        }
156    }
157
158    /// Decapsulate a shared secret
159    pub fn decapsulate(
160        &self,
161        algorithm: &str,
162        private_key: &Uint8Array,
163        ciphertext: &Uint8Array,
164    ) -> Result<JsValue, JsValue> {
165        // Parse and validate algorithm
166        let algorithm = parse_algorithm_wasm(algorithm)?;
167
168        // Validate algorithm category
169        match convert_result(
170            self.security_validator
171                .validate_algorithm_category(algorithm, AlgorithmCategory::Kem),
172        ) {
173            Ok(_) => {}
174            Err(error) => return Err(error),
175        }
176
177        // Convert inputs
178        let private_key_bytes = private_key.to_vec();
179        let ciphertext_bytes = ciphertext.to_vec();
180
181        // Validate key size
182        match convert_result(self.security_validator.validate_key_size(
183            algorithm,
184            &private_key_bytes,
185            true,
186        )) {
187            Ok(_) => {}
188            Err(error) => return Err(error),
189        }
190
191        // Create proper key type
192        let secret_key = crate::traits::KemSecretKey::new(private_key_bytes.to_vec());
193
194        // Decapsulate
195        let result = convert_result(self.inner.decapsulate(
196            algorithm,
197            &secret_key,
198            &ciphertext_bytes,
199        ))?;
200
201        // Serialize and return
202        match secure_serialize(&result) {
203            Ok(value) => Ok(value),
204            Err(error) => Err(error),
205        }
206    }
207
208    /// Get supported algorithms
209    pub fn get_supported_algorithms(&self) -> Result<JsValue, JsValue> {
210        let algorithms = alloc::vec!["ml-kem-512", "ml-kem-768", "ml-kem-1024"];
211        match secure_serialize(&algorithms) {
212            Ok(value) => Ok(value),
213            Err(error) => Err(error),
214        }
215    }
216}
217
218/// Secure WASM Signature Context
219///
220/// This context provides secure signature operations with:
221/// - Consistent error handling using Result<T, JsValue>
222/// - Security validation for all inputs
223/// - Protection against timing attacks
224/// - Memory safety with automatic cleanup
225#[cfg_attr(feature = "wasm", wasm_bindgen)]
226pub struct SecureWasmSignatureContext {
227    inner: SignatureContext,
228    security_validator: SecurityValidator,
229}
230
231#[cfg_attr(feature = "wasm", wasm_bindgen)]
232impl SecureWasmSignatureContext {
233    /// Create a new secure WASM signature context
234    #[cfg_attr(feature = "wasm", wasm_bindgen(constructor))]
235    pub fn new() -> Result<SecureWasmSignatureContext, JsValue> {
236        let provider = Box::new(LibQCryptoProvider::new()?);
237        let inner = SignatureContext::with_provider(provider);
238        let security_validator = SecurityValidator::new()?;
239
240        Ok(SecureWasmSignatureContext {
241            inner,
242            security_validator,
243        })
244    }
245
246    /// Generate a signature keypair
247    pub fn generate_keypair(
248        &mut self,
249        algorithm: &str,
250        randomness: Option<Uint8Array>,
251    ) -> Result<JsValue, JsValue> {
252        // Parse and validate algorithm
253        let algorithm = parse_algorithm_wasm(algorithm)?;
254
255        // Validate algorithm category
256        match convert_result(
257            self.security_validator
258                .validate_algorithm_category(algorithm, AlgorithmCategory::Signature),
259        ) {
260            Ok(_) => {}
261            Err(error) => return Err(error),
262        }
263
264        // Convert randomness if provided
265        let randomness_bytes = randomness.map(|rand| rand.to_vec());
266
267        // Generate keypair
268        let keypair = convert_result(
269            self.inner
270                .generate_keypair(algorithm, randomness_bytes.as_deref()),
271        )?;
272
273        // Serialize and return
274        match secure_serialize(&keypair) {
275            Ok(value) => Ok(value),
276            Err(error) => Err(error),
277        }
278    }
279
280    /// Sign a message
281    pub fn sign(
282        &self,
283        algorithm: &str,
284        private_key: &Uint8Array,
285        message: &Uint8Array,
286        randomness: Option<Uint8Array>,
287    ) -> Result<JsValue, JsValue> {
288        // Parse and validate algorithm
289        let algorithm = parse_algorithm_wasm(algorithm)?;
290
291        // Validate algorithm category
292        match convert_result(
293            self.security_validator
294                .validate_algorithm_category(algorithm, AlgorithmCategory::Signature),
295        ) {
296            Ok(_) => {}
297            Err(error) => return Err(error),
298        }
299
300        // Convert inputs
301        let private_key_bytes = private_key.to_vec();
302        let message_bytes = message.to_vec();
303        let randomness_bytes = randomness.map(|rand| rand.to_vec());
304
305        // Validate key size
306        match convert_result(self.security_validator.validate_key_size(
307            algorithm,
308            &private_key_bytes,
309            true,
310        )) {
311            Ok(_) => {}
312            Err(error) => return Err(error),
313        }
314
315        // Validate message size
316        match convert_result(
317            self.security_validator
318                .validate_signature_message(&message_bytes),
319        ) {
320            Ok(_) => {}
321            Err(error) => return Err(error),
322        }
323
324        // Create proper key type
325        let secret_key = crate::traits::SigSecretKey::new(private_key_bytes.to_vec());
326
327        // Sign
328        let signature = convert_result(self.inner.sign(
329            algorithm,
330            &secret_key,
331            &message_bytes,
332            randomness_bytes.as_deref(),
333        ))?;
334
335        // Serialize and return
336        match secure_serialize(&signature) {
337            Ok(value) => Ok(value),
338            Err(error) => Err(error),
339        }
340    }
341
342    /// Verify a signature
343    pub fn verify(
344        &self,
345        algorithm: &str,
346        public_key: &Uint8Array,
347        message: &Uint8Array,
348        signature: &Uint8Array,
349    ) -> Result<JsValue, JsValue> {
350        // Parse and validate algorithm
351        let algorithm = parse_algorithm_wasm(algorithm)?;
352
353        // Validate algorithm category
354        match convert_result(
355            self.security_validator
356                .validate_algorithm_category(algorithm, AlgorithmCategory::Signature),
357        ) {
358            Ok(_) => {}
359            Err(error) => return Err(error),
360        }
361
362        // Convert inputs
363        let public_key_bytes = public_key.to_vec();
364        let message_bytes = message.to_vec();
365        let signature_bytes = signature.to_vec();
366
367        // Validate key size
368        match convert_result(self.security_validator.validate_key_size(
369            algorithm,
370            &public_key_bytes,
371            false,
372        )) {
373            Ok(_) => {}
374            Err(error) => return Err(error),
375        }
376
377        // Validate message size
378        match convert_result(
379            self.security_validator
380                .validate_signature_message(&message_bytes),
381        ) {
382            Ok(_) => {}
383            Err(error) => return Err(error),
384        }
385
386        // Create proper key type
387        let public_key = crate::traits::SigPublicKey::new(public_key_bytes.to_vec());
388
389        // Verify
390        let is_valid = convert_result(self.inner.verify(
391            algorithm,
392            &public_key,
393            &message_bytes,
394            &signature_bytes,
395        ))?;
396
397        // Serialize and return
398        match secure_serialize(&is_valid) {
399            Ok(value) => Ok(value),
400            Err(error) => Err(error),
401        }
402    }
403
404    /// Sign a message under a signing context (FIPS-204 / FIPS-205 domain separation)
405    ///
406    /// The resulting signature verifies only under the same `context` bytes — see
407    /// [`Self::verify_with_context`]. An empty `context` matches [`Self::sign`].
408    pub fn sign_with_context(
409        &self,
410        algorithm: &str,
411        private_key: &Uint8Array,
412        message: &Uint8Array,
413        context: &Uint8Array,
414        randomness: Option<Uint8Array>,
415    ) -> Result<JsValue, JsValue> {
416        let algorithm = parse_algorithm_wasm(algorithm)?;
417
418        convert_result(
419            self.security_validator
420                .validate_algorithm_category(algorithm, AlgorithmCategory::Signature),
421        )?;
422
423        let private_key_bytes = private_key.to_vec();
424        let message_bytes = message.to_vec();
425        let context_bytes = context.to_vec();
426        let randomness_bytes = randomness.map(|rand| rand.to_vec());
427
428        convert_result(self.security_validator.validate_key_size(
429            algorithm,
430            &private_key_bytes,
431            true,
432        ))?;
433
434        convert_result(
435            self.security_validator
436                .validate_signature_message(&message_bytes),
437        )?;
438
439        let secret_key = crate::traits::SigSecretKey::new(private_key_bytes.to_vec());
440
441        let signature = convert_result(self.inner.sign_with_context(
442            algorithm,
443            &secret_key,
444            &message_bytes,
445            &context_bytes,
446            randomness_bytes.as_deref(),
447        ))?;
448
449        secure_serialize(&signature)
450    }
451
452    /// Verify a signature under a signing context (FIPS-204 / FIPS-205 domain separation)
453    ///
454    /// Returns `false` unless `context` matches the context the signature was produced under.
455    /// An empty `context` matches [`Self::verify`].
456    pub fn verify_with_context(
457        &self,
458        algorithm: &str,
459        public_key: &Uint8Array,
460        message: &Uint8Array,
461        context: &Uint8Array,
462        signature: &Uint8Array,
463    ) -> Result<JsValue, JsValue> {
464        let algorithm = parse_algorithm_wasm(algorithm)?;
465
466        convert_result(
467            self.security_validator
468                .validate_algorithm_category(algorithm, AlgorithmCategory::Signature),
469        )?;
470
471        let public_key_bytes = public_key.to_vec();
472        let message_bytes = message.to_vec();
473        let context_bytes = context.to_vec();
474        let signature_bytes = signature.to_vec();
475
476        convert_result(self.security_validator.validate_key_size(
477            algorithm,
478            &public_key_bytes,
479            false,
480        ))?;
481
482        convert_result(
483            self.security_validator
484                .validate_signature_message(&message_bytes),
485        )?;
486
487        let public_key = crate::traits::SigPublicKey::new(public_key_bytes.to_vec());
488
489        let is_valid = convert_result(self.inner.verify_with_context(
490            algorithm,
491            &public_key,
492            &message_bytes,
493            &context_bytes,
494            &signature_bytes,
495        ))?;
496
497        secure_serialize(&is_valid)
498    }
499
500    /// Get supported algorithms
501    pub fn get_supported_algorithms(&self) -> Result<JsValue, JsValue> {
502        match secure_serialize(&WASM_SIGNATURE_ALGORITHM_IDS) {
503            Ok(value) => Ok(value),
504            Err(error) => Err(error),
505        }
506    }
507}
508
509/// Secure WASM Hash Context
510///
511/// This context provides secure hash operations with:
512/// - Consistent error handling using Result<T, JsValue>
513/// - Security validation for all inputs
514/// - Protection against timing attacks
515/// - Memory safety with automatic cleanup
516#[cfg_attr(feature = "wasm", wasm_bindgen)]
517pub struct SecureWasmHashContext {
518    inner: HashContext,
519    security_validator: SecurityValidator,
520}
521
522#[cfg_attr(feature = "wasm", wasm_bindgen)]
523impl SecureWasmHashContext {
524    /// Create a new secure WASM hash context
525    #[cfg_attr(feature = "wasm", wasm_bindgen(constructor))]
526    pub fn new() -> Result<SecureWasmHashContext, JsValue> {
527        let provider = Box::new(LibQCryptoProvider::new()?);
528        let inner = HashContext::with_provider(provider);
529        let security_validator = SecurityValidator::new()?;
530
531        Ok(SecureWasmHashContext {
532            inner,
533            security_validator,
534        })
535    }
536
537    /// Hash data
538    pub fn hash(&mut self, algorithm: &str, data: &Uint8Array) -> Result<JsValue, JsValue> {
539        // Parse and validate algorithm
540        let algorithm = parse_algorithm_wasm(algorithm)?;
541
542        // Validate algorithm category
543        match convert_result(
544            self.security_validator
545                .validate_algorithm_category(algorithm, AlgorithmCategory::Hash),
546        ) {
547            Ok(_) => {}
548            Err(error) => return Err(error),
549        }
550
551        // Convert inputs
552        let data_bytes = data.to_vec();
553
554        // Validate message size
555        match convert_result(self.security_validator.validate_hash_input(&data_bytes)) {
556            Ok(_) => {}
557            Err(error) => return Err(error),
558        }
559
560        // Hash
561        let hash = convert_result(self.inner.hash(algorithm, &data_bytes))?;
562
563        // Serialize and return
564        match secure_serialize(&hash) {
565            Ok(value) => Ok(value),
566            Err(error) => Err(error),
567        }
568    }
569
570    /// Get supported algorithms
571    pub fn get_supported_algorithms(&self) -> Result<JsValue, JsValue> {
572        let algorithms = alloc::vec![
573            "sha3-224", "sha3-256", "sha3-384", "sha3-512", "shake128", "shake256",
574        ];
575        match secure_serialize(&algorithms) {
576            Ok(value) => Ok(value),
577            Err(error) => Err(error),
578        }
579    }
580}
581
582/// Secure WASM AEAD Context
583///
584/// This context provides secure AEAD operations with:
585/// - Consistent error handling using Result<T, JsValue>
586/// - Security validation for all inputs
587/// - Protection against timing attacks
588/// - Memory safety with automatic cleanup
589#[cfg_attr(feature = "wasm", wasm_bindgen)]
590pub struct SecureWasmAeadContext {
591    inner: AeadContext,
592    security_validator: SecurityValidator,
593}
594
595#[cfg_attr(feature = "wasm", wasm_bindgen)]
596impl SecureWasmAeadContext {
597    /// Create a new secure WASM AEAD context
598    #[cfg_attr(feature = "wasm", wasm_bindgen(constructor))]
599    pub fn new() -> Result<SecureWasmAeadContext, JsValue> {
600        let provider = Box::new(LibQCryptoProvider::new()?);
601        let inner = AeadContext::with_provider(provider);
602        let security_validator = SecurityValidator::new()?;
603
604        Ok(SecureWasmAeadContext {
605            inner,
606            security_validator,
607        })
608    }
609
610    /// Encrypt data
611    pub fn encrypt(
612        &mut self,
613        algorithm: &str,
614        key: &Uint8Array,
615        nonce: &Uint8Array,
616        plaintext: &Uint8Array,
617        associated_data: Option<Uint8Array>,
618    ) -> Result<JsValue, JsValue> {
619        // Parse and validate algorithm
620        let algorithm = parse_algorithm_wasm(algorithm)?;
621
622        // Validate algorithm category
623        match convert_result(
624            self.security_validator
625                .validate_algorithm_category(algorithm, AlgorithmCategory::Aead),
626        ) {
627            Ok(_) => {}
628            Err(error) => return Err(error),
629        }
630
631        // Convert inputs
632        let key_bytes = key.to_vec();
633        let nonce_bytes = nonce.to_vec();
634        let plaintext_bytes = plaintext.to_vec();
635        let associated_data_bytes = associated_data.map(|ad| ad.to_vec());
636
637        // Validate key size
638        match convert_result(
639            self.security_validator
640                .validate_key_size(algorithm, &key_bytes, true),
641        ) {
642            Ok(_) => {}
643            Err(error) => return Err(error),
644        }
645
646        // Validate nonce
647        match convert_result(self.security_validator.validate_nonce(&nonce_bytes)) {
648            Ok(_) => {}
649            Err(error) => return Err(error),
650        }
651
652        // Validate message size
653        match convert_result(
654            self.security_validator
655                .validate_aead_message(&plaintext_bytes),
656        ) {
657            Ok(_) => {}
658            Err(error) => return Err(error),
659        }
660
661        // Create key and nonce objects
662        let aead_key = AeadKey::new(key_bytes.to_vec());
663        let aead_nonce = Nonce::new(nonce_bytes.to_vec());
664
665        // Encrypt
666        let ciphertext = convert_result(self.inner.encrypt(
667            algorithm,
668            &aead_key,
669            &aead_nonce,
670            &plaintext_bytes,
671            associated_data_bytes.as_deref(),
672        ))?;
673
674        // Serialize and return
675        match secure_serialize(&ciphertext) {
676            Ok(value) => Ok(value),
677            Err(error) => Err(error),
678        }
679    }
680
681    /// Decrypt data (Layer A `Result` ABI only; see [`crate::AeadDecryptSemantic`] for Layer B).
682    pub fn decrypt(
683        &self,
684        algorithm: &str,
685        key: &Uint8Array,
686        nonce: &Uint8Array,
687        ciphertext: &Uint8Array,
688        associated_data: Option<Uint8Array>,
689    ) -> Result<JsValue, JsValue> {
690        // Parse and validate algorithm
691        let algorithm = parse_algorithm_wasm(algorithm)?;
692
693        // Validate algorithm category
694        match convert_result(
695            self.security_validator
696                .validate_algorithm_category(algorithm, AlgorithmCategory::Aead),
697        ) {
698            Ok(_) => {}
699            Err(error) => return Err(error),
700        }
701
702        // Convert inputs
703        let key_bytes = key.to_vec();
704        let nonce_bytes = nonce.to_vec();
705        let ciphertext_bytes = ciphertext.to_vec();
706        let associated_data_bytes = associated_data.map(|ad| ad.to_vec());
707
708        // Validate key size
709        match convert_result(
710            self.security_validator
711                .validate_key_size(algorithm, &key_bytes, true),
712        ) {
713            Ok(_) => {}
714            Err(error) => return Err(error),
715        }
716
717        // Validate nonce
718        match convert_result(self.security_validator.validate_nonce(&nonce_bytes)) {
719            Ok(_) => {}
720            Err(error) => return Err(error),
721        }
722
723        // Validate message size
724        match convert_result(
725            self.security_validator
726                .validate_aead_message(&ciphertext_bytes),
727        ) {
728            Ok(_) => {}
729            Err(error) => return Err(error),
730        }
731
732        // Create key and nonce objects
733        let aead_key = AeadKey::new(key_bytes.to_vec());
734        let aead_nonce = Nonce::new(nonce_bytes.to_vec());
735
736        // Decrypt
737        let plaintext = convert_result(self.inner.decrypt(
738            algorithm,
739            &aead_key,
740            &aead_nonce,
741            &ciphertext_bytes,
742            associated_data_bytes.as_deref(),
743        ))?;
744
745        // Serialize and return
746        match secure_serialize(&plaintext) {
747            Ok(value) => Ok(value),
748            Err(error) => Err(error),
749        }
750    }
751
752    /// Get supported algorithms
753    pub fn get_supported_algorithms(&self) -> Result<JsValue, JsValue> {
754        let algorithms = alloc::vec!["saturnin", "shake256-aead"];
755        match secure_serialize(&algorithms) {
756            Ok(value) => Ok(value),
757            Err(error) => Err(error),
758        }
759    }
760}