Skip to content

Commit 90f3477

Browse files
committed
Add an optimized function for linear combination of two points (x * k + y * l)
1 parent 2c12fc5 commit 90f3477

File tree

1 file changed

+75
-0
lines changed

1 file changed

+75
-0
lines changed

k256/src/arithmetic/mul.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,61 @@ fn mul_windowed(x: &ProjectivePoint, k: &Scalar) -> ProjectivePoint {
203203
acc
204204
}
205205

206+
/// Calculates `(x * k + y * l)`.
207+
pub fn lincomb(
208+
x: &ProjectivePoint,
209+
y: &ProjectivePoint,
210+
k: &Scalar,
211+
l: &Scalar,
212+
) -> ProjectivePoint {
213+
let (k_r1, k_r2) = decompose_scalar(k);
214+
let (l_r1, l_r2) = decompose_scalar(l);
215+
let x_beta = x.endomorphism();
216+
let y_beta = y.endomorphism();
217+
218+
let k_r1_sign = k_r1.is_high();
219+
let k_r1_c = Scalar::conditional_select(&k_r1, &-k_r1, k_r1_sign);
220+
let k_r2_sign = k_r2.is_high();
221+
let k_r2_c = Scalar::conditional_select(&k_r2, &-k_r2, k_r2_sign);
222+
223+
let l_r1_sign = l_r1.is_high();
224+
let l_r1_c = Scalar::conditional_select(&l_r1, &-l_r1, l_r1_sign);
225+
let l_r2_sign = l_r2.is_high();
226+
let l_r2_c = Scalar::conditional_select(&l_r2, &-l_r2, l_r2_sign);
227+
228+
let x_table1 = LookupTable::from(&ProjectivePoint::conditional_select(x, &-x, k_r1_sign));
229+
let x_table2 = LookupTable::from(&ProjectivePoint::conditional_select(
230+
&x_beta, &-x_beta, k_r2_sign,
231+
));
232+
233+
let y_table1 = LookupTable::from(&ProjectivePoint::conditional_select(y, &-y, l_r1_sign));
234+
let y_table2 = LookupTable::from(&ProjectivePoint::conditional_select(
235+
&y_beta, &-y_beta, l_r2_sign,
236+
));
237+
238+
let k_digits1 = to_radix_16_half(&k_r1_c);
239+
let k_digits2 = to_radix_16_half(&k_r2_c);
240+
241+
let l_digits1 = to_radix_16_half(&l_r1_c);
242+
let l_digits2 = to_radix_16_half(&l_r2_c);
243+
244+
let mut acc = x_table1.select(k_digits1[32])
245+
+ x_table2.select(k_digits2[32])
246+
+ y_table1.select(l_digits1[32])
247+
+ y_table2.select(l_digits2[32]);
248+
for i in (0..32).rev() {
249+
for _j in 0..4 {
250+
acc = acc.double();
251+
}
252+
253+
acc += &x_table1.select(k_digits1[i]);
254+
acc += &x_table2.select(k_digits2[i]);
255+
acc += &y_table1.select(l_digits1[i]);
256+
acc += &y_table2.select(l_digits2[i]);
257+
}
258+
acc
259+
}
260+
206261
impl Mul<Scalar> for ProjectivePoint {
207262
type Output = ProjectivePoint;
208263

@@ -238,3 +293,23 @@ impl MulAssign<&Scalar> for ProjectivePoint {
238293
*self = mul_windowed(self, rhs);
239294
}
240295
}
296+
297+
#[cfg(test)]
298+
mod tests {
299+
use super::lincomb;
300+
use crate::arithmetic::{ProjectivePoint, Scalar};
301+
use elliptic_curve::rand_core::OsRng;
302+
use elliptic_curve::{Field, Group};
303+
304+
#[test]
305+
fn test_lincomb() {
306+
let x = ProjectivePoint::random(&mut OsRng);
307+
let y = ProjectivePoint::random(&mut OsRng);
308+
let k = Scalar::random(&mut OsRng);
309+
let l = Scalar::random(&mut OsRng);
310+
311+
let reference = &x * &k + &y * &l;
312+
let test = lincomb(&x, &y, &k, &l);
313+
assert_eq!(reference, test);
314+
}
315+
}

0 commit comments

Comments
 (0)