Skip to content

Commit

Permalink
schnorrsig: expose tweak_check_add API
Browse files Browse the repository at this point in the history
  • Loading branch information
apoelstra committed Nov 27, 2020
1 parent 12b0abb commit 37049d7
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 11 deletions.
3 changes: 3 additions & 0 deletions src/lib.rs
Expand Up @@ -521,6 +521,8 @@ pub enum Error {
InvalidRecoveryId,
/// Invalid tweak for add_*_assign or mul_*_assign
InvalidTweak,
/// `tweak_add_check` failed on an xonly public key
TweakCheckFailed,
/// Didn't pass enough memory to context creation with preallocated memory
NotEnoughMemory,
}
Expand All @@ -535,6 +537,7 @@ impl Error {
Error::InvalidSecretKey => "secp: malformed or out-of-range secret key",
Error::InvalidRecoveryId => "secp: bad recovery id",
Error::InvalidTweak => "secp: bad tweak",
Error::TweakCheckFailed => "secp: xonly_pubkey_tewak_add_check failed",
Error::NotEnoughMemory => "secp: not enough memory allocated",
}
}
Expand Down
66 changes: 55 additions & 11 deletions src/schnorrsig.rs
Expand Up @@ -169,7 +169,7 @@ impl KeyPair {
/// Will return an error if the resulting key would be invalid or if
/// the tweak was not a 32-byte length slice.
#[inline]
pub fn add_assign<C: Verification>(
pub fn tweak_add_assign<C: Verification>(
&mut self,
secp: &Secp256k1<C>,
tweak: &[u8],
Expand Down Expand Up @@ -264,14 +264,17 @@ impl PublicKey {
ret
}

/// Tweak a schnorrsig 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.
pub fn add_assign<V: Verification>(
/// Tweak an x-only PublicKey by adding the generator multiplied with the given tweak to it.
///
/// Returns a boolean representing the parity of the tweaked key, which can be provided to
/// `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,
secp: &Secp256k1<V>,
tweak: &[u8],
) -> Result<(), Error> {
) -> Result<bool, Error> {
if tweak.len() != 32 {
return Err(Error::InvalidTweak);
}
Expand All @@ -289,18 +292,57 @@ impl PublicKey {
return Err(Error::InvalidTweak);
}

let mut parity: ::secp256k1_sys::types::c_int = 0;
err = ffi::secp256k1_xonly_pubkey_from_pubkey(
secp.ctx,
&mut self.0 as *mut _,
ptr::null_mut(),
&mut parity as *mut _,
&pubkey,
);

return if err == 0 {
if err == 0 {
Err(Error::InvalidPublicKey)
} 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(())
};
} else {
Err(Error::TweakCheckFailed)
}
}
}
}
Expand Down Expand Up @@ -720,9 +762,11 @@ mod tests {
let mut tweak = [0u8; 32];
thread_rng().fill_bytes(&mut tweak);
let (mut kp, mut pk) = s.generate_schnorrsig_keypair(&mut thread_rng());
kp.add_assign(&s, &tweak).expect("Tweak error");
pk.add_assign(&s, &tweak).expect("Tweak error");
let orig_pk = pk;
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);
orig_pk.tweak_add_check(&s, &pk, parity, &tweak).expect("tweak check");
}
}

Expand Down

0 comments on commit 37049d7

Please sign in to comment.