diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 198982ad6..e2a3364a6 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -1,5 +1,5 @@
- Title: MuSig Key Aggregation + Title: MuSig Author: Status: Draft License: BSD-2-Clause @@ -10,7 +10,7 @@ === Abstract === -This document describes MuSig Key Aggregation in libsecp256k1-zkp. +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. === Copyright === @@ -28,8 +28,9 @@ This document is licensed under the 2-clause BSD license. * The second unique key in the pubkey list given to ''KeyAgg'' (as well as any keys identical to this key) gets the constant KeyAgg coefficient 1 which saves an exponentiation (see the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]). * The public key inputs are serialized using x-only (32 byte) instead of compressed (33 byte) serialization. The reason for this is that as x-only keys are becoming more common, the full key may not be available. * 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. -=== Specification === +=== 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.. * Lowercase variables represent integers or byte arrays. @@ -47,6 +48,7 @@ The following conventions are used, with constants as defined for [https://www.s ** The function ''bytes(x)'', where ''x'' is an integer, returns the 32-byte encoding of ''x'', most significant byte first. ** The function ''bytes(P)'', where ''P'' is a point, returns ''bytes(x(P))''. ** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''. +** The function ''with_even_y(P)'', where ''P'' is a point, returns ''P'' if ''is_infinite(P)'' or ''has_even_y(P)''. Otherwise, ''with_even_y(P)'' returns ''-P''. ** The function ''cbytes(P)'', where ''P'' is a point, returns ''a || bytes(P)'' where ''a'' is a byte that is ''2'' if ''has_even_y(P)'' and ''3'' otherwise. ** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is ''x''. ** The function ''lift_x(x)'', where ''x'' is an integer in range ''0..2256-1'', returns the point ''P'' for which ''x(P) = x'' @@ -60,15 +62,18 @@ The following conventions are used, with constants as defined for [https://www.s ** 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: +** Tuples are written by listing the elements within parentheses and separated by commas. For example, ''(2, 3, 1)'' is a tuple. +=== Specification === ==== Key Sorting ==== Input: -* The number ''u'' of signatures with ''0 < u < 2^32'' +* 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: +The algorithm '''''KeySort(pk1..u)''''' is defined as: * Return ''pk1..u'' sorted in lexicographical order. ==== Key Aggregation ==== @@ -76,37 +81,55 @@ The algorithm ''KeySort(pk1..u)'' is defined as: Input: * The number ''u'' of public keys with ''0 < u < 2^32'' * The public keys ''pk1..u'': ''u'' 32-byte arrays +* The number ''v'' of tweaks with ''0 ≤ v < 2^32'' +* The tweaks ''tweak1..v'': ''v'' 32-byte arrays +* The tweak methods ''is_xonly_t1..v'' : ''v'' booleans -The algorithm ''KeyAgg(pk1..u)'' is defined as: -* Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails. +The algorithm '''''KeyAgg(pk1..u, tweak1..v, is_xonly_t1..v)''''' is defined as: +* Let ''(Q,_,_) = KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''; fail if that fails. * Return ''bytes(Q)''. -The algorithm ''KeyAggInternal(pk1..u)'' is defined as: +The algorithm '''''KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''''' is defined as: * For ''i = 1 .. u'': ** Let ''ai = KeyAggCoeff(pk1..u, pki)''. ** Let ''Pi = point(pki)''; fail if that fails. -* Let ''Q = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' -* Fail if ''is_infinite(Q)''. -* Return ''Q''. - -The algorithm ''HashKeys(pk1..u)'' is defined as: +* Let ''Q0 = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' +* Fail if ''is_infinite(Q0)''. +* Let ''tacc0 = 0'' +* Let ''gacc0 = 1'' +* For ''i = 1 .. v'': +** 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: * Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' -The algorithm ''IsSecond(pk1..u, pk')'' is defined as: +The algorithm '''''IsSecond(pk1..u, pk')''''' is defined as: * For ''j = 1 .. u'': ** If ''pkj ≠ pk1'': *** Return ''true'' if ''pkj = pk' '', otherwise return ''false''. * Return ''false'' -The algorithm ''KeyAggCoeff(pk1..u, pk')'' is defined as: +The algorithm '''''KeyAggCoeff(pk1..u, pk')''''' is defined as: * Let ''L = HashKeys(pk1..u)''. * If ''IsSecond(pk1..u, pk')'': ** 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: +* If ''is_xonly_ti'' and ''not has_even_y(Qi-1)'': +** Let ''gi-1 = -1 mod n'' +* Else: let ''gi-1 = 1'' +* Let ''ti = int(tweaki)''; fail if ''t ≥ n'' +* Let ''Qi = gi-1⋅Qi-1 + ti⋅G'' +** Fail if ''is_infinite(Qi)'' +* Let ''gacci = gi-1⋅gacci-1 mod n'' +* Let ''tacci = ti + gi-1⋅tacci-1 mod n'' +* Return ''(Qi, gacci, tacci)'' + ==== Nonce Generation ==== -The algorithm ''NonceGen()'' is defined as: +The algorithm '''''NonceGen()''''' is defined as: * 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)'' @@ -118,64 +141,65 @@ 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: +The algorithm '''''NonceAgg(pubnonce1..u)''''' is defined as: * For ''i = 1 .. 2'': ** For ''j = 1 .. u'': *** Let ''Ri,j = pointc(pubnoncej[(i-1)*33:i*33])''; fail if that fails ** Let ''R'i = Ri,1 + Ri,2 + ... + Ri,u'' -** Let ''Ri = R'i'' if not ''is_infinite(R'i)'', otherwise let Ri = G'' +** Let ''Ri = R'i'' if not ''is_infinite(R'i)'', otherwise let Ri = G'' (see [[#dealing-with-infinity-in-nonce-aggregation|Dealing with Infinity in Nonce Aggregation]]) * Return ''aggnonce = cbytes(R1) || cbytes(R2)'' -===== Note on ''is_infinite(R'i)'' ===== +==== Session Context ==== -If ''is_infinite(R'i)'' there is at least one dishonest signer (except with negligible probability). -If we fail here, we will never be able to determine who it is. -Therefore, we continue so that the culprit is revealed when collecting and verifying partial signatures. +The Session Context is a data structure consisting of the following elements: +* The aggregate public nonce ''aggnonce'': a 66-byte array +* The number ''u'' of public keys with ''0 < u < 2^32'' +* The public keys ''pk1..u'': ''u'' 32-byte arrays +* The number ''v'' of tweaks with ''0 ≤ v < 2^32'' +* The tweaks ''tweak1..v'': ''v'' 32-byte arrays +* The tweak methods ''is_xonly_t1..v'' : ''v'' booleans +* The message ''m'': a 32-byte array -However, dealing with the point at infinity requires defining a serialization and may require extra code complexity in implementations. -Instead of incurring this complexity, we make two modifications (compared to the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]) to avoid infinity while still allowing us to detect the dishonest signer: -* In ''NonceAgg'', if an output ''R'i'' would be infinity, instead output the generator (an arbitrary choice). -* In ''Sign'', implicitly disallow the input ''aggnonce'' to contain infinity (since the serialization format doesn't support it). +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 entire ''NonceAgg'' function (both the original and modified version) only depends on publicly available data (the set of public pre-nonces from every signer). -In the unforgeability proof, ''NonceAgg'' is considered to be performed by an untrusted party; thus modifications to ''NonceAgg'' do not affect the unforgeability of the scheme. - -The (implicit) modification to ''Sign'' is equivalent to adding a clause, "abort if the input ''aggnonce'' contained infinity". -This modification only depends on the publicly available ''aggnonce''. -Given a successful adversary against the security game (EUF-CMA) for the modified scheme, a reduction can win the security game for the original scheme by simulating the modification (i.e. checking whether to abort) towards the adversary. +The algorithm '''''GetSessionValues(session_ctx)''''' is defined as: +* 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'' +* Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails +* Let ''R = R1 + b⋅R2'' +* Fail if ''is_infinite(R)'' +* Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' +* Return ''(Q, gaccv, taccv, b, R, e)'' -We conclude that these two modifications preserve the security of the MuSig2* scheme. +The algorithm '''''GetSessionKeyAggCoeff(session_ctx, P)''''' is defined as: +* Let ''(_, u, pk1..u, _, _, _, _) = session_ctx'' +* Return ''KeyAggCoeff(pk1..u, bytes(P))'' ==== Signing ==== Input: * The secret nonce ''secnonce'' that has never been used as input to ''Sign'' before: a 64-byte array * The secret key ''sk'': a 32-byte array -* The aggregate public nonce ''aggnonce'': a 66-byte array -* The number ''u'' of public keys with ''0 < u < 2^32'' -* The public keys ''pk1..u'': ''u'' 32-byte arrays -* The message ''m'': a 32-byte array +* The ''session_ctx'': a [[#session-context|Session Context]] data structure -The algorithm ''Sign(secnonce, sk, aggnonce, pk1..u, m)'' is defined as: -* Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails -* Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails -* Let ''b = int(hashMuSig/noncecoef(aggnonce || bytes(Q) || m)) mod n'' -* Let ''R = R1 + b⋅R2'' -* Fail if ''is_infinite(R)'' +The algorithm '''''Sign(secnonce, sk, session_ctx)''''' is defined as: +* 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'' * Let ''k1 = k'1, k2 = k'2 '' if ''has_even_y(R)'', otherwise let ''k1 = n - k'1, k2 = n - k2'' * Let ''d' = int(sk)'' * Fail if ''d' = 0'' or ''d' ≥ n'' * Let ''P = d'⋅G'' -* Let ''d = n - d' '' if ''has_even_y(P) `XOR` has_even_y(Q)'', otherwise let ''d = d' '' -* Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' -* Let ''mu = KeyAggCoeff(pk1..u, bytes(P))'' -* Let ''s = (k1 + b⋅k2 + e⋅mu⋅d) mod n'' +* Let ''a = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails +* Let ''gp = 1'' if ''has_even_y(P)'', otherwise let ''gp = -1 mod n'' +* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' +* Let ''d = gv⋅gaccv⋅gp⋅d' '' (See [[negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]]) +* Let ''s = (k1 + b⋅k2 + e⋅a⋅d) mod n'' * Let ''psig = bytes(s)'' * Let ''pubnonce = cbytes(k'1⋅G) || cbytes(k'2⋅G)'' -* If ''PartialSigVerifyInternal(psig, pubnonce, aggnonce, pk1..u, bytes(P), m)'' (see below) returns failure, abortVerifying the signature before leaving the signer prevents random or attacker provoked computation errors. This prevents publishing invalid signatures which may leak information about the secret key. It is recommended, but can be omitted if the computation cost is prohibitive.. -* Return partial signature ''psig +* If ''PartialSigVerifyInternal(psig, pubnonce, bytes(P), session_ctx)'' (see below) returns failure, abortVerifying the signature before leaving the signer prevents random or attacker provoked computation errors. This prevents publishing invalid signatures which may leak information about the secret key. It is recommended, but can be omitted if the computation cost is prohibitive.. +* Return partial signature ''psig'' ==== Partial Signature Verification ==== @@ -184,12 +208,16 @@ Input: * The number ''u'' of public nonces and public keys with ''0 < u < 2^32'' * The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays * The public keys ''pk1..u'': ''u'' 32-byte arrays +* The number ''v'' of tweaks with ''0 ≤ v < 2^32'' +* The tweaks ''tweak1..v'': ''v'' 32-byte arrays +* The tweak methods ''is_xonly_t1..v'' : ''v'' booleans * 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 index of the signer ''i'' in the public nonces and public keys with ''0 < i ≤ u'' -The algorithm ''PartialSigVerify(psig, pubnonce1..u, pk1..u, m, i)'' is defined as: +The algorithm '''''PartialSigVerify(psig, pubnonce1..u, pk1..u, tweak1..v, is_xonly_t1..v, m, i)''''' is defined as: * Let ''aggnonce = NonceAgg(pubnonce1..u)''; fail if that fails -* Run ''PartialSigVerifyInternal(psig, pubnoncei, aggnonce, pk1..u, pki, m)'' +* 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 ===== @@ -197,57 +225,173 @@ The algorithm ''PartialSigVerify(psig, pubnonce1..u, pk1..u1..u'': ''u'' 32-byte arrays -* The public key of the signer ''pk*'' (in ''pk1..u''): a 32-byte array -* The message ''m'': a 32-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, aggnonce, pk1..u, pk*, m)'' is defined as: +The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, pk*, session_ctx)''''' is defined as: +* Let ''(Q, gaccv, _, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails * Let ''s = int(psig)''; fail if ''s ≥ n'' -* Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails -* Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails -* Let ''b = int(hashMuSig/noncecoef(aggnonce || bytes(Q) || m)) mod n'' -* Let ''R = R1 + b⋅R2'' * Let ''R*1 = pointc(pubnonce[0:33]), R*2 = pointc(pubnonce[33:66])'' * Let ''R*' = R*1 + b⋅R*2'' * Let ''R* = R*' '' if ''has_even_y(R)'', otherwise let ''R* = -R*' '' -* Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' -* Let ''mu = KeyAggCoeff(pk1..u, pk*)'' -* Let ''P' = point(pk*)''; fail if that fails -* Let ''P = P' '' if ''has_even_y(Q)'', otherwise let ''P = -P' '' -* Fail if ''s⋅G ≠ R* + e⋅mu⋅P'' +* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' +* Let ''g' = gv⋅gaccv mod n'' +* Let ''P = g'⋅point(pk*)''; fail if that fails (See [[#negation-of-the-public-key-when-partially-verifying|Negation Of The Public Key When Partially Verifying]]) +* Let ''a = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails +* Fail if ''s⋅G ≠ R* + e⋅a⋅P'' * Return success iff no failure occurred before reaching this point. ==== Partial Signature Aggregation ==== Input: -* The final nonce ''R'' as created during ''Sign'' or ''PartialSigVerify'': a point * The number ''u'' of signatures with ''0 < u < 2^32'' -* The partial signatures ''sig1..u'': ''u'' 32-byte arrays +* The partial signatures ''psig1..u'': ''u'' 32-byte arrays +* The ''session_ctx'': a [[#session-context|Session Context]] data structure -The algorithm ''SigAgg(R, sig1..u)'' is defined as: +The algorithm '''''PartialSigAgg(psig1..u, session_ctx)''''' is defined as: +* Let ''(Q, _, taccv, _, _, R, e) = GetSessionValues(session_ctx)''; fail if that fails * For ''i = 1 .. u'': -** Let ''si = int(sigi)''; fail if ''si ≥ n''. -* Let ''s = s1 + ... + su mod n'' +** Let ''si = int(psigi)''; fail if ''si ≥ n''. +* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' +* Let ''s = s1 + ... + su + e⋅gv⋅taccv mod n'' * Return ''sig = ''bytes(R) || bytes(s)'' === Signing Flow === -Note that this specification unnecessarily recomputes intermediary values (such as the aggregate public key) that can be cached in real implementations. +Note that this specification unnecessarily recomputes intermediary values (such as the aggregate and tweaked public key) that can be cached in real implementations. There are multiple ways to use above algorithms and arrive at a final Schnorr signature. One of them can be described as follows: The signers ''1'' to ''n'' each run ''NonceGen'' to compute ''secnonce'' and ''pubnonce''. Every signer sends its public key and ''pubnonce'' to every other signer and all signers agree on a single message to sign. Then, the signers run ''NonceAgg'' and ''Sign'' with their secret signing key and ''secnonce''. -They send the resulting partial signature to every other signer and combine them with the ''SigAgg'' algorithm. +They send the resulting partial signature to every other signer and combine them with the ''PartialSigAgg'' algorithm. ''IMPORTANT'': The ''Sign'' algorithm must '''not''' be executed twice with the same ''secnonce''. Otherwise, it is possible to extract the secret signing key from the partial signatures. 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 === + +==== Tweaking ==== + +This MuSig 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: +* Return ''P + t⋅G'' + +The algorithm '''''XonlyTweak(P, t)''''' is defined as: +* Return ''with_even_y(P) + t⋅G'' + +==== 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. + ++The following public keys arise as intermediate steps in the MuSig protocol: +• ''Pi'' as computed in ''KeyAggInternal'' is the point corresponding to the ''i''-th signer's x-only public key. Defining ''d'i'' to be the ''d' '' value as computed in the ''Sign'' algorithm of the ''i''-th signer, we have + ''Pi = with_even_y(d'i⋅G) ''. +• ''Q0'' is an aggregate of the signer's public keys and defined in ''KeyAggInternal'' as + ''Q0 = a1⋅P1 + a2⋅P1 + ... + au⋅Pu''. +• ''Qi'' as computed in ''Tweak'' for ''1 ≤ i ≤ v'' is the tweaked public key after the ''i''-th tweaking operation. It holds that + ''Qi = f(i-1) + ti⋅G'' for ''i = 1, ..., v'' where + ''f(i) := with_even_y(Qi)'' if ''is_xonly_ti+1'' and + ''f(i) := Qi'' otherwise. + + +The goal is to produce a partial signature corresponding to the output of ''KeyAgg'', i.e., the final (x-only) public key point after ''v'' tweaking operations ''with_even_y(Qv)''. + ++We define ''gpi'' for ''1 ≤ i ≤ u'' to be ''gp '' as computed in the ''Sign'' algorithm of the ''i''-th signer. It holds that + ''Pi = gpi⋅d'i⋅G''. + +For ''0 ≤ i ≤ v-1'', the ''Tweak'' algorithm called from ''KeyAggInternal'' sets ''gi'' to ''-1 mod n'' if and only if ''is_xonly_ti+1'' is true and ''Qi'' has an odd Y coordinate. Therefore, we have + ''f(i) = gi⋅Qi'' for ''0 ≤ i ≤ v - 1''. + +Furthermore, the ''Sign'' and ''PartialSigVerify'' algorithms set ''gv'' such that + ''with_even_y(Qv) = gv⋅Qv''. + + ++So, the (x-only) final public key is + ''with_even_y(Qv) + = gv⋅Qv + = gv⋅(f(v-1) + tv⋅G) + = gv⋅(gv-1⋅(f(v-2) + tv-1⋅G) + tv⋅G) + = gv⋅gv-1⋅f(v-2) + gv⋅(tv + gv-1⋅tv-1)⋅G + = gv⋅gv-1⋅f(v-2) + (sumi=v-1..v ti⋅prodj=i..v gj)⋅G + = gv⋅gv-1⋅...⋅g1⋅f(0) + (sumi=1..v ti⋅prodj=i..v gj)⋅G + = gv⋅...⋅g0⋅Q0 + gv⋅taccv⋅G'' + where ''tacci'' is computed by ''KeyAggInternal'' and ''Tweak'' as follows: + ''tacc0 = 0 + tacci = ti + gi-1⋅tacci-1 for i=1..v mod n'' + for which it holds that ''gv⋅taccv = sumi=1..v ti⋅prodj=i..v gj''. + + ++''KeyAggInternal'' and ''Tweak'' compute + ''gacc0 = 1 + gacci = gi-1⋅gacci-1 for i=1..v mod n'' +So we can rewrite above equation for the final public key as + ''with_even_y(Qv) = gv⋅gaccv⋅Q0 + gv⋅taccv⋅G''. + + ++Then we have + ''with_even_y(Qv) - gv⋅taccv⋅G + = gv⋅gaccv⋅Q0 + = gv⋅gaccv⋅(a1⋅P1 + ... + au⋅Pu) + = gv⋅gaccv⋅(a1⋅gp1⋅d'1⋅G + ... + au⋅gpu⋅d'u⋅G) + = sumi=1..u(gv⋅gaccv⋅gpi⋅ai⋅d'i)*G''. + + +Thus, signer ''i'' multiplies its secret key ''d'i'' with ''gv⋅gaccv⋅gpi'' in the ''[[#Sign negation|Sign]]'' algorithm. + +==== Negation Of The Public Key When Partially Verifying ==== + ++As explained in [[#negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]] the signer uses a possibly negated secret key + ''d = gv⋅gaccv⋅gp⋅d' mod n'' +when producing a partial signature to ensure that the aggregate signature will correspond to an aggregate public key with even Y coordinate. + + ++The ''[[#SigVerify negation|PartialSigVerifyInternal]]'' algorithm is supposed to check + ''s⋅G = R* + e⋅a⋅d⋅G''. + + ++The verifier doesn't have access to ''d⋅G'', but can construct it using the xonly public key ''pk*'' as follows: +''d⋅G + = gv⋅gaccv⋅gp⋅d'⋅G + = gv⋅gaccv⋅point(pk*)'' + + +==== Dealing with Infinity in Nonce Aggregation ==== + +If it happens that ''is_infinite(R'i)'' inside ''[[#NonceAgg infinity|NonceAgg]]'' there is at least one dishonest signer (except with negligible probability). +If we fail here, we will never be able to determine who it is. +Therefore, we continue so that the culprit is revealed when collecting and verifying partial signatures. + +However, dealing with the point at infinity requires defining a serialization and may require extra code complexity in implementations. +Instead of incurring this complexity, we make two modifications (compared to the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]) to avoid infinity while still allowing us to detect the dishonest signer: +* In ''NonceAgg'', if an output ''R'i'' would be infinity, instead output the generator (an arbitrary choice). +* In ''Sign'', implicitly disallow the input ''aggnonce'' to contain infinity (since the serialization format doesn't support it). + +The entire ''NonceAgg'' function (both the original and modified version) only depends on publicly available data (the set of public pre-nonces from every signer). +In the unforgeability proof, ''NonceAgg'' is considered to be performed by an untrusted party; thus modifications to ''NonceAgg'' do not affect the unforgeability of the scheme. + +The (implicit) modification to ''Sign'' is equivalent to adding a clause, "abort if the input ''aggnonce'' contained infinity". +This modification only depends on the publicly available ''aggnonce''. +Given a successful adversary against the security game (EUF-CMA) for the modified scheme, a reduction can win the security game for the original scheme by simulating the modification (i.e. checking whether to abort) towards the adversary. + +We conclude that these two modifications preserve the security of the MuSig2* scheme. + == Applications == == Test Vectors and Reference Code == diff --git a/src/modules/musig/keyagg.h b/src/modules/musig/keyagg.h index 69c1f34ad..a56dc9efe 100644 --- a/src/modules/musig/keyagg.h +++ b/src/modules/musig/keyagg.h @@ -18,8 +18,11 @@ typedef struct { secp256k1_ge pk; secp256k1_fe second_pk_x; unsigned char pk_hash[32]; + /* tweak is identical to value tacc[v] in the specification. */ secp256k1_scalar tweak; - int internal_key_parity; + /* parity_acc corresponds to gacc[v] in the spec. If gacc[v] is -1, + * parity_acc is 1. Otherwise, parity_acc is 0. */ + int parity_acc; } secp256k1_keyagg_cache_internal; /* Requires that the saved point is not infinity */ diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h index c7f18b370..33cdec2a9 100644 --- a/src/modules/musig/keyagg_impl.h +++ b/src/modules/musig/keyagg_impl.h @@ -69,7 +69,7 @@ static void secp256k1_keyagg_cache_save(secp256k1_musig_keyagg_cache *cache, sec ptr += 32; memcpy(ptr, cache_i->pk_hash, 32); ptr += 32; - *ptr = cache_i->internal_key_parity; + *ptr = cache_i->parity_acc; ptr += 1; secp256k1_scalar_get_b32(ptr, &cache_i->tweak); } @@ -84,7 +84,7 @@ static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_k ptr += 32; memcpy(cache_i->pk_hash, ptr, 32); ptr += 32; - cache_i->internal_key_parity = *ptr & 1; + cache_i->parity_acc = *ptr & 1; ptr += 1; secp256k1_scalar_set_b32(&cache_i->tweak, ptr, NULL); return 1; @@ -278,7 +278,7 @@ static int secp256k1_musig_pubkey_tweak_add_internal(const secp256k1_context* ct return 0; } if (xonly && secp256k1_extrakeys_ge_even_y(&cache_i.pk)) { - cache_i.internal_key_parity ^= 1; + cache_i.parity_acc ^= 1; secp256k1_scalar_negate(&cache_i.tweak, &cache_i.tweak); } secp256k1_scalar_add(&cache_i.tweak, &cache_i.tweak, &tweak); diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 191158d00..88e27e174 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -513,81 +513,19 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p return 0; } secp256k1_fe_normalize_var(&pk.y); - /* Determine if the secret key sk should be negated before signing. - * - * We use the following notation: - * - |.| is a function that normalizes a point to an even Y by negating - * if necessary, similar to secp256k1_extrakeys_ge_even_y - * - mu[i] is the i-th KeyAgg coefficient - * - t[i] is the i-th tweak - * - * The following public keys arise as intermediate steps: - * - P[i] is the i-th public key with corresponding secret key x[i] - * P[i] := x[i]*G - * - P_agg[0] is the aggregate public key - * P_agg[0] := mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]| - * - P_agg[i] for 1 <= i <= m is the tweaked public key after the i-th - * tweaking operation. There are two types of tweaking: x-only and ordinary - * "EC" tweaking. We define a boolean predicate xonly(i) that is true if - * the i-th tweaking operation is x-only tweaking and false otherwise - * (ordinary tweaking). - * Let - * P_agg[i] := f(i, P_agg[i-1]) + t[i]*G for i = 1, ..., m - * where f(i, X) := |X| if xonly(i) - * f(i, X) := X otherwise - * - * Note that our goal is to produce a partial signature corresponding to - * the final public key after m tweaking operations P_final = |P_agg[m]|. - * - * Define d[i] for 0 <= i <= n-1 and d_agg[i] for 0 <= i <= m so that: - * - |P[i]| = d[i]*P[i] - * - f(i+1, P_agg[i]) = d_agg[i]*P_agg[i] for 0 <= i <= m - 1 - * - |P_agg[m]| = d_agg[m]*P_agg[m] - * - * In other words, d[i] = 1 if P[i] has even y coordinate, -1 otherwise. - * For 0 <= i <= m-1, d_agg[i] is -1 if and only if xonly(i+1) is true and - * P_agg[i] has an odd Y coordinate. - * - * The (x-only) final public key is P_final = |P_agg[m]| - * = d_agg[m]*P_agg[m] - * = d_agg[m]*(f(m, P_agg[m-1]) + t[m]*G) - * = d_agg[m]*(d_agg[m-1]*(f(m-1, P_agg[m-2]) + t[m-1]*G) + t[m]*G) - * = d_agg[m]*...*d_agg[0]*P_agg[0] + (d_agg[m]*t[m]+...+*d_agg[1]*t[1])*G. - * To simplify the equation let us define - * d_agg := d_agg[m]*...*d_agg[0]. - * t := d_agg[m]*t[m]+...+*d_agg[1]*t[1] if m > 0, otherwise t := 0 - * Then we have - * P_final - t*G - * = d_agg*P_agg[0] - * = d_agg*(mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]|) - * = d_agg*(d[0]*mu[0]*P[0] + ... + d[n-1]*mu[n-1]*P[n-1]) - * = sum((d_agg*d[i])*mu[i]*x[i])*G. - * - * Thus whether signer i should use the negated x[i] depends on the product - * d_agg[m]*...*d_agg[1]*d_agg[0]*d[i]. In other words, negate if and only - * if the following holds: - * (P[i] has odd y) XOR (xonly(1) and P_agg[0] has odd y) - * XOR (xonly(2) and P_agg[1] has odd y) - * XOR ... XOR (xonly(m) and P_agg[m-1] has odd y) - * XOR (P_agg[m] has odd y) - * - * Let us now look at how the terms in the equation correspond to the if - * condition below for some values of m: - * m = 0: P[i] has odd y = secp256k1_fe_is_odd(&pk.y) - * P_agg[0] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) - * cache_i.internal_key_parity = 0 - * m = 1: P[i] has odd y = secp256k1_fe_is_odd(&pk.y) - * xonly(1) and P_agg[0] has odd y = cache_i.internal_key_parity - * P_agg[1] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) - * m = 2: P[i] has odd y = secp256k1_fe_is_odd(&pk.y) - * (xonly(1) and P_agg[0] has odd y) - XOR (xonly(2) and P_agg[1] has odd y) = cache_i.internal_key_parity - * P_agg[2] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) - * etc. + + /* The specification requires that the secret key is multiplied by + * g[v]*g*gp. All factors are -1 or 1. The value g[v] is -1 iff + * secp256k1_fe_is_odd(&cache_i.pk.y)), g is is -1 iff parity_acc is 1 and + * gp is -1 if secp256k1_fe_is_odd(&pk.y). Therefore, multiplying by + * g[v]*g*gp is equivalent to negating if + * secp256k1_fe_is_odd(&cache_i.pk.y)) + * XOR cache_i.parity_acc + * XOR secp256k1_fe_is_odd(&pk.y). */ - if ((secp256k1_fe_is_odd(&pk.y) - != secp256k1_fe_is_odd(&cache_i.pk.y)) - != cache_i.internal_key_parity) { + if ((secp256k1_fe_is_odd(&cache_i.pk.y) + != cache_i.parity_acc) + != secp256k1_fe_is_odd(&pk.y)) { secp256k1_scalar_negate(&sk, &sk); } @@ -659,44 +597,13 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 secp256k1_musig_keyaggcoef(&mu, &cache_i, &pkp.x); secp256k1_scalar_mul(&e, &session_i.challenge, &mu); - /* When producing a partial signature, signer i uses a possibly - * negated secret key: - * - * sk[i] = (d_agg*d[i])*x[i] - * - * to ensure that the aggregate signature will correspond to - * an aggregate public key with even Y coordinate (see the - * notation and explanation in musig_partial_sign). - * - * We use the following additional notation: - * - e is the (Schnorr signature) challenge - * - r[i] is the i-th signer's secret nonce - * - R[i] = r[i]*G is the i-th signer's public nonce - * - R is the aggregated public nonce - * - d_nonce is chosen so that |R| = d_nonce*R - * - * The i-th partial signature is: - * - * s[i] = d_nonce*r[i] + mu[i]*e*sk[i] - * - * In order to verify this partial signature, we need to check: - * - * s[i]*G = d_nonce*R[i] + mu[i]*e*sk[i]*G - * - * The verifier doesn't have access to sk[i]*G, but can construct - * it using the xonly public key |P[i]| as follows: - * - * sk[i]*G = d_agg*d[i]*x[i]*G - * = d_agg*d[i]*P[i] - * = d_agg*|P[i]| - * - * The if condition below is true whenever d_agg is negative (again, see the - * explanation in musig_partial_sign). In this case, the verifier negates e - * which will have the same end result as negating |P[i]|, since they are - * multiplied later anyway. - */ + /* The specification requires that the public key is multiplied by g[v]*g. + * All factors are -1 or 1. The value g[v] is -1 iff + * secp256k1_fe_is_odd(&cache_i.pk.y)) and g is is -1 iff parity_acc is 1. + * Therefore, multiplying by g[v]*g is equivalent to negating if + * fe_is_odd(&cache_i.pk.y) XOR parity_acc. */ if (secp256k1_fe_is_odd(&cache_i.pk.y) - != cache_i.internal_key_parity) { + != cache_i.parity_acc) { secp256k1_scalar_negate(&e, &e); } diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 70512360b..aab686153 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -883,7 +883,7 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) { * that key. If xonly is set to true, the function f is normalizes the input * point to have an even X-coordinate ("xonly-tweaking"). * Otherwise, the function f is the identity function. */ - for (i = 1; i < N_TWEAKS; i++) { + for (i = 1; i <= N_TWEAKS; i++) { unsigned char tweak[32]; int P_parity; int xonly = secp256k1_testrand_bits(1);