Skip to content

Commit

Permalink
refactor: switch bls library (#359)
Browse files Browse the repository at this point in the history
* refactor: switch bls library

* fix tests
  • Loading branch information
ncitron authored Aug 30, 2024
1 parent 943c387 commit 3643038
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 73 deletions.
75 changes: 19 additions & 56 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ ethereum_ssz_derive = "0.6.0"
ethereum_ssz = "0.6.0"
tree_hash_derive = "0.7.0"
tree_hash = "0.7.0"
sha2 = "0.10.8"
milagro_bls = { package = "snowbridge-milagro-bls", git = "https://github.com/Snowfork/milagro_bls", rev = "6a95c9e33c6a41d9137761e593d53742ebb964de"}
sha2 = "0.9"
bls12_381 = { version = "0.8.0", features = ["experimental"] }

# execution
alloy = { version = "0.2.1", features = [
Expand Down
1 change: 0 additions & 1 deletion common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ superstruct.workspace = true
thiserror.workspace = true
tracing.workspace = true
zduny-wasm-timer.workspace = true
milagro_bls.workspace = true
2 changes: 1 addition & 1 deletion consensus-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2021"

[dependencies]
alloy = { version = "0.2.1", features = ["consensus", "rpc-types", "ssz", "rlp", "k256"] }
bls12_381.workspace = true
ssz_types.workspace = true
ethereum_ssz_derive.workspace = true
ethereum_ssz.workspace = true
Expand All @@ -18,6 +19,5 @@ superstruct.workspace = true
thiserror.workspace = true
tracing.workspace = true
zduny-wasm-timer.workspace = true
milagro_bls.workspace = true

common = { path = "../common" }
139 changes: 127 additions & 12 deletions consensus-core/src/types/bls.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use milagro_bls::{AggregateSignature, PublicKey as MilagroPK};
use bls12_381::{
hash_to_curve::{ExpandMsgXmd, HashToCurve},
multi_miller_loop, G1Affine, G1Projective, G2Affine, G2Prepared, G2Projective, Gt, Scalar,
};
use eyre::{eyre, Result};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use tree_hash_derive::TreeHash;
Expand All @@ -19,22 +23,133 @@ pub struct Signature {
inner: ByteVector<typenum::U96>,
}

impl PublicKey {
fn point(&self) -> Result<G1Affine> {
let bytes = self.inner.inner.to_vec();
let bytes = bytes.as_slice().try_into()?;
let point_opt = G1Affine::from_compressed(bytes);
if point_opt.is_some().into() {
Ok(point_opt.unwrap())
} else {
Err(eyre!("invalid point"))
}
}
}

impl Signature {
/// FastAggregateVerify
///
/// Verifies an AggregateSignature against a list of PublicKeys.
/// PublicKeys must all be verified via Proof of Possession before running this function.
/// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-02#section-3.3.4
pub fn verify(&self, msg: &[u8], pks: &[PublicKey]) -> bool {
if let Ok(agg) = AggregateSignature::from_bytes(&self.inner.inner) {
let pks_res = pks
.iter()
.map(|pk| MilagroPK::from_bytes(&pk.inner.inner))
.collect::<Result<Vec<_>, _>>();

if let Ok(pks) = pks_res {
let pks = pks.iter().collect::<Vec<_>>();
agg.fast_aggregate_verify(msg, &pks)
let sig_point = if let Ok(point) = self.point() {
point
} else {
return false;
};

// Subgroup check for signature
if !subgroup_check_g2(&sig_point) {
return false;
}

// Aggregate PublicKeys
let aggregate_public_key = if let Ok(agg) = aggregate(pks) {
agg
} else {
return false;
};

// Ensure AggregatePublicKey is not infinity
if aggregate_public_key.is_identity().into() {
return false;
}

// Points must be affine for pairing
let key_point = aggregate_public_key;
let msg_hash = G2Affine::from(hash_to_curve(msg));

let generator_g1_negative = G1Affine::from(-G1Projective::generator());

// Faster ate2 evaualtion checks e(S, -G1) * e(H, PK) == 1
ate2_evaluation(&sig_point, &generator_g1_negative, &msg_hash, &key_point)
}

fn point(&self) -> Result<G2Affine> {
let bytes = self.inner.inner.to_vec();
let bytes = bytes.as_slice().try_into()?;
let point_opt = G2Affine::from_compressed(bytes);
if point_opt.is_some().into() {
Ok(point_opt.unwrap())
} else {
Err(eyre!("invalid point"))
}
}
}

/// Aggregates multiple keys into one aggragate key
fn aggregate(pks: &[PublicKey]) -> Result<G1Affine> {
if pks.is_empty() {
return Err(eyre!("no keys to aggregate"));
}

let mut agg_key = G1Projective::identity();
for key in pks {
agg_key += G1Projective::from(key.point()?)
}

Ok(G1Affine::from(agg_key))
}

/// Verifies a G2 point is in subgroup `r`.
fn subgroup_check_g2(point: &G2Affine) -> bool {
const CURVE_ORDER: &str = "73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001";
let r = hex_to_scalar(CURVE_ORDER).unwrap();
let check = point * r;
check.is_identity().into()
}

/// Evaluation of e(S, -G1) * e(H, PK) == 1
fn ate2_evaluation(p1: &G2Affine, q1: &G1Affine, r1: &G2Affine, s1: &G1Affine) -> bool {
// Prepare G2 points for efficient pairing
let signature_prepared = G2Prepared::from(*p1);
let msg_hash_prepared = G2Prepared::from(*r1);

// Compute e(S, -G1) * e(H, PK)
let pairing = multi_miller_loop(&[(q1, &signature_prepared), (s1, &msg_hash_prepared)]);

// Perform final exponentiation
let result = pairing.final_exponentiation();

// Check if the result is equal to the identity element of Gt
result == Gt::identity()
}

/// Hash a message to the curve
fn hash_to_curve(msg: &[u8]) -> G2Projective {
const DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
<G2Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve(msg, DST)
}

/// Converts hex string to scalar
fn hex_to_scalar(hex: &str) -> Option<Scalar> {
if hex.len() != 64 {
return None;
}

let mut raw = [0u64; 4];
for (i, chunk) in hex.as_bytes().chunks(16).enumerate().take(4) {
if let Ok(hex_chunk) = core::str::from_utf8(chunk) {
if let Ok(value) = u64::from_str_radix(hex_chunk, 16) {
raw[3 - i] = value.to_le();
} else {
false
return None;
}
} else {
false
return None;
}
}

Some(Scalar::from_raw(raw))
}
1 change: 0 additions & 1 deletion consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ edition = "2021"

[dependencies]
# consensus
milagro_bls.workspace = true
tree_hash.workspace = true

# async/futures
Expand Down

0 comments on commit 3643038

Please sign in to comment.