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 [_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
229#[cfg_attr(verify, aeneas::rename("IdentityCurveModelsProjectivePoint"))]
230impl Identity for ProjectivePoint {
231    fn identity() -> ProjectivePoint {
232        ProjectivePoint {
233            X: FieldElement::ZERO,
234            Y: FieldElement::ONE,
235            Z: FieldElement::ONE,
236        }
237    }
238}
239
240impl Identity for ProjectiveNielsPoint {
241    fn identity() -> ProjectiveNielsPoint {
242        ProjectiveNielsPoint {
243            Y_plus_X: FieldElement::ONE,
244            Y_minus_X: FieldElement::ONE,
245            Z: FieldElement::ONE,
246            T2d: FieldElement::ZERO,
247        }
248    }
249}
250
251impl Default for ProjectiveNielsPoint {
252    fn default() -> ProjectiveNielsPoint {
253        ProjectiveNielsPoint::identity()
254    }
255}
256
257impl Identity for AffineNielsPoint {
258    fn identity() -> AffineNielsPoint {
259        AffineNielsPoint {
260            y_plus_x: FieldElement::ONE,
261            y_minus_x: FieldElement::ONE,
262            xy2d: FieldElement::ZERO,
263        }
264    }
265}
266
267impl Default for AffineNielsPoint {
268    fn default() -> AffineNielsPoint {
269        AffineNielsPoint::identity()
270    }
271}
272
273// ------------------------------------------------------------------------
274// Validity checks (for debugging, not CT)
275// ------------------------------------------------------------------------
276
277impl ValidityCheck for ProjectivePoint {
278    fn is_valid(&self) -> bool {
279        // Curve equation is    -x^2 + y^2 = 1 + d*x^2*y^2,
280        // homogenized as (-X^2 + Y^2)*Z^2 = Z^4 + d*X^2*Y^2
281        let XX = self.X.square();
282        let YY = self.Y.square();
283        let ZZ = self.Z.square();
284        let ZZZZ = ZZ.square();
285        let lhs = &(&YY - &XX) * &ZZ;
286        let rhs = &ZZZZ + &(&constants::EDWARDS_D * &(&XX * &YY));
287
288        lhs == rhs
289    }
290}
291
292// ------------------------------------------------------------------------
293// Constant-time assignment
294// ------------------------------------------------------------------------
295
296impl ConditionallySelectable for ProjectiveNielsPoint {
297    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
298        ProjectiveNielsPoint {
299            Y_plus_X: FieldElement::conditional_select(&a.Y_plus_X, &b.Y_plus_X, choice),
300            Y_minus_X: FieldElement::conditional_select(&a.Y_minus_X, &b.Y_minus_X, choice),
301            Z: FieldElement::conditional_select(&a.Z, &b.Z, choice),
302            T2d: FieldElement::conditional_select(&a.T2d, &b.T2d, choice),
303        }
304    }
305
306    fn conditional_assign(&mut self, other: &Self, choice: Choice) {
307        self.Y_plus_X.conditional_assign(&other.Y_plus_X, choice);
308        self.Y_minus_X.conditional_assign(&other.Y_minus_X, choice);
309        self.Z.conditional_assign(&other.Z, choice);
310        self.T2d.conditional_assign(&other.T2d, choice);
311    }
312}
313
314impl ConditionallySelectable for AffineNielsPoint {
315    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
316        AffineNielsPoint {
317            y_plus_x: FieldElement::conditional_select(&a.y_plus_x, &b.y_plus_x, choice),
318            y_minus_x: FieldElement::conditional_select(&a.y_minus_x, &b.y_minus_x, choice),
319            xy2d: FieldElement::conditional_select(&a.xy2d, &b.xy2d, choice),
320        }
321    }
322
323    fn conditional_assign(&mut self, other: &Self, choice: Choice) {
324        self.y_plus_x.conditional_assign(&other.y_plus_x, choice);
325        self.y_minus_x.conditional_assign(&other.y_minus_x, choice);
326        self.xy2d.conditional_assign(&other.xy2d, choice);
327    }
328}
329
330// ------------------------------------------------------------------------
331// Point conversions
332// ------------------------------------------------------------------------
333
334impl ProjectivePoint {
335    /// Convert this point from the \\( \mathbb P\^2 \\) model to the
336    /// \\( \mathbb P\^3 \\) model.
337    ///
338    /// This costs \\(3 \mathrm M + 1 \mathrm S\\).
339    pub fn as_extended(&self) -> EdwardsPoint {
340        EdwardsPoint {
341            X: &self.X * &self.Z,
342            Y: &self.Y * &self.Z,
343            Z: self.Z.square(),
344            T: &self.X * &self.Y,
345        }
346    }
347}
348
349impl CompletedPoint {
350    /// Convert this point from the \\( \mathbb P\^1 \times \mathbb P\^1
351    /// \\) model to the \\( \mathbb P\^2 \\) model.
352    ///
353    /// This costs \\(3 \mathrm M \\).
354    pub fn as_projective(&self) -> ProjectivePoint {
355        ProjectivePoint {
356            X: &self.X * &self.T,
357            Y: &self.Y * &self.Z,
358            Z: &self.Z * &self.T,
359        }
360    }
361
362    /// Convert this point from the \\( \mathbb P\^1 \times \mathbb P\^1
363    /// \\) model to the \\( \mathbb P\^3 \\) model.
364    ///
365    /// This costs \\(4 \mathrm M \\).
366    pub fn as_extended(&self) -> EdwardsPoint {
367        EdwardsPoint {
368            X: &self.X * &self.T,
369            Y: &self.Y * &self.Z,
370            Z: &self.Z * &self.T,
371            T: &self.X * &self.Y,
372        }
373    }
374}
375
376// ------------------------------------------------------------------------
377// Doubling
378// ------------------------------------------------------------------------
379
380impl ProjectivePoint {
381    /// Double this point: return self + self
382    pub fn double(&self) -> CompletedPoint {
383        // Double()
384        let XX = self.X.square();
385        let YY = self.Y.square();
386        let ZZ2 = self.Z.square2();
387        let X_plus_Y = &self.X + &self.Y;
388        let X_plus_Y_sq = X_plus_Y.square();
389        let YY_plus_XX = &YY + &XX;
390        let YY_minus_XX = &YY - &XX;
391
392        CompletedPoint {
393            X: &X_plus_Y_sq - &YY_plus_XX,
394            Y: YY_plus_XX,
395            Z: YY_minus_XX,
396            T: &ZZ2 - &YY_minus_XX,
397        }
398    }
399}
400
401// ------------------------------------------------------------------------
402// Addition and Subtraction
403// ------------------------------------------------------------------------
404
405// XXX(hdevalence) These were doc(hidden) so they don't appear in the
406// public API docs.
407// However, that prevents them being used with --document-private-items,
408// so comment out the doc(hidden) for now until this is resolved
409//
410// upstream rust issue: https://github.com/rust-lang/rust/issues/46380
411//#[doc(hidden)]
412impl<'a> Add<&'a ProjectiveNielsPoint> for &EdwardsPoint {
413    type Output = CompletedPoint;
414
415    fn add(self, other: &'a ProjectiveNielsPoint) -> CompletedPoint {
416        let Y_plus_X = &self.Y + &self.X;
417        let Y_minus_X = &self.Y - &self.X;
418        let PP = &Y_plus_X * &other.Y_plus_X;
419        let MM = &Y_minus_X * &other.Y_minus_X;
420        let TT2d = &self.T * &other.T2d;
421        let ZZ = &self.Z * &other.Z;
422        let ZZ2 = &ZZ + &ZZ;
423
424        CompletedPoint {
425            X: &PP - &MM,
426            Y: &PP + &MM,
427            Z: &ZZ2 + &TT2d,
428            T: &ZZ2 - &TT2d,
429        }
430    }
431}
432
433//#[doc(hidden)]
434impl<'a> Sub<&'a ProjectiveNielsPoint> for &EdwardsPoint {
435    type Output = CompletedPoint;
436
437    fn sub(self, other: &'a ProjectiveNielsPoint) -> CompletedPoint {
438        let Y_plus_X = &self.Y + &self.X;
439        let Y_minus_X = &self.Y - &self.X;
440        let PM = &Y_plus_X * &other.Y_minus_X;
441        let MP = &Y_minus_X * &other.Y_plus_X;
442        let TT2d = &self.T * &other.T2d;
443        let ZZ = &self.Z * &other.Z;
444        let ZZ2 = &ZZ + &ZZ;
445
446        CompletedPoint {
447            X: &PM - &MP,
448            Y: &PM + &MP,
449            Z: &ZZ2 - &TT2d,
450            T: &ZZ2 + &TT2d,
451        }
452    }
453}
454
455//#[doc(hidden)]
456impl<'a> Add<&'a AffineNielsPoint> for &EdwardsPoint {
457    type Output = CompletedPoint;
458
459    fn add(self, other: &'a AffineNielsPoint) -> CompletedPoint {
460        let Y_plus_X = &self.Y + &self.X;
461        let Y_minus_X = &self.Y - &self.X;
462        let PP = &Y_plus_X * &other.y_plus_x;
463        let MM = &Y_minus_X * &other.y_minus_x;
464        let Txy2d = &self.T * &other.xy2d;
465        let Z2 = &self.Z + &self.Z;
466
467        CompletedPoint {
468            X: &PP - &MM,
469            Y: &PP + &MM,
470            Z: &Z2 + &Txy2d,
471            T: &Z2 - &Txy2d,
472        }
473    }
474}
475
476//#[doc(hidden)]
477impl<'a> Sub<&'a AffineNielsPoint> for &EdwardsPoint {
478    type Output = CompletedPoint;
479
480    fn sub(self, other: &'a AffineNielsPoint) -> CompletedPoint {
481        let Y_plus_X = &self.Y + &self.X;
482        let Y_minus_X = &self.Y - &self.X;
483        let PM = &Y_plus_X * &other.y_minus_x;
484        let MP = &Y_minus_X * &other.y_plus_x;
485        let Txy2d = &self.T * &other.xy2d;
486        let Z2 = &self.Z + &self.Z;
487
488        CompletedPoint {
489            X: &PM - &MP,
490            Y: &PM + &MP,
491            Z: &Z2 - &Txy2d,
492            T: &Z2 + &Txy2d,
493        }
494    }
495}
496
497// ------------------------------------------------------------------------
498// Negation
499// ------------------------------------------------------------------------
500
501impl Neg for &ProjectiveNielsPoint {
502    type Output = ProjectiveNielsPoint;
503
504    fn neg(self) -> ProjectiveNielsPoint {
505        ProjectiveNielsPoint {
506            Y_plus_X: self.Y_minus_X,
507            Y_minus_X: self.Y_plus_X,
508            Z: self.Z,
509            T2d: -(&self.T2d),
510        }
511    }
512}
513
514impl Neg for ProjectiveNielsPoint {
515    type Output = ProjectiveNielsPoint;
516
517    fn neg(self) -> ProjectiveNielsPoint {
518        -&self
519    }
520}
521
522impl Neg for &AffineNielsPoint {
523    type Output = AffineNielsPoint;
524
525    fn neg(self) -> AffineNielsPoint {
526        AffineNielsPoint {
527            y_plus_x: self.y_minus_x,
528            y_minus_x: self.y_plus_x,
529            xy2d: -(&self.xy2d),
530        }
531    }
532}
533
534impl Neg for AffineNielsPoint {
535    type Output = AffineNielsPoint;
536
537    fn neg(self) -> AffineNielsPoint {
538        -&self
539    }
540}
541
542// ------------------------------------------------------------------------
543// Debug traits
544// ------------------------------------------------------------------------
545
546impl Debug for ProjectivePoint {
547    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
548        write!(
549            f,
550            "ProjectivePoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?}\n}}",
551            &self.X, &self.Y, &self.Z
552        )
553    }
554}
555
556impl Debug for CompletedPoint {
557    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
558        write!(
559            f,
560            "CompletedPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}",
561            &self.X, &self.Y, &self.Z, &self.T
562        )
563    }
564}
565
566impl Debug for AffineNielsPoint {
567    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
568        write!(
569            f,
570            "AffineNielsPoint{{\n\ty_plus_x: {:?},\n\ty_minus_x: {:?},\n\txy2d: {:?}\n}}",
571            &self.y_plus_x, &self.y_minus_x, &self.xy2d
572        )
573    }
574}
575
576impl Debug for ProjectiveNielsPoint {
577    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
578        write!(f, "ProjectiveNielsPoint{{\n\tY_plus_X: {:?},\n\tY_minus_X: {:?},\n\tZ: {:?},\n\tT2d: {:?}\n}}",
579               &self.Y_plus_X, &self.Y_minus_X, &self.Z, &self.T2d)
580    }
581}