diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index b41f5c62..43564522 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -1,25 +1,27 @@
- Title: MuSig + BIP: ? + Title: MuSig2 Author: Status: Draft - License: BSD-2-Clause - Created: 2020-01-19 + License: BSD-3-Clause + Type: Informational + Created: 2022-03-22== Introduction == === Abstract === -This document proposes a standard for the MuSig2 protocol that supports ''tweaking'' and outputs [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] public keys and signatures. +This document proposes a standard for the [https://eprint.iacr.org/2020/1261.pdf MuSig2] protocol. +The standard is compatible with [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] public keys and signatures. +It also supports ''tweaking'', which allows creating [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] Taproot outputs with key and script paths. === Copyright === -This document is licensed under the 2-clause BSD license. +This document is licensed under the 3-clause BSD license. === Motivation === -== Description == - === Design === * The output of the ''KeyAgg'' algorithm depends on the order of the input public keys. @@ -30,9 +32,13 @@ This document is licensed under the 2-clause BSD license. * The public nonces are serialized in compressed format (33 bytes). We accept the small overhead compared to x-only serialization to avoid complicating the specification. * This specification supports signing for ''tweaked'' aggregate public keys. There are two modes of tweaking. ''Ordinary'' tweaking allows deriving child aggregate public keys per [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]. ''X-only'' tweaking allows creating a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] Taproot tweak. See section [[#tweaking|Tweaking]] below for details. +== Description == + +When implementing the specification, make sure to understand this section thoroughly, particularly the [[#signing-flow|Signing Flow]], to avoid subtle mistakes that lead to catastrophic failure. + === Notation === -The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure schemeAmong other pitfalls, using the specification with a curve whose order is not close to the size of the range of the nonce derivation function is insecure.. +The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure scheme. * Lowercase variables represent integers or byte arrays. ** The constant ''p'' refers to the field size, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F''. ** The constant ''n'' refers to the curve order, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141''. @@ -59,7 +65,7 @@ The following conventions are used, with constants as defined for [https://www.s *** Fail if ''c ≠ y'2 mod p''. *** Let ''y = y' '' if ''y' mod 2 = 0'', otherwise let ''y = p - y' ''. *** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y''. -** The function ''point(x)'', where ''x'' is a 32-byte array ("x-only" serialization), returns ''lift_x(int(x))''. Fail if ''lift_x'' fails. +** The function ''point(x)'', where ''x'' is a 32-byte array ("X-only" serialization), returns ''lift_x(int(x))''. Fail if ''lift_x'' fails. ** The function ''pointc(x)'', where ''x'' is a 33-byte array (compressed serialization), sets ''P = lift_x(int(x[1:33]))'' and fails if that fails. If ''x[0] = 2'' it returns ''P'' and if ''x[0] = 3'' it returns ''-P''. Otherwise, it fails. ** The function ''hashtag(x)'' where ''tag'' is a UTF-8 encoded tag name and ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)''. * Other: @@ -73,7 +79,7 @@ Input: * The number ''u'' of public keys with ''0 < u < 2^32'' * The public keys ''pk1..u'': ''u'' 32-byte arrays -The algorithm '''''KeySort(pk1..u)''''' is defined as: +'''''KeySort(pk1..u)''''': * Return ''pk1..u'' sorted in lexicographical order. ==== Key Aggregation ==== @@ -85,11 +91,11 @@ Input: * The tweaks ''tweak1..v'': ''v'' 32-byte arrays * The tweak methods ''is_xonly_t1..v'' : ''v'' booleans -The algorithm '''''KeyAgg(pk1..u, tweak1..v, is_xonly_t1..v)''''' is defined as: +'''''KeyAgg(pk1..u, tweak1..v, is_xonly_t1..v)''''': * Let ''(Q,_,_) = KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''; fail if that fails. * Return ''bytes(Q)''. -The algorithm '''''KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''''' is defined as: +'''''KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''''': * Let ''pk2 = GetSecondKey(pk1..u)'' * For ''i = 1 .. u'': ** Let ''Pi = point(pki)''; fail if that fails. @@ -102,26 +108,26 @@ The algorithm '''''KeyAggInternal(pk1..u, tweak1..v, is_xo ** Let ''(Qi, gacci, tacci) = Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''; fail if that fails * Return ''(Qv, gaccv, taccv)''. -The algorithm '''''HashKeys(pk1..u)''''' is defined as: +'''''HashKeys(pk1..u)''''': * Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' -The algorithm '''''GetSecondKey(pk1..u)''''' is defined as: +'''''GetSecondKey(pk1..u)''''': * For ''j = 1 .. u'': ** If ''pkj ≠ pk1'': *** Return ''pkj'' * Return ''bytes(0)'' -The algorithm '''''KeyAggCoeff(pk1..u, pk')''''' is defined as: +'''''KeyAggCoeff(pk1..u, pk')''''': * Let ''pk2 = GetSecondKey(pk1..u)'': * Return ''KeyAggCoeff'(pk1..u, pk', pk2)'' -The algorithm '''''KeyAggCoeff'(pk1..u, pk', pk2)''''' is defined as: +'''''KeyAggCoeff'(pk1..u, pk', pk2)''''': * Let ''L = HashKeys(pk1..u)'' * If ''pk' = pk2'': ** Return 1 * Return ''int(hashKeyAgg coefficient(L || pk')) mod n'' -The algorithm '''''Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''''' is defined as: +'''''Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''''': * If ''is_xonly_ti'' and ''not has_even_y(Qi-1)'': ** Let ''gi-1 = -1 mod n'' * Else: let ''gi-1 = 1'' @@ -134,7 +140,7 @@ The algorithm '''''Tweak(Qi-1, gacci-1, tweaki, ==== Nonce Generation ==== -The algorithm '''''NonceGen()''''' is defined as: +'''''NonceGen()''''': * Generate two random integers ''k1, k2'' in the range ''1...n-1'' * Let ''R*1 = k1⋅G, R*2 = k2⋅G'' * Let ''pubnonce = cbytes(R*1) || cbytes(R*2)'' @@ -146,7 +152,7 @@ The algorithm '''''NonceGen()''''' is defined as: * The number ''u'' of ''pubnonces'' with ''0 < u < 2^32'' * The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays -The algorithm '''''NonceAgg(pubnonce1..u)''''' is defined as: +'''''NonceAgg(pubnonce1..u)''''': * For ''i = 1 .. 2'': ** For ''j = 1 .. u'': *** Let ''Ri,j = pointc(pubnoncej[(i-1)*33:i*33])''; fail if that fails @@ -167,7 +173,7 @@ The Session Context is a data structure consisting of the following elements: We write "Let ''(aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m) = session_ctx''" to assign names to the elements of a Session Context. -The algorithm '''''GetSessionValues(session_ctx)''''' is defined as: +'''''GetSessionValues(session_ctx)''''': * Let ''(aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m) = session_ctx'' * Let ''(Q, gaccv, taccv) = KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''; fail if that fails * Let ''b = int(hashMuSig/noncecoef(aggnonce || bytes(Q) || m)) mod n'' @@ -177,7 +183,7 @@ The algorithm '''''GetSessionValues(session_ctx)''''' is defined as: * Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' * Return ''(Q, gaccv, taccv, b, R, e)'' -The algorithm '''''GetSessionKeyAggCoeff(session_ctx, P)''''' is defined as: +'''''GetSessionKeyAggCoeff(session_ctx, P)''''': * Let ''(_, u, pk1..u, _, _, _, _) = session_ctx'' * Return ''KeyAggCoeff(pk1..u, bytes(P))'' @@ -188,7 +194,7 @@ Input: * The secret key ''sk'': a 32-byte array * The ''session_ctx'': a [[#session-context|Session Context]] data structure -The algorithm '''''Sign(secnonce, sk, session_ctx)''''' is defined as: +'''''Sign(secnonce, sk, session_ctx)''''': * Let ''(Q, gaccv, _, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails * Let ''k'1 = int(secnonce[0:32]), k'2 = int(secnonce[32:64])'' * Fail if ''k'i = 0'' or ''k'i ≥ n'' for ''i = 1..2'' @@ -219,21 +225,19 @@ Input: * The message ''m'': a 32-byte array * The index of the signer ''i'' in the public nonces and public keys with ''0 < i ≤ u'' -The algorithm '''''PartialSigVerify(psig, pubnonce1..u, pk1..u, tweak1..v, is_xonly_t1..v, m, i)''''' is defined as: +'''''PartialSigVerify(psig, pubnonce1..u, pk1..u, tweak1..v, is_xonly_t1..v, m, i)''''': * Let ''aggnonce = NonceAgg(pubnonce1..u)''; fail if that fails * Let ''session_ctx = (aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m)'' * Run ''PartialSigVerifyInternal(psig, pubnoncei, pki, session_ctx)'' * Return success iff no failure occurred before reaching this point. -===== PartialSigVerifyInternal ===== - Input: * The partial signature ''psig'': a 32-byte array * The public nonce of the signer ''pubnonce'': a 66-byte array * The public key of the signer ''pk*'' (in ''pk1..u'' of the session_ctx''): a 32-byte array * The ''session_ctx'': a [[#session-context|Session Context]] data structure -The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, pk*, session_ctx)''''' is defined as: +'''''PartialSigVerifyInternal(psig, pubnonce, pk*, session_ctx)''''': * Let ''(Q, gaccv, _, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails * Let ''s = int(psig)''; fail if ''s ≥ n'' * Let ''R*1 = pointc(pubnonce[0:33]), R*2 = pointc(pubnonce[33:66])'' @@ -253,7 +257,7 @@ Input: * The partial signatures ''psig1..u'': ''u'' 32-byte arrays * The ''session_ctx'': a [[#session-context|Session Context]] data structure -The algorithm '''''PartialSigAgg(psig1..u, session_ctx)''''' is defined as: +'''''PartialSigAgg(psig1..u, session_ctx)''''': * Let ''(Q, _, taccv, _, _, R, e) = GetSessionValues(session_ctx)''; fail if that fails * For ''i = 1 .. u'': ** Let ''si = int(psigi)''; fail if ''si ≥ n''. @@ -277,29 +281,34 @@ Otherwise, it is possible to extract the secret signing key from the partial sig An implementation may invalidate the secnonce argument after ''Sign'' to avoid any reuse. Avoiding reuse also implies that the ''NonceGen'' algorithm must compute unbiased, uniformly random values ''k1'' and ''k2''. -=== Remarks on Security and Correctness === +=== Test Vectors and Reference Code === -==== Tweaking ==== +There are some vectors in libsecp256k1's [https://github.com/ElementsProject/secp256k1-zkp/blob/master/src/modules/musig/tests_impl.h MuSig test file]. +Search for the ''musig_test_vectors_keyagg'' and ''musig_test_vectors_sign'' functions. -This MuSig specification supports two modes of tweaking that correspond to the following algorithms: +== Remarks on Security and Correctness == + +=== Tweaking === + +This MuSig2 specification supports two modes of tweaking that correspond to the following algorithms: Input: * ''P'': a point * The tweak ''t'': an integer with ''0 ≤ t < n '' -The algorithm '''''OrdinaryTweak(P, t)''''' is defined as: +'''''OrdinaryTweak(P, t)''''': * Return ''P + t⋅G'' -The algorithm '''''XonlyTweak(P, t)''''' is defined as: +'''''XonlyTweak(P, t)''''': * Return ''with_even_y(P) + t⋅G'' -==== Negation Of The Secret Key When Signing ==== +=== Negation Of The Secret Key When Signing === -In order to produce a partial signature for an x-only public key that is an aggregate of ''u'' x-only keys and tweaked ''v'' times (x-only or ordinarily), the ''[[#Sign negation|Sign]]'' algorithm may need to negate the secret key during the signing process. +In order to produce a partial signature for an X-only public key that is an aggregate of ''u'' X-only keys and tweaked ''v'' times (X-only or ordinarily), the ''[[#Sign negation|Sign]]'' algorithm may need to negate the secret key during the signing process.