Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of secp256k1 Group #844

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,623 changes: 662 additions & 961 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions fastcrypto-tbls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ tap = { version = "1.0.1", features = [] }
[dev-dependencies]
criterion = "0.4.0"
generic-tests = "0.1.2"
paste = "1.0.15"

[[bench]]
name = "polynomial"
Expand Down
165 changes: 133 additions & 32 deletions fastcrypto-tbls/src/tests/dkg_v1_tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::dkg::{Confirmation, Party, DKG_MESSAGES_MAX_SIZE};
use crate::dkg::{Confirmation, Output, Party, DKG_MESSAGES_MAX_SIZE};
use crate::dkg_v0::create_fake_complaint;
use crate::dkg_v1::{Message, ProcessedMessage};
use crate::ecies::{PrivateKey, PublicKey};
Expand All @@ -12,29 +12,54 @@ use crate::random_oracle::RandomOracle;
use crate::tbls::ThresholdBls;
use crate::types::ThresholdBls12381MinSig;
use fastcrypto::error::FastCryptoError;
use fastcrypto::groups::bls12381::{G2Element, Scalar};
use fastcrypto::groups::GroupElement;
use fastcrypto::groups::bls12381::G2Element;
use fastcrypto::groups::secp256k1::ProjectivePoint;
use fastcrypto::groups::{FiatShamirChallenge, GroupElement, HashToGroupElement, MultiScalarMul};
use fastcrypto::traits::AllowedRng;
use itertools::Itertools;
use paste::paste;
use rand::prelude::StdRng;
use rand::rngs::ThreadRng;
use rand::{thread_rng, SeedableRng};
use serde::de::DeserializeOwned;
use serde::Serialize;
use zeroize::Zeroize;

const MSG: [u8; 4] = [1, 2, 3, 4];

type G = G2Element;
type S = ThresholdBls12381MinSig;
type EG = G2Element;

type KeyNodePair<EG> = (PartyId, PrivateKey<EG>, PublicKey<EG>);

fn gen_keys_and_nodes(n: usize) -> (Vec<KeyNodePair<EG>>, Nodes<EG>) {
gen_keys_and_nodes_rng(n, &mut thread_rng())
macro_rules! generate_tests {
($test_fn:ident, $( ($type1:ty, $type2:ty, $alias:ident) ),* $(,)? ) => {
$(
paste! {
#[test]
fn [<test_ $test_fn _ $alias>]() {
$test_fn::<$type1, $type2>();
}
}
)*
}
}

fn gen_keys_and_nodes_rng<R: AllowedRng>(
fn gen_keys_and_nodes<EG>(n: usize) -> (Vec<KeyNodePair<EG>>, Nodes<EG>)
where
EG: GroupElement + Serialize + DeserializeOwned,
EG::ScalarType: FiatShamirChallenge + Zeroize,
{
gen_keys_and_nodes_rng::<EG, ThreadRng>(n, &mut thread_rng())
}

fn gen_keys_and_nodes_rng<EG, R: AllowedRng>(
n: usize,
rng: &mut R,
) -> (Vec<KeyNodePair<EG>>, Nodes<EG>) {
) -> (Vec<KeyNodePair<EG>>, Nodes<EG>)
where
EG: GroupElement + Serialize + DeserializeOwned,
EG::ScalarType: FiatShamirChallenge + Zeroize,
{
let keys = (0..n)
.map(|id| {
let sk = PrivateKey::<EG>::new(rng);
Expand All @@ -54,10 +79,12 @@ fn gen_keys_and_nodes_rng<R: AllowedRng>(
(keys, nodes)
}

// Enable if logs are needed
// #[traced_test]
#[test]
fn test_dkg_e2e_5_parties_min_weight_2_threshold_3() {
fn dkg_e2e_5_parties_min_weight_2_threshold_3<G, EG>() -> (u16, Vec<Option<Output<G, EG>>>)
where
G: GroupElement + MultiScalarMul + Serialize + DeserializeOwned,
EG: GroupElement + Serialize + DeserializeOwned + HashToGroupElement,
EG::ScalarType: FiatShamirChallenge + Zeroize,
{
let ro = RandomOracle::new("dkg");
let t = 3;
let (keys, nodes) = gen_keys_and_nodes(6);
Expand Down Expand Up @@ -250,7 +277,7 @@ fn test_dkg_e2e_5_parties_min_weight_2_threshold_3() {
assert_eq!(ver_msg5.len(), 2);

let o0 = d0.aggregate_v1(&ver_msg0);
let _o1 = d1.aggregate_v1(&ver_msg1);
let o1 = d1.aggregate_v1(&ver_msg1);
let o2 = d2.aggregate_v1(&ver_msg2);
let o3 = d3.aggregate_v1(&ver_msg3);
let o5 = d5.aggregate_v1(&ver_msg5);
Expand All @@ -267,6 +294,16 @@ fn test_dkg_e2e_5_parties_min_weight_2_threshold_3() {
poly += &msg5.vss_pk;
assert_eq!(poly, o0.vss_pk);

(
t,
vec![Some(o0), Some(o1), Some(o2), Some(o3), None, Some(o5)],
)
}

fn sign_with_shares(threshold: u16, outputs: Vec<Option<Output<G2Element, G2Element>>>) {
let o0 = outputs[0].clone().unwrap();
let o3 = outputs[3].clone().unwrap();

// Use the shares to sign the message.
let sig00 = S::partial_sign(&o0.shares.as_ref().unwrap()[0], &MSG);
let sig30 = S::partial_sign(&o3.shares.as_ref().unwrap()[0], &MSG);
Expand All @@ -277,16 +314,21 @@ fn test_dkg_e2e_5_parties_min_weight_2_threshold_3() {
S::partial_verify(&o3.vss_pk, &MSG, &sig31).unwrap();

let sigs = vec![sig00, sig30, sig31];
let sig = S::aggregate(d0.t(), sigs.iter()).unwrap();
let sig = S::aggregate(threshold, sigs.iter()).unwrap();
S::verify(o0.vss_pk.c0(), &MSG, &sig).unwrap();
}

fn decrypt_and_prepare_for_reenc(
fn decrypt_and_prepare_for_reenc<G, EG>(
keys: &[KeyNodePair<EG>],
nodes: &Nodes<EG>,
msg0: &Message<G, EG>,
ro: &RandomOracle,
) -> Vec<(PublicKey<EG>, Vec<u8>)> {
) -> Vec<(PublicKey<EG>, Vec<u8>)>
where
G: GroupElement + MultiScalarMul + Serialize + DeserializeOwned,
EG: GroupElement + Serialize + DeserializeOwned + HashToGroupElement,
EG::ScalarType: FiatShamirChallenge + Zeroize,
{
nodes
.iter()
.map(|n| {
Expand All @@ -303,8 +345,21 @@ fn decrypt_and_prepare_for_reenc(
.collect::<Vec<_>>()
}

// Enable if logs are needed
// #[traced_test]
#[test]
fn test_party_new_errors() {
fn test_dkg_e2e_5_parties_min_weight_2_threshold_3() {
dkg_e2e_5_parties_min_weight_2_threshold_3::<ProjectivePoint, ProjectivePoint>();
let (threshold, outputs) = dkg_e2e_5_parties_min_weight_2_threshold_3::<G2Element, G2Element>();
sign_with_shares(threshold, outputs);
}

fn party_new_errors<G, EG>()
where
G: GroupElement + MultiScalarMul + Serialize + DeserializeOwned,
EG: GroupElement + Serialize + DeserializeOwned + HashToGroupElement,
EG::ScalarType: FiatShamirChallenge + Zeroize,
{
let ro = RandomOracle::new("dkg");
let (keys, nodes) = gen_keys_and_nodes(4);

Expand Down Expand Up @@ -337,8 +392,18 @@ fn test_party_new_errors() {
.is_err());
}

#[test]
fn test_process_message_failures() {
generate_tests!(
party_new_errors,
(ProjectivePoint, ProjectivePoint, secp256k1),
(G2Element, G2Element, bls12381),
);

fn process_message_failures<G, EG>()
where
G: GroupElement + MultiScalarMul + Serialize + DeserializeOwned,
EG: GroupElement + Serialize + DeserializeOwned + HashToGroupElement,
EG::ScalarType: FiatShamirChallenge + Zeroize,
{
let ro = RandomOracle::new("dkg");
let t = 3;
let (keys, nodes) = gen_keys_and_nodes(4);
Expand Down Expand Up @@ -468,8 +533,18 @@ fn test_process_message_failures() {
};
}

#[test]
fn test_test_process_confirmations() {
generate_tests!(
process_message_failures,
(ProjectivePoint, ProjectivePoint, secp256k1),
(G2Element, G2Element, bls12381),
);

fn process_confirmations<G, EG>()
where
G: GroupElement + MultiScalarMul + Serialize + DeserializeOwned,
EG: GroupElement + Serialize + DeserializeOwned + HashToGroupElement,
EG::ScalarType: FiatShamirChallenge + Zeroize,
{
let ro = RandomOracle::new("dkg");
let t = 3;
let (keys, nodes) = gen_keys_and_nodes(6);
Expand Down Expand Up @@ -607,8 +682,18 @@ fn test_test_process_confirmations() {
);
}

#[test]
fn create_message_generates_valid_message() {
generate_tests!(
process_confirmations,
(ProjectivePoint, ProjectivePoint, secp256k1),
(G2Element, G2Element, bls12381),
);

fn create_message_generates_valid_message<G, EG>()
where
G: GroupElement + MultiScalarMul + Serialize + DeserializeOwned,
EG: GroupElement + Serialize + DeserializeOwned + HashToGroupElement,
EG::ScalarType: FiatShamirChallenge + Zeroize,
{
let (keys, nodes) = gen_keys_and_nodes(4);
let d = Party::<G, EG>::new(
keys.get(1_usize).unwrap().1.clone(),
Expand All @@ -625,18 +710,28 @@ fn create_message_generates_valid_message() {
assert_eq!(msg.vss_pk.degree(), 2);
}

#[test]
fn test_size_limits() {
generate_tests!(
create_message_generates_valid_message,
(ProjectivePoint, ProjectivePoint, secp256k1),
(G2Element, G2Element, bls12381),
);

fn size_limits<G, EG>()
where
G: GroupElement,
EG: GroupElement + Serialize + DeserializeOwned + HashToGroupElement,
EG::ScalarType: FiatShamirChallenge + Zeroize,
{
// Confirm that messages sizes are within the limit for the extreme expected parameters.
let n = 3333;
let t = n / 3;
let k = 400;

// an approximation of the weights
let w = n / k;
let shares = (0..w).map(Scalar::from).collect_vec();
let shares = (0..w).map(G::ScalarType::from).collect_vec();

let p = Poly::<<G2Element as GroupElement>::ScalarType>::rand(t as u16, &mut thread_rng());
let p = Poly::<EG::ScalarType>::rand(t as u16, &mut thread_rng());
let ro = RandomOracle::new("test");
let keys_and_msg = (0..k)
.map(|_| {
Expand Down Expand Up @@ -670,38 +765,44 @@ fn test_size_limits() {
assert!(bcs::to_bytes(&conf).unwrap().len() <= DKG_MESSAGES_MAX_SIZE);
}

generate_tests!(
size_limits,
(ProjectivePoint, ProjectivePoint, secp256k1),
(G2Element, G2Element, bls12381),
);

#[test]
fn test_serialized_message_regression() {
let ro = RandomOracle::new("dkg");
let t = 3;
let mut rng = StdRng::from_seed([1; 32]);
let (keys, nodes) = gen_keys_and_nodes_rng(6, &mut rng);

let d0 = Party::<G, EG>::new(
let d0 = Party::<G2Element, G2Element>::new(
keys.first().unwrap().1.clone(),
nodes.clone(),
t,
ro.clone(),
&mut rng,
)
.unwrap();
let d1 = Party::<G, EG>::new(
let d1 = Party::<G2Element, G2Element>::new(
keys.get(1_usize).unwrap().1.clone(),
nodes.clone(),
t,
ro.clone(),
&mut rng,
)
.unwrap();
let _d2 = Party::<G, EG>::new(
let _d2 = Party::<G2Element, G2Element>::new(
keys.get(2_usize).unwrap().1.clone(),
nodes.clone(),
t,
ro.clone(),
&mut rng,
)
.unwrap();
let d3 = Party::<G, EG>::new(
let d3 = Party::<G2Element, G2Element>::new(
keys.get(3_usize).unwrap().1.clone(),
nodes.clone(),
t,
Expand Down
20 changes: 17 additions & 3 deletions fastcrypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,31 @@ description = "Common cryptographic library used at Mysten Labs"
repository = "https://github.com/MystenLabs/fastcrypto"

[dependencies]
k256 = { version = "0.13.4", features = [
"ecdsa",
"sha256",
"hash2curve",
"arithmetic",
"expose-field",
] }
base64ct = { version = "1.5.3", features = ["alloc"] }
bs58 = "0.4.0"
ed25519-consensus = { version = "2.1.0", features = ["serde"] }
hex = "0.4.3"
hex-literal = "0.4.1"
hkdf = { version = "0.12.3", features = ["std"] }
rand.workspace = true
rust_secp256k1 = { version = "0.27.0", package = "secp256k1", features = ["recovery", "rand-std", "bitcoin_hashes", "global-context"] }
rust_secp256k1 = { version = "0.27.0", package = "secp256k1", features = [
"recovery",
"rand-std",
"bitcoin_hashes",
"global-context",
] }
serde.workspace = true
serde_with = { version = "3", default-features = false, features = ["alloc", "macros"] }
serde_with = { version = "3", default-features = false, features = [
"alloc",
"macros",
] }
signature = { version = "2.0.0" }
tokio = { version = "1.24.1", features = ["sync", "rt", "macros"] }
zeroize.workspace = true
Expand Down Expand Up @@ -115,7 +130,6 @@ aes = ["dep:aes", "dep:cbc", "dep:aes-gcm", "dep:ctr"]

[dev-dependencies]
criterion = "0.4.0"
k256 = { version = "0.11.6", features = ["ecdsa", "sha256", "keccak256"] }
proptest = "1.1.0"
serde-reflection = "0.3.6"
wycheproof = "0.5.0"
Expand Down
2 changes: 2 additions & 0 deletions fastcrypto/src/groups/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use std::ops::{AddAssign, SubAssign};

pub mod bls12381;
pub mod ristretto255;
#[cfg(any(test, feature = "experimental"))]
pub mod secp256k1;
benr-ml marked this conversation as resolved.
Show resolved Hide resolved
pub mod secp256r1;

pub mod multiplier;
Expand Down
Loading