Skip to content

Commit 2a6865f

Browse files
committed
Add lincomb() as an alias for a 2-point linear combination
1 parent a85e930 commit 2a6865f

File tree

6 files changed

+55
-5
lines changed

6 files changed

+55
-5
lines changed

k256/bench/scalar.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use criterion::{
66
use hex_literal::hex;
77
use k256::{
88
elliptic_curve::{generic_array::arr, group::ff::PrimeField},
9-
ProjectivePoint, Scalar,
9+
lincomb, ProjectivePoint, Scalar,
1010
};
1111

1212
fn test_scalar_x() -> Scalar {
@@ -34,9 +34,18 @@ fn bench_point_mul<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) {
3434
group.bench_function("point-scalar mul", |b| b.iter(|| &p * &s));
3535
}
3636

37+
fn bench_point_lincomb<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) {
38+
let p = ProjectivePoint::generator();
39+
let m = hex!("AA5E28D6A97A2479A65527F7290311A3624D4CC0FA1578598EE3C2613BF99522");
40+
let s = Scalar::from_repr(m.into()).unwrap();
41+
group.bench_function("lincomb via mul+add", |b| b.iter(|| &p * &s + &p * &s));
42+
group.bench_function("lincomb()", |b| b.iter(|| lincomb(&p, &s, &p, &s)));
43+
}
44+
3745
fn bench_high_level(c: &mut Criterion) {
3846
let mut group = c.benchmark_group("high-level operations");
3947
bench_point_mul(&mut group);
48+
bench_point_lincomb(&mut group);
4049
group.finish();
4150
}
4251

k256/src/arithmetic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub(crate) mod scalar;
88
mod util;
99

1010
pub use field::FieldElement;
11+
pub use mul::lincomb;
1112

1213
use affine::AffinePoint;
1314
use projective::ProjectivePoint;

k256/src/arithmetic/mul.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,16 @@ fn mul(x: &ProjectivePoint, k: &Scalar) -> ProjectivePoint {
298298
lincomb_generic(&[*x], &[*k])
299299
}
300300

301+
/// Calculates `x * k + y * l`.
302+
pub fn lincomb(
303+
x: &ProjectivePoint,
304+
k: &Scalar,
305+
y: &ProjectivePoint,
306+
l: &Scalar,
307+
) -> ProjectivePoint {
308+
lincomb_generic(&[*x, *y], &[*k, *l])
309+
}
310+
301311
impl Mul<Scalar> for ProjectivePoint {
302312
type Output = ProjectivePoint;
303313

@@ -333,3 +343,23 @@ impl MulAssign<&Scalar> for ProjectivePoint {
333343
*self = mul(self, rhs);
334344
}
335345
}
346+
347+
#[cfg(test)]
348+
mod tests {
349+
use super::lincomb;
350+
use crate::arithmetic::{ProjectivePoint, Scalar};
351+
use elliptic_curve::rand_core::OsRng;
352+
use elliptic_curve::{Field, Group};
353+
354+
#[test]
355+
fn test_lincomb() {
356+
let x = ProjectivePoint::random(&mut OsRng);
357+
let y = ProjectivePoint::random(&mut OsRng);
358+
let k = Scalar::random(&mut OsRng);
359+
let l = Scalar::random(&mut OsRng);
360+
361+
let reference = &x * &k + &y * &l;
362+
let test = lincomb(&x, &k, &y, &l);
363+
assert_eq!(reference, test);
364+
}
365+
}

k256/src/ecdsa/recoverable.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ use core::{
4141
};
4242
use ecdsa_core::{signature::Signature as _, Error};
4343

44+
use crate::arithmetic::lincomb;
45+
4446
#[cfg(feature = "ecdsa")]
4547
use crate::{
4648
ecdsa::{
@@ -185,7 +187,7 @@ impl Signature {
185187
let r_inv = r.invert().unwrap();
186188
let u1 = -(r_inv * z);
187189
let u2 = r_inv * *s;
188-
let pk = ((ProjectivePoint::generator() * u1) + (R * u2)).to_affine();
190+
let pk = lincomb(&ProjectivePoint::generator(), &u1, &R, &u2).to_affine();
189191

190192
// TODO(tarcieri): ensure the signature verifies?
191193
Ok(VerifyingKey::from(&pk))

k256/src/ecdsa/verify.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! ECDSA verifier
22
33
use super::{recoverable, Error, Signature};
4+
use crate::arithmetic::lincomb;
45
use crate::{
56
AffinePoint, CompressedPoint, EncodedPoint, ProjectivePoint, PublicKey, Scalar, Secp256k1,
67
};
@@ -90,9 +91,14 @@ impl VerifyPrimitive<Secp256k1> for AffinePoint {
9091
let u1 = z * &s_inv;
9192
let u2 = *r * s_inv;
9293

93-
let x = ((ProjectivePoint::generator() * u1) + (ProjectivePoint::from(*self) * u2))
94-
.to_affine()
95-
.x;
94+
let x = lincomb(
95+
&ProjectivePoint::generator(),
96+
&u1,
97+
&ProjectivePoint::from(*self),
98+
&u2,
99+
)
100+
.to_affine()
101+
.x;
96102

97103
if Scalar::from_bytes_reduced(&x.to_bytes()).eq(&r) {
98104
Ok(())

k256/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ pub use arithmetic::{affine::AffinePoint, projective::ProjectivePoint, scalar::S
7272
#[cfg(feature = "expose-field")]
7373
pub use arithmetic::FieldElement;
7474

75+
pub use arithmetic::lincomb;
76+
7577
#[cfg(feature = "pkcs8")]
7678
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
7779
pub use elliptic_curve::pkcs8;

0 commit comments

Comments
 (0)