From df98b160d84ec678099673b87cf7a0fd76dcc389 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Thu, 4 Jul 2024 14:42:27 +0300 Subject: [PATCH] Make schnorr sign/verify accept a message slice --- src/key.rs | 6 +- src/schnorr.rs | 242 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 225 insertions(+), 23 deletions(-) diff --git a/src/key.rs b/src/key.rs index 9a868980d..781a8ed79 100644 --- a/src/key.rs +++ b/src/key.rs @@ -958,8 +958,8 @@ impl Keypair { /// Constructs an schnorr signature for `msg` using the global [`SECP256K1`] context. #[inline] #[cfg(all(feature = "global-context", feature = "rand-std"))] - pub fn sign_schnorr(&self, msg: Message) -> schnorr::Signature { - SECP256K1.sign_schnorr(&msg, self) + pub fn sign_schnorr(&self, msg: &[u8]) -> schnorr::Signature { + SECP256K1.sign_schnorr(msg, self) } /// Attempts to erase the secret within the underlying array. @@ -1316,7 +1316,7 @@ impl XOnlyPublicKey { pub fn verify( &self, secp: &Secp256k1, - msg: &Message, + msg: &[u8], sig: &schnorr::Signature, ) -> Result<(), Error> { secp.verify_schnorr(sig, msg, self) diff --git a/src/schnorr.rs b/src/schnorr.rs index e8c767b8a..4451d92a1 100644 --- a/src/schnorr.rs +++ b/src/schnorr.rs @@ -7,12 +7,13 @@ use core::{fmt, ptr, str}; #[cfg(feature = "rand")] use rand::{CryptoRng, Rng}; +use secp256k1_sys::SchnorrSigExtraParams; use crate::ffi::{self, CPtr}; use crate::key::{Keypair, XOnlyPublicKey}; #[cfg(feature = "global-context")] use crate::SECP256K1; -use crate::{constants, from_hex, Error, Message, Secp256k1, Signing, Verification}; +use crate::{constants, from_hex, Error, Secp256k1, Signing, Verification}; /// Represents a schnorr signature. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -93,7 +94,7 @@ impl Signature { /// Verifies a schnorr signature for `msg` using `pk` and the global [`SECP256K1`] context. #[inline] #[cfg(feature = "global-context")] - pub fn verify(&self, msg: &Message, pk: &XOnlyPublicKey) -> Result<(), Error> { + pub fn verify(&self, msg: &[u8], pk: &XOnlyPublicKey) -> Result<(), Error> { SECP256K1.verify_schnorr(self, msg, pk) } } @@ -101,20 +102,22 @@ impl Signature { impl Secp256k1 { fn sign_schnorr_helper( &self, - msg: &Message, + msg: &[u8], keypair: &Keypair, nonce_data: *const ffi::types::c_uchar, ) -> Signature { unsafe { let mut sig = [0u8; constants::SCHNORR_SIGNATURE_SIZE]; + let extra = SchnorrSigExtraParams::new(None, nonce_data.cast()); assert_eq!( 1, - ffi::secp256k1_schnorrsig_sign( + ffi::secp256k1_schnorrsig_sign_custom( self.ctx.as_ptr(), sig.as_mut_c_ptr(), msg.as_c_ptr(), + msg.len(), keypair.as_c_ptr(), - nonce_data, + &extra, ) ); @@ -125,19 +128,19 @@ impl Secp256k1 { /// Creates a schnorr signature internally using the [`rand::rngs::ThreadRng`] random number /// generator to generate the auxiliary random data. #[cfg(feature = "rand-std")] - pub fn sign_schnorr(&self, msg: &Message, keypair: &Keypair) -> Signature { + pub fn sign_schnorr(&self, msg: &[u8], keypair: &Keypair) -> Signature { self.sign_schnorr_with_rng(msg, keypair, &mut rand::thread_rng()) } /// Creates a schnorr signature without using any auxiliary random data. - pub fn sign_schnorr_no_aux_rand(&self, msg: &Message, keypair: &Keypair) -> Signature { + pub fn sign_schnorr_no_aux_rand(&self, msg: &[u8], keypair: &Keypair) -> Signature { self.sign_schnorr_helper(msg, keypair, ptr::null()) } /// Creates a schnorr signature using the given auxiliary random data. pub fn sign_schnorr_with_aux_rand( &self, - msg: &Message, + msg: &[u8], keypair: &Keypair, aux_rand: &[u8; 32], ) -> Signature { @@ -149,7 +152,7 @@ impl Secp256k1 { #[cfg(feature = "rand")] pub fn sign_schnorr_with_rng( &self, - msg: &Message, + msg: &[u8], keypair: &Keypair, rng: &mut R, ) -> Signature { @@ -164,7 +167,7 @@ impl Secp256k1 { pub fn verify_schnorr( &self, sig: &Signature, - msg: &Message, + msg: &[u8], pubkey: &XOnlyPublicKey, ) -> Result<(), Error> { unsafe { @@ -172,7 +175,7 @@ impl Secp256k1 { self.ctx.as_ptr(), sig.as_c_ptr(), msg.as_c_ptr(), - 32, + msg.len(), pubkey.as_c_ptr(), ); @@ -235,9 +238,7 @@ mod tests { } #[cfg(feature = "rand-std")] - fn sign_helper( - sign: fn(&Secp256k1, &Message, &Keypair, &mut ThreadRng) -> Signature, - ) { + fn sign_helper(sign: fn(&Secp256k1, &[u8], &Keypair, &mut ThreadRng) -> Signature) { let secp = Secp256k1::new(); let mut rng = rand::thread_rng(); @@ -246,7 +247,6 @@ mod tests { for _ in 0..100 { let msg = crate::random_32_bytes(&mut rand::thread_rng()); - let msg = Message::from_digest_slice(&msg).unwrap(); let sig = sign(&secp, &msg, &kp, &mut rng); @@ -260,8 +260,7 @@ mod tests { fn schnorr_sign() { let secp = Secp256k1::new(); - let hex_msg = hex_32!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); - let msg = Message::from_digest_slice(&hex_msg).unwrap(); + let msg = hex_32!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); let sk = Keypair::from_seckey_str( &secp, "688C77BC2D5AAFF5491CF309D4753B732135470D05B7B2CD21ADD0744FE97BEF", @@ -282,8 +281,7 @@ mod tests { fn schnorr_verify() { let secp = Secp256k1::new(); - let hex_msg = hex_32!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); - let msg = Message::from_digest_slice(&hex_msg).unwrap(); + let msg = hex_32!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); let sig = Signature::from_str("6470FD1303DDA4FDA717B9837153C24A6EAB377183FC438F939E0ED2B620E9EE5077C4A8B8DCA28963D772A94F5F0DDF598E1C47C137F91933274C7C3EDADCE8").unwrap(); let pubkey = XOnlyPublicKey::from_str( "B33CC9EDC096D0A83416964BD3C6247B8FECD256E4EFA7870D2C854BDEB33390", @@ -462,7 +460,7 @@ mod tests { let s = Secp256k1::new(); - let msg = Message::from_digest_slice(&[1; 32]).unwrap(); + let msg = [1; 32]; let keypair = Keypair::from_seckey_slice(&s, &[2; 32]).unwrap(); let aux = [3u8; 32]; let sig = s.sign_schnorr_with_aux_rand(&msg, &keypair, &aux); @@ -506,4 +504,208 @@ mod tests { assert_tokens(&pk.readable(), &[Token::Str(PK_STR)]); assert_tokens(&pk.readable(), &[Token::String(PK_STR)]); } + + #[test] + #[cfg(feature = "alloc")] + #[cfg(not(secp256k1_fuzz))] // fixed sig vectors can't work with fuzz-sigs + fn bip340_test_vectors() { + struct TestVector { + secret_key: Option<[u8; 32]>, + public_key: [u8; 32], + aux_rand: Option<[u8; 32]>, + message: Vec, + signature: [u8; 64], + should_fail_verify: bool, + } + fn hex_arr, const N: usize>(s: &str) -> T { + let mut out = [0; N]; + from_hex(s, &mut out).unwrap(); + out.into() + } + let hex_vec = |s: &str| { + let mut v = vec![0u8; s.len() / 2]; + from_hex(s, v.as_mut_slice()).unwrap(); + v + }; + + let vectors = [ + TestVector { + secret_key: hex_arr("0000000000000000000000000000000000000000000000000000000000000003"), + public_key: hex_arr("F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9"), + aux_rand: hex_arr("0000000000000000000000000000000000000000000000000000000000000000"), + message: hex_vec("0000000000000000000000000000000000000000000000000000000000000000"), + signature: hex_arr("E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0"), + should_fail_verify: false, + }, + TestVector { + secret_key: hex_arr("B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF"), + public_key: hex_arr("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + aux_rand: hex_arr("0000000000000000000000000000000000000000000000000000000000000001"), + message: hex_vec("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + signature: hex_arr("6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A"), + should_fail_verify: false, + }, + TestVector { + secret_key: hex_arr("C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9"), + public_key: hex_arr("DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8"), + aux_rand: hex_arr("C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906"), + message: hex_vec("7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C"), + signature: hex_arr("5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7"), + should_fail_verify: false, + }, + TestVector { + secret_key: hex_arr("0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710"), + public_key: hex_arr("25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517"), + aux_rand: hex_arr("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + message: hex_vec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + signature: hex_arr("7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3"), + should_fail_verify: false, + }, + TestVector { + secret_key: None, + public_key: hex_arr("D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9"), + aux_rand: None, + message: hex_vec("4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703"), + signature: hex_arr("00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4"), + should_fail_verify: false, + }, + TestVector { + secret_key: None, + public_key: hex_arr("EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34"), + aux_rand: None, + message: hex_vec("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + signature: hex_arr("6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + aux_rand: None, + message: hex_vec("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + signature: hex_arr("FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2"), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + aux_rand: None, + message: hex_vec("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + signature: hex_arr("1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD"), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + aux_rand: None, + message: hex_vec("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + signature: hex_arr("6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6"), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + aux_rand: None, + message: hex_vec("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + signature: hex_arr("0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051"), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + aux_rand: None, + message: hex_vec("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + signature: hex_arr("00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197"), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + aux_rand: None, + message: hex_vec("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + signature: hex_arr("4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + aux_rand: None, + message: hex_vec("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + signature: hex_arr("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + aux_rand: None, + message: hex_vec("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + signature: hex_arr("6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"), + should_fail_verify: true, + }, + TestVector { + secret_key: None, + public_key: hex_arr("778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117"), + aux_rand: None, + message: hex_vec("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + signature: hex_arr("6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"), + should_fail_verify: true, + }, + TestVector { + secret_key: hex_arr("0340034003400340034003400340034003400340034003400340034003400340"), + public_key: hex_arr("778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117"), + aux_rand: hex_arr("0000000000000000000000000000000000000000000000000000000000000000"), + message: hex_vec(""), + signature: hex_arr("71535DB165ECD9FBBC046E5FFAEA61186BB6AD436732FCCC25291A55895464CF6069CE26BF03466228F19A3A62DB8A649F2D560FAC652827D1AF0574E427AB63"), + should_fail_verify: false, + }, + TestVector { + secret_key: hex_arr("0340034003400340034003400340034003400340034003400340034003400340"), + public_key: hex_arr("778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117"), + aux_rand: hex_arr("0000000000000000000000000000000000000000000000000000000000000000"), + message: hex_vec("11"), + signature: hex_arr("08A20A0AFEF64124649232E0693C583AB1B9934AE63B4C3511F3AE1134C6A303EA3173BFEA6683BD101FA5AA5DBC1996FE7CACFC5A577D33EC14564CEC2BACBF"), + should_fail_verify: false, + }, + TestVector { + secret_key: hex_arr("0340034003400340034003400340034003400340034003400340034003400340"), + public_key: hex_arr("778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117"), + aux_rand: hex_arr("0000000000000000000000000000000000000000000000000000000000000000"), + message: hex_vec("0102030405060708090A0B0C0D0E0F1011"), + signature: hex_arr("5130F39A4059B43BC7CAC09A19ECE52B5D8699D1A71E3C52DA9AFDB6B50AC370C4A482B77BF960F8681540E25B6771ECE1E5A37FD80E5A51897C5566A97EA5A5"), + should_fail_verify: false, + }, + TestVector { + secret_key: hex_arr("0340034003400340034003400340034003400340034003400340034003400340"), + public_key: hex_arr("778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117"), + aux_rand: hex_arr("0000000000000000000000000000000000000000000000000000000000000000"), + message: hex_vec("99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"), + signature: hex_arr("403B12B0D8555A344175EA7EC746566303321E5DBFA8BE6F091635163ECA79A8585ED3E3170807E7C03B720FC54C7B23897FCBA0E9D0B4A06894CFD249F22367"), + should_fail_verify: false, + }, + ]; + let secp = Secp256k1::new(); + + for TestVector { + secret_key, + public_key, + aux_rand, + message, + signature, + should_fail_verify, + } in vectors + { + if let (Some(secret_key), Some(aux_rand)) = (secret_key, aux_rand) { + let keypair = Keypair::from_seckey_slice(&secp, &secret_key).unwrap(); + assert_eq!(keypair.x_only_public_key().0.serialize(), public_key); + let sig = secp.sign_schnorr_with_aux_rand(&message, &keypair, &aux_rand); + assert_eq!(sig.serialize(), signature); + } + let sig = Signature::from_slice(&signature).unwrap(); + let is_verified = if let Ok(pubkey) = XOnlyPublicKey::from_slice(&public_key) { + secp.verify_schnorr(&sig, &message, &pubkey).is_ok() + } else { + false + }; + assert_eq!(is_verified, !should_fail_verify); + } + } }