From f93ca813481ae0d1bad0504319e0f942b62979e1 Mon Sep 17 00:00:00 2001 From: junderw Date: Sun, 20 Mar 2022 11:54:31 +0900 Subject: [PATCH] Add sign_ecdsa_with_noncedata and sign_ecdsa_recoverable_with_noncedata --- src/ecdsa/mod.rs | 32 +++++++++++++++--- src/ecdsa/recovery.rs | 77 ++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 20 +++++++++++ 3 files changed, 121 insertions(+), 8 deletions(-) diff --git a/src/ecdsa/mod.rs b/src/ecdsa/mod.rs index 99f1c501a..1e8725ac5 100644 --- a/src/ecdsa/mod.rs +++ b/src/ecdsa/mod.rs @@ -340,20 +340,44 @@ impl Secp256k1 { 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, diff --git a/src/ecdsa/recovery.rs b/src/ecdsa/recovery.rs index 687999c9a..02e0c83b1 100644 --- a/src/ecdsa/recovery.rs +++ b/src/ecdsa/recovery.rs @@ -159,9 +159,12 @@ impl Secp256k1 { 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 @@ -173,7 +176,7 @@ impl Secp256k1 { msg.as_c_ptr(), sk.as_c_ptr(), super_ffi::secp256k1_nonce_function_rfc6979, - ptr::null() + noncedata_ptr ), 1 ); @@ -181,6 +184,27 @@ impl Secp256k1 { 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 Secp256k1 { @@ -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() { @@ -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() { diff --git a/src/lib.rs b/src/lib.rs index 1dd51a093..0fcb5c2ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -807,6 +807,7 @@ 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(); @@ -814,6 +815,8 @@ mod tests { 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); @@ -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"))]