1mod glv;
38
39use super::{ProjectivePoint, scalar::Scalar};
40use core::array;
41use elliptic_curve::{
42 array::sizes::{U5, U33},
43 ops::{LinearCombination, Mul, MulAssign, MulByGeneratorVartime, MulVartime},
44 scalar::IsHigh,
45 subtle::ConditionallySelectable,
46};
47use primeorder::Radix16Decomposition;
48
49#[cfg(feature = "alloc")]
50use alloc::vec::Vec;
51#[cfg(feature = "precomputed-tables")]
52use {super::tables::BASEPOINT_TABLE, elliptic_curve::array::sizes::U65};
53
54type LookupTable = primeorder::LookupTable<ProjectivePoint>;
56
57type WnafWindowSize = U5;
59
60type WnafBase = wnaf::WnafBase<ProjectivePoint, WnafWindowSize>;
62
63type WnafScalar = wnaf::WnafScalar<Scalar, WnafWindowSize>;
65
66impl<const N: usize> LinearCombination<[(ProjectivePoint, Scalar); N]> for ProjectivePoint {
67 fn lincomb(points_and_scalars: &[(ProjectivePoint, Scalar); N]) -> Self {
68 let mut tables = [(LookupTable::default(), LookupTable::default()); N];
69 let mut digits: [(Radix16Decomposition<U33>, Radix16Decomposition<U33>); N] =
70 array::from_fn(|_| Default::default());
71 lincomb(points_and_scalars, &mut tables, &mut digits)
72 }
73
74 fn lincomb_vartime(points_and_scalars: &[(ProjectivePoint, Scalar); N]) -> Self {
75 let decomposed: [_; N] = array::from_fn(|i| {
76 let (x, k) = &points_and_scalars[i];
77 glv::decompose_wnaf(x, k)
78 });
79
80 lincomb_vartime_glv_wnaf(&decomposed)
81 }
82}
83
84impl LinearCombination<[(ProjectivePoint, Scalar)]> for ProjectivePoint {
85 #[cfg(feature = "alloc")]
86 fn lincomb(points_and_scalars: &[(ProjectivePoint, Scalar)]) -> Self {
87 let mut tables =
88 vec![(LookupTable::default(), LookupTable::default()); points_and_scalars.len()];
89 let mut digits = vec![
90 (
91 Radix16Decomposition::<U33>::default(),
92 Radix16Decomposition::<U33>::default(),
93 );
94 points_and_scalars.len()
95 ];
96
97 lincomb(points_and_scalars, &mut tables, &mut digits)
98 }
99
100 #[cfg(feature = "alloc")]
101 fn lincomb_vartime(points_and_scalars: &[(ProjectivePoint, Scalar)]) -> Self {
102 let decomposed: Vec<_> = points_and_scalars
103 .iter()
104 .map(|(x, k)| glv::decompose_wnaf(x, k))
105 .collect();
106
107 lincomb_vartime_glv_wnaf(&decomposed)
108 }
109}
110
111fn lincomb(
113 xks: &[(ProjectivePoint, Scalar)],
114 tables: &mut [(LookupTable, LookupTable)],
115 digits: &mut [(Radix16Decomposition<U33>, Radix16Decomposition<U33>)],
116) -> ProjectivePoint {
117 xks.iter().enumerate().for_each(|(i, (x, k))| {
118 let (r1, r2) = glv::decompose_scalar(k);
119 let x_beta = x.endomorphism();
120 let (r1_sign, r2_sign) = (r1.is_high(), r2.is_high());
121
122 let (r1_c, r2_c) = (
123 Scalar::conditional_select(&r1, &-r1, r1_sign),
124 Scalar::conditional_select(&r2, &-r2, r2_sign),
125 );
126
127 tables[i] = (
128 LookupTable::new(ProjectivePoint::conditional_select(x, &-*x, r1_sign)),
129 LookupTable::new(ProjectivePoint::conditional_select(
130 &x_beta, &-x_beta, r2_sign,
131 )),
132 );
133
134 digits[i] = (
135 Radix16Decomposition::<U33>::new(&r1_c),
136 Radix16Decomposition::<U33>::new(&r2_c),
137 );
138 });
139
140 let mut acc = ProjectivePoint::IDENTITY;
141 for component in 0..xks.len() {
142 let (digit1, digit2) = &digits[component];
143 let (table1, table2) = tables[component];
144
145 acc += &table1.select(digit1[32]);
146 acc += &table2.select(digit2[32]);
147 }
148
149 for i in (0..32).rev() {
150 for _j in 0..4 {
151 acc.double_in_place();
152 }
153
154 for component in 0..xks.len() {
155 let (digit1, digit2) = &digits[component];
156 let (table1, table2) = tables[component];
157
158 acc += &table1.select(digit1[i]);
159 acc += &table2.select(digit2[i]);
160 }
161 }
162 acc
163}
164
165fn lincomb_vartime_glv_wnaf(
168 decomposed_xks: &[([WnafBase; 2], [WnafScalar; 2])],
169) -> ProjectivePoint {
170 let terms = decomposed_xks
171 .iter()
172 .flat_map(|(bases, scalars)| bases.iter().zip(scalars.iter()));
173
174 WnafBase::multiscalar_mul(terms)
175}
176
177impl ProjectivePoint {
178 #[must_use]
180 pub fn mul_by_generator(k: &Scalar) -> ProjectivePoint {
181 #[cfg(feature = "precomputed-tables")]
182 {
183 let digits = Radix16Decomposition::<U65>::new(k);
184 let table = *BASEPOINT_TABLE;
185 let mut acc = table[32].select(digits[64]);
186 let mut acc2 = ProjectivePoint::IDENTITY;
187 for i in (0..32).rev() {
188 acc2 += &table[i].select(digits[i * 2 + 1]);
189 acc += &table[i].select(digits[i * 2]);
190 }
191 for _ in 0..4 {
194 acc2.double_in_place();
195 }
196 acc + acc2
197 }
198
199 #[cfg(not(feature = "precomputed-tables"))]
200 {
201 ProjectivePoint::GENERATOR * k
202 }
203 }
204
205 #[must_use]
207 pub fn mul_by_generator_vartime(k: &Scalar) -> ProjectivePoint {
208 #[cfg(feature = "precomputed-tables")]
209 {
210 let digits = Radix16Decomposition::<U65>::new(k);
211 let table = *BASEPOINT_TABLE;
212 let mut acc = table[32].select_vartime(digits[64]);
213 let mut acc2 = ProjectivePoint::IDENTITY;
214 for i in (0..32).rev() {
215 acc2 += &table[i].select_vartime(digits[i * 2 + 1]);
216 acc += &table[i].select_vartime(digits[i * 2]);
217 }
218
219 for _ in 0..4 {
222 acc2.double_in_place();
223 }
224
225 acc + acc2
226 }
227
228 #[cfg(not(feature = "precomputed-tables"))]
229 {
230 ProjectivePoint::GENERATOR.mul_vartime(k)
231 }
232 }
233}
234
235#[inline]
236fn mul(x: &ProjectivePoint, k: &Scalar) -> ProjectivePoint {
237 ProjectivePoint::lincomb(&[(*x, *k)])
238}
239
240#[inline]
242fn mul_vartime(x: &ProjectivePoint, k: &Scalar) -> ProjectivePoint {
243 let mut bases = [WnafBase::default(), WnafBase::default()];
244 let mut scalars = [WnafScalar::default(), WnafScalar::default()];
245 glv::decompose_wnaf_into(x, k, &mut bases, &mut scalars);
246 WnafBase::multiscalar_mul([(&bases[0], &scalars[0]), (&bases[1], &scalars[1])].into_iter())
247}
248
249impl Mul<Scalar> for ProjectivePoint {
250 type Output = ProjectivePoint;
251
252 #[inline]
253 fn mul(self, other: Scalar) -> ProjectivePoint {
254 mul(&self, &other)
255 }
256}
257
258impl Mul<&Scalar> for &ProjectivePoint {
259 type Output = ProjectivePoint;
260
261 #[inline]
262 fn mul(self, other: &Scalar) -> ProjectivePoint {
263 mul(self, other)
264 }
265}
266
267impl Mul<&Scalar> for ProjectivePoint {
268 type Output = ProjectivePoint;
269
270 #[inline]
271 fn mul(self, other: &Scalar) -> ProjectivePoint {
272 mul(&self, other)
273 }
274}
275
276impl MulVartime<Scalar> for ProjectivePoint {
277 #[inline]
278 fn mul_vartime(self, other: Scalar) -> ProjectivePoint {
279 mul_vartime(&self, &other)
280 }
281}
282
283impl MulVartime<&Scalar> for &ProjectivePoint {
284 #[inline]
285 fn mul_vartime(self, other: &Scalar) -> ProjectivePoint {
286 mul_vartime(self, other)
287 }
288}
289
290impl MulVartime<&Scalar> for ProjectivePoint {
291 #[inline]
292 fn mul_vartime(self, other: &Scalar) -> ProjectivePoint {
293 mul_vartime(&self, other)
294 }
295}
296
297impl MulByGeneratorVartime for ProjectivePoint {
298 #[inline]
299 fn mul_by_generator_vartime(k: &Scalar) -> ProjectivePoint {
300 Self::mul_by_generator_vartime(k)
301 }
302
303 fn mul_by_generator_and_mul_add_vartime(a: &Self::Scalar, b: &Self::Scalar, p: &Self) -> Self {
304 let decomposed = [
305 glv::decompose_wnaf(&ProjectivePoint::GENERATOR, a),
306 glv::decompose_wnaf(p, b),
307 ];
308
309 lincomb_vartime_glv_wnaf(&decomposed)
310 }
311}
312
313impl MulAssign<Scalar> for ProjectivePoint {
314 fn mul_assign(&mut self, rhs: Scalar) {
315 *self = mul(self, &rhs);
316 }
317}
318
319impl MulAssign<&Scalar> for ProjectivePoint {
320 fn mul_assign(&mut self, rhs: &Scalar) {
321 *self = mul(self, rhs);
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328 use crate::arithmetic::{ProjectivePoint, Scalar};
329
330 #[cfg(feature = "getrandom")]
331 use elliptic_curve::Generate;
332
333 #[test]
334 #[cfg(feature = "getrandom")]
335 fn test_lincomb() {
336 let x = ProjectivePoint::generate();
337 let y = ProjectivePoint::generate();
338 let k = Scalar::generate();
339 let l = Scalar::generate();
340
341 let reference = x * k + y * l;
342 let test = ProjectivePoint::lincomb(&[(x, k), (y, l)]);
343 assert_eq!(reference, test);
344 }
345
346 #[test]
347 #[cfg(feature = "getrandom")]
348 fn test_mul_by_generator() {
349 let k = Scalar::generate();
350 let reference = ProjectivePoint::GENERATOR * k;
351 let test = ProjectivePoint::mul_by_generator(&k);
352 assert_eq!(reference, test);
353 }
354
355 #[test]
356 fn test_mul_vartime() {
357 let p = ProjectivePoint::GENERATOR;
358 assert_eq!(p.mul(&Scalar::ZERO), ProjectivePoint::IDENTITY);
359 assert_eq!(p.mul(&Scalar::ONE), p);
360 assert_eq!(p.mul(&-Scalar::ONE), -p);
361 assert_eq!(
362 ProjectivePoint::IDENTITY.mul(&Scalar::ONE),
363 ProjectivePoint::IDENTITY
364 );
365 }
366
367 #[cfg(all(feature = "alloc", feature = "getrandom"))]
368 #[test]
369 fn test_lincomb_slice() {
370 let x = ProjectivePoint::generate();
371 let y = ProjectivePoint::generate();
372 let k = Scalar::generate();
373 let l = Scalar::generate();
374
375 let reference = x * k + y * l;
376 let points_and_scalars = vec![(x, k), (y, l)];
377
378 let test = ProjectivePoint::lincomb(points_and_scalars.as_slice());
379 assert_eq!(reference, test);
380 }
381}