-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rust-arkworks
hash_to_curve
reference fn
upgrade
It was transferred to appropriate location, relevant dependepcies updated, code was improved and reworked. Find below some rationale on the most discussionable choice of the rework. ## Rationale `from_random_bytes` might be preferred way that will be found in the actual hash_to_curve functions, but it doesn't let us be generic in this reference example. `[&u8]` interface is broad and makes too few assumptions to be sure it's able to preserve properties of our function. Adding that behind broad interface only shallow trait definition, it's hard to be sure in its behavior in different contexts. These design choices could be justified to discourage its usage out of context of particular (curve) implementation. That said, devs using the trait should be seeking standardized primitives, and delve into all the details down the chain of calls when it's unavoidable. I feel like input randomness could be damaged in subtle ways using `from_random_bytes` without thorough understanding of particular implementation. Future updates can damage it too in non-obvious way as crate(s) isn't quite stable yet. So as far as it's just an example *let's go with naive but transparent approach.* Complexity of production implemetation can grow to very significant level as seen at <https://docs.rs/ark-ec/0.4.2/ark_ec/hashing/curve_maps/swu/trait.SWUConfig.html>.
- Loading branch information
Showing
2 changed files
with
59 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,71 @@ | ||
// use ark_ec::{AffineCurve, ProjectiveCurve}; | ||
use ark_ec::{AffineRepr, CurveGroup, CurveConfig, short_weierstrass::SWCurveConfig}; | ||
use tiny_keccak::{Hasher, Shake, Xof}; | ||
use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest}; | ||
use k256::AffinePoint; | ||
use k256::sha2::Sha256; | ||
use elliptic_curve::sec1::ToEncodedPoint; | ||
// use ark_ec::short_weierstrass_jacobian::GroupAffine; | ||
use k256::{ProjectivePoint, Secp256k1}; | ||
use ark_ff::{BigInteger, BigInt, fields::{Field, PrimeField}}; | ||
#![feature(iter_array_chunks)] | ||
|
||
const fn digest_len(modulus_bit_size: u32) -> usize {(modulus_bit_size/8 + if modulus_bit_size % 8 != 0 {1} else {0}) as usize} | ||
const fn bigint_len(bytearray_size: usize) -> usize {bytearray_size/8 + if bytearray_size % 8 != 0 {1} else {0}} | ||
use ark_ec::{ | ||
short_weierstrass::{Affine, SWCurveConfig}, | ||
AffineRepr, CurveConfig, | ||
}; | ||
use ark_ff::{ | ||
fields::{Field, PrimeField}, | ||
BigInteger, | ||
}; | ||
use bitvec::prelude::*; | ||
use tiny_keccak::{Hasher, Shake, Xof}; | ||
|
||
/// Kobi's hash_to_curve function, here for reference only | ||
struct NaiveBytes(Vec<u8>); | ||
impl Default for NaiveBytes {fn default() -> Self {Self(vec![0])}} | ||
impl NaiveBytes {fn incement(&mut self) { | ||
// TODO would be nice to make field private so that `unwrap` had no chance to panic | ||
if self.0.iter().last().unwrap() == &u8::MAX {self.0.push(0);} | ||
else { | ||
self.0[self.0.len() - 1] += 1; | ||
} | ||
}} | ||
|
||
pub fn _try_and_increment<C: CurveGroup + SWCurveConfig>(msg: &[u8]) -> C::Affine { | ||
// pub fn _try_and_increment<C: SWCurveConfig>(msg: &[u8]) -> Affine<C> { | ||
// `SWCurveConfig` chosen here just as a most general curve description, which can be tuned further with more appropriate to the task modules | ||
let nonce = NaiveBytes::default(); | ||
/// | ||
/// WARNING: Not tested -- bugs expected. Don't use the reference example for practical cryptography. | ||
pub fn _try_and_increment<C: SWCurveConfig>(msg: &[u8]) -> Affine<C> { | ||
/* `SWCurveConfig` is chosen here just as a most general curve description, which can be | ||
tuned further with more appropriate to the task modules. | ||
Anyway if you really need to implement _hash to curve_, you should start from most close | ||
implementation in the library that serves your particular needs, not from generic example. */ | ||
let mut nonce = NaiveBytes::default(); | ||
loop { | ||
let mut h = Shake::v128(); | ||
h.update(&nonce.0); | ||
h.update(msg.as_ref()); | ||
// let width_bits = C::Affine::MODULUS_BIT_SIZE /* + 1 */; | ||
// let output_size = width_bits / 8 + if width_bits % 8 != 0 {1} else {0}; | ||
|
||
|
||
// as this one isn't intended to work with greater than 256 bits groups, checks for digest being long enough are omitted here | ||
assert!(<<C as CurveGroup>::BaseField as Field>::BasePrimeField::MODULUS_BIT_SIZE <= 256u32); // just to be sure | ||
|
||
let mut output_u8 = [0u8; digest_len(<<C as CurveGroup>::BaseField as Field>::BasePrimeField::MODULUS_BIT_SIZE)]; | ||
assert!(<C::BaseField as Field>::BasePrimeField::MODULUS_BIT_SIZE <= 256u32); // just to be sure | ||
|
||
let mut output_u8 = | ||
vec![0u8; digest_len(<C::BaseField as Field>::BasePrimeField::MODULUS_BIT_SIZE)]; | ||
h.squeeze(&mut output_u8); | ||
|
||
// `from_bytes_be(sign: Sign, bytes: &[u8])` | ||
// `assert_eq!(BigInt::from_bytes_be(Sign::Plus, b"A"), | ||
// BigInt::parse_bytes(b"65", 10).unwrap());` | ||
|
||
let output_u64 = output_u8.into_iter().chunk(8).map(|(i, chunk)| {chunk as u64}); | ||
// TODO check that `BigInt::new` is actually BE | ||
if BigInt::new(output_u64).into() < <<C as CurveGroup>::BaseField as Field>::BasePrimeField::MODULUS { | ||
if let Some( | ||
result | ||
) = ark_ec::short_weierstrass::Affine::get_point_from_x_unchecked( | ||
// ) = ark_ec::models::short_weierstrass::Affine::get_point_from_x_unchecked( | ||
<<C as CurveConfig>::BaseField as Field>::BasePrimeField::from_be_bytes_mod_order(&output_u8), | ||
|
||
let output_bigint = <<<C as CurveConfig>::BaseField as Field>::BasePrimeField as PrimeField>::BigInt::from_bits_le( | ||
bitvec::vec::BitVec::<u8, Lsb0>::from_vec(output_u8.clone()).into_iter().collect::<Vec<bool>>().as_slice() | ||
); | ||
if output_bigint < <<C::BaseField as Field>::BasePrimeField as PrimeField>::MODULUS { | ||
if let Some(result) = Affine::get_point_from_x_unchecked( | ||
<C as CurveConfig>::BaseField::from_base_prime_field( | ||
<<<C as CurveConfig>::BaseField as Field>::BasePrimeField as PrimeField>::from_be_bytes_mod_order(&output_u8) | ||
), | ||
nonce.0.iter().last().unwrap() % 2 == 1 | ||
) {return result.into_group().into_affine();} | ||
) {return result.clear_cofactor();} | ||
} | ||
// else {dbg!(nonce.0)} | ||
nonce.increment(); | ||
} | ||
} | ||
|
||
/// Takes bit size and returns minimal number of bytes to fit these bits | ||
const fn digest_len(modulus_bit_size: u32) -> usize { | ||
(modulus_bit_size / 8 + if modulus_bit_size % 8 != 0 { 1 } else { 0 }) as usize | ||
} | ||
|
||
struct NaiveBytes(Vec<u8>); | ||
impl Default for NaiveBytes { | ||
fn default() -> Self { | ||
Self(vec![0]) | ||
} | ||
} | ||
impl NaiveBytes { | ||
fn increment(&mut self) { | ||
// TODO would be nice to make field private so that `unwrap` had no chance to panic | ||
if self.0.iter().last().unwrap() == &u8::MAX { | ||
self.0.push(0); | ||
} else { | ||
*self.0.last_mut().unwrap() += 1; | ||
} | ||
} | ||
} |