From f19333c0eab66f7de40c15deac69a646047853ee Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Sun, 24 Mar 2024 10:21:34 -0500 Subject: [PATCH 1/3] Change hash_to_point algorithm --- node/Cargo.lock | 2 ++ node/Cargo.toml | 2 ++ node/libs/crypto/Cargo.toml | 2 ++ node/libs/crypto/src/bn254/hash.rs | 49 +++++++++++++++++++++--------- node/libs/crypto/src/bn254/mod.rs | 11 ++++--- 5 files changed, 47 insertions(+), 19 deletions(-) diff --git a/node/Cargo.lock b/node/Cargo.lock index 95ad4a8f..45c8afb0 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -3735,6 +3735,8 @@ dependencies = [ "ed25519-dalek", "ff_ce", "hex", + "num-bigint", + "num-traits", "pairing_ce", "rand 0.4.6", "rand 0.8.5", diff --git a/node/Cargo.toml b/node/Cargo.toml index 0d2e3bcd..fb3a564a 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -87,6 +87,8 @@ kube = { version = "0.88.1", features = ["runtime", "derive"] } k8s-openapi = { version = "0.21.0", features = ["latest"] } jsonrpsee = { version = "0.21.0", features = ["server", "http-client"] } tower = { version = "0.4.13" } +num-bigint = "0.4.4" +num-traits = "0.2.18" # Note that "bench" profile inherits from "release" profile and # "test" profile inherits from "dev" profile. diff --git a/node/libs/crypto/Cargo.toml b/node/libs/crypto/Cargo.toml index 97a782fb..6d3f8a7f 100644 --- a/node/libs/crypto/Cargo.toml +++ b/node/libs/crypto/Cargo.toml @@ -18,6 +18,8 @@ rand04.workspace = true thiserror.workspace = true tracing.workspace = true ff_ce.workspace = true +num-bigint.workspace = true +num-traits.workspace = true [dev-dependencies] criterion.workspace = true diff --git a/node/libs/crypto/src/bn254/hash.rs b/node/libs/crypto/src/bn254/hash.rs index 53d89f54..cc6abdaa 100644 --- a/node/libs/crypto/src/bn254/hash.rs +++ b/node/libs/crypto/src/bn254/hash.rs @@ -1,27 +1,48 @@ //! Hash operations. +use ff_ce::{Field, PrimeField, SqrtField}; +use num_bigint::BigUint; +use num_traits::Num; use pairing::{ - bn256::{G1Affine, G1Compressed}, - EncodedPoint, + bn256::{fq, Fq, FqRepr, Fr, G1Affine}, + CurveAffine, }; use sha3::Digest as _; /// Hashes an arbitrary message and maps it to an elliptic curve point in G1. -pub(crate) fn hash_to_g1(msg: &[u8]) -> G1Affine { - for i in 0..256 { - // Hash the message with the index as suffix. - let bytes: [u8; 32] = sha3::Keccak256::new() - .chain_update(msg) - .chain_update((i as u32).to_be_bytes()) - .finalize() - .into(); +pub(crate) fn hash_to_point(msg: &[u8]) -> (G1Affine, u8) { + let hash: [u8; 32] = sha3::Keccak256::new().chain_update(msg).finalize().into(); - // Try to get a G1 point from the hash. The probability that this works is around 1/8. - let p = G1Compressed::from_fixed_bytes(bytes).into_affine(); - if let Ok(p) = p { - return p; + let hash_num = BigUint::from_bytes_be(&hash); + let prime_field_modulus = BigUint::from_str_radix( + "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47", + 16, + ) + .unwrap(); + let x_num = hash_num % prime_field_modulus; + + let mut arr = [0u64; 4]; + arr.copy_from_slice(x_num.to_u64_digits().as_slice()); + let mut x = Fq::from_repr(FqRepr(arr)).unwrap(); + + for i in 0..255 { + let p = get_point_from_x(x); + if let Some(p) = p { + return (p, i); } + x.add_assign(&Fq::one()); } + // It should be statistically infeasible to finish the loop without finding a point. unreachable!() } + +fn get_point_from_x(mut x: Fq) -> Option { + // Compute x^3 + b. + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&fq::B_COEFF); + // Try find the square root. + x3b.sqrt().map(|y| G1Affine::from_xy_unchecked(x, y)) +} diff --git a/node/libs/crypto/src/bn254/mod.rs b/node/libs/crypto/src/bn254/mod.rs index df02482f..668166ee 100644 --- a/node/libs/crypto/src/bn254/mod.rs +++ b/node/libs/crypto/src/bn254/mod.rs @@ -44,8 +44,8 @@ impl SecretKey { /// Produces a signature using this [`SecretKey`] pub fn sign(&self, msg: &[u8]) -> Signature { - let hash_point = hash::hash_to_g1(msg); - let sig = hash_point.mul(self.0); + let (msg_point, _) = hash::hash_to_point(msg); + let sig = msg_point.mul(self.0); Signature(sig) } } @@ -130,10 +130,10 @@ impl Signature { /// This optimization is needed for ensuring that tests can run within a reasonable time frame. #[inline(never)] pub fn verify(&self, msg: &[u8], pk: &PublicKey) -> Result<(), Error> { - let hash_point = hash::hash_to_g1(msg); + let (msg_point, _) = hash::hash_to_point(msg); // First pair: e(H(m): G1, pk: G2) - let a = Bn256::pairing(hash_point, pk.0); + let a = Bn256::pairing(msg_point, pk.0); // Second pair: e(sig: G1, generator: G2) let b = Bn256::pairing(self.0, G2Affine::one()); @@ -203,7 +203,8 @@ impl AggregateSignature { // Second pair: e(H(m1): G1, pk1: G2) * ... * e(H(m1000): G1, pk1000: G2) let mut b = Fq12::one(); for (msg, pk) in pairs { - b.mul_assign(&Bn256::pairing(hash::hash_to_g1(msg), pk.0)) + let (msg_point, _) = hash::hash_to_point(msg); + b.mul_assign(&Bn256::pairing(msg_point, pk.0)) } if a == b { From 03bca110b36a8a256d7a67b3c6153b5ffbf85e98 Mon Sep 17 00:00:00 2001 From: Moshe Shababo <17073733+moshababo@users.noreply.github.com> Date: Mon, 25 Mar 2024 20:46:34 -0500 Subject: [PATCH 2/3] simplify vec to arr --- node/libs/crypto/src/bn254/hash.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/node/libs/crypto/src/bn254/hash.rs b/node/libs/crypto/src/bn254/hash.rs index cc6abdaa..7a813776 100644 --- a/node/libs/crypto/src/bn254/hash.rs +++ b/node/libs/crypto/src/bn254/hash.rs @@ -20,10 +20,8 @@ pub(crate) fn hash_to_point(msg: &[u8]) -> (G1Affine, u8) { ) .unwrap(); let x_num = hash_num % prime_field_modulus; - - let mut arr = [0u64; 4]; - arr.copy_from_slice(x_num.to_u64_digits().as_slice()); - let mut x = Fq::from_repr(FqRepr(arr)).unwrap(); + let x_arr: [u64; 4] = x_num.to_u64_digits().try_into().unwrap(); + let mut x = Fq::from_repr(FqRepr(x_arr)).unwrap(); for i in 0..255 { let p = get_point_from_x(x); From defa67322ce1a91636680289ae6b7fb198551c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Fran=C3=A7a?= Date: Tue, 9 Apr 2024 15:13:27 +0100 Subject: [PATCH 3/3] cargo clippy + fmt --- node/actors/network/src/consensus/mod.rs | 3 +-- node/libs/crypto/src/bn254/hash.rs | 4 ++-- node/libs/crypto/src/ed25519/tests.rs | 6 ++++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/node/actors/network/src/consensus/mod.rs b/node/actors/network/src/consensus/mod.rs index 7d3242a2..3de0243c 100644 --- a/node/actors/network/src/consensus/mod.rs +++ b/node/actors/network/src/consensus/mod.rs @@ -1,7 +1,6 @@ //! Consensus network is a full graph of connections between all validators. //! BFT consensus messages are exchanged over this network. -use crate::rpc::Rpc as _; -use crate::{config, gossip, io, noise, pool::PoolWatch, preface, rpc}; +use crate::{config, gossip, io, noise, pool::PoolWatch, preface, rpc, rpc::Rpc as _}; use anyhow::Context as _; use rand::seq::SliceRandom; use std::{collections::HashSet, sync::Arc}; diff --git a/node/libs/crypto/src/bn254/hash.rs b/node/libs/crypto/src/bn254/hash.rs index 7a813776..30359abd 100644 --- a/node/libs/crypto/src/bn254/hash.rs +++ b/node/libs/crypto/src/bn254/hash.rs @@ -4,7 +4,7 @@ use ff_ce::{Field, PrimeField, SqrtField}; use num_bigint::BigUint; use num_traits::Num; use pairing::{ - bn256::{fq, Fq, FqRepr, Fr, G1Affine}, + bn256::{fq, Fq, FqRepr, G1Affine}, CurveAffine, }; use sha3::Digest as _; @@ -35,7 +35,7 @@ pub(crate) fn hash_to_point(msg: &[u8]) -> (G1Affine, u8) { unreachable!() } -fn get_point_from_x(mut x: Fq) -> Option { +fn get_point_from_x(x: Fq) -> Option { // Compute x^3 + b. let mut x3b = x; x3b.square(); diff --git a/node/libs/crypto/src/ed25519/tests.rs b/node/libs/crypto/src/ed25519/tests.rs index e51fbe1a..d1588cbd 100644 --- a/node/libs/crypto/src/ed25519/tests.rs +++ b/node/libs/crypto/src/ed25519/tests.rs @@ -1,5 +1,7 @@ -use crate::ed25519::{PublicKey, SecretKey, Signature}; -use crate::ByteFmt; +use crate::{ + ed25519::{PublicKey, SecretKey, Signature}, + ByteFmt, +}; #[test] fn test_ed25519() -> anyhow::Result<()> {