Skip to content

Commit 2c45c24

Browse files
committed
perf: switch SW_EC to projective coordinates - guest-libs (INT-6136)
Update k256, p256, and pairing guest libraries to use projective coordinates: - Add z coordinate to all point generators (z=1) - Normalize before extracting affine coordinates (x_be_bytes, y_be_bytes, AffineCoordinates, ToEncodedPoint, to_affine) - Use cross-multiplication for projective ConstantTimeEq - Select all 3 coordinates in ConditionallySelectable - Remove DefaultIsZeroes (identity is (0,1,0), not all zeros) - Switch G2 pairing modules from impl_sw_affine! to impl_sw_proj! - Add .normalize() in tests before comparing to known affine values
1 parent e133071 commit 2c45c24

File tree

10 files changed

+76
-48
lines changed

10 files changed

+76
-48
lines changed

guest-libs/k256/src/internal.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use core::ops::{Add, Neg};
1+
use core::ops::Add;
22

33
use hex_literal::hex;
44
use openvm_algebra_guest::IntMod;
@@ -41,6 +41,7 @@ impl CyclicGroup for Secp256k1Point {
4141
y: Secp256k1Coord::from_const_bytes(hex!(
4242
"B8D410FB8FD0479C195485A648B417FDA808110EFCFBA45D65C4A32677DA3A48"
4343
)),
44+
z: Secp256k1Coord::from_const_u8(1),
4445
};
4546
const NEG_GENERATOR: Self = Secp256k1Point {
4647
x: Secp256k1Coord::from_const_bytes(hex!(
@@ -49,6 +50,7 @@ impl CyclicGroup for Secp256k1Point {
4950
y: Secp256k1Coord::from_const_bytes(hex!(
5051
"7727EF046F2FB863E6AB7A59B74BE80257F7EEF103045BA29A3B5CD98825C5B7"
5152
)),
53+
z: Secp256k1Coord::from_const_u8(1),
5254
};
5355
}
5456

@@ -74,10 +76,12 @@ impl IntrinsicCurve for Secp256k1 {
7476

7577
impl Secp256k1Point {
7678
pub fn x_be_bytes(&self) -> [u8; 32] {
77-
<Self as WeierstrassPoint>::x(self).to_be_bytes()
79+
let n = self.normalize();
80+
<Self as WeierstrassPoint>::x(&n).to_be_bytes()
7881
}
7982

8083
pub fn y_be_bytes(&self) -> [u8; 32] {
81-
<Self as WeierstrassPoint>::y(self).to_be_bytes()
84+
let n = self.normalize();
85+
<Self as WeierstrassPoint>::y(&n).to_be_bytes()
8286
}
8387
}

guest-libs/k256/src/point.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use elliptic_curve::{
1010
rand_core::RngCore,
1111
sec1::{FromEncodedPoint, ToEncodedPoint},
1212
subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption},
13-
zeroize::DefaultIsZeroes,
1413
FieldBytesEncoding,
1514
};
1615
use openvm_algebra_guest::IntMod;
@@ -42,11 +41,13 @@ impl AffineCoordinates for Secp256k1Point {
4241
type FieldRepr = FieldBytes;
4342

4443
fn x(&self) -> FieldBytes {
45-
*FieldBytes::from_slice(&<Self as WeierstrassPoint>::x(self).to_be_bytes())
44+
let n = self.normalize();
45+
*FieldBytes::from_slice(&<Self as WeierstrassPoint>::x(&n).to_be_bytes())
4646
}
4747

4848
fn y_is_odd(&self) -> Choice {
49-
(self.y().as_le_bytes()[0] & 1).into()
49+
let n = self.normalize();
50+
(n.y().as_le_bytes()[0] & 1).into()
5051
}
5152
}
5253

@@ -58,21 +59,26 @@ impl ConditionallySelectable for Secp256k1Point {
5859
b: &Secp256k1Point,
5960
choice: Choice,
6061
) -> Secp256k1Point {
61-
Secp256k1Point::from_xy_unchecked(
62+
Secp256k1Point::from_xyz_unchecked(
6263
Secp256k1Coord::conditional_select(
6364
<Self as WeierstrassPoint>::x(a),
6465
<Self as WeierstrassPoint>::x(b),
6566
choice,
6667
),
6768
Secp256k1Coord::conditional_select(a.y(), b.y(), choice),
69+
Secp256k1Coord::conditional_select(a.z(), b.z(), choice),
6870
)
6971
}
7072
}
7173

7274
impl ConstantTimeEq for Secp256k1Point {
7375
fn ct_eq(&self, other: &Secp256k1Point) -> Choice {
74-
<Self as WeierstrassPoint>::x(self).ct_eq(<Self as WeierstrassPoint>::x(other))
75-
& self.y().ct_eq(other.y())
76+
// Projective equivalence: (X1*Z2 == X2*Z1) && (Y1*Z2 == Y2*Z1)
77+
let x1z2 = <Self as WeierstrassPoint>::x(self) * other.z();
78+
let x2z1 = <Self as WeierstrassPoint>::x(other) * self.z();
79+
let y1z2 = self.y() * other.z();
80+
let y2z1 = other.y() * self.z();
81+
x1z2.ct_eq(&x2z1) & y1z2.ct_eq(&y2z1)
7682
}
7783
}
7884

@@ -82,8 +88,6 @@ impl Default for Secp256k1Point {
8288
}
8389
}
8490

85-
impl DefaultIsZeroes for Secp256k1Point {}
86-
8791
impl Sum for Secp256k1Point {
8892
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
8993
iter.fold(<Self as WeierstrassPoint>::IDENTITY, |a, b| a + b)
@@ -161,7 +165,7 @@ impl elliptic_curve::group::Curve for Secp256k1Point {
161165
type AffineRepr = Secp256k1Point;
162166

163167
fn to_affine(&self) -> Secp256k1Point {
164-
*self
168+
self.normalize()
165169
}
166170
}
167171

@@ -216,10 +220,11 @@ impl FromEncodedPoint<Secp256k1> for Secp256k1Point {
216220

217221
impl ToEncodedPoint<Secp256k1> for Secp256k1Point {
218222
fn to_encoded_point(&self, compress: bool) -> EncodedPoint {
223+
let n = self.normalize();
219224
EncodedPoint::conditional_select(
220225
&EncodedPoint::from_affine_coordinates(
221-
&<Self as WeierstrassPoint>::x(self).to_be_bytes().into(),
222-
&<Self as WeierstrassPoint>::y(self).to_be_bytes().into(),
226+
&<Self as WeierstrassPoint>::x(&n).to_be_bytes().into(),
227+
&<Self as WeierstrassPoint>::y(&n).to_be_bytes().into(),
223228
compress,
224229
),
225230
&EncodedPoint::identity(),

guest-libs/k256/tests/lib.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -303,23 +303,25 @@ mod host_tests {
303303

304304
// Generic add can handle equal or unequal points.
305305
#[allow(clippy::op_ref)]
306-
let p3 = &p1 + &p2;
306+
let p3 = (&p1 + &p2).normalize();
307307
if p3.x() != &x3 || p3.y() != &y3 {
308308
panic!();
309309
}
310310
#[allow(clippy::op_ref)]
311-
let p4 = &p2 + &p2;
311+
let p4 = (&p2 + &p2).normalize();
312312
if p4.x() != &x4 || p4.y() != &y4 {
313313
panic!();
314314
}
315315

316316
// Add assign and double assign
317317
p1 += &p2;
318-
if p1.x() != &x3 || p1.y() != &y3 {
318+
let p1n = p1.normalize();
319+
if p1n.x() != &x3 || p1n.y() != &y3 {
319320
panic!();
320321
}
321322
p2.double_assign();
322-
if p2.x() != &x4 || p2.y() != &y4 {
323+
let p2n = p2.normalize();
324+
if p2n.x() != &x4 || p2n.y() != &y4 {
323325
panic!();
324326
}
325327

@@ -333,7 +335,7 @@ mod host_tests {
333335
let y5 = Secp256k1Coord::from_le_bytes_unchecked(&hex!(
334336
"9E272F746DA7BED171E522610212B6AEEAAFDB2AD9F4B530B8E1B27293B19B2C"
335337
));
336-
let result = msm(&[scalar], &[p1]);
338+
let result = msm(&[scalar], &[p1]).normalize();
337339
if result.x() != &x5 || result.y() != &y5 {
338340
panic!();
339341
}

guest-libs/p256/src/internal.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use core::ops::{Add, Neg};
1+
use core::ops::Add;
22

33
use hex_literal::hex;
44
use openvm_algebra_guest::IntMod;
@@ -42,6 +42,7 @@ impl CyclicGroup for P256Point {
4242
y: P256Coord::from_const_bytes(hex!(
4343
"f551bf376840b6cbce5e316b5733ce2b169e0f7c4aebe78e9b7f1afee242e34f"
4444
)),
45+
z: P256Coord::from_const_u8(1),
4546
};
4647
const NEG_GENERATOR: Self = P256Point {
4748
x: P256Coord::from_const_bytes(hex!(
@@ -50,6 +51,7 @@ impl CyclicGroup for P256Point {
5051
y: P256Coord::from_const_bytes(hex!(
5152
"0aae40c897bf493431a1ce94a9cc31d4e961f083b51418716580e5011cbd1cb0"
5253
)),
54+
z: P256Coord::from_const_u8(1),
5355
};
5456
}
5557

@@ -74,10 +76,12 @@ impl IntrinsicCurve for NistP256 {
7476

7577
impl P256Point {
7678
pub fn x_be_bytes(&self) -> [u8; 32] {
77-
<Self as WeierstrassPoint>::x(self).to_be_bytes()
79+
let n = self.normalize();
80+
<Self as WeierstrassPoint>::x(&n).to_be_bytes()
7881
}
7982

8083
pub fn y_be_bytes(&self) -> [u8; 32] {
81-
<Self as WeierstrassPoint>::y(self).to_be_bytes()
84+
let n = self.normalize();
85+
<Self as WeierstrassPoint>::y(&n).to_be_bytes()
8286
}
8387
}

guest-libs/p256/src/point.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use elliptic_curve::{
1010
rand_core::RngCore,
1111
sec1::{FromEncodedPoint, ToEncodedPoint},
1212
subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption},
13-
zeroize::DefaultIsZeroes,
1413
FieldBytesEncoding,
1514
};
1615
use openvm_algebra_guest::IntMod;
@@ -42,33 +41,40 @@ impl AffineCoordinates for P256Point {
4241
type FieldRepr = FieldBytes;
4342

4443
fn x(&self) -> FieldBytes {
45-
*FieldBytes::from_slice(&<Self as WeierstrassPoint>::x(self).to_be_bytes())
44+
let n = self.normalize();
45+
*FieldBytes::from_slice(&<Self as WeierstrassPoint>::x(&n).to_be_bytes())
4646
}
4747

4848
fn y_is_odd(&self) -> Choice {
49-
(self.y().as_le_bytes()[0] & 1).into()
49+
let n = self.normalize();
50+
(n.y().as_le_bytes()[0] & 1).into()
5051
}
5152
}
5253

5354
impl Copy for P256Point {}
5455

5556
impl ConditionallySelectable for P256Point {
5657
fn conditional_select(a: &P256Point, b: &P256Point, choice: Choice) -> P256Point {
57-
P256Point::from_xy_unchecked(
58+
P256Point::from_xyz_unchecked(
5859
P256Coord::conditional_select(
5960
<Self as WeierstrassPoint>::x(a),
6061
<Self as WeierstrassPoint>::x(b),
6162
choice,
6263
),
6364
P256Coord::conditional_select(a.y(), b.y(), choice),
65+
P256Coord::conditional_select(a.z(), b.z(), choice),
6466
)
6567
}
6668
}
6769

6870
impl ConstantTimeEq for P256Point {
6971
fn ct_eq(&self, other: &P256Point) -> Choice {
70-
<Self as WeierstrassPoint>::x(self).ct_eq(<Self as WeierstrassPoint>::x(other))
71-
& self.y().ct_eq(other.y())
72+
// Projective equivalence: (X1*Z2 == X2*Z1) && (Y1*Z2 == Y2*Z1)
73+
let x1z2 = <Self as WeierstrassPoint>::x(self) * other.z();
74+
let x2z1 = <Self as WeierstrassPoint>::x(other) * self.z();
75+
let y1z2 = self.y() * other.z();
76+
let y2z1 = other.y() * self.z();
77+
x1z2.ct_eq(&x2z1) & y1z2.ct_eq(&y2z1)
7278
}
7379
}
7480

@@ -78,8 +84,6 @@ impl Default for P256Point {
7884
}
7985
}
8086

81-
impl DefaultIsZeroes for P256Point {}
82-
8387
impl Sum for P256Point {
8488
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
8589
iter.fold(<Self as WeierstrassPoint>::IDENTITY, |a, b| a + b)
@@ -157,7 +161,7 @@ impl elliptic_curve::group::Curve for P256Point {
157161
type AffineRepr = P256Point;
158162

159163
fn to_affine(&self) -> P256Point {
160-
*self
164+
self.normalize()
161165
}
162166
}
163167

@@ -211,10 +215,11 @@ impl FromEncodedPoint<NistP256> for P256Point {
211215

212216
impl ToEncodedPoint<NistP256> for P256Point {
213217
fn to_encoded_point(&self, compress: bool) -> EncodedPoint {
218+
let n = self.normalize();
214219
EncodedPoint::conditional_select(
215220
&EncodedPoint::from_affine_coordinates(
216-
&<Self as WeierstrassPoint>::x(self).to_be_bytes().into(),
217-
&<Self as WeierstrassPoint>::y(self).to_be_bytes().into(),
221+
&<Self as WeierstrassPoint>::x(&n).to_be_bytes().into(),
222+
&<Self as WeierstrassPoint>::y(&n).to_be_bytes().into(),
218223
compress,
219224
),
220225
&EncodedPoint::identity(),

guest-libs/p256/tests/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,18 +287,20 @@ mod host_tests {
287287

288288
// Generic add can handle equal or unequal points.
289289
#[allow(clippy::op_ref)]
290-
let p3 = &p1 + &p2;
290+
let p3 = (&p1 + &p2).normalize();
291291
#[allow(clippy::op_ref)]
292-
let p4 = &p2 + &p2;
292+
let p4 = (&p2 + &p2).normalize();
293293

294294
// Add assign and double assign
295295
let mut sum = P256Point::from_xy(x1, y1).unwrap();
296296
sum += &p2;
297+
let sum = sum.normalize();
297298
if sum.x() != p3.x() || sum.y() != p3.y() {
298299
panic!();
299300
}
300301
let mut double = P256Point::from_xy(x2, y2).unwrap();
301302
double.double_assign();
303+
let double = double.normalize();
302304
if double.x() != p4.x() || double.y() != p4.y() {
303305
panic!();
304306
}
@@ -307,8 +309,8 @@ mod host_tests {
307309
let p1 = P256Point::from_xy(x1, y1).unwrap();
308310
let scalar = P256Scalar::from_u32(3);
309311
#[allow(clippy::op_ref)]
310-
let p2 = &p1.double() + &p1;
311-
let result = msm(&[scalar], &[p1]);
312+
let p2 = (&p1.double() + &p1).normalize();
313+
let result = msm(&[scalar], &[p1]).normalize();
312314
if result.x() != p2.x() || result.y() != p2.y() {
313315
panic!();
314316
}

guest-libs/pairing/src/bls12_381/mod.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
extern crate alloc;
22

3-
use core::ops::Neg;
4-
53
use openvm_algebra_guest::IntMod;
64
use openvm_algebra_moduli_macros::moduli_declare;
75
use openvm_ecc_guest::weierstrass::{CyclicGroup, Group, IntrinsicCurve};
@@ -53,6 +51,7 @@ impl CyclicGroup for G1Affine {
5351
y: Bls12_381Fp::from_const_bytes(hex!(
5452
"E1E7C5462923AA0CE48A88A244C73CD0EDB3042CCB18DB00F60AD0D595E0F5FCE48A1D74ED309EA0F1A0AAE381F4B308"
5553
)),
54+
z: Bls12_381Fp::from_const_u8(1),
5655
};
5756
const NEG_GENERATOR: Self = G1Affine {
5857
x: Bls12_381Fp::from_const_bytes(hex!(
@@ -61,6 +60,7 @@ impl CyclicGroup for G1Affine {
6160
y: Bls12_381Fp::from_const_bytes(hex!(
6261
"CAC239B9D6DC54AD1B75CB0EBA386F4E3642ACCAD5B95566C907B51DEF6A8167F2212ECFC8767DAAA845D555681D4D11"
6362
)),
63+
z: Bls12_381Fp::from_const_u8(1),
6464
};
6565
}
6666

@@ -80,14 +80,13 @@ impl IntrinsicCurve for Bls12_381 {
8080
mod g2 {
8181
use openvm_algebra_guest::Field;
8282
use openvm_ecc_guest::weierstrass::{
83-
impl_sw_affine, impl_sw_group_ops, weierstrass::WeierstrassPoint, AffinePoint, Group,
83+
impl_sw_group_ops, impl_sw_proj, weierstrass::WeierstrassPoint, Group,
8484
};
8585

8686
use super::{Fp, Fp2};
8787

88-
const THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::ZERO);
8988
const B: Fp2 = Fp2::new(Fp::from_const_u8(4), Fp::from_const_u8(4));
90-
impl_sw_affine!(G2Affine, Fp2, THREE, B);
89+
impl_sw_proj!(G2Affine, Fp2, B);
9190
impl_sw_group_ops!(G2Affine, Fp2);
9291
}
9392

guest-libs/pairing/src/bls12_381/tests.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,10 @@ fn test_bls12381_g2_affine() {
256256
(expected_neg, r_neg),
257257
(expected_double, r_double),
258258
] {
259-
assert_eq!(convert_g2_affine_halo2_to_openvm(expected), actual);
259+
assert_eq!(
260+
convert_g2_affine_halo2_to_openvm(expected),
261+
actual.normalize()
262+
);
260263
}
261264
}
262265
}

0 commit comments

Comments
 (0)