Skip to main content

kyberlib_wasm/
lib.rs

1// Copyright © 2024-2026 kyberlib. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! # `kyberlib-wasm`
5//!
6//! WebAssembly bindings for [`kyberlib`]. Exposes the KEM API
7//! (`keypair`, `encapsulate`, `decapsulate`) and the parameter
8//! constants as JS-callable functions via `wasm-bindgen`.
9//!
10//! Split out from the main `kyberlib` crate in v0.0.7 (issue #144) so
11//! the safe core has no `wasm-bindgen` dependency and can stay
12//! `#![forbid(unsafe_code)]` under default features.
13
14#![allow(non_snake_case)]
15#![deny(missing_docs)]
16
17extern crate alloc;
18
19use alloc::boxed::Box;
20use kyberlib::{
21    KyberLibError, KYBER_CIPHERTEXT_BYTES, KYBER_PUBLIC_KEY_BYTES,
22    KYBER_SECRET_KEY_BYTES, KYBER_SHARED_SECRET_BYTES,
23};
24use rand::rngs::OsRng;
25use wasm_bindgen::prelude::*;
26
27/// Generate a key pair for Kyber encryption.
28///
29/// # Errors
30///
31/// Returns a `JsError` if an error occurs during key pair generation.
32#[wasm_bindgen]
33pub fn keypair() -> Result<Keys, JsError> {
34    let mut rng = OsRng {};
35    match kyberlib::keypair(&mut rng) {
36        Ok(keys) => Ok(Keys {
37            pubkey: Box::new(keys.public),
38            secret: Box::new(keys.secret),
39        }),
40        Err(KyberLibError::RandomBytesGeneration) => {
41            Err(JsError::new("Error trying to fill random bytes"))
42        }
43        _ => Err(JsError::new("The keypair could not be generated")),
44    }
45}
46
47/// Encapsulate a shared secret using the provided public key.
48///
49/// # Arguments
50///
51/// * `pk` - The public key as a boxed slice of bytes.
52///
53/// # Errors
54///
55/// Returns a `JsValue` that is `null()` if the public key size is incorrect or if an error occurs during encapsulation.
56#[wasm_bindgen]
57pub fn encapsulate(pk: Box<[u8]>) -> Result<Kex, JsValue> {
58    if pk.len() != KYBER_PUBLIC_KEY_BYTES {
59        return Err(JsValue::null());
60    }
61
62    let mut rng = OsRng {};
63    match kyberlib::encapsulate(&pk, &mut rng) {
64        Ok(kex) => Ok(Kex {
65            ciphertext: Box::new(kex.0),
66            sharedSecret: Box::new(kex.1),
67        }),
68        Err(_) => Err(JsValue::null()),
69    }
70}
71
72/// Decapsulate a ciphertext using the provided secret key.
73///
74/// # Arguments
75///
76/// * `ct` - The ciphertext as a boxed slice of bytes.
77/// * `sk` - The secret key as a boxed slice of bytes.
78///
79/// # Errors
80///
81/// Returns a `JsValue` that is `null()` if the input sizes are incorrect or if an error occurs during decapsulation.
82#[wasm_bindgen]
83pub fn decapsulate(
84    ct: Box<[u8]>,
85    sk: Box<[u8]>,
86) -> Result<Box<[u8]>, JsValue> {
87    if ct.len() != KYBER_CIPHERTEXT_BYTES
88        || sk.len() != KYBER_SECRET_KEY_BYTES
89    {
90        return Err(JsValue::null());
91    }
92
93    match kyberlib::decapsulate(&ct, &sk) {
94        Ok(ss) => Ok(Box::new(ss)),
95        Err(_) => Err(JsValue::null()),
96    }
97}
98
99/// Represents Kyber key pair.
100#[wasm_bindgen]
101#[derive(Debug)]
102pub struct Keys {
103    pubkey: Box<[u8]>,
104    secret: Box<[u8]>,
105}
106
107/// Represents Kyber encapsulated shared secret.
108#[wasm_bindgen]
109#[derive(Debug)]
110pub struct Kex {
111    ciphertext: Box<[u8]>,
112    sharedSecret: Box<[u8]>,
113}
114
115#[wasm_bindgen]
116impl Keys {
117    /// Create a new key pair.
118    ///
119    /// This function generates a new Kyber key pair and returns it as a `Keys` struct.
120    ///
121    /// # Errors
122    ///
123    /// Returns a `JsError` if an error occurs during key pair generation.
124    #[wasm_bindgen(constructor)]
125    pub fn new() -> Result<Keys, JsError> {
126        keypair()
127    }
128
129    /// Get the public key.
130    ///
131    /// Returns the public key as a boxed slice of bytes.
132    #[wasm_bindgen(getter)]
133    pub fn pubkey(&self) -> Box<[u8]> {
134        self.pubkey.clone()
135    }
136
137    /// Get the secret key.
138    ///
139    /// Returns the secret key as a boxed slice of bytes.
140    #[wasm_bindgen(getter)]
141    pub fn secret(&self) -> Box<[u8]> {
142        self.secret.clone()
143    }
144}
145
146#[wasm_bindgen]
147impl Kex {
148    /// Create a new Kex instance by encapsulating with a given public key.
149    ///
150    /// # Arguments
151    ///
152    /// * `public_key` - The public key as a boxed slice of bytes.
153    ///
154    /// # Panics
155    ///
156    /// Panics if the public key size is incorrect.
157    #[wasm_bindgen(constructor)]
158    pub fn new(public_key: Box<[u8]>) -> Self {
159        encapsulate(public_key).expect("Invalid Public Key Size")
160    }
161
162    /// Get the ciphertext.
163    ///
164    /// Returns the ciphertext as a boxed slice of bytes.
165    #[wasm_bindgen(getter)]
166    pub fn ciphertext(&self) -> Box<[u8]> {
167        self.ciphertext.clone()
168    }
169
170    /// Get the shared secret.
171    ///
172    /// Returns the shared secret as a boxed slice of bytes.
173    #[wasm_bindgen(getter)]
174    pub fn sharedSecret(&self) -> Box<[u8]> {
175        self.sharedSecret.clone()
176    }
177
178    /// Set the ciphertext.
179    ///
180    /// # Arguments
181    ///
182    /// * `ciphertext` - The ciphertext as a boxed slice of bytes.
183    #[wasm_bindgen(setter)]
184    pub fn set_ciphertext(&mut self, ciphertext: Box<[u8]>) {
185        self.ciphertext = ciphertext;
186    }
187
188    /// Set the shared secret.
189    ///
190    /// # Arguments
191    ///
192    /// * `sharedSecret` - The shared secret as a boxed slice of bytes.
193    #[wasm_bindgen(setter)]
194    pub fn set_sharedSecret(&mut self, sharedSecret: Box<[u8]>) {
195        self.sharedSecret = sharedSecret;
196    }
197}
198
199/// Represents Kyber parameters.
200#[wasm_bindgen]
201#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
202pub struct Params {
203    /// The size of public key bytes.
204    #[wasm_bindgen(readonly)]
205    pub publicKeyBytes: usize,
206    /// The size of secret key bytes.
207    #[wasm_bindgen(readonly)]
208    pub secretKeyBytes: usize,
209    /// The size of ciphertext bytes.
210    #[wasm_bindgen(readonly)]
211    pub ciphertextBytes: usize,
212    /// The size of shared secret bytes.
213    #[wasm_bindgen(readonly)]
214    pub sharedSecretBytes: usize,
215}
216
217#[wasm_bindgen]
218impl Params {
219    /// Get the size of public key bytes.
220    #[wasm_bindgen(getter)]
221    pub fn publicKeyBytes() -> usize {
222        KYBER_PUBLIC_KEY_BYTES
223    }
224
225    /// Get the size of secret key bytes.
226    #[wasm_bindgen(getter)]
227    pub fn secretKeyBytes() -> usize {
228        KYBER_SECRET_KEY_BYTES
229    }
230
231    /// Get the size of ciphertext bytes.
232    #[wasm_bindgen(getter)]
233    pub fn ciphertextBytes() -> usize {
234        KYBER_CIPHERTEXT_BYTES
235    }
236
237    /// Get the size of shared secret bytes.
238    #[wasm_bindgen(getter)]
239    pub fn sharedSecretBytes() -> usize {
240        KYBER_SHARED_SECRET_BYTES
241    }
242}