- Deps: Remove direct dependence on
once_cell(#11f09e4397) - Fixed panic: Previously, if the third-party type
secp256k1::Scalarwas passed to any of the myriad methods ofmusig2which acceptimpl Into<secp::Scalar>, it would cause a panic whensecp256k1::Scalar::ZEROis given. This was fixed upstream (84a9c4c1) and applied inmusig2in this commit. - Zero Tweaks: The
KeyAggContexttype previously accepted only non-zero scalar types - i.e. any type implementingInto<Scalar>- in its tweaking methods. These methods now acceptimpl Into<MaybeScalar>(#16), allowing callers to tweak aKeyAggContextwith a no-op/normalization tweak to better comply with BIP327.
-
The
musig2API no longer accepts the third-party typesecp256k1::Scalarin parameters which must implementInto<secp::Scalar>. This is becausesecp256k1::Scalarcan be zero, butsecp::Scalarcannot. This change was required to prevent panics. -
The following input parameter type restrictions have been changed on the methods of
KeyAggContext:
-pub fn with_tweak(self, tweak: impl Into<Scalar>, is_xonly: bool) -> Result<Self, TweakError>;
+pub fn with_tweak(self, tweak: impl Into<MaybeScalar>, is_xonly: bool) -> Result<Self, TweakError>;
pub fn with_tweaks<S, I>(mut self, tweaks: I) -> Result<Self, TweakError>
where
I: IntoIterator<Item = (S, bool)>,
- S: Into<Scalar>,
+ S: Into<MaybeScalar>
-pub fn with_plain_tweak(self, tweak: impl Into<Scalar>) -> Result<Self, TweakError>;
+pub fn with_plain_tweak(self, tweak: impl Into<MaybeScalar>) -> Result<Self, TweakError>;
-pub fn with_xonly_tweak(self, tweak: impl Into<Scalar>) -> Result<Self, TweakError>;
+pub fn with_xonly_tweak(self, tweak: impl Into<MaybeScalar>) -> Result<Self, TweakError>;For most use-cases, this change should be backwards compatible. Most types which implement Into<Scalar> also implement Into<MaybeScalar>. However if you have implemented Into<Scalar> on a custom type, you may also need to implement Into<MaybeScalar> explicitly as well to make that type a valid argument to these methods.
- DX: New benchmarks for verification, signing and comparison against libsecp256k1.
- SecNonce changes: To fully comply with BIP-327, the
SecNonceandSecNonceBuilderAPI have been changed (#17). TheSecNoncedata structure now includes the signer's public key. See below for migration instructions. - Security Improvement: To align with BIP-327 security recommendations, we now validate the pubkey contained in
SecNonceagainst the key provided by the signer in thesign_partialandadaptor::sign_partialfunctions, and if they do not align we return a new error enum memberSigningError::SecNoncePubkeyMismatch. In the state-machine API, this can only happen if you pass in the secret key of a different pubkey within the sameKeyAggContextas passed toFirstRound::new.
- The
SecNonceserialization format has been updated to comply with BIP327.
/// The size of a serialized [`SecNonce`] in bytes.
-pub const SEC_NONCE_SIZE: usize = 64;
+pub const SEC_NONCE_SIZE: usize = 97;This affects any downstream callers who store SecNonce out of band, and load it later (e.g long-lived signing sessions).
Recommended Fix: Add middleware code or perform database migration to append the signer's individual 33-byte public key before deserializing a SecNonce created by musig2 version v0.3.1 or earlier.
- The
SecNonceBuilderAPI has changed.
impl<'snb> SecNonceBuilder<'snb> {
- pub fn new(nonce_seed: impl Into<NonceSeed>) -> SecNonceBuilder<'snb>;
- pub fn with_pubkey(self, pubkey: impl Into<secp::Point>) -> SecNonceBuilder<'snb>;
- pub fn with_seckey(self, seckey: impl Into<secp::Scalar>) -> SecNonceBuilder<'snb>;
+ pub fn from_pubkey(
+ nonce_seed: impl Into<NonceSeed>,
+ pubkey: impl Into<secp::Point>,
+ ) -> SecNonceBuilder<'snb>;
+ pub fn from_seckey(
+ nonce_seed: impl Into<NonceSeed>,
+ seckey: impl Into<secp::Scalar>,
+ ) -> SecNonceBuilder<'snb>;
}To comply with BIP-327, a public or secret key is now a mandatory parameter needed to build a SecNonce. As such, the SecNonceBuilder::with_pubkey and SecNonceBuilder::with_seckey methods have been removed, and the SecNonceBuilder::new(nonce_seed) constructor method has been replaced with two new constructors:
SecNonceBuilder::from_pubkey(nonce_seed, pubkey), which takes any type that converts tosecp::Point.SecNonceBuilder::from_seckey(nonce_seed, seckey), which takes any type that converts tosecp::Scalar.
Recommended Fix: The state-machine API will handle this adjustment for you. If you use the state-machine API, no action should be needed. If you use the functional API, you will need to update your code to provide a public key, or if possible a secret key, when building a SecNonce using one of the two new constructors. Callers who wish to update a SecNonceBuilder with a secret key after calling a constructor may still do so, using the SecNonceBuilder::with_spices method, but beware this overwrites any public key (or secret key) set by the constructor.
- The
SecNonceAPI has changed.
impl SecNonce {
- pub fn new<T: Into<secp::Scalar>>(k1: T, k2: T) -> SecNonce;
- pub fn build<'snb>(nonce_seed: impl Into<NonceSeed>) -> SecNonceBuilder<'snb>;
- pub fn random<R>(rng: &mut R) -> SecNonce
+ pub fn new<T: Into<secp::Scalar>>(k1: T, k2: T, pubkey: impl Into<secp::Point>) -> SecNonce;
+ pub fn build_with_pubkey<'snb>(
+ nonce_seed: impl Into<NonceSeed>,
+ pubkey: impl Into<Point>,
+ ) -> SecNonceBuilder<'snb>;
+ pub fn build_with_seckey<'snb>(
+ nonce_seed: impl Into<NonceSeed>,
+ seckey: impl Into<Scalar>,
+ ) -> SecNonceBuilder<'snb>;
+ pub fn random<R>(rng: &mut R, pubkey: impl Into<secp::Point>) -> SecNonce
where
R: rand::RngCore + rand::CryptoRng;
}These changes are mostly a reflection of the above changes to the SecNonceBuilder API.
- The
SecNonce::newconstructor arguments have been extended with a mandatory public key parameter. - The
SecNonce::buildshortcut method has been split into two new methods to reflect the new constructors forSecNonceBuilder:SecNonce::build_with_pubkeyis an alias toSecNonceBuilder::from_pubkey.SecNonce::build_with_seckeyis an alias toSecNonceBuilder::from_seckey.
- The
SecNonce::randommethod arguments have been extended with a mandatory public key parameter.
Recommended Fix: The state-machine API will handle this adjustment for you. If you use the state-machine API, no action should be needed. If you use the functional API, you will need to update your code to provide a public or secret key when building a SecNonce.
FirstRound::newreturns a new error type,RoundSetupError.
impl FirstRound {
pub fn new(
key_agg_ctx: KeyAggContext,
nonce_seed: impl Into<NonceSeed>,
signer_index: usize,
spices: SecNonceSpices<'_>,
- ) -> Result<FirstRound, SignerIndexError>;
+ ) -> Result<FirstRound, RoundSetupError>;
}This change reflects a new check included in FirstRound::new which ensures any secret key provided in the spices parameter aligns with the public key implied by the key_agg_ctx and signer_index parameters.
See the docs for FirstRound::new for more info.
Recommended Fix: Adjust error handling code as needed. If you do not use the seckey field of SecNonceSpices, or if the seckey field is always guaranteed to match with the pubkey at signer_index, this doesn't affect you.