-
Notifications
You must be signed in to change notification settings - Fork 39
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
refactor(crypto): change hash_to_point algorithm (BFT-404) #80
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,46 @@ | ||
//! 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, 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 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); | ||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same way, please cite the literature. Even though we know the numbers of group elements, I do not remember any estimates on the distance between X coordinates There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAIU it’s about being quadratic residue, so distribution is about 50%. I’ll need some help in verifying that, and with finding the appropriate reference to literature. |
||
unreachable!() | ||
} | ||
|
||
fn get_point_from_x(x: Fq) -> Option<G1Affine> { | ||
// 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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both +y and -y will give you a point on curve and in the correct subgroup. So you should pick and fix an ambiguity here, by e.g. picking one that is smaller as an integer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I refrained from that because it adds extra steps and so gas usage in the onchain verification. |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
64 bytes by IETF suggestion, or please cite the literature why non-uniform field element for X doesn't affect a protocol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need 64 bytes? Maybe I'm misunderstanding something, but the prime field modulus is just 254 bits long. Generating a 64 bytes hash just seems pointless.