Files
bitcoin/src/musig.h
merge-script 2669019fe8 Merge bitcoin/bitcoin#35269: musig: Include pubnonce in session id
2ef6679c2c test: Check that MuSig2 signing does not reuse nonces (Ava Chow)
bb05986c0a musig: Include pubnonce in session id (Ava Chow)

Pull request description:

  It is safe to have multiple musig signing sessions over the same message so long as the nonces used are different. Including the pubnonce in the session id allows for multiple simultaneous signing sessions over the same message, rather than asserting when the user tries to do this.

  The second commit tests this behavior, both ensuring that there is no crash, and verifying that both sessions produce unique nonces and signatures to verify that no reuse is occurring.

  Lastly, the assertion in `SetMuSig2SecNonce` is retained as hitting it now would indicate that a nonce has been reused. We prefer to assert and crash rather than do something that is highly likely to leak a private key.

  Fixes #35250

ACKs for top commit:
  rkrux:
    lgtm ACK 2ef6679c2c
  junbyjun1238:
    utACK 2ef6679c2c
  theStack:
    ACK 2ef6679c2c

Tree-SHA512: 9fb60b68ebe0ea9656408afb65b9ec9f280632e1bb84a4821b074c8d8569847845f7c29da800c757b9ddf3aa31aa890dd9e3646cf119917a714e7daf20be2198
2026-06-02 21:33:01 +02:00

71 lines
3.3 KiB
C++

// Copyright (c) 2024-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_MUSIG_H
#define BITCOIN_MUSIG_H
#include <pubkey.h>
#include <optional>
#include <vector>
class CKey;
struct secp256k1_musig_keyagg_cache;
class MuSig2SecNonceImpl;
struct secp256k1_musig_secnonce;
constexpr size_t MUSIG2_PUBNONCE_SIZE{66};
//! Compute the full aggregate pubkey from the given participant pubkeys in their current order.
//! Outputs the secp256k1_musig_keyagg_cache and validates that the computed aggregate pubkey matches an expected aggregate pubkey.
//! This is necessary for most MuSig2 operations.
std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache, const std::optional<CPubKey>& expected_aggregate);
std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys);
//! Construct the BIP 328 synthetic xpub for a pubkey
CExtPubKey CreateMuSig2SyntheticXpub(const CPubKey& pubkey);
/**
* MuSig2SecNonce encapsulates a secret nonce in use in a MuSig2 signing session.
* Since this nonce persists outside of libsecp256k1 signing code, we must handle
* its construction and destruction ourselves.
* The secret nonce must be kept a secret, otherwise the private key may be leaked.
* As such, it needs to be treated in the same way that CKeys are treated.
* So this class handles the secure allocation of the secp256k1_musig_secnonce object
* that libsecp256k1 uses, and only gives out references to this object to avoid
* any possibility of copies being made. Furthermore, objects of this class are not
* copyable to avoid nonce reuse.
*/
class MuSig2SecNonce
{
private:
std::unique_ptr<MuSig2SecNonceImpl> m_impl;
public:
MuSig2SecNonce();
MuSig2SecNonce(MuSig2SecNonce&&) noexcept;
MuSig2SecNonce& operator=(MuSig2SecNonce&&) noexcept;
~MuSig2SecNonce();
// Delete copy constructors
MuSig2SecNonce(const MuSig2SecNonce&) = delete;
MuSig2SecNonce& operator=(const MuSig2SecNonce&) = delete;
secp256k1_musig_secnonce* Get() const;
void Invalidate();
bool IsValid();
};
/**
* Computes an arbitrary unique session ID to identify ongoing signing sessions.
* It is the SHA256 of the aggregate xonly key, the participant pubkey, the sighash, and the pubnonce
*/
uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256& sighash, const std::vector<uint8_t>& pubnonce);
std::vector<uint8_t> CreateMuSig2Nonce(MuSig2SecNonce& secnonce, const uint256& sighash, const CKey& our_seckey, const CPubKey& aggregate_pubkey, const std::vector<CPubKey>& pubkeys);
std::optional<uint256> CreateMuSig2PartialSig(const uint256& hash, const CKey& our_seckey, const CPubKey& aggregate_pubkey, const std::vector<CPubKey>& pubkeys, const std::map<CPubKey, std::vector<uint8_t>>& pubnonces, MuSig2SecNonce& secnonce, const std::vector<std::pair<uint256, bool>>& tweaks);
std::optional<std::vector<uint8_t>> CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, const CPubKey& aggregate_pubkey, const std::vector<std::pair<uint256, bool>>& tweaks, const uint256& sighash, const std::map<CPubKey, std::vector<uint8_t>>& pubnonces, const std::map<CPubKey, uint256>& partial_sigs);
#endif // BITCOIN_MUSIG_H