From d716fc04921313447e46f1d13ad94f7b37d011d2 Mon Sep 17 00:00:00 2001 From: Pablo Deymonnaz Date: Wed, 11 Dec 2024 11:36:10 -0300 Subject: [PATCH] Fix BLS signature (#174) This PR changes the signature of `sign_message` to: ``` rust pub fn sign_message(&self, message: &[u8; 32]) -> Signature ``` and fixes the implementation of map_to_curve. There was a minor bug in the parsing of the input (endianness mismatch). --- Cargo.lock | 2 +- crates/crypto/bls/Cargo.toml | 2 + crates/crypto/bls/src/lib.rs | 52 ++++++++++++++++++- crates/crypto/bn254/Cargo.toml | 1 - crates/crypto/bn254/src/utils.rs | 15 ++---- .../services/bls_aggregation/src/bls_agg.rs | 8 ++- 6 files changed, 64 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2efaa123..f388c1cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2627,6 +2627,7 @@ dependencies = [ "ark-serialize 0.5.0", "ark-std 0.4.0", "eigen-crypto-bn254", + "eigen-testing-utils", "eigen-utils", "rand", "serde", @@ -2642,7 +2643,6 @@ dependencies = [ "ark-bn254 0.5.0", "ark-ec 0.5.0", "ark-ff 0.5.0", - "num-bigint 0.4.6", "rand", "rust-bls-bn254", "tokio", diff --git a/crates/crypto/bls/Cargo.toml b/crates/crypto/bls/Cargo.toml index 51471ea5..5cbfe527 100644 --- a/crates/crypto/bls/Cargo.toml +++ b/crates/crypto/bls/Cargo.toml @@ -19,7 +19,9 @@ ark-std = { version = "0.4.0", default-features = false } serde.workspace = true eigen-crypto-bn254.workspace = true eigen-utils.workspace = true + [dev-dependencies] +eigen-testing-utils.workspace = true rand = "0.8.4" tokio = { workspace = true, features = ["full"] } serde_json.workspace = true diff --git a/crates/crypto/bls/src/lib.rs b/crates/crypto/bls/src/lib.rs index cf9eee6d..766480fe 100644 --- a/crates/crypto/bls/src/lib.rs +++ b/crates/crypto/bls/src/lib.rs @@ -175,7 +175,7 @@ impl BlsKeyPair { Signature::new(r.into_affine()) } - pub fn sign_message(&self, message: &[u8]) -> Signature { + pub fn sign_message(&self, message: &[u8; 32]) -> Signature { let g1 = map_to_curve(message); let sk_int: BigInteger256 = self.priv_key.into(); let r = g1.mul_bigint(sk_int); @@ -375,6 +375,8 @@ mod tests { use super::*; use ark_bn254::Fq2; use eigen_crypto_bn254::utils::verify_message; + use eigen_testing_utils::test_data::TestData; + type Fp = ark_ff::Fp, 4>; #[test] fn test_convert_to_g1_point() { @@ -677,4 +679,52 @@ mod tests { "The deserialized point does not match the original" ); } + + #[test] + fn test_bls_signature() { + #[derive(Deserialize, Debug)] + struct Input { + message_bytes: String, + bls_priv_key: String, + } + + let test_data = TestData::new(Input { + message_bytes: "Hello, world!Hello, world!123456".to_string(), + bls_priv_key: + "12248929636257230549931416853095037629726205319386239410403476017439825112537" + .to_string(), + }); + + let message_bytes: &[u8; 32] = test_data.input.message_bytes.as_bytes().try_into().unwrap(); + let bls_priv_key = test_data.input.bls_priv_key; + let bls_key_pair = BlsKeyPair::new(bls_priv_key).unwrap(); + let signature = bls_key_pair.sign_message(message_bytes); + + let g1_point = signature.g1_point().g1(); + let x = g1_point.x().unwrap(); + let y = g1_point.y().unwrap(); + + // assert x and y with the values obtained in Go SDK + assert_eq!( + x, + Fp::from_bigint( + BigInt::from_str( + "15790168376429033610067099039091292283117017641532256477437243974517959682102", + ) + .unwrap() + ) + .unwrap() + ); + + assert_eq!( + y, + Fp::from_bigint( + BigInt::from_str( + "4960450323239587206117776989095741074887370703941588742100855592356200866613", + ) + .unwrap() + ) + .unwrap() + ); + } } diff --git a/crates/crypto/bn254/Cargo.toml b/crates/crypto/bn254/Cargo.toml index 349e3d09..0641385f 100644 --- a/crates/crypto/bn254/Cargo.toml +++ b/crates/crypto/bn254/Cargo.toml @@ -12,7 +12,6 @@ license-file.workspace = true ark-bn254.workspace = true ark-ec.workspace = true ark-ff.workspace = true -num-bigint.workspace = true rust-bls-bn254.workspace = true diff --git a/crates/crypto/bn254/src/utils.rs b/crates/crypto/bn254/src/utils.rs index bb94cad3..fbae9aa0 100644 --- a/crates/crypto/bn254/src/utils.rs +++ b/crates/crypto/bn254/src/utils.rs @@ -4,7 +4,6 @@ use ark_ff::{ fields::{Field, PrimeField}, One, }; -use num_bigint::BigUint; use rust_bls_bn254::pairing; /// MapToCurve implements the simple hash-and-check (also sometimes try-and-increment) algorithm @@ -12,19 +11,11 @@ use rust_bls_bn254::pairing; /// Note that this function needs to be the same as the one used in the contract: /// https://github.com/Layr-Labs/eigenlayer-middleware/blob/1feb6ae7e12f33ce8eefb361edb69ee26c118b5d/src/libraries/BN254.sol#L292 /// we don't use the newer constant time hash-to-curve algorithms as they are gas-expensive to compute onchain -pub fn map_to_curve(digest: &[u8]) -> G1Affine { +pub fn map_to_curve(bytes: &[u8; 32]) -> G1Affine { let one = Fq::one(); let three = Fq::from(3u64); - let big_int = BigUint::from_bytes_be(digest); - let mut bytes = [0u8; 32]; - big_int - .to_bytes_be() - .iter() - .rev() - .enumerate() - .for_each(|(i, &b)| bytes[i] = b); - let mut x = Fq::from_le_bytes_mod_order(&bytes); + let mut x = Fq::from_be_bytes_mod_order(bytes); loop { // y = x^3 + 3 @@ -44,7 +35,7 @@ pub fn map_to_curve(digest: &[u8]) -> G1Affine { } /// Verifies message on G2 -pub fn verify_message(public_key: G2Affine, message: &[u8], signature: G1Affine) -> bool { +pub fn verify_message(public_key: G2Affine, message: &[u8; 32], signature: G1Affine) -> bool { if !signature.is_in_correct_subgroup_assuming_on_curve() || !signature.is_on_curve() { return false; } diff --git a/crates/services/bls_aggregation/src/bls_agg.rs b/crates/services/bls_aggregation/src/bls_agg.rs index 517d5e2e..8e2a49b9 100644 --- a/crates/services/bls_aggregation/src/bls_agg.rs +++ b/crates/services/bls_aggregation/src/bls_agg.rs @@ -636,9 +636,15 @@ impl BlsAggregatorService return Err(SignatureVerificationError::OperatorPublicKeyNotFound); }; + let message = signed_task_response_digest + .task_response_digest + .as_slice() + .try_into() + .map_err(|_| SignatureVerificationError::IncorrectSignature)?; + verify_message( pub_keys.g2_pub_key.g2(), - signed_task_response_digest.task_response_digest.as_slice(), + message, signed_task_response_digest.bls_signature.g1_point().g1(), ) .then_some(())