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 //! Implementation of the interleaved window method, also known as Straus' method.
12 
13 #![allow(non_snake_case)]
14 
15 use core::borrow::Borrow;
16 
17 use edwards::EdwardsPoint;
18 use scalar::Scalar;
19 use traits::MultiscalarMul;
20 use traits::VartimeMultiscalarMul;
21 
22 #[allow(unused_imports)]
23 use prelude::*;
24 
25 /// Perform multiscalar multiplication by the interleaved window
26 /// method, also known as Straus' method (since it was apparently
27 /// [first published][solution] by Straus in 1964, as a solution to [a
28 /// problem][problem] posted in the American Mathematical Monthly in
29 /// 1963).
30 ///
31 /// It is easy enough to reinvent, and has been repeatedly.  The basic
32 /// idea is that when computing
33 /// \\[
34 /// Q = s_1 P_1 + \cdots + s_n P_n
35 /// \\]
36 /// by means of additions and doublings, the doublings can be shared
37 /// across the \\( P_i \\\).
38 ///
39 /// We implement two versions, a constant-time algorithm using fixed
40 /// windows and a variable-time algorithm using sliding windows.  They
41 /// are slight variations on the same idea, and are described in more
42 /// detail in the respective implementations.
43 ///
44 /// [solution]: https://www.jstor.org/stable/2310929
45 /// [problem]: https://www.jstor.org/stable/2312273
46 pub struct Straus {}
47 
48 impl MultiscalarMul for Straus {
49     type Point = EdwardsPoint;
50 
51     /// Constant-time Straus using a fixed window of size \\(4\\).
52     ///
53     /// Our goal is to compute
54     /// \\[
55     /// Q = s_1 P_1 + \cdots + s_n P_n.
56     /// \\]
57     ///
58     /// For each point \\( P_i \\), precompute a lookup table of
59     /// \\[
60     /// P_i, 2P_i, 3P_i, 4P_i, 5P_i, 6P_i, 7P_i, 8P_i.
61     /// \\]
62     ///
63     /// For each scalar \\( s_i \\), compute its radix-\\(2^4\\)
64     /// signed digits \\( s_{i,j} \\), i.e.,
65     /// \\[
66     ///    s_i = s_{i,0} + s_{i,1} 16^1 + ... + s_{i,63} 16^{63},
67     /// \\]
68     /// with \\( -8 \leq s_{i,j} < 8 \\).  Since \\( 0 \leq |s_{i,j}|
69     /// \leq 8 \\), we can retrieve \\( s_{i,j} P_i \\) from the
70     /// lookup table with a conditional negation: using signed
71     /// digits halves the required table size.
72     ///
73     /// Then as in the single-base fixed window case, we have
74     /// \\[
75     /// \begin{aligned}
76     /// s_i P_i &= P_i (s_{i,0} +     s_{i,1} 16^1 + \cdots +     s_{i,63} 16^{63})   \\\\
77     /// s_i P_i &= P_i s_{i,0} + P_i s_{i,1} 16^1 + \cdots + P_i s_{i,63} 16^{63}     \\\\
78     /// s_i P_i &= P_i s_{i,0} + 16(P_i s_{i,1} + 16( \cdots +16P_i s_{i,63})\cdots )
79     /// \end{aligned}
80     /// \\]
81     /// so each \\( s_i P_i \\) can be computed by alternately adding
82     /// a precomputed multiple \\( P_i s_{i,j} \\) of \\( P_i \\) and
83     /// repeatedly doubling.
84     ///
85     /// Now consider the two-dimensional sum
86     /// \\[
87     /// \begin{aligned}
88     /// s\_1 P\_1 &=& P\_1 s\_{1,0} &+& 16 (P\_1 s\_{1,1} &+& 16 ( \cdots &+& 16 P\_1 s\_{1,63}&) \cdots ) \\\\
89     ///     +     & &      +        & &      +            & &             & &     +            &           \\\\
90     /// s\_2 P\_2 &=& P\_2 s\_{2,0} &+& 16 (P\_2 s\_{2,1} &+& 16 ( \cdots &+& 16 P\_2 s\_{2,63}&) \cdots ) \\\\
91     ///     +     & &      +        & &      +            & &             & &     +            &           \\\\
92     /// \vdots    & &  \vdots       & &   \vdots          & &             & &  \vdots          &           \\\\
93     ///     +     & &      +        & &      +            & &             & &     +            &           \\\\
94     /// s\_n P\_n &=& P\_n s\_{n,0} &+& 16 (P\_n s\_{n,1} &+& 16 ( \cdots &+& 16 P\_n s\_{n,63}&) \cdots )
95     /// \end{aligned}
96     /// \\]
97     /// The sum of the left-hand column is the result \\( Q \\); by
98     /// computing the two-dimensional sum on the right column-wise,
99     /// top-to-bottom, then right-to-left, we need to multiply by \\(
100     /// 16\\) only once per column, sharing the doublings across all
101     /// of the input points.
multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint where I: IntoIterator, I::Item: Borrow<Scalar>, J: IntoIterator, J::Item: Borrow<EdwardsPoint>,102     fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
103     where
104         I: IntoIterator,
105         I::Item: Borrow<Scalar>,
106         J: IntoIterator,
107         J::Item: Borrow<EdwardsPoint>,
108     {
109         use zeroize::Zeroizing;
110 
111         use backend::serial::curve_models::ProjectiveNielsPoint;
112         use window::LookupTable;
113         use traits::Identity;
114 
115         let lookup_tables: Vec<_> = points
116             .into_iter()
117             .map(|point| LookupTable::<ProjectiveNielsPoint>::from(point.borrow()))
118             .collect();
119 
120         // This puts the scalar digits into a heap-allocated Vec.
121         // To ensure that these are erased, pass ownership of the Vec into a
122         // Zeroizing wrapper.
123         let scalar_digits_vec: Vec<_> = scalars
124             .into_iter()
125             .map(|s| s.borrow().to_radix_16())
126             .collect();
127         let scalar_digits = Zeroizing::new(scalar_digits_vec);
128 
129         let mut Q = EdwardsPoint::identity();
130         for j in (0..64).rev() {
131             Q = Q.mul_by_pow_2(4);
132             let it = scalar_digits.iter().zip(lookup_tables.iter());
133             for (s_i, lookup_table_i) in it {
134                 // R_i = s_{i,j} * P_i
135                 let R_i = lookup_table_i.select(s_i[j]);
136                 // Q = Q + R_i
137                 Q = (&Q + &R_i).to_extended();
138             }
139         }
140 
141         Q
142     }
143 }
144 
145 impl VartimeMultiscalarMul for Straus {
146     type Point = EdwardsPoint;
147 
148     /// Variable-time Straus using a non-adjacent form of width \\(5\\).
149     ///
150     /// This is completely similar to the constant-time code, but we
151     /// use a non-adjacent form for the scalar, and do not do table
152     /// lookups in constant time.
153     ///
154     /// The non-adjacent form has signed, odd digits.  Using only odd
155     /// digits halves the table size (since we only need odd
156     /// multiples), or gives fewer additions for the same table size.
optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint> where I: IntoIterator, I::Item: Borrow<Scalar>, J: IntoIterator<Item = Option<EdwardsPoint>>,157     fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
158     where
159         I: IntoIterator,
160         I::Item: Borrow<Scalar>,
161         J: IntoIterator<Item = Option<EdwardsPoint>>,
162     {
163         use backend::serial::curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint};
164         use window::NafLookupTable5;
165         use traits::Identity;
166 
167         let nafs: Vec<_> = scalars
168             .into_iter()
169             .map(|c| c.borrow().non_adjacent_form(5))
170             .collect();
171 
172         let lookup_tables = points
173             .into_iter()
174             .map(|P_opt| P_opt.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::from(&P)))
175             .collect::<Option<Vec<_>>>()?;
176 
177         let mut r = ProjectivePoint::identity();
178 
179         for i in (0..256).rev() {
180             let mut t: CompletedPoint = r.double();
181 
182             for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
183                 if naf[i] > 0 {
184                     t = &t.to_extended() + &lookup_table.select(naf[i] as usize);
185                 } else if naf[i] < 0 {
186                     t = &t.to_extended() - &lookup_table.select(-naf[i] as usize);
187                 }
188             }
189 
190             r = t.to_projective();
191         }
192 
193         Some(r.to_extended())
194     }
195 }
196