1313
1414#![ allow( non_snake_case) ]
1515
16+ #[ cfg( feature = "alloc" ) ]
1617use alloc:: vec:: Vec ;
1718
19+ #[ cfg( feature = "alloc" ) ]
1820use core:: borrow:: Borrow ;
19- use core:: cmp:: Ordering ;
2021
22+ use crate :: backend:: serial:: curve_models:: ProjectiveNielsPoint ;
2123use crate :: edwards:: EdwardsPoint ;
2224use crate :: scalar:: Scalar ;
25+ use crate :: traits:: Identity ;
2326use crate :: traits:: MultiscalarMul ;
27+ #[ cfg( feature = "alloc" ) ]
2428use crate :: traits:: VartimeMultiscalarMul ;
29+ use crate :: window:: LookupTable ;
2530
2631/// Perform multiscalar multiplication by the interleaved window
2732/// method, also known as Straus' method (since it was apparently
@@ -49,68 +54,26 @@ pub struct Straus {}
4954impl MultiscalarMul for Straus {
5055 type Point = EdwardsPoint ;
5156
52- /// Constant-time Straus using a fixed window of size \\(4\\).
53- ///
54- /// Our goal is to compute
55- /// \\[
56- /// Q = s_1 P_1 + \cdots + s_n P_n.
57- /// \\]
58- ///
59- /// For each point \\( P_i \\), precompute a lookup table of
60- /// \\[
61- /// P_i, 2P_i, 3P_i, 4P_i, 5P_i, 6P_i, 7P_i, 8P_i.
62- /// \\]
63- ///
64- /// For each scalar \\( s_i \\), compute its radix-\\(2^4\\)
65- /// signed digits \\( s_{i,j} \\), i.e.,
66- /// \\[
67- /// s_i = s_{i,0} + s_{i,1} 16^1 + ... + s_{i,63} 16^{63},
68- /// \\]
69- /// with \\( -8 \leq s_{i,j} < 8 \\). Since \\( 0 \leq |s_{i,j}|
70- /// \leq 8 \\), we can retrieve \\( s_{i,j} P_i \\) from the
71- /// lookup table with a conditional negation: using signed
72- /// digits halves the required table size.
73- ///
74- /// Then as in the single-base fixed window case, we have
75- /// \\[
76- /// \begin{aligned}
77- /// s_i P_i &= P_i (s_{i,0} + s_{i,1} 16^1 + \cdots + s_{i,63} 16^{63}) \\\\
78- /// s_i P_i &= P_i s_{i,0} + P_i s_{i,1} 16^1 + \cdots + P_i s_{i,63} 16^{63} \\\\
79- /// s_i P_i &= P_i s_{i,0} + 16(P_i s_{i,1} + 16( \cdots +16P_i s_{i,63})\cdots )
80- /// \end{aligned}
81- /// \\]
82- /// so each \\( s_i P_i \\) can be computed by alternately adding
83- /// a precomputed multiple \\( P_i s_{i,j} \\) of \\( P_i \\) and
84- /// repeatedly doubling.
85- ///
86- /// Now consider the two-dimensional sum
87- /// \\[
88- /// \begin{aligned}
89- /// s\_1 P\_1 &=& P\_1 s\_{1,0} &+& 16 (P\_1 s\_{1,1} &+& 16 ( \cdots &+& 16 P\_1 s\_{1,63}&) \cdots ) \\\\
90- /// + & & + & & + & & & & + & \\\\
91- /// s\_2 P\_2 &=& P\_2 s\_{2,0} &+& 16 (P\_2 s\_{2,1} &+& 16 ( \cdots &+& 16 P\_2 s\_{2,63}&) \cdots ) \\\\
92- /// + & & + & & + & & & & + & \\\\
93- /// \vdots & & \vdots & & \vdots & & & & \vdots & \\\\
94- /// + & & + & & + & & & & + & \\\\
95- /// s\_n P\_n &=& P\_n s\_{n,0} &+& 16 (P\_n s\_{n,1} &+& 16 ( \cdots &+& 16 P\_n s\_{n,63}&) \cdots )
96- /// \end{aligned}
97- /// \\]
98- /// The sum of the left-hand column is the result \\( Q \\); by
99- /// computing the two-dimensional sum on the right column-wise,
100- /// top-to-bottom, then right-to-left, we need to multiply by \\(
101- /// 16\\) only once per column, sharing the doublings across all
102- /// of the input points.
103- fn multiscalar_mul < I , J > ( scalars : I , points : J ) -> EdwardsPoint
57+ fn multiscalar_mul < const N : usize > (
58+ scalars : & [ Scalar ; N ] ,
59+ points : & [ EdwardsPoint ; N ] ,
60+ ) -> EdwardsPoint {
61+ let lookup_tables: [ _ ; N ] =
62+ core:: array:: from_fn ( |index| LookupTable :: < ProjectiveNielsPoint > :: from ( & points[ index] ) ) ;
63+
64+ let scalar_digits: [ _ ; N ] = core:: array:: from_fn ( |index| scalars[ index] . as_radix_16 ( ) ) ;
65+
66+ multiscalar_mul ( & scalar_digits, & lookup_tables)
67+ }
68+
69+ #[ cfg( feature = "alloc" ) ]
70+ fn multiscalar_mul_alloc < I , J > ( scalars : I , points : J ) -> EdwardsPoint
10471 where
10572 I : IntoIterator ,
10673 I :: Item : Borrow < Scalar > ,
10774 J : IntoIterator ,
10875 J :: Item : Borrow < EdwardsPoint > ,
10976 {
110- use crate :: backend:: serial:: curve_models:: ProjectiveNielsPoint ;
111- use crate :: traits:: Identity ;
112- use crate :: window:: LookupTable ;
113-
11477 let lookup_tables: Vec < _ > = points
11578 . into_iter ( )
11679 . map ( |point| LookupTable :: < ProjectiveNielsPoint > :: from ( point. borrow ( ) ) )
@@ -125,25 +88,86 @@ impl MultiscalarMul for Straus {
12588 . map ( |s| s. borrow ( ) . as_radix_16 ( ) )
12689 . collect ( ) ;
12790
128- let mut Q = EdwardsPoint :: identity ( ) ;
129- for j in ( 0 ..64 ) . rev ( ) {
130- Q = Q . mul_by_pow_2 ( 4 ) ;
131- let it = scalar_digits. iter ( ) . zip ( lookup_tables. iter ( ) ) ;
132- for ( s_i, lookup_table_i) in it {
133- // R_i = s_{i,j} * P_i
134- let R_i = lookup_table_i. select ( s_i[ j] ) ;
135- // Q = Q + R_i
136- Q = ( & Q + & R_i ) . as_extended ( ) ;
137- }
138- }
91+ let Q = multiscalar_mul ( & scalar_digits, & lookup_tables) ;
13992
14093 #[ cfg( feature = "zeroize" ) ]
141- zeroize:: Zeroize :: zeroize ( & mut scalar_digits) ;
94+ zeroize:: Zeroize :: zeroize ( & mut scalar_digits. iter_mut ( ) ) ;
14295
14396 Q
14497 }
14598}
14699
100+ /// Constant-time Straus using a fixed window of size \\(4\\).
101+ ///
102+ /// Our goal is to compute
103+ /// \\[
104+ /// Q = s_1 P_1 + \cdots + s_n P_n.
105+ /// \\]
106+ ///
107+ /// For each point \\( P_i \\), precompute a lookup table of
108+ /// \\[
109+ /// P_i, 2P_i, 3P_i, 4P_i, 5P_i, 6P_i, 7P_i, 8P_i.
110+ /// \\]
111+ ///
112+ /// For each scalar \\( s_i \\), compute its radix-\\(2^4\\)
113+ /// signed digits \\( s_{i,j} \\), i.e.,
114+ /// \\[
115+ /// s_i = s_{i,0} + s_{i,1} 16^1 + ... + s_{i,63} 16^{63},
116+ /// \\]
117+ /// with \\( -8 \leq s_{i,j} < 8 \\). Since \\( 0 \leq |s_{i,j}|
118+ /// \leq 8 \\), we can retrieve \\( s_{i,j} P_i \\) from the
119+ /// lookup table with a conditional negation: using signed
120+ /// digits halves the required table size.
121+ ///
122+ /// Then as in the single-base fixed window case, we have
123+ /// \\[
124+ /// \begin{aligned}
125+ /// s_i P_i &= P_i (s_{i,0} + s_{i,1} 16^1 + \cdots + s_{i,63} 16^{63}) \\\\
126+ /// s_i P_i &= P_i s_{i,0} + P_i s_{i,1} 16^1 + \cdots + P_i s_{i,63} 16^{63} \\\\
127+ /// s_i P_i &= P_i s_{i,0} + 16(P_i s_{i,1} + 16( \cdots +16P_i s_{i,63})\cdots )
128+ /// \end{aligned}
129+ /// \\]
130+ /// so each \\( s_i P_i \\) can be computed by alternately adding
131+ /// a precomputed multiple \\( P_i s_{i,j} \\) of \\( P_i \\) and
132+ /// repeatedly doubling.
133+ ///
134+ /// Now consider the two-dimensional sum
135+ /// \\[
136+ /// \begin{aligned}
137+ /// s\_1 P\_1 &=& P\_1 s\_{1,0} &+& 16 (P\_1 s\_{1,1} &+& 16 ( \cdots &+& 16 P\_1 s\_{1,63}&) \cdots ) \\\\
138+ /// + & & + & & + & & & & + & \\\\
139+ /// s\_2 P\_2 &=& P\_2 s\_{2,0} &+& 16 (P\_2 s\_{2,1} &+& 16 ( \cdots &+& 16 P\_2 s\_{2,63}&) \cdots ) \\\\
140+ /// + & & + & & + & & & & + & \\\\
141+ /// \vdots & & \vdots & & \vdots & & & & \vdots & \\\\
142+ /// + & & + & & + & & & & + & \\\\
143+ /// s\_n P\_n &=& P\_n s\_{n,0} &+& 16 (P\_n s\_{n,1} &+& 16 ( \cdots &+& 16 P\_n s\_{n,63}&) \cdots )
144+ /// \end{aligned}
145+ /// \\]
146+ /// The sum of the left-hand column is the result \\( Q \\); by
147+ /// computing the two-dimensional sum on the right column-wise,
148+ /// top-to-bottom, then right-to-left, we need to multiply by \\(
149+ /// 16\\) only once per column, sharing the doublings across all
150+ /// of the input points.
151+ fn multiscalar_mul (
152+ scalar_digits : & [ [ i8 ; 64 ] ] ,
153+ lookup_tables : & [ LookupTable < ProjectiveNielsPoint > ] ,
154+ ) -> EdwardsPoint {
155+ let mut Q = EdwardsPoint :: identity ( ) ;
156+ for j in ( 0 ..64 ) . rev ( ) {
157+ Q = Q . mul_by_pow_2 ( 4 ) ;
158+ let it = scalar_digits. iter ( ) . zip ( lookup_tables. iter ( ) ) ;
159+ for ( s_i, lookup_table_i) in it {
160+ // R_i = s_{i,j} * P_i
161+ let R_i = lookup_table_i. select ( s_i[ j] ) ;
162+ // Q = Q + R_i
163+ Q = ( & Q + & R_i ) . as_extended ( ) ;
164+ }
165+ }
166+
167+ Q
168+ }
169+
170+ #[ cfg( feature = "alloc" ) ]
147171impl VartimeMultiscalarMul for Straus {
148172 type Point = EdwardsPoint ;
149173
@@ -167,6 +191,7 @@ impl VartimeMultiscalarMul for Straus {
167191 } ;
168192 use crate :: traits:: Identity ;
169193 use crate :: window:: NafLookupTable5 ;
194+ use core:: cmp:: Ordering ;
170195
171196 let nafs: Vec < _ > = scalars
172197 . into_iter ( )
0 commit comments