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}