1#[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};
39use crate::providers::LibQCryptoProvider;
41use crate::security::SecurityValidator;
42use crate::traits::{
43 AeadKey,
44 Nonce,
45};
46use crate::wasm::conversions::WASM_SIGNATURE_ALGORITHM_IDS;
48use crate::wasm::error::{
49 convert_result,
50 error_to_js_value,
51 parse_algorithm_wasm,
52 };
54
55#[cfg_attr(feature = "wasm", wasm_bindgen)]
63pub struct WasmKemContext {
64 inner: KemContext,
65 security_validator: SecurityValidator,
66}
67
68impl WasmKemContext {
69 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 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 pub fn generate_keypair(
103 &mut self,
104 algorithm: &str,
105 randomness: Option<Uint8Array>,
106 ) -> Result<JsValue, JsValue> {
107 let algorithm = self
109 .parse_kem_algorithm(algorithm)
110 .map_err(error_to_js_value)?;
111
112 convert_result(
114 self.security_validator
115 .validate_algorithm_category(algorithm, algorithm.category()),
116 )?;
117
118 let randomness_vec = randomness.map(|rand| rand.to_vec());
120 let randomness_bytes = randomness_vec.as_deref();
121
122 let keypair = self
124 .inner
125 .generate_keypair(algorithm, randomness_bytes)
126 .map_err(error_to_js_value)?;
127
128 #[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 });
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 pub fn encapsulate(
155 &self,
156 algorithm: &str,
157 public_key_data: &Uint8Array,
158 randomness: Option<Uint8Array>,
159 ) -> Result<JsValue, JsValue> {
160 let algorithm = self
162 .parse_kem_algorithm(algorithm)
163 .map_err(error_to_js_value)?;
164
165 if public_key_data.length() == 0 {
167 return Err(JsValue::from_str("Invalid KEM public key: empty key"));
168 }
169
170 let randomness_vec = randomness.map(|rand| rand.to_vec());
172 let randomness_bytes = randomness_vec.as_deref();
173
174 let public_key = crate::traits::KemPublicKey::new(public_key_data.to_vec());
176
177 let (ciphertext, shared_secret) = self
179 .inner
180 .encapsulate(algorithm, &public_key, randomness_bytes)
181 .map_err(error_to_js_value)?;
182
183 #[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 });
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 pub fn decapsulate(
210 &self,
211 algorithm: &str,
212 secret_key_data: &Uint8Array,
213 ciphertext: &Uint8Array,
214 ) -> Result<Vec<u8>, JsValue> {
215 let algorithm = self
217 .parse_kem_algorithm(algorithm)
218 .map_err(error_to_js_value)?;
219
220 self.security_validator
222 .validate_algorithm_category(algorithm, AlgorithmCategory::Kem)
223 .map_err(error_to_js_value)?;
224
225 if secret_key_data.length() == 0 {
227 return Err(JsValue::from_str("Invalid KEM secret key: empty key"));
228 }
229
230 if ciphertext.length() == 0 {
232 return Err(JsValue::from_str("Invalid message size: empty data"));
233 }
234
235 let secret_key = crate::traits::KemSecretKey::new(secret_key_data.to_vec());
237
238 self.security_validator
240 .validate_secret_key(algorithm, secret_key.as_bytes())
241 .map_err(error_to_js_value)?;
242
243 self.security_validator
245 .validate_ciphertext(algorithm, &ciphertext.to_vec())
246 .map_err(error_to_js_value)?;
247
248 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 pub fn security_level(&self) -> u32 {
259 256 }
262
263 pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
265 self.parse_kem_algorithm(algorithm).is_ok()
266 }
267
268 pub fn supported_algorithms(&self) -> String {
270 #[allow(unused_mut)] 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 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#[cfg_attr(feature = "wasm", wasm_bindgen)]
298pub struct WasmSignatureContext {
299 inner: SignatureContext,
300 security_validator: SecurityValidator,
301}
302
303impl WasmSignatureContext {
304 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 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 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 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 pub fn generate_keypair(
355 &mut self,
356 algorithm: &str,
357 randomness: Option<Uint8Array>,
358 ) -> Result<JsValue, JsValue> {
359 let algorithm = self
361 .parse_signature_algorithm(algorithm)
362 .map_err(error_to_js_value)?;
363
364 convert_result(
366 self.security_validator
367 .validate_algorithm_category(algorithm, algorithm.category()),
368 )?;
369
370 let randomness_vec = randomness.map(|rand| rand.to_vec());
372 let randomness_bytes = randomness_vec.as_deref();
373
374 let keypair = self
376 .inner
377 .generate_keypair(algorithm, randomness_bytes)
378 .map_err(error_to_js_value)?;
379
380 #[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 });
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 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 let algorithm = self
409 .parse_signature_algorithm(algorithm)
410 .map_err(error_to_js_value)?;
411
412 if secret_key_data.length() == 0 {
414 return Err(JsValue::from_str("Invalid signature secret key: empty key"));
415 }
416
417 if message.length() == 0 {
419 return Err(JsValue::from_str("Invalid message size: empty data"));
420 }
421
422 let randomness_vec = randomness.map(|rand| rand.to_vec());
424 let randomness_bytes = randomness_vec.as_deref();
425
426 let secret_key = crate::traits::SigSecretKey::new(secret_key_data.to_vec());
428
429 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 pub fn verify(
439 &self,
440 algorithm: &str,
441 public_key_data: &Uint8Array,
442 message: &Uint8Array,
443 signature: &Uint8Array,
444 ) -> Result<bool, JsValue> {
445 let algorithm = self
447 .parse_signature_algorithm(algorithm)
448 .map_err(error_to_js_value)?;
449
450 if public_key_data.length() == 0 {
452 return Err(JsValue::from_str("Invalid signature public key: empty key"));
453 }
454
455 if message.length() == 0 {
457 return Err(JsValue::from_str("Invalid message size: empty data"));
458 }
459
460 if signature.length() == 0 {
462 return Err(JsValue::from_str("Invalid signature: empty data"));
463 }
464
465 let public_key = crate::traits::SigPublicKey::new(public_key_data.to_vec());
467
468 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 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 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 pub fn security_level(&self) -> u32 {
568 256 }
570
571 pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
573 self.parse_signature_algorithm(algorithm).is_ok()
574 }
575
576 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#[cfg_attr(feature = "wasm", wasm_bindgen)]
598pub struct WasmHashContext {
599 inner: HashContext,
600 security_validator: SecurityValidator,
601}
602
603impl WasmHashContext {
604 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 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 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 pub fn hash(&mut self, algorithm: &str, data: &Uint8Array) -> Result<JsValue, JsValue> {
642 let algorithm = self
644 .parse_hash_algorithm(algorithm)
645 .map_err(error_to_js_value)?;
646
647 self.security_validator
649 .validate_algorithm_category(algorithm, AlgorithmCategory::Hash)
650 .map_err(error_to_js_value)?;
651
652 self.security_validator
654 .validate_hash_input(&data.to_vec())
655 .map_err(error_to_js_value)?;
656
657 let hash = self
659 .inner
660 .hash(algorithm, &data.to_vec())
661 .map_err(error_to_js_value)?;
662
663 #[cfg(feature = "wasm")]
665 {
666 let result = serde_json::json!({
667 "hash": hash,
668 "algorithm": algorithm.to_string(),
669 "security_level": 256 });
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 pub fn security_level(&self) -> u32 {
685 256 }
687
688 pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
690 self.parse_hash_algorithm(algorithm).is_ok()
691 }
692
693 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 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#[cfg_attr(feature = "wasm", wasm_bindgen)]
750pub struct WasmAeadContext {
751 inner: AeadContext,
752 security_validator: SecurityValidator,
753}
754
755impl WasmAeadContext {
756 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 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 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 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 let algorithm = self
805 .parse_aead_algorithm(algorithm)
806 .map_err(error_to_js_value)?;
807
808 self.security_validator
810 .validate_algorithm_category(algorithm, AlgorithmCategory::Aead)
811 .map_err(error_to_js_value)?;
812
813 self.security_validator
815 .validate_key_size(algorithm, &key.to_vec(), true)
816 .map_err(error_to_js_value)?;
817
818 self.security_validator
820 .validate_nonce(&nonce.to_vec())
821 .map_err(error_to_js_value)?;
822
823 self.security_validator
825 .validate_aead_message(&plaintext.to_vec())
826 .map_err(error_to_js_value)?;
827
828 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 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 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 let algorithm = self
868 .parse_aead_algorithm(algorithm)
869 .map_err(error_to_js_value)?;
870
871 self.security_validator
873 .validate_algorithm_category(algorithm, AlgorithmCategory::Aead)
874 .map_err(error_to_js_value)?;
875
876 self.security_validator
878 .validate_key_size(algorithm, &key.to_vec(), true)
879 .map_err(error_to_js_value)?;
880
881 self.security_validator
883 .validate_nonce(&nonce.to_vec())
884 .map_err(error_to_js_value)?;
885
886 self.security_validator
888 .validate_ciphertext(algorithm, &ciphertext.to_vec())
889 .map_err(error_to_js_value)?;
890
891 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 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 pub fn security_level(&self) -> u32 {
917 256 }
919
920 pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
922 self.parse_aead_algorithm(algorithm).is_ok()
923 }
924
925 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 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#[cfg_attr(feature = "wasm", wasm_bindgen)]
953pub struct WasmCryptoProvider {
954 inner: LibQCryptoProvider,
955}
956
957impl WasmCryptoProvider {
958 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 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 pub fn is_algorithm_supported(&self, algorithm: &str) -> bool {
1002 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 pub fn supported_algorithms(&self) -> String {
1025 #[cfg(feature = "wasm")]
1026 {
1027 #[allow(unused_mut)] 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 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 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 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 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}