Skip to content

Commit

Permalink
patch improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
rkm0959 committed Jan 2, 2025
1 parent 27abb77 commit 10da64a
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 86 deletions.
2 changes: 0 additions & 2 deletions src/fp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,7 @@ impl Fp {
// that (2^384 - 1)*c is an acceptable product for the reduction. Therefore, the
// reduction always works so long as `c` is in the field; in this case it is either the
// constant `R2` or `R3`.
limbs[6] %= MODULUS[5];
let d1 = Fp([limbs[11], limbs[10], limbs[9], limbs[8], limbs[7], limbs[6]]);
limbs[0] %= MODULUS[5];
let d0 = Fp([limbs[5], limbs[4], limbs[3], limbs[2], limbs[1], limbs[0]]);
// Convert to Montgomery form
d0 * R2 + d1 * R3
Expand Down
1 change: 1 addition & 0 deletions src/fp12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ impl Fp12 {
}

#[inline]
// This function panics when the input is non-canonical, unlike `Fp`'s `from_bytes`.
pub fn from_bytes(bytes: &[u8; 576]) -> CtOption<Fp12> {
let c0 = Fp6::from_bytes(&bytes[..288].try_into().unwrap());
let c1 = Fp6::from_bytes(&bytes[288..].try_into().unwrap());
Expand Down
1 change: 1 addition & 0 deletions src/fp2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ impl Fp2 {
}

#[inline]
// This function panics when the input is non-canonical, unlike `Fp`'s `from_bytes`.
pub fn from_bytes(bytes: &[u8; 96]) -> CtOption<Fp2> {
let c0 = Fp::from_bytes(&bytes[..48].try_into().unwrap());
let c1 = Fp::from_bytes(&bytes[48..].try_into().unwrap());
Expand Down
13 changes: 7 additions & 6 deletions src/fp6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,31 +349,31 @@ impl Fp6 {

Fp6 {
c0: Fp2 {
c0: Fp::sum_of_products(
c0: Fp::sum_of_products_cpu(
[a.c0.c0, -a.c0.c1, a.c1.c0, -a.c1.c1, a.c2.c0, -a.c2.c1],
[b.c0.c0, b.c0.c1, b20_m_b21, b20_p_b21, b10_m_b11, b10_p_b11],
),
c1: Fp::sum_of_products(
c1: Fp::sum_of_products_cpu(
[a.c0.c0, a.c0.c1, a.c1.c0, a.c1.c1, a.c2.c0, a.c2.c1],
[b.c0.c1, b.c0.c0, b20_p_b21, b20_m_b21, b10_p_b11, b10_m_b11],
),
},
c1: Fp2 {
c0: Fp::sum_of_products(
c0: Fp::sum_of_products_cpu(
[a.c0.c0, -a.c0.c1, a.c1.c0, -a.c1.c1, a.c2.c0, -a.c2.c1],
[b.c1.c0, b.c1.c1, b.c0.c0, b.c0.c1, b20_m_b21, b20_p_b21],
),
c1: Fp::sum_of_products(
c1: Fp::sum_of_products_cpu(
[a.c0.c0, a.c0.c1, a.c1.c0, a.c1.c1, a.c2.c0, a.c2.c1],
[b.c1.c1, b.c1.c0, b.c0.c1, b.c0.c0, b20_p_b21, b20_m_b21],
),
},
c2: Fp2 {
c0: Fp::sum_of_products(
c0: Fp::sum_of_products_cpu(
[a.c0.c0, -a.c0.c1, a.c1.c0, -a.c1.c1, a.c2.c0, -a.c2.c1],
[b.c2.c0, b.c2.c1, b.c1.c0, b.c1.c1, b.c0.c0, b.c0.c1],
),
c1: Fp::sum_of_products(
c1: Fp::sum_of_products_cpu(
[a.c0.c0, a.c0.c1, a.c1.c0, a.c1.c1, a.c2.c0, a.c2.c1],
[b.c2.c1, b.c2.c0, b.c1.c1, b.c1.c0, b.c0.c1, b.c0.c0],
),
Expand Down Expand Up @@ -431,6 +431,7 @@ impl Fp6 {
}

#[inline]
// This function panics when the input is non-canonical, unlike `Fp`'s `from_bytes`.
pub fn from_bytes(bytes: &[u8; 288]) -> CtOption<Fp6> {
let c0 = Fp2::from_bytes(&bytes[..96].try_into().unwrap());
let c1 = Fp2::from_bytes(&bytes[96..192].try_into().unwrap());
Expand Down
120 changes: 52 additions & 68 deletions src/g1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,86 +335,69 @@ impl G1Affine {
/// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization)
/// for details about how group elements are serialized.
pub fn from_compressed(bytes: &[u8; 48]) -> CtOption<Self> {
cfg_if::cfg_if! {
if #[cfg(target_os = "zkvm")] {
// by the y-coordinate recovery procedure in decompress_pubkey().
let decompressed = decompress_pubkey(bytes).unwrap();
// We already know the point is on the curve because this is established
// by the y-coordinate recovery procedure in from_compressed_unchecked().

// Extra checks do not have to be done because because the precompile already does it for us.
G1Affine::from_uncompressed_unchecked(&decompressed).and_then(|p| CtOption::new(p, p.is_torsion_free()))
} else {
Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free()))
}
}
Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free()))
}

/// Attempts to deserialize an uncompressed element, not checking if the
/// element is in the correct subgroup.
/// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
/// API invariants may be broken.** Please consider using `from_compressed()` instead.
pub fn from_compressed_unchecked(bytes: &[u8; 48]) -> CtOption<Self> {
cfg_if::cfg_if! {
if #[cfg(target_os = "zkvm")] {
// by the y-coordinate recovery procedure in decompress_pubkey().
let decompressed = decompress_pubkey(bytes).unwrap();
// Obtain the three flags from the start of the byte sequence
let compression_flag_set = Choice::from((bytes[0] >> 7) & 1);
let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1);
let sort_flag_set = Choice::from((bytes[0] >> 5) & 1);

// Attempt to obtain the x-coordinate
let x = {
let mut tmp = [0; 48];
tmp.copy_from_slice(&bytes[0..48]);

// Mask away the flag bits
tmp[0] &= 0b0001_1111;

Fp::from_bytes(&tmp)
};

x.and_then(|x| {
// If the infinity flag is set, return the value assuming
// the x-coordinate is zero and the sort bit is not set.
//
// Otherwise, return a recovered point (assuming the correct
// y-coordinate can be found) so long as the infinity flag
// was not set.
CtOption::new(
G1Affine::identity(),
infinity_flag_set & // Infinity flag should be set
compression_flag_set & // Compression flag should be set
(!sort_flag_set) & // Sort flag should not be set
x.is_zero(), // The x-coordinate should be zero
)
.or_else(|| {
// Recover a y-coordinate given x by y = sqrt(x^3 + 4)
((x.square() * x) + B).sqrt().and_then(|y| {
// Switch to the correct y-coordinate if necessary.
let y = Fp::conditional_select(
&y,
&-y,
y.lexicographically_largest() ^ sort_flag_set,
);

// Extra checks do not have to be done because because the precompile already does it for us.
G1Affine::from_uncompressed_unchecked(&decompressed)
} else {
// Obtain the three flags from the start of the byte sequence
let compression_flag_set = Choice::from((bytes[0] >> 7) & 1);
let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1);
let sort_flag_set = Choice::from((bytes[0] >> 5) & 1);

// Attempt to obtain the x-coordinate
let x = {
let mut tmp = [0; 48];
tmp.copy_from_slice(&bytes[0..48]);

// Mask away the flag bits
tmp[0] &= 0b0001_1111;

Fp::from_bytes(&tmp)
};

x.and_then(|x| {
// If the infinity flag is set, return the value assuming
// the x-coordinate is zero and the sort bit is not set.
//
// Otherwise, return a recovered point (assuming the correct
// y-coordinate can be found) so long as the infinity flag
// was not set.
CtOption::new(
G1Affine::identity(),
infinity_flag_set & // Infinity flag should be set
compression_flag_set & // Compression flag should be set
(!sort_flag_set) & // Sort flag should not be set
x.is_zero(), // The x-coordinate should be zero
G1Affine {
x,
y,
infinity: infinity_flag_set,
},
(!infinity_flag_set) & // Infinity flag should not be set
compression_flag_set, // Compression flag should be set
)
.or_else(|| {
// Recover a y-coordinate given x by y = sqrt(x^3 + 4)
((x.square() * x) + B).sqrt().and_then(|y| {
// Switch to the correct y-coordinate if necessary.
let y = Fp::conditional_select(
&y,
&-y,
y.lexicographically_largest() ^ sort_flag_set,
);

CtOption::new(
G1Affine {
x,
y,
infinity: infinity_flag_set,
},
(!infinity_flag_set) & // Infinity flag should not be set
compression_flag_set, // Compression flag should be set
)
})
})
})
}
}
})
})
}

/// Returns true if this element is the identity (the point at infinity).
Expand Down Expand Up @@ -485,6 +468,7 @@ impl G1Affine {
res
} else {
// In this case, we know that P == -Q, since `x` is equal but `y` is different, so we can just return the identity
assert!(self.y + rhs.y == Fp::zero());
Self::identity()
}
} else {
Expand Down
18 changes: 8 additions & 10 deletions src/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,23 +540,21 @@ impl Scalar {
pub fn invert(&self) -> CtOption<Self> {
#[cfg(target_os = "zkvm")]
{
if self.is_zero().into() {
return CtOption::new(Self::zero(), Choice::from(0u8));
}
unconstrained! {
let mut buf = [0u8; 33];
let mut buf = [0u8; 32];
self.cpu_invert().map(|inv| {
buf[0..32].copy_from_slice(&inv.to_bytes());
buf[32] = 1;
});
hint_slice(&buf);
}
let byte_vec = read_vec();
let bytes: [u8; 33] = byte_vec.try_into().unwrap();
match bytes[32] {
0 => CtOption::new(Scalar::zero(), Choice::from(0u8)),
_ => {
let inv = Scalar::from_bytes(&bytes[0..32].try_into().unwrap()).unwrap();
CtOption::new(inv, (self * inv).ct_eq(&Scalar::one()))
}
}
let bytes: [u8; 32] = byte_vec.try_into().unwrap();
let inv = Scalar::from_bytes(&bytes).unwrap();
assert!(self * &inv == Scalar::one(), "Invalid hint: Scalar invert");
return CtOption::new(inv, Choice::from(1u8));
}
#[cfg(not(target_os = "zkvm"))]
{
Expand Down

0 comments on commit 10da64a

Please sign in to comment.