-
Notifications
You must be signed in to change notification settings - Fork 174
Add endo scalar decomposition #24
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,11 @@ | ||
| use crate::arithmetic::mul_512; | ||
| use crate::arithmetic::sbb; | ||
| use crate::arithmetic::CurveEndo; | ||
| use crate::arithmetic::EndoParameters; | ||
| use crate::bn256::Fq; | ||
| use crate::bn256::Fq2; | ||
| use crate::bn256::Fr; | ||
| use crate::endo; | ||
| use crate::ff::WithSmallOrderMulGroup; | ||
| use crate::ff::{Field, PrimeField}; | ||
| use crate::group::Curve; | ||
|
|
@@ -16,6 +21,7 @@ use core::fmt::Debug; | |
| use core::iter::Sum; | ||
| use core::ops::{Add, Mul, Neg, Sub}; | ||
| use rand::RngCore; | ||
| use std::convert::TryInto; | ||
| use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; | ||
|
|
||
| #[cfg(feature = "derive_serde")] | ||
|
|
@@ -111,6 +117,26 @@ const G2_GENERATOR_Y: Fq2 = Fq2 { | |
| ]), | ||
| }; | ||
|
|
||
| // Generated using https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go | ||
| // with `bn256::Fr::ZETA` | ||
| // See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md | ||
| // to have more details about the endomorphism. | ||
| const ENDO_PARAMS: EndoParameters = EndoParameters { | ||
|
Collaborator
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. Are these derived directly or taken form some implementation/ article? I think it would be good to add some reference in any case. Without some indication I know future me will forget what these are x)
Contributor
Author
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 think this is resolved with de71b18 |
||
| // round(b2/n) | ||
| gamma1: [ | ||
| 0x7a7bd9d4391eb18du64, | ||
| 0x4ccef014a773d2cfu64, | ||
| 0x0000000000000002u64, | ||
| 0u64, | ||
| ], | ||
| // round(-b1/n) | ||
| gamma2: [0xd91d232ec7e0b3d7u64, 0x0000000000000002u64, 0u64, 0u64], | ||
| b1: [0x8211bbeb7d4f1128u64, 0x6f4d8248eeb859fcu64, 0u64, 0u64], | ||
| b2: [0x89d3256894d213e3u64, 0u64, 0u64, 0u64], | ||
| }; | ||
|
|
||
| endo!(G1, Fr, ENDO_PARAMS); | ||
|
|
||
| impl group::cofactor::CofactorGroup for G1 { | ||
| type Subgroup = G1; | ||
|
|
||
|
|
@@ -180,9 +206,14 @@ impl CofactorGroup for G2 { | |
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
|
|
||
| use crate::arithmetic::CurveEndo; | ||
| use crate::bn256::{Fr, G1, G2}; | ||
| use crate::CurveExt; | ||
| use ff::Field; | ||
| use ff::PrimeField; | ||
| use ff::WithSmallOrderMulGroup; | ||
| use rand_core::OsRng; | ||
|
|
||
| #[test] | ||
| fn test_curve() { | ||
|
|
@@ -191,12 +222,24 @@ mod tests { | |
| } | ||
|
|
||
| #[test] | ||
| fn test_endo_consistency() { | ||
| fn test_endo() { | ||
| let g = G1::generator(); | ||
| assert_eq!(g * Fr::ZETA, g.endo()); | ||
|
|
||
| let g = G2::generator(); | ||
| assert_eq!(g * Fr::ZETA, g.endo()); | ||
| for _ in 0..100000 { | ||
| let k = Fr::random(OsRng); | ||
| let (k1, k1_neg, k2, k2_neg) = G1::decompose_scalar(&k); | ||
| if k1_neg & k2_neg { | ||
| assert_eq!(k, -Fr::from_u128(k1) + Fr::ZETA * Fr::from_u128(k2)) | ||
| } else if k1_neg { | ||
| assert_eq!(k, -Fr::from_u128(k1) - Fr::ZETA * Fr::from_u128(k2)) | ||
| } else if k2_neg { | ||
| assert_eq!(k, Fr::from_u128(k1) + Fr::ZETA * Fr::from_u128(k2)) | ||
| } else { | ||
| assert_eq!(k, Fr::from_u128(k1) - Fr::ZETA * Fr::from_u128(k2)) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -140,6 +140,57 @@ macro_rules! batch_add { | |
| }; | ||
| } | ||
|
|
||
| #[macro_export] | ||
| macro_rules! endo { | ||
| ($name:ident, $field:ident, $params:expr) => { | ||
| impl CurveEndo for $name { | ||
| fn decompose_scalar(k: &$field) -> (u128, bool, u128, bool) { | ||
|
Collaborator
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. Looks good! However there are some non-obvious optimizations that make the algorithm quite hard to follow. |
||
| let to_limbs = |e: &$field| { | ||
| let repr = e.to_repr(); | ||
| let repr = repr.as_ref(); | ||
| let tmp0 = u64::from_le_bytes(repr[0..8].try_into().unwrap()); | ||
| let tmp1 = u64::from_le_bytes(repr[8..16].try_into().unwrap()); | ||
| let tmp2 = u64::from_le_bytes(repr[16..24].try_into().unwrap()); | ||
| let tmp3 = u64::from_le_bytes(repr[24..32].try_into().unwrap()); | ||
| [tmp0, tmp1, tmp2, tmp3] | ||
| }; | ||
|
|
||
| let get_lower_128 = |e: &$field| { | ||
| let e = to_limbs(e); | ||
| u128::from(e[0]) | (u128::from(e[1]) << 64) | ||
| }; | ||
|
|
||
| let is_neg = |e: &$field| { | ||
| let e = to_limbs(e); | ||
|
Collaborator
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 am unsure of the criteria of this function. Could you give some further explanation please? :)
Contributor
Author
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. This trick actually reveals whether a element is ~128 bit or ~F::NUM_BITS. if latter we see that element is negative and subtract it from modulus and get the actual ~128 bit element. It was originally like below However in some fields we hit scalars decomposed into 129 bits so I just also made third control limb sparse�
Collaborator
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 see, thanks for the explanation! |
||
| let (_, borrow) = sbb(0xffffffffffffffff, e[0], 0); | ||
| let (_, borrow) = sbb(0xffffffffffffffff, e[1], borrow); | ||
| let (_, borrow) = sbb(0xffffffffffffffff, e[2], borrow); | ||
| let (_, borrow) = sbb(0x00, e[3], borrow); | ||
| borrow & 1 != 0 | ||
| }; | ||
|
|
||
| let input = to_limbs(&k); | ||
| let c1 = mul_512($params.gamma2, input); | ||
| let c2 = mul_512($params.gamma1, input); | ||
| let c1 = [c1[4], c1[5], c1[6], c1[7]]; | ||
| let c2 = [c2[4], c2[5], c2[6], c2[7]]; | ||
| let q1 = mul_512(c1, $params.b1); | ||
| let q2 = mul_512(c2, $params.b2); | ||
| let q1 = $field::from_raw([q1[0], q1[1], q1[2], q1[3]]); | ||
| let q2 = $field::from_raw([q2[0], q2[1], q2[2], q2[3]]); | ||
| let k2 = q2 - q1; | ||
| let k1 = k + k2 * $field::ZETA; | ||
| let k1_neg = is_neg(&k1); | ||
| let k2_neg = is_neg(&k2); | ||
| let k1 = if k1_neg { -k1 } else { k1 }; | ||
| let k2 = if k2_neg { -k2 } else { k2 }; | ||
|
CPerezz marked this conversation as resolved.
|
||
|
|
||
| (get_lower_128(&k1), k1_neg, get_lower_128(&k2), k2_neg) | ||
| } | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| #[macro_export] | ||
| macro_rules! new_curve_impl { | ||
| (($($privacy:tt)*), | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.