Skip to content

Commit b370f33

Browse files
committed
wip
1 parent 944b5c2 commit b370f33

File tree

1 file changed

+26
-9
lines changed

1 file changed

+26
-9
lines changed

src/util.rs

+26-9
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,10 @@ pub fn scalar_exp_vartime(x: &Scalar, mut n: u64) -> Scalar {
103103
}
104104

105105
/// Computes the sum of all the powers of \\(x\\) \\(S(n) = (x^0 + \dots + x^{n-1})\\)
106-
/// using \\(O(\lg n)\\) multiplications and additions. Length \\(n\\) is not considered secret
107-
/// and algorithm is fastest when \\(n\\) is the power of two.
106+
/// using \\(O(\lg n)\\) multiplications. Length \\(n\\) is not considered secret
107+
/// and algorithm is fastest when \\(n\\) is the power of two (\\(2\lg n + 1\\) multiplications).
108108
///
109-
/// ### Algorithm overview
109+
/// ### Algorithm description
110110
///
111111
/// First, let \\(n\\) be a power of two.
112112
/// Then, we can divide the polynomial in two halves like so:
@@ -127,9 +127,26 @@ pub fn scalar_exp_vartime(x: &Scalar, mut n: u64) -> Scalar {
127127
/// s_i &= s_{i-1} + x^{2^{i-1}} s_{i-1}
128128
/// \end{aligned}
129129
/// \\]
130-
/// This representation allows us to square \\(x\\) only \\(\lg n\\) times.
130+
/// This representation allows us to do only \\(2 \cdot \lg n\\) multiplications:
131+
/// squaring \\(x\\) and multiplying it by \\(s_{i-1}\\) at each iteration.
131132
///
132-
/// Lets apply this to \\(n\\) which is not a power of two (\\(2^{k-1} < n < 2^k\\)) which can be represented in binary using
133+
/// Lets apply this to \\(n\\) which is not a power of two. The intuition behind the generalized
134+
/// algorithm is to combine all intermediate power-of-two-degree polynomials corresponding to the
135+
/// bits of \\(n\\) that are equal to 1.
136+
///
137+
/// 1. Represent \\(n\\) in binary.
138+
/// 2. For each bit which is set (from the lowest to the highest):
139+
/// 1. Compute a corresponding power-of-two-degree polynomial using the above algorithm.
140+
/// Since we can reuse all intermediate polynomials, this adds no overhead to computing
141+
/// a polynomial for the highest bit.
142+
/// 2. Multiply the polynomial by the next power of \\(x\\), relative to the degree of the
143+
/// already computed result. This effectively _offsets_ the polynomial to a correct range of
144+
/// powers, so it can be added directly with the rest.
145+
/// The next power of \\(x\\) is computed along all the intermediate polynomials,
146+
/// by multiplying it by power-of-two power of \\(x\\) computed in step 2.1.
147+
/// 3. Add to the result.
148+
///
149+
/// (\\(2^{k-1} < n < 2^k\\)) which can be represented in binary using
133150
/// bits \\(b_i\\) in \\(\\{0,1\\}\\):
134151
/// \\[
135152
/// n = b_0 2^0 + \dots + b_{k-1} 2^{k-1}
@@ -151,16 +168,16 @@ pub fn scalar_exp_vartime(x: &Scalar, mut n: u64) -> Scalar {
151168
/// \\]
152169
pub fn sum_of_powers(x: &Scalar, mut n: usize) -> Scalar {
153170
let mut result = Scalar::zero();
154-
let mut f = Scalar::one(); // power of x to offset subsequent polynomials based on lower bits of n.
155-
let mut s = Scalar::one(); // power-of-two polynomial: 1, 1+x, 1+x+x^2+x^3, ...
156-
let mut p = *x; // x, x^2, x^4, ..., x^{2^i}
171+
let mut f = Scalar::one(); // next-power-of-x to offset subsequent polynomials based on preceding bits of n.
172+
let mut s = Scalar::one(); // power-of-two polynomials: (1, 1+x, 1+x+x^2+x^3, 1+...+x^7, , 1+...+x^15, ...)
173+
let mut p = *x; // power-of-two powers of x: (x, x^2, x^4, ..., x^{2^i})
157174
while n > 0 {
158175
// take a bit from n
159176
let bit = n & 1;
160177
n = n >> 1;
161178

162179
if bit == 1 {
163-
// bits of `n` are not secret, so it's okay to be vartime because of `n` value.
180+
// `n` is not secret, so it's okay to be vartime on bits of `n`.
164181
result += f * s;
165182
if n > 0 { // avoid multiplication if no bits left
166183
f = f * p;

0 commit comments

Comments
 (0)