From b8f4e75d89071515231be03727d47a34b1c12cab Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Sat, 1 Jan 2022 20:49:39 +0000 Subject: [PATCH 1/5] musig-spec: move to doc directory --- {src/modules/musig => doc}/musig-spec.mediawiki | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {src/modules/musig => doc}/musig-spec.mediawiki (100%) diff --git a/src/modules/musig/musig-spec.mediawiki b/doc/musig-spec.mediawiki similarity index 100% rename from src/modules/musig/musig-spec.mediawiki rename to doc/musig-spec.mediawiki From e0bb2d7009eebe2b25dfe977fe3534ad507251ab Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 29 Dec 2021 19:40:18 +0000 Subject: [PATCH 2/5] musig-spec: improve KeyAgg description It's easier to identify a signer with a public key instead of an index in KeyAggCoef because it doesn't force the signer to know its index. --- doc/musig-spec.mediawiki | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 09ebd6c92..f94e5a7a7 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -22,10 +22,11 @@ This document is licensed under the 2-clause BSD license. === Design === -* A function for sorting public keys allows to aggregate keys independent of the (initial) order. +* The output of the ''KeyAgg'' algorithm depends on the order of the input public keys. +* It is possible to sort the public keys with the ''KeySort'' algorithm before key aggregation to ensure the same output, independent of the (initial) order. * The KeyAgg coefficient is computed by hashing the key instead of key index. Otherwise, if the pubkey list gets sorted, the signer needs to translate between key indices pre- and post-sorting. -* The second unique key in the pubkey list 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 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. === Specification === @@ -44,8 +45,9 @@ The following conventions are used, with constants as defined for [https://www.s ** The function ''x[i:j]'', where ''x'' is a byte array and ''i, j ≥ 0'', returns a ''(j - i)''-byte array with a copy of the ''i''-th byte (inclusive) to the ''j''-th byte (exclusive) of ''x''. ** 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 ''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 ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''. +** The function ''cbytes(P)'', where ''P'' is a point, returns ''a || bytes(P)'' where ''a'' 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..p-1'', returns the point ''P'' for which ''x(P) = x'' Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x3 + 7 mod p'' and they can be computed as ''y = ±c(p+1)/4 mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. and ''has_even_y(P)'', or fails if no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode: *** Let ''c = x3 + 7 mod p''. @@ -67,34 +69,39 @@ The algorithm ''KeySort(pk1..u)'' is defined as: ==== Key Aggregation ==== 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 ''KeyAgg(pk1..u)'' is defined as: * For ''i = 1 .. u'': -** Let ''ai = KeyAggCoeff(pk1..u, i)''. +** Let ''ai = KeyAggCoeff(pk1..u, pki)''. ** Let ''Pi = lift_x(int(pki))''; fail if it fails. -* Let ''S = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' -* Fail if ''is_infinite(S)''. -* Return ''bytes(S)''. +* Let ''Q = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' +* Fail if ''is_infinite(Q)''. +* Return ''bytes(Q)''. The algorithm ''HashKeys(pk1..u)'' is defined as: * Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' -The algorithm ''IsSecond(pk1..u, i)'' is defined as: +The algorithm ''IsSecond(pk1..u, pk')'' is defined as: * For ''j = 1 .. u'': ** If ''pkj ≠ pk1'': -*** Return ''true'' if ''pkj = pki'', otherwise return ''false''. +*** Return ''true'' if ''pkj = pk' '', otherwise return ''false''. * Return ''false'' -The algorithm ''KeyAggCoeff(pk1..u, i)'' is defined as: +The algorithm ''KeyAggCoeff(pk1..u, pk')'' is defined as: * Let ''L = HashKeys(pk1..u)''. -* Return 1 if ''IsSecond(pk1..u, i)'', otherwise return ''int(hashKeyAgg coefficient(L || pki) mod n''. +* If ''IsSecond(pk1..u, pk')'': +** Return 1 +* Return ''int(hashKeyAgg coefficient(L || pk')) mod n'' == Applications == == Test Vectors and Reference Code == +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'' function. + == Footnotes == From 3c122d07807dfaea6457d8a48ba4adc7a15f1182 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 5 Jan 2022 22:53:58 +0000 Subject: [PATCH 3/5] musig-spec: improve definition of lift_x --- doc/musig-spec.mediawiki | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index f94e5a7a7..28db422ec 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -48,12 +48,14 @@ The following conventions are used, with constants as defined for [https://www.s ** 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 ''cbytes(P)'', where ''P'' is a point, returns ''a || bytes(P)'' where ''a'' 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..p-1'', returns the point ''P'' for which ''x(P) = x'' - Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x3 + 7 mod p'' and they can be computed as ''y = ±c(p+1)/4 mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. and ''has_even_y(P)'', or fails if no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode: +** The function ''lift_x(x)'', where ''x'' is an integer in range ''0..2256-1'', returns the point ''P'' for which ''x(P) = x'' + Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x3 + 7 mod p'' and they can be computed as ''y = ±c(p+1)/4 mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. and ''has_even_y(P)'', or fails if ''x'' is greater than ''p-1'' or no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode: +*** Fail if ''x > p-1''. *** Let ''c = x3 + 7 mod p''. -*** Let ''y = c(p+1)/4 mod p''. -*** Fail if ''c ≠ y2 mod p''. -*** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y'' if ''y mod 2 = 0'' or ''y(P) = p-y'' otherwise. +*** Let ''y' = c(p+1)/4 mod p''. +*** 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 ''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)''. From 4824220bb71102064babf832372f4e5ae43ef16f Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 5 Jan 2022 22:54:24 +0000 Subject: [PATCH 4/5] musig-spec: describe NonceGen, NonceAgg, Sign,PartialSig{Verify,Agg} --- doc/musig-spec.mediawiki | 139 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 4 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 28db422ec..7d5fca5d8 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -27,6 +27,7 @@ This document is licensed under the 2-clause BSD license. * The KeyAgg coefficient is computed by hashing the key instead of key index. Otherwise, if the pubkey list gets sorted, the signer needs to translate between key indices pre- and post-sorting. * 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. === Specification === @@ -46,7 +47,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 ''cbytes(P)'', where ''P'' is a point, returns ''a || bytes(P)'' where ''a'' is ''2'' if ''has_even_y(P)'' and ''3'' otherwise. +** 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'' Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x3 + 7 mod p'' and they can be computed as ''y = ±c(p+1)/4 mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. and ''has_even_y(P)'', or fails if ''x'' is greater than ''p-1'' or no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode: @@ -56,6 +57,8 @@ 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 ''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)''. @@ -75,12 +78,16 @@ Input: * The public keys ''pk1..u'': ''u'' 32-byte arrays The algorithm ''KeyAgg(pk1..u)'' is defined as: +* Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails. +* Return ''bytes(Q)''. + +The algorithm ''KeyAggInternal(pk1..u)'' is defined as: * For ''i = 1 .. u'': ** Let ''ai = KeyAggCoeff(pk1..u, pki)''. -** Let ''Pi = lift_x(int(pki))''; fail if it fails. +** Let ''Pi = point(pki)''; fail if that fails. * Let ''Q = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' * Fail if ''is_infinite(Q)''. -* Return ''bytes(Q)''. +* Return ''Q''. The algorithm ''HashKeys(pk1..u)'' is defined as: * Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' @@ -97,12 +104,136 @@ The algorithm ''KeyAggCoeff(pk1..u, pk')'' is defined as: ** Return 1 * Return ''int(hashKeyAgg coefficient(L || pk')) mod n'' +==== Nonce Generation ==== + +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)'' +* Let ''secnonce = bytes(k1) || bytes(k2)'' +* Return ''secnonce'' and ''pubnonce'' + +==== Nonce Aggregation ==== + +* 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: +* 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'' +* Return ''aggnonce = cbytes(R1) || cbytes(R2)'' + +==== 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 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)'' +* 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 ''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 + +==== Partial Signature Verification ==== + +Input: +* The partial signature ''psig'': a 32-byte array +* 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 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, m, i)'' is defined as: +* Let ''aggnonce = NonceAgg(pubnonce1..u)''; fail if that fails +* Run ''PartialSigVerifyInternal(psig, pubnoncei, aggnonce, pk1..u, pki, m)'' +* 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 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 public key of the signer ''pk*'' (in ''pk1..u''): a 32-byte array +* The message ''m'': a 32-byte array + +The algorithm ''PartialSigVerifyInternal(psig, pubnonce, aggnonce, pk1..u, pk*, m)'' is defined as: +* 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'' +* 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 algorithm ''SigAgg(R, sig1..u)'' is defined as: +* For ''i = 1 .. u'': +** Let ''si = int(sigi)''; fail if ''si ≥ n''. +* Let ''s = s1 + ... + su 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. + +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. + +''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''. + == Applications == == Test Vectors and Reference Code == 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'' function. +Search for the ''musig_test_vectors_keyagg'' and ''musig_test_vectors_sign'' functions. == Footnotes == From 69b392f3cbd4dbff953ec8f2ff44f6a8f612b661 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 31 Dec 2021 20:56:49 +0000 Subject: [PATCH 5/5] musig: move explanation for aggnonce=inf to spec --- doc/musig-spec.mediawiki | 13 +++++++++++++ src/modules/musig/session_impl.h | 16 +--------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 7d5fca5d8..7a6eea78d 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -126,6 +126,19 @@ The algorithm ''NonceAgg(pubnonce1..u)'' is defined as: ** Let ''Ri = R'i'' if not ''is_infinite(R'i)'', otherwise let Ri = G'' * Return ''aggnonce = cbytes(R1) || cbytes(R2)'' +===== Note on ''is_infinite(R'i)'' ===== + +If ''is_infinite(R'i)'' there is at least one dishonest signer (except with negligible probability). +If we would fail here, we will never be able to determine who it is. +Therefore, we should continue such 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, we set the aggregate nonce to some arbitrary point, the generator. + +This modification does not affect the security of the scheme. +''NonceAgg'' (both the original and modified version) only depends on publicly available data (the set of public pre-nonces from every signer). +Thus in the multi-signature security game (EUF-CMA), we can consider ''NonceAgg'' to be performed by the adversary (rather than the challenger) without loss of generality. +The modification changes neither the behavior of the EUF-CMA challenger nor the condition required to win the security game (the adversary still has to output a valid forgery according to the unmodified MuSig2* scheme). Since we've already proved that MuSig2* is secure against an arbitrary adversary, we can conclude that the modified scheme is still secure. + ==== Signing ==== Input: diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 7cdcebe3e..1231b2fc0 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -362,21 +362,7 @@ int secp256k1_musig_nonce_agg(const secp256k1_context* ctx, secp256k1_musig_aggn } for (i = 0; i < 2; i++) { if (secp256k1_gej_is_infinity(&aggnonce_ptj[i])) { - /* There must be at least one dishonest signer. If we would return 0 - here, we will never be able to determine who it is. Therefore, we - should continue such that the culprit is revealed when collecting - and verifying partial signatures. - - However, dealing with the point at infinity (loading, - de-/serializing) would require a lot of extra code complexity. - Instead, we set the aggregate nonce to some arbitrary point (the - generator). This is secure, because it only restricts the - abilities of the attacker: an attacker that forces the sum of - nonces to be infinity by sending some maliciously generated nonce - pairs can be turned into an attacker that forces the sum to be - the generator (by simply adding the generator to one of the - malicious nonces), and this does not change the winning condition - of the EUF-CMA game. */ + /* Set to G according to the specification */ aggnonce_pt[i] = secp256k1_ge_const_g; } else { secp256k1_ge_set_gej(&aggnonce_pt[i], &aggnonce_ptj[i]);