diff --git a/Cargo.toml b/Cargo.toml index 38ff7033..264c6ba3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,14 +58,19 @@ version = "0.6" default-features = false [dependencies.subtle] -version = "2.2.1" +version = "2.6.1" default-features = false [dependencies.zeroize] -version = "1.4" +version = "1.8" default-features = false optional = true +[dependencies.sphinx-zkvm] +git = "https://github.com/argumentcomputer/sphinx" +branch = "dev" +optional = true + [features] default = ["groups", "pairings", "alloc", "bits"] bits = ["ff/bits"] @@ -74,3 +79,4 @@ pairings = ["groups", "pairing"] alloc = ["group/alloc"] experimental = ["digest"] nightly = ["subtle/nightly"] +zkvm = ["dep:sphinx-zkvm"] diff --git a/src/fp.rs b/src/fp.rs index 0dbcedb9..8f7780c4 100644 --- a/src/fp.rs +++ b/src/fp.rs @@ -347,14 +347,29 @@ impl Fp { // works for elements that are actually quadratic residue, // so we check that we got the correct result at the end. - let sqrt = self.pow_vartime(&[ - 0xee7f_bfff_ffff_eaab, - 0x07aa_ffff_ac54_ffff, - 0xd9cc_34a8_3dac_3d89, - 0xd91d_d2e1_3ce1_44af, - 0x92c6_e9ed_90d2_eb35, - 0x0680_447a_8e5f_f9a6, - ]); + cfg_if::cfg_if! { + // NOTE: the branches are meant to be identical except for the use of the unconstrained! macro + if #[cfg(target_os = "zkvm")] { + let sqrt = sphinx_zkvm::precompiles::unconstrained!(self.pow_vartime(&[ + 0xee7f_bfff_ffff_eaab, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6, + ])); + } + else { + let sqrt = self.pow_vartime(&[ + 0xee7f_bfff_ffff_eaab, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6, + ]); + } + } CtOption::new(sqrt, sqrt.square().ct_eq(self)) } @@ -364,17 +379,35 @@ impl Fp { /// element, returning None in the case that this element /// is zero. pub fn invert(&self) -> CtOption { - // Exponentiate by p - 2 - let t = self.pow_vartime(&[ - 0xb9fe_ffff_ffff_aaa9, - 0x1eab_fffe_b153_ffff, - 0x6730_d2a0_f6b0_f624, - 0x6477_4b84_f385_12bf, - 0x4b1b_a7b6_434b_acd7, - 0x1a01_11ea_397f_e69a, - ]); - - CtOption::new(t, !self.is_zero()) + cfg_if::cfg_if! { + // NOTE: the branches are meant to be identical except for the use of the unconstrained! macro + if #[cfg(target_os = "zkvm")] { + // Exponentiate by p - 2 + let t = sphinx_zkvm::precompiles::unconstrained!(self.pow_vartime(&[ + 0xb9fe_ffff_ffff_aaa9, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a, + ])); + + CtOption::new(t, !self.is_zero() && (self.mul(&t)).ct_eq(&Fp::one())) + } + else { + // Exponentiate by p - 2 + let t = self.pow_vartime(&[ + 0xb9fe_ffff_ffff_aaa9, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a, + ]); + + CtOption::new(t, !self.is_zero()) + } + } } #[inline] diff --git a/src/fp2.rs b/src/fp2.rs index 5d085b09..fc7838fd 100644 --- a/src/fp2.rs +++ b/src/fp2.rs @@ -400,52 +400,104 @@ impl Fp2 { // Algorithm 9, https://eprint.iacr.org/2012/685.pdf // with constant time modifications. - CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| { - // a1 = self^((p - 3) / 4) - let a1 = self.pow_vartime(&[ - 0xee7f_bfff_ffff_eaaa, - 0x07aa_ffff_ac54_ffff, - 0xd9cc_34a8_3dac_3d89, - 0xd91d_d2e1_3ce1_44af, - 0x92c6_e9ed_90d2_eb35, - 0x0680_447a_8e5f_f9a6, - ]); - - // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) - let alpha = a1.square() * self; - - // x0 = self^((p + 1) / 4) - let x0 = a1 * self; - - // In the event that alpha = -1, the element is order p - 1 and so - // we're just trying to get the square of an element of the subfield - // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element - // x0 = a + bu has b = 0, the solution is therefore au. - CtOption::new( - Fp2 { - c0: -x0.c1, - c1: x0.c0, - }, - alpha.ct_eq(&(&Fp2::one()).neg()), - ) - // Otherwise, the correct solution is (1 + alpha)^((q - 1) // 2) * x0 - .or_else(|| { + cfg_if::cfg_if! { + // NOTE: the branches are meant to be identical except for the use of the unconstrained! macro + if #[cfg(target_os = "zkvm")] { + let sqrt_option = unconstrained!( + CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| { + // a1 = self^((p - 3) / 4) + let a1 = self.pow_vartime(&[ + 0xee7f_bfff_ffff_eaaa, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6, + ]); + + // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) + let alpha = a1.square() * self; + + // x0 = self^((p + 1) / 4) + let x0 = a1 * self; + + // In the event that alpha = -1, the element is order p - 1 and so + // we're just trying to get the square of an element of the subfield + // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element + // x0 = a + bu has b = 0, the solution is therefore au. + CtOption::new( + Fp2 { + c0: -x0.c1, + c1: x0.c0, + }, + alpha.ct_eq(&(&Fp2::one()).neg()), + ) + // Otherwise, the correct solution is (1 + alpha)^((q - 1) // 2) * x0 + .or_else(|| { + CtOption::new( + (alpha + Fp2::one()).pow_vartime(&[ + 0xdcff_7fff_ffff_d555, + 0x0f55_ffff_58a9_ffff, + 0xb398_6950_7b58_7b12, + 0xb23b_a5c2_79c2_895f, + 0x258d_d3db_21a5_d66b, + 0x0d00_88f5_1cbf_f34d, + ]) * x0, + Choice::from(1), + ) + }) + }) + ); + } else { + let sqrt_option = CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| { + // a1 = self^((p - 3) / 4) + let a1 = self.pow_vartime(&[ + 0xee7f_bfff_ffff_eaaa, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6, + ]); + + // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) + let alpha = a1.square() * self; + + // x0 = self^((p + 1) / 4) + let x0 = a1 * self; + + // In the event that alpha = -1, the element is order p - 1 and so + // we're just trying to get the square of an element of the subfield + // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element + // x0 = a + bu has b = 0, the solution is therefore au. CtOption::new( - (alpha + Fp2::one()).pow_vartime(&[ - 0xdcff_7fff_ffff_d555, - 0x0f55_ffff_58a9_ffff, - 0xb398_6950_7b58_7b12, - 0xb23b_a5c2_79c2_895f, - 0x258d_d3db_21a5_d66b, - 0x0d00_88f5_1cbf_f34d, - ]) * x0, - Choice::from(1), + Fp2 { + c0: -x0.c1, + c1: x0.c0, + }, + alpha.ct_eq(&(&Fp2::one()).neg()), ) - }) - // Only return the result if it's really the square root (and so - // self is actually quadratic nonresidue) - .and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self))) - }) + // Otherwise, the correct solution is (1 + alpha)^((q - 1) // 2) * x0 + .or_else(|| { + CtOption::new( + (alpha + Fp2::one()).pow_vartime(&[ + 0xdcff_7fff_ffff_d555, + 0x0f55_ffff_58a9_ffff, + 0xb398_6950_7b58_7b12, + 0xb23b_a5c2_79c2_895f, + 0x258d_d3db_21a5_d66b, + 0x0d00_88f5_1cbf_f34d, + ]) * x0, + Choice::from(1), + ) + }) + }); + } + } + + // Only return the result if it's really the square root (and so + // self is actually quadratic nonresidue) + sqrt_option.and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self))) } /// Computes the multiplicative inverse of this field