Skip to content

Commit

Permalink
expose ElGamalPubkey and ElGamalKeypair via wasm_bindgen
Browse files Browse the repository at this point in the history
  • Loading branch information
samkim-crypto committed Sep 30, 2024
1 parent 002808a commit 8923382
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 7 deletions.
24 changes: 17 additions & 7 deletions zk-sdk/src/encryption/elgamal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ use {
sha3::Sha3_512,
std::{convert::TryInto, fmt},
subtle::{Choice, ConstantTimeEq},
wasm_bindgen::prelude::*,
zeroize::Zeroize,
};

Expand Down Expand Up @@ -143,6 +144,7 @@ impl ElGamal {
/// A (twisted) ElGamal encryption keypair.
///
/// The instances of the secret key are zeroized on drop.
#[wasm_bindgen]
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, Zeroize)]
pub struct ElGamalKeypair {
/// The public half of this keypair.
Expand All @@ -151,6 +153,20 @@ pub struct ElGamalKeypair {
secret: ElGamalSecretKey,
}

#[wasm_bindgen]
impl ElGamalKeypair {
/// Generates the public and secret keys for ElGamal encryption.
///
/// This function is randomized. It internally samples a scalar element using `OsRng`.
pub fn new_rand() -> Self {
ElGamal::keygen()
}

pub fn pubkey_owned(&self) -> ElGamalPubkey {
self.public
}
}

impl ElGamalKeypair {
/// Create an ElGamal keypair from an ElGamal public key and an ElGamal secret key.
///
Expand Down Expand Up @@ -197,13 +213,6 @@ impl ElGamalKeypair {
Ok(Self::new(secret))
}

/// Generates the public and secret keys for ElGamal encryption.
///
/// This function is randomized. It internally samples a scalar element using `OsRng`.
pub fn new_rand() -> Self {
ElGamal::keygen()
}

pub fn pubkey(&self) -> &ElGamalPubkey {
&self.public
}
Expand Down Expand Up @@ -327,6 +336,7 @@ impl EncodableKeypair for ElGamalKeypair {
}

/// Public key for the ElGamal encryption scheme.
#[wasm_bindgen]
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, Zeroize)]
pub struct ElGamalPubkey(RistrettoPoint);
impl ElGamalPubkey {
Expand Down
2 changes: 2 additions & 0 deletions zk-sdk/src/encryption/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub mod grouped_elgamal;
#[cfg(not(target_os = "solana"))]
pub mod pedersen;
pub mod pod;
#[cfg(not(target_os = "solana"))]
pub mod wasm;

/// Byte length of an authenticated encryption secret key
pub const AE_KEY_LEN: usize = 16;
Expand Down
82 changes: 82 additions & 0 deletions zk-sdk/src/encryption/wasm/elgamal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use {
crate::{
encryption::{elgamal::ElGamalPubkey, pod::elgamal::PodElGamalPubkey},
wasm::display_to_jsvalue,
},
bytemuck::{Pod, Zeroable},
js_sys::{Array, Uint8Array},
wasm_bindgen::{prelude::*, JsCast},
};

#[wasm_bindgen]
#[derive(Clone, Copy, Default, Pod, Zeroable, PartialEq, Eq)]
#[repr(transparent)]
pub struct CompressedElGamalPubkey(PodElGamalPubkey);

#[allow(non_snake_case)]
#[wasm_bindgen]
impl CompressedElGamalPubkey {
/// Create a new `PodElGamalPubkey` object
///
/// * `value` - optional public key as a base64 encoded string, `Uint8Array`, `[number]`
#[wasm_bindgen(constructor)]
pub fn constructor(value: JsValue) -> Result<CompressedElGamalPubkey, JsValue> {
if let Some(base64_str) = value.as_string() {
base64_str
.parse::<PodElGamalPubkey>()
.map_err(display_to_jsvalue)
.map(CompressedElGamalPubkey)
} else if let Some(uint8_array) = value.dyn_ref::<Uint8Array>() {
bytemuck::try_from_bytes(&uint8_array.to_vec())
.map_err(|err| JsValue::from(format!("Invalid Uint8Array ElGamalPubkey: {err:?}")))
.map(|pubkey| CompressedElGamalPubkey(*pubkey))
} else if let Some(array) = value.dyn_ref::<Array>() {
let mut bytes = vec![];
let iterator = js_sys::try_iter(&array.values())?.expect("array to be iterable");
for x in iterator {
let x = x?;

if let Some(n) = x.as_f64() {
if (0. ..=255.).contains(&n) {
bytes.push(n as u8);
continue;
}
}
return Err(format!("Invalid array argument: {:?}", x).into());
}

bytemuck::try_from_bytes(&bytes)
.map_err(|err| JsValue::from(format!("Invalid Array pubkey: {err:?}")))
.map(|pubkey| CompressedElGamalPubkey(*pubkey))
} else if value.is_undefined() {
Ok(Self(PodElGamalPubkey::default()))
} else {
Err("Unsupported argument".into())
}
}

/// Return the base64 string representation of the public key
pub fn toString(&self) -> String {
self.0.to_string()
}

/// Checks if two `ElGamalPubkey`s are equal
pub fn equals(&self, other: &CompressedElGamalPubkey) -> bool {
self == other
}

/// Return the `Uint8Array` representation of the public key
pub fn toBytes(&self) -> Box<[u8]> {
self.0 .0.into()
}

pub fn compressed(decoded: &ElGamalPubkey) -> Self {
Self((*decoded).into())
}

pub fn decompressed(&self) -> Result<ElGamalPubkey, JsValue> {
self.0
.try_into()
.map_err(|err| JsValue::from(format!("Invalid ElGamalPubkey: {err:?}")))
}
}
1 change: 1 addition & 0 deletions zk-sdk/src/encryption/wasm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod elgamal;
2 changes: 2 additions & 0 deletions zk-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pub mod pod;
mod range_proof;
mod sigma_proofs;
mod transcript;
#[cfg(not(target_os = "solana"))]
pub mod wasm;
#[cfg(not(target_arch = "wasm32"))]
pub mod zk_elgamal_proof_program;

Expand Down
5 changes: 5 additions & 0 deletions zk-sdk/src/wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use wasm_bindgen::prelude::*;

pub fn display_to_jsvalue<T: std::fmt::Display>(display: T) -> JsValue {
display.to_string().into()
}

0 comments on commit 8923382

Please sign in to comment.