From 633d01add0f259abe638a9f2763685bbaecd527f Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 4 Feb 2022 14:10:09 +0000 Subject: [PATCH] musig-spec: add x-only and ordinary tweaking to musig --- doc/musig-spec.mediawiki | 70 ++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index 1bb8edae..df4c26a0 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -81,18 +81,25 @@ The algorithm '''''KeySort(pk1..u)''''' is defined as: Input: * The number ''u'' of public keys with ''0 < u < 2^32'' * The public keys ''pk1..u'': ''u'' 32-byte arrays +* The number ''v'' of tweaks with ''0 ≤ v < 2^32'' +* The tweaks ''tweak1..v'': ''v'' 32-byte arrays +* The tweak methods ''is_xonly_t1..v'' : ''v'' booleans -The algorithm '''''KeyAgg(pk1..u)''''' is defined as: -* Let ''Q = KeyAggInternal(pk1..u)''; fail if that fails. +The algorithm '''''KeyAgg(pk1..u, tweak1..v, is_xonly_t1..v)''''' is defined as: +* Let ''(Q,_,_) = KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''; fail if that fails. * Return ''bytes(Q)''. -The algorithm '''''KeyAggInternal(pk1..u)''''' is defined as: +The algorithm '''''KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''''' is defined as: * For ''i = 1 .. u'': ** Let ''ai = KeyAggCoeff(pk1..u, pki)''. ** Let ''Pi = point(pki)''; fail if that fails. -* Let ''Q = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' -* Fail if ''is_infinite(Q)''. -* Return ''Q''. +* Let ''Q0 = a1⋅P1 + a2⋅P1 + ... + au⋅Pu'' +* Fail if ''is_infinite(Q0)''. +* Let ''tacc0 = 0'' +* Let ''gacc0 = 1'' +* For ''i = 1 .. v'': +** Let ''(Qi, gacci, tacci) = Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''; fail if that fails +* Return ''(Qv, gaccv, taccv)''. The algorithm '''''HashKeys(pk1..u)''''' is defined as: * Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' @@ -109,6 +116,17 @@ The algorithm '''''KeyAggCoeff(pk1..u, pk')''''' is defined as: ** Return 1 * Return ''int(hashKeyAgg coefficient(L || pk')) mod n'' +The algorithm '''''Tweak(Qi-1, gacci-1, tweaki, tacci-1, is_xonly_ti)''''' is defined as: +* If ''is_xonly_ti'' and ''not has_even_y(Qi-1)'': +** Let ''gi-1 = -1 mod n'' +* Else: let ''gi-1 = 1'' +* Let ''ti = int(tweaki)''; fail if ''t ≥ n'' +* Let ''Qi = gi-1⋅Qi-1 + ti⋅G'' +** Fail if ''is_infinite(Qi)'' +* Let ''gacci = gi-1⋅gacci-1 mod n'' +* Let ''tacci = ti + gi-1⋅tacci-1 mod n'' +* Return ''(Qi, gacci, tacci)'' + ==== Nonce Generation ==== The algorithm '''''NonceGen()''''' is defined as: @@ -137,22 +155,25 @@ 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 number ''v'' of tweaks with ''0 ≤ v < 2^32'' +* The tweaks ''tweak1..v'': ''v'' 32-byte arrays +* The tweak methods ''is_xonly_t1..v'' : ''v'' booleans * The message ''m'': a 32-byte array -We write "Let ''(aggnonce, u, pk1..u, m) = session_ctx''" to assign names to the elements of a Session Context. +We write "Let ''(aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, 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 ''(aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m) = session_ctx'' +* Let ''(Q, gaccv, taccv) = KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''; 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)'' +* Return ''(Q, gaccv, taccv, b, R, e)'' The algorithm '''''GetSessionKeyAggCoeff(session_ctx, P)''''' is defined as: -* Let ''(_, u, pk1..u, _) = session_ctx'' +* Let ''(_, u, pk1..u, _, _, _, _) = session_ctx'' * Return ''KeyAggCoeff(pk1..u, bytes(P))'' ==== Signing ==== @@ -163,7 +184,7 @@ Input: * 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 ''(Q, gaccv, _, 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'' @@ -171,7 +192,9 @@ The algorithm '''''Sign(secnonce, sk, session_ctx)''''' is defined as: * 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 ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' +* Let ''gp = 1'' if ''has_even_y(P)'', otherwise let ''gp = -1 mod n'' +* Let ''d = gv⋅gaccv⋅gp⋅d' '' * Let ''s = (k1 + b⋅k2 + e⋅mu⋅d) mod n'' * Let ''psig = bytes(s)'' * Let ''pubnonce = cbytes(k'1⋅G) || cbytes(k'2⋅G)'' @@ -185,12 +208,15 @@ Input: * The number ''u'' of public nonces and public keys with ''0 < u < 2^32'' * The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays * The public keys ''pk1..u'': ''u'' 32-byte arrays +* The number ''v'' of tweaks with ''0 ≤ v < 2^32'' +* The tweaks ''tweak1..v'': ''v'' 32-byte arrays +* The tweak methods ''is_xonly_t1..v'' : ''v'' booleans * The message ''m'': a 32-byte array * The index of the signer ''i'' in the public nonces and public keys with ''0 < i ≤ u'' -The algorithm '''''PartialSigVerify(psig, pubnonce1..u, pk1..u, m, i)''''' is defined as: +The algorithm '''''PartialSigVerify(psig, pubnonce1..u, pk1..u, tweak1..v, is_xonly_t1..v, m, i)''''' is defined as: * Let ''aggnonce = NonceAgg(pubnonce1..u)''; fail if that fails -* Let ''session_ctx = (aggnonce, u, pk1..u, m)'' +* Let ''session_ctx = (aggnonce, u, pk1..u, v, tweak1..v, is_xonly_t1..v, m)'' * Run ''PartialSigVerifyInternal(psig, pubnoncei, pki, session_ctx)'' * Return success iff no failure occurred before reaching this point. @@ -203,13 +229,14 @@ Input: * The ''session_ctx'': a [[#session-context|Session Context]] data structure The algorithm '''''PartialSigVerifyInternal(psig, pubnonce, pk*, session_ctx)''''' is defined as: -* Let ''(Q, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails +* Let ''(Q, gaccv, _, b, R, e) = GetSessionValues(session_ctx)''; fail if that fails * Let ''s = int(psig)''; fail if ''s ≥ n'' * 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 ''P' = point(pk*)''; fail if that fails -* Let ''P = P' '' if ''has_even_y(Q)'', otherwise let ''P = -P' '' +* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' +* Let ''g' = gv⋅gaccv mod n'' +* Let ''P = g'⋅point(pk*)''; fail if that fails * 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. @@ -222,15 +249,16 @@ Input: * The ''session_ctx'': a [[#session-context|Session Context]] data structure The algorithm '''''PartialSigAgg(psig1..u, session_ctx)''''' is defined as: -* Let ''(_, _, R, _) = GetSessionValues(session_ctx)''; fail if that fails +* Let ''(Q, _, taccv, _, _, R, e) = 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'' +* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' +* Let ''s = s1 + ... + su + e⋅gv⋅taccv mod n'' * Return ''sig = ''bytes(R) || bytes(s)'' === Signing Flow === -Note that this specification unnecessarily recomputes intermediary values (such as the aggregate public key) that can be cached in real implementations. +Note that this specification unnecessarily recomputes intermediary values (such as the aggregate and tweaked public key) that can be cached in real implementations. There are multiple ways to use above algorithms and arrive at a final Schnorr signature. One of them can be described as follows: