Skip to content
Back to skills

Musig2

ASecurity

MuSig2 (BIP327): two-round Schnorr key aggregation for n-of-n multisig. Aggregates n public keys into a single 32-byte x-only pubkey indistinguishable from a single-sig. Used in Taproot cooperative spends. Quick refs: protocol round-trip, key aggregation math, common attacks. USE WHEN: implementing n-of-n cooperative Taproot signing, designing Lightning Taproot channels, joint custody.

  • 31 stars
  • 0 votes
  • 0 copies
  • 7 views
  • Added September 8, 2026
ai-agentsrustgoexpressgitapisecurity

Works with

  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned September 22, 2026

npx -y skills add claude-dev-suite/claude-dev-suite --skill musig2 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Musig2?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Musig2
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/claude-dev-suite-musig2/badge)](https://www.skillsdirectory.com/skills/claude-dev-suite-musig2)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: bitcoin-musig2
description: |
  MuSig2 (BIP327): two-round Schnorr key aggregation for n-of-n
  multisig. Aggregates n public keys into a single 32-byte x-only
  pubkey indistinguishable from a single-sig. Used in Taproot
  cooperative spends.
  Quick refs: protocol round-trip, key aggregation math, common attacks.
  USE WHEN: implementing n-of-n cooperative Taproot signing, designing
  Lightning Taproot channels, joint custody.
allowed-tools: Read, Grep, Glob
---

# MuSig2 (BIP327)

> **Quick refs**: [protocol.md](quick-ref/protocol.md), [key-agg.md](quick-ref/key-agg.md), [attacks.md](quick-ref/attacks.md)

MuSig2 is the **standard** Schnorr key-aggregation protocol for Bitcoin.
n parties hold separate private keys, jointly produce a single public
key `Q` and a single signature `(R, s)` indistinguishable from a
single-key Schnorr signature.

Replaces MuSig1 (3-round) and MuSig-DN with simpler 2-round + better
security proofs.

BIP327 is **Status: Deployed**, spec version 1.0.4 (as of September
2026).

## Properties

- **Indistinguishable** — observers cannot tell `Q` was aggregated.
- **Off-chain coordination** — n-of-n in 2 rounds with no on-chain
  multisig.
- **Provably secure** in the Algorithmic One-More-Discrete-Log (AOMDL)
  + Random Oracle Model.
- **Fits Taproot key-path** — cheapest spend; no script-path needed
  for cooperative case.

## Two-round protocol summary

### Round 1 — Nonce exchange

Each signer i:
1. Generates two ephemeral nonces `(k_{i,1}, k_{i,2})`.
2. Computes `(R_{i,1}, R_{i,2}) = (k_{i,1}*G, k_{i,2}*G)`.
3. Broadcasts `R_{i,1}, R_{i,2}` to all other signers (66 bytes total).

Aggregator computes:
- `R_1 = sum(R_{i,1})`, `R_2 = sum(R_{i,2})`.
- `b = TaggedHash("MuSig/noncecoef", R_1 || R_2 || agg_pubkey || msg)`.
- `R = R_1 + b*R_2`.

### Round 2 — Partial sig exchange

Each signer i:
1. Computes their partial sig `s_i` using `R`, message, key-agg
   coefficient `a_i`, etc.
2. Broadcasts `s_i`.

Aggregator: `s = sum(s_i) mod n`.

Final sig: `(R.x, s)` — 64 bytes, valid BIP340 sig under `Q`.

## Key aggregation

```
Inputs: pubkeys P_1, ..., P_n (in agreed-upon order)
1. L = sorted(P_i)   (canonical order, byte-lex)
2. a_i = TaggedHash("KeyAgg coefficient", L || P_i.x)
3. Q = sum(a_i * P_i)
```

If `Q.y` is odd, parity bit `g_q = -1`, otherwise `g_q = +1`. This
parity is tracked through signing.

The coefficients `a_i` defeat the **rogue-key attack** that MuSig1
patched but earlier schemes had: an attacker who could pick their
public key after seeing others' would otherwise control the sum.

## Tweaks (Taproot integration)

After key aggregation, Taproot tweak applies:
```
Q_tweaked = Q + t*G  where t = TaggedHash("TapTweak", Q.x || merkle_root)
```

Signing for `Q_tweaked` requires an extra adjustment in the partial
sig phase. BIP327 specifies "tweak context" maintaining the
accumulated tweak.

## Pre-shared nonces (NonceGen variants)

- `NonceGen` deterministic from secret + msg + extra_in.
- "MuSig2 with non-determinism" requires careful aux entropy.
- **Critical**: nonces must NOT be reused across different signing
  sessions, even with different messages. State must be persisted
  carefully (e.g., in encrypted nonce wallet).

## Use cases in Bitcoin

1. **Joint custody** — 2 partners co-spend with single-sig appearance.
2. **Lightning** — Taproot channels (BOLT update) replace OP_2 pubkey
   with MuSig2-aggregated key for channel funding output.
3. **DAO multisig with privacy** — n directors sign as one.
4. **Vault hot/cold cooperative spend** — cold key + hot key
   aggregated for everyday use.
5. **Coinjoin servers** with k-anonymity → users + service jointly
   sign.

## Descriptors, PSBTs and wallet integration

BIP327 covers only the signing math. Three companion BIPs (all by Ava
Chow) carry MuSig2 into wallet software — statuses as of September
2026:

- **BIP328** (Complete) — *Derivation Scheme for MuSig2 Aggregate
  Keys*. Wraps a plain aggregate pubkey in a **synthetic xpub**:
  depth 0, child number 0, fixed chaincode
  `868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965`
  (SHA256 of the text `MuSig2MuSig2MuSig2`). Only unhardened
  derivation is possible — there is no aggregate private key. Each
  `CKDpub` step's `I_L` enters the session context as a **plain**
  tweak (`is_xonly_t = false`), so every signer must recompute the
  derivation tweaks for the child key being signed for.
- **BIP390** (Draft, v0.2.0) — the `musig(KEY,KEY,...,KEY)` descriptor
  key expression. Allowed only inside `tr()`, `rawtr()` or `sp()`,
  never nested in another `musig()`. Keys are sorted with `KeySort`
  after all derivation and before aggregation, so the order written
  in the descriptor does not matter. `musig(...)/NUM/.../*` derives
  from the aggregate key per BIP328, and is legal only when every
  participant is an xpub (or derived from one) and no participant
  itself uses `/*` or `/<NUM;NUM;...>`. No hardened steps after
  `musig()`.
- **BIP373** (Complete) — *MuSig2 PSBT Fields*, carrying both rounds
  through an otherwise ordinary PSBT workflow:
  `PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS` (`0x1a`),
  `PSBT_IN_MUSIG2_PUB_NONCE` (`0x1b`),
  `PSBT_IN_MUSIG2_PARTIAL_SIG` (`0x1c`) and
  `PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS` (`0x08`).

Bitcoin Core parses `musig()` descriptors and reads/writes the BIP373
PSBT fields from **v30.0 (October 2025)**; descriptor support merged
in PR #31244 (2025-07-31). Core accepts `musig()` only inside `tr()`
— not `rawtr()` or `sp()` — per `doc/descriptors.md` (v31.1, July
2026).

## Security caveats

- **Concurrent sessions** — naive per-signer parallel sessions enable
  "Wagner-style" forge attacks. Solution in BIP327: nonce binding to
  message (the `b` coefficient).
- **State persistence** — nonces must be wiped after one use. Power
  loss between round 1 and round 2 → must restart with fresh nonces.
- **Hardware wallet support** — still thin as of September 2026. The
  Ledger Bitcoin app has shipped `musig()` key expressions since
  v2.4.0 (CHANGELOG 2025-03-07; GitHub release 2025-03-17): at most
  5 keys per `musig()`, at most 8 parallel signing sessions
  (persistent state is scarce), `musig()` allowed in `multi_a` but
  not `sortedmulti_a`, and only `musig(...)/**` or
  `musig(...)/<M;N>/*` — participants must be aggregated without
  further per-key derivation. v2.5.1 (CHANGELOG 2026-09-09) fixed
  `musig()` inside `multi_a` fragments. Coldcard is the opposite
  case: the Mk4 and Q1 board configs both set `NGU_INCL_MUSIG = 0`
  ("Exclude unused optional libngu secp256k1 features to save
  firmware space"), so MuSig is compiled out of the firmware
  entirely (as of September 2026). Either way a coordinator that
  speaks BIP373 PSBTs is required.

## Implementations

- `libsecp256k1` (`secp256k1_musig_*` — no `2` in the prefix).
  Mainline since v0.6.0 (2024-11-04) and built by default
  (`--enable-module-musig` is `[default=yes]`); not experimental.
  `secp256k1_musig_keyagg_cache` and `secp256k1_musig_session` still
  have no serialization/parsing functions as of v0.8.0 (2026-08-03),
  so signing state cannot be persisted across a process restart
  between round 1 and round 2.
- `rust-secp256k1` — the module is `musig`, not `musig2`
  (`secp256k1::musig`: `KeyAggCache`, `SecretNonce`/`PublicNonce`,
  `AggregatedNonce`, `Session`, `PartialSignature`,
  `AggregatedSignature`). Support was added in the 0.32 development
  series (CHANGELOG entry "0.32.0 - 2025-10-02", which also removed
  the `Secp256k1` context from the public API), but **no stable
  0.32.0 was ever published** — the three `0.32.0-beta.*` crates are
  yanked. The first stable release carrying MuSig2 is 0.33.0
  (2026-08-28); current stable is 0.33.1 (2026-08-29).
- **BDK has no MuSig2 support** as of September 2026. The blocker is
  `rust-miniscript`: PR #954 (*Add BIP390 `musig()` descriptor key
  expressions*) is still open, so `bdk_wallet` 3.1.0 (2026-06-14)
  cannot even parse `tr(musig(...))` — bdk-ffi pins that behaviour in
  a test literally named `cannotCreateMusigDescriptor`.

## Common bugs

- Forgetting to apply parity flips during partial-sig phase
  (`g_q` and `g_b` must propagate).
- Reusing nonces across two attempts of the same session → key leak.
- Missing key-aggregation coefficient `a_i` → produces a sig under a
  different aggregated pubkey.

## See also

- [schnorr/SKILL.md](../schnorr/SKILL.md)
- [frost/SKILL.md](../frost/SKILL.md)
- [adaptor-sigs/SKILL.md](../adaptor-sigs/SKILL.md)
- [../../protocol/taproot/SKILL.md](../../protocol/taproot/SKILL.md)

Files in this skill

  • SKILL.md4.7 KB
  • quick-ref/attacks.md3.5 KB
  • quick-ref/key-agg.md3.1 KB
  • quick-ref/protocol.md3.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.