diff --git a/src/derive/curve.rs b/src/derive/curve.rs index 66d673dabb..4aa569e485 100644 --- a/src/derive/curve.rs +++ b/src/derive/curve.rs @@ -833,6 +833,10 @@ macro_rules! new_curve_impl { impl<'a, 'b> Mul<&'b $scalar> for &'a $name { type Output = $name; + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + fn mul(self, other: &'b $scalar) -> Self::Output { let mut acc = $name::identity(); for bit in other @@ -935,21 +939,17 @@ macro_rules! new_curve_impl { type Output = $name; fn mul(self, other: &'b $scalar) -> Self::Output { - // TODO: make this faster - let mut acc = $name::identity(); // This is a simple double-and-add implementation of point // multiplication, moving from most significant to least // significant bit of the scalar. - // - // NOTE: We skip the leading bit because it's always unset. + for bit in other .to_repr() .iter() .rev() .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) { acc = acc.double(); acc = $name::conditional_select(&acc, &(acc + self), bit); diff --git a/src/secp256k1/curve.rs b/src/secp256k1/curve.rs index 51c2340007..64f48a5af7 100644 --- a/src/secp256k1/curve.rs +++ b/src/secp256k1/curve.rs @@ -75,3 +75,62 @@ impl CurveAffineExt for Secp256k1Affine { fn test_curve() { crate::tests::curve::curve_tests::(); } + +#[test] +fn ecdsa_example() { + use crate::group::Curve; + use crate::{CurveAffine, FieldExt}; + use rand_core::OsRng; + + fn mod_n(x: Fp) -> Fq { + let mut x_repr = [0u8; 32]; + x_repr.copy_from_slice(x.to_repr().as_ref()); + let mut x_bytes = [0u8; 64]; + x_bytes[..32].copy_from_slice(&x_repr[..]); + Fq::from_bytes_wide(&x_bytes) + } + + let g = Secp256k1::generator(); + + for _ in 0..1000 { + // Generate a key pair + let sk = Fq::random(OsRng); + let pk = (g * sk).to_affine(); + + // Generate a valid signature + // Suppose `m_hash` is the message hash + let msg_hash = Fq::random(OsRng); + + let (r, s) = { + // Draw arandomness + let k = Fq::random(OsRng); + let k_inv = k.invert().unwrap(); + + // Calculate `r` + let r_point = (g * k).to_affine().coordinates().unwrap(); + let x = r_point.x(); + let r = mod_n(*x); + + // Calculate `s` + let s = k_inv * (msg_hash + (r * sk)); + + (r, s) + }; + + { + // Verify + let s_inv = s.invert().unwrap(); + let u_1 = msg_hash * s_inv; + let u_2 = r * s_inv; + + let v_1 = g * u_1; + let v_2 = pk * u_2; + + let r_point = (v_1 + v_2).to_affine().coordinates().unwrap(); + let x_candidate = r_point.x(); + let r_candidate = mod_n(*x_candidate); + + assert_eq!(r, r_candidate); + } + } +}