Multisig, the Janus Attack & the Burning Bug
M-of-N key aggregation and its rounds, plus two subtle failure modes every contributor must know — the Janus subaddress-linking attack and the burning bug — and their mitigations.
Three things in this lesson have caused real headaches in Monero's history and are exactly the kind of subtle, non-obvious failure a contributor is expected to know cold: how multisig is actually built, the Janus attack on subaddresses, and the burning bug.
How Monero Multisig Works
Monero supports M-of-N multisig built on the same ring-signature machinery, by splitting and aggregating keys rather than bolting on a separate scheme. The essentials:
- Each participant holds a key share; the group's effective spend key is an aggregation of shares such that the one-time output private key can only be reconstructed by
Mcooperating signers. - Setup needs multiple rounds of exchanging public key data:
N-of-Nneeds one exchange round; generalM-of-Nneeds more rounds to distribute the right combinations of shares. Contributors must get the round count and the data exchanged right, or the wallet silently produces an unspendable or insecure setup. - Signing is interactive: partial signatures (and the shared nonces) are combined to produce a valid CLSAG. Nonce handling is critical — reused or predictable nonces across signers leak key material, the same failure mode as single-sig but with more parties to coordinate.
Because it reuses the on-chain format, a multisig transaction is indistinguishable from any other on-chain — privacy isn't reduced by using it.
The Janus Attack
Subaddresses are supposed to be unlinkable: an observer (or a payer) shouldn't be able to tell that two of your subaddresses belong to the same wallet. A Janus attack tries to break exactly that. The attacker, who suspects two subaddresses S₁ and S₂ are yours, crafts a payment mixing data from both (e.g. uses one subaddress's component while directing the transaction public key from the other). If your wallet recognizes the funds, that recognition confirms S₁ and S₂ share a wallet — linking them.
Mitigation: on receipt, the wallet verifies that the transaction public key is consistent with the specific subaddress that the output was sent to (an extra check tying R to the receiving subaddress) before treating the output as received. If the consistency check fails, the wallet doesn't acknowledge the funds, denying the attacker their signal. Contributors implementing receive logic must include this check.
The Burning Bug
The defense against double-spends is "one output, one key image." The burning bug is the dual problem: two different on-chain outputs that share the same one-time public key P. Because the key image I = x·Hp(P) depends only on P (and your x), both outputs have the same key image — so once you spend one, the other is permanently unspendable ("burned"), even though the chain shows you received both amounts.
How can two outputs share P? If someone sends multiple payments such that the one-time key collides (e.g. by reusing the transaction key against your address, or you receiving to the same derivation twice), the wallet might credit both while only one is ever spendable — an attacker could exploit this to make you believe you received more than you can spend (e.g. against a merchant).
Mitigation: wallets detect duplicate one-time keys / key images among received outputs and refuse to count the duplicates as independently spendable, surfacing a warning. Contributors handling output scanning and balance computation must dedupe by one-time key, not assume uniqueness.
The Theme
All three share a lesson: Monero's guarantees hold only if the wallet-side checks are implemented. The chain enforces consensus; unlinkability and correct crediting are enforced by client code. Miss a check and you reintroduce a break the protocol was designed to prevent.
Finally, where all of this is heading — Seraphis, Jamtis & FCMP++.
Comments
Log in or create a free account to comment.
No comments yet — be the first.