Skip to content

Commit 37049d7

Browse files
committed
schnorrsig: expose tweak_check_add API
1 parent 12b0abb commit 37049d7

File tree

2 files changed

+58
-11
lines changed

2 files changed

+58
-11
lines changed

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,8 @@ pub enum Error {
521521
InvalidRecoveryId,
522522
/// Invalid tweak for add_*_assign or mul_*_assign
523523
InvalidTweak,
524+
/// `tweak_add_check` failed on an xonly public key
525+
TweakCheckFailed,
524526
/// Didn't pass enough memory to context creation with preallocated memory
525527
NotEnoughMemory,
526528
}
@@ -535,6 +537,7 @@ impl Error {
535537
Error::InvalidSecretKey => "secp: malformed or out-of-range secret key",
536538
Error::InvalidRecoveryId => "secp: bad recovery id",
537539
Error::InvalidTweak => "secp: bad tweak",
540+
Error::TweakCheckFailed => "secp: xonly_pubkey_tewak_add_check failed",
538541
Error::NotEnoughMemory => "secp: not enough memory allocated",
539542
}
540543
}

src/schnorrsig.rs

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ impl KeyPair {
169169
/// Will return an error if the resulting key would be invalid or if
170170
/// the tweak was not a 32-byte length slice.
171171
#[inline]
172-
pub fn add_assign<C: Verification>(
172+
pub fn tweak_add_assign<C: Verification>(
173173
&mut self,
174174
secp: &Secp256k1<C>,
175175
tweak: &[u8],
@@ -264,14 +264,17 @@ impl PublicKey {
264264
ret
265265
}
266266

267-
/// Tweak a schnorrsig PublicKey by adding the generator multiplied with the given tweak to it.
268-
/// Will return an error if the resulting key would be invalid or if
269-
/// the tweak was not a 32-byte length slice.
270-
pub fn add_assign<V: Verification>(
267+
/// Tweak an x-only PublicKey by adding the generator multiplied with the given tweak to it.
268+
///
269+
/// Returns a boolean representing the parity of the tweaked key, which can be provided to
270+
/// `tweak_add_check` which can be used to verify a tweak more efficiently than regenerating
271+
/// it and checking equality. Will return an error if the resulting key would be invalid or
272+
/// if the tweak was not a 32-byte length slice.
273+
pub fn tweak_add_assign<V: Verification>(
271274
&mut self,
272275
secp: &Secp256k1<V>,
273276
tweak: &[u8],
274-
) -> Result<(), Error> {
277+
) -> Result<bool, Error> {
275278
if tweak.len() != 32 {
276279
return Err(Error::InvalidTweak);
277280
}
@@ -289,18 +292,57 @@ impl PublicKey {
289292
return Err(Error::InvalidTweak);
290293
}
291294

295+
let mut parity: ::secp256k1_sys::types::c_int = 0;
292296
err = ffi::secp256k1_xonly_pubkey_from_pubkey(
293297
secp.ctx,
294298
&mut self.0 as *mut _,
295-
ptr::null_mut(),
299+
&mut parity as *mut _,
296300
&pubkey,
297301
);
298302

299-
return if err == 0 {
303+
if err == 0 {
300304
Err(Error::InvalidPublicKey)
301305
} else {
306+
Ok(parity != 0)
307+
}
308+
}
309+
}
310+
311+
/// Verify that a tweak produced by `tweak_add_assign` was computed correctly
312+
///
313+
/// Should be called on the original untweaked key. Takes the tweaked key and
314+
/// output parity from `tweak_add_assign` as input.
315+
///
316+
/// Currently this is not much more efficient than just recomputing the tweak
317+
/// and checking equality. However, in future this API will support batch
318+
/// verification, which is significantly faster, so it is wise to design
319+
/// protocols with this in mind.
320+
pub fn tweak_add_check<V: Verification>(
321+
&self,
322+
secp: &Secp256k1<V>,
323+
tweaked_key: &Self,
324+
tweaked_parity: bool,
325+
tweak: &[u8],
326+
) -> Result<(), Error> {
327+
if tweak.len() != 32 {
328+
return Err(Error::InvalidTweak);
329+
}
330+
331+
let tweaked_ser = tweaked_key.serialize();
332+
unsafe {
333+
let err = ffi::secp256k1_xonly_pubkey_tweak_add_check(
334+
secp.ctx,
335+
tweaked_ser.as_c_ptr(),
336+
if tweaked_parity { 1 } else { 0 },
337+
&self.0 as *const _,
338+
tweak.as_c_ptr(),
339+
);
340+
341+
if err == 1 {
302342
Ok(())
303-
};
343+
} else {
344+
Err(Error::TweakCheckFailed)
345+
}
304346
}
305347
}
306348
}
@@ -720,9 +762,11 @@ mod tests {
720762
let mut tweak = [0u8; 32];
721763
thread_rng().fill_bytes(&mut tweak);
722764
let (mut kp, mut pk) = s.generate_schnorrsig_keypair(&mut thread_rng());
723-
kp.add_assign(&s, &tweak).expect("Tweak error");
724-
pk.add_assign(&s, &tweak).expect("Tweak error");
765+
let orig_pk = pk;
766+
kp.tweak_add_assign(&s, &tweak).expect("Tweak error");
767+
let parity = pk.tweak_add_assign(&s, &tweak).expect("Tweak error");
725768
assert_eq!(PublicKey::from_keypair(&s, &kp), pk);
769+
orig_pk.tweak_add_check(&s, &pk, parity, &tweak).expect("tweak check");
726770
}
727771
}
728772

0 commit comments

Comments
 (0)