ec/
point_jacobi_quartic.rs1use core::fmt;
19use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
20
21use crate::curve_jacobi_quartic::JacobiQuarticCurve;
22use crate::point_ops::{PointAdd, PointOps};
23use fp::field_ops::FieldOps;
24
25#[derive(Debug, Clone, Copy)]
27pub struct JacobiQuarticPoint<F: FieldOps> {
28 pub x: F,
30 pub y: F,
32}
33
34impl<F> fmt::Display for JacobiQuarticPoint<F>
35where
36 F: FieldOps + fmt::Display,
37{
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 if self.is_identity() {
40 if f.alternate() {
41 write!(f, "JacobiQuarticPoint {{ O = (0,1) }}")
42 } else {
43 write!(f, "O")
44 }
45 } else if f.alternate() {
46 write!(f, "JacobiQuarticPoint {{ x = {}, y = {} }}", self.x, self.y)
47 } else {
48 write!(f, "({}, {})", self.x, self.y)
49 }
50 }
51}
52
53impl<F: FieldOps> PartialEq for JacobiQuarticPoint<F> {
54 fn eq(&self, other: &Self) -> bool {
55 self.x == other.x && self.y == other.y
56 }
57}
58
59impl<F: FieldOps> Eq for JacobiQuarticPoint<F> {}
60
61impl<F: FieldOps> JacobiQuarticPoint<F> {
62 pub fn new(x: F, y: F) -> Self {
64 Self { x, y }
65 }
66
67 pub fn identity() -> Self {
69 Self {
70 x: F::zero(),
71 y: F::one(),
72 }
73 }
74
75 pub fn is_identity(&self) -> bool {
77 self.x == F::zero() && self.y == F::one()
78 }
79
80 pub fn order_two_point() -> Self {
82 Self {
83 x: F::zero(),
84 y: -F::one(),
85 }
86 }
87}
88
89impl<F> ConditionallySelectable for JacobiQuarticPoint<F>
90where
91 F: FieldOps + Copy,
92{
93 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
94 Self {
95 x: F::conditional_select(&a.x, &b.x, choice),
96 y: F::conditional_select(&a.y, &b.y, choice),
97 }
98 }
99
100 fn conditional_assign(&mut self, other: &Self, choice: Choice) {
101 self.x.conditional_assign(&other.x, choice);
102 self.y.conditional_assign(&other.y, choice);
103 }
104
105 fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
106 F::conditional_swap(&mut a.x, &mut b.x, choice);
107 F::conditional_swap(&mut a.y, &mut b.y, choice);
108 }
109}
110
111impl<F> ConstantTimeEq for JacobiQuarticPoint<F>
112where
113 F: FieldOps + Copy + ConstantTimeEq,
114{
115 fn ct_eq(&self, other: &Self) -> Choice {
116 self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y)
117 }
118
119 fn ct_ne(&self, other: &Self) -> Choice {
120 !self.ct_eq(other)
121 }
122}
123
124impl<F: FieldOps> JacobiQuarticPoint<F> {
125 pub fn negate(&self, _curve: &JacobiQuarticCurve<F>) -> Self {
127 Self::new(-self.x, self.y)
128 }
129
130 pub fn double(&self, curve: &JacobiQuarticCurve<F>) -> Self {
139 if self.is_identity() {
140 return *self;
141 }
142
143 let x2 = <F as FieldOps>::square(&self.x);
144 let y2 = <F as FieldOps>::square(&self.y);
145 let two = <F as FieldOps>::double(&F::one());
146
147 let denom = two + two * curve.a * x2 - y2;
148 let denom_inv = denom.invert().into_option()
149 .expect("Jacobi quartic doubling denominator vanished; result leaves affine chart or input is exceptional");
150
151 let mu = two * self.y * denom_inv;
152 let x3 = mu * self.x;
153 let y3 = mu * (mu - self.y) - F::one();
154
155 Self::new(x3, y3)
156 }
157
158 pub fn add(&self, other: &Self, curve: &JacobiQuarticCurve<F>) -> Self {
167 if self.is_identity() {
168 return *other;
169 }
170 if other.is_identity() {
171 return *self;
172 }
173 if *self == *other {
174 return self.double(curve);
175 }
176 if *other == self.negate(curve) {
177 return Self::identity();
178 }
179
180 let x1 = self.x;
181 let y1 = self.y;
182 let x2 = other.x;
183 let y2 = other.y;
184
185 let x1_sq = <F as FieldOps>::square(&x1);
186 let x2_sq = <F as FieldOps>::square(&x2);
187 let x1x2 = x1 * x2;
188 let y1y2 = y1 * y2;
189 let dx4 = curve.d * x1_sq * x2_sq;
190 let two = <F as FieldOps>::double(&F::one());
191
192 let denom = F::one() - dx4;
193 let denom_inv = denom.invert().into_option()
194 .expect("Jacobi quartic addition denominator vanished; choose d nonsquare / odd-order subgroup or use a projective model");
195
196 let x3 = (x1 * y2 + y1 * x2) * denom_inv;
197
198 let numer_y = (y1y2 + two * curve.a * x1x2) * (F::one() + dx4)
199 + two * curve.d * x1x2 * (x1_sq + x2_sq);
200 let y3 = numer_y * <F as FieldOps>::square(&denom_inv);
201
202 Self::new(x3, y3)
203 }
204
205 pub fn scalar_mul(&self, k: &[u64], curve: &JacobiQuarticCurve<F>) -> Self {
207 let mut result = Self::identity();
208
209 for &limb in k.iter().rev() {
210 for bit in (0..64).rev() {
211 let doubled = result.double(curve);
212 let added = doubled.add(self, curve);
213 let choice = Choice::from(((limb >> bit) & 1) as u8);
214 result = Self::conditional_select(&doubled, &added, choice);
215 }
216 }
217
218 result
219 }
220}
221
222impl<F: FieldOps> PointOps for JacobiQuarticPoint<F> {
223 type BaseField = F;
224 type Curve = JacobiQuarticCurve<F>;
225
226 fn identity(_curve: &Self::Curve) -> Self {
227 JacobiQuarticPoint::<F>::identity()
228 }
229
230 fn is_identity(&self) -> bool {
231 JacobiQuarticPoint::<F>::is_identity(self)
232 }
233
234 fn negate(&self, curve: &Self::Curve) -> Self {
235 JacobiQuarticPoint::<F>::negate(self, curve)
236 }
237
238 fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self {
239 JacobiQuarticPoint::<F>::scalar_mul(self, k, curve)
240 }
241}
242
243impl<F: FieldOps> PointAdd for JacobiQuarticPoint<F> {
244 fn add(&self, other: &Self, curve: &Self::Curve) -> Self {
245 JacobiQuarticPoint::<F>::add(self, other, curve)
246 }
247}