curve25519_dalek/backend/serial/curve_models/mod.rs
1// -*- mode: rust; -*-
2//
3// This file is part of curve25519-dalek.
4// Copyright (c) 2016-2021 isis lovecruft
5// Copyright (c) 2016-2019 Henry de Valence
6// See LICENSE for licensing information.
7//
8// Authors:
9// - isis agora lovecruft <isis@patternsinthevoid.net>
10// - Henry de Valence <hdevalence@hdevalence.ca>
11
12//! Internal curve representations which are not part of the public API.
13//!
14//! # Curve representations
15//!
16//! Internally, we use several different models for the curve. Here
17//! is a sketch of the relationship between the models, following [a
18//! post][smith-moderncrypto]
19//! by Ben Smith on the `moderncrypto` mailing list. This is also briefly
20//! discussed in section 2.5 of [_Montgomery curves and their
21//! arithmetic_][costello-smith-2017] by Costello and Smith.
22//!
23//! Begin with the affine equation for the curve,
24//! $$
25//! -x\^2 + y\^2 = 1 + dx\^2y\^2.
26//! $$
27//! Next, pass to the projective closure \\(\mathbb P\^1 \times \mathbb
28//! P\^1 \\) by setting \\(x=X/Z\\), \\(y=Y/T.\\) Clearing denominators
29//! gives the model
30//! $$
31//! -X\^2T\^2 + Y\^2Z\^2 = Z\^2T\^2 + dX\^2Y\^2.
32//! $$
33//! In `curve25519-dalek`, this is represented as the `CompletedPoint`
34//! struct.
35//! To map from \\(\mathbb P\^1 \times \mathbb P\^1 \\), a product of
36//! two lines, to \\(\mathbb P\^3\\), we use the [Segre
37//! embedding](https://en.wikipedia.org/wiki/Segre_embedding)
38//! $$
39//! \sigma : ((X:Z),(Y:T)) \mapsto (XY:XT:ZY:ZT).
40//! $$
41//! Using coordinates \\( (W_0:W_1:W_2:W_3) \\) for \\(\mathbb P\^3\\),
42//! the image \\(\sigma (\mathbb P\^1 \times \mathbb P\^1) \\) is the
43//! surface defined by \\( W_0 W_3 = W_1 W_2 \\), and under \\(
44//! \sigma\\), the equation above becomes
45//! $$
46//! -W\_1\^2 + W\_2\^2 = W\_3\^2 + dW\_0\^2,
47//! $$
48//! so that the curve is given by the pair of equations
49//! $$
50//! \begin{aligned}
51//! -W\_1\^2 + W\_2\^2 &= W\_3\^2 + dW\_0\^2, \\\\ W_0 W_3 &= W_1 W_2.
52//! \end{aligned}
53//! $$
54//! Up to variable naming, this is exactly the "extended" curve model
55//! introduced in [_Twisted Edwards Curves
56//! Revisited_][hisil-wong-carter-dawson-2008] by Hisil, Wong, Carter,
57//! and Dawson. In `curve25519-dalek`, it is represented as the
58//! `EdwardsPoint` struct. We can map from \\(\mathbb P\^3 \\) to
59//! \\(\mathbb P\^2 \\) by sending \\( (W\_0:W\_1:W\_2:W\_3) \\) to \\(
60//! (W\_1:W\_2:W\_3) \\). Notice that
61//! $$
62//! \frac {W\_1} {W\_3} = \frac {XT} {ZT} = \frac X Z = x,
63//! $$
64//! and
65//! $$
66//! \frac {W\_2} {W\_3} = \frac {YZ} {ZT} = \frac Y T = y,
67//! $$
68//! so this is the same as if we had started with the affine model
69//! and passed to \\( \mathbb P\^2 \\) by setting \\( x = W\_1 / W\_3
70//! \\), \\(y = W\_2 / W\_3 \\).
71//! Up to variable naming, this is the projective representation
72//! introduced in in [_Twisted Edwards
73//! Curves_][bernstein-birkner-joye-lange-peters-2008] by Bernstein,
74//! Birkner, Joye, Lange, and Peters. In `curve25519-dalek`, it is
75//! represented by the `ProjectivePoint` struct.
76//!
77//! # Passing between curve models
78//!
79//! Although the \\( \mathbb P\^3 \\) model provides faster addition
80//! formulas, the \\( \mathbb P\^2 \\) model provides faster doubling
81//! formulas. Hisil, Wong, Carter, and Dawson therefore suggest mixing
82//! coordinate systems for scalar multiplication, attributing the idea
83//! to [a 1998 paper][cohen-miyaji-ono-1998] of Cohen, Miyagi, and Ono.
84//!
85//! Their suggestion is to vary the formulas used by context, using a
86//! \\( \mathbb P\^2 \rightarrow \mathbb P\^2 \\) doubling formula when
87//! a doubling is followed
88//! by another doubling, a \\( \mathbb P\^2 \rightarrow \mathbb P\^3 \\)
89//! doubling formula when a doubling is followed by an addition, and
90//! computing point additions using a \\( \mathbb P\^3 \times \mathbb P\^3
91//! \rightarrow \mathbb P\^2 \\) formula.
92//!
93//! The `ref10` reference implementation of [Ed25519][ed25519], by
94//! Bernstein, Duif, Lange, Schwabe, and Yang, tweaks
95//! this strategy, factoring the addition formulas through the
96//! completion \\( \mathbb P\^1 \times \mathbb P\^1 \\), so that the
97//! output of an addition or doubling always lies in \\( \mathbb P\^1 \times
98//! \mathbb P\^1\\), and the choice of which formula to use is replaced
99//! by a choice of whether to convert the result to \\( \mathbb P\^2 \\)
100//! or \\(\mathbb P\^3 \\). However, this tweak is not described in
101//! their paper, only in their software.
102//!
103//! Our naming for the `CompletedPoint` (\\(\mathbb P\^1 \times \mathbb
104//! P\^1 \\)), `ProjectivePoint` (\\(\mathbb P\^2 \\)), and
105//! `EdwardsPoint` (\\(\mathbb P\^3 \\)) structs follows the naming in
106//! Adam Langley's [Golang ed25519][agl-ed25519] implementation, which
107//! `curve25519-dalek` was originally derived from.
108//!
109//! Finally, to accelerate readditions, we use two cached point formats
110//! in "Niels coordinates", named for Niels Duif,
111//! one for the affine model and one for the \\( \mathbb P\^3 \\) model:
112//!
113//! * `AffineNielsPoint`: \\( (y+x, y-x, 2dxy) \\)
114//! * `ProjectiveNielsPoint`: \\( (Y+X, Y-X, Z, 2dXY) \\)
115//!
116//! [smith-moderncrypto]: https://moderncrypto.org/mail-archive/curves/2016/000807.html
117//! [costello-smith-2017]: https://eprint.iacr.org/2017/212
118//! [hisil-wong-carter-dawson-2008]: https://www.iacr.org/archive/asiacrypt2008/53500329/53500329.pdf
119//! [bernstein-birkner-joye-lange-peters-2008]: https://eprint.iacr.org/2008/013
120//! [cohen-miyaji-ono-1998]: https://link.springer.com/content/pdf/10.1007%2F3-540-49649-1_6.pdf
121//! [ed25519]: https://eprint.iacr.org/2011/368
122//! [agl-ed25519]: https://github.com/agl/ed25519
123
124#![allow(non_snake_case)]
125
126use core::fmt::Debug;
127use core::ops::{Add, Neg, Sub};
128
129use subtle::Choice;
130use subtle::ConditionallySelectable;
131
132#[cfg(feature = "zeroize")]
133use zeroize::Zeroize;
134
135use crate::constants;
136
137use crate::edwards::EdwardsPoint;
138use crate::field::FieldElement;
139use crate::traits::ValidityCheck;
140
141// ------------------------------------------------------------------------
142// Internal point representations
143// ------------------------------------------------------------------------
144
145/// A `ProjectivePoint` is a point \\((X:Y:Z)\\) on the \\(\mathbb
146/// P\^2\\) model of the curve.
147/// A point \\((x,y)\\) in the affine model corresponds to
148/// \\((x:y:1)\\).
149///
150/// More details on the relationships between the different curve models
151/// can be found in the module-level documentation.
152#[allow(missing_docs)]
153#[derive(Copy, Clone)]
154pub struct ProjectivePoint {
155 pub X: FieldElement,
156 pub Y: FieldElement,
157 pub Z: FieldElement,
158}
159
160/// A `CompletedPoint` is a point \\(((X:Z), (Y:T))\\) on the \\(\mathbb
161/// P\^1 \times \mathbb P\^1 \\) model of the curve.
162/// A point (x,y) in the affine model corresponds to \\( ((x:1),(y:1))
163/// \\).
164///
165/// More details on the relationships between the different curve models
166/// can be found in the module-level documentation.
167#[derive(Copy, Clone)]
168#[allow(missing_docs)]
169pub struct CompletedPoint {
170 pub X: FieldElement,
171 pub Y: FieldElement,
172 pub Z: FieldElement,
173 pub T: FieldElement,
174}
175
176/// A pre-computed point in the affine model for the curve, represented as
177/// \\((y+x, y-x, 2dxy)\\) in "Niels coordinates".
178///
179/// More details on the relationships between the different curve models
180/// can be found in the module-level documentation.
181// Safe to derive Eq because affine coordinates.
182#[derive(Copy, Clone, Eq, PartialEq)]
183#[allow(missing_docs)]
184pub struct AffineNielsPoint {
185 pub y_plus_x: FieldElement,
186 pub y_minus_x: FieldElement,
187 pub xy2d: FieldElement,
188}
189
190#[cfg(feature = "zeroize")]
191impl Zeroize for AffineNielsPoint {
192 fn zeroize(&mut self) {
193 self.y_plus_x.zeroize();
194 self.y_minus_x.zeroize();
195 self.xy2d.zeroize();
196 }
197}
198
199/// A pre-computed point on the \\( \mathbb P\^3 \\) model for the
200/// curve, represented as \\((Y+X, Y-X, Z, 2dXY)\\) in "Niels coordinates".
201///
202/// More details on the relationships between the different curve models
203/// can be found in the module-level documentation.
204#[derive(Copy, Clone)]
205#[allow(missing_docs)]
206pub struct ProjectiveNielsPoint {
207 pub Y_plus_X: FieldElement,
208 pub Y_minus_X: FieldElement,
209 pub Z: FieldElement,
210 pub T2d: FieldElement,
211}
212
213#[cfg(feature = "zeroize")]
214impl Zeroize for ProjectiveNielsPoint {
215 fn zeroize(&mut self) {
216 self.Y_plus_X.zeroize();
217 self.Y_minus_X.zeroize();
218 self.Z.zeroize();
219 self.T2d.zeroize();
220 }
221}
222
223// ------------------------------------------------------------------------
224// Constructors
225// ------------------------------------------------------------------------
226
227use crate::traits::Identity;
228
229impl Identity for ProjectivePoint {
230 fn identity() -> ProjectivePoint {
231 ProjectivePoint {
232 X: FieldElement::ZERO,
233 Y: FieldElement::ONE,
234 Z: FieldElement::ONE,
235 }
236 }
237}
238
239impl Identity for ProjectiveNielsPoint {
240 fn identity() -> ProjectiveNielsPoint {
241 ProjectiveNielsPoint {
242 Y_plus_X: FieldElement::ONE,
243 Y_minus_X: FieldElement::ONE,
244 Z: FieldElement::ONE,
245 T2d: FieldElement::ZERO,
246 }
247 }
248}
249
250impl Default for ProjectiveNielsPoint {
251 fn default() -> ProjectiveNielsPoint {
252 ProjectiveNielsPoint::identity()
253 }
254}
255
256impl Identity for AffineNielsPoint {
257 fn identity() -> AffineNielsPoint {
258 AffineNielsPoint {
259 y_plus_x: FieldElement::ONE,
260 y_minus_x: FieldElement::ONE,
261 xy2d: FieldElement::ZERO,
262 }
263 }
264}
265
266impl Default for AffineNielsPoint {
267 fn default() -> AffineNielsPoint {
268 AffineNielsPoint::identity()
269 }
270}
271
272// ------------------------------------------------------------------------
273// Validity checks (for debugging, not CT)
274// ------------------------------------------------------------------------
275
276impl ValidityCheck for ProjectivePoint {
277 fn is_valid(&self) -> bool {
278 // Curve equation is -x^2 + y^2 = 1 + d*x^2*y^2,
279 // homogenized as (-X^2 + Y^2)*Z^2 = Z^4 + d*X^2*Y^2
280 let XX = self.X.square();
281 let YY = self.Y.square();
282 let ZZ = self.Z.square();
283 let ZZZZ = ZZ.square();
284 let lhs = &(&YY - &XX) * &ZZ;
285 let rhs = &ZZZZ + &(&constants::EDWARDS_D * &(&XX * &YY));
286
287 lhs == rhs
288 }
289}
290
291// ------------------------------------------------------------------------
292// Constant-time assignment
293// ------------------------------------------------------------------------
294
295impl ConditionallySelectable for ProjectiveNielsPoint {
296 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
297 ProjectiveNielsPoint {
298 Y_plus_X: FieldElement::conditional_select(&a.Y_plus_X, &b.Y_plus_X, choice),
299 Y_minus_X: FieldElement::conditional_select(&a.Y_minus_X, &b.Y_minus_X, choice),
300 Z: FieldElement::conditional_select(&a.Z, &b.Z, choice),
301 T2d: FieldElement::conditional_select(&a.T2d, &b.T2d, choice),
302 }
303 }
304
305 fn conditional_assign(&mut self, other: &Self, choice: Choice) {
306 self.Y_plus_X.conditional_assign(&other.Y_plus_X, choice);
307 self.Y_minus_X.conditional_assign(&other.Y_minus_X, choice);
308 self.Z.conditional_assign(&other.Z, choice);
309 self.T2d.conditional_assign(&other.T2d, choice);
310 }
311}
312
313impl ConditionallySelectable for AffineNielsPoint {
314 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
315 AffineNielsPoint {
316 y_plus_x: FieldElement::conditional_select(&a.y_plus_x, &b.y_plus_x, choice),
317 y_minus_x: FieldElement::conditional_select(&a.y_minus_x, &b.y_minus_x, choice),
318 xy2d: FieldElement::conditional_select(&a.xy2d, &b.xy2d, choice),
319 }
320 }
321
322 fn conditional_assign(&mut self, other: &Self, choice: Choice) {
323 self.y_plus_x.conditional_assign(&other.y_plus_x, choice);
324 self.y_minus_x.conditional_assign(&other.y_minus_x, choice);
325 self.xy2d.conditional_assign(&other.xy2d, choice);
326 }
327}
328
329// ------------------------------------------------------------------------
330// Point conversions
331// ------------------------------------------------------------------------
332
333impl ProjectivePoint {
334 /// Convert this point from the \\( \mathbb P\^2 \\) model to the
335 /// \\( \mathbb P\^3 \\) model.
336 ///
337 /// This costs \\(3 \mathrm M + 1 \mathrm S\\).
338 pub fn as_extended(&self) -> EdwardsPoint {
339 EdwardsPoint {
340 X: &self.X * &self.Z,
341 Y: &self.Y * &self.Z,
342 Z: self.Z.square(),
343 T: &self.X * &self.Y,
344 }
345 }
346}
347
348impl CompletedPoint {
349 /// Convert this point from the \\( \mathbb P\^1 \times \mathbb P\^1
350 /// \\) model to the \\( \mathbb P\^2 \\) model.
351 ///
352 /// This costs \\(3 \mathrm M \\).
353 pub fn as_projective(&self) -> ProjectivePoint {
354 ProjectivePoint {
355 X: &self.X * &self.T,
356 Y: &self.Y * &self.Z,
357 Z: &self.Z * &self.T,
358 }
359 }
360
361 /// Convert this point from the \\( \mathbb P\^1 \times \mathbb P\^1
362 /// \\) model to the \\( \mathbb P\^3 \\) model.
363 ///
364 /// This costs \\(4 \mathrm M \\).
365 pub fn as_extended(&self) -> EdwardsPoint {
366 EdwardsPoint {
367 X: &self.X * &self.T,
368 Y: &self.Y * &self.Z,
369 Z: &self.Z * &self.T,
370 T: &self.X * &self.Y,
371 }
372 }
373}
374
375// ------------------------------------------------------------------------
376// Doubling
377// ------------------------------------------------------------------------
378
379impl ProjectivePoint {
380 /// Double this point: return self + self
381 pub fn double(&self) -> CompletedPoint {
382 // Double()
383 let XX = self.X.square();
384 let YY = self.Y.square();
385 let ZZ2 = self.Z.square2();
386 let X_plus_Y = &self.X + &self.Y;
387 let X_plus_Y_sq = X_plus_Y.square();
388 let YY_plus_XX = &YY + &XX;
389 let YY_minus_XX = &YY - &XX;
390
391 CompletedPoint {
392 X: &X_plus_Y_sq - &YY_plus_XX,
393 Y: YY_plus_XX,
394 Z: YY_minus_XX,
395 T: &ZZ2 - &YY_minus_XX,
396 }
397 }
398}
399
400// ------------------------------------------------------------------------
401// Addition and Subtraction
402// ------------------------------------------------------------------------
403
404// XXX(hdevalence) These were doc(hidden) so they don't appear in the
405// public API docs.
406// However, that prevents them being used with --document-private-items,
407// so comment out the doc(hidden) for now until this is resolved
408//
409// upstream rust issue: https://github.com/rust-lang/rust/issues/46380
410//#[doc(hidden)]
411impl<'a, 'b> Add<&'b ProjectiveNielsPoint> for &'a EdwardsPoint {
412 type Output = CompletedPoint;
413
414 fn add(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint {
415 let Y_plus_X = &self.Y + &self.X;
416 let Y_minus_X = &self.Y - &self.X;
417 let PP = &Y_plus_X * &other.Y_plus_X;
418 let MM = &Y_minus_X * &other.Y_minus_X;
419 let TT2d = &self.T * &other.T2d;
420 let ZZ = &self.Z * &other.Z;
421 let ZZ2 = &ZZ + &ZZ;
422
423 CompletedPoint {
424 X: &PP - &MM,
425 Y: &PP + &MM,
426 Z: &ZZ2 + &TT2d,
427 T: &ZZ2 - &TT2d,
428 }
429 }
430}
431
432//#[doc(hidden)]
433impl<'a, 'b> Sub<&'b ProjectiveNielsPoint> for &'a EdwardsPoint {
434 type Output = CompletedPoint;
435
436 fn sub(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint {
437 let Y_plus_X = &self.Y + &self.X;
438 let Y_minus_X = &self.Y - &self.X;
439 let PM = &Y_plus_X * &other.Y_minus_X;
440 let MP = &Y_minus_X * &other.Y_plus_X;
441 let TT2d = &self.T * &other.T2d;
442 let ZZ = &self.Z * &other.Z;
443 let ZZ2 = &ZZ + &ZZ;
444
445 CompletedPoint {
446 X: &PM - &MP,
447 Y: &PM + &MP,
448 Z: &ZZ2 - &TT2d,
449 T: &ZZ2 + &TT2d,
450 }
451 }
452}
453
454//#[doc(hidden)]
455impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a EdwardsPoint {
456 type Output = CompletedPoint;
457
458 fn add(self, other: &'b AffineNielsPoint) -> CompletedPoint {
459 let Y_plus_X = &self.Y + &self.X;
460 let Y_minus_X = &self.Y - &self.X;
461 let PP = &Y_plus_X * &other.y_plus_x;
462 let MM = &Y_minus_X * &other.y_minus_x;
463 let Txy2d = &self.T * &other.xy2d;
464 let Z2 = &self.Z + &self.Z;
465
466 CompletedPoint {
467 X: &PP - &MM,
468 Y: &PP + &MM,
469 Z: &Z2 + &Txy2d,
470 T: &Z2 - &Txy2d,
471 }
472 }
473}
474
475//#[doc(hidden)]
476impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a EdwardsPoint {
477 type Output = CompletedPoint;
478
479 fn sub(self, other: &'b AffineNielsPoint) -> CompletedPoint {
480 let Y_plus_X = &self.Y + &self.X;
481 let Y_minus_X = &self.Y - &self.X;
482 let PM = &Y_plus_X * &other.y_minus_x;
483 let MP = &Y_minus_X * &other.y_plus_x;
484 let Txy2d = &self.T * &other.xy2d;
485 let Z2 = &self.Z + &self.Z;
486
487 CompletedPoint {
488 X: &PM - &MP,
489 Y: &PM + &MP,
490 Z: &Z2 - &Txy2d,
491 T: &Z2 + &Txy2d,
492 }
493 }
494}
495
496// ------------------------------------------------------------------------
497// Negation
498// ------------------------------------------------------------------------
499
500impl<'a> Neg for &'a ProjectiveNielsPoint {
501 type Output = ProjectiveNielsPoint;
502
503 fn neg(self) -> ProjectiveNielsPoint {
504 ProjectiveNielsPoint {
505 Y_plus_X: self.Y_minus_X,
506 Y_minus_X: self.Y_plus_X,
507 Z: self.Z,
508 T2d: -(&self.T2d),
509 }
510 }
511}
512
513impl<'a> Neg for &'a AffineNielsPoint {
514 type Output = AffineNielsPoint;
515
516 fn neg(self) -> AffineNielsPoint {
517 AffineNielsPoint {
518 y_plus_x: self.y_minus_x,
519 y_minus_x: self.y_plus_x,
520 xy2d: -(&self.xy2d),
521 }
522 }
523}
524
525// ------------------------------------------------------------------------
526// Debug traits
527// ------------------------------------------------------------------------
528
529impl Debug for ProjectivePoint {
530 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
531 write!(
532 f,
533 "ProjectivePoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?}\n}}",
534 &self.X, &self.Y, &self.Z
535 )
536 }
537}
538
539impl Debug for CompletedPoint {
540 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
541 write!(
542 f,
543 "CompletedPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}",
544 &self.X, &self.Y, &self.Z, &self.T
545 )
546 }
547}
548
549impl Debug for AffineNielsPoint {
550 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
551 write!(
552 f,
553 "AffineNielsPoint{{\n\ty_plus_x: {:?},\n\ty_minus_x: {:?},\n\txy2d: {:?}\n}}",
554 &self.y_plus_x, &self.y_minus_x, &self.xy2d
555 )
556 }
557}
558
559impl Debug for ProjectiveNielsPoint {
560 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
561 write!(f, "ProjectiveNielsPoint{{\n\tY_plus_X: {:?},\n\tY_minus_X: {:?},\n\tZ: {:?},\n\tT2d: {:?}\n}}",
562 &self.Y_plus_X, &self.Y_minus_X, &self.Z, &self.T2d)
563 }
564}