lambdaworks_math/elliptic_curve/montgomery/
point.rs1use crate::{
2 cyclic_group::IsGroup,
3 elliptic_curve::{
4 point::ProjectivePoint,
5 traits::{EllipticCurveError, FromAffine, IsEllipticCurve},
6 },
7 field::element::FieldElement,
8};
9
10use super::traits::IsMontgomery;
11
12#[derive(Clone, Debug)]
13pub struct MontgomeryProjectivePoint<E: IsEllipticCurve>(ProjectivePoint<E>);
14
15impl<E: IsEllipticCurve + IsMontgomery> MontgomeryProjectivePoint<E> {
16 pub fn new(value: [FieldElement<E::BaseField>; 3]) -> Result<Self, EllipticCurveError> {
18 let (x, y, z) = (&value[0], &value[1], &value[2]);
19
20 if z != &FieldElement::<E::BaseField>::zero()
21 && E::defining_equation_projective(x, y, z) == FieldElement::<E::BaseField>::zero()
22 {
23 Ok(Self(ProjectivePoint::new(value)))
24 } else if x == &FieldElement::<E::BaseField>::zero()
27 && z == &FieldElement::<E::BaseField>::zero()
28 {
29 Ok(Self(ProjectivePoint::new([
30 FieldElement::<E::BaseField>::zero(),
31 FieldElement::<E::BaseField>::one(),
32 FieldElement::<E::BaseField>::zero(),
33 ])))
34 } else {
35 Err(EllipticCurveError::InvalidPoint)
36 }
37 }
38
39 pub fn x(&self) -> &FieldElement<E::BaseField> {
41 self.0.x()
42 }
43
44 pub fn y(&self) -> &FieldElement<E::BaseField> {
46 self.0.y()
47 }
48
49 pub fn z(&self) -> &FieldElement<E::BaseField> {
51 self.0.z()
52 }
53
54 pub fn coordinates(&self) -> &[FieldElement<E::BaseField>; 3] {
56 self.0.coordinates()
57 }
58
59 pub fn to_affine(&self) -> Self {
63 Self(self.0.to_affine())
64 }
65}
66
67impl<E: IsEllipticCurve> PartialEq for MontgomeryProjectivePoint<E> {
68 fn eq(&self, other: &Self) -> bool {
69 self.0 == other.0
70 }
71}
72
73impl<E: IsMontgomery> FromAffine<E::BaseField> for MontgomeryProjectivePoint<E> {
74 fn from_affine(
75 x: FieldElement<E::BaseField>,
76 y: FieldElement<E::BaseField>,
77 ) -> Result<Self, EllipticCurveError> {
78 let coordinates = [x, y, FieldElement::one()];
79 MontgomeryProjectivePoint::new(coordinates)
80 }
81}
82
83impl<E: IsEllipticCurve> Eq for MontgomeryProjectivePoint<E> {}
84
85impl<E: IsMontgomery> IsGroup for MontgomeryProjectivePoint<E> {
86 fn neutral_element() -> Self {
93 let point = Self::new([
97 FieldElement::zero(),
98 FieldElement::one(),
99 FieldElement::zero(),
100 ]);
101 debug_assert!(point.is_ok());
102 point.unwrap()
103 }
104
105 fn is_neutral_element(&self) -> bool {
106 let pz = self.z();
107 pz == &FieldElement::zero()
108 }
109
110 fn operate_with(&self, other: &Self) -> Self {
121 if self.is_neutral_element() {
123 other.clone()
124 } else if other.is_neutral_element() {
125 self.clone()
126 } else {
127 let [x1, y1, _] = self.to_affine().coordinates().clone();
128 let [x2, y2, _] = other.to_affine().coordinates().clone();
129 if x2 == x1 && &y2 + &y1 == FieldElement::zero() {
131 Self::neutral_element()
132 } else if self == other {
134 let one = FieldElement::from(1);
139 let (a, b) = (E::a(), E::b());
140
141 let x1a = &a * &x1;
142 let x1_square = &x1 * &x1;
143 let num = &x1_square + &x1_square + x1_square + &x1a + x1a + &one;
144 let den = (&b + &b) * &y1;
145
146 let div = unsafe { (num / den).unwrap_unchecked() };
150
151 let new_x = &div * &div * &b - (&x1 + x2) - a;
152 let new_y = div * (x1 - &new_x) - y1;
153
154 let point = Self::new([new_x, new_y, one]);
158 point.unwrap()
159 } else {
161 let num = &y2 - &y1;
162 let den = &x2 - &x1;
163
164 let div = unsafe { (num / den).unwrap_unchecked() };
165
166 let new_x = &div * &div * E::b() - (&x1 + &x2) - E::a();
167 let new_y = div * (x1 - &new_x) - y1;
168
169 let point = Self::new([new_x, new_y, FieldElement::one()]);
173 point.unwrap()
174 }
175 }
176 }
177
178 fn neg(&self) -> Self {
185 let [px, py, pz] = self.coordinates();
186 let point = Self::new([px.clone(), -py, pz.clone()]);
190 debug_assert!(point.is_ok());
191 point.unwrap()
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use crate::{
198 cyclic_group::IsGroup,
199 elliptic_curve::{
200 montgomery::{
201 curves::tiny_jub_jub::TinyJubJubMontgomery, point::MontgomeryProjectivePoint,
202 },
203 traits::{EllipticCurveError, IsEllipticCurve},
204 },
205 field::element::FieldElement,
206 };
207
208 fn create_point(x: u64, y: u64) -> MontgomeryProjectivePoint<TinyJubJubMontgomery> {
209 TinyJubJubMontgomery::create_point_from_affine(FieldElement::from(x), FieldElement::from(y))
210 .unwrap()
211 }
212
213 #[test]
214 fn create_valid_point_works() {
215 let p = TinyJubJubMontgomery::create_point_from_affine(
216 FieldElement::from(9),
217 FieldElement::from(2),
218 )
219 .unwrap();
220 assert_eq!(p.x(), &FieldElement::from(9));
221 assert_eq!(p.y(), &FieldElement::from(2));
222 assert_eq!(p.z(), &FieldElement::from(1));
223 }
224
225 #[test]
226 fn create_invalid_point_returns_invalid_point_error() {
227 let result = TinyJubJubMontgomery::create_point_from_affine(
228 FieldElement::from(5),
229 FieldElement::from(4),
230 );
231 assert_eq!(result.unwrap_err(), EllipticCurveError::InvalidPoint);
232 }
233
234 #[test]
235 fn operate_with_works_for_points_in_tiny_jub_jub() {
236 let p = MontgomeryProjectivePoint::<TinyJubJubMontgomery>::new([
237 FieldElement::from(9),
238 FieldElement::from(2),
239 FieldElement::from(1),
240 ])
241 .unwrap();
242 let q = MontgomeryProjectivePoint::<TinyJubJubMontgomery>::new([
243 FieldElement::from(7),
244 FieldElement::from(12),
245 FieldElement::from(1),
246 ])
247 .unwrap();
248 let expected = MontgomeryProjectivePoint::<TinyJubJubMontgomery>::new([
249 FieldElement::from(10),
250 FieldElement::from(3),
251 FieldElement::from(1),
252 ])
253 .unwrap();
254 assert_eq!(p.operate_with(&q), expected);
255 }
256
257 #[test]
258 fn test_negation_in_montgomery() {
259 let a = create_point(9, 2);
260 let b = create_point(9, 13 - 2);
261
262 assert_eq!(a.neg(), b);
263 assert!(a.operate_with(&b).is_neutral_element());
264 }
265
266 #[test]
267 fn operate_with_works_and_cycles_in_tiny_jub_jub() {
268 let g = create_point(9, 2);
269 assert_eq!(
270 g.operate_with_self(0_u16),
271 MontgomeryProjectivePoint::neutral_element()
272 );
273 assert_eq!(g.operate_with_self(1_u16), create_point(9, 2));
274 assert_eq!(g.operate_with_self(2_u16), create_point(7, 12));
275 assert_eq!(g.operate_with_self(3_u16), create_point(10, 3));
276 assert_eq!(g.operate_with_self(4_u16), create_point(8, 12));
277 assert_eq!(g.operate_with_self(5_u16), create_point(1, 9));
278 assert_eq!(g.operate_with_self(6_u16), create_point(5, 1));
279 assert_eq!(g.operate_with_self(7_u16), create_point(4, 9));
280 assert_eq!(g.operate_with_self(8_u16), create_point(2, 9));
281 assert_eq!(g.operate_with_self(9_u16), create_point(3, 5));
282 assert_eq!(g.operate_with_self(10_u16), create_point(0, 0));
283 assert_eq!(g.operate_with_self(11_u16), create_point(3, 8));
284 assert_eq!(g.operate_with_self(12_u16), create_point(2, 4));
285 assert_eq!(g.operate_with_self(13_u16), create_point(4, 4));
286 assert_eq!(g.operate_with_self(14_u16), create_point(5, 12));
287 assert_eq!(g.operate_with_self(15_u16), create_point(1, 4));
288 assert_eq!(g.operate_with_self(16_u16), create_point(8, 1));
289 assert_eq!(g.operate_with_self(17_u16), create_point(10, 10));
290 assert_eq!(g.operate_with_self(18_u16), create_point(7, 1));
291 assert_eq!(g.operate_with_self(19_u16), create_point(9, 11));
292 assert_eq!(
293 g.operate_with_self(20_u16),
294 MontgomeryProjectivePoint::neutral_element()
295 );
296 }
297}