Skip to content

Commit

Permalink
Move ECDSA functionality into ECDSA module
Browse files Browse the repository at this point in the history
  • Loading branch information
thomaseizinger committed Sep 9, 2021
1 parent 905e10f commit 524cba1
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 204 deletions.
4 changes: 2 additions & 2 deletions src/ecdh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ impl SharedSecret {

#[cfg(test)]
mod tests {
use super::*;
use rand::thread_rng;
use super::SharedSecret;
use super::super::Secp256k1;

#[cfg(target_arch = "wasm32")]
Expand Down Expand Up @@ -224,7 +224,7 @@ mod tests {
let x = [5u8; 32];
let y = [7u8; 32];
let mut output = [0u8; 64];
let res = unsafe { super::c_callback(output.as_mut_ptr(), x.as_ptr(), y.as_ptr(), ::ptr::null_mut()) };
let res = unsafe { super::c_callback(output.as_mut_ptr(), x.as_ptr(), y.as_ptr(), ptr::null_mut()) };
assert_eq!(res, 1);
let mut new_x = [0u8; 32];
let mut new_y = [0u8; 32];
Expand Down
203 changes: 199 additions & 4 deletions src/ecdsa/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
//! Structs and functionality related to the ECDSA signature algorithm.
use core::{fmt, str, ops};
use Error;
use core::{fmt, str, ops, ptr, mem};

use {Signing, Verification, Message, PublicKey, Secp256k1, SecretKey, from_hex, Error, ffi};
use ffi::CPtr;
use ffi;
use from_hex;

#[cfg(feature = "recovery")]
mod recovery;
Expand Down Expand Up @@ -305,3 +304,199 @@ impl<'de> ::serde::Deserialize<'de> for Signature {
}
}
}

impl<C: Signing> Secp256k1<C> {

/// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce
/// Requires a signing-capable context.
#[deprecated(since = "0.21.0", note = "Use sign_ecdsa instead.")]
pub fn sign(&self, msg: &Message, sk: &SecretKey) -> Signature {
self.sign_ecdsa(msg, sk)
}

/// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce
/// Requires a signing-capable context.
pub fn sign_ecdsa(&self, msg: &Message, sk: &SecretKey) -> Signature {
unsafe {
let mut ret = ffi::Signature::new();
// We can assume the return value because it's not possible to construct
// an invalid signature from a valid `Message` and `SecretKey`
assert_eq!(ffi::secp256k1_ecdsa_sign(self.ctx, &mut ret, msg.as_c_ptr(),
sk.as_c_ptr(), ffi::secp256k1_nonce_function_rfc6979,
ptr::null()), 1);
Signature::from(ret)
}
}

fn sign_grind_with_check(
&self, msg: &Message,
sk: &SecretKey,
check: impl Fn(&ffi::Signature) -> bool) -> Signature {
let mut entropy_p : *const ffi::types::c_void = ptr::null();
let mut counter : u32 = 0;
let mut extra_entropy = [0u8; 32];
loop {
unsafe {
let mut ret = ffi::Signature::new();
// We can assume the return value because it's not possible to construct
// an invalid signature from a valid `Message` and `SecretKey`
assert_eq!(ffi::secp256k1_ecdsa_sign(self.ctx, &mut ret, msg.as_c_ptr(),
sk.as_c_ptr(), ffi::secp256k1_nonce_function_rfc6979,
entropy_p), 1);
if check(&ret) {
return Signature::from(ret);
}

counter += 1;
// From 1.32 can use `to_le_bytes` instead
let le_counter = counter.to_le();
let le_counter_bytes : [u8; 4] = mem::transmute(le_counter);
for (i, b) in le_counter_bytes.iter().enumerate() {
extra_entropy[i] = *b;
}

entropy_p = extra_entropy.as_ptr() as *const ffi::types::c_void;

// When fuzzing, these checks will usually spinloop forever, so just short-circuit them.
#[cfg(fuzzing)]
return Signature::from(ret);
}
}
}

/// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce
/// and "grinds" the nonce by passing extra entropy if necessary to produce
/// a signature that is less than 71 - bytes_to_grund bytes. The number
/// of signing operation performed by this function is exponential in the
/// number of bytes grinded.
/// Requires a signing capable context.
#[deprecated(since = "0.21.0", note = "Use sign_ecdsa_grind_r instead.")]
pub fn sign_grind_r(&self, msg: &Message, sk: &SecretKey, bytes_to_grind: usize) -> Signature {
self.sign_ecdsa_grind_r(msg, sk, bytes_to_grind)
}

/// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce
/// and "grinds" the nonce by passing extra entropy if necessary to produce
/// a signature that is less than 71 - bytes_to_grund bytes. The number
/// of signing operation performed by this function is exponential in the
/// number of bytes grinded.
/// Requires a signing capable context.
pub fn sign_ecdsa_grind_r(&self, msg: &Message, sk: &SecretKey, bytes_to_grind: usize) -> Signature {
let len_check = |s : &ffi::Signature| der_length_check(s, 71 - bytes_to_grind);
return self.sign_grind_with_check(msg, sk, len_check);
}

/// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce
/// and "grinds" the nonce by passing extra entropy if necessary to produce
/// a signature that is less than 71 bytes and compatible with the low r
/// signature implementation of bitcoin core. In average, this function
/// will perform two signing operations.
/// Requires a signing capable context.
#[deprecated(since = "0.21.0", note = "Use sign_ecdsa_grind_r instead.")]
pub fn sign_low_r(&self, msg: &Message, sk: &SecretKey) -> Signature {
return self.sign_grind_with_check(msg, sk, compact_sig_has_zero_first_bit)
}

/// Constructs a signature for `msg` using the secret key `sk`, RFC6979 nonce
/// and "grinds" the nonce by passing extra entropy if necessary to produce
/// a signature that is less than 71 bytes and compatible with the low r
/// signature implementation of bitcoin core. In average, this function
/// will perform two signing operations.
/// Requires a signing capable context.
pub fn sign_ecdsa_low_r(&self, msg: &Message, sk: &SecretKey) -> Signature {
return self.sign_grind_with_check(msg, sk, compact_sig_has_zero_first_bit)
}
}

impl<C: Verification> Secp256k1<C> {
/// Checks that `sig` is a valid ECDSA signature for `msg` using the public
/// key `pubkey`. Returns `Ok(())` on success. Note that this function cannot
/// be used for Bitcoin consensus checking since there may exist signatures
/// which OpenSSL would verify but not libsecp256k1, or vice-versa. Requires a
/// verify-capable context.
///
/// ```rust
/// # #[cfg(feature="rand")] {
/// # use secp256k1::rand::rngs::OsRng;
/// # use secp256k1::{Secp256k1, Message, Error};
/// #
/// # let secp = Secp256k1::new();
/// # let mut rng = OsRng::new().expect("OsRng");
/// # let (secret_key, public_key) = secp.generate_keypair(&mut rng);
/// #
/// let message = Message::from_slice(&[0xab; 32]).expect("32 bytes");
/// let sig = secp.sign(&message, &secret_key);
/// assert_eq!(secp.verify(&message, &sig, &public_key), Ok(()));
///
/// let message = Message::from_slice(&[0xcd; 32]).expect("32 bytes");
/// assert_eq!(secp.verify(&message, &sig, &public_key), Err(Error::IncorrectSignature));
/// # }
/// ```
#[inline]
#[deprecated(since = "0.21.0", note = "Use verify_ecdsa instead")]
pub fn verify(&self, msg: &Message, sig: &Signature, pk: &PublicKey) -> Result<(), Error> {
self.verify_ecdsa(msg, sig, pk)
}

/// Checks that `sig` is a valid ECDSA signature for `msg` using the public
/// key `pubkey`. Returns `Ok(())` on success. Note that this function cannot
/// be used for Bitcoin consensus checking since there may exist signatures
/// which OpenSSL would verify but not libsecp256k1, or vice-versa. Requires a
/// verify-capable context.
///
/// ```rust
/// # #[cfg(feature="rand")] {
/// # use secp256k1::rand::rngs::OsRng;
/// # use secp256k1::{Secp256k1, Message, Error};
/// #
/// # let secp = Secp256k1::new();
/// # let mut rng = OsRng::new().expect("OsRng");
/// # let (secret_key, public_key) = secp.generate_keypair(&mut rng);
/// #
/// let message = Message::from_slice(&[0xab; 32]).expect("32 bytes");
/// let sig = secp.sign_ecdsa(&message, &secret_key);
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Ok(()));
///
/// let message = Message::from_slice(&[0xcd; 32]).expect("32 bytes");
/// assert_eq!(secp.verify_ecdsa(&message, &sig, &public_key), Err(Error::IncorrectSignature));
/// # }
/// ```
#[inline]
pub fn verify_ecdsa(&self, msg: &Message, sig: &Signature, pk: &PublicKey) -> Result<(), Error> {
unsafe {
if ffi::secp256k1_ecdsa_verify(self.ctx, sig.as_c_ptr(), msg.as_c_ptr(), pk.as_c_ptr()) == 0 {
Err(Error::IncorrectSignature)
} else {
Ok(())
}
}
}
}

pub(crate) fn compact_sig_has_zero_first_bit(sig: &ffi::Signature) -> bool {
let mut compact = [0; 64];
unsafe {
let err = ffi::secp256k1_ecdsa_signature_serialize_compact(
ffi::secp256k1_context_no_precomp,
compact.as_mut_c_ptr(),
sig,
);
debug_assert!(err == 1);
}
compact[0] < 0x80
}

pub(crate) fn der_length_check(sig: &ffi::Signature, max_len: usize) -> bool {
let mut ser_ret = [0; 72];
let mut len: usize = ser_ret.len();
unsafe {
let err = ffi::secp256k1_ecdsa_signature_serialize_der(
ffi::secp256k1_context_no_precomp,
ser_ret.as_mut_c_ptr(),
&mut len,
sig,
);
debug_assert!(err == 1);
}
len <= max_len
}
2 changes: 1 addition & 1 deletion src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ impl KeyPair {
/// Creates a new random secret key. Requires compilation with the "rand" feature.
#[inline]
#[cfg(any(test, feature = "rand"))]
pub fn new<R: Rng + ?Sized, C: Signing>(secp: &Secp256k1<C>, rng: &mut R) -> KeyPair {
pub fn new<R: rand::Rng + ?Sized, C: Signing>(secp: &Secp256k1<C>, rng: &mut R) -> KeyPair {
let mut random_32_bytes = || {
let mut ret = [0u8; 32];
rng.fill_bytes(&mut ret);
Expand Down
Loading

0 comments on commit 524cba1

Please sign in to comment.