dkg.sage 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. # Distributed Key Generation scheme
  2. t = 4 # Threshold
  3. n = 10 # Participants
  4. assert t <= n
  5. # Pallas
  6. p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
  7. q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
  8. Fp = GF(p)
  9. Fq = GF(q)
  10. Ep = EllipticCurve(Fp, (0, 5))
  11. Ep.set_order(q)
  12. # NullifierK Generator: G
  13. G = Ep([0x25e7aa169ca8198d2e375571faf4c9cf5e7eb192ccb5db9bd36f6aa7e447ca75,
  14. 0x155c1f851b1a3384880473442008ff755fe0a49ec1c1b4332db8dce21ae001cc])
  15. # =================================
  16. # Step 1: Secret Share Distribution
  17. # =================================
  18. polynomials = []
  19. shares = {}
  20. public_values = []
  21. broadcast_values = {}
  22. # The participants create their random polynomials and broadcast shares.
  23. for i in range(n):
  24. # Pick a random secret
  25. coeffs = [Fq.random_element()]
  26. # Generate random polynomial of degree t
  27. for _ in range(t):
  28. coeffs.append(Fq.random_element())
  29. polynomials.append(coeffs)
  30. # Compute and send secret shares
  31. shares[i+1] = [sum([coeffs[j] * (k**j) for j in range(t+1)]) for k in range(1, n+1)]
  32. # Compute public value
  33. public_values.append(coeffs[0] * G)
  34. # Broadcast evaluations of the polynomial at public points
  35. broadcast_values[i+1] = [sum([coeffs[j] * (k**j) for j in range(t+1)]) * G for k in range(1, n+1)]
  36. # ====================
  37. # Step 2: Verification
  38. # ====================
  39. # In real-world, it is important to ensure that malicious participants
  40. # cannot raise false complaints to disqualify honest participants.
  41. # Having a robust mechanism to protect against Sybil attacks or malicious
  42. # complaint flodding is essential.
  43. complaints = {}
  44. # Initial check against broadcasted values
  45. for i in range(1, n+1):
  46. for j in range(1, n+1):
  47. if shares[i][j-1] * G != broadcast_values[i][j-1]:
  48. if j not in complaints:
  49. complaints[j] = []
  50. complaints[j].append(i)
  51. # Handle complaints
  52. for complainant, offenders in complaints.items():
  53. for offender in list(offenders): # Using list() to avoid runtime modification issues
  54. # The offender proves they sent a correct share to the complainant
  55. revealed_share = sum([polynomials[offender-1][k] * complainant**k for k in range(t+1)])
  56. if revealed_share * G == broadcast_values[offender][complainant-1]:
  57. complaints[complainant].remove(offender)
  58. # Disqualification step
  59. disqualified = {i for i, comp in complaints.items() if len(comp) > 0}
  60. # ===================================
  61. # Step 3: Secret Share Reconstruction
  62. # ===================================
  63. # Sum the secrets of qualified participants to get the group's secret share
  64. # In a real-world application, this isn't safe and measures should be in
  65. # place to prevent this scenario.
  66. qualified_shares = [polynomials[i][0] for i in range(n) if i+1 not in disqualified]
  67. if len(qualified_shares) < t+1:
  68. raise ValueError("Too many disqualifications. DKG failed.")
  69. group_secret = sum(qualified_shares)
  70. group_public_0 = group_secret * G
  71. # However we can also do this without ever giving a single party enough
  72. # shares to reconstruct the secret:
  73. participant_pubkeys = [polynomials[i][0] * G for i in range(n) if i+1 not in disqualified]
  74. if len(participant_pubkeys) < t+1:
  75. raise ValueError("Too many disqualifications. DKG failed.")
  76. group_public_1 = sum(participant_pubkeys)
  77. assert group_public_0 == group_public_1