Skip to content

Commit

Permalink
musig-spec: move description of secret key negation to spec
Browse files Browse the repository at this point in the history
Also fix bug in description that resulted in a wrong definition of t.
And rename keyagg coefficient from 'mu' to 'a' since we don't use the term "musig
coefficient" anymore and a is what is used in the paper.
  • Loading branch information
jonasnick committed Mar 21, 2022
1 parent 633d01a commit 57eb6b4
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 113 deletions.
98 changes: 91 additions & 7 deletions doc/musig-spec.mediawiki
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,11 @@ The algorithm '''''Sign(secnonce, sk, session_ctx)''''' is defined as:
* Let ''d' = int(sk)''
* Fail if ''d' = 0'' or ''d' ≥ n''
* Let ''P = d'⋅G''
* Let ''mu = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails
* Let ''g<sub>v</sub> = 1'' if ''has_even_y(Q)'', otherwise let ''g<sub>v</sub> = -1 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 ''d = g<sub>v</sub>⋅gacc<sub>v</sub>⋅gp⋅d' ''
* Let ''s = (k<sub>1</sub> + b⋅k<sub>2</sub> + e⋅mu⋅d) mod n''
* Let ''g<sub>v</sub> = 1'' if ''has_even_y(Q)'', otherwise let ''g<sub>v</sub> = -1 mod n''
* <div id="Sign negation"></div>Let ''d = g<sub>v</sub>⋅gacc<sub>v</sub>⋅gp⋅d' '' (See [[negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]])
* Let ''s = (k<sub>1</sub> + b⋅k<sub>2</sub> + e⋅a⋅d) mod n''
* Let ''psig = bytes(s)''
* Let ''pubnonce = cbytes(k'<sub>1</sub>⋅G) || cbytes(k'<sub>2</sub>⋅G)''
* If ''PartialSigVerifyInternal(psig, pubnonce, bytes(P), session_ctx)'' (see below) returns failure, abort<ref>Verifying 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.</ref>.
Expand Down Expand Up @@ -236,9 +236,9 @@ The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, pk<sup>*</sup>, sess
* Let ''R<sup>*</sup> = R<sup>*</sup>' '' if ''has_even_y(R)'', otherwise let ''R<sup>*</sup> = -R<sup>*</sup>' ''
* Let ''g<sub>v</sub> = 1'' if ''has_even_y(Q)'', otherwise let ''g<sub>v</sub> = -1 mod n''
* Let ''g' = g<sub>v</sub>⋅gacc<sub>v</sub> mod n''
* Let ''P = g'⋅point(pk<sup>*</sup>)''; fail if that fails
* Let ''mu = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails
* Fail if ''s⋅G &ne; R<sup>*</sup> + e⋅mu⋅P''
* <div id="SigVerify negation"></div>Let ''P = g'⋅point(pk<sup>*</sup>)''; 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 &ne; R<sup>*</sup> + e⋅a⋅P''
* Return success iff no failure occurred before reaching this point.
==== Partial Signature Aggregation ====
Expand Down Expand Up @@ -288,6 +288,90 @@ The algorithm '''''OrdinaryTweak(P, t)''''' is defined as:
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.

<poem>
The following public keys arise as intermediate steps in the MuSig protocol:
''P<sub>i</sub>'' as computed in ''KeyAggInternal'' is the point corresponding to the ''i''-th signer's x-only public key. Defining ''d'<sub>i</sub>'' to be the ''d' '' value as computed in the ''Sign'' algorithm of the ''i''-th signer, we have
''P<sub>i</sub> = with_even_y(d'<sub>i</sub>⋅G) ''.
''Q<sub>0</sub>'' is an aggregate of the signer's public keys and defined in ''KeyAggInternal'' as
''Q<sub>0</sub> = a<sub>1</sub>⋅P<sub>1</sub> + a<sub>2</sub>⋅P<sub>1</sub> + ... + a<sub>u</sub>⋅P<sub>u</sub>''.
''Q<sub>i</sub>'' as computed in ''Tweak'' for ''1 &le; i &le; v'' is the tweaked public key after the ''i''-th tweaking operation. It holds that
''Q<sub>i</sub> = f(i-1) + t<sub>i</sub>⋅G'' for ''i = 1, ..., v'' where
''f(i) := with_even_y(Q<sub>i</sub>)'' if ''is_xonly_t<sub>i+1</sub>'' and
''f(i) := Q<sub>i</sub>'' otherwise.
</poem>

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(Q<sub>v</sub>)''.

<poem>
We define ''gp<sub>i</sub>'' for ''1 &le; i &le; u'' to be ''gp '' as computed in the ''Sign'' algorithm of the ''i''-th signer. It holds that
''P<sub>i</sub> = gp<sub>i</sub>⋅d'<sub>i</sub>⋅G''.

For ''0 &le; i &le; v-1'', the ''Tweak'' algorithm called from ''KeyAggInternal'' sets ''g<sub>i</sub>'' to ''-1 mod n'' if and only if ''is_xonly_t<sub>i+1</sub>'' is true and ''Q<sub>i</sub>'' has an odd Y coordinate. Therefore, we have
''f(i) = g<sub>i</sub>⋅Q<sub>i</sub>'' for ''0 &le; i &le; v - 1''.

Furthermore, the ''Sign'' and ''PartialSigVerify'' algorithms set ''g<sub>v</sub>'' such that
''with_even_y(Q<sub>v</sub>) = g<sub>v</sub>⋅Q<sub>v</sub>''.
</poem>

<poem>
So, the (x-only) final public key is
''with_even_y(Q<sub>v</sub>)
= g<sub>v</sub>⋅Q<sub>v</sub>
= g<sub>v</sub>⋅(f(v-1) + t<sub>v</sub>⋅G)
= g<sub>v</sub>⋅(g<sub>v-1</sub>⋅(f(v-2) + t<sub>v-1</sub>⋅G) + t<sub>v</sub>⋅G)
= g<sub>v</sub>⋅g<sub>v-1</sub>⋅f(v-2) + g<sub>v</sub>⋅(t<sub>v</sub> + g<sub>v-1</sub>⋅t<sub>v-1</sub>)⋅G
= g<sub>v</sub>⋅g<sub>v-1</sub>⋅f(v-2) + (sum<sub>i=v-1..v</sub> t<sub>i</sub>⋅prod<sub>j=i..v</sub> g<sub>j</sub>)⋅G
= g<sub>v</sub>⋅g<sub>v-1</sub>⋅...⋅g<sub>1</sub>⋅f(0) + (sum<sub>i=1..v</sub> t<sub>i</sub>⋅prod<sub>j=i..v</sub> g<sub>j</sub>)⋅G
= g<sub>v</sub>⋅...⋅g<sub>0</sub>⋅Q<sub>0</sub> + g<sub>v</sub>⋅tacc<sub>v</sub>⋅G''
where ''tacc<sub>i</sub>'' is computed by ''KeyAggInternal'' and ''Tweak'' as follows:
''tacc<sub>0</sub> = 0
tacc<sub>i</sub> = t<sub>i</sub> + g<sub>i-1</sub>⋅tacc<sub>i-1</sub> for i=1..v mod n''
for which it holds that ''g<sub>v</sub>⋅tacc<sub>v</sub> = sum<sub>i=1..v</sub> t<sub>i</sub>⋅prod<sub>j=i..v</sub> g<sub>j</sub>''.
</poem>

<poem>
''KeyAggInternal'' and ''Tweak'' compute
''gacc<sub>0</sub> = 1
gacc<sub>i</sub> = g<sub>i-1</sub>⋅gacc<sub>i-1</sub> for i=1..v mod n''
So we can rewrite above equation for the final public key as
''with_even_y(Q<sub>v</sub>) = g<sub>v</sub>⋅gacc<sub>v</sub>⋅Q<sub>0</sub> + g<sub>v</sub>⋅tacc<sub>v</sub>⋅G''.
</poem>

<poem>
Then we have
''with_even_y(Q<sub>v</sub>) - g<sub>v</sub>⋅tacc<sub>v</sub>⋅G
= g<sub>v</sub>⋅gacc<sub>v</sub>⋅Q<sub>0</sub>
= g<sub>v</sub>⋅gacc<sub>v</sub>⋅(a<sub>1</sub>⋅P<sub>1</sub> + ... + a<sub>u</sub>⋅P<sub>u</sub>)
= g<sub>v</sub>⋅gacc<sub>v</sub>⋅(a<sub>1</sub>⋅gp<sub>1</sub>⋅d'<sub>1</sub>⋅G + ... + a<sub>u</sub>⋅gp<sub>u</sub>⋅d'<sub>u</sub>⋅G)
= sum<sub>i=1..u</sub>(g<sub>v</sub>⋅gacc<sub>v</sub>⋅gp<sub>i</sub>⋅a<sub>i</sub>⋅d'<sub>i</sub>)*G''.
</poem>

Thus, signer ''i'' multiplies its secret key ''d'<sub>i</sub>'' with ''g<sub>v</sub>⋅gacc<sub>v</sub>⋅gp<sub>i</sub>'' in the ''[[#Sign negation|Sign]]'' algorithm.

==== Negation Of The Public Key When Partially Verifying ====

<poem>
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 = g<sub>v</sub>⋅gacc<sub>v</sub>⋅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.
</poem>

<poem>
The ''[[#SigVerify negation|PartialSigVerifyInternal]]'' algorithm is supposed to check
''s⋅G = R<sup>*</sup> + e⋅a⋅d⋅G''.
</poem>

<poem>
The verifier doesn't have access to ''d⋅G'', but can construct it using the xonly public key ''pk<sup>*</sup>'' as follows:
''d⋅G
= g<sub>v</sub>⋅gacc<sub>v</sub>⋅gp⋅d'⋅G
= g<sub>v</sub>⋅gacc<sub>v</sub>⋅point(pk<sup>*</sup>)''
</poem>

==== Dealing with Infinity in Nonce Aggregation ====

If it happens that ''is_infinite(R'<sub>i</sub>)'' inside ''[[#NonceAgg infinity|NonceAgg]]'' there is at least one dishonest signer (except with negligible probability).
Expand Down
122 changes: 16 additions & 106 deletions src/modules/musig/session_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -513,77 +513,17 @@ 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*gp = g[0]*...g[v]*gp.
* Since all factors are 1 or -1, the key is negated if and only if
* (P[i] has odd y) XOR (is_xonly_t[1] and Q[0] has odd y)
* XOR (is_xonly_t[2] and Q[1] has odd y)
* XOR ... XOR (is_xonly_t[v] and Q[v-1] has odd y)
* XOR (Q[v] has odd y)
* which is equivalent to
* secp256k1_fe_is_odd(&pk.y)
* XOR cache_i.internal_key_parity
* XOR secp256k1_fe_is_odd(&cache_i.pk.y)).
*/
if ((secp256k1_fe_is_odd(&pk.y)
!= secp256k1_fe_is_odd(&cache_i.pk.y))
Expand Down Expand Up @@ -659,41 +599,11 @@ 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 which
* is negative if and only if fe_is_odd(&cache_i.pk.y) XOR
* internal_key_parity. Instead of multiplying g with the public key, we
* negate e which will have the same end result, since e and the public key
* are multiplied later anyway.
*/
if (secp256k1_fe_is_odd(&cache_i.pk.y)
!= cache_i.internal_key_parity) {
Expand Down

0 comments on commit 57eb6b4

Please sign in to comment.