Skip to content
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

Remove generated iso curve #160

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 35 additions & 17 deletions src/hash_to_curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@ use ff::{Field, FromUniformBytes, PrimeField};
use pasta_curves::arithmetic::CurveExt;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};

pub enum Method {
SSWU,
pub enum Method<C: CurveExt> {
SSWU(Iso<C>),
SVDW,
}

/// Map the homogeneous coordinates of a point from the isogenous curve to a point in the original curve.
#[allow(clippy::type_complexity)]
davidnevadoc marked this conversation as resolved.
Show resolved Hide resolved
pub struct Iso<C: CurveExt> {
pub(crate) a: C::Base,
pub(crate) b: C::Base,
pub(crate) map: Box<dyn Fn(C::Base, C::Base, C::Base) -> C>,
davidnevadoc marked this conversation as resolved.
Show resolved Hide resolved
}

pub struct Suite<C: CurveExt, D: Digest + BlockSizeUser, const L: usize> {
domain: Vec<u8>,
map_to_curve: Box<dyn Fn(C::Base) -> C>,
Expand Down Expand Up @@ -92,14 +100,20 @@ impl<C: CurveExt, D: Digest + BlockSizeUser, const L: usize> Suite<C, D, L>
where
C::Base: Legendre + FromUniformBytes<L>,
{
pub(crate) fn new(domain: &[u8], z: C::Base, method: Method) -> Self {
pub(crate) fn new(domain: &[u8], z: C::Base, method: Method<C>) -> Self {
// Check for the target bits of security `k`. Currently, the target security is 128 bits.
// See: <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#section-5.1>
// L = ceil((ceil(log2(p)) + k) / 8)
assert!((C::Base::NUM_BITS as usize + 128) / 8 <= L);

let map_to_curve: Box<dyn Fn(C::Base) -> C> = match method {
Method::SSWU => Box::new(move |u| sswu_map_to_curve::<C>(u, z)),
Method::SSWU(iso) => {
let Iso { a, b, map } = iso;
Box::new(move |u| {
let (x, y, z) = sswu_map_to_curve::<C>(u, z, a, b);
map(x, y, z)
})
}

Method::SVDW => {
let [c1, c2, c3, c4] = svdw_precomputed_constants::<C>(z);
Box::new(move |u| svdw_map_to_curve::<C>(u, c1, c2, c3, c4, z))
Expand Down Expand Up @@ -164,7 +178,12 @@ pub(crate) fn svdw_precomputed_constants<C: CurveExt>(z: C::Base) -> [C::Base; 4

// Implementation of <https://datatracker.ietf.org/doc/html/rfc9380#name-simplified-swu-method>
#[allow(clippy::too_many_arguments)]
pub(crate) fn sswu_map_to_curve<C>(u: C::Base, z: C::Base) -> C
pub(crate) fn sswu_map_to_curve<C>(
u: C::Base,
z: C::Base,
a: C::Base,
b: C::Base,
) -> (C::Base, C::Base, C::Base)
where
C: CurveExt,
{
Expand Down Expand Up @@ -205,11 +224,6 @@ where
)
}

let zero = C::Base::ZERO;
let one = C::Base::ONE;
let a = C::a();
let b = C::b();

//1. tv1 = u^2
let tv1 = u.square();
//2. tv1 = Z * tv1
Expand All @@ -219,11 +233,11 @@ where
//4. tv2 = tv2 + tv1
let tv2 = tv2 + tv1;
//5. tv3 = tv2 + 1
let tv3 = tv2 + one;
let tv3 = tv2 + C::Base::ONE;
//6. tv3 = B * tv3
let tv3 = b * tv3;
//7. tv4 = CMOV(Z, -tv2, tv2 != 0) # tv4 = z if tv2 is 0 else tv4 = -tv2
let tv2_is_not_zero = !tv2.ct_eq(&zero);
let tv2_is_not_zero = !tv2.ct_eq(&C::Base::ZERO);
let tv4 = C::Base::conditional_select(&z, &-tv2, tv2_is_not_zero);
//8. tv4 = A * tv4
let tv4 = a * tv4;
Expand Down Expand Up @@ -259,10 +273,14 @@ where
let e1 = u.is_odd().ct_eq(&y.is_odd());
//24. y = CMOV(-y, y, e1) # Select correct sign of y
let y = C::Base::conditional_select(&-y, &y, e1);
//25. x = x / tv4
let x = x * tv4.invert().unwrap();
//26. return (x, y)
C::new_jacobian(x, y, one).unwrap()

// In the original algorithm:
// 25. x = x / tv4
// 26. return (x, y)
//
// we omit instruction 25. and return the result in homogeneous coordinates (instead of the previous jacobi).

(x, y * tv4, tv4)
}

#[allow(clippy::too_many_arguments)]
Expand Down
131 changes: 34 additions & 97 deletions src/secp256k1/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use core::cmp;
use core::fmt::Debug;
use core::iter::Sum;
use core::ops::{Add, Mul, Neg, Sub};
use group::cofactor::CofactorGroup;
use rand::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};

Expand Down Expand Up @@ -65,110 +64,52 @@ new_curve_impl!(
SECP_A,
SECP_B,
"secp256k1",
|domain_prefix| hash_to_curve(domain_prefix),
|domain_prefix| hash_to_curve(domain_prefix, hash_to_curve_suite(b"secp256k1_XMD:SHA-256_SSWU_RO_")),
);

impl Secp256k1 {
fn hash_to_curve_suite(domain: &[u8]) -> crate::hash_to_curve::Suite<Secp256k1, sha2::Sha256, 48> {
// Z = -11 (reference: <https://www.rfc-editor.org/rfc/rfc9380.html#name-suites-for-secp256k1>)
// 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc24
#[allow(dead_code)]
const SSWU_Z: Fp = Fp::from_raw([
davidnevadoc marked this conversation as resolved.
Show resolved Hide resolved
0xfffffffefffffc24,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
]);
}

#[allow(clippy::type_complexity)]
pub(crate) fn hash_to_curve<'a>(domain_prefix: &'a str) -> Box<dyn Fn(&[u8]) -> Secp256k1 + 'a> {
Box::new(move |message| {
let r0 = IsoSecp256k1::hash_to_curve(domain_prefix)(message);
let r1 = iso_map_secp256k1(r0);
r1.clear_cofactor()
})
}

// Simplified SWU for AB == 0 <https://www.rfc-editor.org/rfc/rfc9380.html#name-simplified-swu-for-ab-0>
//
// E': y'^2 = x'^3 + A' * x' + B', where
// A': 0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533
// B': 1771
// (reference: <https://www.rfc-editor.org/rfc/rfc9380.html#name-suites-for-secp256k1>)
pub const ISO_SECP_A: Fp = Fp::from_raw([
0x405447c01a444533,
0xe953d363cb6f0e5d,
0xa08a5558f0f5d272,
0x3f8731abdd661adc,
]);
pub const ISO_SECP_B: Fp = Fp::from_raw([1771, 0, 0, 0]);
// E': y'^2 = x'^3 + A' * x' + B', where
// A': 0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533
// B': 1771
// (reference: <https://www.rfc-editor.org/rfc/rfc9380.html#name-suites-for-secp256k1>)
pub const ISO_SECP_A: Fp = Fp::from_raw([
0x405447c01a444533,
0xe953d363cb6f0e5d,
0xa08a5558f0f5d272,
0x3f8731abdd661adc,
]);

const ISO_SECP_GENERATOR_X: Fp = Fp::from_raw([
0xD11D739D05A9F7A8,
0x00E448E38AF94593,
0x2287B72788F0933A,
0xC49B6C192E36AB1A,
]);
const ISO_SECP_GENERATOR_Y: Fp = Fp::from_raw([
0x10836BBAD9E12F4F,
0xC054381C214E65D4,
0x6DF11CC434B9FAC0,
0x9A9322D799106965,
]);
pub const ISO_SECP_B: Fp = Fp::from_raw([1771, 0, 0, 0]);

impl group::cofactor::CofactorGroup for IsoSecp256k1 {
type Subgroup = IsoSecp256k1;
let iso_map = crate::hash_to_curve::Iso {
a: ISO_SECP_A,
b: ISO_SECP_B,
map: Box::new(iso_map),
};

fn clear_cofactor(&self) -> Self {
*self
}

fn into_subgroup(self) -> CtOption<Self::Subgroup> {
CtOption::new(self, 1.into())
}

fn is_torsion_free(&self) -> Choice {
1.into()
}
crate::hash_to_curve::Suite::new(domain, SSWU_Z, crate::hash_to_curve::Method::SSWU(iso_map))
}

new_curve_impl!(
(pub(crate)),
IsoSecp256k1,
IsoSecp256k1Affine,
Fp,
Fq,
(ISO_SECP_GENERATOR_X, ISO_SECP_GENERATOR_Y),
ISO_SECP_A,
ISO_SECP_B,
"secp256k1",
|domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, IsoSecp256k1::default_hash_to_curve_suite()),
);

impl IsoSecp256k1 {
// Z = -11 (reference: <https://www.rfc-editor.org/rfc/rfc9380.html#name-suites-for-secp256k1>)
// 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc24
// NOTE: This `Z` is the `SSWU_Z` of `Secp256k1` curve.
const SSWU_Z: Fp = Fp::from_raw([
0xfffffffefffffc24,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
]);

fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite<IsoSecp256k1, sha2::Sha256, 48>
{
crate::hash_to_curve::Suite::<IsoSecp256k1, sha2::Sha256, 48>::new(
b"secp256k1_XMD:SHA-256_SSWU_RO_",
Self::SSWU_Z,
crate::hash_to_curve::Method::SSWU,
)
}
#[allow(clippy::type_complexity)]
pub(crate) fn hash_to_curve<'a>(
domain_prefix: &'a str,
suite: crate::hash_to_curve::Suite<Secp256k1, sha2::Sha256, 48>,
) -> Box<dyn Fn(&[u8]) -> Secp256k1 + 'a> {
Box::new(move |message| suite.hash_to_curve(domain_prefix, message))
}

/// 3-Isogeny Map for Secp256k1
/// Reference: <https://www.rfc-editor.org/rfc/rfc9380.html#name-3-isogeny-map-for-secp256k1>
pub(crate) fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 {
pub(crate) fn iso_map(x: Fp, y: Fp, z: Fp) -> Secp256k1 {
// constants for secp256k1 iso_map computation
const K: [[Fp; 4]; 5] = [
[Fp::ZERO; 4],
Expand Down Expand Up @@ -263,26 +204,22 @@ pub(crate) fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 {
],
];

let (x, y, z) = rp.jacobian_coordinates();

let z2 = z.square();
let z3 = z2 * z;
let z4 = z2.square();
let z6 = z3.square();

// iso_map logic (avoid inversion)
// iso_map logic (avoid inversion) in projective coordinates
// reference: <https://github.com/zcash/pasta_curves/blob/main/src/hashtocurve.rs#L80-L106>
let x_num = ((K[1][3] * x + K[1][2] * z2) * x + K[1][1] * z4) * x + K[1][0] * z6;
let x_den = (z2 * x + K[2][1] * z4) * x + K[2][0] * z6;
let x_num = ((K[1][3] * x + K[1][2] * z) * x + K[1][1] * z2) * x + K[1][0] * z3;
let x_den = (z * x + K[2][1] * z2) * x + K[2][0] * z3;

let y_num = (((K[3][3] * x + K[3][2] * z2) * x + K[3][1] * z4) * x + K[3][0] * z6) * y;
let y_den = (((x + K[4][2] * z2) * x + K[4][1] * z4) * x + K[4][0] * z6) * z3;
let y_num = (((K[3][3] * x + K[3][2] * z) * x + K[3][1] * z2) * x + K[3][0] * z3) * y;
let y_den = (((x + K[4][2] * z) * x + K[4][1] * z2) * x + K[4][0] * z3) * z;

let z = x_den * y_den;
let x = x_num * y_den * z;
let y = y_num * x_den * z.square();
let x = x_num * y_den;
let y = y_num * x_den;

Secp256k1::new_jacobian(x, y, z).unwrap()
Secp256k1 { x, y, z }
}

#[cfg(test)]
Expand Down
26 changes: 17 additions & 9 deletions src/secp256r1/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ new_curve_impl!(
SECP_A,
SECP_B,
"secp256r1",
|domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, Secp256r1::default_hash_to_curve_suite()),
|domain_prefix| hash_to_curve(domain_prefix, hash_to_curve_suite(b"P256_XMD:SHA-256_SSWU_RO_")),
);

impl Secp256r1 {
fn hash_to_curve_suite(domain: &[u8]) -> crate::hash_to_curve::Suite<Secp256r1, sha2::Sha256, 48> {
// Optimal Z with: <https://datatracker.ietf.org/doc/html/rfc9380#sswu-z-code>
// 0xffffffff00000001000000000000000000000000fffffffffffffffffffffff5
// Z = -10 (reference: <https://www.rfc-editor.org/rfc/rfc9380.html#section-8.2>)
davidnevadoc marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -89,13 +89,21 @@ impl Secp256r1 {
0xffffffff00000001,
]);

fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite<Secp256r1, sha2::Sha256, 48> {
crate::hash_to_curve::Suite::<Secp256r1, sha2::Sha256, 48>::new(
b"P256_XMD:SHA-256_SSWU_RO_",
Self::SSWU_Z,
crate::hash_to_curve::Method::SSWU,
)
}
let iso_map = crate::hash_to_curve::Iso {
a: Secp256r1::a(),
b: Secp256r1::b(),
map: Box::new(move |x, y, z| Secp256r1 { x, y, z }),
};

crate::hash_to_curve::Suite::new(domain, SSWU_Z, crate::hash_to_curve::Method::SSWU(iso_map))
}

#[allow(clippy::type_complexity)]
pub(crate) fn hash_to_curve<'a>(
domain_prefix: &'a str,
suite: crate::hash_to_curve::Suite<Secp256r1, sha2::Sha256, 48>,
) -> Box<dyn Fn(&[u8]) -> Secp256r1 + 'a> {
Box::new(move |message| suite.hash_to_curve(domain_prefix, message))
}

#[cfg(test)]
Expand Down
Loading