schnorrsig: expose tweak_check_add API

This commit is contained in:
Andrew Poelstra 2020-11-27 18:31:00 +00:00
parent 12b0abbcf8
commit 37049d743e
2 changed files with 58 additions and 11 deletions

View File

@ -521,6 +521,8 @@ pub enum Error {
InvalidRecoveryId, InvalidRecoveryId,
/// Invalid tweak for add_*_assign or mul_*_assign /// Invalid tweak for add_*_assign or mul_*_assign
InvalidTweak, InvalidTweak,
/// `tweak_add_check` failed on an xonly public key
TweakCheckFailed,
/// Didn't pass enough memory to context creation with preallocated memory /// Didn't pass enough memory to context creation with preallocated memory
NotEnoughMemory, NotEnoughMemory,
} }
@ -535,6 +537,7 @@ impl Error {
Error::InvalidSecretKey => "secp: malformed or out-of-range secret key", Error::InvalidSecretKey => "secp: malformed or out-of-range secret key",
Error::InvalidRecoveryId => "secp: bad recovery id", Error::InvalidRecoveryId => "secp: bad recovery id",
Error::InvalidTweak => "secp: bad tweak", Error::InvalidTweak => "secp: bad tweak",
Error::TweakCheckFailed => "secp: xonly_pubkey_tewak_add_check failed",
Error::NotEnoughMemory => "secp: not enough memory allocated", Error::NotEnoughMemory => "secp: not enough memory allocated",
} }
} }

View File

@ -169,7 +169,7 @@ impl KeyPair {
/// Will return an error if the resulting key would be invalid or if /// Will return an error if the resulting key would be invalid or if
/// the tweak was not a 32-byte length slice. /// the tweak was not a 32-byte length slice.
#[inline] #[inline]
pub fn add_assign<C: Verification>( pub fn tweak_add_assign<C: Verification>(
&mut self, &mut self,
secp: &Secp256k1<C>, secp: &Secp256k1<C>,
tweak: &[u8], tweak: &[u8],
@ -264,14 +264,17 @@ impl PublicKey {
ret ret
} }
/// Tweak a schnorrsig PublicKey by adding the generator multiplied with the given tweak to it. /// Tweak an x-only PublicKey by adding the generator multiplied with the given tweak to it.
/// Will return an error if the resulting key would be invalid or if ///
/// the tweak was not a 32-byte length slice. /// Returns a boolean representing the parity of the tweaked key, which can be provided to
pub fn add_assign<V: Verification>( /// `tweak_add_check` which can be used to verify a tweak more efficiently than regenerating
/// it and checking equality. Will return an error if the resulting key would be invalid or
/// if the tweak was not a 32-byte length slice.
pub fn tweak_add_assign<V: Verification>(
&mut self, &mut self,
secp: &Secp256k1<V>, secp: &Secp256k1<V>,
tweak: &[u8], tweak: &[u8],
) -> Result<(), Error> { ) -> Result<bool, Error> {
if tweak.len() != 32 { if tweak.len() != 32 {
return Err(Error::InvalidTweak); return Err(Error::InvalidTweak);
} }
@ -289,18 +292,57 @@ impl PublicKey {
return Err(Error::InvalidTweak); return Err(Error::InvalidTweak);
} }
let mut parity: ::secp256k1_sys::types::c_int = 0;
err = ffi::secp256k1_xonly_pubkey_from_pubkey( err = ffi::secp256k1_xonly_pubkey_from_pubkey(
secp.ctx, secp.ctx,
&mut self.0 as *mut _, &mut self.0 as *mut _,
ptr::null_mut(), &mut parity as *mut _,
&pubkey, &pubkey,
); );
return if err == 0 { if err == 0 {
Err(Error::InvalidPublicKey) Err(Error::InvalidPublicKey)
} else { } else {
Ok(parity != 0)
}
}
}
/// Verify that a tweak produced by `tweak_add_assign` was computed correctly
///
/// Should be called on the original untweaked key. Takes the tweaked key and
/// output parity from `tweak_add_assign` as input.
///
/// Currently this is not much more efficient than just recomputing the tweak
/// and checking equality. However, in future this API will support batch
/// verification, which is significantly faster, so it is wise to design
/// protocols with this in mind.
pub fn tweak_add_check<V: Verification>(
&self,
secp: &Secp256k1<V>,
tweaked_key: &Self,
tweaked_parity: bool,
tweak: &[u8],
) -> Result<(), Error> {
if tweak.len() != 32 {
return Err(Error::InvalidTweak);
}
let tweaked_ser = tweaked_key.serialize();
unsafe {
let err = ffi::secp256k1_xonly_pubkey_tweak_add_check(
secp.ctx,
tweaked_ser.as_c_ptr(),
if tweaked_parity { 1 } else { 0 },
&self.0 as *const _,
tweak.as_c_ptr(),
);
if err == 1 {
Ok(()) Ok(())
}; } else {
Err(Error::TweakCheckFailed)
}
} }
} }
} }
@ -720,9 +762,11 @@ mod tests {
let mut tweak = [0u8; 32]; let mut tweak = [0u8; 32];
thread_rng().fill_bytes(&mut tweak); thread_rng().fill_bytes(&mut tweak);
let (mut kp, mut pk) = s.generate_schnorrsig_keypair(&mut thread_rng()); let (mut kp, mut pk) = s.generate_schnorrsig_keypair(&mut thread_rng());
kp.add_assign(&s, &tweak).expect("Tweak error"); let orig_pk = pk;
pk.add_assign(&s, &tweak).expect("Tweak error"); kp.tweak_add_assign(&s, &tweak).expect("Tweak error");
let parity = pk.tweak_add_assign(&s, &tweak).expect("Tweak error");
assert_eq!(PublicKey::from_keypair(&s, &kp), pk); assert_eq!(PublicKey::from_keypair(&s, &kp), pk);
orig_pk.tweak_add_check(&s, &pk, parity, &tweak).expect("tweak check");
} }
} }