musig-spec: add Session Context to simplify sign/verify/sigagg

Besides reducing the number of arguments, this also removes the R argument from
PartialSigAgg which was not defined precisely:
* The final nonce ''R'' as created during  ''Sign'' or ''PartialSigVerify'': a point

Moreover, this paves the way for adding the tweaking, which requires
PartialSigAgg to also have access to challenge e and can now be easily computed
from the Session Context.
This commit is contained in:
Jonas Nick 2022-02-07 13:14:26 +00:00
parent 3aec4332b5
commit fb060a0c4e

View File

@ -60,6 +60,8 @@ 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 ''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.
==== Key Sorting ====
@ -126,35 +128,51 @@ The algorithm '''''NonceAgg(pubnonce<sub>1..u</sub>)''''' is defined as:
** <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>)''
==== Signing ====
==== Session Context ====
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 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 ''pk<sub>1..u</sub>'': ''u'' 32-byte arrays
* The message ''m'': a 32-byte array
The algorithm '''''Sign(secnonce, sk, aggnonce, pk<sub>1..u</sub>, m)''''' is defined as:
* Let ''R<sub>1</sub> = pointc(aggnonce[0:33]), R<sub>2</sub> = pointc(aggnonce[33:66])''; fail if that fails
We write "Let ''(aggnonce, u, pk<sub>1..u</sub>, m) = session_ctx''" to assign names to the elements of a Session Context.
The algorithm '''''GetSessionValues(session_ctx)''''' is defined as:
* Let ''(aggnonce, u, pk<sub>1..u</sub>, m) = session_ctx''
* Let ''Q = KeyAggInternal(pk<sub>1..u</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 = 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''
* Return ''(Q, b, R, e)''
The algorithm '''''GetSessionKeyAggCoeff(session_ctx, P)''''' is defined as:
* Let ''(_, u, pk<sub>1..u</sub>, _) = session_ctx''
* Return ''KeyAggCoeff(pk<sub>1..u</sub>, 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 ''session_ctx'': a [[#session-context|Session Context]] data structure
The algorithm '''''Sign(secnonce, sk, session_ctx)''''' is defined as:
* Let ''(Q, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails
* Let ''k'<sub>1</sub> = int(secnonce[0:32]), k'<sub>2</sub> = int(secnonce[32:64])''
* Fail if ''k'<sub>i</sub> = 0'' or ''k'<sub>i</sub> &ge; n'' for ''i = 1..2''
* Let ''k<sub>1</sub> = k'<sub>1</sub>, k<sub>2</sub> = k'<sub>2</sub> '' if ''has_even_y(R)'', otherwise let ''k<sub>1</sub> = n - k'<sub>1</sub>, k<sub>2</sub> = n - k<sub>2</sub>''
* Let ''d' = int(sk)''
* Fail if ''d' = 0'' or ''d' &ge; n''
* Let ''P = d'⋅G''
* Let ''mu = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails
* Let ''d = n - d' '' if ''has_even_y(P) `XOR` has_even_y(Q)'', otherwise let ''d = d' ''
* Let ''e = int(hash<sub>BIP0340/challenge</sub>(bytes(R) || bytes(Q) || m)) mod n''
* Let ''mu = KeyAggCoeff(pk<sub>1..u</sub>, bytes(P))''
* Let ''s = (k<sub>1</sub> + b⋅k<sub>2</sub> + e⋅mu⋅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, aggnonce, pk<sub>1..u</sub>, bytes(P), m)'' (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>.
* 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>.
* Return partial signature ''psig''
==== Partial Signature Verification ====
@ -169,7 +187,8 @@ Input:
The algorithm '''''PartialSigVerify(psig, pubnonce<sub>1..u</sub>, pk<sub>1..u</sub>, m, i)''''' is defined as:
* Let ''aggnonce = NonceAgg(pubnonce<sub>1..u</sub>)''; fail if that fails
* Run ''PartialSigVerifyInternal(psig, pubnonce<sub>i</sub>, aggnonce, pk<sub>1..u</sub>, pk<sub>i</sub>, m)''
* Let ''session_ctx = (aggnonce, u, pk<sub>1..u</sub>, m)''
* Run ''PartialSigVerifyInternal(psig, pubnonce<sub>i</sub>, pk<sub>i</sub>, session_ctx)''
* Return success iff no failure occurred before reaching this point.
===== PartialSigVerifyInternal =====
@ -177,36 +196,30 @@ The algorithm '''''PartialSigVerify(psig, pubnonce<sub>1..u</sub>, pk<sub>1..u</
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 ''pk<sub>1..u</sub>'': ''u'' 32-byte arrays
* The public key of the signer ''pk<sup>*</sup>'' (in ''pk<sub>1..u</sub>''): a 32-byte array
* The message ''m'': a 32-byte array
* The public key of the signer ''pk<sup>*</sup>'' (in ''pk<sub>1..u</sub>'' of the session_ctx''): a 32-byte array
* The ''session_ctx'': a [[#session-context|Session Context]] data structure
The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, aggnonce, pk<sub>1..u</sub>, pk<sup>*</sup>, m)''''' is defined as:
The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, pk<sup>*</sup>, session_ctx)''''' is defined as:
* Let ''(Q, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails
* Let ''s = int(psig)''; fail if ''s &ge; n''
* Let ''R<sub>1</sub> = pointc(aggnonce[0:33]), R<sub>2</sub> = pointc(aggnonce[33:66])''; fail if that fails
* Let ''Q = KeyAggInternal(pk<sub>1..u</sub>)''; fail if that fails
* Let ''b = int(hash<sub>MuSig/noncecoef</sub>(aggnonce || bytes(Q) || m)) mod n''
* Let ''R = R<sub>1</sub> + b⋅R<sub>2</sub>''
* 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>' = 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 ''e = int(hash<sub>BIP0340/challenge</sub>(bytes(R) || bytes(Q) || m)) mod n''
* Let ''mu = KeyAggCoeff(pk<sub>1..u</sub>, pk<sup>*</sup>)''
* Let ''P' = point(pk<sup>*</sup>)''; fail if that fails
* Let ''P = P' '' if ''has_even_y(Q)'', otherwise let ''P = -P' ''
* Let ''mu = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails
* Fail if ''s⋅G &ne; R<sup>*</sup> + 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 ''psig<sub>1..u</sub>'': ''u'' 32-byte arrays
* The ''session_ctx'': a [[#session-context|Session Context]] data structure
The algorithm '''''PartialSigAgg(R, psig<sub>1..u</sub>)''''' is defined as:
The algorithm '''''PartialSigAgg(psig<sub>1..u</sub>, session_ctx)''''' is defined as:
* Let ''(_, _, R, _) = GetSessionValues(session_ctx)''; fail if that fails
* For ''i = 1 .. u'':
** Let ''s<sub>i</sub> = int(psig<sub>i</sub>)''; fail if ''s<sub>i</sub> &ge; n''.
* Let ''s = s<sub>1</sub> + ... + s<sub>u</sub> mod n''