Skip to content

Commit

Permalink
wasm crypto: simpler conversion to/from Uint8Array
Browse files Browse the repository at this point in the history
  • Loading branch information
cairomassimo committed Feb 15, 2024
1 parent 194d7ba commit 54f422d
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 96 deletions.
5 changes: 4 additions & 1 deletion ng/wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ crate-type = ["cdylib", "rlib"]

[dependencies]
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
blsful = { version = "2", default-features = false, features = ["rust"] }
# TODO(cairomassimo): remove git/branch if https://github.com/mikelodder7/blsful/pull/3 lands on crates.io
blsful = { git = "https://github.com/cairomassimo/blsful.git", branch = "add-inner-point-share-u8-slice-conversions", version = "2", default-features = false, features = [
"rust",
] }
base64_light = "0.1"
getrandom = { version = "0.2", features = ["js"] }
hex = "0.4"
Expand Down
18 changes: 18 additions & 0 deletions ng/wasm/src/abi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
//! Utilities from converting to/from JS
use std::convert::TryFrom;

use js_sys::Uint8Array;
use serde::{de::DeserializeOwned, Serialize};
use serde_bytes::Bytes;
use wasm_bindgen::{JsCast, JsError, JsValue};

pub type JsResult<T> = Result<T, JsError>;
Expand All @@ -16,3 +20,17 @@ pub fn into_js<T: JsCast>(value: &(impl Serialize + ?Sized)) -> JsResult<T> {
.map_err(|v| JsError::new(&format!("unexpected serializer output type: {:?}", v)))?;
Ok(value)
}

pub fn from_uint8array<T: TryFrom<Vec<u8>>>(value: Uint8Array) -> JsResult<T> {
let value = from_js::<Vec<u8>>(value)?;
let value = T::try_from(value);
let value = value
.ok()
.ok_or_else(|| JsError::new("cannot deserialize"))?;

Ok(value)
}

pub fn into_uint8array<T: JsCast>(value: impl AsRef<[u8]>) -> JsResult<T> {
into_js(Bytes::new(value.as_ref()))
}
88 changes: 15 additions & 73 deletions ng/wasm/src/bls.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
use std::{
array::TryFromSliceError,
convert::{TryFrom, TryInto as _},
};
use std::convert::TryFrom;

use blsful::{
Bls12381G1Impl, Bls12381G2Impl, BlsSignatureImpl, InnerPointShareG1, InnerPointShareG2,
PublicKey, Signature, SignatureSchemes, TimeCryptCiphertext,
Bls12381G1Impl, Bls12381G2Impl, BlsSignatureImpl, PublicKey, Signature, SignatureSchemes,
TimeCryptCiphertext,
};
use elliptic_curve::group::GroupEncoding;
use js_sys::Uint8Array;
use serde::Deserialize;
use serde_bytes::Bytes;
use tsify::Tsify;
use wasm_bindgen::prelude::*;

use crate::abi::{from_js, into_js, JsResult};
use crate::abi::{from_js, from_uint8array, into_uint8array, JsResult};

#[derive(Tsify, Deserialize)]
#[tsify(from_wasm_abi)]
Expand All @@ -25,55 +21,30 @@ pub enum BlsVariant {

struct Bls<C>(C);

// TODO(cairomassimo): add missing `TryFrom` impls to `blsful` and remove ours once merged

pub trait TryFrom2<T>: Sized {
type Error;

fn try_from2(value: T) -> Result<Self, Self::Error>;
}

impl<'a> TryFrom2<&'a [u8]> for InnerPointShareG1 {
type Error = TryFromSliceError;

fn try_from2(bytes: &'a [u8]) -> Result<Self, Self::Error> {
Ok(Self(bytes.try_into()?))
}
}

impl<'a> TryFrom2<&'a [u8]> for InnerPointShareG2 {
type Error = TryFromSliceError;

fn try_from2(bytes: &'a [u8]) -> Result<Self, Self::Error> {
Ok(Self(bytes.try_into()?))
}
}

impl<C: BlsSignatureImpl> Bls<C>
where
C::PublicKey: for<'a> TryFrom<&'a [u8]>,
C::Signature: for<'a> TryFrom<&'a [u8]>,
C::SignatureShare: for<'a> TryFrom2<&'a [u8]>,
C::PublicKey: TryFrom<Vec<u8>>,
C::Signature: TryFrom<Vec<u8>>,
C::SignatureShare: TryFrom<Vec<u8>>,
{
pub fn combine(signature_shares: Vec<Uint8Array>) -> JsResult<Uint8Array> {
let signature_shares = signature_shares
.into_iter()
.map(Self::signature_share_from_js)
.map(from_uint8array)
.collect::<JsResult<Vec<_>>>()?;

let signature = C::core_combine_signature_shares(&signature_shares)?;
let signature = into_js(Bytes::new(signature.to_bytes().as_ref()))?;

Ok(signature)
into_uint8array(signature.to_bytes())
}

pub fn verify(
public_key: Uint8Array,
message: Uint8Array,
signature: Uint8Array,
) -> JsResult<()> {
let public_key = Self::public_key_from_js(public_key)?;
let signature = Self::signature_from_js(signature)?;
let public_key = from_uint8array(public_key)?;
let signature = from_uint8array(signature)?;
let message = from_js::<Vec<u8>>(message)?;

let signature = Signature::<C>::ProofOfPossession(signature);
Expand All @@ -88,7 +59,7 @@ where
message: Uint8Array,
identity: Uint8Array,
) -> JsResult<Uint8Array> {
let encryption_key = Self::public_key_from_js(encryption_key)?;
let encryption_key = from_uint8array(encryption_key)?;
let encryption_key = PublicKey::<C>(encryption_key);

let message = from_js::<Vec<u8>>(message)?;
Expand All @@ -100,50 +71,21 @@ where
identity,
)?;
let ciphertext = serde_bare::to_vec(&ciphertext)?;
let ciphertext = into_js(Bytes::new(&ciphertext))?;

Ok(ciphertext)
into_uint8array(ciphertext)
}

pub fn decrypt(ciphertext: Uint8Array, decryption_key: Uint8Array) -> JsResult<Uint8Array> {
let decryption_key = Self::signature_from_js(decryption_key)?;
let decryption_key = from_uint8array(decryption_key)?;

let ciphertext = from_js::<Vec<u8>>(ciphertext)?;
let ciphertext = serde_bare::from_slice::<TimeCryptCiphertext<C>>(&ciphertext)?;

let message = ciphertext.decrypt(&Signature::ProofOfPossession(decryption_key));
let message =
Option::<Vec<u8>>::from(message).ok_or_else(|| JsError::new("decryption failed"))?;
let message = into_js(Bytes::new(&message))?;

Ok(message)
}

fn public_key_from_js(k: Uint8Array) -> JsResult<C::PublicKey> {
let k = from_js::<Vec<u8>>(k)?;
let k = C::PublicKey::try_from(&k);
let k = k
.ok()
.ok_or_else(|| JsError::new("cannot deserialize public key"))?;
Ok(k)
}

fn signature_from_js(s: Uint8Array) -> JsResult<C::Signature> {
let s = from_js::<Vec<u8>>(s)?;
let s = C::Signature::try_from(&s);
let s = s
.ok()
.ok_or_else(|| JsError::new("cannot deserialize signature"))?;
Ok(s)
}

fn signature_share_from_js(s: Uint8Array) -> JsResult<C::SignatureShare> {
let s = from_js::<Vec<u8>>(s)?;
let s = C::SignatureShare::try_from2(&s);
let s = s
.ok()
.ok_or_else(|| JsError::new("cannot deserialize signature share"))?;
Ok(s)
into_uint8array(message)
}
}

Expand Down
9 changes: 3 additions & 6 deletions ng/wasm/src/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use serde_bytes::Bytes;
use tsify::Tsify;
use wasm_bindgen::{prelude::*, JsError};

use crate::abi::{from_js, into_js, JsResult};
use crate::abi::{from_js, into_js, into_uint8array, JsResult};

#[derive(Tsify, Deserialize)]
#[tsify(from_wasm_abi)]
Expand Down Expand Up @@ -85,10 +85,8 @@ where

let k = deriver.hd_derive_public_key(&public_keys);
let k = k.to_encoded_point(false);
let k = Bytes::new(k.as_bytes().as_ref());
let k = into_js(&k)?;

Ok(k)
into_uint8array(k.as_bytes())
}

fn scalar_from_js(s: Uint8Array) -> JsResult<C::Scalar> {
Expand Down Expand Up @@ -116,9 +114,8 @@ where
let s = s.to_repr();

let bytes = Self::concat_rsv(r, s, v);
let signature = into_js(Bytes::new(&bytes))?;

Ok(signature)
into_uint8array(bytes)
}

fn concat_rsv(
Expand Down
27 changes: 11 additions & 16 deletions ng/wasm/src/frost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ use frost_core::{
};
use js_sys::Uint8Array;
use serde::Deserialize;
use serde_bytes::Bytes;
use tsify::Tsify;
use wasm_bindgen::prelude::*;

use crate::abi::{from_js, into_js, JsResult};
use crate::abi::{from_js, into_uint8array, JsResult};

#[derive(Tsify, Deserialize)]
#[tsify(from_wasm_abi)]
Expand Down Expand Up @@ -94,9 +93,7 @@ pub fn combine_signature<C: Ciphersuite>(
&public_key_package,
)?;

let signature = into_js(Bytes::new(signature.serialize().as_ref()))?;

Ok(signature)
into_uint8array(signature.serialize())
}

pub fn verify_signature<C: Ciphersuite>(
Expand Down Expand Up @@ -178,14 +175,12 @@ mod tests {
use std::convert::TryInto;

use frost_core::Signature;
use js_sys::Uint8Array;
use rand::SeedableRng as _;
use serde_bytes::Bytes;
use wasm_bindgen::JsValue;
use wasm_bindgen_test::{console_log, wasm_bindgen_test};

use crate::{
abi::{from_js, into_js},
abi::{from_js, into_uint8array},
frost::combine_signature,
};

Expand All @@ -196,19 +191,19 @@ mod tests {

let msg = hex::decode("74657374").unwrap();

let message = into_js::<Uint8Array>(Bytes::new(&msg))?;
let public_key = into_js::<Uint8Array>(Bytes::new(
let message = into_uint8array(&msg)?;
let public_key = into_uint8array(
&hex::decode("899196af442a2c0d32d9c18b837a838379db18b37148bf35a4917202e0214658")
.unwrap(),
))?;
)?;

let identifiers = [
"0100000000000000000000000000000000000000000000000000000000000000",
"0200000000000000000000000000000000000000000000000000000000000000",
"0300000000000000000000000000000000000000000000000000000000000000",
]
.iter()
.map(|s| into_js::<Uint8Array>(Bytes::new(&hex::decode(s).unwrap())))
.map(|s| into_uint8array(hex::decode(s).unwrap()))
.collect::<Result<Vec<_>, _>>()?;

let hiding_nonces = [
Expand All @@ -217,7 +212,7 @@ mod tests {
"7762508c2d030f72359daf77e82c9ecdc99d39a2f36f7d9cbc69ba9153e85013",
]
.iter()
.map(|s| into_js::<Uint8Array>(Bytes::new(&hex::decode(s).unwrap())))
.map(|s| into_uint8array(hex::decode(s).unwrap()))
.collect::<Result<Vec<_>, _>>()?;

let binding_nonces = [
Expand All @@ -226,7 +221,7 @@ mod tests {
"1c2172836dc0b927e3d226458bd0be8d624cacca13fa82a258367eb025f41a38",
]
.iter()
.map(|s| into_js::<Uint8Array>(Bytes::new(&hex::decode(s).unwrap())))
.map(|s| into_uint8array(hex::decode(s).unwrap()))
.collect::<Result<Vec<_>, _>>()?;

let signature_shares = [
Expand All @@ -235,7 +230,7 @@ mod tests {
"2527bed7775274fd49c72e94beddb2cb16be356db29ac5b8a1bc795fc714e402",
]
.iter()
.map(|s| into_js::<Uint8Array>(Bytes::new(&hex::decode(s).unwrap())))
.map(|s| into_uint8array(hex::decode(s).unwrap()))
.collect::<Result<Vec<_>, _>>()?;

let verifying_shares = [
Expand All @@ -244,7 +239,7 @@ mod tests {
"3d6b6fdc64465c5d515770211fa981b799e3237b5d7023bf7f6a7e370add3ea7",
]
.iter()
.map(|s| into_js::<Uint8Array>(Bytes::new(&hex::decode(s).unwrap())))
.map(|s| into_uint8array(hex::decode(s).unwrap()))
.collect::<Result<Vec<_>, _>>()?;

let signature = combine_signature::<frost_ed25519::Ed25519Sha512>(
Expand Down

0 comments on commit 54f422d

Please sign in to comment.