From 0365f29b8c2b6c6b8f0e83d787b688c79542ce4c Mon Sep 17 00:00:00 2001 From: Michael Nielsen <4518975+mikdk@users.noreply.github.com> Date: Fri, 10 May 2024 14:29:35 +0200 Subject: [PATCH] Added XYZZ coordinates and let z=0 represent infinity --- crates/crypto/examples/consts_pedersen.rs | 12 ++-- crates/crypto/src/algebra/curve/affine.rs | 14 ++++ crates/crypto/src/algebra/curve/mod.rs | 3 + crates/crypto/src/algebra/curve/projective.rs | 43 ++++++------ crates/crypto/src/algebra/curve/xyzz.rs | 69 +++++++++++++++++++ crates/crypto/src/hash/pedersen/gens.rs | 12 ++-- crates/crypto/src/hash/pedersen/hash.rs | 8 ++- crates/crypto/src/lib.rs | 8 +-- crates/crypto/src/signature/mod.rs | 7 +- 9 files changed, 126 insertions(+), 50 deletions(-) create mode 100644 crates/crypto/src/algebra/curve/xyzz.rs diff --git a/crates/crypto/examples/consts_pedersen.rs b/crates/crypto/examples/consts_pedersen.rs index 9ac1d114bf..55c083b3ff 100644 --- a/crates/crypto/examples/consts_pedersen.rs +++ b/crates/crypto/examples/consts_pedersen.rs @@ -33,13 +33,13 @@ fn generate_consts(bits: u32) -> Result { write!(buf, "use crate::algebra::curve::AffinePoint;\n\n")?; write!(buf, "pub const CURVE_CONSTS_BITS: usize = {bits};\n\n")?; - push_points(&mut buf, "P1", &PEDERSEN_P1, 248, bits)?; + push_points(&mut buf, "P1", PEDERSEN_P1, 248, bits)?; buf.push_str("\n\n\n"); - push_points(&mut buf, "P2", &PEDERSEN_P2, 4, bits)?; + push_points(&mut buf, "P2", PEDERSEN_P2, 4, bits)?; buf.push_str("\n\n\n"); - push_points(&mut buf, "P3", &PEDERSEN_P3, 248, bits)?; + push_points(&mut buf, "P3", PEDERSEN_P3, 248, bits)?; buf.push_str("\n\n\n"); - push_points(&mut buf, "P4", &PEDERSEN_P4, 4, bits)?; + push_points(&mut buf, "P4", PEDERSEN_P4, 4, bits)?; Ok(buf) } @@ -48,12 +48,10 @@ fn generate_consts(bits: u32) -> Result { fn push_points( buf: &mut String, name: &str, - base: &ProjectivePoint, + base: AffinePoint, max_bits: u32, bits: u32, ) -> std::fmt::Result { - let base = AffinePoint::from(base); - let full_chunks = max_bits / bits; let leftover_bits = max_bits % bits; let table_size_full = (1 << bits) - 1; diff --git a/crates/crypto/src/algebra/curve/affine.rs b/crates/crypto/src/algebra/curve/affine.rs index 310bc9e64c..8020e832ae 100644 --- a/crates/crypto/src/algebra/curve/affine.rs +++ b/crates/crypto/src/algebra/curve/affine.rs @@ -25,6 +25,20 @@ impl From<&ProjectivePoint> for AffinePoint { } } +impl From<&XYZZPoint> for AffinePoint { + fn from(p: &XYZZPoint) -> Self { + let a = p.zzz.inverse().unwrap(); + let b = (p.zz * a).square(); + let x = p.x * b; + let y = p.y * a; + AffinePoint { + x, + y, + infinity: false, + } + } +} + impl AffinePoint { /// Create a point from (x,y) as raw u64's in Montgomery representation pub const fn from_raw(x: [u64; 4], y: [u64; 4]) -> Self { diff --git a/crates/crypto/src/algebra/curve/mod.rs b/crates/crypto/src/algebra/curve/mod.rs index 0a80b0b07a..161356bbd9 100644 --- a/crates/crypto/src/algebra/curve/mod.rs +++ b/crates/crypto/src/algebra/curve/mod.rs @@ -4,9 +4,12 @@ mod gen_mul; mod params; mod projective; +mod xyzz; + pub use affine::AffinePoint; pub use params::{CURVE_A, CURVE_B, CURVE_G, CURVE_ORDER}; pub use projective::ProjectivePoint; +pub use xyzz::XYZZPoint; #[cfg(test)] mod tests; diff --git a/crates/crypto/src/algebra/curve/projective.rs b/crates/crypto/src/algebra/curve/projective.rs index 9808d01e42..3db083be0a 100644 --- a/crates/crypto/src/algebra/curve/projective.rs +++ b/crates/crypto/src/algebra/curve/projective.rs @@ -4,13 +4,14 @@ use bitvec::slice::BitSlice; use crate::algebra::curve::*; use crate::algebra::field::*; -/// A projective point on an elliptic curve over [MontFelt]. +/// A projective point on an elliptic curve over [MontFelt] satisfying: +/// x = X / Z +/// y = Y / Z #[derive(Clone, Debug, Eq, PartialEq)] pub struct ProjectivePoint { pub x: MontFelt, pub y: MontFelt, pub z: MontFelt, - pub infinity: bool, } impl From<&AffinePoint> for ProjectivePoint { @@ -18,12 +19,7 @@ impl From<&AffinePoint> for ProjectivePoint { let x = p.x; let y = p.y; let z = MontFelt::ONE; - ProjectivePoint { - x, - y, - z, - infinity: false, - } + ProjectivePoint { x, y, z } } } @@ -36,7 +32,6 @@ impl ProjectivePoint { x, y, z: MontFelt::ONE, - infinity: false, } } @@ -48,7 +43,6 @@ impl ProjectivePoint { x, y, z: MontFelt::ONE, - infinity: false, } } @@ -63,8 +57,7 @@ impl ProjectivePoint { Self { x: MontFelt::ZERO, y: MontFelt::ZERO, - z: MontFelt::ONE, - infinity: true, + z: MontFelt::ZERO, } } @@ -73,9 +66,14 @@ impl ProjectivePoint { self.y = -self.y; } + /// Check if the point is the point of infinity + pub fn is_infinity(&self) -> bool { + self.z.is_zero() + } + /// Double a point pub fn double(&mut self) { - if self.infinity { + if self.is_infinity() { return; } @@ -99,14 +97,13 @@ impl ProjectivePoint { /// Add a point to this point pub fn add(&mut self, other: &ProjectivePoint) { - if other.infinity { + if other.is_infinity() { return; } - if self.infinity { + if self.is_infinity() { self.x = other.x; self.y = other.y; self.z = other.z; - self.infinity = other.infinity; return; } let u0 = self.x * other.z; @@ -115,7 +112,7 @@ impl ProjectivePoint { let t1 = other.y * self.z; if u0 == u1 { if t0 != t1 { - self.infinity = true; + self.z = MontFelt::ZERO; } else { self.double(); } @@ -144,11 +141,15 @@ impl ProjectivePoint { if other.infinity { return; } - if self.infinity { + if self.is_infinity() { self.x = other.x; self.y = other.y; - self.z = MontFelt::ONE; - self.infinity = other.infinity; + self.z = if other.infinity { + MontFelt::ZERO + } else { + MontFelt::ONE + }; + return; } let u0 = self.x; @@ -157,7 +158,7 @@ impl ProjectivePoint { let t1 = other.y * self.z; if u0 == u1 { if t0 != t1 { - self.infinity = true; + self.z = MontFelt::ZERO; return; } else { self.double(); diff --git a/crates/crypto/src/algebra/curve/xyzz.rs b/crates/crypto/src/algebra/curve/xyzz.rs new file mode 100644 index 0000000000..944f89da6b --- /dev/null +++ b/crates/crypto/src/algebra/curve/xyzz.rs @@ -0,0 +1,69 @@ +use crate::algebra::curve::*; +use crate::algebra::field::*; + +/// A XYZZ point on an elliptic curve over [MontFelt] satisfying: +/// x = X / ZZ +/// y = Y / ZZ +/// ZZ^3 = ZZZ^2 +/// +/// This point representation is used for fast table-based scalar multiplication +/// and only include add_affine and add_affine_unchecked operations. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct XYZZPoint { + pub x: MontFelt, + pub y: MontFelt, + pub zz: MontFelt, + pub zzz: MontFelt, +} + +impl From<&AffinePoint> for XYZZPoint { + fn from(p: &AffinePoint) -> Self { + let x = p.x; + let y = p.y; + let zz = MontFelt::ONE; + let zzz = MontFelt::ONE; + XYZZPoint { x, y, zz, zzz } + } +} + +impl XYZZPoint { + /// Check if the point is the point of infinity + pub fn is_infinity(&self) -> bool { + self.zz.is_zero() + } + + /// Add an affine point to this point + pub fn add_affine(&mut self, other: &AffinePoint) { + if other.infinity { + return; + } + if self.is_infinity() { + self.x = other.x; + self.y = other.y; + let z = if other.infinity { + MontFelt::ZERO + } else { + MontFelt::ONE + }; + self.zz = z; + self.zzz = z; + + return; + } + self.add_affine_unchecked(other); + } + + /// Add an affine point to this point, neither must be the point of infinity + pub fn add_affine_unchecked(&mut self, other: &AffinePoint) { + // See https://www.hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-madd-2008-s + let p = other.x * self.zz - self.x; + let r = other.y * self.zzz - self.y; + let pp = p.square(); + let ppp = p * pp; + let q = self.x * pp; + self.x = r.square() - ppp - q.double(); + self.y = r * (q - self.x) - self.y * ppp; + self.zz *= pp; + self.zzz *= ppp; + } +} diff --git a/crates/crypto/src/hash/pedersen/gens.rs b/crates/crypto/src/hash/pedersen/gens.rs index 7f2869e1a0..8c4513d9f6 100644 --- a/crates/crypto/src/hash/pedersen/gens.rs +++ b/crates/crypto/src/hash/pedersen/gens.rs @@ -1,34 +1,34 @@ //! Generators for the Pedersen hash function. //! //! See -use crate::algebra::curve::ProjectivePoint; +use crate::algebra::curve::AffinePoint; /// Montgomery representation of the Stark curve constant P0. -pub const PEDERSEN_P0: ProjectivePoint = ProjectivePoint::from_hex( +pub const PEDERSEN_P0: AffinePoint = AffinePoint::from_hex( "49EE3EBA8C1600700EE1B87EB599F16716B0B1022947733551FDE4050CA6804", "3CA0CFE4B3BC6DDF346D49D06EA0ED34E621062C0E056C1D0405D266E10268A", ); /// Montgomery representation of the Stark curve constant P1. -pub const PEDERSEN_P1: ProjectivePoint = ProjectivePoint::from_hex( +pub const PEDERSEN_P1: AffinePoint = AffinePoint::from_hex( "234287DCBAFFE7F969C748655FCA9E58FA8120B6D56EB0C1080D17957EBE47B", "3B056F100F96FB21E889527D41F4E39940135DD7A6C94CC6ED0268EE89E5615", ); /// Montgomery representation of the Stark curve constant P2. -pub const PEDERSEN_P2: ProjectivePoint = ProjectivePoint::from_hex( +pub const PEDERSEN_P2: AffinePoint = AffinePoint::from_hex( "4FA56F376C83DB33F9DAB2656558F3399099EC1DE5E3018B7A6932DBA8AA378", "3FA0984C931C9E38113E0C0E47E4401562761F92A7A23B45168F4E80FF5B54D", ); /// Montgomery representation of the Stark curve constant P3. -pub const PEDERSEN_P3: ProjectivePoint = ProjectivePoint::from_hex( +pub const PEDERSEN_P3: AffinePoint = AffinePoint::from_hex( "4BA4CC166BE8DEC764910F75B45F74B40C690C74709E90F3AA372F0BD2D6997", "40301CF5C1751F4B971E46C4EDE85FCAC5C59A5CE5AE7C48151F27B24B219C", ); /// Montgomery representation of the Stark curve constant P4. -pub const PEDERSEN_P4: ProjectivePoint = ProjectivePoint::from_hex( +pub const PEDERSEN_P4: AffinePoint = AffinePoint::from_hex( "54302DCB0E6CC1C6E44CCA8F61A63BB2CA65048D53FB325D36FF12C49A58202", "1B77B3E37D13504B348046268D8AE25CE98AD783C25561A879DCC77E99C2426", ); diff --git a/crates/crypto/src/hash/pedersen/hash.rs b/crates/crypto/src/hash/pedersen/hash.rs index 13348da54f..1458bb51ba 100644 --- a/crates/crypto/src/hash/pedersen/hash.rs +++ b/crates/crypto/src/hash/pedersen/hash.rs @@ -19,20 +19,22 @@ pub fn pedersen_hash(a: Felt, b: Felt) -> Felt { // Preprocessed material is lookup-tables for each chunk of bits let table_size = (1 << CURVE_CONSTS_BITS) - 1; - let add_points = |acc: &mut ProjectivePoint, bits: &BitSlice, prep: &[AffinePoint]| { + let add_points = |acc: &mut XYZZPoint, bits: &BitSlice, prep: &[AffinePoint]| { bits.chunks(CURVE_CONSTS_BITS) .enumerate() .for_each(|(i, v)| { let offset: usize = v.load_le(); if offset > 0 { // Table lookup at 'offset-1' in table for chunk 'i' - acc.add_affine(&prep[i * table_size + offset - 1]); + // We can add unchecked, since the accumulator and prep. point cannot be infinity. + acc.add_affine_unchecked(&prep[i * table_size + offset - 1]); } }); }; // Compute hash - let mut acc = PEDERSEN_P0; + let mut acc = XYZZPoint::from(&PEDERSEN_P0); + add_points(&mut acc, &a_bits[..248], &CURVE_CONSTS_P1); // Add a_low * P1 add_points(&mut acc, &a_bits[248..252], &CURVE_CONSTS_P2); // Add a_high * P2 add_points(&mut acc, &b_bits[..248], &CURVE_CONSTS_P3); // Add b_low * P3 diff --git a/crates/crypto/src/lib.rs b/crates/crypto/src/lib.rs index 543a4cee54..5a6e71f974 100644 --- a/crates/crypto/src/lib.rs +++ b/crates/crypto/src/lib.rs @@ -11,11 +11,5 @@ pub mod hash; pub mod signature; pub use algebra::{ - AffinePoint, - CurveOrderMontFelt, - Felt, - HexParseError, - MontFelt, - OverflowError, - ProjectivePoint, + AffinePoint, CurveOrderMontFelt, Felt, HexParseError, MontFelt, OverflowError, ProjectivePoint, }; diff --git a/crates/crypto/src/signature/mod.rs b/crates/crypto/src/signature/mod.rs index 0621d619b3..b9963a7968 100644 --- a/crates/crypto/src/signature/mod.rs +++ b/crates/crypto/src/signature/mod.rs @@ -1,10 +1,5 @@ mod ecdsa; pub use ecdsa::{ - ecdsa_sign, - ecdsa_sign_k, - ecdsa_verify, - ecdsa_verify_partial, - get_pk, - SignatureError, + ecdsa_sign, ecdsa_sign_k, ecdsa_verify, ecdsa_verify_partial, get_pk, SignatureError, };