Skip to content
Draft
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
11 changes: 5 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ authors = ["Doron <[email protected]>", "Omer <[email protected]>", "Denis <dsurv@ya
description = "threshold BLS library"
edition = "2018"


[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_derive = "1.0"
Expand All @@ -15,16 +14,16 @@ pairing-plus = "0.19"
ff-zeroize = "0.6.3"
round-based = { version = "0.1.0", features = [] }
thiserror = "1.0.23"
sha2 = "0.9"
old-sha2 = { package = "sha2", version = "0.8" }

[dependencies.curv]
git = "https://github.com/ZenGo-X/curv"
tag = "v0.6.2"
[dependencies.curv-kzen]
version = "0.9"
default-features = false

[dev-dependencies]
criterion = "0.3.3"
bls_sigs_ref = "0.3.0"
sha2 = "0.8.0"
round-based = { version = "0.1.0", features = ["dev"] }

# Example dependencies
Expand All @@ -48,7 +47,7 @@ tonic-build = "0.4.2"
crate-type = ["lib"]

[features]
default = ["curv/rust-gmp-kzen"]
default = ["curv-kzen/rust-gmp-kzen"]
# Internally used feature for testing purposes. You normally don't want to use it.
dev = []

Expand Down
3 changes: 0 additions & 3 deletions build.rs

This file was deleted.

14 changes: 6 additions & 8 deletions src/aggregated_bls/mod.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
#![allow(non_snake_case)]

use curv::cryptographic_primitives::hashing::hash_sha256::HSha256;
use curv::cryptographic_primitives::hashing::traits::Hash;
use curv::elliptic::curves::bls12_381::g2::GE as GE2;
use curv::elliptic::curves::traits::ECScalar;
use curv::cryptographic_primitives::hashing::{Digest, DigestExt};
use curv::elliptic::curves::*;
use curv::BigInt;
use sha2::Sha256;

pub mod party_i;
#[cfg(any(test, feature = "dev"))]
pub mod test;

pub fn h1(index: usize, pk_vec: &[GE2]) -> BigInt {
pub fn h1(index: usize, pk_vec: &[Point<Bls12_381_2>]) -> BigInt {
let mut pk = vec![&pk_vec[index]];
let pk_ref_vec: Vec<_> = pk_vec.iter().map(|k| k).collect();
let pk_ref_vec: Vec<_> = pk_vec.iter().collect();
pk.extend_from_slice(&pk_ref_vec[..]);
let result1 = HSha256::create_hash_from_ge(&pk);
result1.to_big_int()
Sha256::new().chain_points(pk).result_bigint()
}
73 changes: 36 additions & 37 deletions src/aggregated_bls/party_i.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,25 @@
use curv::arithmetic::traits::Modulo;
use curv::elliptic::curves::bls12_381::g1::FE as FE1;
use curv::elliptic::curves::bls12_381::g1::GE as GE1;
use curv::elliptic::curves::bls12_381::g2::FE as FE2;
use curv::elliptic::curves::bls12_381::g2::GE as GE2;
use curv::elliptic::curves::bls12_381::Pair;
use curv::elliptic::curves::traits::ECPoint;
use curv::elliptic::curves::traits::ECScalar;
use curv::BigInt;
use curv::elliptic::curves::bls12_381::{self, Pair};
use curv::elliptic::curves::*;

use crate::aggregated_bls::h1;
use crate::basic_bls::BLSSignature;

/// This is an implementation of BDN18 [https://eprint.iacr.org/2018/483.pdf]
/// protocol 3.1 (MSP): pairing-based multi-signature with public-key aggregation
#[derive(Copy, PartialEq, Clone, Debug)]
#[derive(PartialEq, Clone, Debug)]
pub struct Keys {
pub sk_i: FE2,
pub pk_i: GE2,
pub sk_i: Scalar<Bls12_381_2>,
pub pk_i: Point<Bls12_381_2>,
pub party_index: usize,
}

pub type APK = GE2;
pub type SIG = GE1;
pub type APK = Point<Bls12_381_2>;
pub type SIG = Point<Bls12_381_1>;

impl Keys {
pub fn new(index: usize) -> Self {
let u = ECScalar::new_random();
let y = &ECPoint::generator() * &u;
let u = Scalar::random();
let y = Point::generator() * &u;

Keys {
sk_i: u,
Expand All @@ -35,25 +28,26 @@ impl Keys {
}
}

pub fn aggregate(pk_vec: &[GE2]) -> APK {
let apk_plus_g = pk_vec.iter().fold(GE2::generator(), |acc, x| {
let i = pk_vec.iter().position(|y| y == x).unwrap();
acc + (pk_vec[i] * &ECScalar::from(&h1(i, pk_vec)))
});
apk_plus_g.sub_point(&GE2::generator().get_element())
pub fn aggregate(pk_vec: &[Point<Bls12_381_2>]) -> APK {
pk_vec
.iter()
.enumerate()
.map(|(i, pk_i)| pk_i * Scalar::from_bigint(&h1(i, pk_vec)))
.sum()
}

pub fn local_sign(&self, message: &[u8], pk_vec: &[GE2]) -> SIG {
let a_i = h1(self.party_index.clone(), pk_vec);
let exp = BigInt::mod_mul(&a_i, &self.sk_i.to_big_int(), &FE1::q());
let exp_fe1: FE1 = ECScalar::from(&exp);
let h_0_m = GE1::hash_to_curve(message);
h_0_m * exp_fe1
pub fn local_sign(&self, message: &[u8], pk_vec: &[Point<Bls12_381_2>]) -> SIG {
let a_i = Scalar::from_bigint(&h1(self.party_index, pk_vec));
let exp = a_i * &self.sk_i;
// Convert FE2 -> FE1
let exp = Scalar::from_raw(exp.into_raw());
let h_0_m = Point::from_raw(bls12_381::g1::G1Point::hash_to_curve(message))
.expect("hash_to_curve must return valid point");
h_0_m * exp
}

pub fn combine_local_signatures(sigs: &[SIG]) -> BLSSignature {
let (head, tail) = sigs.split_at(1);
let sig_sum = tail.iter().fold(head[0], |acc, x| acc + x);
let sig_sum = sigs.iter().sum();
BLSSignature { sigma: sig_sum }
}

Expand All @@ -62,16 +56,21 @@ impl Keys {
}

pub fn batch_aggregate_bls(sig_vec: &[BLSSignature]) -> BLSSignature {
let (head, tail) = sig_vec.split_at(1);
BLSSignature {
sigma: tail.iter().fold(head[0].sigma, |acc, x| acc + x.sigma),
sigma: sig_vec.iter().map(|s| &s.sigma).sum(),
}
}

fn core_aggregate_verify(apk_vec: &[APK], msg_vec: &[&[u8]], sig: &BLSSignature) -> bool {
assert!(apk_vec.len() >= 1);
let product_c2 = Pair::compute_pairing(&sig.sigma, &GE2::generator());
let vec_g1: Vec<GE1> = msg_vec.iter().map(|&x| GE1::hash_to_curve(&x)).collect();
assert!(!apk_vec.is_empty());
let product_c2 = Pair::compute_pairing(&sig.sigma, &Point::generator());
let vec_g1: Vec<Point<Bls12_381_1>> = msg_vec
.iter()
.map(|&x| {
Point::from_raw(bls12_381::g1::G1Point::hash_to_curve(x))
.expect("hash_to_curve must return valid point")
})
.collect();
let vec: Vec<_> = vec_g1.iter().zip(apk_vec.iter()).collect();
let (head, tail) = vec.split_at(1);
let product_c1 = tail
Expand All @@ -84,12 +83,12 @@ impl Keys {

pub fn aggregate_verify(apk_vec: &[APK], msg_vec: &[&[u8]], sig: &BLSSignature) -> bool {
assert!(apk_vec.len() == msg_vec.len());
if {
let res = {
let mut tmp = msg_vec.to_vec();
tmp.sort();
tmp.dedup();
tmp.len() != msg_vec.len()
} {
}; if res {
return false; // verification fails if there is a repeated message
}
Keys::core_aggregate_verify(apk_vec, msg_vec, sig)
Expand Down
51 changes: 20 additions & 31 deletions src/aggregated_bls/test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::aggregated_bls::party_i::{Keys, APK};
use crate::basic_bls::BLSSignature;
use curv::elliptic::curves::bls12_381::g2::GE as GE2;
use curv::elliptic::curves::*;

// test 3 out of 3
#[test]
Expand All @@ -10,7 +10,11 @@ fn agg_sig_test_3() {
let p3_keys = Keys::new(2);

// each party broadcasts its public key pk_i
let pk_vec = vec![p1_keys.pk_i, p2_keys.pk_i, p3_keys.pk_i];
let pk_vec = vec![
p1_keys.pk_i.clone(),
p2_keys.pk_i.clone(),
p3_keys.pk_i.clone(),
];

// each party computes APK
let apk = Keys::aggregate(&pk_vec);
Expand Down Expand Up @@ -60,14 +64,8 @@ pub fn test_agg_sig_3_batch_5() {

#[test]
pub fn test_agg_sig_3_batch_2() {
let msg_vec = vec![
[1].as_ref(),
[2].as_ref(),
];
let bad_m_v = vec![
[6].as_ref(),
[7].as_ref(),
];
let msg_vec = vec![[1].as_ref(), [2].as_ref()];
let bad_m_v = vec![[6].as_ref(), [7].as_ref()];
agg_sig_test_n_batch_m(3, &msg_vec, &bad_m_v);
}

Expand All @@ -80,41 +78,32 @@ pub fn agg_sig_test_n_batch_m(n: usize, msg_vec: &[&[u8]], bad_m_v: &[&[u8]]) {
let bls_sig = sign_batch(n, &mkey_vec, &pk_vec, msg_vec);

// test batch aggregation to verify as correct
assert_eq!(
Keys::aggregate_verify(&apk_vec, msg_vec, &bls_sig),
true
);
assert_eq!(Keys::aggregate_verify(&apk_vec, msg_vec, &bls_sig), true);

// test verification to fail a bad entry in apk_vec
let (_, _, bad_a_v) = keygen_batch(n, m);
assert_ne!(
Keys::aggregate_verify(&bad_a_v, msg_vec, &bls_sig),
true
);
assert_ne!(Keys::aggregate_verify(&bad_a_v, msg_vec, &bls_sig), true);

// test verification to fail a bad entry in msg_vec
assert_ne!(
Keys::aggregate_verify(&apk_vec, bad_m_v, &bls_sig),
true
);
assert_ne!(Keys::aggregate_verify(&apk_vec, bad_m_v, &bls_sig), true);

// test verification to fail a bad bls signature
let (bad_k_v, bad_p_v, _) = keygen_batch(n, m);
let bad_b_s = sign_batch(n, &bad_k_v, &bad_p_v, msg_vec);
assert_ne!(
Keys::aggregate_verify(&apk_vec, msg_vec, &bad_b_s),
true
);
assert_ne!(Keys::aggregate_verify(&apk_vec, msg_vec, &bad_b_s), true);
}

fn keygen(n_parties: usize) -> (Vec<Keys>, Vec<GE2>, APK) {
let keys_vec: Vec<Keys> = (0..n_parties).map(|i| Keys::new(i)).collect();
let pk_vec: Vec<GE2> = keys_vec.iter().map(|x| x.pk_i).collect();
fn keygen(n_parties: usize) -> (Vec<Keys>, Vec<Point<Bls12_381_2>>, APK) {
let keys_vec: Vec<Keys> = (0..n_parties).map(Keys::new).collect();
let pk_vec: Vec<Point<Bls12_381_2>> = keys_vec.iter().map(|x| x.pk_i.clone()).collect();
let apk = Keys::aggregate(&pk_vec);
(keys_vec, pk_vec, apk)
}

fn keygen_batch(n_parties: usize, m_batches: usize) -> (Vec<Vec<Keys>>, Vec<Vec<GE2>>, Vec<APK>) {
fn keygen_batch(
n_parties: usize,
m_batches: usize,
) -> (Vec<Vec<Keys>>, Vec<Vec<Point<Bls12_381_2>>>, Vec<APK>) {
let keygen_vec_batch: Vec<_> = (0..m_batches).map(|_| keygen(n_parties)).collect();
let keys_vec_batch = keygen_vec_batch.iter().map(|x| x.0.clone()).collect();
let pk_vec_batch = keygen_vec_batch.iter().map(|x| x.1.clone()).collect();
Expand All @@ -125,7 +114,7 @@ fn keygen_batch(n_parties: usize, m_batches: usize) -> (Vec<Vec<Keys>>, Vec<Vec<
fn sign_batch(
n_parties: usize,
key_vec: &Vec<Vec<Keys>>,
pk_vec: &Vec<Vec<GE2>>,
pk_vec: &Vec<Vec<Point<Bls12_381_2>>>,
msg_vec: &[&[u8]],
) -> BLSSignature {
let sig_vec: Vec<Vec<_>> = (0..msg_vec.len())
Expand Down
44 changes: 20 additions & 24 deletions src/basic_bls.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,55 @@
#![allow(non_snake_case)]

use curv::elliptic::curves::bls12_381::g1::FE as FE1;
use curv::elliptic::curves::bls12_381::g1::GE as GE1;
use curv::elliptic::curves::bls12_381::g2::FE as FE2;
use curv::elliptic::curves::bls12_381::g2::GE as GE2;
use curv::elliptic::curves::bls12_381::Pair;
use curv::elliptic::curves::traits::{ECPoint, ECScalar};
use curv::elliptic::curves::bls12_381::{self, Pair};
use curv::elliptic::curves::*;

use ff_zeroize::Field;
use pairing_plus::bls12_381::{Fq12, G1Affine};
use pairing_plus::serdes::SerDes;
use pairing_plus::bls12_381::Fq12;

/// Based on https://eprint.iacr.org/2018/483.pdf

#[derive(Clone, Copy, Debug)]
#[derive(Clone, Debug)]
pub struct KeyPairG2 {
Y: GE2,
x: FE2,
Y: Point<Bls12_381_2>,
x: Scalar<Bls12_381_2>,
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub struct BLSSignature {
pub sigma: GE1,
pub sigma: Point<Bls12_381_1>,
}

impl KeyPairG2 {
pub fn new() -> Self {
let x: FE2 = ECScalar::new_random();
let Y = GE2::generator() * &x;
let x = Scalar::random();
let Y = Point::generator() * &x;
KeyPairG2 { x, Y }
}
}

impl BLSSignature {
// compute sigma = x H(m)
pub fn sign(message: &[u8], keys: &KeyPairG2) -> Self {
let H_m = GE1::hash_to_curve(message);
let fe1_x: FE1 = ECScalar::from(&ECScalar::to_big_int(&keys.x));
let H_m = Point::from_raw(bls12_381::g1::G1Point::hash_to_curve(message))
.expect("hash_to_curve must return valid point");
// Convert FE2 -> FE1
let fe1_x = Scalar::from_raw(keys.x.clone().into_raw());
BLSSignature {
sigma: H_m * &fe1_x,
}
}

// check e(H(m), Y) == e(sigma, g2)
pub fn verify(&self, message: &[u8], pubkey: &GE2) -> bool {
let H_m = GE1::hash_to_curve(message);
let product = Pair::efficient_pairing_mul(&H_m, pubkey, &self.sigma, &(-GE2::generator()));
pub fn verify(&self, message: &[u8], pubkey: &Point<Bls12_381_2>) -> bool {
let H_m = Point::from_raw(bls12_381::g1::G1Point::hash_to_curve(message))
.expect("hash_to_curve must return valid point");
let product =
Pair::efficient_pairing_mul(&H_m, pubkey, &self.sigma, &(-Point::generator()));
product.e == Fq12::one()
}

pub fn to_bytes(&self, compressed: bool) -> Vec<u8> {
let mut pk = vec![];
G1Affine::serialize(&self.sigma.get_element(), &mut pk, compressed)
.expect("serialize to vec should always succeed");
pk
self.sigma.to_bytes(compressed).to_vec()
}
}

Expand Down
Loading