1 // -*- mode: rust; -*-
2 //
3 // This file is part of curve25519-dalek.
4 // Copyright (c) 2016-2019 Isis Lovecruft, Henry de Valence
5 // See LICENSE for licensing information.
6 //
7 // Authors:
8 // - Isis Agora Lovecruft <isis@patternsinthevoid.net>
9 // - Henry de Valence <hdevalence@hdevalence.ca>
10 
11 //! Parallel Edwards Arithmetic for Curve25519.
12 //!
13 //! This module currently has two point types:
14 //!
15 //! * `ExtendedPoint`: a point stored in vector-friendly format, with
16 //! vectorized doubling and addition;
17 //!
18 //! * `CachedPoint`: used for readdition.
19 //!
20 //! Details on the formulas can be found in the documentation for the
21 //! parent `avx2` module.
22 //!
23 //! This API is designed to be safe: vectorized points can only be
24 //! created from serial points (which do validation on decompression),
25 //! and operations on valid points return valid points, so invalid
26 //! point states should be unrepresentable.
27 //!
28 //! This design goal is met, with one exception: the `Neg`
29 //! implementation for the `CachedPoint` performs a lazy negation, so
30 //! that subtraction can be efficiently implemented as a negation and
31 //! an addition.  Repeatedly negating a `CachedPoint` will cause its
32 //! coefficients to grow and eventually overflow.  Repeatedly negating
33 //! a point should not be necessary anyways.
34 
35 #![allow(non_snake_case)]
36 
37 use core::convert::From;
38 use core::ops::{Add, Neg, Sub};
39 
40 use subtle::Choice;
41 use subtle::ConditionallySelectable;
42 
43 use edwards;
44 use window::{LookupTable, NafLookupTable5, NafLookupTable8};
45 
46 use traits::Identity;
47 
48 use super::constants;
49 use super::field::{FieldElement2625x4, Lanes, Shuffle};
50 
51 /// A point on Curve25519, using parallel Edwards formulas for curve
52 /// operations.
53 ///
54 /// # Invariant
55 ///
56 /// The coefficients of an `ExtendedPoint` are bounded with
57 /// \\( b < 0.007 \\).
58 #[derive(Copy, Clone, Debug)]
59 pub struct ExtendedPoint(pub(super) FieldElement2625x4);
60 
61 impl From<edwards::EdwardsPoint> for ExtendedPoint {
from(P: edwards::EdwardsPoint) -> ExtendedPoint62     fn from(P: edwards::EdwardsPoint) -> ExtendedPoint {
63         ExtendedPoint(FieldElement2625x4::new(&P.X, &P.Y, &P.Z, &P.T))
64     }
65 }
66 
67 impl From<ExtendedPoint> for edwards::EdwardsPoint {
from(P: ExtendedPoint) -> edwards::EdwardsPoint68     fn from(P: ExtendedPoint) -> edwards::EdwardsPoint {
69         let tmp = P.0.split();
70         edwards::EdwardsPoint {
71             X: tmp[0],
72             Y: tmp[1],
73             Z: tmp[2],
74             T: tmp[3],
75         }
76     }
77 }
78 
79 impl ConditionallySelectable for ExtendedPoint {
conditional_select(a: &Self, b: &Self, choice: Choice) -> Self80     fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
81         ExtendedPoint(FieldElement2625x4::conditional_select(&a.0, &b.0, choice))
82     }
83 
conditional_assign(&mut self, other: &Self, choice: Choice)84     fn conditional_assign(&mut self, other: &Self, choice: Choice) {
85         self.0.conditional_assign(&other.0, choice);
86     }
87 }
88 
89 impl Default for ExtendedPoint {
default() -> ExtendedPoint90     fn default() -> ExtendedPoint {
91         ExtendedPoint::identity()
92     }
93 }
94 
95 impl Identity for ExtendedPoint {
identity() -> ExtendedPoint96     fn identity() -> ExtendedPoint {
97         constants::EXTENDEDPOINT_IDENTITY
98     }
99 }
100 
101 impl ExtendedPoint {
102     /// Compute the double of this point.
double(&self) -> ExtendedPoint103     pub fn double(&self) -> ExtendedPoint {
104         // Want to compute (X1 Y1 Z1 X1+Y1).
105         // Not sure how to do this less expensively than computing
106         // (X1 Y1 Z1 T1) --(256bit shuffle)--> (X1 Y1 X1 Y1)
107         // (X1 Y1 X1 Y1) --(2x128b shuffle)--> (Y1 X1 Y1 X1)
108         // and then adding.
109 
110         // Set tmp0 = (X1 Y1 X1 Y1)
111         let mut tmp0 = self.0.shuffle(Shuffle::ABAB);
112 
113         // Set tmp1 = (Y1 X1 Y1 X1)
114         let mut tmp1 = tmp0.shuffle(Shuffle::BADC);
115 
116         // Set tmp0 = (X1 Y1 Z1 X1+Y1)
117         tmp0 = self.0.blend(tmp0 + tmp1, Lanes::D);
118 
119         // Set tmp1 = tmp0^2, negating the D values
120         tmp1 = tmp0.square_and_negate_D();
121         // Now tmp1 = (S1 S2 S3 -S4) with b < 0.007
122 
123         // See discussion of bounds in the module-level documentation.
124         // We want to compute
125         //
126         //    + | S1 | S1 | S1 | S1 |
127         //    + | S2 |    |    | S2 |
128         //    + |    |    | S3 |    |
129         //    + |    |    | S3 |    |
130         //    + |    |    |    |-S4 |
131         //    + |    | 2p | 2p |    |
132         //    - |    | S2 | S2 |    |
133         //    =======================
134         //        S5   S6   S8   S9
135 
136         let zero = FieldElement2625x4::zero();
137         let S_1 = tmp1.shuffle(Shuffle::AAAA);
138         let S_2 = tmp1.shuffle(Shuffle::BBBB);
139 
140         tmp0 = zero.blend(tmp1 + tmp1, Lanes::C);
141         // tmp0 = (0, 0,  2S_3, 0)
142         tmp0 = tmp0.blend(tmp1, Lanes::D);
143         // tmp0 = (0, 0,  2S_3, -S_4)
144         tmp0 = tmp0 + S_1;
145         // tmp0 = (  S_1,   S_1, S_1 + 2S_3, S_1 - S_4)
146         tmp0 = tmp0 + zero.blend(S_2, Lanes::AD);
147         // tmp0 = (S_1 + S_2,   S_1, S_1 + 2S_3, S_1 + S_2 - S_4)
148         tmp0 = tmp0 + zero.blend(S_2.negate_lazy(), Lanes::BC);
149         // tmp0 = (S_1 + S_2, S_1 - S_2, S_1 - S_2 + 2S_3, S_1 + S_2 - S_4)
150         //    b < (     1.01,       1.6,             2.33,             1.6)
151         // Now tmp0 = (S_5, S_6, S_8, S_9)
152 
153         // Set tmp1 = ( S_9,  S_6,  S_6,  S_9)
154         //        b < ( 1.6,  1.6,  1.6,  1.6)
155         tmp1 = tmp0.shuffle(Shuffle::DBBD);
156         // Set tmp1 = ( S_8,  S_5,  S_8,  S_5)
157         //        b < (2.33, 1.01, 2.33, 1.01)
158         tmp0 = tmp0.shuffle(Shuffle::CACA);
159 
160         // Bounds on (tmp0, tmp1) are (2.33, 1.6) < (2.5, 1.75).
161         ExtendedPoint(&tmp0 * &tmp1)
162     }
163 
mul_by_pow_2(&self, k: u32) -> ExtendedPoint164     pub fn mul_by_pow_2(&self, k: u32) -> ExtendedPoint {
165         let mut tmp: ExtendedPoint = *self;
166         for _ in 0..k {
167             tmp = tmp.double();
168         }
169         tmp
170     }
171 }
172 
173 /// A cached point with some precomputed variables used for readdition.
174 ///
175 /// # Warning
176 ///
177 /// It is not safe to negate this point more than once.
178 ///
179 /// # Invariant
180 ///
181 /// As long as the `CachedPoint` is not repeatedly negated, its
182 /// coefficients will be bounded with \\( b < 1.0 \\).
183 #[derive(Copy, Clone, Debug)]
184 pub struct CachedPoint(pub(super) FieldElement2625x4);
185 
186 impl From<ExtendedPoint> for CachedPoint {
from(P: ExtendedPoint) -> CachedPoint187     fn from(P: ExtendedPoint) -> CachedPoint {
188         let mut x = P.0;
189 
190         x = x.blend(x.diff_sum(), Lanes::AB);
191         // x = (X1 - Y1, X2 + Y2, Z2, T2) = (S2 S3 Z2 T2)
192 
193         x = x * (121666, 121666, 2 * 121666, 2 * 121665);
194         // x = (121666*S2 121666*S3 2*121666*Z2 2*121665*T2)
195 
196         x = x.blend(-x, Lanes::D);
197         // x = (121666*S2 121666*S3 2*121666*Z2 -2*121665*T2)
198 
199         // The coefficients of the output are bounded with b < 0.007.
200         CachedPoint(x)
201     }
202 }
203 
204 impl Default for CachedPoint {
default() -> CachedPoint205     fn default() -> CachedPoint {
206         CachedPoint::identity()
207     }
208 }
209 
210 impl Identity for CachedPoint {
identity() -> CachedPoint211     fn identity() -> CachedPoint {
212         constants::CACHEDPOINT_IDENTITY
213     }
214 }
215 
216 impl ConditionallySelectable for CachedPoint {
conditional_select(a: &Self, b: &Self, choice: Choice) -> Self217     fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
218         CachedPoint(FieldElement2625x4::conditional_select(&a.0, &b.0, choice))
219     }
220 
conditional_assign(&mut self, other: &Self, choice: Choice)221     fn conditional_assign(&mut self, other: &Self, choice: Choice) {
222         self.0.conditional_assign(&other.0, choice);
223     }
224 }
225 
226 impl<'a> Neg for &'a CachedPoint {
227     type Output = CachedPoint;
228     /// Lazily negate the point.
229     ///
230     /// # Warning
231     ///
232     /// Because this method does not perform a reduction, it is not
233     /// safe to repeatedly negate a point.
neg(self) -> CachedPoint234     fn neg(self) -> CachedPoint {
235         let swapped = self.0.shuffle(Shuffle::BACD);
236         CachedPoint(swapped.blend(swapped.negate_lazy(), Lanes::D))
237     }
238 }
239 
240 impl<'a, 'b> Add<&'b CachedPoint> for &'a ExtendedPoint {
241     type Output = ExtendedPoint;
242 
243     /// Add an `ExtendedPoint` and a `CachedPoint`.
add(self, other: &'b CachedPoint) -> ExtendedPoint244     fn add(self, other: &'b CachedPoint) -> ExtendedPoint {
245         // The coefficients of an `ExtendedPoint` are reduced after
246         // every operation.  If the `CachedPoint` was negated, its
247         // coefficients grow by one bit.  So on input, `self` is
248         // bounded with `b < 0.007` and `other` is bounded with
249         // `b < 1.0`.
250 
251         let mut tmp = self.0;
252 
253         tmp = tmp.blend(tmp.diff_sum(), Lanes::AB);
254         // tmp = (Y1-X1 Y1+X1 Z1 T1) = (S0 S1 Z1 T1) with b < 1.6
255 
256         // (tmp, other) bounded with b < (1.6, 1.0) < (2.5, 1.75).
257         tmp = &tmp * &other.0;
258         // tmp = (S0*S2' S1*S3' Z1*Z2' T1*T2') = (S8 S9 S10 S11)
259 
260         tmp = tmp.shuffle(Shuffle::ABDC);
261         // tmp = (S8 S9 S11 S10)
262 
263         tmp = tmp.diff_sum();
264         // tmp = (S9-S8 S9+S8 S10-S11 S10+S11) = (S12 S13 S14 S15)
265 
266         let t0 = tmp.shuffle(Shuffle::ADDA);
267         // t0 = (S12 S15 S15 S12)
268         let t1 = tmp.shuffle(Shuffle::CBCB);
269         // t1 = (S14 S13 S14 S13)
270 
271         // All coefficients of t0, t1 are bounded with b < 1.6.
272         // Return (S12*S14 S15*S13 S15*S14 S12*S13) = (X3 Y3 Z3 T3)
273         ExtendedPoint(&t0 * &t1)
274     }
275 }
276 
277 impl<'a, 'b> Sub<&'b CachedPoint> for &'a ExtendedPoint {
278     type Output = ExtendedPoint;
279 
280     /// Implement subtraction by negating the point and adding.
281     ///
282     /// Empirically, this seems about the same cost as a custom
283     /// subtraction impl (maybe because the benefit is cancelled by
284     /// increased code size?)
sub(self, other: &'b CachedPoint) -> ExtendedPoint285     fn sub(self, other: &'b CachedPoint) -> ExtendedPoint {
286         self + &(-other)
287     }
288 }
289 
290 impl<'a> From<&'a edwards::EdwardsPoint> for LookupTable<CachedPoint> {
from(point: &'a edwards::EdwardsPoint) -> Self291     fn from(point: &'a edwards::EdwardsPoint) -> Self {
292         let P = ExtendedPoint::from(*point);
293         let mut points = [CachedPoint::from(P); 8];
294         for i in 0..7 {
295             points[i + 1] = (&P + &points[i]).into();
296         }
297         LookupTable(points)
298     }
299 }
300 
301 impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable5<CachedPoint> {
from(point: &'a edwards::EdwardsPoint) -> Self302     fn from(point: &'a edwards::EdwardsPoint) -> Self {
303         let A = ExtendedPoint::from(*point);
304         let mut Ai = [CachedPoint::from(A); 8];
305         let A2 = A.double();
306         for i in 0..7 {
307             Ai[i + 1] = (&A2 + &Ai[i]).into();
308         }
309         // Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A]
310         NafLookupTable5(Ai)
311     }
312 }
313 
314 impl<'a> From<&'a edwards::EdwardsPoint> for NafLookupTable8<CachedPoint> {
from(point: &'a edwards::EdwardsPoint) -> Self315     fn from(point: &'a edwards::EdwardsPoint) -> Self {
316         let A = ExtendedPoint::from(*point);
317         let mut Ai = [CachedPoint::from(A); 64];
318         let A2 = A.double();
319         for i in 0..63 {
320             Ai[i + 1] = (&A2 + &Ai[i]).into();
321         }
322         // Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A, ..., 127A]
323         NafLookupTable8(Ai)
324     }
325 }
326 
327 #[cfg(test)]
328 mod test {
329     use super::*;
330 
serial_add(P: edwards::EdwardsPoint, Q: edwards::EdwardsPoint) -> edwards::EdwardsPoint331     fn serial_add(P: edwards::EdwardsPoint, Q: edwards::EdwardsPoint) -> edwards::EdwardsPoint {
332         use backend::serial::u64::field::FieldElement51;
333 
334         let (X1, Y1, Z1, T1) = (P.X, P.Y, P.Z, P.T);
335         let (X2, Y2, Z2, T2) = (Q.X, Q.Y, Q.Z, Q.T);
336 
337         macro_rules! print_var {
338             ($x:ident) => {
339                 println!("{} = {:?}", stringify!($x), $x.to_bytes());
340             };
341         }
342 
343         let S0 = &Y1 - &X1; // R1
344         let S1 = &Y1 + &X1; // R3
345         let S2 = &Y2 - &X2; // R2
346         let S3 = &Y2 + &X2; // R4
347         print_var!(S0);
348         print_var!(S1);
349         print_var!(S2);
350         print_var!(S3);
351         println!("");
352 
353         let S4 = &S0 * &S2; // R5 = R1 * R2
354         let S5 = &S1 * &S3; // R6 = R3 * R4
355         let S6 = &Z1 * &Z2; // R8
356         let S7 = &T1 * &T2; // R7
357         print_var!(S4);
358         print_var!(S5);
359         print_var!(S6);
360         print_var!(S7);
361         println!("");
362 
363         let S8  =  &S4 *    &FieldElement51([  121666,0,0,0,0]);  // R5
364         let S9  =  &S5 *    &FieldElement51([  121666,0,0,0,0]);  // R6
365         let S10 =  &S6 *    &FieldElement51([2*121666,0,0,0,0]);  // R8
366         let S11 =  &S7 * &(-&FieldElement51([2*121665,0,0,0,0])); // R7
367         print_var!(S8);
368         print_var!(S9);
369         print_var!(S10);
370         print_var!(S11);
371         println!("");
372 
373         let S12 =  &S9 - &S8;  // R1
374         let S13 =  &S9 + &S8;  // R4
375         let S14 = &S10 - &S11; // R2
376         let S15 = &S10 + &S11; // R3
377         print_var!(S12);
378         print_var!(S13);
379         print_var!(S14);
380         print_var!(S15);
381         println!("");
382 
383         let X3 = &S12 * &S14; // R1 * R2
384         let Y3 = &S15 * &S13; // R3 * R4
385         let Z3 = &S15 * &S14; // R2 * R3
386         let T3 = &S12 * &S13; // R1 * R4
387 
388         edwards::EdwardsPoint {
389             X: X3,
390             Y: Y3,
391             Z: Z3,
392             T: T3,
393         }
394     }
395 
addition_test_helper(P: edwards::EdwardsPoint, Q: edwards::EdwardsPoint)396     fn addition_test_helper(P: edwards::EdwardsPoint, Q: edwards::EdwardsPoint) {
397         // Test the serial implementation of the parallel addition formulas
398         let R_serial: edwards::EdwardsPoint = serial_add(P.into(), Q.into()).into();
399 
400         // Test the vector implementation of the parallel readdition formulas
401         let cached_Q = CachedPoint::from(ExtendedPoint::from(Q));
402         let R_vector: edwards::EdwardsPoint = (&ExtendedPoint::from(P) + &cached_Q).into();
403         let S_vector: edwards::EdwardsPoint = (&ExtendedPoint::from(P) - &cached_Q).into();
404 
405         println!("Testing point addition:");
406         println!("P = {:?}", P);
407         println!("Q = {:?}", Q);
408         println!("cached Q = {:?}", cached_Q);
409         println!("R = P + Q = {:?}", &P + &Q);
410         println!("R_serial = {:?}", R_serial);
411         println!("R_vector = {:?}", R_vector);
412         println!("S = P - Q = {:?}", &P - &Q);
413         println!("S_vector = {:?}", S_vector);
414         assert_eq!(R_serial.compress(), (&P + &Q).compress());
415         assert_eq!(R_vector.compress(), (&P + &Q).compress());
416         assert_eq!(S_vector.compress(), (&P - &Q).compress());
417         println!("OK!\n");
418     }
419 
420     #[test]
vector_addition_vs_serial_addition_vs_edwards_extendedpoint()421     fn vector_addition_vs_serial_addition_vs_edwards_extendedpoint() {
422         use constants;
423         use scalar::Scalar;
424 
425         println!("Testing id +- id");
426         let P = edwards::EdwardsPoint::identity();
427         let Q = edwards::EdwardsPoint::identity();
428         addition_test_helper(P, Q);
429 
430         println!("Testing id +- B");
431         let P = edwards::EdwardsPoint::identity();
432         let Q = constants::ED25519_BASEPOINT_POINT;
433         addition_test_helper(P, Q);
434 
435         println!("Testing B +- B");
436         let P = constants::ED25519_BASEPOINT_POINT;
437         let Q = constants::ED25519_BASEPOINT_POINT;
438         addition_test_helper(P, Q);
439 
440         println!("Testing B +- kB");
441         let P = constants::ED25519_BASEPOINT_POINT;
442         let Q = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64);
443         addition_test_helper(P, Q);
444     }
445 
serial_double(P: edwards::EdwardsPoint) -> edwards::EdwardsPoint446     fn serial_double(P: edwards::EdwardsPoint) -> edwards::EdwardsPoint {
447         let (X1, Y1, Z1, _T1) = (P.X, P.Y, P.Z, P.T);
448 
449         macro_rules! print_var {
450             ($x:ident) => {
451                 println!("{} = {:?}", stringify!($x), $x.to_bytes());
452             };
453         }
454 
455         let S0 = &X1 + &Y1; // R1
456         print_var!(S0);
457         println!("");
458 
459         let S1 = X1.square();
460         let S2 = Y1.square();
461         let S3 = Z1.square();
462         let S4 = S0.square();
463         print_var!(S1);
464         print_var!(S2);
465         print_var!(S3);
466         print_var!(S4);
467         println!("");
468 
469         let S5 = &S1 + &S2;
470         let S6 = &S1 - &S2;
471         let S7 = &S3 + &S3;
472         let S8 = &S7 + &S6;
473         let S9 = &S5 - &S4;
474         print_var!(S5);
475         print_var!(S6);
476         print_var!(S7);
477         print_var!(S8);
478         print_var!(S9);
479         println!("");
480 
481         let X3 = &S8 * &S9;
482         let Y3 = &S5 * &S6;
483         let Z3 = &S8 * &S6;
484         let T3 = &S5 * &S9;
485 
486         edwards::EdwardsPoint {
487             X: X3,
488             Y: Y3,
489             Z: Z3,
490             T: T3,
491         }
492     }
493 
doubling_test_helper(P: edwards::EdwardsPoint)494     fn doubling_test_helper(P: edwards::EdwardsPoint) {
495         let R1: edwards::EdwardsPoint = serial_double(P.into()).into();
496         let R2: edwards::EdwardsPoint = ExtendedPoint::from(P).double().into();
497         println!("Testing point doubling:");
498         println!("P = {:?}", P);
499         println!("(serial) R1 = {:?}", R1);
500         println!("(vector) R2 = {:?}", R2);
501         println!("P + P = {:?}", &P + &P);
502         assert_eq!(R1.compress(), (&P + &P).compress());
503         assert_eq!(R2.compress(), (&P + &P).compress());
504         println!("OK!\n");
505     }
506 
507     #[test]
vector_doubling_vs_serial_doubling_vs_edwards_extendedpoint()508     fn vector_doubling_vs_serial_doubling_vs_edwards_extendedpoint() {
509         use constants;
510         use scalar::Scalar;
511 
512         println!("Testing [2]id");
513         let P = edwards::EdwardsPoint::identity();
514         doubling_test_helper(P);
515 
516         println!("Testing [2]B");
517         let P = constants::ED25519_BASEPOINT_POINT;
518         doubling_test_helper(P);
519 
520         println!("Testing [2]([k]B)");
521         let P = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from(8475983829u64);
522         doubling_test_helper(P);
523     }
524 }
525