musig2 0.4.0

Flexible Rust implementation of the MuSig2 multisignature protocol, compatible with Bitcoin.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
//! Various error types for different kinds of failures.

use crate::KeyAggContext;

use std::error::Error;
use std::fmt;

/// Returned when aggregating a collection of public keys with [`KeyAggContext`]
/// results in the point at infinity.
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub struct KeyAggError;
impl fmt::Display for KeyAggError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("computed an invalid aggregated key from a collection of public keys")
    }
}
impl Error for KeyAggError {}
impl From<secp::errors::InfinityPointError> for KeyAggError {
    fn from(_: secp::errors::InfinityPointError) -> Self {
        KeyAggError
    }
}

/// Returned when aggregating a collection of secret keys with [`KeyAggContext`],
/// but some secret keys are missing, or the keys are not the correct secret keys
/// for the pubkeys contained in the key agg context.
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub struct InvalidSecretKeysError;
impl fmt::Display for InvalidSecretKeysError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("missing or invalid secret keys provided for aggregation")
    }
}
impl Error for InvalidSecretKeysError {}
impl From<secp::errors::ZeroScalarError> for InvalidSecretKeysError {
    fn from(_: secp::errors::ZeroScalarError) -> Self {
        InvalidSecretKeysError
    }
}

/// Returned when tweaking a [`KeyAggContext`] results in the point
/// at infinity, or if using [`KeyAggContext::with_taproot_tweak`]
/// when the tweak input results in a hash which exceeds the curve
/// order (exceedingly unlikely)"
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub struct TweakError;
impl fmt::Display for TweakError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("tweak value is invalid")
    }
}
impl Error for TweakError {}
impl From<secp::errors::InfinityPointError> for TweakError {
    fn from(_: secp::errors::InfinityPointError) -> Self {
        TweakError
    }
}

/// A class of error returned by the state-machine API when creating a
/// [`FirstRound`][crate::FirstRound] fails.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum RoundSetupError {
    /// Returned when passing a signer index which is out of range for a
    /// group of signers.
    InvalidSignerIndex {
        /// The index of the signer we did not expect to receive.
        index: usize,

        /// The total size of the signing group.
        n_signers: usize,
    },

    /// Returned when a secret key is passed into [`SecNonceSpices`][crate::SecNonceSpices],
    /// but that secret key does not match the public key implied by the signer index.
    MismatchingSecretKey,
}

impl RoundSetupError {
    /// Construct a new `InvalidSignerIndex` error, indicating we received an
    /// invalid index for the given group size of signers.
    pub(crate) fn signer_index(index: usize, n_signers: usize) -> Self {
        Self::InvalidSignerIndex { index, n_signers }
    }
}

impl fmt::Display for RoundSetupError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            RoundSetupError::InvalidSignerIndex { index, n_signers } => write!(
                f,
                "signer index {} is out of range for group of {} signers",
                index, n_signers
            ),
            RoundSetupError::MismatchingSecretKey => write!(
                f,
                "secret key given in SecNonceSpices does not correspond to the requested signer_index"
            ),
        }
    }
}
impl Error for RoundSetupError {}

/// Error returned when (partial) signing fails.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum SigningError {
    /// Indicates an unknown secret key was provided when
    /// using [`sign_partial`][crate::sign_partial] or
    /// finalizing the [`FirstRound`][crate::FirstRound].
    UnknownKey,

    /// The public key bound into the secret nonce does not match the secret
    /// key being used for signing.
    ///
    /// This is a local consistency check for callers carrying a `SecNonce`
    /// from nonce generation to signing. It does not authenticate raw
    /// `SecNonce` bytes supplied by another party.
    SecNoncePubkeyMismatch,

    /// We could not verify the signature we produced.
    /// This may indicate a malicious actor attempted to make us
    /// produce a signature which could reveal our secret key. The
    /// signing session should be aborted and retried with new nonces.
    SelfVerifyFail,
}
impl fmt::Display for SigningError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "failed to create signature: {}",
            match self {
                Self::UnknownKey => "signing key is not a member of the group",
                Self::SecNoncePubkeyMismatch =>
                    "secret nonce public key does not match signing key",
                Self::SelfVerifyFail => "failed to verify our own signature; something is wrong",
            }
        )
    }
}
impl Error for SigningError {}

/// Error returned when verification fails.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum VerifyError {
    /// Indicates a public key was provided which is not
    /// a member of the signing group, and thus partial
    /// signature verification on this key has no meaning.
    UnknownKey,

    /// The signature is not valid for the given key and message.
    BadSignature,
}
impl fmt::Display for VerifyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "failed to verify signature: {}",
            match self {
                Self::UnknownKey => "public key is not a member of the group",
                Self::BadSignature => "signature is invalid",
            }
        )
    }
}
impl Error for VerifyError {}

impl From<VerifyError> for SigningError {
    fn from(_: VerifyError) -> Self {
        SigningError::SelfVerifyFail
    }
}

/// Enumerates the causes for why receiving a contribution from a peer
/// might fail.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ContributionFaultReason {
    /// The signer's index is out of range for the given
    /// number of signers in the group. Embeds `n_signers`
    /// (the number of signers).
    OutOfRange(usize),

    /// Indicates we received different contribution values from
    /// this peer for the same round. If we receive the same
    /// nonce or signature from this peer more than once this is
    /// acceptable and treated as a no-op, but receiving inconsistent
    /// contributions from the same signer may indicate there is
    /// malicious behavior occurring.
    InconsistentContribution,

    /// Indicates we received an invalid partial signature. Only returned by
    /// [`SecondRound::receive_signature`][crate::SecondRound::receive_signature].
    InvalidSignature,
}

/// This error is returned by when a peer provides an invalid contribution
/// to one of the signing rounds.
///
/// This is either because the signer's index exceeds the maximum, or
/// because we received an invalid contribution from this signer.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RoundContributionError {
    /// The erroneous signer index.
    pub index: usize,

    /// The reason why the signer's contribution was rejected.
    pub reason: ContributionFaultReason,
}

impl RoundContributionError {
    /// Create a new out of range signer index error.
    pub fn out_of_range(index: usize, n_signers: usize) -> RoundContributionError {
        RoundContributionError {
            index,
            reason: ContributionFaultReason::OutOfRange(n_signers),
        }
    }

    /// Create an error caused by an inconsistent contribution.
    pub fn inconsistent_contribution(index: usize) -> RoundContributionError {
        RoundContributionError {
            index,
            reason: ContributionFaultReason::InconsistentContribution,
        }
    }

    /// Create a new error caused by an invalid partial signature.
    pub fn invalid_signature(index: usize) -> RoundContributionError {
        RoundContributionError {
            index,
            reason: ContributionFaultReason::InvalidSignature,
        }
    }
}

impl fmt::Display for RoundContributionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use ContributionFaultReason::*;
        write!(
            f,
            "invalid signer index {}: {}",
            self.index,
            match self.reason {
                OutOfRange(n_signers) => format!("exceeds max index for {} signers", n_signers),
                InconsistentContribution =>
                    "received inconsistent contributions from same signer".to_string(),
                InvalidSignature => "received invalid partial signature from peer".to_string(),
            }
        )
    }
}

impl Error for RoundContributionError {}

/// Returned when finalizing [`FirstRound`][crate::FirstRound] or
/// [`SecondRound`][crate::SecondRound] fails.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum RoundFinalizeError {
    /// Contributions from all signers in the group are required to finalize
    /// a signing round. This error is returned if attempting to finalize
    /// a round before all contributions are received.
    Incomplete,

    /// Indicates partial signing failed unexpectedly. This is likely because
    /// the wrong secret key was provided. Only returned by
    /// [`FirstRound::finalize`][crate::FirstRound::finalize].
    SigningError(SigningError),

    /// Indicates the final aggregated signature is invalid. Only returned by
    /// [`SecondRound::finalize`][crate::SecondRound::finalize].
    InvalidAggregatedSignature(VerifyError),
}

impl fmt::Display for RoundFinalizeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "cannot finalize round: {}",
            match self {
                Self::Incomplete => "not all signers have contributed".to_string(),
                Self::SigningError(e) => format!("signing failed, {}", e),
                Self::InvalidAggregatedSignature(e) =>
                    format!("could not verify aggregated signature: {}", e),
            }
        )
    }
}

impl Error for RoundFinalizeError {}

impl From<SigningError> for RoundFinalizeError {
    fn from(e: SigningError) -> Self {
        RoundFinalizeError::SigningError(e)
    }
}

impl From<VerifyError> for RoundFinalizeError {
    fn from(e: VerifyError) -> Self {
        RoundFinalizeError::InvalidAggregatedSignature(e)
    }
}

/// Enumerates the various reasons why binary or hex decoding
/// could fail.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum DecodeFailureReason {
    /// The hex string's format was incorrect, which could mean
    /// it either was the wrong length or held invalid characters.
    BadHexFormat(base16ct::Error),

    /// The byte slice we tried to deserialize had the wrong length.
    BadLength(usize),

    /// The bytes contained coordinates to a point that is not on
    /// the secp256k1 curve.
    InvalidPoint,

    /// The bytes slice contained a representation of a scalar which
    /// is outside the required finite field's range.
    InvalidScalar,

    /// Custom error reason.
    Custom(String),
}

/// Returned when decoding a certain data structure of type `T` fails.
///
/// The type `T` only serves as a compile-time safety check; no
/// data of type `T` is actually owned by this error.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DecodeError<T> {
    /// The reason for the decoding failure.
    pub reason: DecodeFailureReason,
    phantom: std::marker::PhantomData<T>,
}

impl<T> DecodeError<T> {
    /// Construct a new decoding error for type `T` given a cause
    /// for the failure.
    pub fn new(reason: DecodeFailureReason) -> Self {
        DecodeError {
            reason,
            phantom: std::marker::PhantomData,
        }
    }

    /// Create a decoding error caused by an incorrect input byte
    /// slice length.
    pub fn bad_length(size: usize) -> Self {
        let reason = DecodeFailureReason::BadLength(size);
        DecodeError::new(reason)
    }

    /// Create a custom decoding failure.
    pub fn custom(s: impl fmt::Display) -> Self {
        let reason = DecodeFailureReason::Custom(s.to_string());
        DecodeError::new(reason)
    }

    /// Converts the decoding error for one type into that of another type.
    pub fn convert<U>(self) -> DecodeError<U> {
        DecodeError::new(self.reason)
    }
}

impl<T> fmt::Display for DecodeError<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use DecodeFailureReason::*;

        write!(
            f,
            "error decoding {}: {}",
            std::any::type_name::<T>(),
            match &self.reason {
                BadHexFormat(e) => format!("hex decoding error: {}", e),
                BadLength(size) => format!("unexpected length {}", size),
                InvalidPoint => secp::errors::InvalidPointBytes.to_string(),
                InvalidScalar => secp::errors::InvalidScalarBytes.to_string(),
                Custom(s) => s.to_string(),
            }
        )
    }
}

impl<T: fmt::Debug> std::error::Error for DecodeError<T> {}

impl<T> From<secp::errors::InvalidPointBytes> for DecodeError<T> {
    fn from(_: secp::errors::InvalidPointBytes) -> Self {
        DecodeError::new(DecodeFailureReason::InvalidPoint)
    }
}

impl<T> From<secp::errors::InvalidScalarBytes> for DecodeError<T> {
    fn from(_: secp::errors::InvalidScalarBytes) -> Self {
        DecodeError::new(DecodeFailureReason::InvalidScalar)
    }
}

impl<T> From<base16ct::Error> for DecodeError<T> {
    fn from(e: base16ct::Error) -> Self {
        DecodeError::new(DecodeFailureReason::BadHexFormat(e))
    }
}

impl From<KeyAggError> for DecodeError<KeyAggContext> {
    fn from(e: KeyAggError) -> Self {
        DecodeError::custom(e)
    }
}

impl From<TweakError> for DecodeError<KeyAggContext> {
    fn from(_: TweakError) -> Self {
        DecodeError::custom("serialized KeyAggContext contains an invalid tweak")
    }
}