From fb060a0c4e36486fed4d1981b2314949b6a3fbb8 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 7 Feb 2022 13:14:26 +0000 Subject: [PATCH] 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. --- doc/musig-spec.mediawiki | 61 ++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index d042363f..5f0f72ff 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -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 ''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)''. +* 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(pubnonce1..u)''''' is defined as: **
Let ''Ri = R'i'' if not ''is_infinite(R'i)'', otherwise let Ri = G'' (see [[#dealing-with-infinity-in-nonce-aggregation|Dealing with Infinity in Nonce Aggregation]]) * Return ''aggnonce = cbytes(R1) || cbytes(R2)'' -==== 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 ''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 +We write "Let ''(aggnonce, u, pk1..u, m) = session_ctx''" to assign names to the elements of a Session Context. + +The algorithm '''''GetSessionValues(session_ctx)''''' is defined as: +* Let ''(aggnonce, u, pk1..u, m) = session_ctx'' * Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails * Let ''b = int(hashMuSig/noncecoef(aggnonce || bytes(Q) || m)) mod n'' +* Let ''R1 = pointc(aggnonce[0:33]), R2 = pointc(aggnonce[33:66])''; fail if that fails * Let ''R = R1 + b⋅R2'' * Fail if ''is_infinite(R)'' +* Let ''e = int(hashBIP0340/challenge(bytes(R) || bytes(Q) || m)) mod n'' +* Return ''(Q, b, R, e)'' + +The algorithm '''''GetSessionKeyAggCoeff(session_ctx, P)''''' is defined as: +* Let ''(_, u, pk1..u, _) = session_ctx'' +* Return ''KeyAggCoeff(pk1..u, 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'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 ''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(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.. +* If ''PartialSigVerifyInternal(psig, pubnonce, bytes(P), session_ctx)'' (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 ==== @@ -169,7 +187,8 @@ Input: 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)'' +* Let ''session_ctx = (aggnonce, u, pk1..u, m)'' +* Run ''PartialSigVerifyInternal(psig, pubnoncei, pki, session_ctx)'' * Return success iff no failure occurred before reaching this point. ===== PartialSigVerifyInternal ===== @@ -177,36 +196,30 @@ The algorithm '''''PartialSigVerify(psig, pubnonce1..u, pk1..u1..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 public key of the signer ''pk*'' (in ''pk1..u'' of the session_ctx''): a 32-byte array +* The ''session_ctx'': a [[#session-context|Session Context]] data structure -The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, aggnonce, pk1..u, pk*, m)''''' is defined as: +The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, pk*, 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 ''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' '' +* Let ''mu = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails * 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 ''psig1..u'': ''u'' 32-byte arrays +* The ''session_ctx'': a [[#session-context|Session Context]] data structure -The algorithm '''''PartialSigAgg(R, psig1..u)''''' is defined as: +The algorithm '''''PartialSigAgg(psig1..u, session_ctx)''''' is defined as: +* Let ''(_, _, R, _) = GetSessionValues(session_ctx)''; fail if that fails * For ''i = 1 .. u'': ** Let ''si = int(psigi)''; fail if ''si ≥ n''. * Let ''s = s1 + ... + su mod n''