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''