Skip to content

Commit

Permalink
chore: Refactor sqrt,inv functions and update Cargo.toml for zkvm
Browse files Browse the repository at this point in the history
- Modified the `Cargo.toml` file to restrict `sphinx-zkvm` dependency to a `zkvm` target OS context, mitigating potential circular dependency issues
- Removed `zkvm` from the feature flags in the `Cargo.toml` file
- Introduced a `sqrt_impl` macro in `src/fp2.rs` to refactor the square root calculations in `sqrt` method
- Did the same with a inv_impl for the inverse
  • Loading branch information
huitseeker committed Aug 21, 2024
1 parent 8464a4e commit c5e40f9
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 145 deletions.
3 changes: 0 additions & 3 deletions .vscode/settings.json

This file was deleted.

13 changes: 7 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,12 @@ version = "1.4"
default-features = false
optional = true

[dependencies.sphinx-zkvm]
git = "https://github.com/argumentcomputer/sphinx"
branch = "dev"
optional = true
# The sphinx-zkvm crate should only ever be a dependency in a target_os = zkvm context
# See https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies
# Sphinx imports bls12_381 in a non-zkvm context, and bls12_381 imported sphinx in that context, it
# could create a circular dependency.
[target.'cfg(target_os = "zkvm")'.dependencies]
sphinx-precompiles = { git = "https://github.com/argumentcomputer/sphinx", branch = "dev" }

[features]
default = ["groups", "pairings", "alloc", "bits"]
Expand All @@ -78,5 +80,4 @@ groups = ["group"]
pairings = ["groups", "pairing"]
alloc = ["group/alloc"]
experimental = ["digest"]
nightly = ["subtle/nightly"]
zkvm = ["dep:sphinx-zkvm"]
nightly = ["subtle/nightly"]
97 changes: 55 additions & 42 deletions src/fp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,32 @@ impl<'a, 'b> Mul<&'b Fp> for &'a Fp {
impl_binops_additive!(Fp, Fp);
impl_binops_multiplicative!(Fp, Fp);

macro_rules! sqrt_impl {
($self:expr) => {
$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,
])
};
}

macro_rules! invert_impl {
($self:expr) => {
$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,
])
};
}

impl Fp {
/// Returns zero, the additive identity.
#[inline]
Expand Down Expand Up @@ -346,32 +372,26 @@ impl Fp {
// we only need to exponentiate by (p+1)/4. This only
// works for elements that are actually quadratic residue,
// so we check that we got the correct result at the end.

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,
]);
sphinx_precompiles::unconstrained! {
let mut buf = [0u8; 48];
let sqrt = sqrt_impl!(self);
buf[0..48].copy_from_slice(&sqrt.to_bytes());
sphinx_precompiles::io::hint_slice(&buf);
}

let byte_vec = sphinx_precompiles::io::read_vec();
let bytes: [u8; 48] = byte_vec.try_into().unwrap();
let root = Fp::from_bytes(&bytes[0..48].try_into().unwrap()).unwrap();
CtOption::new(root, !self.is_zero() & (root * root).ct_eq(self))
} else {
let sqrt: Self = sqrt_impl!(self);
// Only return the result if it's really the square root (and so
// self is actually quadratic nonresidue)
CtOption::new(sqrt, sqrt.square().ct_eq(self))
}
}

CtOption::new(sqrt, sqrt.square().ct_eq(self))
}

#[inline]
Expand All @@ -380,30 +400,23 @@ impl Fp {
/// is zero.
pub fn invert(&self) -> CtOption<Self> {
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()))
// Compute the inverse using the zkvm syscall
sphinx_precompiles::unconstrained! {
let mut buf = [0u8; 48];
let inv = invert_impl!(self);
buf[0..48].copy_from_slice(&inv.to_bytes());
sphinx_precompiles::io::hint_slice(&buf);
}

let byte_vec = sphinx_precompiles::io::read_vec();
let bytes: [u8; 48] = byte_vec.try_into().unwrap();
let inv = Fp::from_bytes(&bytes[0..48].try_into().unwrap()).unwrap();
CtOption::new(inv, !self.is_zero() & (self * inv).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,
]);
let t: Self = invert_impl!(self);

CtOption::new(t, !self.is_zero())
}
Expand Down
168 changes: 74 additions & 94 deletions src/fp2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,54 @@ impl<'a, 'b> Mul<&'b Fp2> for &'a Fp2 {
impl_binops_additive!(Fp2, Fp2);
impl_binops_multiplicative!(Fp2, Fp2);

macro_rules! sqrt_impl {
($self:expr) => {{
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),
)
})
})
}};
}

impl Fp2 {
#[inline]
pub const fn zero() -> Fp2 {
Expand Down Expand Up @@ -399,105 +447,37 @@ impl Fp2 {
pub fn sqrt(&self) -> CtOption<Self> {
// Algorithm 9, https://eprint.iacr.org/2012/685.pdf
// with constant time modifications.

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),
)
})
})
);
sphinx_precompiles::unconstrained! {
let mut buf = [0u8; 97]; // Allocate 97 bytes to include the flag
let sqrt_opt = sqrt_impl!(self);
sqrt_opt.map(|root| {
buf[0..48].copy_from_slice(&root.c0.to_bytes());
buf[48..96].copy_from_slice(&root.c1.to_bytes());
buf[97] = 1; // Set the flag to 1 indicating the result is valid
});
sphinx_precompiles::io::hint_slice(&buf);
}

let byte_vec = sphinx_precompiles::io::read_vec();
let bytes: [u8; 97] = byte_vec.try_into().unwrap();
match bytes[96] {
0 => CtOption::new(Fp2::zero(), Choice::from(0u8)), // Return None if the flag is 0
_ => {
let c0 = Fp::from_bytes(&bytes[0..48].try_into().unwrap()).unwrap();
let c1 = Fp::from_bytes(&bytes[48..96].try_into().unwrap()).unwrap();
let root = Fp2 { c0, c1 };
CtOption::new(root, !self.is_zero() & (root * root).ct_eq(self))
}
}
} 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(
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),
)
})
});
let sqrt_option: CtOption<Self> = sqrt_impl!(self);
// 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)))
}
}

// 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
Expand Down

0 comments on commit c5e40f9

Please sign in to comment.