Skip to content

Commit 76c079a

Browse files
[fix] Eliminate potential divide by zero by using fallback implementation (#24)
Closes INT-3385 --------- Co-authored-by: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com>
1 parent 46d4932 commit 76c079a

File tree

3 files changed

+102
-34
lines changed

3 files changed

+102
-34
lines changed

extensions/pairing/guest/src/bls12_381/pairing.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,19 @@ impl PairingCheck for Bls12_381 {
340340
P: &[AffinePoint<Self::Fp>],
341341
Q: &[AffinePoint<Self::Fp2>],
342342
) -> Result<(), PairingCheckError> {
343+
Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| {
344+
let f = Self::multi_miller_loop(P, Q);
345+
exp_check_fallback(&f, &Self::FINAL_EXPONENT)
346+
})
347+
}
348+
}
349+
350+
#[allow(non_snake_case)]
351+
impl Bls12_381 {
352+
fn try_honest_pairing_check(
353+
P: &[AffinePoint<<Self as PairingCheck>::Fp>],
354+
Q: &[AffinePoint<<Self as PairingCheck>::Fp2>],
355+
) -> Option<Result<(), PairingCheckError>> {
343356
let (c, s) = Self::pairing_check_hint(P, Q);
344357

345358
// f * s = c^{q - x}
@@ -352,18 +365,21 @@ impl PairingCheck for Bls12_381 {
352365
// y = -x to get c^-y and finally compute c'^-y as input to the miller loop:
353366
// f * c'^-y * c^-q * s = 1
354367
let c_q = FieldExtension::frobenius_map(&c, 1);
355-
let c_conj_inv = Fp12::ONE.div_unsafe(&c.conjugate());
368+
let c_conj = c.conjugate();
369+
if c_conj == Fp12::ZERO {
370+
return None;
371+
}
372+
let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj);
356373

357374
// fc = f_{Miller,x,Q}(P) * c^{x}
358375
// where
359376
// fc = conjugate( f_{Miller,-x,Q}(P) * c'^{-x} ), with c' denoting the conjugate of c
360377
let fc = Self::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv));
361378

362379
if fc * s == c_q {
363-
Ok(())
380+
Some(Ok(()))
364381
} else {
365-
let f = Self::multi_miller_loop(P, Q);
366-
exp_check_fallback(&f, &Self::FINAL_EXPONENT)
382+
None
367383
}
368384
}
369385
}

extensions/pairing/guest/src/bn254/pairing.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,23 @@ impl PairingCheck for Bn254 {
372372
P: &[AffinePoint<Self::Fp>],
373373
Q: &[AffinePoint<Self::Fp2>],
374374
) -> Result<(), PairingCheckError> {
375+
Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| {
376+
let f = Self::multi_miller_loop(P, Q);
377+
exp_check_fallback(&f, &Self::FINAL_EXPONENT)
378+
})
379+
}
380+
}
381+
382+
#[allow(non_snake_case)]
383+
impl Bn254 {
384+
fn try_honest_pairing_check(
385+
P: &[AffinePoint<<Self as PairingCheck>::Fp>],
386+
Q: &[AffinePoint<<Self as PairingCheck>::Fp2>],
387+
) -> Option<Result<(), PairingCheckError>> {
375388
let (c, u) = Self::pairing_check_hint(P, Q);
389+
if c == Fp12::ZERO {
390+
return None;
391+
}
376392
let c_inv = Fp12::ONE.div_unsafe(&c);
377393

378394
// f * u == c^λ
@@ -389,10 +405,9 @@ impl PairingCheck for Bn254 {
389405
let fc = Self::multi_miller_loop_embedded_exp(P, Q, Some(c_inv));
390406

391407
if fc * c_mul * u == Fp12::ONE {
392-
Ok(())
408+
Some(Ok(()))
393409
} else {
394-
let f = Self::multi_miller_loop(P, Q);
395-
exp_check_fallback(&f, &Self::FINAL_EXPONENT)
410+
None
396411
}
397412
}
398413
}

extensions/pairing/tests/programs/examples/pairing_check_fallback.rs

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -48,35 +48,55 @@ mod bn254 {
4848
_Q: &[AffinePoint<Self::Fp2>],
4949
) -> (Self::Fp12, Self::Fp12) {
5050
// return dummy values
51-
(Fp12::ONE, Fp12::ZERO)
51+
(Fp12::ZERO, Fp12::ZERO)
5252
}
5353

54+
// copied from Bn254::pairing_check
5455
fn pairing_check(
5556
P: &[AffinePoint<Self::Fp>],
5657
Q: &[AffinePoint<Self::Fp2>],
5758
) -> Result<(), PairingCheckError> {
58-
let (c, u) = Self::pairing_check_hint(P, Q);
59-
// TODO: handle c = 0
60-
let c_inv = Fp12::ONE.div_unsafe(&c);
61-
62-
// f * u == c^λ
63-
// f * u == c^{6x + 2 + q^3 - q^2 + q}
64-
// f * c^-{6x + 2} * u * c^-{q^3 - q^2 + q} == 1
65-
// where fc == f * c^-{6x + 2}
66-
// c_mul = c^-{q^3 - q^2 + q}
67-
let c_q3_inv = FieldExtension::frobenius_map(&c_inv, 3);
68-
let c_q2 = FieldExtension::frobenius_map(&c, 2);
69-
let c_q_inv = FieldExtension::frobenius_map(&c_inv, 1);
70-
let c_mul = c_q3_inv * c_q2 * c_q_inv;
71-
72-
// Compute miller loop with c_inv
73-
let fc = Bn254::multi_miller_loop_embedded_exp(P, Q, Some(c_inv));
74-
75-
if fc * c_mul * u == Fp12::ONE {
76-
Ok(())
77-
} else {
59+
Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| {
7860
let f = Bn254::multi_miller_loop(P, Q);
7961
exp_check_fallback(&f, &Bn254::FINAL_EXPONENT)
62+
})
63+
}
64+
}
65+
66+
#[allow(non_snake_case)]
67+
impl Bn254Wrapper {
68+
// copied from Bn254::try_honest_pairing_check
69+
fn try_honest_pairing_check(
70+
P: &[AffinePoint<<Self as PairingCheck>::Fp>],
71+
Q: &[AffinePoint<<Self as PairingCheck>::Fp2>],
72+
) -> Option<Result<(), PairingCheckError>> {
73+
let (c, s) = Self::pairing_check_hint(P, Q);
74+
75+
// f * s = c^{q - x}
76+
// f * s = c^q * c^-x
77+
// f * c^x * c^-q * s = 1,
78+
// where fc = f * c'^x (embedded Miller loop with c conjugate inverse),
79+
// and the curve seed x = -0xd201000000010000
80+
// the miller loop computation includes a conjugation at the end because the value of the
81+
// seed is negative, so we need to conjugate the miller loop input c as c'. We then substitute
82+
// y = -x to get c^-y and finally compute c'^-y as input to the miller loop:
83+
// f * c'^-y * c^-q * s = 1
84+
let c_q = FieldExtension::frobenius_map(&c, 1);
85+
let c_conj = c.conjugate();
86+
if c_conj == Fp12::ZERO {
87+
return None;
88+
}
89+
let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj);
90+
91+
// fc = f_{Miller,x,Q}(P) * c^{x}
92+
// where
93+
// fc = conjugate( f_{Miller,-x,Q}(P) * c'^{-x} ), with c' denoting the conjugate of c
94+
let fc = Bn254::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv));
95+
96+
if fc * s == c_q {
97+
Some(Ok(()))
98+
} else {
99+
None
80100
}
81101
}
82102
}
@@ -141,13 +161,28 @@ mod bls12_381 {
141161
_Q: &[AffinePoint<Self::Fp2>],
142162
) -> (Self::Fp12, Self::Fp12) {
143163
// return dummy values
144-
(Fp12::ONE, Fp12::ZERO)
164+
(Fp12::ZERO, Fp12::ZERO)
145165
}
146166

167+
// copied from Bls12_381::pairing_check
147168
fn pairing_check(
148169
P: &[AffinePoint<Self::Fp>],
149170
Q: &[AffinePoint<Self::Fp2>],
150171
) -> Result<(), PairingCheckError> {
172+
Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| {
173+
let f = Bls12_381::multi_miller_loop(P, Q);
174+
exp_check_fallback(&f, &Bls12_381::FINAL_EXPONENT)
175+
})
176+
}
177+
}
178+
179+
#[allow(non_snake_case)]
180+
impl Bls12_381Wrapper {
181+
// copied from Bls12_381::try_honest_pairing_check
182+
fn try_honest_pairing_check(
183+
P: &[AffinePoint<<Self as PairingCheck>::Fp>],
184+
Q: &[AffinePoint<<Self as PairingCheck>::Fp2>],
185+
) -> Option<Result<(), PairingCheckError>> {
151186
let (c, s) = Self::pairing_check_hint(P, Q);
152187

153188
// f * s = c^{q - x}
@@ -160,19 +195,21 @@ mod bls12_381 {
160195
// y = -x to get c^-y and finally compute c'^-y as input to the miller loop:
161196
// f * c'^-y * c^-q * s = 1
162197
let c_q = FieldExtension::frobenius_map(&c, 1);
163-
// TODO: handle c = 0
164-
let c_conj_inv = Fp12::ONE.div_unsafe(&c.conjugate());
198+
let c_conj = c.conjugate();
199+
if c_conj == Fp12::ZERO {
200+
return None;
201+
}
202+
let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj);
165203

166204
// fc = f_{Miller,x,Q}(P) * c^{x}
167205
// where
168206
// fc = conjugate( f_{Miller,-x,Q}(P) * c'^{-x} ), with c' denoting the conjugate of c
169207
let fc = Bls12_381::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv));
170208

171209
if fc * s == c_q {
172-
Ok(())
210+
Some(Ok(()))
173211
} else {
174-
let f = Bls12_381::multi_miller_loop(P, Q);
175-
exp_check_fallback(&f, &Bls12_381::FINAL_EXPONENT)
212+
None
176213
}
177214
}
178215
}

0 commit comments

Comments
 (0)