cryptography

Rogue Key Attack on Gennaro et al. DKG for Polynomials of Excessive Degree

A rogue key attack on Gennaro et al. DKG for polynomials of excessive degree, which lets an attacker fully control the private key

By Meridian Client4
Rogue Key Attack on Gennaro et al. DKG for Polynomials of Excessive Degree
Photo by Meridian Client3 Dziedzic

Rogue Key Attack on Genanro DKG for Polynomials of Excessive Degree

Rogue Key Attack on Gennaro et al. DKG for Polynomials of Excessive Degree

TL;DR

Distributed Key Generation (DKG) is a protocol employed to create a shared secret over a network. It is often employed in a case where no one individual should know the secret but with enough users gathered jointly the secret can be recovered (or alternatively sign a message) without recovering the underlying secret.

There is a threshold number of malicious users who may work jointly to attempt to recover the secret. A threshold of tt, means that tt malicious users cannot recover any concrete details about the secret. Even so, t+1t + 1 malicious users can recover the full secret.

This blog describes a rogue key style attack on the Joint-Feldman and DKG protocols found in this paper which allows one user to gain full knowledge of the shared secret. Given there are nn users in the DKG and a threshold tt we require n−t+1n - t + 1 malicious users. It helps to note that the protocol already calls for t<n2t < \frac{n}{2} for this exact reason. Hence this attack is only viable where configurations are employed with polynomials of degree greater than n2\frac{n}{2}.

The attack is based on the fact that a user has not in fact committed to their initial polynomial(s) of degree tt until at least t+1t + 1 users have verified their commitment.

This attack was first seen in Drand where the threshold is called for to be greater than 50% to prevent forking the chain. To overcome the attack a lower threshold closer to n2\frac{n}{2} was chosen. Another protocol, Dfinity, uses a polynomial which may be larger than n2\frac{n}{2}, though already has the constraint that the threshold is in the range t∈[f+1,n−f]t \in [f + 1, n - f] as described in Section 7 of the Dfinity paper. Thus, to pull off this attack would need f+1f + 1 nodes which is above the protocol's failure threshold, ff.

Joint-Feldman

The Joint-Feldman protocol described in the paper shares a secret over a discrete log based problem (e.g. DLP and ECDLP).

First we need to define some variables:

  • Finite Field - Fq\mathbb{F}_q
  • Generator of the Group in Fq\mathbb{F}_q - gg
  • Size of the Group - pp
  • Threshold (also the degree of the polynomials)- tt
  • The total number of nodes - nn
  • Nodes are indexed - [1,n][1, n]

Note in this discussion we will use multiplicative notation to align with the discrete log over a finite field.

Steps

The protocol consists of four stages.

1. Generating the polynomials and sharing its discrete log

Each node, ii, will generate a polynomial of degree tt by selecting t+1t + 1 random values ai,0,ai,1,...,ai,ta_{i,0}, a_{i,1}, ..., a_{i,t} with each of these values in the field Fq\mathbb{F}_q. We get the polynomial fi(z)f_i(z),

fi(z)=ai,0+ai,1z+...+ai,tztf_i(z) = a_{i,0} + a_{i,1}z + ... + a_{i,t}z^t

We then create a commitment to this polynomial that we may share with other users. So to not give the values away we exponentiate these ai,ja_{i,j} values. That is we do Ai,j=gai,jA_{i,j} = g^{a_{i,j}} such that we cannot work out ai,ja_{i,j} from Ai,jA_{i,j} without solving the discrete logarithm (assumed to be computationally hard).

We are now able to send our exponentiated polynomial with the other users. That is broadcast Ai,jA_{i,j} to all other nodes.

In addition we will secretly send a share of our polynomial to each node jj. The share we send is si,j=fi(j)s_{i,j} = f_i(j) where we are node ii.

2. Verification of shares

Each node jj can now verify that the shares sent to them by other nodes are accurate. We check the following equality,

gsi,j= Πk=0t(Ai,k)jkmod pg^{s_{i,j}} =\ \Pi^{t}_{k=0}(A_{i,k})^{j^k} mod\ p

Here the right hand side is computed from the public shares and left hand side is computed from the private share. Noting both sides of the equation should be equivalent to gfi(j)g^{f_i(j)}.

The premise here is that the node sending the share si,js_{i,j} can only know this value if they in fact know the underlying polynomial to work out this. Which is true so long as there are t+1t + 1 good nodes who verify the shares. But more on that later.

If a node receives and shares which fail to verify the above equation they broadcast a complaint.

3. Setting the qualified groups

If any node receives more than t+1t + 1 complaints they are immediately ejected. Nodes who have a complaint and are not ejected are asked to broadcast the correct share si,js_{i,j} linked with the complaint. If that share fails verification the node is ejected.

The non-ejected nodes form the group QUAL⊑[1,n]QUAL \sqsubseteq [1, n].

4. Calculating the final values

The final public value is computed as y=Πi∈QUALAi,0y = \Pi_{i \in QUAL} A_{i,0}. Each node can compute their portion of the shared secret as xi=Σi∈QUALsi,jx_i = \NFT Bounty_{i \in QUAL} s_{i,j}.

Attacking Joint-Feldman

Crafting the rogue key

Our goal here is to manipulate the final public value to one which we know the discrete logarithm of. The attack on the Joint-Feldman protocol has the prerequisite that we are able to see all the other nodes' public commitments before calculating our own (a synchronicity requirement). That is for y=Πi∈QUALAi,0y = \Pi_{i \in QUAL} A_{i,0} we must know ww in y=gwy = g^w.

Now letting w=Σi∈QUAL ai,0w = \NFT Bounty_{i \in QUAL} \ a_{i,0} and letting our node index be, ee, if we can craft our Ae,0A_{e,0} value to be

Ae,0=gl−∑i≠eai,0A_{e,0} = g^{l -\sum_{i \ne e} a_{i,0}}

then the final secret will be

w=Σi≠e(ai,0)+ae,0=Σi≠e(ai,0)+l−∑i≠eai,0=lw = \NFT Bounty_{i \ne e}(a_{i,0}) + a_{e,0} = \NFT Bounty_{i \ne e}(a_{i,0}) + l - \sum_{i \ne e} a_{i,0} = l

So here the final secret will be ll which we know because we set it and thus we would have full control over the final secret!

Now we do not know the values of ai,ja_{i,j} other than our own (otherwise we'd already know the final secret!). Hence we cannot work out Σi≠e(ai,0)\NFT Bounty_{i \ne e}(a_{i,0}). So how do we craft our Ae,0A_{e,0} to be that above?

Well each user's commitment, Ai,0A_{i,0}, are publicly shared in step 1. so multiplying them in tandem (excluding ours) gives Πi≠e(Ai,0)\Pi_{i \ne e}(A_{i,0}) which is the same as

Πi≠e(Ai,0)=gΣi≠e(ai,0)\Pi_{i \ne e}(A_{i,0}) = g^{\NFT Bounty_{i \ne e}(a_{i,0})}

We are now getting pretty close to the desired value, we just need to take the inverse of the above (a log(p)log(p) reckoning) to get g−Σi≠e(ai,0)g^{-\NFT Bounty_{i \ne e}(a_{i,0})} select our value ll that only we know and do glg^l. Then multiplying these two in tandem we get

Ae,0=gl−∑i≠eai,0A_{e,0} = g^{l -\sum_{i \ne e} a_{i,0}}

So reiterating why we want this. Looking again at step 4. we see that the final public key is y=Πi∈QUALAi,0=gΣi∈QUALai,0=gly = \Pi_{i \in QUAL} A_{i,0} = g^{\NFT Bounty_{i \in QUAL} a_{i,0}} = g^l and we know ll!

Passing the verification step (crafting the remainder of the polynomial)

Now comes the challenging part, passing the commitment verification step. If we are unable to have our commitments verified by nodes we will be ejected from the protocol in step 3.

We need to pass the equality

gse,j= Πk=0t(Ae,k)jkmod pg^{s_{e,j}} =\ \Pi^{t}_{k=0}(A_{e,k})^{j^k} mod\ p

or

gfe(j)= Πk=0t(Ae,k)jkmod pg^{f_e(j)} =\ \Pi^{t}_{k=0}(A_{e,k})^{j^k} mod\ p

To do this we need to be able to work out fe(j)f_e(j) for each node jj. We do this by cleverly crafting our polynomial fef_e via two helper polynomials, uu and vv. The reason will become clear as we go on. Our polynomial is set as,

fe(z)=(z−Σi≠eai,0)u(z)+v(z)f_e(z) = (z - \NFT Bounty_{i \ne e} a_{i,0}) u(z) + v(z)

We need this polynomial to have ae,0=l−∑i≠eai,0a_{e,0} = l -\sum_{i \ne e} a_{i,0} that is

fe(0)=l−∑i≠eai,0f_e(0) = l -\sum_{i \ne e} a_{i,0}

which will occur if u(0)=1u(0) = 1 and v(0)=lv(0) = l i.e.

fe(0)=(0−Σi≠eai,0)u(z)+v(z)=(0−Σi≠eai,0)∗1+l=l−∑i≠eai,0f_e(0) = (0 - \NFT Bounty_{i \ne e} a_{i,0}) u(z) + v(z) = (0 - \NFT Bounty_{i \ne e} a_{i,0}) * 1 + l = l -\sum_{i \ne e} a_{i,0}

From the u(0)u(0) and v(0)v(0) computations we know the constant term of u(0)=1u(0) = 1 and v(0)=lv(0) = l. Now to hide the fact that we do not actually know Σi≠eai,0\NFT Bounty_{i \ne e} a_{i,0} we attempt to set,

u(z)=0 for j∈QUALu(z) = 0\ for\ j \in QUAL

such that,

fe(j)=(j−Σi≠eai,0)u(j)+v(j)=(j−Σi≠eai,0)∗0+v(j)=v(j)f_e(j) = (j - \NFT Bounty_{i \ne e} a_{i,0}) u(j) + v(j) = (j - \NFT Bounty_{i \ne e} a_{i,0}) * 0 + v(j) = v(j)

This is material as we will know the values v(j)v(j) so we are able to send valid shares se,j=fe(j)=v(j)s_{e,j} = f_e(j) = v(j) to the other nodes.

Defining u(z)u(z) as,

u(z)=1+b1∗z+b2∗z2+...+bt−1∗zt−1u(z) = 1 + b_1 * z + b_2 * z^2 + ... + b_{t-1} * z^{t-1}

Note since fef_e is a polynomial of degree tt and we have (z−Σi≠eai,0)u(z)(z - \NFT Bounty_{i \ne e} a_{i,0}) u(z) therefore u(z)u(z) must be of most degree t−1t - 1.

By evaluating the polynomials such that u(0)=1u(0) = 1 and u(j)=0 for j∈QUALu(j) = 0\ for\ j \in QUAL we will have a system of linear equations,

j=0 gives 1=1+b1∗0+...+bt−1∗0=1j = 0 \ gives \ 1 = 1 + b_1 * 0 + ... + b_{t-1} * 0 = 1 (no variables to solve for!)

j=1 gives 0=1+b1+b2+...+bt−1j = 1 \ gives \ 0 = 1 + b_1 + b_2 + ... + b_{t-1}

j=2 gives 0=1+2b1+4b2+...+2t−1bt−1j = 2 \ gives \ 0 = 1 + 2 b_1 + 4b_2 + ... + 2^{t-1}b_{t-1}

...

j=n gives 0=1+nb1+n2b2+...+nt−1bt−1j = n \ gives \ 0 = 1 + nb_1 + n^2b_2 + ... + n^{t-1}b_{t-1}

There are a large number of ways to solve systems of linear equations often done through with an augmented matrix, see solving systems of linear equations. To be guaranteed a solution we must have at least as many variables as linearly independent equations.

The number of equations we right now have is nn and need to solve for the t−1t - 1, bib_i variables. Since n>t−1n > t - 1 we will likely not get a solution to our system of equations. So we need to cut the number of equations to ≤t−1\leq t - 1.

Each equation is only verified by one other node hence we may also cut the number of equations by having other malicious nodes claim they verified our commitments (when the commitments do not actually verify) and of course claiming our own commitment is valid. Let mm be the number of malicious nodes helping us, including ourself.

Hence, the number of malicious nodes we need is,

n−m≤t−1n - m \leq t - 1

m≥n−t+1m \geq n - t + 1

So with m≥n−t+1m \geq n - t + 1 malicious nodes we are able to generate a polynomial u(z)u(z), which will allow us to pass the commitment verification step.

Note here we use the degree of the polynomial as tt which means it would be a t+1t + 1 of nn threshold scheme as you would need t+1t + 1 valid nodes to recover the secret. So to account for this having m≥n−t+2m \geq n - t + 2 malicious nodes in a tt of nn threshold scheme will allow the secret to be rogue key attacked.

The polynomial v(z)v(z) should be randomly generated (by us so we know the co-efficients). Its purpose is so the values of fe(j)≠0f_e(j) \ne 0 for each valid node as,

fe(j)=(j−Σi≠eai,0)u(j)+v(j)=v(j)f_e(j) = (j - \NFT Bounty_{i \ne e} a_{i,0}) u(j) + v(j) = v(j)

Hence, if we did not use a the polynomial v(z)v(z) then fe(j)=0f_e(j) = 0, which would make our attack obvious.

From here we would need to compute the public commitments to our polynomial. We already have Ae,0A_{e,0} worked out above the rest are trivially done as,

Ae,i=gbi−1∗(Πe≠iAi,0)bi∗gv(i)A_{e,i} = g^{b_{i-1}} * (\Pi_{e \ne i}A_{i,0})^{b_i} * g^{v(i)}

Gennaro et al. DKG

Differences from Joint-Feldman

A simplified summary of the differences is that we now use two polynomials during the commitment stage rather than one.

Step 1. a) Generate polynomials

Each node will now have two polynomials,

fi(z)=ai,0+ai,1z+...+ai,tztf_i(z) = a_{i,0} + a_{i,1}z + ... + a_{i,t}z^t

fi′(z)=bi,0+bi,1z+...+bi,tztf'_i(z) = b_{i,0} + b_{i,1}z + ... + b_{i,t}z^t

There are now two generators (who's discrete log should not be known) gg and hh.

The initial public commitments are Ci,k=gai,khbi,kC_{i,k} = g^{a_{i,k}} h^{b_{i,k}}.

The nodes will also share two secret values si,j=fi(j)s_{i,j} = f_i(j) and si,j′=fi′(j)s'_{i,j} = f'_i(j)

Step 1. b) Verify commitments

The commitments are verified as,

gsi,jhsi,j′= Πk=0t(Ci,k)jkmod pg^{s_{i,j}} h^{s'_{i,j}} =\ \Pi^{t}_{k=0}(C_{i,k})^{j^k} mod\ p

Step 1. c) Complain if commitments are invalid

This is the same as Joint-Feldman complaints are made if verification in step 1. b) fails.

Step 1. d) Eject malicious nodes

This is the same as Joint-Feldman, nodes who have a tt complaints against them or fail to prove a complaint are ejected.

Step 2. Make the qualified set

All nodes who are not ejected form the set QUALQUAL.

Step 3. Private shares

Each node's private shares are now x=Σi∈QUALsi,jx = \NFT Bounty_{i \in QUAL}s_{i,j} and x′=Σi∈QUALsi,j′x' = \NFT Bounty_{i \in QUAL}s'_{i,j}.

Step 4. Extracting the public key

First each node will expose their public polynomial of fi(z)f_i(z) by broadcasting Ai,k=gai,kA_{i,k} = g^{a_{i,k}}.

The remaining nodes will verify this as,

gsi,j= Πk=0t(Ai,k)jkmod pg^{s_{i,j}} =\ \Pi^{t}_{k=0}(A_{i,k})^{j^k} mod\ p

For any node, rr that fail this test their polynomial can be reconstructed by the other nodes though via each individual sr,is_{r, i} values shared in step 1.

We can now work out the final public key as y=Πi∈QUALAi,0y = \Pi_{i \in QUAL} A_{i,0}.

Attacking Gennaro et al. DKG

Modifications during step 1.

DKG is that we must create two polynomials for each The modifications to attack Gennaro et al. fef_e and fe′f'_e. Setting ourself as ee again we have,

fe(z)=(z−Σi≠eai,0)u(z)=v(z)f_e(z) = (z - \NFT Bounty_{i \ne e} a_{i,0}) u(z) = v(z)

fe′(z)=(z−Σi≠eai,0)u′(z)=v′(z)f'_e(z) = (z - \NFT Bounty_{i \ne e} a_{i,0}) u'(z) = v'(z)

We need to have u(0)=1u(0) = 1 and u(j)=0 for j∈QUALu(j) = 0\ for\ j \in QUAL and u′(0)=1u'(0) = 1 and u′(j)=0 for j∈QUALu'(j) = 0\ for\ j \in QUAL (Note thus u(z)=u′(z)u(z) = u'(z)). The caculations of this polynomial stays the same as for Joint-Feldman.

Then we must set our inital commitment Ce,0C_{e,0} as,

Ce,0=gv(0)−Σi≠eai,0hv′(0)−Σi≠ebi,0C_{e,0} = g^{v(0) - \NFT Bounty_{i \ne e} a_{i,0}} h^{v'(0) - \NFT Bounty_{i \ne e} b_{i,0}}

Which can be worked out by taking the inverse of Πi≠eCi,0\Pi_{i \ne e}C_{i,0} giving g−Σi≠eai,0h−Σi≠ebi,0g^{-\NFT Bounty_{i \ne e} a_{i,0}} h^{-\NFT Bounty_{i \ne e} b_{i,0}} then multiplying it by gv(0)g^{v(0)} and hv′(0)h^{v'(0)}.

We are then able to share our secret shares as se,j=fe(j)s_{e,j} = f_e(j) and se,j′=fe(j)s'_{e,j} = f_e(j).

Modification to step 4.

During step 4. we must again work out our Ae,0A_{e,0} after receiving the other Ai,0A_{i,0} values as,

Ae,0=gv(0)(Πi≠eAi,0)−1A_{e,0} = g^{v(0)} (\Pi_{i \ne e} A_{i,0})^{-1}

Thus, again we will have the final public key as,

y=ΠAi,0=Ae,0Πi≠eAi,0=gv(0)(Πi≠eAi,0)−1∗Πi≠eAi,0=gv(0)y = \Pi A_{i,0} = A_{e,0} \Pi_{i \ne e} A_{i,0} = g^{v(0)} (\Pi_{i \ne e} A_{i,0})^{-1} * \Pi_{i \ne e} A_{i,0} = g^{v(0)}

to which we know the value v(0)v(0).

Requirements

The requirements again fall in the computation of the polynomial u(z)u(z) which is unchanged and so will call for the same number of malicious nodes to be m≥n−t+1m \geq n - t + 1 where tt is the degree of the polynomial (t+1t+1 of nn threshold scheme).

Mitigations

A plain mitigation to this issue is by adding an further step to the beginning which forces users to commit to their polynomial before obtaining any information about the other users polynomial.

For example a commitment step where each node uses a secure hash function to over their polynomial. Hash(A_{i,0} || A_{i,1} || .. || A_{i,t}) where || stands for string concatenation. Obviously ensuring the encoding of a polynomial is unique.

Working on something in this space?

NFT Bounty audits Ethereum protocols, smart contracts, and consensus implementations.

Book a scoping talk