lambdaworks_math/elliptic_curve/
point.rs

1use crate::elliptic_curve::traits::IsEllipticCurve;
2use crate::field::element::FieldElement;
3use core::fmt::Debug;
4/// Represents an elliptic curve point using the projective short Weierstrass form:
5/// y^2 * z = x^3 + a * x * z^2 + b * z^3,
6/// where `x`, `y` and `z` variables are field elements.
7#[derive(Debug, Clone)]
8pub struct ProjectivePoint<E: IsEllipticCurve> {
9    pub value: [FieldElement<E::BaseField>; 3],
10}
11
12impl<E: IsEllipticCurve> ProjectivePoint<E> {
13    /// Creates an elliptic curve point giving the projective [x: y: z] coordinates.
14    pub const fn new(value: [FieldElement<E::BaseField>; 3]) -> Self {
15        Self { value }
16    }
17
18    /// Returns the `x` coordinate of the point.
19    pub fn x(&self) -> &FieldElement<E::BaseField> {
20        &self.value[0]
21    }
22
23    /// Returns the `y` coordinate of the point.
24    pub fn y(&self) -> &FieldElement<E::BaseField> {
25        &self.value[1]
26    }
27
28    /// Returns the `z` coordinate of the point.
29    pub fn z(&self) -> &FieldElement<E::BaseField> {
30        &self.value[2]
31    }
32
33    /// Returns a tuple [x, y, z] with the coordinates of the point.
34    pub fn coordinates(&self) -> &[FieldElement<E::BaseField>; 3] {
35        &self.value
36    }
37
38    /// Creates the same point in affine coordinates. That is,
39    /// returns [x / z: y / z: 1] where `self` is [x: y: z].
40    /// Panics if `self` is the point at infinity.
41    pub fn to_affine(&self) -> Self {
42        let [x, y, z] = self.coordinates();
43        // If it's the point at infinite
44        if z == &FieldElement::zero() {
45            // We make sure all the points at infinite have the same values
46            return Self::new([
47                FieldElement::zero(),
48                FieldElement::one(),
49                FieldElement::zero(),
50            ]);
51        };
52        let inv_z = z.inv().unwrap();
53        ProjectivePoint::new([x * &inv_z, y * inv_z, FieldElement::one()])
54    }
55}
56
57impl<E: IsEllipticCurve> PartialEq for ProjectivePoint<E> {
58    fn eq(&self, other: &Self) -> bool {
59        let [px, py, pz] = self.coordinates();
60        let [qx, qy, qz] = other.coordinates();
61        (px * qz == pz * qx) && (py * qz == qy * pz)
62    }
63}
64
65impl<E: IsEllipticCurve> Eq for ProjectivePoint<E> {}
66#[derive(Debug, Clone)]
67
68pub struct JacobianPoint<E: IsEllipticCurve> {
69    pub value: [FieldElement<E::BaseField>; 3],
70}
71
72impl<E: IsEllipticCurve> JacobianPoint<E> {
73    /// Creates an elliptic curve point giving the Jacobian [x: y: z] coordinates.
74    pub const fn new(value: [FieldElement<E::BaseField>; 3]) -> Self {
75        Self { value }
76    }
77
78    /// Returns the `x` coordinate of the point.
79    pub fn x(&self) -> &FieldElement<E::BaseField> {
80        &self.value[0]
81    }
82
83    /// Returns the `y` coordinate of the point.
84    pub fn y(&self) -> &FieldElement<E::BaseField> {
85        &self.value[1]
86    }
87
88    /// Returns the `z` coordinate of the point.
89    pub fn z(&self) -> &FieldElement<E::BaseField> {
90        &self.value[2]
91    }
92
93    /// Returns a tuple [x, y, z] with the coordinates of the point.
94    pub fn coordinates(&self) -> &[FieldElement<E::BaseField>; 3] {
95        &self.value
96    }
97
98    pub fn to_affine(&self) -> Self {
99        let [x, y, z] = self.coordinates();
100        // If it's the point at infinite
101        if z == &FieldElement::zero() {
102            // We make sure all the points at infinite have the same values
103            return Self::new([
104                FieldElement::one(),
105                FieldElement::one(),
106                FieldElement::zero(),
107            ]);
108        };
109        let inv_z = z.inv().unwrap();
110        let inv_z_square = inv_z.square();
111        let inv_z_cube = &inv_z_square * &inv_z;
112        JacobianPoint::new([x * inv_z_square, y * inv_z_cube, FieldElement::one()])
113    }
114}
115
116impl<E: IsEllipticCurve> PartialEq for JacobianPoint<E> {
117    fn eq(&self, other: &Self) -> bool {
118        // In Jacobian coordinates, the equality of two points is defined as:
119        // X1 * Z2^2 == X2 * Z1^2 y Y1 * Z2^3 == Y2 * Z1^3
120
121        let [px, py, pz] = self.coordinates();
122        let [qx, qy, qz] = other.coordinates();
123
124        let zp_sq = pz.square();
125        let zq_sq = qz.square();
126
127        let zp_cu = &zp_sq * pz;
128        let zq_cu = &zq_sq * qz;
129
130        let xp_zq_sq = px * zq_sq;
131        let xq_zp_sq = qx * zp_sq;
132
133        let yp_zq_cu = py * zq_cu;
134        let yq_zp_cu = qy * zp_cu;
135
136        (xp_zq_sq == xq_zp_sq) && (yp_zq_cu == yq_zp_cu)
137    }
138}
139impl<E: IsEllipticCurve> Eq for JacobianPoint<E> {}
140#[cfg(test)]
141mod tests {
142    use crate::cyclic_group::IsGroup;
143    use crate::elliptic_curve::short_weierstrass::curves::test_curve_1::{
144        TestCurve1, TestCurvePrimeField, TestCurveQuadraticNonResidue,
145        TEST_CURVE_1_MAIN_SUBGROUP_ORDER,
146    };
147    use crate::elliptic_curve::short_weierstrass::curves::test_curve_2::TestCurve2;
148    use crate::field::element::FieldElement;
149    use crate::unsigned_integer::element::U384;
150    //use crate::elliptic_curve::curves::test_curve_2::TestCurve2;
151    use crate::elliptic_curve::traits::{EllipticCurveError, IsEllipticCurve};
152    use crate::field::extensions::quadratic::QuadraticExtensionFieldElement;
153
154    #[allow(clippy::upper_case_acronyms)]
155    type FEE = QuadraticExtensionFieldElement<TestCurvePrimeField, TestCurveQuadraticNonResidue>;
156
157    // This tests only apply for the specific curve found in the configuration file.
158    #[test]
159    fn create_valid_point_works() {
160        let point = TestCurve1::create_point_from_affine(FEE::from(35), FEE::from(31)).unwrap();
161        assert_eq!(*point.x(), FEE::from(35));
162        assert_eq!(*point.y(), FEE::from(31));
163        assert_eq!(*point.z(), FEE::from(1));
164    }
165
166    #[test]
167    fn create_invalid_points_panics() {
168        let a = TestCurve1::create_point_from_affine(FEE::from(0), FEE::from(1));
169        assert_eq!(EllipticCurveError::InvalidPoint, a.unwrap_err());
170    }
171
172    #[test]
173    fn equality_works() {
174        let g = TestCurve1::generator();
175        let g2 = g.operate_with(&g);
176        assert_ne!(&g2, &g);
177        assert_eq!(&g, &g);
178    }
179
180    #[test]
181    fn operate_with_self_works_1() {
182        let g = TestCurve1::generator();
183        assert_eq!(
184            g.operate_with(&g).operate_with(&g),
185            g.operate_with_self(3_u16)
186        );
187    }
188
189    #[test]
190    fn operate_with_self_works_2() {
191        let mut point_1 = TestCurve1::generator();
192        point_1 = point_1.operate_with_self(TEST_CURVE_1_MAIN_SUBGROUP_ORDER as u128);
193        assert!(point_1.is_neutral_element());
194    }
195
196    #[test]
197    fn doubling_a_point_works() {
198        let point = TestCurve1::create_point_from_affine(FEE::from(35), FEE::from(31)).unwrap();
199        let expected_result =
200            TestCurve1::create_point_from_affine(FEE::from(25), FEE::from(29)).unwrap();
201        assert_eq!(point.operate_with_self(2_u16).to_affine(), expected_result);
202    }
203
204    #[test]
205    fn operate_with_self_works_with_test_curve_2() {
206        let mut point_1 = TestCurve2::generator();
207        point_1 = point_1.operate_with_self(15_u16);
208
209        let expected_result = TestCurve2::create_point_from_affine(
210            FieldElement::new([
211                FieldElement::new(U384::from_hex_unchecked(
212                    "7b8ee59e422e702458174c18eb3302e17",
213                )),
214                FieldElement::new(U384::from_hex_unchecked(
215                    "1395065adef5a6a5457f1ea600b5a3e4fb",
216                )),
217            ]),
218            FieldElement::new([
219                FieldElement::new(U384::from_hex_unchecked(
220                    "e29d5b15c42124cd8f05d3c8500451c33",
221                )),
222                FieldElement::new(U384::from_hex_unchecked(
223                    "e836ef62db0a47a63304b67c0de69b140",
224                )),
225            ]),
226        )
227        .unwrap();
228
229        assert_eq!(point_1, expected_result);
230    }
231
232    #[test]
233    fn coordinate_getters_work() {
234        let x = FEE::from(35);
235        let y = FEE::from(31);
236        let z = FEE::from(1);
237        let point = TestCurve1::create_point_from_affine(x.clone(), y.clone()).unwrap();
238        let coordinates = point.coordinates();
239        assert_eq!(&x, point.x());
240        assert_eq!(&y, point.y());
241        assert_eq!(&z, point.z());
242        assert_eq!(x, coordinates[0]);
243        assert_eq!(y, coordinates[1]);
244        assert_eq!(z, coordinates[2]);
245    }
246}