pqc_binary_format/
wasm.rs

1//! WebAssembly bindings for PQC Binary Format
2//!
3//! This module provides WASM bindings for use in JavaScript/TypeScript environments.
4
5use std::collections::HashMap;
6use wasm_bindgen::prelude::*;
7
8use crate::{
9    Algorithm, CompressionParameters, EncParameters, FormatFlags, KemParameters, PqcBinaryFormat,
10    PqcMetadata, SigParameters,
11};
12
13/// WebAssembly wrapper for Algorithm
14#[wasm_bindgen]
15pub struct WasmAlgorithm {
16    inner: Algorithm,
17}
18
19#[wasm_bindgen]
20impl WasmAlgorithm {
21    /// Create a new algorithm from name
22    #[wasm_bindgen(constructor)]
23    pub fn new(name: &str) -> Result<WasmAlgorithm, JsValue> {
24        let inner = match name.to_lowercase().as_str() {
25            "classical" => Algorithm::Classical,
26            "hybrid" => Algorithm::Hybrid,
27            "post-quantum" | "postquantum" => Algorithm::PostQuantum,
28            "ml-kem-1024" | "mlkem1024" => Algorithm::MlKem1024,
29            "multi-algorithm" | "multialgorithm" => Algorithm::MultiAlgorithm,
30            "multi-kem" | "multikem" => Algorithm::MultiKem,
31            "multi-kem-triple" | "multikemtriple" => Algorithm::MultiKemTriple,
32            "quad-layer" | "quadlayer" => Algorithm::QuadLayer,
33            "pq3-stack" | "pq3stack" => Algorithm::Pq3Stack,
34            "lattice-code-hybrid" | "latticecodehybrid" => Algorithm::LatticeCodeHybrid,
35            _ => {
36                return Err(JsValue::from_str(&format!("Unknown algorithm: {}", name)));
37            }
38        };
39        Ok(Self { inner })
40    }
41
42    /// Get algorithm name
43    #[wasm_bindgen(getter)]
44    pub fn name(&self) -> String {
45        self.inner.name().to_string()
46    }
47
48    /// Get algorithm ID
49    #[wasm_bindgen(getter)]
50    pub fn id(&self) -> u16 {
51        self.inner.as_id()
52    }
53
54    /// Get all supported algorithm names
55    #[wasm_bindgen(js_name = supportedAlgorithms)]
56    pub fn supported_algorithms() -> Vec<JsValue> {
57        vec![
58            "classical",
59            "hybrid",
60            "post-quantum",
61            "ml-kem-1024",
62            "multi-algorithm",
63            "multi-kem",
64            "multi-kem-triple",
65            "quad-layer",
66            "pq3-stack",
67            "lattice-code-hybrid",
68        ]
69        .into_iter()
70        .map(JsValue::from_str)
71        .collect()
72    }
73}
74
75/// WebAssembly wrapper for EncParameters
76#[wasm_bindgen]
77pub struct WasmEncParameters {
78    inner: EncParameters,
79}
80
81#[wasm_bindgen]
82impl WasmEncParameters {
83    /// Create new encryption parameters
84    #[wasm_bindgen(constructor)]
85    pub fn new(iv: Vec<u8>, tag: Vec<u8>) -> Self {
86        Self {
87            inner: EncParameters {
88                iv,
89                tag,
90                params: HashMap::new(),
91            },
92        }
93    }
94
95    /// Get IV/nonce
96    #[wasm_bindgen(getter)]
97    pub fn iv(&self) -> Vec<u8> {
98        self.inner.iv.clone()
99    }
100
101    /// Set IV/nonce
102    #[wasm_bindgen(setter)]
103    pub fn set_iv(&mut self, iv: Vec<u8>) {
104        self.inner.iv = iv;
105    }
106
107    /// Get authentication tag
108    #[wasm_bindgen(getter)]
109    pub fn tag(&self) -> Vec<u8> {
110        self.inner.tag.clone()
111    }
112
113    /// Set authentication tag
114    #[wasm_bindgen(setter)]
115    pub fn set_tag(&mut self, tag: Vec<u8>) {
116        self.inner.tag = tag;
117    }
118}
119
120/// WebAssembly wrapper for KemParameters
121#[wasm_bindgen]
122pub struct WasmKemParameters {
123    inner: KemParameters,
124}
125
126#[wasm_bindgen]
127impl WasmKemParameters {
128    /// Create new KEM parameters
129    #[wasm_bindgen(constructor)]
130    pub fn new(public_key: Vec<u8>, ciphertext: Vec<u8>) -> Self {
131        Self {
132            inner: KemParameters {
133                public_key,
134                ciphertext,
135                params: HashMap::new(),
136            },
137        }
138    }
139
140    /// Get public key
141    #[wasm_bindgen(getter)]
142    pub fn public_key(&self) -> Vec<u8> {
143        self.inner.public_key.clone()
144    }
145
146    /// Get ciphertext
147    #[wasm_bindgen(getter)]
148    pub fn ciphertext(&self) -> Vec<u8> {
149        self.inner.ciphertext.clone()
150    }
151}
152
153/// WebAssembly wrapper for SigParameters
154#[wasm_bindgen]
155pub struct WasmSigParameters {
156    inner: SigParameters,
157}
158
159#[wasm_bindgen]
160impl WasmSigParameters {
161    /// Create new signature parameters
162    #[wasm_bindgen(constructor)]
163    pub fn new(public_key: Vec<u8>, signature: Vec<u8>) -> Self {
164        Self {
165            inner: SigParameters {
166                public_key,
167                signature,
168                params: HashMap::new(),
169            },
170        }
171    }
172
173    /// Get public key
174    #[wasm_bindgen(getter)]
175    pub fn public_key(&self) -> Vec<u8> {
176        self.inner.public_key.clone()
177    }
178
179    /// Get signature
180    #[wasm_bindgen(getter)]
181    pub fn signature(&self) -> Vec<u8> {
182        self.inner.signature.clone()
183    }
184}
185
186/// WebAssembly wrapper for CompressionParameters
187#[wasm_bindgen]
188pub struct WasmCompressionParameters {
189    inner: CompressionParameters,
190}
191
192#[wasm_bindgen]
193impl WasmCompressionParameters {
194    /// Create new compression parameters
195    #[wasm_bindgen(constructor)]
196    pub fn new(algorithm: String, level: u8, original_size: u64) -> Self {
197        Self {
198            inner: CompressionParameters {
199                algorithm,
200                level,
201                original_size,
202                params: HashMap::new(),
203            },
204        }
205    }
206
207    /// Get compression algorithm
208    #[wasm_bindgen(getter)]
209    pub fn algorithm(&self) -> String {
210        self.inner.algorithm.clone()
211    }
212
213    /// Get compression level
214    #[wasm_bindgen(getter)]
215    pub fn level(&self) -> u8 {
216        self.inner.level
217    }
218
219    /// Get original size
220    #[wasm_bindgen(getter)]
221    pub fn original_size(&self) -> u64 {
222        self.inner.original_size
223    }
224}
225
226/// WebAssembly wrapper for PqcMetadata
227#[wasm_bindgen]
228pub struct WasmPqcMetadata {
229    enc_params: WasmEncParameters,
230    kem_params: Option<WasmKemParameters>,
231    sig_params: Option<WasmSigParameters>,
232    compression_params: Option<WasmCompressionParameters>,
233}
234
235#[wasm_bindgen]
236impl WasmPqcMetadata {
237    /// Create new metadata
238    #[wasm_bindgen(constructor)]
239    pub fn new(enc_params: WasmEncParameters) -> Self {
240        Self {
241            enc_params,
242            kem_params: None,
243            sig_params: None,
244            compression_params: None,
245        }
246    }
247
248    /// Set KEM parameters
249    #[wasm_bindgen(js_name = setKemParams)]
250    pub fn set_kem_params(&mut self, kem_params: WasmKemParameters) {
251        self.kem_params = Some(kem_params);
252    }
253
254    /// Set signature parameters
255    #[wasm_bindgen(js_name = setSigParams)]
256    pub fn set_sig_params(&mut self, sig_params: WasmSigParameters) {
257        self.sig_params = Some(sig_params);
258    }
259
260    /// Set compression parameters
261    #[wasm_bindgen(js_name = setCompressionParams)]
262    pub fn set_compression_params(&mut self, compression_params: WasmCompressionParameters) {
263        self.compression_params = Some(compression_params);
264    }
265
266    fn to_rust(&self) -> PqcMetadata {
267        PqcMetadata {
268            kem_params: self.kem_params.as_ref().map(|k| k.inner.clone()),
269            sig_params: self.sig_params.as_ref().map(|s| s.inner.clone()),
270            enc_params: self.enc_params.inner.clone(),
271            compression_params: self.compression_params.as_ref().map(|c| c.inner.clone()),
272            custom: HashMap::new(),
273        }
274    }
275}
276
277/// WebAssembly wrapper for FormatFlags
278#[wasm_bindgen]
279pub struct WasmFormatFlags {
280    inner: FormatFlags,
281}
282
283#[wasm_bindgen]
284impl WasmFormatFlags {
285    /// Create new empty flags
286    #[wasm_bindgen(constructor)]
287    pub fn new() -> Self {
288        Self {
289            inner: FormatFlags::new(),
290        }
291    }
292
293    /// Enable compression flag
294    #[wasm_bindgen(js_name = withCompression)]
295    pub fn with_compression(&self) -> WasmFormatFlags {
296        Self {
297            inner: self.inner.with_compression(),
298        }
299    }
300
301    /// Enable streaming flag
302    #[wasm_bindgen(js_name = withStreaming)]
303    pub fn with_streaming(&self) -> WasmFormatFlags {
304        Self {
305            inner: self.inner.with_streaming(),
306        }
307    }
308
309    /// Enable additional auth flag
310    #[wasm_bindgen(js_name = withAdditionalAuth)]
311    pub fn with_additional_auth(&self) -> WasmFormatFlags {
312        Self {
313            inner: self.inner.with_additional_auth(),
314        }
315    }
316
317    /// Enable experimental features flag
318    #[wasm_bindgen(js_name = withExperimental)]
319    pub fn with_experimental(&self) -> WasmFormatFlags {
320        Self {
321            inner: self.inner.with_experimental(),
322        }
323    }
324
325    /// Check if compression is enabled
326    #[wasm_bindgen(js_name = hasCompression)]
327    pub fn has_compression(&self) -> bool {
328        self.inner.has_compression()
329    }
330
331    /// Check if streaming is enabled
332    #[wasm_bindgen(js_name = hasStreaming)]
333    pub fn has_streaming(&self) -> bool {
334        self.inner.has_streaming()
335    }
336
337    /// Check if additional auth is enabled
338    #[wasm_bindgen(js_name = hasAdditionalAuth)]
339    pub fn has_additional_auth(&self) -> bool {
340        self.inner.has_additional_auth()
341    }
342
343    /// Check if experimental features are enabled
344    #[wasm_bindgen(js_name = hasExperimental)]
345    pub fn has_experimental(&self) -> bool {
346        self.inner.has_experimental()
347    }
348}
349
350/// WebAssembly wrapper for PqcBinaryFormat
351#[wasm_bindgen]
352pub struct WasmPqcBinaryFormat {
353    inner: PqcBinaryFormat,
354}
355
356#[wasm_bindgen]
357impl WasmPqcBinaryFormat {
358    /// Create a new PQC Binary Format structure
359    ///
360    /// @param {WasmAlgorithm} algorithm - Algorithm to use
361    /// @param {WasmPqcMetadata} metadata - Metadata container
362    /// @param {Uint8Array} data - Encrypted data bytes
363    /// @returns {WasmPqcBinaryFormat} New instance
364    #[wasm_bindgen(constructor)]
365    pub fn new(
366        algorithm: WasmAlgorithm,
367        metadata: WasmPqcMetadata,
368        data: Vec<u8>,
369    ) -> WasmPqcBinaryFormat {
370        let rust_metadata = metadata.to_rust();
371        let inner = PqcBinaryFormat::new(algorithm.inner, rust_metadata, data);
372        Self { inner }
373    }
374
375    /// Create with specific flags
376    ///
377    /// @param {WasmAlgorithm} algorithm - Algorithm to use
378    /// @param {WasmFormatFlags} flags - Format flags
379    /// @param {WasmPqcMetadata} metadata - Metadata container
380    /// @param {Uint8Array} data - Encrypted data bytes
381    /// @returns {WasmPqcBinaryFormat} New instance
382    #[wasm_bindgen(js_name = withFlags)]
383    pub fn with_flags(
384        algorithm: WasmAlgorithm,
385        flags: WasmFormatFlags,
386        metadata: WasmPqcMetadata,
387        data: Vec<u8>,
388    ) -> WasmPqcBinaryFormat {
389        let rust_metadata = metadata.to_rust();
390        let inner = PqcBinaryFormat::with_flags(algorithm.inner, flags.inner, rust_metadata, data);
391        Self { inner }
392    }
393
394    /// Serialize to bytes
395    ///
396    /// @returns {Uint8Array} Serialized bytes
397    /// @throws {Error} If serialization fails
398    #[wasm_bindgen(js_name = toBytes)]
399    pub fn to_bytes(&self) -> Result<Vec<u8>, JsValue> {
400        self.inner
401            .to_bytes()
402            .map_err(|e| JsValue::from_str(&e.to_string()))
403    }
404
405    /// Deserialize from bytes
406    ///
407    /// @param {Uint8Array} data - Bytes to deserialize
408    /// @returns {WasmPqcBinaryFormat} Deserialized instance
409    /// @throws {Error} If deserialization fails
410    #[wasm_bindgen(js_name = fromBytes)]
411    pub fn from_bytes(data: &[u8]) -> Result<WasmPqcBinaryFormat, JsValue> {
412        let inner =
413            PqcBinaryFormat::from_bytes(data).map_err(|e| JsValue::from_str(&e.to_string()))?;
414        Ok(Self { inner })
415    }
416
417    /// Validate the format structure
418    ///
419    /// @throws {Error} If validation fails
420    pub fn validate(&self) -> Result<(), JsValue> {
421        self.inner
422            .validate()
423            .map_err(|e| JsValue::from_str(&e.to_string()))
424    }
425
426    /// Get algorithm
427    ///
428    /// @returns {WasmAlgorithm} Algorithm used
429    #[wasm_bindgen(getter)]
430    pub fn algorithm(&self) -> WasmAlgorithm {
431        WasmAlgorithm {
432            inner: self.inner.algorithm(),
433        }
434    }
435
436    /// Get encrypted data
437    ///
438    /// @returns {Uint8Array} Encrypted data
439    #[wasm_bindgen(getter)]
440    pub fn data(&self) -> Vec<u8> {
441        self.inner.data().to_vec()
442    }
443
444    /// Get format flags
445    ///
446    /// @returns {WasmFormatFlags} Format flags
447    #[wasm_bindgen(getter)]
448    pub fn flags(&self) -> WasmFormatFlags {
449        WasmFormatFlags {
450            inner: self.inner.flags(),
451        }
452    }
453
454    /// Get total serialized size
455    ///
456    /// @returns {number} Size in bytes
457    #[wasm_bindgen(js_name = totalSize)]
458    pub fn total_size(&self) -> usize {
459        self.inner.total_size()
460    }
461}
462
463/// Get the PQC Binary Format version
464#[wasm_bindgen(js_name = getVersion)]
465pub fn get_version() -> String {
466    crate::VERSION.to_string()
467}
468
469/// Get the PQC Binary Format spec version
470#[wasm_bindgen(js_name = getBinaryVersion)]
471pub fn get_binary_version() -> u8 {
472    crate::PQC_BINARY_VERSION
473}