diff --git a/lib-py/src/lib.rs b/lib-py/src/lib.rs index 63767fe..1f013e2 100644 --- a/lib-py/src/lib.rs +++ b/lib-py/src/lib.rs @@ -78,6 +78,13 @@ pub fn sign_schnorr_with_nonce(msg: String, nonce_sec_hex: String, index: u32) - .map_err(|e| PyErr::new::(e)) } +/// Verify a Schnorr signature over a message, using a child key +#[pyfunction] +pub fn verify_schnorr(msg: String, signature_hex: String, index: u32) -> PyResult { + dlccryptlib::verify_schnorr(&msg, &signature_hex, index) + .map_err(|e| PyErr::new::(e)) +} + /// Combine a number of public keys into one #[pyfunction] pub fn combine_pubkeys(keys_hex: String) -> PyResult { @@ -182,6 +189,7 @@ fn dlccryptlib_py(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(sign_hash_ecdsa, m)?)?; m.add_function(wrap_pyfunction!(create_deterministic_nonce, m)?)?; m.add_function(wrap_pyfunction!(sign_schnorr_with_nonce, m)?)?; + m.add_function(wrap_pyfunction!(verify_schnorr, m)?)?; m.add_function(wrap_pyfunction!(combine_pubkeys, m)?)?; m.add_function(wrap_pyfunction!(combine_seckeys, m)?)?; m.add_function(wrap_pyfunction!(create_cet_adaptor_sigs, m)?)?; diff --git a/src/adaptor_signature.rs b/src/adaptor_signature.rs index b2770c5..7d63763 100644 --- a/src/adaptor_signature.rs +++ b/src/adaptor_signature.rs @@ -153,6 +153,30 @@ fn message_hash(msg: &str) -> Result { .map_err(|e| e.to_string()) } +/// Verify a Schnorr signature on a message +pub(crate) fn verify_schnorr( + secp: &Secp256k1, + sig: &SchnorrSignature, + public_key: &XOnlyPublicKey, + msg: &str, +) -> Result { + let msg_hash = sha256::Hash::hash(msg.as_bytes()).to_byte_array(); + let msg_msg = Message::from_digest(msg_hash); + + let res_num: i32; + unsafe { + res_num = secp256k1_sys::secp256k1_schnorrsig_verify( + secp.ctx().as_ref(), + sig.as_c_ptr(), + msg_msg.as_c_ptr(), + 32_usize, + public_key.as_c_ptr(), + ); + } + + Ok(res_num != 0) +} + pub(crate) fn create_digit_adaptor_sig_point( secp: &Secp256k1, oracle_pubkey: &PublicKey, diff --git a/src/lib.rs b/src/lib.rs index e096059..be24610 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,10 +94,7 @@ pub fn sign_hash_ecdsa( Ok(sig.to_lower_hex_string()) } -pub fn create_deterministic_nonce( - event_id: &str, - index: u32, -) -> Result<(String, String), String> { +pub fn create_deterministic_nonce(event_id: &str, index: u32) -> Result<(String, String), String> { let (sk, pk) = global_lib() .read() .unwrap() @@ -120,6 +117,17 @@ pub fn sign_schnorr_with_nonce( Ok(sig.to_string()) } +// Schnorr signature verification +pub fn verify_schnorr(msg: &str, signature_hex: &str, index: u32) -> Result { + let signature = schnorr_sig_from_hex(signature_hex) + .map_err(|e| format!("Error in signature hex string {}", e))?; + let res = global_lib() + .read() + .unwrap() + .verify_schnorr(msg, &signature, index)?; + Ok(res) +} + pub fn combine_pubkeys(keys_hex: &str) -> Result { let keys_split: Vec<_> = keys_hex.split(" ").collect(); let mut keys = Vec::::with_capacity(keys_split.len()); @@ -557,4 +565,3 @@ fn error_as_cstr_prefix(error: String) -> *mut c_char { .unwrap() .into_raw() } - diff --git a/src/lib_struct.rs b/src/lib_struct.rs index dce87a3..a72a8b3 100644 --- a/src/lib_struct.rs +++ b/src/lib_struct.rs @@ -4,7 +4,7 @@ use crate::adaptor_signature::{ create_cet_adaptor_signatures, create_final_cet_signatures, sign_hash_ecdsa_with_key, - sign_schnorr_with_nonce_sec, verify_cet_adaptor_signatures, + sign_schnorr_with_nonce_sec, verify_cet_adaptor_signatures, verify_schnorr, }; use crate::hd_wallet_storage::HDWalletStorage; @@ -151,6 +151,17 @@ impl Lib { sign_schnorr_with_nonce_sec(&self.secp, &kp, msg, nonce_sec) } + /// Verify a Schnorr signature on a message using Schnorr, using a child key + pub(crate) fn verify_schnorr( + &self, + msg: &str, + signature: &SchnorrSignature, + index: u32, + ) -> Result { + let pubkey = self.get_child_public_key(index)?.x_only_public_key().0; + verify_schnorr(&self.secp, signature, &pubkey, msg) + } + pub(crate) fn combine_seckeys(secrets: &Vec) -> Result { if secrets.len() == 0 { return Err("At least one key is required".to_string()); diff --git a/src/test_lib.rs b/src/test_lib.rs index f1c634e..bba2f8d 100644 --- a/src/test_lib.rs +++ b/src/test_lib.rs @@ -4,9 +4,9 @@ use crate::adaptor_signature::verify_ecdsa_signature; use crate::{ - combine_pubkeys, combine_seckeys, create_deterministic_nonce, - get_public_key, init_with_entropy, keypair_from_sec_key_hex, - sign_schnorr_with_nonce, verify_public_key, Lib, + combine_pubkeys, combine_seckeys, create_deterministic_nonce, get_public_key, + init_with_entropy, keypair_from_sec_key_hex, sign_schnorr_with_nonce, verify_public_key, + verify_schnorr, Lib, }; use bitcoin::hex::FromHex; use bitcoin::secp256k1::PublicKey; @@ -145,6 +145,34 @@ fn test_sign_schnorr_with_nonce() { assert_eq!(sig3.to_string(), "4578740620e7a2c56eabea07c835dba35e832115930d023d0a7778652fbbf7d97a9f4a207dcb1456f1b0f57c4856085c32c79f4efce81cd276c272190aab5e3c"); } +#[test] +fn test_verify_schnorr() { + let _xpub = init_with_entropy(DUMMY_ENTROPY_STR, DEFAULT_NETWORK).unwrap(); + + let msg = "This is a message"; + + // Constant signature + let sig1 = "ff4cb99e0a9be8ec7dea1e51904cf22f71717c19fc3e7dcbc8346eb28bebffbb892c4c41e05c2383efda00f5acc9c7f3622d88a90630cd62d49db598c8ce10b9"; + let verify_res = verify_schnorr(msg, &sig1, 0).unwrap(); + assert_eq!(verify_res, true); + + // Signature created here + let nonce = "0123450000000000006897528962743076432965432697856340567500000100"; + let sig = sign_schnorr_with_nonce(msg, nonce, 0).unwrap(); + + let verify_res = verify_schnorr(msg, &sig, 0).unwrap(); + assert_eq!(verify_res, true); + + // Verify with different key index + let verify_res = verify_schnorr(msg, &sig, 1).unwrap(); + assert_eq!(verify_res, false); + + // Verify with a different message + let msg2 = "This is ANOTHER message"; + let verify_res = verify_schnorr(msg2, &sig, 0).unwrap(); + assert_eq!(verify_res, false); +} + fn create_dummy_pubkey(index: u8) -> PublicKey { let sechex = format!( "012345000000000000689752896274307643296543269785634056750000000{}",