diff --git a/script/research/dkg/dkg.sage b/script/research/dkg/dkg.sage index 6670439ee..cbc13bedf 100644 --- a/script/research/dkg/dkg.sage +++ b/script/research/dkg/dkg.sage @@ -1,8 +1,8 @@ -# Distributed Key Generation -# A naive approach. +# Distributed Key Generation scheme -t = 4 # Threshold +t = 4 # Threshold n = 10 # Participants +assert t <= n # Pallas p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001 @@ -24,49 +24,70 @@ shares = {} public_values = [] broadcast_values = {} +# The participants create their random polynomials and broadcast shares. for i in range(n): + # Pick a random secret + coeffs = [Fq.random_element()] # Generate random polynomial of degree t - coeffs = [Fq.random_element() for _ in range(t+1)] - # Set a_0 as a random secret - coeffs[0] = Fq.random_element() + for _ in range(t): + coeffs.append(Fq.random_element()) polynomials.append(coeffs) # Compute and send secret shares - shares[i+1] = [sum([coeffs[j] * (i+1)**j for j in range(t+1)]) for i in range(n)] + shares[i+1] = [sum([coeffs[j] * (k**j) for j in range(t+1)]) for k in range(1, n+1)] # Compute public value public_values.append(coeffs[0] * G) - # Broadcast f_i(1)*G, f_i(2)*G, ..., f_i(n)*G - broadcast_values[i+1] = [sum([coeffs[j] * k**j for j in range(t+1)]) * G for k in range(1, n+1)] + # Broadcast evaluations of the polynomial at public points + broadcast_values[i+1] = [sum([coeffs[j] * (k**j) for j in range(t+1)]) * G for k in range(1, n+1)] # ==================== # Step 2: Verification # ==================== + +# In real-world, it is important to ensure that malicious participants +# cannot raise false complaints to disqualify honest participants. +# Having a robust mechanism to protect against Sybil attacks or malicious +# complaint flodding is essential. complaints = {} -for j in range(1, n+1): # For each participant P_j - complaints[j] = [] - for i in range(1, n+1): # From each participant P_i + +# Initial check against broadcasted values +for i in range(1, n+1): + for j in range(1, n+1): if shares[i][j-1] * G != broadcast_values[i][j-1]: - complaints[j].append(i) # P_j complains against P_i + if j not in complaints: + complaints[j] = [] + complaints[j].append(i) + +# Handle complaints +for complainant, offenders in complaints.items(): + for offender in list(offenders): # Using list() to avoid runtime modification issues + # The offender proves they sent a correct share to the complainant + revealed_share = sum([polynomials[offender-1][k] * complainant**k for k in range(t+1)]) + if revealed_share * G == broadcast_values[offender][complainant-1]: + complaints[complainant].remove(offender) + +# Disqualification step +disqualified = {i for i, comp in complaints.items() if len(comp) > 0} # =================================== # Step 3: Secret Share Reconstruction # =================================== -disqualified = {i for i, comp in complaints.items() if len(comp) > t} -# Sum the shares of qualified participants to get the group's secret share -# This is assuming a single party holds enough shares. -qualified_shares = [shares[i][i-1] for i in range(1, n+1) if i not in disqualified] +# Sum the secrets of qualified participants to get the group's secret share +# In a real-world application, this isn't safe and measures should be in +# place to prevent this scenario. +qualified_shares = [polynomials[i][0] for i in range(n) if i+1 not in disqualified] if len(qualified_shares) < t+1: - raise Exception("Too many disqualifications. DKG failed.") + raise ValueError("Too many disqualifications. DKG failed.") group_secret = sum(qualified_shares) group_public_0 = group_secret * G # However we can also do this without ever giving a single party enough # shares to reconstruct the secret: -participant_pubkeys = [shares[i][i-1] * G for i in range(1, n+1) if i not in disqualified] +participant_pubkeys = [polynomials[i][0] * G for i in range(n) if i+1 not in disqualified] if len(participant_pubkeys) < t+1: - raise Exception("Too many disqualifications. DKG failed.") + raise ValueError("Too many disqualifications. DKG failed.") group_public_1 = sum(participant_pubkeys) assert group_public_0 == group_public_1