musig2.sage 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. # MuSig2: Simple Two-Round Schnorr Multi-Signatures
  2. # https://eprint.iacr.org/2020/1261.pdf
  3. # This scheme is n-of-n, not threshold.
  4. from hashlib import sha256
  5. # Nonces
  6. v = 3
  7. # Participants
  8. n = 5
  9. # Pallas
  10. p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
  11. q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
  12. Fp = GF(p)
  13. Fq = GF(q)
  14. Ep = EllipticCurve(Fp, (0, 5))
  15. Ep.set_order(q)
  16. # NullifierK Generator: g
  17. nfk_x = 0x25e7aa169ca8198d2e375571faf4c9cf5e7eb192ccb5db9bd36f6aa7e447ca75
  18. nfk_y = 0x155c1f851b1a3384880473442008ff755fe0a49ec1c1b4332db8dce21ae001cc
  19. g = Ep([nfk_x, nfk_y])
  20. def hash_domain(domain, *args):
  21. concat = domain.encode() + b"".join(str(arg).encode() for arg in args)
  22. return Fq(int(sha256(concat).hexdigest(), 16))
  23. # Domain separator for H_agg
  24. def H_AGG(*args):
  25. return hash_domain("musig2_H_agg", *args)
  26. # Domain separator for H_non
  27. def H_NON(*args):
  28. return hash_domain("musig2_H_non", *args)
  29. # Domain separator for H_sig
  30. def H_SIG(*args):
  31. return hash_domain("musig2_H_sig", *args)
  32. # =================
  33. # 1. Key generation
  34. # =================
  35. x = [Fq.random_element() for _ in range(n)]
  36. X = [x_i * g for x_i in x]
  37. # ==================
  38. # 2. Key aggregation
  39. # ==================
  40. L = b"".join(str(X_i).encode() for X_i in X)
  41. X_tilde = Fq(0) * g
  42. for i in range(n):
  43. a_i = H_AGG(L, X[i])
  44. X_tilde += X[i] * a_i
  45. # ======================
  46. # 3. First signing round
  47. # ======================
  48. R_i = [] # Each participant's public nonces
  49. r_i = [] # Each participant's secret nonces
  50. for _ in range(n):
  51. r_j = [Fq.random_element() for _ in range(v)]
  52. R_j = [r * g for r in r_j]
  53. r_i.append(r_j)
  54. R_i.append(R_j)
  55. # Sum up the nonces for all participants for each j
  56. R = [sum(R_ij[j] for R_ij in R_i) for j in range(v)]
  57. assert len(R) == v
  58. # =======================
  59. # 4. Second signing round
  60. # =======================
  61. message = "Hello MuSig2"
  62. s_i = []
  63. b = H_NON(X_tilde, *R, message)
  64. R_total = sum(R[j] * b * (j+1) for j in range(v))
  65. c = H_SIG(X_tilde, R_total, message) # Compute the challenge based on R_total
  66. for i in range(n):
  67. a_i = H_AGG(L, X[i])
  68. s_partial = c * a_i * x[i] + sum(r_i[i][j] * (b * (j+1)) for j in range(v))
  69. s_i.append(s_partial)
  70. s = sum(s_i)
  71. # ===============
  72. # 5. Verification
  73. # ===============
  74. c = H_SIG(X_tilde, R_total, message)
  75. assert g * s == R_total + X_tilde * c