ed448_goldilocks/curve/twedwards/
extended.rs1#![allow(non_snake_case)]
2#![allow(dead_code)]
3
4use super::IsogenyMap;
5use super::extensible::ExtensiblePoint;
6use super::projective::ProjectiveNielsPoint;
7use super::{IsogenyMapResult, affine::AffineNielsPoint};
8use crate::edwards::EdwardsPoint as EdwardsExtendedPoint;
9use crate::field::FieldElement;
10use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
11
12#[derive(Copy, Clone, Debug)]
13pub struct ExtendedPoint {
14 pub(crate) X: FieldElement,
15 pub(crate) Y: FieldElement,
16 pub(crate) Z: FieldElement,
17 pub(crate) T: FieldElement,
18}
19
20impl ConstantTimeEq for ExtendedPoint {
21 fn ct_eq(&self, other: &Self) -> Choice {
22 let XZ = self.X * other.Z;
23 let ZX = self.Z * other.X;
24
25 let YZ = self.Y * other.Z;
26 let ZY = self.Z * other.Y;
27
28 (XZ.ct_eq(&ZX)) & (YZ.ct_eq(&ZY))
29 }
30}
31
32impl ConditionallySelectable for ExtendedPoint {
33 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
34 ExtendedPoint {
35 X: FieldElement::conditional_select(&a.X, &b.X, choice),
36 Y: FieldElement::conditional_select(&a.Y, &b.Y, choice),
37 Z: FieldElement::conditional_select(&a.Z, &b.Z, choice),
38 T: FieldElement::conditional_select(&a.T, &b.T, choice),
39 }
40 }
41}
42
43impl PartialEq for ExtendedPoint {
44 fn eq(&self, other: &ExtendedPoint) -> bool {
45 self.ct_eq(other).into()
46 }
47}
48impl PartialEq<ExtensiblePoint> for ExtendedPoint {
49 fn eq(&self, other: &ExtensiblePoint) -> bool {
50 self.to_extensible().ct_eq(other).into()
51 }
52}
53impl Eq for ExtendedPoint {}
54
55impl Default for ExtendedPoint {
56 fn default() -> ExtendedPoint {
57 ExtendedPoint::IDENTITY
58 }
59}
60
61impl elliptic_curve::zeroize::DefaultIsZeroes for ExtendedPoint {}
62
63impl ExtendedPoint {
64 pub const GENERATOR: ExtendedPoint = ExtendedPoint {
66 X: crate::TWISTED_EDWARDS_BASE_POINT.X,
67 Y: crate::TWISTED_EDWARDS_BASE_POINT.Y,
68 Z: crate::TWISTED_EDWARDS_BASE_POINT.Z,
69 T: crate::TWISTED_EDWARDS_BASE_POINT.T,
70 };
71 pub const IDENTITY: ExtendedPoint = ExtendedPoint {
73 X: FieldElement::ZERO,
74 Y: FieldElement::ONE,
75 Z: FieldElement::ONE,
76 T: FieldElement::ZERO,
77 };
78
79 pub fn add_extended(&self, other: &ExtendedPoint) -> ExtensiblePoint {
83 let A = self.X * other.X;
84 let B = self.Y * other.Y;
85 let C = self.T * other.T * FieldElement::TWISTED_D;
86 let D = self.Z * other.Z;
87 let E = (self.X + self.Y) * (other.X + other.Y) - A - B;
88 let F = D - C;
89 let G = D + C;
90 let H = B + A;
91 ExtensiblePoint {
92 X: E * F,
93 Y: G * H,
94 T1: E,
95 T2: H,
96 Z: F * G,
97 }
98 }
99
100 pub fn sub_extended(&self, other: &ExtendedPoint) -> ExtensiblePoint {
104 let A = self.X * other.X;
105 let B = self.Y * other.Y;
106 let C = self.T * other.T * FieldElement::TWISTED_D;
107 let D = self.Z * other.Z;
108 let E = (self.X + self.Y) * (other.Y - other.X) + A - B;
109 let F = D + C;
110 let G = D - C;
111 let H = B - A;
112 ExtensiblePoint {
113 X: E * F,
114 Y: G * H,
115 T1: E,
116 T2: H,
117 Z: F * G,
118 }
119 }
120
121 pub fn add_affine_niels(&self, other: AffineNielsPoint) -> ExtensiblePoint {
124 let A = other.y_minus_x * (self.Y - self.X);
125 let B = other.y_plus_x * (self.X + self.Y);
126 let C = other.td * self.T;
127 let D = B + A;
128 let E = B - A;
129 let F = self.Z - C;
130 let G = self.Z + C;
131 ExtensiblePoint {
132 X: E * F,
133 Y: G * D,
134 Z: F * G,
135 T1: E,
136 T2: D,
137 }
138 }
139
140 pub fn add_projective_niels(&self, other: &ProjectiveNielsPoint) -> ExtensiblePoint {
146 let Z = self.Z * other.Z;
148
149 let A = (self.Y - self.X) * other.Y_minus_X;
150 let B = (self.Y + self.X) * other.Y_plus_X;
151 let C = other.Td * self.T;
152 let D = B + A;
153 let E = B - A;
154 let F = Z - C;
155 let G = Z + C;
156 ExtensiblePoint {
157 X: E * F,
158 Y: G * D,
159 Z: F * G,
160 T1: E,
161 T2: D,
162 }
163 }
164
165 pub fn to_extensible(self) -> ExtensiblePoint {
167 ExtensiblePoint {
168 X: self.X,
169 Y: self.Y,
170 Z: self.Z,
171 T1: self.T,
172 T2: FieldElement::ONE,
173 }
174 }
175
176 pub fn to_untwisted(self) -> EdwardsExtendedPoint {
178 let IsogenyMapResult { X, Y, Z, T1, T2 } = IsogenyMap {
179 X: self.X,
180 Y: self.Y,
181 Z: self.Z,
182 T: self.T,
183 }
184 .map(|f| -f);
185
186 EdwardsExtendedPoint {
187 X,
188 Y,
189 Z,
190 T: T1 * T2,
191 }
192 }
193
194 pub fn to_projective_niels(self) -> ProjectiveNielsPoint {
196 ProjectiveNielsPoint {
197 Y_plus_X: self.X + self.Y,
198 Y_minus_X: self.Y - self.X,
199 Z: self.Z.double(),
200 Td: self.T * FieldElement::TWO_TIMES_TWISTED_D,
201 }
202 }
203
204 pub(crate) fn is_on_curve(&self) -> Choice {
206 let XY = self.X * self.Y;
207 let ZT = self.Z * self.T;
208
209 let YY = self.Y.square();
212 let XX = self.X.square();
213 let ZZ = self.Z.square();
214 let TT = self.T.square();
215 let lhs = YY - XX;
216 let rhs = ZZ + TT * FieldElement::TWISTED_D;
217
218 XY.ct_eq(&ZT) & lhs.ct_eq(&rhs)
219 }
220
221 pub fn negate(&self) -> ExtendedPoint {
223 ExtendedPoint {
224 X: -self.X,
225 Y: self.Y,
226 Z: self.Z,
227 T: -self.T,
228 }
229 }
230
231 pub fn torque(&self) -> ExtendedPoint {
233 ExtendedPoint {
234 X: -self.X,
235 Y: -self.Y,
236 Z: self.Z,
237 T: self.T,
238 }
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use crate::curve::twedwards::affine::AffinePoint;
246 use crate::{GOLDILOCKS_BASE_POINT, TWISTED_EDWARDS_BASE_POINT};
247
248 fn hex_to_field(hex: &'static str) -> FieldElement {
249 assert_eq!(hex.len(), 56 * 2);
250 let mut bytes =
251 hex_literal::decode(&[hex.as_bytes()]).expect("Output array length should be correct");
252 bytes.reverse();
253 FieldElement::from_bytes(&bytes)
254 }
255
256 #[test]
257 fn test_isogeny() {
258 let x = hex_to_field(
259 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa955555555555555555555555555555555555555555555555555555555",
260 );
261 let y = hex_to_field(
262 "ae05e9634ad7048db359d6205086c2b0036ed7a035884dd7b7e36d728ad8c4b80d6565833a2a3098bbbcb2bed1cda06bdaeafbcdea9386ed",
263 );
264 let a = AffinePoint { x, y }.to_extensible();
265 let twist_a = a.to_extended().to_untwisted().to_twisted();
266 assert_eq!(twist_a, a.double().double())
267 }
268
269 #[test]
270 fn test_is_on_curve() {
271 assert_eq!(TWISTED_EDWARDS_BASE_POINT.is_on_curve().unwrap_u8(), 1u8);
274
275 let invalid_point = ExtendedPoint {
277 X: GOLDILOCKS_BASE_POINT.X,
278 Y: GOLDILOCKS_BASE_POINT.Y,
279 Z: GOLDILOCKS_BASE_POINT.Z,
280 T: GOLDILOCKS_BASE_POINT.T,
281 };
282 assert_eq!(invalid_point.is_on_curve().unwrap_u8(), 0u8);
283 }
284
285 #[test]
286 fn test_point_add() {
287 let a = TWISTED_EDWARDS_BASE_POINT;
288 let b = a.to_extensible().double().to_extended();
289
290 let c_1 = a.add_extended(&b).to_extended();
292 let c_2 = b.add_extended(&a).to_extended();
293 assert!(c_1 == c_2);
294
295 let c = c_1.add_extended(&ExtendedPoint::IDENTITY);
297 assert!(c == c_1);
298 }
299
300 #[test]
301 fn test_point_sub() {
302 let a = TWISTED_EDWARDS_BASE_POINT;
303 let b = a.to_extensible().double().to_extended();
304
305 let c_1 = a.sub_extended(&b).to_extended();
307
308 let c_2 = b.negate().add_extended(&a).to_extended();
310 assert!(c_1 == c_2);
311 }
312
313 #[test]
314 fn test_negate() {
315 let a = TWISTED_EDWARDS_BASE_POINT;
316 let neg_a = a.negate();
317
318 assert!(a.add_extended(&neg_a) == ExtensiblePoint::IDENTITY);
319 }
320}