Skip to main content

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