Skip to content

Commit

Permalink
Threshold integration (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri authored May 15, 2024
2 parents 4367432 + 6a4e59c commit 7df95fe
Show file tree
Hide file tree
Showing 26 changed files with 1,261 additions and 362 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ jobs:
- name: Generate code coverage
run: cargo llvm-cov --workspace --release --lcov --output-path lcov.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v4.0.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: lcov.info
fail_ci_if_error: true

Expand Down
35 changes: 12 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,31 +72,20 @@ loop {
// can be done in parallel, with the results being assembled into `accum`
// sequentially in the host task.
let destinations = session.broadcast_destinations();
if let Some(destinations) = destinations {
let destinations = session.message_destinations();
for destination in destinations.iter() {
// In production usage, this will happen in a spawned task
let message = session.make_broadcast(&mut OsRng).unwrap();
for destination in destinations.iter() {
// <<< send out `message` to `destination` here >>>
}
}
// (since it can take some time to create a message),
// and the artifact will be sent back to the host task
// to be added to the accumulator.
let (message, artifact) = session
.make_message(&mut OsRng, destination)
.unwrap();
let destinations = session.direct_message_destinations();
if let Some(destinations) = destinations {
for destination in destinations.iter() {
// In production usage, this will happen in a spawned task
// (since it can take some time to create a message),
// and the artifact will be sent back to the host task
// to be added to the accumulator.
let (message, artifact) = session
.make_direct_message(&mut OsRng, destination)
.unwrap();
// <<< send out `message` to `destination` here >>>
// This will happen in a host task
accum.add_artifact(artifact).unwrap();
}
// <<< send out `message` to `destination` here >>>
// This will happen in a host task
accum.add_artifact(artifact).unwrap();
}
for preprocessed in cached_messages {
Expand Down
3 changes: 2 additions & 1 deletion synedrion-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ js-sys = "0.3.55"
getrandom = { version = "0.2", features = ["js"] }
rand_core = { version = "0.6.4", features = ["getrandom"] }
wasm-bindgen = "0.2.88"
wasm-bindgen-derive = "0.2"
wasm-bindgen-derive = "0.3"
serde = "1"

[dev-dependencies]
wasm-bindgen-test = "0.3.28"
63 changes: 47 additions & 16 deletions synedrion-wasm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::fmt;

use bincode::{
config::{
BigEndian, Bounded, RejectTrailing, VarintEncoding, WithOtherEndian, WithOtherIntEncoding,
Expand All @@ -7,8 +9,10 @@ use bincode::{
};
use js_sys::Error;
use rand_core::OsRng;
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
use wasm_bindgen_derive::TryFromJsValue;
use serde::{Serialize, Serializer};
use synedrion::k256::ecdsa;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen_derive::{try_from_js_array, try_from_js_option, TryFromJsValue};

use synedrion::TestParams;

Expand All @@ -19,32 +23,64 @@ const MAX_MSG_LEN: u64 = 1000 * 1000; // 1 MB

#[wasm_bindgen]
extern "C" {
/// A type alias for optional `SigningKey`
#[wasm_bindgen(typescript_type = "SigningKey | undefined")]
pub type OptionalSigningKey;

/// A type alias for optional `SigningKey`
#[wasm_bindgen(typescript_type = "VerifyingKey[]")]
pub type VerifyingKeyList;

}

fn map_js_err<T: fmt::Display>(err: T) -> Error {
Error::new(&format!("{err}"))
}

/// Secp256k1 signing key.
#[derive(TryFromJsValue)]
#[wasm_bindgen]
#[derive(Clone)]
pub struct SigningKey(synedrion::k256::ecdsa::SigningKey);
pub struct SigningKey(ecdsa::SigningKey);

#[wasm_bindgen]
impl SigningKey {
/// Creates the object from the serialized big-endian scalar
#[wasm_bindgen(js_name = fromBeBytes)]
pub fn from_be_bytes(bytes: &[u8]) -> Result<SigningKey, Error> {
synedrion::k256::ecdsa::SigningKey::from_slice(bytes)
ecdsa::SigningKey::from_slice(bytes)
.map(Self)
.map_err(|err| Error::new(&format!("{}", err)))
}
}

#[derive(TryFromJsValue)]
#[wasm_bindgen]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct VerifyingKey(ecdsa::VerifyingKey);

#[wasm_bindgen]
impl VerifyingKey {
#[wasm_bindgen]
pub fn random() -> Self {
Self(*ecdsa::SigningKey::random(&mut OsRng).verifying_key())
}
}

impl Serialize for VerifyingKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(self.0.to_encoded_point(true).as_bytes())
}
}

/// Synedrion key share.
#[derive(TryFromJsValue)]
#[wasm_bindgen]
#[derive(Clone)]
pub struct KeyShare(synedrion::KeyShare<TestParams>);
pub struct KeyShare(synedrion::KeyShare<TestParams, VerifyingKey>);

#[wasm_bindgen]
impl KeyShare {
Expand All @@ -61,21 +97,16 @@ impl KeyShare {
/// Creates a set of key shares.
#[wasm_bindgen(js_name = newCentralized)]
pub fn new_centralized(
num_parties: usize,
parties: &VerifyingKeyList,
signing_key: &OptionalSigningKey,
) -> Result<Vec<KeyShare>, Error> {
let sk_js: &JsValue = signing_key.as_ref();
let typed_sk: Option<SigningKey> = if sk_js.is_undefined() {
None
} else {
Some(SigningKey::try_from(sk_js).map_err(|err| Error::new(&err))?)
};

let backend_sk = typed_sk.map(|sk| sk.0);
let sk = try_from_js_option::<SigningKey>(signing_key).map_err(map_js_err)?;
let backend_sk = sk.map(|sk| sk.0);
let parties = try_from_js_array::<VerifyingKey>(parties).map_err(map_js_err)?;

let shares = synedrion::KeyShare::<TestParams>::new_centralized(
let shares = synedrion::KeyShare::<TestParams, VerifyingKey>::new_centralized(
&mut OsRng,
num_parties,
&parties,
backend_sk.as_ref(),
);
Ok(shares.into_vec().into_iter().map(KeyShare).collect())
Expand Down
8 changes: 5 additions & 3 deletions synedrion-wasm/tests-js/src/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {
KeyShare,
KeyShare, VerifyingKey
} from "synedrion";

describe("KeyShare.newCentralized()", () => {
it("creates shares", () => {
const shares = KeyShare.newCentralized(2, undefined);
const parties = [VerifyingKey.random(), VerifyingKey.random()];
const shares = KeyShare.newCentralized(parties, undefined);
expect(shares.length).toEqual(2);
expect(shares[0]).toEqual(expect.any(KeyShare));
expect(shares[1]).toEqual(expect.any(KeyShare));
Expand All @@ -13,7 +14,8 @@ describe("KeyShare.newCentralized()", () => {

describe("KeyShare", () => {
it("serializes", () => {
const shares = KeyShare.newCentralized(2, undefined);
const parties = [VerifyingKey.random(), VerifyingKey.random()];
const shares = KeyShare.newCentralized(parties, undefined);
expect(shares[0].toBytes()).toEqual(expect.any(Uint8Array));
});
});
18 changes: 16 additions & 2 deletions synedrion-wasm/tests/wasm.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_test::wasm_bindgen_test;

use synedrion_wasm::{KeyShare, SigningKey};
use synedrion_wasm::{KeyShare, SigningKey, VerifyingKey};

fn into_js_option<T, U>(val: Option<U>) -> T
where
Expand All @@ -15,10 +15,24 @@ where
js_val.unchecked_into::<T>()
}

fn into_js_array<T, U>(value: impl IntoIterator<Item = U>) -> T
where
JsValue: From<U>,
T: JsCast,
{
value
.into_iter()
.map(JsValue::from)
.collect::<js_sys::Array>()
.unchecked_into::<T>()
}

#[wasm_bindgen_test]
fn test_make_key_shares() {
let sk: Option<SigningKey> = None;
let shares: Vec<KeyShare> = KeyShare::new_centralized(3, &into_js_option(sk)).unwrap();
let parties = (0..3).map(|_| VerifyingKey::random()).collect::<Vec<_>>();
let shares: Vec<KeyShare> =
KeyShare::new_centralized(&into_js_array(parties), &into_js_option(sk)).unwrap();
let _shares_serialized = shares
.iter()
.map(|share| share.to_bytes())
Expand Down
5 changes: 4 additions & 1 deletion synedrion/benches/bench.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use criterion::{criterion_group, criterion_main, Criterion};
use rand_core::OsRng;

use synedrion::{cggmp21::benches, KeyShare, PresigningData, TestParams};
use synedrion::{
bench_internals::{benches, KeyShare, PresigningData},
TestParams,
};

fn bench_happy_paths(c: &mut Criterion) {
let mut group = c.benchmark_group("happy path");
Expand Down
4 changes: 4 additions & 0 deletions synedrion/src/bench_internals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//! Public exports for use in benchmarks.
pub use crate::cggmp21::benches;
pub use crate::common::{KeyShare, PresigningData};
2 changes: 1 addition & 1 deletion synedrion/src/cggmp21.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ mod sigma;
pub mod benches;

pub use params::{ProductionParams, SchemeParams, TestParams};
pub(crate) use protocols::{interactive_signing, key_gen, key_refresh};
pub(crate) use protocols::{interactive_signing, key_gen, key_init, key_refresh};
pub use protocols::{
InteractiveSigningError, InteractiveSigningProof, InteractiveSigningResult, KeyGenError,
KeyGenProof, KeyGenResult, KeyInitError, KeyInitResult, KeyRefreshResult, PresigningError,
Expand Down
3 changes: 2 additions & 1 deletion synedrion/src/cggmp21/protocols/key_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,12 +378,13 @@ impl<P: SchemeParams> FinalizableToResult for Round3<P> {
_payloads: BTreeMap<PartyIdx, <Self as Round>::Payload>,
_artifacts: BTreeMap<PartyIdx, <Self as Round>::Artifact>,
) -> Result<<Self::Result as ProtocolResult>::Success, FinalizeError<Self::Result>> {
let index = self.party_idx();
let all_data = self.others_data.into_vec(self.context.public_data);
let all_cap_x = all_data.into_iter().map(|data| data.cap_x).collect();
Ok(KeyShareSeed {
index,
secret_share: self.context.x,
public_shares: all_cap_x,
init_id: self.rid,
})
}
}
Expand Down
6 changes: 4 additions & 2 deletions synedrion/src/cggmp21/protocols/presigning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,11 @@ impl<P: SchemeParams> FirstRound for Round1<P> {
// This includes the info of $ssid$ in the paper
// (scheme parameters + public data from all shares - hashed in `share_set_id`),
// with the session randomness added.
let ssid_hash = Hash::new_with_dst(b"SSID")
let ssid_hash = Hash::new_with_dst(b"ShareSetID")
.chain_type::<P>()
.chain(&shared_randomness)
.chain(&key_share.share_set_id)
.chain_slice(&inputs.public_shares)
.chain_slice(&inputs.public_aux)
.finalize();

// TODO (#68): check that KeyShare is consistent with num_parties/party_idx
Expand Down
6 changes: 4 additions & 2 deletions synedrion/src/cggmp21/protocols/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,11 @@ impl<P: SchemeParams> FirstRound for Round1<P> {
// This includes the info of $ssid$ in the paper
// (scheme parameters + public data from all shares - hashed in `share_set_id`),
// with the session randomness added.
let ssid_hash = Hash::new_with_dst(b"SSID")
let ssid_hash = Hash::new_with_dst(b"ShareSetID")
.chain_type::<P>()
.chain(&shared_randomness)
.chain(&inputs.key_share.share_set_id)
.chain_slice(&inputs.key_share.public_shares)
.chain_slice(&inputs.key_share.public_aux)
.finalize();

let r = inputs.presigning.nonce;
Expand Down
Loading

0 comments on commit 7df95fe

Please sign in to comment.