use core::marker::PhantomData;
use crate::{models::short_weierstrass::SWCurveConfig, CurveConfig};
use ark_ff::batch_inversion;
use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial};
use crate::{
hashing::{map_to_curve_hasher::MapToCurve, HashToCurveError},
models::short_weierstrass::{Affine, Projective},
AffineRepr,
};
use super::swu::{SWUConfig, SWUMap};
type BaseField<MP> = <MP as CurveConfig>::BaseField;
pub struct IsogenyMap<
'a,
Domain: SWCurveConfig,
Codomain: SWCurveConfig<BaseField = BaseField<Domain>>,
> {
pub x_map_numerator: &'a [BaseField<Domain>],
pub x_map_denominator: &'a [BaseField<Codomain>],
pub y_map_numerator: &'a [BaseField<Domain>],
pub y_map_denominator: &'a [BaseField<Codomain>],
}
impl<'a, Domain, Codomain> IsogenyMap<'a, Domain, Codomain>
where
Domain: SWCurveConfig,
Codomain: SWCurveConfig<BaseField = BaseField<Domain>>,
{
fn apply(&self, domain_point: Affine<Domain>) -> Result<Affine<Codomain>, HashToCurveError> {
match domain_point.xy() {
Some((x, y)) => {
let x_num = DensePolynomial::from_coefficients_slice(self.x_map_numerator);
let x_den = DensePolynomial::from_coefficients_slice(self.x_map_denominator);
let y_num = DensePolynomial::from_coefficients_slice(self.y_map_numerator);
let y_den = DensePolynomial::from_coefficients_slice(self.y_map_denominator);
let mut v: [BaseField<Domain>; 2] = [x_den.evaluate(&x), y_den.evaluate(&x)];
batch_inversion(&mut v);
let img_x = x_num.evaluate(&x) * v[0];
let img_y = (y_num.evaluate(&x) * y) * v[1];
Ok(Affine::<Codomain>::new_unchecked(img_x, img_y))
},
None => Ok(Affine::identity()),
}
}
}
pub trait WBConfig: SWCurveConfig + Sized {
type IsogenousCurve: SWUConfig<BaseField = BaseField<Self>>;
const ISOGENY_MAP: IsogenyMap<'static, Self::IsogenousCurve, Self>;
}
pub struct WBMap<P: WBConfig> {
swu_field_curve_hasher: PhantomData<SWUMap<P::IsogenousCurve>>,
curve_params: PhantomData<fn() -> P>,
}
impl<P: WBConfig> MapToCurve<Projective<P>> for WBMap<P> {
fn check_parameters() -> Result<(), HashToCurveError> {
match P::ISOGENY_MAP.apply(P::IsogenousCurve::GENERATOR) {
Ok(point_on_curve) => {
debug_assert!(point_on_curve.is_on_curve(),
"the isogeny maps the generator of its domain: {} into {} which does not belong to its codomain.",P::IsogenousCurve::GENERATOR, point_on_curve);
},
Err(e) => return Err(e),
}
SWUMap::<P::IsogenousCurve>::check_parameters().unwrap(); Ok(())
}
fn map_to_curve(
element: <Affine<P> as AffineRepr>::BaseField,
) -> Result<Affine<P>, HashToCurveError> {
let point_on_isogenious_curve = SWUMap::<P::IsogenousCurve>::map_to_curve(element).unwrap();
P::ISOGENY_MAP.apply(point_on_isogenious_curve)
}
}
#[cfg(test)]
mod test {
use crate::{
hashing::{
curve_maps::{
swu::SWUConfig,
wb::{IsogenyMap, WBConfig, WBMap},
},
map_to_curve_hasher::MapToCurveBasedHasher,
HashToCurve,
},
models::short_weierstrass::SWCurveConfig,
short_weierstrass::{Affine, Projective},
CurveConfig,
};
use ark_ff::{field_hashers::DefaultFieldHasher, fields::Fp64, MontBackend, MontFp};
#[derive(ark_ff::MontConfig)]
#[modulus = "127"]
#[generator = "6"]
pub struct F127Config;
pub type F127 = Fp64<MontBackend<F127Config, 1>>;
const F127_ZERO: F127 = MontFp!("0");
const F127_ONE: F127 = MontFp!("1");
struct TestWBF127MapToCurveConfig;
impl CurveConfig for TestWBF127MapToCurveConfig {
const COFACTOR: &'static [u64] = &[1];
#[rustfmt::skip]
const COFACTOR_INV: F127 = F127_ONE;
type BaseField = F127;
type ScalarField = F127;
}
impl SWCurveConfig for TestWBF127MapToCurveConfig {
const COEFF_A: F127 = F127_ZERO;
#[rustfmt::skip]
const COEFF_B: F127 = MontFp!("3");
const GENERATOR: Affine<Self> = Affine::new_unchecked(MontFp!("62"), MontFp!("70"));
}
struct TestSWU127MapToIsogenousCurveConfig;
impl CurveConfig for TestSWU127MapToIsogenousCurveConfig {
const COFACTOR: &'static [u64] = &[1];
#[rustfmt::skip]
const COFACTOR_INV: F127 = F127_ONE;
type BaseField = F127;
type ScalarField = F127;
}
impl SWCurveConfig for TestSWU127MapToIsogenousCurveConfig {
const COEFF_A: F127 = MontFp!("109");
#[rustfmt::skip]
const COEFF_B: F127 = MontFp!("124");
const GENERATOR: Affine<Self> = Affine::new_unchecked(MontFp!("84"), MontFp!("2"));
}
impl SWUConfig for TestSWU127MapToIsogenousCurveConfig {
const ZETA: F127 = MontFp!("-1");
}
const ISOGENY_MAP_TESTWBF127: IsogenyMap<
'_,
TestSWU127MapToIsogenousCurveConfig,
TestWBF127MapToCurveConfig,
> = IsogenyMap {
x_map_numerator: &[
MontFp!("4"),
MontFp!("63"),
MontFp!("23"),
MontFp!("39"),
MontFp!("-14"),
MontFp!("23"),
MontFp!("-32"),
MontFp!("32"),
MontFp!("-13"),
MontFp!("40"),
MontFp!("34"),
MontFp!("10"),
MontFp!("-21"),
MontFp!("-57"),
],
x_map_denominator: &[
MontFp!("2"),
MontFp!("31"),
MontFp!("-10"),
MontFp!("-20"),
MontFp!("63"),
MontFp!("-44"),
MontFp!("34"),
MontFp!("30"),
MontFp!("-30"),
MontFp!("-33"),
MontFp!("11"),
MontFp!("-13"),
MontFp!("1"),
],
y_map_numerator: &[
MontFp!("-34"),
MontFp!("-57"),
MontFp!("30"),
MontFp!("-18"),
MontFp!("-60"),
MontFp!("-43"),
MontFp!("-63"),
MontFp!("-18"),
MontFp!("-49"),
MontFp!("36"),
MontFp!("12"),
MontFp!("62"),
MontFp!("5"),
MontFp!("6"),
MontFp!("-7"),
MontFp!("48"),
MontFp!("41"),
MontFp!("59"),
MontFp!("10"),
],
y_map_denominator: &[
MontFp!("32"),
MontFp!("-18"),
MontFp!("-24"),
MontFp!("23"),
MontFp!("18"),
MontFp!("-55"),
MontFp!("-16"),
MontFp!("-61"),
MontFp!("-46"),
MontFp!("-13"),
MontFp!("-42"),
MontFp!("11"),
MontFp!("-30"),
MontFp!("38"),
MontFp!("3"),
MontFp!("52"),
MontFp!("-63"),
MontFp!("44"),
MontFp!("1"),
],
};
impl WBConfig for TestWBF127MapToCurveConfig {
type IsogenousCurve = TestSWU127MapToIsogenousCurveConfig;
const ISOGENY_MAP: super::IsogenyMap<'static, Self::IsogenousCurve, Self> =
ISOGENY_MAP_TESTWBF127;
}
#[test]
fn hash_arbitrary_string_to_curve_wb() {
use sha2::Sha256;
let test_wb_to_curve_hasher = MapToCurveBasedHasher::<
Projective<TestWBF127MapToCurveConfig>,
DefaultFieldHasher<Sha256, 128>,
WBMap<TestWBF127MapToCurveConfig>,
>::new(&[1])
.unwrap();
let hash_result = test_wb_to_curve_hasher.hash(b"if you stick a Babel fish in your ear you can instantly understand anything said to you in any form of language.").expect("fail to hash the string to curve");
assert!(
hash_result.x != F127_ZERO && hash_result.y != F127_ZERO,
"we assume that not both a and b coefficienst are zero for the test curve"
);
assert!(
hash_result.is_on_curve(),
"hash results into a point off the curve"
);
}
}