diff --git a/doc/musig-reference.py b/doc/musig-reference.py deleted file mode 100644 index f7702492d..000000000 --- a/doc/musig-reference.py +++ /dev/null @@ -1,500 +0,0 @@ -from collections import namedtuple -from typing import Any, List, Optional, Tuple -import hashlib -import secrets -import time - -# WARNING: Implementers should be aware that some inputs could -# trigger assertion errors, and proceed with caution. For example, -# an assertion error raised in one of the functions below should not -# cause a server process to crash. - -# -# The following helper functions were copied from the BIP-340 reference implementation: -# https://github.com/bitcoin/bips/blob/master/bip-0340/reference.py -# - -p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F -n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - -# Points are tuples of X and Y coordinates and the point at infinity is -# represented by the None keyword. -G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8) - -Point = Tuple[int, int] - -# This implementation can be sped up by storing the midstate after hashing -# tag_hash instead of rehashing it all the time. -def tagged_hash(tag: str, msg: bytes) -> bytes: - tag_hash = hashlib.sha256(tag.encode()).digest() - return hashlib.sha256(tag_hash + tag_hash + msg).digest() - -def is_infinite(P: Optional[Point]) -> bool: - return P is None - -def x(P: Point) -> int: - assert not is_infinite(P) - return P[0] - -def y(P: Point) -> int: - assert not is_infinite(P) - return P[1] - -def point_add(P1: Optional[Point], P2: Optional[Point]) -> Optional[Point]: - if P1 is None: - return P2 - if P2 is None: - return P1 - if (x(P1) == x(P2)) and (y(P1) != y(P2)): - return None - if P1 == P2: - lam = (3 * x(P1) * x(P1) * pow(2 * y(P1), p - 2, p)) % p - else: - lam = ((y(P2) - y(P1)) * pow(x(P2) - x(P1), p - 2, p)) % p - x3 = (lam * lam - x(P1) - x(P2)) % p - return (x3, (lam * (x(P1) - x3) - y(P1)) % p) - -def point_mul(P: Optional[Point], n: int) -> Optional[Point]: - R = None - for i in range(256): - if (n >> i) & 1: - R = point_add(R, P) - P = point_add(P, P) - return R - -def bytes_from_int(x: int) -> bytes: - return x.to_bytes(32, byteorder="big") - -def bytes_from_point(P: Point) -> bytes: - return bytes_from_int(x(P)) - -def lift_x(b: bytes) -> Optional[Point]: - x = int_from_bytes(b) - if x >= p: - return None - y_sq = (pow(x, 3, p) + 7) % p - y = pow(y_sq, (p + 1) // 4, p) - if pow(y, 2, p) != y_sq: - return None - return (x, y if y & 1 == 0 else p-y) - -def int_from_bytes(b: bytes) -> int: - return int.from_bytes(b, byteorder="big") - -def has_even_y(P: Point) -> bool: - assert not is_infinite(P) - return y(P) % 2 == 0 - -def schnorr_verify(msg: bytes, pubkey: bytes, sig: bytes) -> bool: - if len(msg) != 32: - raise ValueError('The message must be a 32-byte array.') - if len(pubkey) != 32: - raise ValueError('The public key must be a 32-byte array.') - if len(sig) != 64: - raise ValueError('The signature must be a 64-byte array.') - P = lift_x(pubkey) - r = int_from_bytes(sig[0:32]) - s = int_from_bytes(sig[32:64]) - if (P is None) or (r >= p) or (s >= n): - return False - e = int_from_bytes(tagged_hash("BIP0340/challenge", sig[0:32] + pubkey + msg)) % n - R = point_add(point_mul(G, s), point_mul(P, n - e)) - if (R is None) or (not has_even_y(R)) or (x(R) != r): - return False - return True - -# -# End of helper functions copied from BIP-340 reference implementation. -# - -infinity = None - -def cbytes(P: Point) -> bytes: - a = b'\x02' if has_even_y(P) else b'\x03' - return a + bytes_from_point(P) - -def point_negate(P: Optional[Point]) -> Optional[Point]: - if P is None: - return P - return (x(P), p - y(P)) - -def pointc(x: bytes) -> Point: - P = lift_x(x[1:33]) - if P is None: - raise ValueError('x is not a valid compressed point.') - if x[0] == 2: - return P - elif x[0] == 3: - P = point_negate(P) - assert P is not None - return P - else: - raise ValueError('x is not a valid compressed point.') - -def key_agg(pubkeys: List[bytes], tweaks: List[bytes], is_xonly: List[bool]) -> bytes: - Q, _, _ = key_agg_internal(pubkeys, tweaks, is_xonly) - return bytes_from_point(Q) - -def key_agg_internal(pubkeys: List[bytes], tweaks: List[bytes], is_xonly: List[bool]) -> Tuple[Point, int, int]: - pk2 = get_second_key(pubkeys) - u = len(pubkeys) - Q = infinity - for i in range(u): - P_i = lift_x(pubkeys[i]) - a_i = key_agg_coeff_internal(pubkeys, pubkeys[i], pk2) - Q = point_add(Q, point_mul(P_i, a_i)) - if Q is None: - raise ValueError('The aggregate public key cannot be infinity.') - gacc = 1 - tacc = 0 - v = len(tweaks) - for i in range(v): - Q, gacc, tacc = apply_tweak(Q, gacc, tacc, tweaks[i], is_xonly[i]) - return Q, gacc, tacc - -def hash_keys(pubkeys: List[bytes]) -> bytes: - return tagged_hash('KeyAgg list', b''.join(pubkeys)) - -def get_second_key(pubkeys: List[bytes]) -> bytes: - u = len(pubkeys) - for j in range(1, u): - if pubkeys[j] != pubkeys[0]: - return pubkeys[j] - return bytes_from_int(0) - -def key_agg_coeff(pubkeys: List[bytes], pk_: bytes) -> int: - pk2 = get_second_key(pubkeys) - return key_agg_coeff_internal(pubkeys, pk_, pk2) - -def key_agg_coeff_internal(pubkeys: List[bytes], pk_: bytes, pk2: bytes) -> int: - L = hash_keys(pubkeys) - if pk_ == pk2: - return 1 - return int_from_bytes(tagged_hash('KeyAgg coefficient', L + pk_)) % n - -def apply_tweak(Q: Point, gacc: int, tacc: int, tweak_i: bytes, is_xonly_i: bool) -> Tuple[Point, int, int]: - if len(tweak_i) != 32: - raise ValueError('The tweak must be a 32-byte array.') - if is_xonly_i and not has_even_y(Q): - g = n - 1 - else: - g = 1 - t_i = int_from_bytes(tweak_i) - if t_i >= n: - raise ValueError('The tweak must be less than n.') - Q_i = point_add(point_mul(Q, g), point_mul(G, t_i)) - if Q_i is None: - raise ValueError('The result of tweaking cannot be infinity.') - gacc_i = g * gacc % n - tacc_i = (t_i + g * tacc) % n - return Q_i, gacc_i, tacc_i - -def bytes_xor(a: bytes, b: bytes) -> bytes: - return bytes(x ^ y for x, y in zip(a, b)) - -def nonce_hash(rand: bytes, aggpk: bytes, i: int, msg: bytes, extra_in: bytes) -> int: - buf = b'' - buf += rand - buf += len(aggpk).to_bytes(1, 'big') - buf += aggpk - buf += i.to_bytes(1, 'big') - buf += len(msg).to_bytes(1, 'big') - buf += msg - buf += len(extra_in).to_bytes(4, 'big') - buf += extra_in - return int_from_bytes(tagged_hash('MuSig/nonce', buf)) - -def nonce_gen(sk: bytes, aggpk: bytes, msg: bytes, extra_in: bytes) -> Tuple[bytes, bytes]: - if len(sk) not in (0, 32): - raise ValueError('The optional byte array sk must have length 0 or 32.') - if len(aggpk) not in (0, 32): - raise ValueError('The optional byte array aggpk must have length 0 or 32.') - if len(msg) not in (0, 32): - raise ValueError('The optional byte array msg must have length 0 or 32.') - rand_ = secrets.token_bytes(32) - if len(sk) > 0: - rand = bytes_xor(sk, tagged_hash('MuSig/aux', rand_)) - else: - rand = rand_ - k_1 = nonce_hash(rand, aggpk, 1, msg, extra_in) - k_2 = nonce_hash(rand, aggpk, 2, msg, extra_in) - # k_1 == 0 or k_2 == 0 cannot occur except with negligible probability. - assert k_1 != 0 - assert k_2 != 0 - R_1_ = point_mul(G, k_1) - R_2_ = point_mul(G, k_2) - assert R_1_ is not None - assert R_2_ is not None - pubnonce = cbytes(R_1_) + cbytes(R_2_) - secnonce = bytes_from_int(k_1) + bytes_from_int(k_2) - return secnonce, pubnonce - -def nonce_agg(pubnonces: List[bytes]) -> bytes: - u = len(pubnonces) - aggnonce = b'' - for i in (1, 2): - R_i_ = infinity - for j in range(u): - R_i_ = point_add(R_i_, pointc(pubnonces[j][(i-1)*33:i*33])) - R_i = R_i_ if not is_infinite(R_i_) else G - assert R_i is not None - aggnonce += cbytes(R_i) - return aggnonce - -SessionContext = namedtuple('SessionContext', ['aggnonce', 'pubkeys', 'tweaks', 'is_xonly', 'msg']) - -def get_session_values(session_ctx: SessionContext) -> tuple[Point, int, int, int, Point, int]: - (aggnonce, pubkeys, tweaks, is_xonly, msg) = session_ctx - Q, gacc_v, tacc_v = key_agg_internal(pubkeys, tweaks, is_xonly) - b = int_from_bytes(tagged_hash('MuSig/noncecoef', aggnonce + bytes_from_point(Q) + msg)) % n - R_1 = pointc(aggnonce[0:33]) - R_2 = pointc(aggnonce[33:66]) - R = point_add(R_1, point_mul(R_2, b)) - # The aggregate public nonce cannot be infinity except with negligible probability. - assert R is not None - e = int_from_bytes(tagged_hash('BIP0340/challenge', bytes_from_point(R) + bytes_from_point(Q) + msg)) % n - return (Q, gacc_v, tacc_v, b, R, e) - -def get_session_key_agg_coeff(session_ctx: SessionContext, P: Point) -> int: - (_, pubkeys, _, _, _) = session_ctx - return key_agg_coeff(pubkeys, bytes_from_point(P)) - -# Callers should overwrite secnonce with zeros after calling sign. -def sign(secnonce: bytes, sk: bytes, session_ctx: SessionContext) -> bytes: - (Q, gacc_v, _, b, R, e) = get_session_values(session_ctx) - k_1_ = int_from_bytes(secnonce[0:32]) - k_2_ = int_from_bytes(secnonce[32:64]) - if not 0 < k_1_ < n: - raise ValueError('first secnonce value is out of range.') - if not 0 < k_2_ < n: - raise ValueError('second secnonce value is out of range.') - k_1 = k_1_ if has_even_y(R) else n - k_1_ - k_2 = k_2_ if has_even_y(R) else n - k_2_ - d_ = int_from_bytes(sk) - if not 0 < d_ < n: - raise ValueError('secret key value is out of range.') - P = point_mul(G, d_) - assert P is not None - a = get_session_key_agg_coeff(session_ctx, P) - gp = 1 if has_even_y(P) else n - 1 - g_v = 1 if has_even_y(Q) else n - 1 - d = g_v * gacc_v * gp * d_ % n - s = (k_1 + b * k_2 + e * a * d) % n - psig = bytes_from_int(s) - R_1_ = point_mul(G, k_1_) - R_2_ = point_mul(G, k_2_) - assert R_1_ is not None - assert R_2_ is not None - pubnonce = cbytes(R_1_) + cbytes(R_2_) - # Optional correctness check. The result of signing should pass signature verification. - assert partial_sig_verify_internal(psig, pubnonce, bytes_from_point(P), session_ctx) - return psig - -def partial_sig_verify(psig: bytes, pubnonces: List[bytes], pubkeys: List[bytes], tweaks: List[bytes], is_xonly: List[bool], msg: bytes, i: int) -> bool: - aggnonce = nonce_agg(pubnonces) - session_ctx = SessionContext(aggnonce, pubkeys, tweaks, is_xonly, msg) - return partial_sig_verify_internal(psig, pubnonces[i], pubkeys[i], session_ctx) - -def partial_sig_verify_internal(psig: bytes, pubnonce: bytes, pk_: bytes, session_ctx: SessionContext) -> bool: - (Q, gacc_v, _, b, R, e) = get_session_values(session_ctx) - s = int_from_bytes(psig) - if s >= n: - return False - R_1_ = pointc(pubnonce[0:33]) - R_2_ = pointc(pubnonce[33:66]) - R__ = point_add(R_1_, point_mul(R_2_, b)) - R_ = R__ if has_even_y(R) else point_negate(R__) - g_v = 1 if has_even_y(Q) else n - 1 - g_ = g_v * gacc_v % n - P = point_mul(lift_x(pk_), g_) - if P is None: - return False - a = get_session_key_agg_coeff(session_ctx, P) - return point_mul(G, s) == point_add(R_, point_mul(P, e * a % n)) - -def partial_sig_agg(psigs: List[bytes], session_ctx: SessionContext) -> Optional[bytes]: - (Q, _, tacc_v, _, R, e) = get_session_values(session_ctx) - s = 0 - u = len(psigs) - for i in range(u): - s_i = int_from_bytes(psigs[i]) - if s_i >= n: - return None - s = (s + s_i) % n - g_v = 1 if has_even_y(Q) else n - 1 - s = (s + e * g_v * tacc_v) % n - return bytes_from_point(R) + bytes_from_int(s) -# -# The following code is only used for testing. -# Test vectors were copied from libsecp256k1-zkp's MuSig test file. -# See `musig_test_vectors_keyagg` and `musig_test_vectors_sign` in -# https://github.com/ElementsProject/secp256k1-zkp/blob/master/src/modules/musig/tests_impl.h -# -def fromhex_all(l): - return [bytes.fromhex(l_i) for l_i in l] - -def test_key_agg_vectors(): - X = fromhex_all([ - 'F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9', - 'DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659', - '3590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66', - ]) - - expected = fromhex_all([ - 'E5830140512195D74C8307E39637CBE5FB730EBEAB80EC514CF88A877CEEEE0B', - 'D70CD69A2647F7390973DF48CBFA2CCC407B8B2D60B08C5F1641185C7998A290', - '81A8B093912C9E481408D09776CEFB48AEB8B65481B6BAAFB3C5810106717BEB', - '2EB18851887E7BDC5E830E89B19DDBC28078F1FA88AAD0AD01CA06FE4F80210B', - ]) - - assert key_agg([X[0], X[1], X[2]], [], []) == expected[0] - assert key_agg([X[2], X[1], X[0]], [], []) == expected[1] - assert key_agg([X[0], X[0], X[0]], [], []) == expected[2] - assert key_agg([X[0], X[0], X[1], X[1]], [], []) == expected[3] - -def test_sign_vectors(): - X = fromhex_all([ - 'F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9', - 'DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659', - ]) - - secnonce = bytes.fromhex( - '508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61' + - 'FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F7') - - aggnonce = bytes.fromhex( - '028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61' + - '037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9') - - sk = bytes.fromhex('7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671') - msg = bytes.fromhex('F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF') - - expected = fromhex_all([ - '68537CC5234E505BD14061F8DA9E90C220A181855FD8BDB7F127BB12403B4D3B', - '2DF67BFFF18E3DE797E13C6475C963048138DAEC5CB20A357CECA7C8424295EA', - '0D5B651E6DE34A29A12DE7A8B4183B4AE6A7F7FBE15CDCAFA4A3D1BCAABC7517', - ]) - - pk = bytes_from_point(point_mul(G, int_from_bytes(sk))) - - session_ctx = SessionContext(aggnonce, [pk, X[0], X[1]], [], [], msg) - assert sign(secnonce, sk, session_ctx) == expected[0] - # WARNING: An actual implementation should clear the secnonce after use, - # e.g. by setting secnonce = bytes(64) after usage. Reusing the secnonce, as - # we do here for testing purposes, can leak the secret key. - - session_ctx = SessionContext(aggnonce, [X[0], pk, X[1]], [], [], msg) - assert sign(secnonce, sk, session_ctx) == expected[1] - - session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], [], [], msg) - assert sign(secnonce, sk, session_ctx) == expected[2] - -def test_tweak_vectors(): - X = fromhex_all([ - 'F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9', - 'DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659', - ]) - - secnonce = bytes.fromhex( - '508B81A611F100A6B2B6B29656590898AF488BCF2E1F55CF22E5CFB84421FE61' + - 'FA27FD49B1D50085B481285E1CA205D55C82CC1B31FF5CD54A489829355901F7') - - aggnonce = bytes.fromhex( - '028465FCF0BBDBCF443AABCCE533D42B4B5A10966AC09A49655E8C42DAAB8FCD61' + - '037496A3CC86926D452CAFCFD55D25972CA1675D549310DE296BFF42F72EEEA8C9') - - sk = bytes.fromhex('7FB9E0E687ADA1EEBF7ECFE2F21E73EBDB51A7D450948DFE8D76D7F2D1007671') - msg = bytes.fromhex('F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF') - - tweaks = fromhex_all([ - 'E8F791FF9225A2AF0102AFFF4A9A723D9612A682A25EBE79802B263CDFCD83BB', - 'AE2EA797CC0FE72AC5B97B97F3C6957D7E4199A167A58EB08BCAFFDA70AC0455', - 'F52ECBC565B3D8BEA2DFD5B75A4F457E54369809322E4120831626F290FA87E0', - '1969AD73CC177FA0B4FCED6DF1F7BF9907E665FDE9BA196A74FED0A3CF5AEF9D', - ]) - - expected = fromhex_all([ - '5E24C7496B565DEBC3B9639E6F1304A21597F9603D3AB05B4913641775E1375B', - '78408DDCAB4813D1394C97D493EF1084195C1D4B52E63ECD7BC5991644E44DDD', - 'C3A829A81480E36EC3AB052964509A94EBF34210403D16B226A6F16EC85B7357', - '8C4473C6A382BD3C4AD7BE59818DA5ED7CF8CEC4BC21996CFDA08BB4316B8BC7', - ]) - - pk = bytes_from_point(point_mul(G, int_from_bytes(sk))) - - # A single x-only tweak - session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], tweaks[:1], [True], msg) - assert sign(secnonce, sk, session_ctx) == expected[0] - # WARNING: An actual implementation should clear the secnonce after use, - # e.g. by setting secnonce = bytes(64) after usage. Reusing the secnonce, as - # we do here for testing purposes, can leak the secret key. - - # A single ordinary tweak - session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], tweaks[:1], [False], msg) - assert sign(secnonce, sk, session_ctx) == expected[1] - - # An ordinary tweak followed by an x-only tweak - session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], tweaks[:2], [False, True], msg) - assert sign(secnonce, sk, session_ctx) == expected[2] - - # Four tweaks: x-only, ordinary, x-only, ordinary - session_ctx = SessionContext(aggnonce, [X[0], X[1], pk], tweaks[:4], [True, False, True, False], msg) - assert sign(secnonce, sk, session_ctx) == expected[3] - -def test_sign_and_verify_random(iters): - for i in range(iters): - sk_1 = secrets.token_bytes(32) - sk_2 = secrets.token_bytes(32) - pk_1 = bytes_from_point(point_mul(G, int_from_bytes(sk_1))) - pk_2 = bytes_from_point(point_mul(G, int_from_bytes(sk_2))) - pubkeys = [pk_1, pk_2] - - # In this example, the message and aggregate pubkey are known - # before nonce generation, so they can be passed into the nonce - # generation function as a defense-in-depth measure to protect - # against nonce reuse. - # - # If these values are not known when nonce_gen is called, empty - # byte arrays can be passed in for the corresponding arguments - # instead. - msg = secrets.token_bytes(32) - v = secrets.randbelow(4) - tweaks = [secrets.token_bytes(32) for _ in range(v)] - is_xonly = [secrets.choice([False, True]) for _ in range(v)] - aggpk = key_agg(pubkeys, tweaks, is_xonly) - - # Use a non-repeating counter for extra_in - secnonce_1, pubnonce_1 = nonce_gen(sk_1, aggpk, msg, i.to_bytes(4, 'big')) - - # Use a clock for extra_in - t = time.clock_gettime_ns(time.CLOCK_MONOTONIC) - secnonce_2, pubnonce_2 = nonce_gen(sk_2, aggpk, msg, t.to_bytes(8, 'big')) - - pubnonces = [pubnonce_1, pubnonce_2] - aggnonce = nonce_agg(pubnonces) - - session_ctx = SessionContext(aggnonce, pubkeys, tweaks, is_xonly, msg) - psig_1 = sign(secnonce_1, sk_1, session_ctx) - # Clear the secnonce after use - secnonce_1 = bytes(64) - assert partial_sig_verify(psig_1, pubnonces, pubkeys, tweaks, is_xonly, msg, 0) - - # Wrong signer index - assert not partial_sig_verify(psig_1, pubnonces, pubkeys, tweaks, is_xonly, msg, 1) - - # Wrong message - assert not partial_sig_verify(psig_1, pubnonces, pubkeys, tweaks, is_xonly, secrets.token_bytes(32), 0) - - psig_2 = sign(secnonce_2, sk_2, session_ctx) - # Clear the secnonce after use - secnonce_2 = bytes(64) - assert partial_sig_verify(psig_2, pubnonces, pubkeys, tweaks, is_xonly, msg, 1) - - sig = partial_sig_agg([psig_1, psig_2], session_ctx) - assert schnorr_verify(msg, aggpk, sig) - -if __name__ == '__main__': - test_key_agg_vectors() - test_sign_vectors() - test_tweak_vectors() - test_sign_and_verify_random(4) diff --git a/doc/musig-spec.mediawiki b/doc/musig-spec.mediawiki index a714e7f45..017a0c3e4 100644 --- a/doc/musig-spec.mediawiki +++ b/doc/musig-spec.mediawiki @@ -1,558 +1 @@ -
-  BIP: ?
-  Title: MuSig2
-  Author: Jonas Nick 
-          Tim Ruffing 
-          Elliott Jin 
-  Status: Draft
-  License: BSD-3-Clause
-  Type: Informational
-  Created: 2022-03-22
-
- -== Introduction == - -=== Abstract === - -This document proposes a standard for the [https://eprint.iacr.org/2020/1261.pdf MuSig2] protocol. -The standard is compatible with [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] public keys and signatures. -It supports ''tweaking'', which allows deriving [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32] child keys from aggregate keys and creating [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] Taproot outputs with key and script paths. - -=== Copyright === - -This document is licensed under the 3-clause BSD license. - -=== Motivation === - -MuSig2 is a multi-signature scheme that allows multiple signers to create a single aggregate public key and cooperatively create ordinary Schnorr signatures valid under the aggregate key. -Signing requires interaction between ''all'' signers involved in key aggregation. -(MuSig2 is a ''n-of-n'' multi-signature scheme and not a ''t-of-n' threshold-signature scheme.) - -The primary motivation for MuSig2 is the activation of Taproot ([https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341]) on the Bitcoin network, which introduced the ability to authorize transactions with Schnorr signatures. -This standard allows the creation of aggregate public keys that can be used in Taproot outputs. - -The on-chain footprint of a MuSig2 Taproot output is a single BIP340 public key, and a transaction spending the output only requires a single signature cooperatively produced by all signers. This is '''more compact''' and has '''lower verification cost''' than each signer providing an individual public key and signature, as would be required by an ''n-of-n'' policy implemented using OP_CHECKSIGADD as introduced in ([https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki BIP342]). -As a side effect, the number ''n'' of signers is not limited by any consensus rules when using MuSig2. - -Moreover, MuSig2 offers a '''higher level of privacy''' than OP_CHECKSIGADD: MuSig2 Taproot outputs are indistinguishable for a blockchain observer from regular, single-signer Taproot outputs even though they are actually controlled by multiple signers. By tweaking an aggregate key, the shared Taproot output can have script spending paths that are hidden unless used. - -There are multi-signature schemes other than MuSig2 that are fully compatible with Schnorr signatures. -The MuSig2 variant in this specification stands out by combining all of the following features: -* '''Simple Key Setup''': Key aggregation is non-interactive and fully compatible with BIP340 public keys. -* '''Two Communication Rounds''': MuSig2 is faster in practice than previous three-round multi-signature protocols such as MuSig1, particularly when signers are connected through high-latency anonymous links. Moreover, the need for fewer communication rounds simplifies the specification and reduces the probability that implementations and users make security-relevant mistakes. -* '''Provable security''': MuSig2 has been [https://eprint.iacr.org/2020/1261.pdf proven existentially unforgeable] under the algebraic one-more discrete logarithm (AOMDL) assumption (instead of the discrete logarithm assumption required for single-signer Schnorr signatures). AOMDL is a falsifiable and weaker variant of the well-studied OMDL problem. -* '''Low complexity''': MuSig2 has a substantially lower computational and implementation complexity than alternative schemes like [https://eprint.iacr.org/2020/1057 MuSig-DN]. However, this comes at the cost of having no ability to generate nonces deterministically and the requirement to securely handle signing state. - -=== Design === - -* '''Compatibility with BIP340''': The aggregate public key created as part of this MuSig2 specification is a BIP340 X-only public key, and the signature output at the end of the protocol is a BIP340 signature that passes BIP340 verification for the aggregate key and a message. The public keys that are input to the key aggregation algorithm are also X-only public keys. Compared to compressed serialization, this adds complexity to the specification, but as X-only keys are becoming more common, the full key may not be available. -* '''Tweaking for BIP32 derivations and Taproot''': The specification supports tweaking aggregate public keys and signing for tweaked aggregate public keys. We distinguish two modes of tweaking: ''Ordinary'' tweaking can be used to derive child aggregate public keys per [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]. ''X-only'' tweaking, on the other hand, allows creating a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] tweak to add script paths to a Taproot output. See section [[#tweaking|Tweaking]] below for details. -* '''Non-interactive signing with preprocessing''': The first communication round, exchanging the nonces, can happen before the message or even the exact set of signers is determined. Therefore, the signers can view it as a preprocessing step. Later, when the parameters of the signing session are chosen, they can send partial signatures without additional interaction. -* '''Key aggregation optionally independent of order''': The output of the key aggregation algorithm depends on the order of the input public keys. The specification defines a function to sort the public keys before key aggregation. This will ensure the same output, independent of the initial order. Key aggregation does not sort the public keys by default because applications often already have a canonical order of signers. Nonetheless, applications using this specification can mandate sorting before aggregationApplications that sort input public keys before aggregation should ensure that the sort implementation is reasonably efficient, and in particular does not degenerate to quadratic runtime on pathological inputs.. -* '''Third party nonce aggregation''': Instead of every signer sending their nonce to every other signer, it is possible to use an untrusted third party that collects all signers' nonces, computes an aggregate nonce, and broadcasts it to the signers. This reduces the communication complexity from quadratic to linear in the number of signers. If the aggregator sends an incorrect aggregate nonce, the signing session will fail to produce a valid Schnorr signature. However, the aggregator cannot negatively affect the unforgeability of the scheme. -* '''Partial signature verification''': If any signer sends a partial signature contribution that was not created by honestly following the protocol, the signing session will fail to produce a valid Schnorr signature. This standard specifies a partial signature verification algorithm to identify disruptive signers. It is incompatible with third-party nonce aggregation because the individual nonce is required for partial verification. -* '''MuSig2* optimization''': The specification uses an optimization that allows saving a point multiplication in key aggregation. The MuSig2 scheme with this optimization is called MuSig2* and proven secure in the appendix of the [https://eprint.iacr.org/2020/1261 MuSig2 paper]. The optimization is that the second distinct key in the list of public keys given to the key aggregation algorithm (as well as any keys identical to this key) gets the constant key aggregation coefficient ''1''. -* '''Parameterization of MuSig2 and security''': In this specification, each signer's nonce consists of two elliptic curve points. The [https://eprint.iacr.org/2020/1261 MuSig2 paper] gives distinct security proofs depending on the number of points that constitute a nonce. See section [[#choosing-the-size-of-the-nonce|Choosing the Size of the Nonce]] for a discussion. - -This specification is written with a focus on clarity. -As a result, the specified algorithms are not always optimal in terms of computation and space. -In particular, some values are recomputed but can be cached in actual implementations (see [[#signing-flow|Signing Flow]]). -Also, the signers' public nonces are serialized in compressed format (33 bytes) instead of the smaller (32 bytes) but more complicated X-only serialization. - -== Description == - -When implementing the specification, make sure to understand this section thoroughly, particularly the [[#signing-flow|Signing Flow]], to avoid subtle mistakes that may lead to catastrophic failure. - -=== Signing Flow === - -The basic order of operations to create a multi-signature with the specification is as follows: -The signers start by exchanging public keys and computing an aggregate public key using the ''KeyAgg'' algorithm. -When they want to sign a message, each signer starts the signing session by running ''NonceGen'' to compute ''secnonce'' and ''pubnonce''. -Then, the signers broadcast their ''pubnonce'' to each other and run ''NonceAgg'' to compute an aggregate nonce. -At this point, every signer has the required data to sign, which, in the specification, is stored in a data structure called [[#session-context|Session Context]]. -After running ''Sign'' with the secret signing key, the ''secnonce'' and the session context, each signer sends their partial signature to an aggregator node, which produces a final signature using ''PartialSigAgg''. -If all signers behaved honestly, the result passes [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] verification. - -'''IMPORTANT''': The ''Sign'' algorithm must '''not''' be executed twice with the same ''secnonce''. -Otherwise, it is possible to extract the secret signing key from the two partial signatures output by the two executions of ''Sign''. -To avoid accidental reuse of ''secnonce'', an implementation may securely erase the ''secnonce'' argument by overwriting it with 64 zero bytes after it has been read by ''Sign'. -A ''secnonce'' consisting of only zero bytes is invalid for ''Sign'' and will cause it to fail. - -To simplify the specification, some intermediary values are unnecessarily recomputed from scratch, e.g., when executing ''GetSessionValues'' multiple times. -Actual implementations can cache these values. -As a result, the [[#session-context|Session Context]] may look very different in implementations or may not exist at all. - -==== Public Key Aggregation ==== - -The output of ''KeyAgg'' is dependent on the order of the input public keys. -If the application does not have a canonical order of the signers, the public keys can be sorted with the ''KeySort'' algorithm to ensure that the aggregate key is independent of the order of signers. - -The same public key is allowed to occur more than once in the input of ''KeyAgg'' and ''KeySort''. -This is by design: All algorithms in this specification handle multiple signers who (claim to) have identical public keys properly, -and applications are not required to check for duplicate public keys. -In fact, applications are recommended to omit checks for duplicate public keys in order to simplify error handling. -Moreover, it is often impossible to tell at key aggregation which signer is to blame for the duplicate, i.e., which signer came up with the public key honestly and which disruptive signer copied it. -In contrast, MuSig2 is designed to identify disruptive signers at signing time: any signer who prevents a signing session from completing successfully by sending incorrect contributions in the session can be identified and held accountable (see below). - -==== Nonce Generation ==== - -'''IMPORTANT''': ''NonceGen'' must have access to a high-quality random generator to draw an unbiased, uniformly random value ''rand' ''. -In contrast to BIP340 signing, the values ''k1'' and ''k2'' '''must not be derived deterministically''' from the session parameters because otherwise active attackers can [https://medium.com/blockstream/musig-dn-schnorr-multisignatures-with-verifiably-deterministic-nonces-27424b5df9d6#e3b6 trick the victim into reusing a nonce]. - -The optional arguments to ''NonceGen'' enable a defense-in-depth mechanism that may prevent secret key exposure if ''rand' '' is accidentally not drawn uniformly at random. -If the value ''rand' '' was identical in two ''NonceGen'' invocations, but any optional argument was different, the ''secnonce'' would still be guaranteed be different as well (with overwhelming probability), and thus accidentally using the same ''secnonce'' for ''Sign'' in both sessions would be avoided. -Therefore, it is recommended to provide the optional arguments ''sk'', ''aggpk'', and ''m'' if these session parameters are already determined during nonce generation. -The auxiliary input ''in'' can contain additional contextual data that has a chance of changing between ''NonceGen'' runs, -e.g., a supposedly unique session id (taken from the application), a session counter wide enough not to repeat in practice, any nonces by other signers (if already known), or the serialization of a data structure containing multiple of the above. -However, the protection provided by the optional arguments should only be viewed as a last resort. -In most conceivable scenarios, the assumption that the arguments are different between two executions of ''NonceGen'' is relatively strong, particularly when facing an active attacker. - -In some applications, it is beneficial to generate and exchange ''pubnonces'' before the signer's secret key, the final set of signers, or the message to sign is known. -In this case, only the available arguments are provided to the ''NonceGen'' algorithm. -After this preprocessing phase, the ''Sign'' algorithm can be run immediately when the message and set of signers is determined. -This way, the final signature is created quicker and with fewer roundtrips. -However, applications that use this method presumably store the nonces for a longer time and must therefore be even more careful not to reuse them. -Moreover, this method is not compatible with the defense-in-depth mechanism described in the previous paragraph. - -Instead of every signer broadcasting their ''pubnonce'' to every other signer, the signers can send their ''pubnonce'' to a single aggregator node that runs ''NonceAgg'' and sends the ''aggnonce'' back to the signers. -This technique reduces the overall communication. -The aggregator node does not need to be trusted for the scheme's security to hold. -All the aggregator node can do is prevent the signing session from succeeding by sending out incorrect aggregate nonces. - -In general, MuSig2 signers are stateful in the sense that they first generate ''secnonce'' and then need to store it until they receive the other signer's ''pubnonces'' or the ''aggnonce''. -However, it is possible for one of the signers to be stateless. -This signer waits until it receives the ''pubnonce'' of all the other signers and until session parameters such as a message to sign, public keys, and tweaks are determined. -Then, the signer can run ''NonceGen'', ''NonceAgg'' and ''Sign'' in sequence and send out its ''pubnonce'' along with its partial signature. - -==== Identifiying Disruptive Signers ==== - -If any signer sends an incorrect partial signature, i.e., one that has not then been created with ''Sign'' and the right arguments for the session, the MuSig2 protocol may fail to output a valid Schnorr signature. -This standard provides the method ''PartialSigVerify'' to verify the correctness of partial signatures. -If partial signatures are received over authenticated channels, this method can be used to identify disruptive signers and hold them accountable. -Note that partial signatures are ''not'' signatures. -An adversary can forge a partial signature, i.e., create a partial signature without knowing the secret key for the claimed public keyAssume an adversary wants to forge a partial signature for public key ''P''. It joins the signing session pretending to be two different signers, one with public key ''P' and one with another public key. The adversary can then set the second signer's nonce such that it will be able to produce a partial signature for ''P'', but not for the other claimed signer.. -However, if ''PartialSigVerify'' succeeds for all partial signatures then ''PartialSigAgg'' will return a valid Schnorr signature. - -==== Tweaking ==== - -In addition to public keys, the ''KeyAgg'' algorithm accepts tweaks, which modify the aggregate public key as defined in the [[#tweaking-definition|Tweaking Definition]] subsection. -For example, if ''KeyAgg'' is run with ''v = 2'', ''is_xonly_t1 = false'', ''is_xonly_t2 = true'', then the aggregate key is first ordinarily tweaked with ''tweak1'' and then X-only tweaked with ''tweak2''. - -The purpose of specifying tweaking is to ensure compatibility with existing uses of tweaking, i.e., that the result of signing is a valid signature for the tweaked public key. -The MuSig2 algorithms take arbitrary tweaks as input but accepting arbitrary tweaks may negatively affect the protocol's security. -Instead, signers should obtain the tweaks according to other specifications. -This typically involves deriving the tweaks from a hash of the aggregate public key and some other information. - -Ordinary tweaking can be used to derive child public keys from an aggregate public key using [https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki BIP32]. -On the other hand, X-only tweaking is required for Taproot tweaking per [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341]. -A Taproot-tweaked public key commits to a ''script path'', allowing users to create transaction outputs that are spendable either with a MuSig2 multi-signature or by providing inputs that satisfy the script path. - -==== Modifications to Nonce Generation ==== - -Implementors must avoid modifying the ''NonceGen'' algorithm without being fully aware of the implications. -The following two modifications are secure when applied correctly and may be useful in special circumstances, e.g., in very restricted environments where secure randomness is not available. - -First, on systems where obtaining uniformly random values is much harder than maintaining a global atomic counter, it can be beneficial to modify ''NonceGen''. -Instead of drawing ''rand' '' uniformly at random, ''rand' '' can be the value of an atomic counter that is incremented whenever it is read. -With this modification, the secret signing key ''sk'' of the signer generating the nonce is '''not''' an optional argument and must be provided to ''NonceGen''. -The security of the resulting scheme is then depending on the requirement that the counter must never return the same output in two ''NonceGen'' invocations with the same ''sk''. - -Second, if there is a unique signer who is supposed to send the ''pubnonce'' last, it is possible to modify nonce generation for this single signer to not require high-quality randomness. -If randomness is entirely unavailable, nonce generation for this signer can also be made deterministic. -To obtain such a nonce generation algorithm ''NonceGen' '', the algorithm ''NonceGen'' should be modified as follows: The arguments ''sk'', ''aggpk'' and ''m'' are not optional and must be set precisely to the signer's secret key, the aggregate public key, and message of the session, respectively. -In addition, ''NonceGen '' requires the ''pubnonce'' values of '''all''' other signers (concatenated in the order of signers), which can be provided via the ''in'' argument. -Hence, using ''NonceGen' '' is only possible for the last signer to generate a nonce and makes the signer stateless, similar to the stateless signer described in the [[#nonce-generation|Nonce Generation]] section. -Further inputs can be to added ''in'' as described in the [[#nonce-generation|Nonce Generation]] section. -Lastly, if no randomness, not even low-quality randomness, is available, ''NonceGen' '' can be made deterministic by removing ''rand' '' and setting ''rand'' to ''sk''. -Failure to provide the correct arguments to ''NonceGen' '' will allow attackers to extract secret keys. - -=== Notation === - -The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure scheme. -* Lowercase variables represent integers or byte arrays. -** The constant ''p'' refers to the field size, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F''. -** The constant ''n'' refers to the curve order, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141''. -* Uppercase variables refer to points on the curve with equation ''y2 = x3 + 7'' over the integers modulo ''p''. -** ''is_infinite(P)'' returns whether or not ''P'' is the point at infinity. -** ''x(P)'' and ''y(P)'' are integers in the range ''0..p-1'' and refer to the X and Y coordinates of a point ''P'' (assuming it is not infinity). -** The constant ''G'' refers to the base point, for which ''x(G) = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798'' and ''y(G) = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8''. -** Addition of points refers to the usual [https://en.wikipedia.org/wiki/Elliptic_curve#The_group_law elliptic curve group operation]. -** [https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication Multiplication (⋅) of an integer and a point] refers to the repeated application of the group operation. -* Functions and operations: -** ''||'' refers to byte array concatenation. -** The function ''x[i:j]'', where ''x'' is a byte array and ''i, j ≥ 0'', returns a ''(j - i)''-byte array with a copy of the ''i''-th byte (inclusive) to the ''j''-th byte (exclusive) of ''x''. -** The function ''bytes(n, x)'', where ''x'' is an integer, returns the n-byte encoding of ''x'', most significant byte first. -** The function ''bytes(P)'', where ''P'' is a point, returns ''bytes(x(P))''. -** The function ''len(x)'' where ''x'' is a byte array returns the length of the array. -** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''. -** The function ''with_even_y(P)'', where ''P'' is a point, returns ''P'' if ''is_infinite(P)'' or ''has_even_y(P)''. Otherwise, ''with_even_y(P)'' returns ''-P''. -** The function ''cbytes(P)'', where ''P'' is a point, returns ''a || bytes(P)'' where ''a'' is a byte that is ''2'' if ''has_even_y(P)'' and ''3'' otherwise. -** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is ''x''. -** The function ''lift_x(x)'', where ''x'' is an integer in range ''0..2256-1'', returns the point ''P'' for which ''x(P) = x'' - Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x3 + 7 mod p'' and they can be computed as ''y = ±c(p+1)/4 mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''. and ''has_even_y(P)'', or fails if ''x'' is greater than ''p-1'' or no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode: -*** Fail if ''x > p-1''. -*** Let ''c = x3 + 7 mod p''. -*** Let ''y' = c(p+1)/4 mod p''. -*** Fail if ''c ≠ y'2 mod p''. -*** Let ''y = y' '' if ''y' mod 2 = 0'', otherwise let ''y = p - y' ''. -*** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y''. -** 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. - -=== Specification === - -==== Key Sorting ==== - -Input: -* The number ''u'' of public keys with ''0 < u < 2^32'' -* The public keys ''pk1..u'': ''u'' 32-byte arrays - -'''''KeySort(pk1..u)''''': -* Return ''pk1..u'' sorted in lexicographical order. - -==== Key Aggregation ==== - -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 - -'''''KeyAgg(pk1..u, tweak1..v, is_xonly_t1..v)''''': -* Let ''(Q,_,_) = KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''; fail if that fails. -* Return ''bytes(Q)''. - -'''''KeyAggInternal(pk1..u, tweak1..v, is_xonly_t1..v)''''': -* Let ''pk2 = GetSecondKey(pk1..u)'' -* For ''i = 1 .. u'': -** Let ''Pi = point(pki)''; fail if that fails. -** Let ''ai = KeyAggCoeffInternal(pk1..u, pki, pk2)''. -* 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) = ApplyTweak(Qi-1, gacci-1, tacci-1, tweaki, is_xonly_ti)''; fail if that fails -* Return ''(Qv, gaccv, taccv)''. - -'''''HashKeys(pk1..u)''''': -* Return ''hashKeyAgg list(pk1 || pk2 || ... || pku)'' - -'''''GetSecondKey(pk1..u)''''': -* For ''j = 1 .. u'': -** If ''pkj ≠ pk1'': -*** Return ''pkj'' -* Return ''bytes(32, 0)'' - -'''''KeyAggCoeff(pk1..u, pk')''''': -* Let ''pk2 = GetSecondKey(pk1..u)'': -* Return ''KeyAggCoeffInternal(pk1..u, pk', pk2)'' - -'''''KeyAggCoeffInternal(pk1..u, pk', pk2)''''': -* Let ''L = HashKeys(pk1..u)'' -* If ''pk' = pk2'': -** Return 1 -* Return ''int(hashKeyAgg coefficient(L || pk')) mod n''The key aggregation coefficient is computed by hashing the public key instead of its index, which requires one more invocation of the SHA-256 compression function. However, it results in significantly simpler implementations because signers do not need to translate between public key indices before and after sorting. - -'''''ApplyTweak(Qi-1, gacci-1, tacci-1, tweaki, is_xonly_ti)''''': -* 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 ==== - -Input: -* The secret signing key ''sk'': a 32-byte array or 0-byte array (optional argument) -* The aggregate public key ''aggpk'': a 32-byte array or 0-byte array (optional argument) -* The message ''m'': a 32-byte array or 0-byte array (optional argument) -* The auxiliary input ''in'': a byte array with ''0 ≤ len(in) ≤ 232-1'' (optional argument) - -'''''NonceGen(sk, aggpk, m, in)''''': -* Let ''rand' '' be a 32-byte array freshly drawn uniformly at random -* If ''len(sk) > 0'': -** Let ''rand'' be the byte-wise xor of ''sk'' and ''hashMuSig/aux(rand')''The random data is hashed (with a unique tag) as a precaution against situations where the randomness may be correlated with the secret signing key itself. It is xored with the secret key (rather than combined with it in a hash) to reduce the number of operations exposed to the actual secret key.. -* Else: let ''rand = rand' '' -* Let ''ki = int(hashMuSig/nonce(rand || bytes(1, len(aggpk)) || aggpk || bytes(1, i) || bytes(1, len(m)) || m || bytes(4, len(in)) || in)) mod n'' for ''i = 1,2'' -* Fail if ''k1 = 0'' or ''k2 = 0'' -* Let ''R*1 = k1⋅G, R*2 = k2⋅G'' -* Let ''pubnonce = cbytes(R*1) || cbytes(R*2)'' -* Let ''secnonce = bytes(32, k1) || bytes(32, k2)'' -* Return ''secnonce'' and ''pubnonce'' - -==== Nonce Aggregation ==== - -Input: -* The number ''u'' of ''pubnonces'' with ''0 < u < 2^32'' -* The public nonces ''pubnonce1..u'': ''u'' 66-byte arrays - -'''''NonceAgg(pubnonce1..u)''''': -* For ''i = 1 .. 2'': -** For ''j = 1 .. u'': -*** Let ''Ri,j = pointc(pubnoncej[(i-1)*33:i*33])''; fail if that fails -** Let ''R'i = Ri,1 + Ri,2 + ... + Ri,u'' -**
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)'' - -==== Session Context ==== - -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, v, tweak1..v, is_xonly_t1..v, m) = session_ctx''" to assign names to the elements of a Session Context. - -'''''GetSessionValues(session_ctx)''''': -* 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, gaccv, taccv, b, R, e)'' - - -'''''GetSessionKeyAggCoeff(session_ctx, P)''''': -* 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 - -'''''Sign(secnonce, sk, session_ctx)''''': -* 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'' -* Let ''d' = int(sk)'' -* Fail if ''d' = 0'' or ''d' ≥ n'' -* Let ''P = d'⋅G'' -* Let ''a = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails -* Let ''gp = 1'' if ''has_even_y(P)'', otherwise let ''gp = -1 mod n'' -* Let ''gv = 1'' if ''has_even_y(Q)'', otherwise let ''gv = -1 mod n'' -*
Let ''d = gv⋅gaccv⋅gp⋅d' mod n'' (See [[negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]]) -* Let ''s = (k1 + b⋅k2 + e⋅a⋅d) mod n'' -* Let ''psig = bytes(32, s)'' -* Let ''pubnonce = cbytes(k'1⋅G) || cbytes(k'2⋅G)'' -* 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 ==== - -Input: -* The partial signature ''psig'': a 32-byte array -* 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'' - -'''''PartialSigVerify(psig, pubnonce1..u, pk1..u, tweak1..v, is_xonly_t1..v, m, i)''''': -* Let ''aggnonce = NonceAgg(pubnonce1..u)''; fail if that fails -* 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. - -'''''PartialSigVerifyInternal(psig, pubnonce, pk*, session_ctx)''''': -* 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 ''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 (See [[#negation-of-the-public-key-when-partially-verifying|Negation Of The Public Key When Partially Verifying]]) -* Let ''a = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails -* Fail if ''s⋅G ≠ R* + e⋅a⋅P'' -* Return success iff no failure occurred before reaching this point. - -==== Partial Signature Aggregation ==== - -Input: -* 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 - -'''''PartialSigAgg(psig1..u, session_ctx)''''': -* 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 ''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(32, s)'' - -=== Test Vectors and Reference Code === - -We provide a naive, highly inefficient, and non-constant time [[musig-reference.py|pure Python 3 reference implementation of the key aggregation, partial signing, and partial signature verification algorithms, together with some test vectors]]. -The reference implementation is for demonstration purposes only and not to be used in production environments. - -== Remarks on Security and Correctness == - -=== Tweaking Definition === - -This MuSig2 specification supports two modes of tweaking that correspond to the following algorithms: - -Input: -* ''P'': a point -* The tweak ''t'': an integer with ''0 ≤ t < n '' - -'''''ApplyOrdinaryTweak(P, t)''''': -* Return ''P + t⋅G'' - -'''''ApplyXonlyTweak(P, t)''''': -* Return ''with_even_y(P) + t⋅G'' - -=== Negation Of The Secret Key When Signing === - -In order to produce a partial signature for an X-only public key that is an aggregate of ''u'' X-only keys and tweaked ''v'' times (X-only or ordinarily), the ''[[#Sign negation|Sign]]'' algorithm may need to negate the secret key during the signing process. - - -The following elliptic curve points arise as intermediate steps in the MuSig2 protocol: -• ''Pi'' as computed in ''KeyAggInternal'' is the point corresponding to the ''i''-th signer's X-only public key. Defining ''d'i'' to be the ''i''-th signer's secret key as an integer, i.e. the ''d' '' value as computed in the ''Sign'' algorithm of the ''i''-th signer, we have - ''Pi = with_even_y(d'i⋅G) ''. -• ''Q0'' is an aggregate of the signer's public keys and defined in ''KeyAggInternal'' as - ''Q0 = a1⋅P1 + a2⋅P1 + ... + au⋅Pu''. -• ''Qi'' as computed in ''Tweak'' for ''1 ≤ i ≤ v'' is the tweaked public key after the ''i''-th tweaking operation. It holds that - ''Qi = f(i-1) + ti⋅G'' for ''i = 1, ..., v'' where - ''f(i-1) := with_even_y(Qi-1)'' if ''is_xonly_ti'' and - ''f(i-1) := Qi-1'' otherwise. -• ''with_even_y(Qv)'' is the final result of ''KeyAgg''. - - -The signer's goal is to produce a partial signature corresponding to the final result of ''KeyAgg'', i.e. the X-only public key ''with_even_y(Qv)''. - - -We define ''gpi'' for ''1 ≤ i ≤ u'' to be ''gp '' as computed in the ''Sign'' algorithm of the ''i''-th signer. Note that ''gpi'' indicates whether the ''i''-th signer needed to negate their secret key to produce an X-only public key. In particular, - ''Pi = gpi⋅d'i⋅G''. - -For ''0 ≤ i ≤ v-1'', the ''Tweak'' algorithm called from ''KeyAggInternal'' sets ''gi'' to ''-1 mod n'' if and only if ''is_xonly_ti+1'' is true and ''Qi'' has an odd Y coordinate. In other words, ''gi'' indicates whether ''Qi'' needed to be negated to apply an X-only tweak: - ''f(i) = gi⋅Qi'' for ''0 ≤ i ≤ v - 1''. - -Furthermore, the ''Sign'' and ''PartialSigVerify'' algorithms set ''gv'' depending on whether ''Qv'' needed to be negated to produce the (X-only) final output of ''KeyAgg': - ''with_even_y(Qv) = gv⋅Qv''. - - - -So, the (X-only) final public key is - ''with_even_y(Qv) - = gv⋅Qv - = gv⋅(f(v-1) + tv⋅G) - = gv⋅(gv-1⋅(f(v-2) + tv-1⋅G) + tv⋅G) - = gv⋅gv-1⋅f(v-2) + gv⋅(tv + gv-1⋅tv-1)⋅G - = gv⋅gv-1⋅f(v-2) + (sumi=v-1..v ti⋅prodj=i..v gj)⋅G - = gv⋅gv-1⋅...⋅g1⋅f(0) + (sumi=1..v ti⋅prodj=i..v gj)⋅G - = gv⋅...⋅g0⋅Q0 + gv⋅taccv⋅G'' - where ''tacci'' is computed by ''KeyAggInternal'' and ''Tweak'' as follows: - ''tacc0 = 0 - tacci = ti + gi-1⋅tacci-1 for i=1..v mod n'' - for which it holds that ''gv⋅taccv = sumi=1..v ti⋅prodj=i..v gj''. - - - -''KeyAggInternal'' and ''Tweak'' compute - ''gacc0 = 1 - gacci = gi-1⋅gacci-1 for i=1..v mod n'' -So we can rewrite above equation for the final public key as - ''with_even_y(Qv) = gv⋅gaccv⋅Q0 + gv⋅taccv⋅G''. - - - -Then we have - ''with_even_y(Qv) - gv⋅taccv⋅G - = gv⋅gaccv⋅Q0 - = gv⋅gaccv⋅(a1⋅P1 + ... + au⋅Pu) - = gv⋅gaccv⋅(a1⋅gp1⋅d'1⋅G + ... + au⋅gpu⋅d'u⋅G) - = sumi=1..u(gv⋅gaccv⋅gpi⋅ai⋅d'i)*G''. - - -Intuitively, ''gacci'' tracks accumulated sign flipping and ''tacci'' tracks the accumulated tweak value after applying the first ''i'' individual tweaks. Additionally, ''gv'' indicates whether ''Qv'' needed to be negated to produce the final X-only result, and ''gpi'' indicates whether ''d'i'' needs to be negated to produce the initial X-only key ''Pi''. Thus, signer ''i'' multiplies its secret key ''d'i'' with ''gv⋅gaccv⋅gpi'' in the ''[[#Sign negation|Sign]]'' algorithm. - -==== Negation Of The Public Key When Partially Verifying ==== - - -As explained in [[#negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]] the signer uses a possibly negated secret key - ''d = gv⋅gaccv⋅gp⋅d' mod n'' -when producing a partial signature to ensure that the aggregate signature will correspond to an aggregate public key with even Y coordinate. - - - -The ''[[#SigVerify negation|PartialSigVerifyInternal]]'' algorithm is supposed to check - ''s⋅G = R* + e⋅a⋅d⋅G''. - - - -The verifier doesn't have access to ''d⋅G'', but can construct it using the xonly public key ''pk*'' as follows: -''d⋅G - = gv⋅gaccv⋅gp⋅d'⋅G - = gv⋅gaccv⋅point(pk*)'' -Note that the aggregate public key and list of tweaks are inputs to partial signature verification, so the verifier can also construct ''gv'' and ''gaccv''. - - -=== Dealing with Infinity in Nonce Aggregation === - -If it happens that ''is_infinite(R'i)'' inside ''[[#NonceAgg infinity|NonceAgg]]'' there is at least one dishonest signer (except with negligible probability). -If we fail here, we will never be able to determine who it is. -Therefore, we continue so that the culprit is revealed when collecting and verifying partial signatures. - -However, dealing with the point at infinity requires defining a serialization and may require extra code complexity in implementations. -Instead of incurring this complexity, we make two modifications (compared to the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]) to avoid infinity while still allowing us to detect the dishonest signer: -* In ''NonceAgg'', if an output ''R'i'' would be infinity, instead output the generator (an arbitrary choice). -* In ''Sign'', implicitly disallow the input ''aggnonce'' to contain infinity (since the serialization format doesn't support it). - -The entire ''NonceAgg'' function (both the original and modified version) only depends on publicly available data (the set of public pre-nonces from every signer). -In the unforgeability proof, ''NonceAgg'' is considered to be performed by an untrusted party; thus modifications to ''NonceAgg'' do not affect the unforgeability of the scheme. - -The (implicit) modification to ''Sign'' is equivalent to adding a clause, "abort if the input ''aggnonce'' contained infinity". -This modification only depends on the publicly available ''aggnonce''. -Given a successful adversary against the security game (EUF-CMA) for the modified scheme, a reduction can win the security game for the original scheme by simulating the modification (i.e. checking whether to abort) towards the adversary. - -We conclude that these two modifications preserve the security of the MuSig2* scheme. - - -=== Choosing the Size of the Nonce === - -The [https://eprint.iacr.org/2020/1261 MuSig2 paper] contains two security proofs that apply to different protocol variants. -The first is for a variant where each signer's nonce consists of four elliptic curve points and uses the random oracle model (ROM). -In the second variant, the signers' nonces consist of only two points. -Its proof requires a stronger model, namely the combination of the ROM and the algebraic group model (AGM). -Relying on the stronger model is a legitimate choice for the following reasons: - -First, an approach widely taken is interpreting a Forking Lemma proof in the ROM merely as design justification and ignoring the loss of security due to the Forking Lemma. -If one believes in this approach, then the ROM may not be the optimal model in the first place because some parts of the concrete security bound are arbitrarily ignored. -One may just as well move to the ROM+AGM model, which produces bounds close to the best-known attacks, e.g., for Schnorr signatures. - -Second, as of this writing, there is no instance of a serious protocol with a security proof in the AGM that is not secure in practice. -There are, however, insecure toy schemes with AGM security proofs, but those explicitly violate the requirements of the AGM. -[https://eprint.iacr.org/2022/226.pdf Broken AGM proofs of toy schemes] provide group elements to the adversary without declaring them as group element inputs. -In contrast, in MuSig2, all group elements that arise in the protocol are known to the adversary and declared as group element inputs. -A scheme very similar to MuSig2 and with two-point nonces was independently proven secure in the ROM and AGM by [https://eprint.iacr.org/2020/1245 Alper and Burdges]. - -== Footnotes == - - - -== Acknowledgements == - -We thank Brandon Black, Riccardo Casatta, Russell O'Connor, and Pieter Wuille for their contributions to this document. +This document was moved to [https://github.com/jonasnick/bips/blob/musig2/bip-musig2.mediawiki https://github.com/jonasnick/bips/blob/musig2/bip-musig2.mediawiki]. \ No newline at end of file