Skip to content

Commit

Permalink
Merge pull request bitcoin#1 from jonasnick/musig2-fixups
Browse files Browse the repository at this point in the history
  • Loading branch information
real-or-random authored Apr 5, 2022
2 parents 4ca7d39 + f32f2c5 commit d51c43b
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 17 deletions.
22 changes: 11 additions & 11 deletions bip-musig2.mediawiki
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ In contrast to BIP340 signing, the values ''k<sub>1</sub>'' and ''k<sub>2</sub>'
The optional arguments to ''NonceGen'' enable a defense-in-depth mechanism that may prevent secret key exposure if ''rand' '' is accidentally not drawn uniformly at random.
If the value ''rand' '' was identical in two ''NonceGen'' invocations, but any optional argument was different, the ''secnonce'' would still be guaranteed be different as well (with overwhelming probability), and thus accidentally using the same ''secnonce'' for ''Sign'' in both sessions would be avoided.
Therefore, it is recommended to provide the optional arguments ''sk'', ''aggpk'', and ''m'' if these session parameters are already determined during nonce generation.
The auxiliary input ''in'' can contain additional contextual data that has a chance of changing between ''NonceGen'' runs,
The auxiliary input ''extra_in'' can contain additional contextual data that has a chance of changing between ''NonceGen'' runs,
e.g., a supposedly unique session id (taken from the application), a session counter wide enough not to repeat in practice, any nonces by other signers (if already known), or the serialization of a data structure containing multiple of the above.
However, the protection provided by the optional arguments should only be viewed as a last resort.
In most conceivable scenarios, the assumption that the arguments are different between two executions of ''NonceGen'' is relatively strong, particularly when facing an active attacker.
Expand Down Expand Up @@ -160,9 +160,9 @@ The security of the resulting scheme is then depending on the requirement that t
Second, if there is a unique signer who is supposed to send the ''pubnonce'' last, it is possible to modify nonce generation for this single signer to not require high-quality randomness.
If randomness is entirely unavailable, nonce generation for this signer can also be made deterministic.
To obtain such a nonce generation algorithm ''NonceGen' '', the algorithm ''NonceGen'' should be modified as follows: The arguments ''sk'', ''aggpk'' and ''m'' are not optional and must be set precisely to the signer's secret key, the aggregate public key, and message of the session, respectively.
In addition, ''NonceGen '' requires the ''pubnonce'' values of '''all''' other signers (concatenated in the order of signers), which can be provided via the ''in'' argument.
In addition, ''NonceGen '' requires the ''pubnonce'' values of '''all''' other signers (concatenated in the order of signers), which can be provided via the ''extra_in'' argument.
Hence, using ''NonceGen' '' is only possible for the last signer to generate a nonce and makes the signer stateless, similar to the stateless signer described in the [[#nonce-generation|Nonce Generation]] section.
Further inputs can be to added ''in'' as described in the [[#nonce-generation|Nonce Generation]] section.
Further inputs can be to added ''extra_in'' as described in the [[#nonce-generation|Nonce Generation]] section.
Lastly, if no randomness, not even low-quality randomness, is available, ''NonceGen' '' can be made deterministic by removing ''rand' '' and setting ''rand'' to ''sk''.
Failure to provide the correct arguments to ''NonceGen' '' will allow attackers to extract secret keys.
Expand Down Expand Up @@ -197,7 +197,7 @@ The following conventions are used, with constants as defined for [https://www.s
*** 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 ''cpoint(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 ''hash<sub>tag</sub>(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.
Expand Down Expand Up @@ -233,8 +233,8 @@ Input:
** Let ''a<sub>i</sub> = KeyAggCoeffInternal(pk<sub>1..u</sub>, pk<sub>i</sub>, pk2)''.
* Let ''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>''
* Fail if ''is_infinite(Q<sub>0</sub>)''.
* Let ''tacc<sub>0</sub> = 0''
* Let ''gacc<sub>0</sub> = 1''
* Let ''tacc<sub>0</sub> = 0''
* For ''i = 1 .. v'':
** Let ''(Q<sub>i</sub>, gacc<sub>i</sub>, tacc<sub>i</sub>) = ApplyTweak(Q<sub>i-1</sub>, gacc<sub>i-1</sub>, tacc<sub>i-1</sub>, tweak<sub>i</sub>, is_xonly_t<sub>i</sub>)''; fail if that fails
* Return ''(Q<sub>v</sub>, gacc<sub>v</sub>, tacc<sub>v</sub>)''.
Expand Down Expand Up @@ -275,14 +275,14 @@ Input:
* The secret signing key ''sk'': a 32-byte array or 0-byte array (optional argument)
* The aggregate public key ''aggpk'': a 32-byte array or 0-byte array (optional argument)
* The message ''m'': a 32-byte array or 0-byte array (optional argument)
* The auxiliary input ''in'': a byte array with ''0 &le; len(in) &le; 2<sup>32</sup>-1'' (optional argument)
* The auxiliary input ''extra_in'': a byte array with ''0 &le; len(in) &le; 2<sup>32</sup>-1'' (optional argument)
'''''NonceGen(sk, aggpk, m, in)''''':
'''''NonceGen(sk, aggpk, m, extra_in)''''':
* Let ''rand' '' be a 32-byte array freshly drawn uniformly at random
* If ''len(sk) > 0'':
** Let ''rand'' be the byte-wise xor of ''sk'' and ''hash<sub>MuSig/aux</sub>(rand')''<ref>The random data is hashed (with a unique tag) as a precaution against situations where the randomness may be correlated with the secret signing key itself. It is xored with the secret key (rather than combined with it in a hash) to reduce the number of operations exposed to the actual secret key.</ref>.
* Else: let ''rand = rand' ''
* Let ''k<sub>i</sub> = int(hash<sub>MuSig/nonce</sub>(rand || bytes(1, len(aggpk)) || aggpk || bytes(1, i) || bytes(1, len(m)) || m || bytes(4, len(in)) || in)) mod n'' for ''i = 1,2''
* Let ''k<sub>i</sub> = int(hash<sub>MuSig/nonce</sub>(rand || bytes(1, len(aggpk)) || aggpk || bytes(1, i) || bytes(1, len(m)) || m || bytes(4, len(extra_in)) || extra_in)) mod n'' for ''i = 1,2''
* Fail if ''k<sub>1</sub> = 0'' or ''k<sub>2</sub> = 0''
* Let ''R<sup>*</sup><sub>1</sub> = k<sub>1</sub>⋅G, R<sup>*</sup><sub>2</sub> = k<sub>2</sub>⋅G''
* Let ''pubnonce = cbytes(R<sup>*</sup><sub>1</sub>) || cbytes(R<sup>*</sup><sub>2</sub>)''
Expand All @@ -298,7 +298,7 @@ Input:
'''''NonceAgg(pubnonce<sub>1..u</sub>)''''':
* For ''i = 1 .. 2'':
** For ''j = 1 .. u'':
*** Let ''R<sub>i,j</sub> = pointc(pubnonce<sub>j</sub>[(i-1)*33:i*33])''; fail if that fails
*** Let ''R<sub>i,j</sub> = cpoint(pubnonce<sub>j</sub>[(i-1)*33:i*33])''; fail if that fails
** Let ''R'<sub>i</sub> = R<sub>i,1</sub> + R<sub>i,2</sub> + ... + R<sub>i,u</sub>''
** <div id="NonceAgg infinity"></div>Let ''R<sub>i</sub> = R'<sub>i</sub>'' if not ''is_infinite(R'<sub>i</sub>)'', otherwise let R<sub>i</sub> = G'' (see [[#dealing-with-infinity-in-nonce-aggregation|Dealing with Infinity in Nonce Aggregation]])
* Return ''aggnonce = cbytes(R<sub>1</sub>) || cbytes(R<sub>2</sub>)''
Expand All @@ -320,7 +320,7 @@ We write "Let ''(aggnonce, u, pk<sub>1..u</sub>, v, tweak<sub>1..v</sub>, is_xon
* Let ''(aggnonce, u, pk<sub>1..u</sub>, v, tweak<sub>1..v</sub>, is_xonly_t<sub>1..v</sub>, m) = session_ctx''
* Let ''(Q, gacc<sub>v</sub>, tacc<sub>v</sub>) = KeyAggInternal(pk<sub>1..u</sub>, tweak<sub>1..v</sub>, is_xonly_t<sub>1..v</sub>)''; fail if that fails
* Let ''b = int(hash<sub>MuSig/noncecoef</sub>(aggnonce || bytes(Q) || m)) mod n''
* Let ''R<sub>1</sub> = pointc(aggnonce[0:33]), R<sub>2</sub> = pointc(aggnonce[33:66])''; fail if that fails
* Let ''R<sub>1</sub> = cpoint(aggnonce[0:33]), R<sub>2</sub> = cpoint(aggnonce[33:66])''; fail if that fails
* Let ''R = R<sub>1</sub> + b⋅R<sub>2</sub>''
* Fail if ''is_infinite(R)''
* Let ''e = int(hash<sub>BIP0340/challenge</sub>(bytes(R) || bytes(Q) || m)) mod n''
Expand Down Expand Up @@ -378,7 +378,7 @@ Input:
'''''PartialSigVerifyInternal(psig, pubnonce, pk<sup>*</sup>, session_ctx)''''':
* Let ''(Q, gacc<sub>v</sub>, _, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails
* Let ''s = int(psig)''; fail if ''s &ge; n''
* Let ''R<sup>*</sup><sub>1</sub> = pointc(pubnonce[0:33]), R<sup>*</sup><sub>2</sub> = pointc(pubnonce[33:66])''
* Let ''R<sup>*</sup><sub>1</sub> = cpoint(pubnonce[0:33]), R<sup>*</sup><sub>2</sub> = cpoint(pubnonce[33:66])''
* Let ''R<sup>*</sup>' = R<sup>*</sup><sub>1</sub> + b⋅R<sup>*</sup><sub>2</sub>''
* 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''
Expand Down
12 changes: 6 additions & 6 deletions bip-musig2/reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def point_negate(P: Optional[Point]) -> Optional[Point]:
return P
return (x(P), p - y(P))

def pointc(x: bytes) -> Point:
def cpoint(x: bytes) -> Point:
P = lift_x(x[1:33])
if P is None:
raise ValueError('x is not a valid compressed point.')
Expand Down Expand Up @@ -235,7 +235,7 @@ def nonce_agg(pubnonces: List[bytes]) -> bytes:
for i in (1, 2):
R_i_ = infinity
for j in range(u):
R_i_ = point_add(R_i_, pointc(pubnonces[j][(i-1)*33:i*33]))
R_i_ = point_add(R_i_, cpoint(pubnonces[j][(i-1)*33:i*33]))
R_i = R_i_ if not is_infinite(R_i_) else G
assert R_i is not None
aggnonce += cbytes(R_i)
Expand All @@ -247,8 +247,8 @@ def get_session_values(session_ctx: SessionContext) -> tuple[Point, int, int, in
(aggnonce, pubkeys, tweaks, is_xonly, msg) = session_ctx
Q, gacc_v, tacc_v = key_agg_internal(pubkeys, tweaks, is_xonly)
b = int_from_bytes(tagged_hash('MuSig/noncecoef', aggnonce + bytes_from_point(Q) + msg)) % n
R_1 = pointc(aggnonce[0:33])
R_2 = pointc(aggnonce[33:66])
R_1 = cpoint(aggnonce[0:33])
R_2 = cpoint(aggnonce[33:66])
R = point_add(R_1, point_mul(R_2, b))
# The aggregate public nonce cannot be infinity except with negligible probability.
assert R is not None
Expand Down Expand Up @@ -300,8 +300,8 @@ def partial_sig_verify_internal(psig: bytes, pubnonce: bytes, pk_: bytes, sessio
s = int_from_bytes(psig)
if s >= n:
return False
R_1_ = pointc(pubnonce[0:33])
R_2_ = pointc(pubnonce[33:66])
R_1_ = cpoint(pubnonce[0:33])
R_2_ = cpoint(pubnonce[33:66])
R__ = point_add(R_1_, point_mul(R_2_, b))
R_ = R__ if has_even_y(R) else point_negate(R__)
g_v = 1 if has_even_y(Q) else n - 1
Expand Down

0 comments on commit d51c43b

Please sign in to comment.