Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sign_ecdsa_with_noncedata and sign_ecdsa_recoverable_with_noncedata #425

Merged
merged 1 commit into from Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 28 additions & 4 deletions src/ecdsa/mod.rs
Expand Up @@ -340,20 +340,44 @@ impl<C: Signing> Secp256k1<C> {
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 {
fn sign_ecdsa_with_noncedata_pointer(
&self,
msg: &Message,
sk: &SecretKey,
noncedata_ptr: *const ffi::types::c_void,
) -> 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);
noncedata_ptr), 1);
Signature::from(ret)
}
}

/// 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 {
self.sign_ecdsa_with_noncedata_pointer(msg, sk, ptr::null())
}

/// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce
/// and includes 32 bytes of noncedata in the nonce generation via inclusion in
/// one of the hash operations during nonce generation. This is useful when multiple
/// signatures are needed for the same Message and SecretKey while still using RFC6979.
/// Requires a signing-capable context.
pub fn sign_ecdsa_with_noncedata(
&self,
msg: &Message,
sk: &SecretKey,
noncedata: &[u8; 32],
) -> Signature {
let noncedata_ptr = noncedata.as_ptr() as *const ffi::types::c_void;
self.sign_ecdsa_with_noncedata_pointer(msg, sk, noncedata_ptr)
}

fn sign_grind_with_check(
&self, msg: &Message,
sk: &SecretKey,
Expand Down
77 changes: 73 additions & 4 deletions src/ecdsa/recovery.rs
Expand Up @@ -159,9 +159,12 @@ impl<C: Signing> Secp256k1<C> {
self.sign_ecdsa_recoverable(msg, sk)
}

/// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce
/// Requires a signing-capable context.
pub fn sign_ecdsa_recoverable(&self, msg: &Message, sk: &key::SecretKey) -> RecoverableSignature {
fn sign_ecdsa_recoverable_with_noncedata_pointer(
&self,
msg: &Message,
sk: &key::SecretKey,
noncedata_ptr: *const super_ffi::types::c_void,
) -> RecoverableSignature {
let mut ret = ffi::RecoverableSignature::new();
unsafe {
// We can assume the return value because it's not possible to construct
Expand All @@ -173,14 +176,35 @@ impl<C: Signing> Secp256k1<C> {
msg.as_c_ptr(),
sk.as_c_ptr(),
super_ffi::secp256k1_nonce_function_rfc6979,
ptr::null()
noncedata_ptr
),
1
);
}

RecoverableSignature::from(ret)
}

/// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce
/// Requires a signing-capable context.
pub fn sign_ecdsa_recoverable(&self, msg: &Message, sk: &key::SecretKey) -> RecoverableSignature {
self.sign_ecdsa_recoverable_with_noncedata_pointer(msg, sk, ptr::null())
}

/// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce
/// and includes 32 bytes of noncedata in the nonce generation via inclusion in
/// one of the hash operations during nonce generation. This is useful when multiple
/// signatures are needed for the same Message and SecretKey while still using RFC6979.
/// Requires a signing-capable context.
pub fn sign_ecdsa_recoverable_with_noncedata(
&self,
msg: &Message,
sk: &key::SecretKey,
noncedata: &[u8; 32],
) -> RecoverableSignature {
let noncedata_ptr = noncedata.as_ptr() as *const super_ffi::types::c_void;
self.sign_ecdsa_recoverable_with_noncedata_pointer(msg, sk, noncedata_ptr)
}
}

impl<C: Verification> Secp256k1<C> {
Expand Down Expand Up @@ -276,6 +300,32 @@ mod tests {
RecoveryId(1)))
}

#[test]
#[cfg(not(fuzzing))] // fixed sig vectors can't work with fuzz-sigs
#[cfg(all(feature="std", feature = "rand-std"))]
fn sign_with_noncedata() {
let mut s = Secp256k1::new();
s.randomize(&mut thread_rng());
let one: [u8; 32] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];

let sk = SecretKey::from_slice(&one).unwrap();
let msg = Message::from_slice(&one).unwrap();
let noncedata = [42u8; 32];

let sig = s.sign_ecdsa_recoverable_with_noncedata(&msg, &sk, &noncedata);
assert_eq!(Ok(sig), RecoverableSignature::from_compact(&[
0xb5, 0x0b, 0xb6, 0x79, 0x5f, 0x31, 0x74, 0x8a,
0x4d, 0x37, 0xc3, 0xa9, 0x7e, 0xbd, 0x06, 0xa2,
0x2e, 0xa3, 0x37, 0x71, 0x04, 0x0f, 0x5c, 0x05,
0xd6, 0xe2, 0xbb, 0x2d, 0x38, 0xc6, 0x22, 0x7c,
0x34, 0x3b, 0x66, 0x59, 0xdb, 0x96, 0x99, 0x59,
0xd9, 0xfd, 0xdb, 0x44, 0xbd, 0x0d, 0xd9, 0xb9,
0xdd, 0x47, 0x66, 0x6a, 0xb5, 0x28, 0x71, 0x90,
0x1d, 0x17, 0x61, 0xeb, 0x82, 0xec, 0x87, 0x22],
RecoveryId(0)))
}

#[test]
#[cfg(all(feature="std", feature = "rand-std"))]
fn sign_and_verify_fail() {
Expand Down Expand Up @@ -317,6 +367,25 @@ mod tests {
assert_eq!(s.recover_ecdsa(&msg, &sig), Ok(pk));
}

#[test]
#[cfg(all(feature="std", feature = "rand-std"))]
fn sign_with_recovery_and_noncedata() {
let mut s = Secp256k1::new();
s.randomize(&mut thread_rng());

let mut msg = [0u8; 32];
thread_rng().fill_bytes(&mut msg);
let msg = Message::from_slice(&msg).unwrap();

let noncedata = [42u8; 32];

let (sk, pk) = s.generate_keypair(&mut thread_rng());

let sig = s.sign_ecdsa_recoverable_with_noncedata(&msg, &sk, &noncedata);

assert_eq!(s.recover_ecdsa(&msg, &sig), Ok(pk));
}

#[test]
#[cfg(all(feature="std", feature = "rand-std"))]
fn bad_recovery() {
Expand Down
20 changes: 20 additions & 0 deletions src/lib.rs
Expand Up @@ -807,13 +807,16 @@ mod tests {
s.randomize(&mut thread_rng());

let mut msg = [0u8; 32];
let noncedata = [42u8; 32];
for _ in 0..100 {
thread_rng().fill_bytes(&mut msg);
let msg = Message::from_slice(&msg).unwrap();

let (sk, pk) = s.generate_keypair(&mut thread_rng());
let sig = s.sign_ecdsa(&msg, &sk);
assert_eq!(s.verify_ecdsa(&msg, &sig, &pk), Ok(()));
let noncedata_sig = s.sign_ecdsa_with_noncedata(&msg, &sk, &noncedata);
assert_eq!(s.verify_ecdsa(&msg, &noncedata_sig, &pk), Ok(()));
let low_r_sig = s.sign_ecdsa_low_r(&msg, &sk);
assert_eq!(s.verify_ecdsa(&msg, &low_r_sig, &pk), Ok(()));
let grind_r_sig = s.sign_ecdsa_grind_r(&msg, &sk, 1);
Expand Down Expand Up @@ -927,6 +930,23 @@ mod tests {
assert!(from_hex("ag", &mut [0u8; 4]).is_err());
}

#[test]
#[cfg(not(fuzzing))] // fuzz-sigs have fixed size/format
#[cfg(any(feature = "alloc", feature = "std"))]
fn test_noncedata() {
let secp = Secp256k1::new();
let msg = hex!("887d04bb1cf1b1554f1b268dfe62d13064ca67ae45348d50d1392ce2d13418ac");
let msg = Message::from_slice(&msg).unwrap();
let noncedata = [42u8; 32];
let sk = SecretKey::from_str("57f0148f94d13095cfda539d0da0d1541304b678d8b36e243980aab4e1b7cead").unwrap();
let expected_sig = hex!("24861b3edd4e7da43319c635091405feced6efa4ec99c3c3c35f6c3ba0ed8816116772e84994084db85a6c20589f6a85af569d42275c2a5dd900da5776b99d5d");
let expected_sig = ecdsa::Signature::from_compact(&expected_sig).unwrap();

let sig = secp.sign_ecdsa_with_noncedata(&msg, &sk, &noncedata);

assert_eq!(expected_sig, sig);
}

#[test]
#[cfg(not(fuzzing))] // fixed sig vectors can't work with fuzz-sigs
#[cfg(any(feature = "alloc", feature = "std"))]
Expand Down