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:
parent
3aec4332b5
commit
fb060a0c4e
@ -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> ≥ 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' ≥ 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 ≥ 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 ≠ 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> ≥ n''.
|
||||
* Let ''s = s<sub>1</sub> + ... + s<sub>u</sub> mod n''
|
||||
|
Loading…
x
Reference in New Issue
Block a user