Skip to content

Commit

Permalink
Add lincomb() as an alias for a 2-point linear combination
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri committed Jul 16, 2021
1 parent a85e930 commit 2a6865f
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 5 deletions.
11 changes: 10 additions & 1 deletion k256/bench/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use criterion::{
use hex_literal::hex;
use k256::{
elliptic_curve::{generic_array::arr, group::ff::PrimeField},
ProjectivePoint, Scalar,
lincomb, ProjectivePoint, Scalar,
};

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

fn bench_point_lincomb<'a, M: Measurement>(group: &mut BenchmarkGroup<'a, M>) {
let p = ProjectivePoint::generator();
let m = hex!("AA5E28D6A97A2479A65527F7290311A3624D4CC0FA1578598EE3C2613BF99522");
let s = Scalar::from_repr(m.into()).unwrap();
group.bench_function("lincomb via mul+add", |b| b.iter(|| &p * &s + &p * &s));
group.bench_function("lincomb()", |b| b.iter(|| lincomb(&p, &s, &p, &s)));
}

fn bench_high_level(c: &mut Criterion) {
let mut group = c.benchmark_group("high-level operations");
bench_point_mul(&mut group);
bench_point_lincomb(&mut group);
group.finish();
}

Expand Down
1 change: 1 addition & 0 deletions k256/src/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub(crate) mod scalar;
mod util;

pub use field::FieldElement;
pub use mul::lincomb;

use affine::AffinePoint;
use projective::ProjectivePoint;
Expand Down
30 changes: 30 additions & 0 deletions k256/src/arithmetic/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,16 @@ fn mul(x: &ProjectivePoint, k: &Scalar) -> ProjectivePoint {
lincomb_generic(&[*x], &[*k])
}

/// Calculates `x * k + y * l`.
pub fn lincomb(
x: &ProjectivePoint,
k: &Scalar,
y: &ProjectivePoint,
l: &Scalar,
) -> ProjectivePoint {
lincomb_generic(&[*x, *y], &[*k, *l])
}

impl Mul<Scalar> for ProjectivePoint {
type Output = ProjectivePoint;

Expand Down Expand Up @@ -333,3 +343,23 @@ impl MulAssign<&Scalar> for ProjectivePoint {
*self = mul(self, rhs);
}
}

#[cfg(test)]
mod tests {
use super::lincomb;
use crate::arithmetic::{ProjectivePoint, Scalar};
use elliptic_curve::rand_core::OsRng;
use elliptic_curve::{Field, Group};

#[test]
fn test_lincomb() {
let x = ProjectivePoint::random(&mut OsRng);
let y = ProjectivePoint::random(&mut OsRng);
let k = Scalar::random(&mut OsRng);
let l = Scalar::random(&mut OsRng);

let reference = &x * &k + &y * &l;
let test = lincomb(&x, &k, &y, &l);
assert_eq!(reference, test);
}
}
4 changes: 3 additions & 1 deletion k256/src/ecdsa/recoverable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ use core::{
};
use ecdsa_core::{signature::Signature as _, Error};

use crate::arithmetic::lincomb;

#[cfg(feature = "ecdsa")]
use crate::{
ecdsa::{
Expand Down Expand Up @@ -185,7 +187,7 @@ impl Signature {
let r_inv = r.invert().unwrap();
let u1 = -(r_inv * z);
let u2 = r_inv * *s;
let pk = ((ProjectivePoint::generator() * u1) + (R * u2)).to_affine();
let pk = lincomb(&ProjectivePoint::generator(), &u1, &R, &u2).to_affine();

// TODO(tarcieri): ensure the signature verifies?
Ok(VerifyingKey::from(&pk))
Expand Down
12 changes: 9 additions & 3 deletions k256/src/ecdsa/verify.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! ECDSA verifier
use super::{recoverable, Error, Signature};
use crate::arithmetic::lincomb;
use crate::{
AffinePoint, CompressedPoint, EncodedPoint, ProjectivePoint, PublicKey, Scalar, Secp256k1,
};
Expand Down Expand Up @@ -90,9 +91,14 @@ impl VerifyPrimitive<Secp256k1> for AffinePoint {
let u1 = z * &s_inv;
let u2 = *r * s_inv;

let x = ((ProjectivePoint::generator() * u1) + (ProjectivePoint::from(*self) * u2))
.to_affine()
.x;
let x = lincomb(
&ProjectivePoint::generator(),
&u1,
&ProjectivePoint::from(*self),
&u2,
)
.to_affine()
.x;

if Scalar::from_bytes_reduced(&x.to_bytes()).eq(&r) {
Ok(())
Expand Down
2 changes: 2 additions & 0 deletions k256/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ pub use arithmetic::{affine::AffinePoint, projective::ProjectivePoint, scalar::S
#[cfg(feature = "expose-field")]
pub use arithmetic::FieldElement;

pub use arithmetic::lincomb;

#[cfg(feature = "pkcs8")]
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
pub use elliptic_curve::pkcs8;
Expand Down

0 comments on commit 2a6865f

Please sign in to comment.