Skip to content

Commit

Permalink
add safety docs
Browse files Browse the repository at this point in the history
  • Loading branch information
jotabulacios committed Feb 17, 2025
1 parent ab5d9b2 commit 2c1faad
Show file tree
Hide file tree
Showing 24 changed files with 269 additions and 10 deletions.
21 changes: 21 additions & 0 deletions math/src/elliptic_curve/edwards/curves/bandersnatch/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,28 @@ impl IsEllipticCurve for BandersnatchCurve {
// Values are from https://github.com/arkworks-rs/curves/blob/5a41d7f27a703a7ea9c48512a4148443ec6c747e/ed_on_bls12_381_bandersnatch/src/curves/mod.rs#L120
// converted to Hex.
// SAFETY: The creation of the generator point is safe since it is a constant that belongs to the curve.
// check which version of the comment is correct.

/// Returns the generator point of the Bandersnatch curve.
///
/// The generator point is defined with coordinates `(x, y, 1)`, where `x` and `y`
/// are precomputed constants that belong to the curve.
///
/// ## Safety
/// - The generator values are taken from the [Arkworks implementation](https://github.com/arkworks-rs/curves/blob/5a41d7f27a703a7ea9c48512a4148443ec6c747e/ed_on_bls12_381_bandersnatch/src/curves/mod.rs#L120)
/// and have been converted to hexadecimal.
/// - `unwrap_unchecked()` is safe because:
/// - The generator point is **known to be valid** on the curve.
/// - The function only uses **hardcoded** and **verified** constants.
/// - This function should **never** be modified unless the new generator is fully verified.
fn generator() -> Self::PointRepresentation {
// SAFETY:
// - The generator point coordinates (x, y) are taken from a well-tested,
// verified implementation.
// - The constructor will only fail if the values are invalid, which is
// impossible given that they are constants taken from a trusted source.
// - `unwrap_unchecked()` avoids unnecessary checks, as we guarantee
// correctness based on external verification.
unsafe {
Self::PointRepresentation::new([
FieldElement::<Self::BaseField>::new_base(
Expand Down
14 changes: 13 additions & 1 deletion math/src/elliptic_curve/edwards/curves/ed448_goldilocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,20 @@ impl IsEllipticCurve for Ed448Goldilocks {
type BaseField = P448GoldilocksPrimeField;
type PointRepresentation = EdwardsProjectivePoint<Self>;

/// Taken from https://www.rfc-editor.org/rfc/rfc7748#page-6
/// Returns the generator point of the Ed448-Goldilocks curve.
///
/// This generator is taken from [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748#page-6).
///
/// ## Safety
/// - The generator coordinates `(x, y, 1)` are well-known, predefined constants.
/// - `unwrap_unchecked()` is used because the values are **known to be valid** points
/// on the Ed448-Goldilocks curve.
/// - This function must **not** be modified unless new constants are mathematically verified.
fn generator() -> Self::PointRepresentation {
// SAFETY:
// - These values are taken from RFC 7748 and are known to be valid.
// - `unwrap_unchecked()` is safe because `new()` will only fail if the point is
// invalid, which is **not possible** with hardcoded, verified values.
unsafe {
Self::PointRepresentation::new([
FieldElement::<Self::BaseField>::from_hex("4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e").unwrap(),
Expand Down
12 changes: 12 additions & 0 deletions math/src/elliptic_curve/edwards/curves/tiny_jub_jub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,19 @@ impl IsEllipticCurve for TinyJubJubEdwards {
type BaseField = U64PrimeField<13>;
type PointRepresentation = EdwardsProjectivePoint<Self>;

/// Returns the generator point of the TinyJubJub Edwards curve.
///
/// This generator is taken from **Moonmath Manual (page 97)**.
///
/// ## Safety
/// - The generator coordinates `(8, 5, 1)` are **predefined** and belong to the TinyJubJub curve.
/// - `unwrap_unchecked()` is used because the generator is a **verified valid point**,
/// meaning there is **no risk** of runtime failure.
/// - This function must **not** be modified unless the new generator is mathematically verified.
fn generator() -> Self::PointRepresentation {
// SAFETY:
// - The generator point `(8, 5, 1)` is **mathematically valid** on the curve.
// - `unwrap_unchecked()` is safe because we **know** the point satisfies the curve equation.
unsafe {
Self::PointRepresentation::new([
FieldElement::from(8),
Expand Down
29 changes: 26 additions & 3 deletions math/src/elliptic_curve/edwards/point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,17 @@ impl<E: IsEdwards> FromAffine<E::BaseField> for EdwardsProjectivePoint<E> {
impl<E: IsEllipticCurve> Eq for EdwardsProjectivePoint<E> {}

impl<E: IsEdwards> IsGroup for EdwardsProjectivePoint<E> {
/// The point at infinity.
/// Returns the point at infinity (neutral element) in projective coordinates.
///
/// ## Safety
/// - The values `[0, 1, 1]` are the **canonical representation** of the neutral element
/// in the Edwards curve, meaning they are guaranteed to be a valid point.
/// - `unwrap_unchecked()` is used because this point is **known** to be valid, so
/// there is no need for additional runtime checks.
fn neutral_element() -> Self {
// SAFETY:
// - `[0, 1, 1]` is a mathematically verified neutral element in Edwards curves.
// - `unwrap_unchecked()` is safe because this point is **always valid**.
unsafe {
Self::new([
FieldElement::zero(),
Expand All @@ -99,8 +108,15 @@ impl<E: IsEdwards> IsGroup for EdwardsProjectivePoint<E> {
px == &FieldElement::zero() && py == pz
}

/// Computes the addition of `self` and `other`.
/// Taken from "Moonmath" (Eq 5.38, page 97)
/// Computes the addition of `self` and `other` using the Edwards curve addition formula.
///
/// This implementation follows Equation (5.38) from "Moonmath" (page 97).
///
/// ## Safety
/// - The function assumes both `self` and `other` are valid points on the curve.
/// - The resulting coordinates are computed using a well-defined formula that
/// maintains the elliptic curve invariants.
/// - `unwrap_unchecked()` is safe because the formula guarantees the result is valid.
fn operate_with(&self, other: &Self) -> Self {
// This avoids dropping, which in turn saves us from having to clone the coordinates.
let (s_affine, o_affine) = (self.to_affine(), other.to_affine());
Expand All @@ -124,8 +140,15 @@ impl<E: IsEdwards> IsGroup for EdwardsProjectivePoint<E> {
}

/// Returns the additive inverse of the projective point `p`
///
/// ## Safety
/// - Negating the x-coordinate of a valid Edwards point results in another valid point.
/// - `unwrap_unchecked()` is safe because negation does not break the curve equation.
fn neg(&self) -> Self {
let [px, py, pz] = self.coordinates();
// SAFETY:
// - The negation formula for Edwards curves is well-defined.
// - The result remains a valid curve point.
unsafe { Self::new([-px, py.clone(), pz.clone()]).unwrap_unchecked() }
}
}
Expand Down
13 changes: 13 additions & 0 deletions math/src/elliptic_curve/montgomery/curves/tiny_jub_jub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,20 @@ impl IsEllipticCurve for TinyJubJubMontgomery {
type BaseField = U64PrimeField<13>;
type PointRepresentation = MontgomeryProjectivePoint<Self>;

/// Returns the generator point of the TinyJubJub Montgomery curve.
///
/// This generator is taken from **Moonmath Manual (page 91)**.
///
/// ## Safety
/// - The generator coordinates `(3, 5, 1)` are **predefined** and are **valid** points
/// on the TinyJubJub Montgomery curve.
/// - `unwrap_unchecked()` is used because the generator is **guaranteed** to satisfy
/// the Montgomery curve equation.
/// - This function must **not** be modified unless the new generator is mathematically verified.
fn generator() -> Self::PointRepresentation {
// SAFETY:
// - The generator point `(3, 5, 1)` is **mathematically verified**.
// - `unwrap_unchecked()` is safe because the input values **guarantee** validity.
unsafe {
Self::PointRepresentation::new([
FieldElement::from(3),
Expand Down
29 changes: 28 additions & 1 deletion math/src/elliptic_curve/montgomery/point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,14 @@ impl<E: IsEllipticCurve> Eq for MontgomeryProjectivePoint<E> {}

impl<E: IsMontgomery> IsGroup for MontgomeryProjectivePoint<E> {
/// The point at infinity.
///
/// ## Safety
/// - The point `(0, 1, 0)` is a well-defined **neutral element** for Montgomery curves.
/// - `unwrap_unchecked()` is used because this point is **always valid**.
fn neutral_element() -> Self {
// SAFETY:
// - `(0, 1, 0)` is **mathematically valid** as the neutral element.
// - `unwrap_unchecked()` is safe because this is **a known valid point**.
unsafe {
Self::new([
FieldElement::zero(),
Expand All @@ -101,7 +108,14 @@ impl<E: IsMontgomery> IsGroup for MontgomeryProjectivePoint<E> {
}

/// Computes the addition of `self` and `other`.
/// Taken from "Moonmath" (Definition 5.2.2.1, page 94)
///
/// This implementation follows the addition law for Montgomery curves as described in:
/// **Moonmath Manual, Definition 5.2.2.1, Page 94**.
///
/// ## Safety
/// - This function assumes that both `self` and `other` are **valid** points on the curve.
/// - The resulting point is **guaranteed** to be valid due to the **Montgomery curve addition formula**.
/// - `unwrap_unchecked()` is used because the formula ensures the result remains a valid curve point.
fn operate_with(&self, other: &Self) -> Self {
// One of them is the neutral element.
if self.is_neutral_element() {
Expand Down Expand Up @@ -132,6 +146,9 @@ impl<E: IsMontgomery> IsGroup for MontgomeryProjectivePoint<E> {
let new_x = &div * &div * &b - (&x1 + x2) - a;
let new_y = div * (x1 - &new_x) - y1;

// SAFETY:
// - The Montgomery addition formula guarantees a **valid** curve point.
// - `unwrap_unchecked()` is safe because the input points are **valid**.
unsafe { Self::new([new_x, new_y, one]).unwrap_unchecked() }
// In the rest of the cases we have x1 != x2
} else {
Expand All @@ -142,14 +159,24 @@ impl<E: IsMontgomery> IsGroup for MontgomeryProjectivePoint<E> {
let new_x = &div * &div * E::b() - (&x1 + &x2) - E::a();
let new_y = div * (x1 - &new_x) - y1;

// SAFETY:
// - The result of the Montgomery addition formula is **guaranteed** to be a valid point.
// - `unwrap_unchecked()` is safe because we **control** the inputs.
unsafe { Self::new([new_x, new_y, FieldElement::one()]).unwrap_unchecked() }
}
}
}

/// Returns the additive inverse of the projective point `p`
///
/// ## Safety
/// - The negation formula preserves the curve equation.
/// - `unwrap_unchecked()` is safe because negation **does not** create invalid points.
fn neg(&self) -> Self {
let [px, py, pz] = self.coordinates();
// SAFETY:
// - Negating `y` maintains the curve structure.
// - `unwrap_unchecked()` is safe because negation **is always valid**.
unsafe { Self::new([px.clone(), -py, pz.clone()]).unwrap_unchecked() }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,18 @@ impl IsEllipticCurve for BLS12377Curve {
type BaseField = BLS12377PrimeField;
type PointRepresentation = ShortWeierstrassProjectivePoint<Self>;

// generator values are taken from https://neuromancer.sk/std/bls/BLS12-377
/// Returns the generator point of the BLS12-377 curve.
///
/// Generator values are taken from [Neuromancer's BLS12-377 page](https://neuromancer.sk/std/bls/BLS12-377).
///
/// ## Safety
/// - The generator point `(x, y, 1)` is predefined and is **known to be a valid point** on the curve.
/// - `unwrap_unchecked()` is used because this point is **mathematically verified**.
/// - Do **not** modify this function unless a new generator has been **mathematically verified**.
fn generator() -> Self::PointRepresentation {
// SAFETY:
// - These values are mathematically verified and known to be valid points on BLS12-377.
// - `unwrap_unchecked()` is safe because we **ensure** the input values satisfy the curve equation.
unsafe {
Self::PointRepresentation::new([
FieldElement::<Self::BaseField>::new_base("8848defe740a67c8fc6225bf87ff5485951e2caa9d41bb188282c8bd37cb5cd5481512ffcd394eeab9b16eb21be9ef"),
Expand Down Expand Up @@ -95,12 +105,23 @@ impl ShortWeierstrassProjectivePoint<BLS12377Curve> {
}

impl ShortWeierstrassProjectivePoint<BLS12377TwistCurve> {
/// 𝜓(P) = 𝜁 ∘ 𝜋ₚ ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(𝔽ₚ₆) −> E(𝔽ₚ₁₂) from the twist to E,, 𝜋ₚ is the p-power frobenius endomorphism
/// Computes 𝜓(P) = 𝜁 ∘ 𝜋ₚ ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(𝔽ₚ₆) −> E(𝔽ₚ₁₂) from the twist to E,, 𝜋ₚ is the p-power frobenius endomorphism
/// and 𝜓 satisifies minmal equation 𝑋² + 𝑡𝑋 + 𝑞 = 𝑂
/// https://eprint.iacr.org/2022/352.pdf 4.2 (7)
/// ψ(P) = (ψ_x * conjugate(x), ψ_y * conjugate(y), conjugate(z))
///
/// ## Safety
/// - This function assumes `self` is a valid point on the BLS12-377 **twist** curve.
/// - The conjugation operation preserves validity.
/// - `unwrap_unchecked()` is used because `psi()` is defined to **always return a valid point**.
fn psi(&self) -> Self {
let [x, y, z] = self.coordinates();
// SAFETY:
// - `conjugate()` preserves the validity of the field element.
// - `ENDO_U` and `ENDO_V` are precomputed constants that ensure the
// resulting point satisfies the curve equation.
// - `unwrap_unchecked()` is safe because the transformation follows
// **a known valid isomorphism** between the twist and E.
unsafe {
Self::new([
x.conjugate() * GAMMA_12,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ fn line(p: &G1Point, t: &G2Point, q: &G2Point) -> (G2Point, Fp12E) {
BLS12377TwistCurve::defining_equation_projective(&x_r, &y_r, &z_r),
Fp2E::zero()
);
// SAFETY: `unwrap_unchecked()` is used here because we ensure that `x_r, y_r, z_r`
// satisfy the curve equation. The previous assertion checks that this is indeed the case.
let r = unsafe { G2Point::new([x_r, y_r, z_r]).unwrap_unchecked() };

let l = Fp12E::new([
Expand Down Expand Up @@ -196,6 +198,8 @@ fn line(p: &G1Point, t: &G2Point, q: &G2Point) -> (G2Point, Fp12E) {
BLS12377TwistCurve::defining_equation_projective(&x_r, &y_r, &z_r),
Fp2E::zero()
);
// SAFETY: The values `x_r, y_r, z_r` are computed correctly to be on the curve.
// The assertion above verifies that the resulting point is valid.
let r = unsafe { G2Point::new([x_r, y_r, z_r]).unwrap_unchecked() };

let l = Fp12E::new([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ impl IsEllipticCurve for BLS12377TwistCurve {
type PointRepresentation = ShortWeierstrassProjectivePoint<Self>;

fn generator() -> Self::PointRepresentation {
// SAFETY:
// - The generator point is mathematically verified to be a valid point on the curve.
// - `unwrap_unchecked()` is safe because the provided coordinates satisfy the curve equation.
unsafe {
Self::PointRepresentation::new([
FieldElement::new([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@ impl IsEllipticCurve for BLS12381Curve {
type BaseField = BLS12381PrimeField;
type PointRepresentation = ShortWeierstrassProjectivePoint<Self>;

/// Returns the generator point of the BLS12-381 curve.
///
/// ## Safety
/// - The generator point is mathematically verified to be a valid point on the curve.
/// - `unwrap_unchecked()` is safe because the provided coordinates satisfy the curve equation.
fn generator() -> Self::PointRepresentation {
// SAFETY:
// - These values are mathematically verified and known to be valid points on BLS12-381.
// - `unwrap_unchecked()` is safe because we **ensure** the input values satisfy the curve equation.
unsafe {
Self::PointRepresentation::new([
FieldElement::<Self::BaseField>::new_base("17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"),
Expand Down Expand Up @@ -91,12 +99,23 @@ impl ShortWeierstrassProjectivePoint<BLS12381Curve> {
}

impl ShortWeierstrassProjectivePoint<BLS12381TwistCurve> {
/// 𝜓(P) = 𝜁 ∘ 𝜋ₚ ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(𝔽ₚ₆) −> E(𝔽ₚ₁₂) from the twist to E,, 𝜋ₚ is the p-power frobenius endomorphism
/// Computes 𝜓(P) 𝜓(P) = 𝜁 ∘ 𝜋ₚ ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(𝔽ₚ₆) −> E(𝔽ₚ₁₂) from the twist to E,, 𝜋ₚ is the p-power frobenius endomorphism
/// and 𝜓 satisifies minmal equation 𝑋² + 𝑡𝑋 + 𝑞 = 𝑂
/// https://eprint.iacr.org/2022/352.pdf 4.2 (7)
///
/// ## Safety
/// - This function assumes `self` is a valid point on the BLS12-381 **twist** curve.
/// - The conjugation operation preserves validity.
/// - `unwrap_unchecked()` is used because `psi()` is defined to **always return a valid point**.
fn psi(&self) -> Self {
let [x, y, z] = self.coordinates();
unsafe {
// SAFETY:
// - `conjugate()` preserves the validity of the field element.
// - `ENDO_U` and `ENDO_V` are precomputed constants that ensure the
// resulting point satisfies the curve equation.
// - `unwrap_unchecked()` is safe because the transformation follows
// **a known valid isomorphism** between the twist and E.
Self::new([
x.conjugate() * ENDO_U,
y.conjugate() * ENDO_V,
Expand Down Expand Up @@ -142,13 +161,22 @@ mod tests {
FieldElement::from_hex_unchecked("0")
]);

// Cmoputes the psi^2() 'Untwist Frobenius Endomorphism'
/// Computes the psi^2() 'Untwist Frobenius Endomorphism'
///
/// # Safety
///
/// - This function assumes `p` is a valid point on the BLS12-381 twist curve.
/// - The transformation involves multiplying the x and y coordinates by known constants.
/// - `unwrap_unchecked()` is used because the resulting point remains valid under the curve equations.
fn psi_square(
p: &ShortWeierstrassProjectivePoint<BLS12381TwistCurve>,
) -> ShortWeierstrassProjectivePoint<BLS12381TwistCurve> {
let [x, y, z] = p.coordinates();
// Since power of frobenius map is 2 we apply once as applying twice is inverse
unsafe {
// SAFETY:
// - `ENDO_U_2` and `ENDO_V_2` are known valid constants.
// - `unwrap_unchecked()` is safe because the transformation preserves curve validity.
ShortWeierstrassProjectivePoint::new([x * ENDO_U_2, y * ENDO_V_2, z.clone()])
.unwrap_unchecked()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ impl IsEllipticCurve for BLS12381TwistCurve {

fn generator() -> Self::PointRepresentation {
unsafe {
// SAFETY:
// - The generator point is mathematically verified to be a valid point on the curve.
// - `unwrap_unchecked()` is safe because the provided coordinates satisfy the curve equation.
Self::PointRepresentation::new([
FieldElement::new([
FieldElement::new(GENERATOR_X_0),
Expand Down
Loading

0 comments on commit 2c1faad

Please sign in to comment.