From 3ce2bffc328b86076481f33347603b8669a72802 Mon Sep 17 00:00:00 2001 From: duguorong Date: Tue, 5 Dec 2023 22:45:11 +0800 Subject: [PATCH 01/18] feat: add "iso_map" for secp256k1 h2c func --- src/hash_to_curve.rs | 100 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs index b27b7ab7..ab7a0186 100644 --- a/src/hash_to_curve.rs +++ b/src/hash_to_curve.rs @@ -173,6 +173,106 @@ where }) } +/// 3-Isogeny Map for Secp256k1 +/// Reference: +fn iso_map_secp256k1(x: C::Base, y: C::Base) -> (C::Base, C::Base) +where + C: CurveExt, + C::Base: FromUniformBytes<64>, +{ + // constants for secp256k1 iso_map computation + const K: [[&str; 4]; 5] = [ + ["0x00", "0x00", "0x00", "0x00"], + [ + "0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7", + "0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581", + "0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262", + "0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c", + ], + [ + "0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b", + "0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14", + "0x00", + "0x00", + ], + [ + "0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c", + "0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3", + "0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931", + "0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84", + ], + [ + "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b", + "0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573", + "0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f", + "0x00", + ], + ]; + let mut k: [[C::Base; 4]; 5] = [[C::Base::from_uniform_bytes(&[0; 64]); 4]; 5]; + for i in 0..5 { + for j in 0..4 { + k[i][j] = C::Base::from_uniform_bytes(&hex_str_to_le_bytes(K[i][j])); + } + } + + let x_squared = x.square(); + let x_cubed = x * x_squared; + + let x_num = k[1][3] * x_cubed + k[1][2] * x_squared + k[1][1] * x + k[1][0]; + let x_den = x_squared + k[2][1] * x + k[2][0]; + + let y_num = k[3][3] * x_cubed + k[3][2] * x_squared + k[3][1] * x + k[3][0]; + let y_den = x_cubed + k[4][2] * x_squared + k[4][1] * x + k[4][0]; + + // Exceptional case MUST return identity + // reference: + if x_den.is_zero().into() || y_den.is_zero().into() { + return (C::Base::ZERO, C::Base::ZERO); + } + + let x = x_num * x_den.invert().unwrap(); + let y = y * (y_num * y_den.invert().unwrap()); + + (x, y) +} + +/// Convert hex string to little-endian bytes array of length `L` +/// +/// NOTE: hex string should be prefixed with "0x" +/// +/// Example: +/// +/// hex_str_to_le_bytes::<4>("0x01020304") -> [4, 3, 2, 1] +/// +/// hex_str_to_le_bytes::<6>("0x01020304") -> [4, 3, 2, 1, 0, 0] +/// +/// hex_str_to_le_bytes::<2>("0x01020304") -> [4, 3] +/// +fn hex_str_to_le_bytes(hex: &str) -> [u8; L] { + let padded_hex_string = if hex.len() % 2 != 0 { + format!("0{}", &hex[2..]) + } else { + hex[2..].to_owned() + }; + + // Convert each pair of hex characters to u8 and collect into a vector + let le_bytes: Result, _> = (0..padded_hex_string.len()) + .step_by(2) + .rev() // Iterate in reverse order for little-endian byte order + .map(|i| { + u8::from_str_radix(&padded_hex_string[i..i + 2], 16) + .map_err(|_| "Invalid hex character") + }) + .collect(); + let le_bytes = le_bytes.expect("Invalid bytes"); + + let mut result = [0; L]; + for i in 0..L.min(le_bytes.len()) { + result[i] = le_bytes[i]; + } + result +} + #[allow(clippy::too_many_arguments)] pub(crate) fn svdw_map_to_curve( u: C::Base, From d3a90c1da370cd8dffc79fce9532eff35fd9f050 Mon Sep 17 00:00:00 2001 From: duguorong Date: Tue, 5 Dec 2023 22:45:30 +0800 Subject: [PATCH 02/18] feat: update "simple_svdw_hash_to_curve" --- src/hash_to_curve.rs | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs index ab7a0186..acf91d73 100644 --- a/src/hash_to_curve.rs +++ b/src/hash_to_curve.rs @@ -87,14 +87,19 @@ fn hash_to_field>( // Implementation of #[allow(clippy::too_many_arguments)] -pub(crate) fn simple_svdw_map_to_curve(u: C::Base, z: C::Base) -> C +pub(crate) fn simple_svdw_map_to_curve( + u: C::Base, + z: C::Base, + iso_a: Option, + iso_b: Option, +) -> (C::Base, C::Base) where C: CurveExt, { let zero = C::Base::ZERO; let one = C::Base::ONE; - let a = C::a(); - let b = C::b(); + let a = iso_a.unwrap_or(C::a()); + let b = iso_b.unwrap_or(C::b()); //1. tv1 = u^2 let tv1 = u.square(); @@ -148,7 +153,7 @@ where //25. x = x / tv4 let x = x * tv4.invert().unwrap(); //26. return (x, y) - C::new_jacobian(x, y, one).unwrap() + (x, y) } #[allow(clippy::type_complexity)] @@ -156,6 +161,8 @@ pub(crate) fn simple_svdw_hash_to_curve<'a, C>( curve_id: &'static str, domain_prefix: &'a str, z: C::Base, + iso_a: Option, + iso_b: Option, ) -> Box C + 'a> where C: CurveExt, @@ -165,9 +172,21 @@ where let mut us = [C::Base::ZERO; 2]; hash_to_field("SSWU", curve_id, domain_prefix, message, &mut us); - let [q0, q1]: [C; 2] = us.map(|u| simple_svdw_map_to_curve(u, z)); + let mut q = [C::identity(); 2]; + for (i, u) in us.into_iter().enumerate() { + let (xp, yp) = simple_svdw_map_to_curve::(u, z, iso_a, iso_b); + let (x, y) = match curve_id { + "secp256k1" => iso_map_secp256k1::(xp, yp), + _ => (xp, yp), + }; + q[i] = if x.is_zero().into() && y.is_zero().into() { + C::identity() + } else { + C::new_jacobian(x, y, C::Base::ONE).unwrap() + }; + } - let r = q0 + &q1; + let r = q[0] + &q[1]; debug_assert!(bool::from(r.is_on_curve())); r }) From 557b081c90b3f37e75bcdfeca524fb894d3e4769 Mon Sep 17 00:00:00 2001 From: duguorong Date: Tue, 5 Dec 2023 22:46:04 +0800 Subject: [PATCH 03/18] refactor: update the "secp256k1" & "secp256r1" --- src/secp256k1/curve.rs | 27 ++++++++++++++++++++++++--- src/secp256r1/curve.rs | 2 +- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index 4bd0f26a..941ace7b 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -1,7 +1,7 @@ use crate::ff::WithSmallOrderMulGroup; use crate::ff::{Field, PrimeField}; use crate::group::{prime::PrimeCurveAffine, Curve, Group as _, GroupEncoding}; -use crate::hash_to_curve::svdw_hash_to_curve; +use crate::hash_to_curve::simple_svdw_hash_to_curve; use crate::secp256k1::Fp; use crate::secp256k1::Fq; use crate::{Coordinates, CurveAffine, CurveExt}; @@ -48,6 +48,20 @@ const SECP_GENERATOR_Y: Fp = Fp::from_raw([ const SECP_A: Fp = Fp::from_raw([0, 0, 0, 0]); const SECP_B: Fp = Fp::from_raw([7, 0, 0, 0]); +// Simplified SWU for AB == 0 +// +// E': y'^2 = x'^3 + A' * x' + B', where +// A': 0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533 +// B': 1771 +// (reference: ) +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]); + use crate::{ impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, @@ -65,11 +79,18 @@ new_curve_impl!( SECP_A, SECP_B, "secp256k1", - |curve_id, domain_prefix| svdw_hash_to_curve(curve_id, domain_prefix, Secp256k1::SVDW_Z), + |curve_id, domain_prefix| simple_svdw_hash_to_curve(curve_id, domain_prefix, Secp256k1::SSWU_Z, Some(ISO_SECP_A), Some(ISO_SECP_B)), ); impl Secp256k1 { - const SVDW_Z: Fp = Fp::ONE; + // Z = -11 (reference: ) + // 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc24 + const SSWU_Z: Fp = Fp([ + 0xfffffffefffffc24, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + ]); } #[test] diff --git a/src/secp256r1/curve.rs b/src/secp256r1/curve.rs index 7e6e24ef..ae42233a 100644 --- a/src/secp256r1/curve.rs +++ b/src/secp256r1/curve.rs @@ -76,7 +76,7 @@ new_curve_impl!( SECP_A, SECP_B, "secp256r1", - |curve_id, domain_prefix| simple_svdw_hash_to_curve(curve_id, domain_prefix, Secp256r1::SSVDW_Z), + |curve_id, domain_prefix| simple_svdw_hash_to_curve(curve_id, domain_prefix, Secp256r1::SSVDW_Z, None, None), ); impl Secp256r1 { From 19210ad289d3cc1aa6ce7b5fcb950da35d8688ed Mon Sep 17 00:00:00 2001 From: duguorong Date: Tue, 12 Dec 2023 11:18:46 +0800 Subject: [PATCH 04/18] refactor: add new "simple_svdw_hash_to_curve_with_iso_map" func --- src/hash_to_curve.rs | 64 ++++++++++++++++++++++++++++-------------- src/secp256k1/curve.rs | 4 +-- src/secp256r1/curve.rs | 2 +- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs index acf91d73..a21f8b25 100644 --- a/src/hash_to_curve.rs +++ b/src/hash_to_curve.rs @@ -90,16 +90,14 @@ fn hash_to_field>( pub(crate) fn simple_svdw_map_to_curve( u: C::Base, z: C::Base, - iso_a: Option, - iso_b: Option, + a: C::Base, + b: C::Base, ) -> (C::Base, C::Base) where C: CurveExt, { let zero = C::Base::ZERO; let one = C::Base::ONE; - let a = iso_a.unwrap_or(C::a()); - let b = iso_b.unwrap_or(C::b()); //1. tv1 = u^2 let tv1 = u.square(); @@ -156,13 +154,12 @@ where (x, y) } +// Implementation of #[allow(clippy::type_complexity)] pub(crate) fn simple_svdw_hash_to_curve<'a, C>( curve_id: &'static str, domain_prefix: &'a str, z: C::Base, - iso_a: Option, - iso_b: Option, ) -> Box C + 'a> where C: CurveExt, @@ -172,21 +169,46 @@ where let mut us = [C::Base::ZERO; 2]; hash_to_field("SSWU", curve_id, domain_prefix, message, &mut us); - let mut q = [C::identity(); 2]; - for (i, u) in us.into_iter().enumerate() { - let (xp, yp) = simple_svdw_map_to_curve::(u, z, iso_a, iso_b); - let (x, y) = match curve_id { - "secp256k1" => iso_map_secp256k1::(xp, yp), - _ => (xp, yp), - }; - q[i] = if x.is_zero().into() && y.is_zero().into() { - C::identity() - } else { - C::new_jacobian(x, y, C::Base::ONE).unwrap() - }; - } + let [q0, q1]: [C; 2] = us + .map(|u| simple_svdw_map_to_curve::(u, z, C::a(), C::b())) + .map(|(x, y)| C::new_jacobian(x, y, C::Base::ONE).unwrap()); + + let r = q0 + &q1; + debug_assert!(bool::from(r.is_on_curve())); + r + }) +} - let r = q[0] + &q[1]; +// Implementation of +#[allow(clippy::type_complexity)] +pub(crate) fn simple_svdw_hash_to_curve_with_iso_map<'a, C>( + curve_id: &'static str, + domain_prefix: &'a str, + z: C::Base, + iso_a: C::Base, + iso_b: C::Base, + iso_map: Box (C::Base, C::Base)>, +) -> Box C + 'a> +where + C: CurveExt, + C::Base: FromUniformBytes<64>, +{ + Box::new(move |message| { + let mut us = [C::Base::ZERO; 2]; + hash_to_field("SSWU", curve_id, domain_prefix, message, &mut us); + + let [q0, q1] = us + .map(|u| simple_svdw_map_to_curve::(u, z, iso_a, iso_b)) + .map(|(xp, yp)| iso_map(xp, yp)) + .map(|(x, y)| { + if x.is_zero().into() && y.is_zero().into() { + C::identity() + } else { + C::new_jacobian(x, y, C::Base::ONE).unwrap() + } + }); + + let r = q0 + &q1; debug_assert!(bool::from(r.is_on_curve())); r }) @@ -194,7 +216,7 @@ where /// 3-Isogeny Map for Secp256k1 /// Reference: -fn iso_map_secp256k1(x: C::Base, y: C::Base) -> (C::Base, C::Base) +pub fn iso_map_secp256k1(x: C::Base, y: C::Base) -> (C::Base, C::Base) where C: CurveExt, C::Base: FromUniformBytes<64>, diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index 941ace7b..e52fc664 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -1,7 +1,7 @@ use crate::ff::WithSmallOrderMulGroup; use crate::ff::{Field, PrimeField}; use crate::group::{prime::PrimeCurveAffine, Curve, Group as _, GroupEncoding}; -use crate::hash_to_curve::simple_svdw_hash_to_curve; +use crate::hash_to_curve::{iso_map_secp256k1, simple_svdw_hash_to_curve_with_iso_map}; use crate::secp256k1::Fp; use crate::secp256k1::Fq; use crate::{Coordinates, CurveAffine, CurveExt}; @@ -79,7 +79,7 @@ new_curve_impl!( SECP_A, SECP_B, "secp256k1", - |curve_id, domain_prefix| simple_svdw_hash_to_curve(curve_id, domain_prefix, Secp256k1::SSWU_Z, Some(ISO_SECP_A), Some(ISO_SECP_B)), + |curve_id, domain_prefix| simple_svdw_hash_to_curve_with_iso_map(curve_id, domain_prefix, Secp256k1::SSWU_Z, ISO_SECP_A, ISO_SECP_B, Box::new(iso_map_secp256k1::)), ); impl Secp256k1 { diff --git a/src/secp256r1/curve.rs b/src/secp256r1/curve.rs index ae42233a..7e6e24ef 100644 --- a/src/secp256r1/curve.rs +++ b/src/secp256r1/curve.rs @@ -76,7 +76,7 @@ new_curve_impl!( SECP_A, SECP_B, "secp256r1", - |curve_id, domain_prefix| simple_svdw_hash_to_curve(curve_id, domain_prefix, Secp256r1::SSVDW_Z, None, None), + |curve_id, domain_prefix| simple_svdw_hash_to_curve(curve_id, domain_prefix, Secp256r1::SSVDW_Z), ); impl Secp256r1 { From 01491d12b6204c294b868ba47f71ac860095753a Mon Sep 17 00:00:00 2001 From: duguorong Date: Wed, 13 Dec 2023 23:57:37 +0800 Subject: [PATCH 05/18] feat: add new curve "IsoSecp256k1" --- src/secp256k1/curve.rs | 83 +++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index e52fc664..3088ce91 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -1,7 +1,7 @@ use crate::ff::WithSmallOrderMulGroup; use crate::ff::{Field, PrimeField}; use crate::group::{prime::PrimeCurveAffine, Curve, Group as _, GroupEncoding}; -use crate::hash_to_curve::{iso_map_secp256k1, simple_svdw_hash_to_curve_with_iso_map}; +use crate::hash_to_curve::{simple_svdw_hash_to_curve, simple_svdw_hash_to_curve_secp256k1}; use crate::secp256k1::Fp; use crate::secp256k1::Fq; use crate::{Coordinates, CurveAffine, CurveExt}; @@ -12,6 +12,12 @@ use core::ops::{Add, Mul, Neg, Sub}; use rand::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; +use crate::{ + impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, + impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, + new_curve_impl, +}; + #[cfg(feature = "derive_serde")] use serde::{Deserialize, Serialize}; @@ -48,6 +54,31 @@ const SECP_GENERATOR_Y: Fp = Fp::from_raw([ const SECP_A: Fp = Fp::from_raw([0, 0, 0, 0]); const SECP_B: Fp = Fp::from_raw([7, 0, 0, 0]); +new_curve_impl!( + (pub), + Secp256k1, + Secp256k1Affine, + true, + Fp, + Fq, + (SECP_GENERATOR_X,SECP_GENERATOR_Y), + SECP_A, + SECP_B, + "secp256k1", + |curve_id, domain_prefix| simple_svdw_hash_to_curve_secp256k1(curve_id, domain_prefix), +); + +impl Secp256k1 { + // Z = -11 (reference: ) + // 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc24 + const SSWU_Z: Fp = Fp([ + 0xfffffffefffffc24, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + ]); +} + // Simplified SWU for AB == 0 // // E': y'^2 = x'^3 + A' * x' + B', where @@ -62,30 +93,54 @@ pub const ISO_SECP_A: Fp = Fp::from_raw([ ]); pub const ISO_SECP_B: Fp = Fp::from_raw([1771, 0, 0, 0]); -use crate::{ - impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, - impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, - new_curve_impl, -}; +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, +]); + +impl group::cofactor::CofactorGroup for IsoSecp256k1 { + type Subgroup = IsoSecp256k1; + + fn clear_cofactor(&self) -> Self { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + 1.into() + } +} new_curve_impl!( (pub), - Secp256k1, - Secp256k1Affine, + IsoSecp256k1, + IsoSecp256k1Affine, true, Fp, Fq, - (SECP_GENERATOR_X,SECP_GENERATOR_Y), - SECP_A, - SECP_B, + (ISO_SECP_GENERATOR_X, ISO_SECP_GENERATOR_Y), + ISO_SECP_A, + ISO_SECP_B, "secp256k1", - |curve_id, domain_prefix| simple_svdw_hash_to_curve_with_iso_map(curve_id, domain_prefix, Secp256k1::SSWU_Z, ISO_SECP_A, ISO_SECP_B, Box::new(iso_map_secp256k1::)), + |curve_id, domain_prefix| simple_svdw_hash_to_curve(curve_id, domain_prefix, IsoSecp256k1::SVDW_Z), ); -impl Secp256k1 { +impl IsoSecp256k1 { // Z = -11 (reference: ) // 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc24 - const SSWU_Z: Fp = Fp([ + // NOTE: This `Z` is the `SSWU_Z` of `Secp256k1` curve. + const SVDW_Z: Fp = Fp([ 0xfffffffefffffc24, 0xffffffffffffffff, 0xffffffffffffffff, From 54f681626d142480f036a3a9266303f75aaa00f7 Mon Sep 17 00:00:00 2001 From: duguorong Date: Wed, 13 Dec 2023 23:58:00 +0800 Subject: [PATCH 06/18] feat: update the H2C logic of "Secp256k1" curve --- src/hash_to_curve.rs | 92 +++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs index a21f8b25..30a68973 100644 --- a/src/hash_to_curve.rs +++ b/src/hash_to_curve.rs @@ -5,7 +5,10 @@ use pasta_curves::arithmetic::CurveExt; use static_assertions::const_assert; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -use crate::ff_ext::Legendre; +use crate::{ + ff_ext::Legendre, + secp256k1::{IsoSecp256k1, Secp256k1}, +}; /// Hashes over a message and writes the output to all of `buf`. /// Modified from https://github.com/zcash/pasta_curves/blob/7e3fc6a4919f6462a32b79dd226cb2587b7961eb/src/hashtocurve.rs#L11. @@ -87,12 +90,7 @@ fn hash_to_field>( // Implementation of #[allow(clippy::too_many_arguments)] -pub(crate) fn simple_svdw_map_to_curve( - u: C::Base, - z: C::Base, - a: C::Base, - b: C::Base, -) -> (C::Base, C::Base) +pub(crate) fn simple_svdw_map_to_curve(u: C::Base, z: C::Base, a: C::Base, b: C::Base) -> C where C: CurveExt, { @@ -151,7 +149,7 @@ where //25. x = x / tv4 let x = x * tv4.invert().unwrap(); //26. return (x, y) - (x, y) + C::new_jacobian(x, y, one).unwrap() } // Implementation of @@ -169,9 +167,7 @@ where let mut us = [C::Base::ZERO; 2]; hash_to_field("SSWU", curve_id, domain_prefix, message, &mut us); - let [q0, q1]: [C; 2] = us - .map(|u| simple_svdw_map_to_curve::(u, z, C::a(), C::b())) - .map(|(x, y)| C::new_jacobian(x, y, C::Base::ONE).unwrap()); + let [q0, q1]: [C; 2] = us.map(|u| simple_svdw_map_to_curve::(u, z, C::a(), C::b())); let r = q0 + &q1; debug_assert!(bool::from(r.is_on_curve())); @@ -181,34 +177,20 @@ where // Implementation of #[allow(clippy::type_complexity)] -pub(crate) fn simple_svdw_hash_to_curve_with_iso_map<'a, C>( - curve_id: &'static str, +pub(crate) fn simple_svdw_hash_to_curve_secp256k1<'a>( + _curve_id: &'static str, domain_prefix: &'a str, - z: C::Base, - iso_a: C::Base, - iso_b: C::Base, - iso_map: Box (C::Base, C::Base)>, -) -> Box C + 'a> -where - C: CurveExt, - C::Base: FromUniformBytes<64>, -{ +) -> Box Secp256k1 + 'a> { Box::new(move |message| { - let mut us = [C::Base::ZERO; 2]; - hash_to_field("SSWU", curve_id, domain_prefix, message, &mut us); + let rp = IsoSecp256k1::hash_to_curve(domain_prefix)(message); - let [q0, q1] = us - .map(|u| simple_svdw_map_to_curve::(u, z, iso_a, iso_b)) - .map(|(xp, yp)| iso_map(xp, yp)) - .map(|(x, y)| { - if x.is_zero().into() && y.is_zero().into() { - C::identity() - } else { - C::new_jacobian(x, y, C::Base::ONE).unwrap() - } - }); + let r = { + let (xp, yp, zp) = rp.jacobian_coordinates(); + let (xp, yp) = jacobian_to_affine::(xp, yp, zp); + let (x, y) = iso_map_secp256k1(xp, yp); + Secp256k1::new_jacobian(x, y, ::Base::ONE).unwrap() + }; - let r = q0 + &q1; debug_assert!(bool::from(r.is_on_curve())); r }) @@ -216,11 +198,10 @@ where /// 3-Isogeny Map for Secp256k1 /// Reference: -pub fn iso_map_secp256k1(x: C::Base, y: C::Base) -> (C::Base, C::Base) -where - C: CurveExt, - C::Base: FromUniformBytes<64>, -{ +pub fn iso_map_secp256k1( + x: ::Base, + y: ::Base, +) -> (::Base, ::Base) { // constants for secp256k1 iso_map computation const K: [[&str; 4]; 5] = [ ["0x00", "0x00", "0x00", "0x00"], @@ -249,10 +230,12 @@ where "0x00", ], ]; - let mut k: [[C::Base; 4]; 5] = [[C::Base::from_uniform_bytes(&[0; 64]); 4]; 5]; + let mut k: [[::Base; 4]; 5] = + [[::Base::from_uniform_bytes(&[0; 64]); 4]; 5]; for i in 0..5 { for j in 0..4 { - k[i][j] = C::Base::from_uniform_bytes(&hex_str_to_le_bytes(K[i][j])); + k[i][j] = + ::Base::from_uniform_bytes(&hex_str_to_le_bytes(K[i][j])); } } @@ -268,7 +251,10 @@ where // Exceptional case MUST return identity // reference: if x_den.is_zero().into() || y_den.is_zero().into() { - return (C::Base::ZERO, C::Base::ZERO); + return ( + ::Base::ZERO, + ::Base::ZERO, + ); } let x = x_num * x_den.invert().unwrap(); @@ -277,6 +263,26 @@ where (x, y) } +/// Converting a point from Jacobian coordinates to affine coordinates on an elliptic curve +fn jacobian_to_affine(x: C::Base, y: C::Base, z: C::Base) -> (C::Base, C::Base) +where + C: CurveExt, + C::Base: FromUniformBytes<64>, +{ + // identity + if z.is_zero().into() { + return (C::Base::ZERO, C::Base::ZERO); + } + + let z_squared = z * z; + let z_cubed = z_squared * z; + + let z_squared_inv = z_squared.invert().unwrap(); + let z_cubed_inv = z_cubed.invert().unwrap(); + + (x * z_squared_inv, y * z_cubed_inv) +} + /// Convert hex string to little-endian bytes array of length `L` /// /// NOTE: hex string should be prefixed with "0x" From 6e9a32b61586349f1d70768226c7cd8d39523d57 Mon Sep 17 00:00:00 2001 From: duguorong Date: Thu, 14 Dec 2023 00:07:59 +0800 Subject: [PATCH 07/18] fix: improve the "iso_map_secp256k1" func --- src/hash_to_curve.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs index 30a68973..686b44de 100644 --- a/src/hash_to_curve.rs +++ b/src/hash_to_curve.rs @@ -1,6 +1,7 @@ #![allow(clippy::op_ref)] use ff::{Field, FromUniformBytes, PrimeField}; +use group::Group; use pasta_curves::arithmetic::CurveExt; use static_assertions::const_assert; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -184,12 +185,7 @@ pub(crate) fn simple_svdw_hash_to_curve_secp256k1<'a>( Box::new(move |message| { let rp = IsoSecp256k1::hash_to_curve(domain_prefix)(message); - let r = { - let (xp, yp, zp) = rp.jacobian_coordinates(); - let (xp, yp) = jacobian_to_affine::(xp, yp, zp); - let (x, y) = iso_map_secp256k1(xp, yp); - Secp256k1::new_jacobian(x, y, ::Base::ONE).unwrap() - }; + let r = iso_map_secp256k1(rp); debug_assert!(bool::from(r.is_on_curve())); r @@ -198,10 +194,7 @@ pub(crate) fn simple_svdw_hash_to_curve_secp256k1<'a>( /// 3-Isogeny Map for Secp256k1 /// Reference: -pub fn iso_map_secp256k1( - x: ::Base, - y: ::Base, -) -> (::Base, ::Base) { +pub fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { // constants for secp256k1 iso_map computation const K: [[&str; 4]; 5] = [ ["0x00", "0x00", "0x00", "0x00"], @@ -239,6 +232,11 @@ pub fn iso_map_secp256k1( } } + // convert to affine form + let (xp, yp, zp) = rp.jacobian_coordinates(); + let (x, y) = jacobian_to_affine::(xp, yp, zp); + + // iso_map logic let x_squared = x.square(); let x_cubed = x * x_squared; @@ -249,25 +247,21 @@ pub fn iso_map_secp256k1( let y_den = x_cubed + k[4][2] * x_squared + k[4][1] * x + k[4][0]; // Exceptional case MUST return identity - // reference: + // reference: if x_den.is_zero().into() || y_den.is_zero().into() { - return ( - ::Base::ZERO, - ::Base::ZERO, - ); + return Secp256k1::identity(); } let x = x_num * x_den.invert().unwrap(); let y = y * (y_num * y_den.invert().unwrap()); - (x, y) + Secp256k1::new_jacobian(x, y, ::Base::ONE).unwrap() } /// Converting a point from Jacobian coordinates to affine coordinates on an elliptic curve fn jacobian_to_affine(x: C::Base, y: C::Base, z: C::Base) -> (C::Base, C::Base) where C: CurveExt, - C::Base: FromUniformBytes<64>, { // identity if z.is_zero().into() { From 2f5d8f8d9970f97fbdd2753285c01ee1a06bcca3 Mon Sep 17 00:00:00 2001 From: duguorong Date: Thu, 14 Dec 2023 00:11:47 +0800 Subject: [PATCH 08/18] chore: fmt --- src/hash_to_curve.rs | 2 +- src/secp256k1/curve.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs index 686b44de..9b59ac86 100644 --- a/src/hash_to_curve.rs +++ b/src/hash_to_curve.rs @@ -246,7 +246,7 @@ pub fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { let y_num = k[3][3] * x_cubed + k[3][2] * x_squared + k[3][1] * x + k[3][0]; let y_den = x_cubed + k[4][2] * x_squared + k[4][1] * x + k[4][0]; - // Exceptional case MUST return identity + // exceptional case MUST return identity // reference: if x_den.is_zero().into() || y_den.is_zero().into() { return Secp256k1::identity(); diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index 3088ce91..9af2330c 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -71,6 +71,7 @@ new_curve_impl!( impl Secp256k1 { // Z = -11 (reference: ) // 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc24 + #[allow(dead_code)] const SSWU_Z: Fp = Fp([ 0xfffffffefffffc24, 0xffffffffffffffff, From 14f9d71d31d5143447bde2cff85820620459d996 Mon Sep 17 00:00:00 2001 From: duguorong Date: Thu, 14 Dec 2023 00:14:15 +0800 Subject: [PATCH 09/18] fix: roll back the "simple_svdw_map_to_curve" func signature --- src/hash_to_curve.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs index 9b59ac86..54f32359 100644 --- a/src/hash_to_curve.rs +++ b/src/hash_to_curve.rs @@ -91,12 +91,14 @@ fn hash_to_field>( // Implementation of #[allow(clippy::too_many_arguments)] -pub(crate) fn simple_svdw_map_to_curve(u: C::Base, z: C::Base, a: C::Base, b: C::Base) -> C +pub(crate) fn simple_svdw_map_to_curve(u: C::Base, z: C::Base) -> C where C: CurveExt, { 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(); @@ -168,7 +170,7 @@ where let mut us = [C::Base::ZERO; 2]; hash_to_field("SSWU", curve_id, domain_prefix, message, &mut us); - let [q0, q1]: [C; 2] = us.map(|u| simple_svdw_map_to_curve::(u, z, C::a(), C::b())); + let [q0, q1]: [C; 2] = us.map(|u| simple_svdw_map_to_curve::(u, z)); let r = q0 + &q1; debug_assert!(bool::from(r.is_on_curve())); From 31690de773abda8ba029f173e614e76f40faea9f Mon Sep 17 00:00:00 2001 From: duguorong Date: Sat, 16 Dec 2023 11:23:23 +0800 Subject: [PATCH 10/18] fix: rename the "simple_svdw_*" with "sswu_*" --- src/hash_to_curve.rs | 8 ++++---- src/secp256k1/curve.rs | 8 ++++---- src/secp256r1/curve.rs | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs index 54f32359..afafaaf6 100644 --- a/src/hash_to_curve.rs +++ b/src/hash_to_curve.rs @@ -91,7 +91,7 @@ fn hash_to_field>( // Implementation of #[allow(clippy::too_many_arguments)] -pub(crate) fn simple_svdw_map_to_curve(u: C::Base, z: C::Base) -> C +pub(crate) fn sswu_map_to_curve(u: C::Base, z: C::Base) -> C where C: CurveExt, { @@ -157,7 +157,7 @@ where // Implementation of #[allow(clippy::type_complexity)] -pub(crate) fn simple_svdw_hash_to_curve<'a, C>( +pub(crate) fn sswu_hash_to_curve<'a, C>( curve_id: &'static str, domain_prefix: &'a str, z: C::Base, @@ -170,7 +170,7 @@ where let mut us = [C::Base::ZERO; 2]; hash_to_field("SSWU", curve_id, domain_prefix, message, &mut us); - let [q0, q1]: [C; 2] = us.map(|u| simple_svdw_map_to_curve::(u, z)); + let [q0, q1]: [C; 2] = us.map(|u| sswu_map_to_curve::(u, z)); let r = q0 + &q1; debug_assert!(bool::from(r.is_on_curve())); @@ -180,7 +180,7 @@ where // Implementation of #[allow(clippy::type_complexity)] -pub(crate) fn simple_svdw_hash_to_curve_secp256k1<'a>( +pub(crate) fn sswu_hash_to_curve_secp256k1<'a>( _curve_id: &'static str, domain_prefix: &'a str, ) -> Box Secp256k1 + 'a> { diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index 9af2330c..d7af866a 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -1,7 +1,7 @@ use crate::ff::WithSmallOrderMulGroup; use crate::ff::{Field, PrimeField}; use crate::group::{prime::PrimeCurveAffine, Curve, Group as _, GroupEncoding}; -use crate::hash_to_curve::{simple_svdw_hash_to_curve, simple_svdw_hash_to_curve_secp256k1}; +use crate::hash_to_curve::{sswu_hash_to_curve, sswu_hash_to_curve_secp256k1}; use crate::secp256k1::Fp; use crate::secp256k1::Fq; use crate::{Coordinates, CurveAffine, CurveExt}; @@ -65,7 +65,7 @@ new_curve_impl!( SECP_A, SECP_B, "secp256k1", - |curve_id, domain_prefix| simple_svdw_hash_to_curve_secp256k1(curve_id, domain_prefix), + |curve_id, domain_prefix| sswu_hash_to_curve_secp256k1(curve_id, domain_prefix), ); impl Secp256k1 { @@ -134,14 +134,14 @@ new_curve_impl!( ISO_SECP_A, ISO_SECP_B, "secp256k1", - |curve_id, domain_prefix| simple_svdw_hash_to_curve(curve_id, domain_prefix, IsoSecp256k1::SVDW_Z), + |curve_id, domain_prefix| sswu_hash_to_curve(curve_id, domain_prefix, IsoSecp256k1::SSWU_Z), ); impl IsoSecp256k1 { // Z = -11 (reference: ) // 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc24 // NOTE: This `Z` is the `SSWU_Z` of `Secp256k1` curve. - const SVDW_Z: Fp = Fp([ + const SSWU_Z: Fp = Fp([ 0xfffffffefffffc24, 0xffffffffffffffff, 0xffffffffffffffff, diff --git a/src/secp256r1/curve.rs b/src/secp256r1/curve.rs index 7e6e24ef..e592d82b 100644 --- a/src/secp256r1/curve.rs +++ b/src/secp256r1/curve.rs @@ -1,7 +1,7 @@ use crate::ff::WithSmallOrderMulGroup; use crate::ff::{Field, PrimeField}; use crate::group::{prime::PrimeCurveAffine, Curve, Group as _, GroupEncoding}; -use crate::hash_to_curve::simple_svdw_hash_to_curve; +use crate::hash_to_curve::sswu_hash_to_curve; use crate::secp256r1::Fp; use crate::secp256r1::Fq; use crate::{Coordinates, CurveAffine, CurveExt}; @@ -76,7 +76,7 @@ new_curve_impl!( SECP_A, SECP_B, "secp256r1", - |curve_id, domain_prefix| simple_svdw_hash_to_curve(curve_id, domain_prefix, Secp256r1::SSVDW_Z), + |curve_id, domain_prefix| sswu_hash_to_curve(curve_id, domain_prefix, Secp256r1::SSVDW_Z), ); impl Secp256r1 { From 459bcdb86c4d14112a870dfdfc966302e34dec90 Mon Sep 17 00:00:00 2001 From: duguorong Date: Sat, 16 Dec 2023 11:51:40 +0800 Subject: [PATCH 11/18] chore: refactor the testing in "secp256k1" curve --- src/secp256k1/curve.rs | 131 +++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index d7af866a..fdc6fb89 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -149,85 +149,90 @@ impl IsoSecp256k1 { ]); } -#[test] -fn test_curve() { - crate::tests::curve::curve_tests::(); -} +#[cfg(test)] +mod tests { + use super::*; -#[test] -fn test_hash_to_curve() { - crate::tests::curve::hash_to_curve_test::(); -} + #[test] + fn test_curve() { + crate::tests::curve::curve_tests::(); + } -#[test] -fn test_serialization() { - crate::tests::curve::random_serialization_test::(); - #[cfg(feature = "derive_serde")] - crate::tests::curve::random_serde_test::(); -} + #[test] + fn test_hash_to_curve() { + crate::tests::curve::hash_to_curve_test::(); + } -#[test] -fn test_endo_consistency() { - let g = Secp256k1::generator(); - assert_eq!(g * Fq::ZETA, g.endo()); -} + #[test] + fn test_serialization() { + crate::tests::curve::random_serialization_test::(); + #[cfg(feature = "derive_serde")] + crate::tests::curve::random_serde_test::(); + } -#[test] -fn ecdsa_example() { - use crate::group::Curve; - use crate::CurveAffine; - use ff::FromUniformBytes; - use rand_core::OsRng; - - fn mod_n(x: Fp) -> Fq { - let mut x_repr = [0u8; 32]; - x_repr.copy_from_slice(x.to_repr().as_ref()); - let mut x_bytes = [0u8; 64]; - x_bytes[..32].copy_from_slice(&x_repr[..]); - Fq::from_uniform_bytes(&x_bytes) + #[test] + fn test_endo_consistency() { + let g = Secp256k1::generator(); + assert_eq!(g * Fq::ZETA, g.endo()); } - let g = Secp256k1::generator(); + #[test] + fn ecdsa_example() { + use crate::group::Curve; + use crate::CurveAffine; + use ff::FromUniformBytes; + use rand_core::OsRng; + + fn mod_n(x: Fp) -> Fq { + let mut x_repr = [0u8; 32]; + x_repr.copy_from_slice(x.to_repr().as_ref()); + let mut x_bytes = [0u8; 64]; + x_bytes[..32].copy_from_slice(&x_repr[..]); + Fq::from_uniform_bytes(&x_bytes) + } + + let g = Secp256k1::generator(); - for _ in 0..1000 { - // Generate a key pair - let sk = Fq::random(OsRng); - let pk = (g * sk).to_affine(); + for _ in 0..1000 { + // Generate a key pair + let sk = Fq::random(OsRng); + let pk = (g * sk).to_affine(); - // Generate a valid signature - // Suppose `m_hash` is the message hash - let msg_hash = Fq::random(OsRng); + // Generate a valid signature + // Suppose `m_hash` is the message hash + let msg_hash = Fq::random(OsRng); - let (r, s) = { - // Draw arandomness - let k = Fq::random(OsRng); - let k_inv = k.invert().unwrap(); + let (r, s) = { + // Draw arandomness + let k = Fq::random(OsRng); + let k_inv = k.invert().unwrap(); - // Calculate `r` - let r_point = (g * k).to_affine().coordinates().unwrap(); - let x = r_point.x(); - let r = mod_n(*x); + // Calculate `r` + let r_point = (g * k).to_affine().coordinates().unwrap(); + let x = r_point.x(); + let r = mod_n(*x); - // Calculate `s` - let s = k_inv * (msg_hash + (r * sk)); + // Calculate `s` + let s = k_inv * (msg_hash + (r * sk)); - (r, s) - }; + (r, s) + }; - { - // Verify - let s_inv = s.invert().unwrap(); - let u_1 = msg_hash * s_inv; - let u_2 = r * s_inv; + { + // Verify + let s_inv = s.invert().unwrap(); + let u_1 = msg_hash * s_inv; + let u_2 = r * s_inv; - let v_1 = g * u_1; - let v_2 = pk * u_2; + let v_1 = g * u_1; + let v_2 = pk * u_2; - let r_point = (v_1 + v_2).to_affine().coordinates().unwrap(); - let x_candidate = r_point.x(); - let r_candidate = mod_n(*x_candidate); + let r_point = (v_1 + v_2).to_affine().coordinates().unwrap(); + let x_candidate = r_point.x(); + let r_candidate = mod_n(*x_candidate); - assert_eq!(r, r_candidate); + assert_eq!(r, r_candidate); + } } } } From 4ed6bb1e599fb0e1928fad78a8286551744aaf45 Mon Sep 17 00:00:00 2001 From: duguorong Date: Sat, 16 Dec 2023 12:52:06 +0800 Subject: [PATCH 12/18] refactor: create "utils" module & move "fe_from_str" --- src/lib.rs | 2 ++ src/tests/curve.rs | 2 +- src/tests/mod.rs | 15 --------------- src/utils.rs | 14 ++++++++++++++ 4 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 src/utils.rs diff --git a/src/lib.rs b/src/lib.rs index 82613ca9..b28ed89e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,8 @@ pub mod secp256k1; pub mod secp256r1; pub mod secq256k1; +pub mod utils; + #[macro_use] mod derive; diff --git a/src/tests/curve.rs b/src/tests/curve.rs index 95bece49..32c61aa3 100644 --- a/src/tests/curve.rs +++ b/src/tests/curve.rs @@ -3,7 +3,7 @@ use crate::ff::Field; use crate::ff_ext::Legendre; use crate::group::prime::PrimeCurveAffine; -use crate::tests::fe_from_str; +use crate::utils::fe_from_str; use crate::{group::GroupEncoding, serde::SerdeObject}; use crate::{hash_to_curve, CurveAffine, CurveExt}; use rand_core::{OsRng, RngCore}; diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 5c6cd819..f773c8d7 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,17 +1,2 @@ -use ff::PrimeField; -use num_bigint::BigUint; -use num_traits::Num; -use std::borrow::Cow; - pub mod curve; pub mod field; - -pub(crate) fn fe_from_str(string: impl AsRef) -> F { - let string = string.as_ref(); - let oct = if let Some(hex) = string.strip_prefix("0x") { - Cow::Owned(BigUint::from_str_radix(hex, 16).unwrap().to_string()) - } else { - Cow::Borrowed(string) - }; - F::from_str_vartime(&oct).unwrap() -} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 00000000..3b595672 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,14 @@ +use ff::PrimeField; +use num_bigint::BigUint; +use num_traits::Num; +use std::borrow::Cow; + +pub(crate) fn fe_from_str(string: impl AsRef) -> F { + let string = string.as_ref(); + let oct = if let Some(hex) = string.strip_prefix("0x") { + Cow::Owned(BigUint::from_str_radix(hex, 16).unwrap().to_string()) + } else { + Cow::Borrowed(string) + }; + F::from_str_vartime(&oct).unwrap() +} From d6fff359892cd61709653e52b646e18cde7a718c Mon Sep 17 00:00:00 2001 From: duguorong Date: Sat, 16 Dec 2023 12:52:35 +0800 Subject: [PATCH 13/18] refactor: use "fe_from_str" in "hash_to_curve" module --- src/hash_to_curve.rs | 46 ++++---------------------------------------- 1 file changed, 4 insertions(+), 42 deletions(-) diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs index afafaaf6..8a1c4130 100644 --- a/src/hash_to_curve.rs +++ b/src/hash_to_curve.rs @@ -9,6 +9,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::{ ff_ext::Legendre, secp256k1::{IsoSecp256k1, Secp256k1}, + utils::fe_from_str, }; /// Hashes over a message and writes the output to all of `buf`. @@ -225,12 +226,10 @@ pub fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { "0x00", ], ]; - let mut k: [[::Base; 4]; 5] = - [[::Base::from_uniform_bytes(&[0; 64]); 4]; 5]; - for i in 0..5 { + let mut k: [[::Base; 4]; 5] = [[fe_from_str("0x00"); 4]; 5]; + for i in 1..5 { for j in 0..4 { - k[i][j] = - ::Base::from_uniform_bytes(&hex_str_to_le_bytes(K[i][j])); + k[i][j] = fe_from_str(K[i][j]); } } @@ -279,43 +278,6 @@ where (x * z_squared_inv, y * z_cubed_inv) } -/// Convert hex string to little-endian bytes array of length `L` -/// -/// NOTE: hex string should be prefixed with "0x" -/// -/// Example: -/// -/// hex_str_to_le_bytes::<4>("0x01020304") -> [4, 3, 2, 1] -/// -/// hex_str_to_le_bytes::<6>("0x01020304") -> [4, 3, 2, 1, 0, 0] -/// -/// hex_str_to_le_bytes::<2>("0x01020304") -> [4, 3] -/// -fn hex_str_to_le_bytes(hex: &str) -> [u8; L] { - let padded_hex_string = if hex.len() % 2 != 0 { - format!("0{}", &hex[2..]) - } else { - hex[2..].to_owned() - }; - - // Convert each pair of hex characters to u8 and collect into a vector - let le_bytes: Result, _> = (0..padded_hex_string.len()) - .step_by(2) - .rev() // Iterate in reverse order for little-endian byte order - .map(|i| { - u8::from_str_radix(&padded_hex_string[i..i + 2], 16) - .map_err(|_| "Invalid hex character") - }) - .collect(); - let le_bytes = le_bytes.expect("Invalid bytes"); - - let mut result = [0; L]; - for i in 0..L.min(le_bytes.len()) { - result[i] = le_bytes[i]; - } - result -} - #[allow(clippy::too_many_arguments)] pub(crate) fn svdw_map_to_curve( u: C::Base, From 161c661ed2bf098cc85a30d1e2702f2132fa1933 Mon Sep 17 00:00:00 2001 From: duguorong Date: Sat, 16 Dec 2023 13:49:41 +0800 Subject: [PATCH 14/18] fix: remove unnecessary func since we use projective coordinates --- src/hash_to_curve.rs | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs index 8a1c4130..7605ec6a 100644 --- a/src/hash_to_curve.rs +++ b/src/hash_to_curve.rs @@ -233,9 +233,11 @@ pub fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { } } - // convert to affine form - let (xp, yp, zp) = rp.jacobian_coordinates(); - let (x, y) = jacobian_to_affine::(xp, yp, zp); + // convert to affine form: (x, y) = (X/Z, Y/Z) + let (x, y) = { + let z_inv = rp.z.invert().unwrap(); + (rp.x * z_inv, rp.y * z_inv) + }; // iso_map logic let x_squared = x.square(); @@ -259,25 +261,6 @@ pub fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { Secp256k1::new_jacobian(x, y, ::Base::ONE).unwrap() } -/// Converting a point from Jacobian coordinates to affine coordinates on an elliptic curve -fn jacobian_to_affine(x: C::Base, y: C::Base, z: C::Base) -> (C::Base, C::Base) -where - C: CurveExt, -{ - // identity - if z.is_zero().into() { - return (C::Base::ZERO, C::Base::ZERO); - } - - let z_squared = z * z; - let z_cubed = z_squared * z; - - let z_squared_inv = z_squared.invert().unwrap(); - let z_cubed_inv = z_cubed.invert().unwrap(); - - (x * z_squared_inv, y * z_cubed_inv) -} - #[allow(clippy::too_many_arguments)] pub(crate) fn svdw_map_to_curve( u: C::Base, From 9ff891e07f55d7ce4f46d428d0c2c63f0e5ecdb4 Mon Sep 17 00:00:00 2001 From: duguorong Date: Mon, 18 Dec 2023 21:36:08 +0800 Subject: [PATCH 15/18] chore: move the "iso_map_secp256k1" --- src/hash_to_curve.rs | 70 +---------------------- src/secp256k1/curve.rs | 126 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 126 insertions(+), 70 deletions(-) diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs index 7605ec6a..b4d19b86 100644 --- a/src/hash_to_curve.rs +++ b/src/hash_to_curve.rs @@ -1,15 +1,13 @@ #![allow(clippy::op_ref)] use ff::{Field, FromUniformBytes, PrimeField}; -use group::Group; use pasta_curves::arithmetic::CurveExt; use static_assertions::const_assert; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::{ ff_ext::Legendre, - secp256k1::{IsoSecp256k1, Secp256k1}, - utils::fe_from_str, + secp256k1::{iso_map_secp256k1, IsoSecp256k1, Secp256k1}, }; /// Hashes over a message and writes the output to all of `buf`. @@ -195,72 +193,6 @@ pub(crate) fn sswu_hash_to_curve_secp256k1<'a>( }) } -/// 3-Isogeny Map for Secp256k1 -/// Reference: -pub fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { - // constants for secp256k1 iso_map computation - const K: [[&str; 4]; 5] = [ - ["0x00", "0x00", "0x00", "0x00"], - [ - "0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7", - "0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581", - "0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262", - "0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c", - ], - [ - "0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b", - "0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14", - "0x00", - "0x00", - ], - [ - "0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c", - "0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3", - "0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931", - "0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84", - ], - [ - "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b", - "0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573", - "0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f", - "0x00", - ], - ]; - let mut k: [[::Base; 4]; 5] = [[fe_from_str("0x00"); 4]; 5]; - for i in 1..5 { - for j in 0..4 { - k[i][j] = fe_from_str(K[i][j]); - } - } - - // convert to affine form: (x, y) = (X/Z, Y/Z) - let (x, y) = { - let z_inv = rp.z.invert().unwrap(); - (rp.x * z_inv, rp.y * z_inv) - }; - - // iso_map logic - let x_squared = x.square(); - let x_cubed = x * x_squared; - - let x_num = k[1][3] * x_cubed + k[1][2] * x_squared + k[1][1] * x + k[1][0]; - let x_den = x_squared + k[2][1] * x + k[2][0]; - - let y_num = k[3][3] * x_cubed + k[3][2] * x_squared + k[3][1] * x + k[3][0]; - let y_den = x_cubed + k[4][2] * x_squared + k[4][1] * x + k[4][0]; - - // exceptional case MUST return identity - // reference: - if x_den.is_zero().into() || y_den.is_zero().into() { - return Secp256k1::identity(); - } - - let x = x_num * x_den.invert().unwrap(); - let y = y * (y_num * y_den.invert().unwrap()); - - Secp256k1::new_jacobian(x, y, ::Base::ONE).unwrap() -} - #[allow(clippy::too_many_arguments)] pub(crate) fn svdw_map_to_curve( u: C::Base, diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index fdc6fb89..a93f75f9 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -124,7 +124,7 @@ impl group::cofactor::CofactorGroup for IsoSecp256k1 { } new_curve_impl!( - (pub), + (pub(crate)), IsoSecp256k1, IsoSecp256k1Affine, true, @@ -149,6 +149,130 @@ impl IsoSecp256k1 { ]); } +/// 3-Isogeny Map for Secp256k1 +/// Reference: +pub(crate) fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { + // constants for secp256k1 iso_map computation + const K: [[Fp; 4]; 5] = [ + [Fp::ZERO; 4], + [ + Fp([ + 0x8e38e38daaaaa8c7, + 0x38e38e38e38e38e3, + 0xe38e38e38e38e38e, + 0x8e38e38e38e38e38, + ]), + Fp([ + 0xdfff1044f17c6581, + 0xd595d2fc0bf63b92, + 0xb9f315cea7fd44c5, + 0x7d3d4c80bc321d5, + ]), + Fp([ + 0x4ecbd0b53d9dd262, + 0xe4506144037c4031, + 0xe2a413deca25caec, + 0x534c328d23f234e6, + ]), + Fp([ + 0x8e38e38daaaaa88c, + 0x38e38e38e38e38e3, + 0xe38e38e38e38e38e, + 0x8e38e38e38e38e38, + ]), + ], + [ + Fp([ + 0x9fe6b745781eb49b, + 0x86cd409542f8487d, + 0x9ca34ccbb7b640dd, + 0xd35771193d94918a, + ]), + Fp([ + 0xc52a56612a8c6d14, + 0x06d36b641f5e41bb, + 0xf7c4b2d51b542254, + 0xedadc6f64383dc1d, + ]), + Fp::ZERO, + Fp::ZERO, + ], + [ + Fp([ + 0xa12f684b8e38e23c, + 0x2f684bda12f684bd, + 0x684bda12f684bda1, + 0x4bda12f684bda12f, + ]), + Fp([ + 0xdffc90fc201d71a3, + 0x647ab046d686da6f, + 0xa9d0a54b12a0a6d5, + 0xc75e0c32d5cb7c0f, + ]), + Fp([ + 0xa765e85a9ecee931, + 0x722830a201be2018, + 0x715209ef6512e576, + 0x29a6194691f91a73, + ]), + Fp([ + 0x84bda12f38e38d84, + 0xbda12f684bda12f6, + 0xa12f684bda12f684, + 0x2f684bda12f684bd, + ]), + ], + [ + Fp([ + 0xfffffffefffff93b, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + ]), + Fp([ + 0xdfb425d2685c2573, + 0x9467c1bfc8e8d978, + 0xd5e9e6632722c298, + 0x7a06534bb8bdb49f, + ]), + Fp([ + 0xa7bf8192bfd2a76f, + 0x0a3d21162f0d6299, + 0xf3a70c3fa8fe337e, + 0x6484aa716545ca2c, + ]), + Fp::ZERO, + ], + ]; + + // Convert to affine coordinates: + let coords = ::AffineExt::coordinates(&rp.into()).unwrap(); + let x = coords.x(); + let y = coords.y(); + + // iso_map logic + let x_squared = x.square(); + let x_cubed = x * x_squared; + + let x_num = K[1][3] * x_cubed + K[1][2] * x_squared + K[1][1] * x + K[1][0]; + let x_den = x_squared + K[2][1] * x + K[2][0]; + + let y_num = K[3][3] * x_cubed + K[3][2] * x_squared + K[3][1] * x + K[3][0]; + let y_den = x_cubed + K[4][2] * x_squared + K[4][1] * x + K[4][0]; + + // exceptional case MUST return identity + // reference: + if x_den.is_zero().into() || y_den.is_zero().into() { + return Secp256k1::identity(); + } + + let x = x_num * x_den.invert().unwrap(); + let y = y * (y_num * y_den.invert().unwrap()); + + Secp256k1::new_jacobian(x, y, ::Base::ONE).unwrap() +} + #[cfg(test)] mod tests { use super::*; From dc54a13f64a9770d8570a1acf9bf81c35bbc5480 Mon Sep 17 00:00:00 2001 From: duguorong Date: Mon, 18 Dec 2023 22:11:16 +0800 Subject: [PATCH 16/18] chore: fix the constants import --- src/secp256k1/curve.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index a93f75f9..ab8c8d50 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -141,7 +141,7 @@ impl IsoSecp256k1 { // Z = -11 (reference: ) // 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc24 // NOTE: This `Z` is the `SSWU_Z` of `Secp256k1` curve. - const SSWU_Z: Fp = Fp([ + const SSWU_Z: Fp = Fp::from_raw([ 0xfffffffefffffc24, 0xffffffffffffffff, 0xffffffffffffffff, @@ -156,25 +156,25 @@ pub(crate) fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { const K: [[Fp; 4]; 5] = [ [Fp::ZERO; 4], [ - Fp([ + Fp::from_raw([ 0x8e38e38daaaaa8c7, 0x38e38e38e38e38e3, 0xe38e38e38e38e38e, 0x8e38e38e38e38e38, ]), - Fp([ + Fp::from_raw([ 0xdfff1044f17c6581, 0xd595d2fc0bf63b92, 0xb9f315cea7fd44c5, 0x7d3d4c80bc321d5, ]), - Fp([ + Fp::from_raw([ 0x4ecbd0b53d9dd262, 0xe4506144037c4031, 0xe2a413deca25caec, 0x534c328d23f234e6, ]), - Fp([ + Fp::from_raw([ 0x8e38e38daaaaa88c, 0x38e38e38e38e38e3, 0xe38e38e38e38e38e, @@ -182,13 +182,13 @@ pub(crate) fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { ]), ], [ - Fp([ + Fp::from_raw([ 0x9fe6b745781eb49b, 0x86cd409542f8487d, 0x9ca34ccbb7b640dd, 0xd35771193d94918a, ]), - Fp([ + Fp::from_raw([ 0xc52a56612a8c6d14, 0x06d36b641f5e41bb, 0xf7c4b2d51b542254, @@ -198,25 +198,25 @@ pub(crate) fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { Fp::ZERO, ], [ - Fp([ + Fp::from_raw([ 0xa12f684b8e38e23c, 0x2f684bda12f684bd, 0x684bda12f684bda1, 0x4bda12f684bda12f, ]), - Fp([ + Fp::from_raw([ 0xdffc90fc201d71a3, 0x647ab046d686da6f, 0xa9d0a54b12a0a6d5, 0xc75e0c32d5cb7c0f, ]), - Fp([ + Fp::from_raw([ 0xa765e85a9ecee931, 0x722830a201be2018, 0x715209ef6512e576, 0x29a6194691f91a73, ]), - Fp([ + Fp::from_raw([ 0x84bda12f38e38d84, 0xbda12f684bda12f6, 0xa12f684bda12f684, @@ -224,19 +224,19 @@ pub(crate) fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { ]), ], [ - Fp([ + Fp::from_raw([ 0xfffffffefffff93b, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, ]), - Fp([ + Fp::from_raw([ 0xdfb425d2685c2573, 0x9467c1bfc8e8d978, 0xd5e9e6632722c298, 0x7a06534bb8bdb49f, ]), - Fp([ + Fp::from_raw([ 0xa7bf8192bfd2a76f, 0x0a3d21162f0d6299, 0xf3a70c3fa8fe337e, From c917ca9ef4104d213960c2dfde9bc4c65c32d79a Mon Sep 17 00:00:00 2001 From: duguorong Date: Mon, 18 Dec 2023 22:12:32 +0800 Subject: [PATCH 17/18] chore: fix the constants import (1) --- src/secp256k1/curve.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index ab8c8d50..d6e3f3e2 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -72,7 +72,7 @@ impl Secp256k1 { // Z = -11 (reference: ) // 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc24 #[allow(dead_code)] - const SSWU_Z: Fp = Fp([ + const SSWU_Z: Fp = Fp::from_raw([ 0xfffffffefffffc24, 0xffffffffffffffff, 0xffffffffffffffff, From 9922e91749d15fa1da41288a30a0436c87d8c136 Mon Sep 17 00:00:00 2001 From: duguorong Date: Fri, 22 Dec 2023 09:51:59 +0800 Subject: [PATCH 18/18] chore: fix the type --- src/secp256k1/curve.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index d6e3f3e2..3390138c 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -270,7 +270,7 @@ pub(crate) fn iso_map_secp256k1(rp: IsoSecp256k1) -> Secp256k1 { let x = x_num * x_den.invert().unwrap(); let y = y * (y_num * y_den.invert().unwrap()); - Secp256k1::new_jacobian(x, y, ::Base::ONE).unwrap() + Secp256k1::new_jacobian(x, y, Fp::ONE).unwrap() } #[cfg(test)]