mirror of
https://github.com/seigler/dips
synced 2025-07-26 01:06:13 +00:00
Security Analysis (#55)
* This adds two sections to dip 0008. The first section considers the security of Chain Locks and provides the calculations needed to evaluate the security. The second added sections provides mitigations of situtaions when attackers do not own the collatoral of the masternodes. * Update dip-0008.md * Fix hyperlinks * Update dip-0008.md * Update dip-0008.md correction of word. Co-Authored-By: UdjinM6 <UdjinM6@users.noreply.github.com> * Update dip-0008.md Need to make sure it's always big choose small. Co-Authored-By: UdjinM6 <UdjinM6@users.noreply.github.com> * Fixes inconsistantcy in gifs * Update dip-0008/quorum_attack.py fix tpyo Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008.md Changing titles, todo change table. Co-Authored-By: UdjinM6 <UdjinM6@users.noreply.github.com> * Clarify table This handles the edge case of witholding a ChainLock correctly, it takes 161 nodes to withold a ChainLock. Also makes table clearer to read. * Clarify malicious chainlock Based on suggestions from AndyFreer a second paragraph is added to explain what can go wrong. * Update dip-0008.md Add line break. Co-Authored-By: UdjinM6 <UdjinM6@users.noreply.github.com> * Apply suggestions of thephez The calculations were updated to refelct the fact that 161 masternodes are needed to withhold a chainlock in a previous commit. This commit updates the text and displayed formulas to reflect this fact. We also alert the reader that we assume that all uncompromised nodes are behaiving as expected. We include the effect of relaxing this assumption, however the calculations are left to the reader. The python function provided makes it easy. * Fix two typos * Update dip-0008.md one missed 160->161 Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008.md Co-Authored-By: thephez <thephez@users.noreply.github.com> * Correct C size in sumation formula * Update dip-0008/quorum_attack.py correct spelling Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py correct spelling Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py correct spelling Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py correct spelling Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py correct spelling Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py Co-Authored-By: thephez <thephez@users.noreply.github.com> * Update dip-0008/quorum_attack.py Co-Authored-By: thephez <thephez@users.noreply.github.com> Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com> Co-authored-by: thephez <thephez@users.noreply.github.com>
This commit is contained in:
parent
41fc2c9413
commit
55aa78cd1e
4 changed files with 230 additions and 0 deletions
123
dip-0008.md
123
dip-0008.md
|
@ -24,6 +24,8 @@
|
|||
1. [Implications of a signed block](#implications-of-a-signed-block)
|
||||
1. [Network partitions](#network-partitions)
|
||||
1. [Initial Block Download](#initial-block-download)
|
||||
1. [Calculations](#calculations)
|
||||
1. [Security Considerations](#security-considerations)
|
||||
1. [Copyright](#copyright)
|
||||
|
||||
## Abstract
|
||||
|
@ -275,6 +277,127 @@ resolve any ambiguities which might occur in the last few blocks.
|
|||
If the need arises to include block signatures in initial block download, we
|
||||
will update this DIP and implementations accordingly.
|
||||
|
||||
## Calculations
|
||||
|
||||
We consider the scenario where an attacker has assumed control of a number of
|
||||
masternodes. We will then calculate two probabilities that the attacker will
|
||||
be able to disrupt the network. Specifically, we will calculate the probability
|
||||
that an attacker will be able to prevent all ChainLocks for a twelve hour period,
|
||||
and we will calculate the probability that an attacker can successfully create
|
||||
a malicious or erroneous ChainLock.
|
||||
|
||||
|
||||
The consequences of an attacker withholding ChainLocks would be that the network
|
||||
would fall back on proof of work. The consequence of a malicious or erroneous
|
||||
ChainLock might depend on the attackers motivations. An attacker with
|
||||
control of a ChainLock quorum would be able to:
|
||||
1. Produce conflicting ChainLocks.
|
||||
2. Withhold ChainLocks and then issue a ChainLock for a conflicting previous block
|
||||
not included in the current chain of most work.
|
||||
3. Secure undo advantage.
|
||||
|
||||
We assume that all masternodes not under malicious control are
|
||||
behaving as expected. In practice this is generally observed. In theory,
|
||||
it is possible for some nodes to be unreachable, or the attacker could
|
||||
combine a DoS attack in conjuction with compromising nodes. If this is the case, it
|
||||
would become more likely an attacker could withhold ChainLocks, and not
|
||||
be any more likely the attacker could produce a malicious ChainLock.
|
||||
Our calculations can easily be modified to model the network operating
|
||||
under other assumptions.
|
||||
|
||||
In this section we will show that the random selection of a LLMQ will
|
||||
not allow an attacker to control a ChainLock quorum without control of a
|
||||
supermajority of masternodes.
|
||||
|
||||
Quorum selection is designed to provide for an effectively simple random
|
||||
choice of masternodes in each quorum. Thus we may assume that each quorum is
|
||||
a simple random selection. If there are a total of 'N' masternodes the
|
||||
number of possible quorums is the number of ways that 400 objects can be
|
||||
chosen out of `N`. In some literature this number is
|
||||
written <sub>`N`</sub>`C`<sub>`400`</sub> and is enunciated as
|
||||
'N choose 400'. Numbers that arise in this fashion are all
|
||||
binomial coefficients and have a delightful relation to Pascal's Triangle.
|
||||
|
||||
For any process with an observable outcome the set of all outcomes is
|
||||
called a sample space. If all outcomes are equally likely, as in the
|
||||
selection of a quorum, the probability of any event is the number of
|
||||
outcomes that satisfy that event over the total number outcomes.
|
||||
|
||||
An attacker can temporarily prevent ChainLocks if they control
|
||||
`161` out of 400 masternodes in the quorum. If the attacker controls
|
||||
`m` out of the `N` total masternodes we can compute the
|
||||
number of outcomes that satisfy the event "exactly `161`
|
||||
nodes of the quorum are controlled by the masternode."
|
||||
Specifically there are
|
||||
<sub>`m`</sub>`C`<sub>`161`</sub> * <sub>`N - m`</sub>`C`<sub>`239`</sub>
|
||||
outcomes where the attacker controls exactly `161` masternodes.
|
||||
Thus, the probability that the attacker controls exactly `161` nodes is
|
||||
|
||||
<p align="center"><img src="dip-0008/ThreshCase.gif" alt="Threshold Probability"></p>
|
||||
|
||||
The attacker could also be successful if they control <b>more</b> than `160`
|
||||
of the nodes in the quorum selection. Thus the exact probability that an
|
||||
attacker can successfully prevent ChainLocks for that quorum round is
|
||||
computed by counting the number of outcomes where the attacker controls
|
||||
160 or more nodes in the quorum divided by the total number of outcomes.
|
||||
This is computed by the sum
|
||||
|
||||
<p align="center"><img src="dip-0008/fin_sum.gif" alt="Final sum" ></p>
|
||||
|
||||
The numbers involved in this sum can be quite large. We may
|
||||
use a <A HREF="dip-0008/quorum_attack.py">Python Script</A> to effectively
|
||||
evaluate this sum. Note
|
||||
that we made the necessary modifications to this formula
|
||||
to calculate the probability that an attacker controls 60% or more
|
||||
of the quorum.
|
||||
|
||||
| N | m | Success probability of withholding a ChainLock | Success probability of creating a malicious ChainLock |
|
||||
|---|---|--------------|--------------------------------|
|
||||
|5000|500| 3.32e-65 | 7.11e-157|
|
||||
|5000|1000| 1.69e-22 | 2.89e-76 |
|
||||
|5000|1500| 3.36e-6 | 1.29e-38 |
|
||||
|2000|200 | 2.12e-87 | 0 |
|
||||
|2000|400 | 1.80e-26 | 9.49e-94 |
|
||||
|2000|600 | 6.20e-7 | 3.94e-45 |
|
||||
|
||||
In all scenarios above, an attacker should not expect to withhold a
|
||||
ChainLock in the next century. The attacker would have a less than
|
||||
one in 100 trillion chance of producing at least
|
||||
one malicious ChainLock in the next sextillion (10^21) years.
|
||||
|
||||
From these calculations we conclude that an attacker with control of fewer
|
||||
than 30% of all masternodes will not have any influence on ChainLocks. For
|
||||
completeness we note that
|
||||
if an attacker controlled 50% of masternodes an attacker would
|
||||
generally be able to prevent ChainLocks, but there is less than 30 out of
|
||||
one billion chance that the attacker will be able to produce at least one
|
||||
malicious ChainLock in the next million years.
|
||||
|
||||
|
||||
|
||||
|
||||
## Security Considerations
|
||||
|
||||
It is of note that it is not required for an attacker to actually own the
|
||||
collateral of masternodes that the attacker controls. A company offering
|
||||
the service of hosting masternodes for clients could be persuaded to run
|
||||
malicious software. Also if node operators do not check the digital signature
|
||||
there could be a malicious download or software that spoofs being an
|
||||
official download. We recommend mitigations of both these side channel attacks.
|
||||
|
||||
It is recommended that Dash users require some transparency
|
||||
from masternode hosting services. It should be expected that a
|
||||
masternode hosting service disclose all masternodes that it hosts as a
|
||||
service. It is then recommended that those who utilize a masternode hosting
|
||||
service check that their node is listed in the disclosure. This
|
||||
information could help identify if nodes hosted by services are
|
||||
behaving as expected.
|
||||
|
||||
It is always recommended that users check the signature on any software they
|
||||
download. As long as the correct public key is acquired then it is
|
||||
cryptographically not possible for the official download to be spoofed.
|
||||
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright (c) 2018 Dash Core Group, Inc. [Licensed under the MIT
|
||||
|
|
BIN
dip-0008/ThreshCase.gif
Normal file
BIN
dip-0008/ThreshCase.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 783 B |
BIN
dip-0008/fin_sum.gif
Normal file
BIN
dip-0008/fin_sum.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
107
dip-0008/quorum_attack.py
Executable file
107
dip-0008/quorum_attack.py
Executable file
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/python
|
||||
#This script was written by Darren Tapp and optimized by thephez
|
||||
|
||||
from decimal import Decimal
|
||||
from math import log
|
||||
from math import factorial as fac
|
||||
from math import log1p
|
||||
from math import exp
|
||||
|
||||
|
||||
|
||||
def binom(x, y):
|
||||
try:
|
||||
binom = fac(x) // fac(y) // fac(x - y)
|
||||
except ValueError:
|
||||
binom = 0
|
||||
return binom
|
||||
|
||||
|
||||
###This function takes inputs and outputs the probability
|
||||
#of success in one trial
|
||||
#pcalc is short for probability calculation
|
||||
def pcalc(masternodes,quorumsize,attacksuccess,Byznodes):
|
||||
SampleSpace = binom(masternodes,quorumsize)
|
||||
pctemp=0
|
||||
for x in range(attacksuccess, quorumsize+1):
|
||||
pctemp = pctemp + binom(Byznodes,x)*binom(masternodes-Byznodes,quorumsize-x)
|
||||
#at this juncture the answer is pctemp/SampleSpace
|
||||
#but that will produce an overflow error. We use logarithms to
|
||||
#calculate this value
|
||||
return 10 ** (log(pctemp,10)- log(SampleSpace,10))
|
||||
|
||||
#Takes the probability of success in one trial and outputs the probability of success in 730 septillion trials
|
||||
#730 septillion trials requires a septillion years of masternode quorum formation.
|
||||
def ZettaYear(probability):
|
||||
trials = 2*365*10 ** 21
|
||||
return 1-exp(trials * log1p(-probability))
|
||||
|
||||
def MegaYear(probability):
|
||||
trials = 2*365*10 ** 6
|
||||
return 1-exp(trials * log1p(-probability))
|
||||
|
||||
|
||||
|
||||
##We evaluate the function pcalc(10,5,3,4)
|
||||
##print pcalc(10,5,3,4)
|
||||
##as a test vector
|
||||
##The answer would be [binom(3,4)*binom(2,6)+(binom(4,4)*binom(1,6)]/binom(10,5)
|
||||
##[4*15+1*6]/252 = 66/252
|
||||
##print float(66)/252
|
||||
|
||||
##quorum size for ChainLocks
|
||||
qs = 400
|
||||
##Number of masternodes
|
||||
mn = 5000
|
||||
|
||||
##Number of Byzantine nodes assuming 5000 nodes
|
||||
Bft = [500,1000,1500]
|
||||
|
||||
##Threshold out of quorum of 400
|
||||
thresh = 161
|
||||
|
||||
for j in range(0,3):
|
||||
print "For ", mn, " masternodes with ", Bft[j],"Byzantine the chance of withholding a ChainLock in one trial is ", pcalc(mn,qs,thresh,Bft[j])
|
||||
|
||||
##Now change the # threshold
|
||||
thresh = 240
|
||||
|
||||
for j in range(0,3):
|
||||
print "For ", mn, " masternodes with ", Bft[j],"Byzantine the chance of producing a malicious ChainLock is ", pcalc(mn,qs,thresh,Bft[j])
|
||||
|
||||
|
||||
#In the case of a smaller number of masternodes
|
||||
mn=2000
|
||||
|
||||
##Number of Byzantine nodes assuming 2000 nodes
|
||||
Bft = [240,400,600]
|
||||
|
||||
##Threshold out of quorum of 400
|
||||
thresh = 161
|
||||
|
||||
for j in range(0,3):
|
||||
print "For ", mn, " masternodes with ", Bft[j],"Byzantine the chance of withholding a ChainLock in one trial is ", pcalc(mn,qs,thresh,Bft[j])
|
||||
|
||||
##Now change the # threshold
|
||||
thresh = 240
|
||||
|
||||
for j in range(0,3):
|
||||
print "For ", mn, " masternodes with ", Bft[j],"Byzantine the chance of producing a malicious ChainLock is ", pcalc(mn,qs,thresh,Bft[j])
|
||||
|
||||
|
||||
print "Security interpretation:"
|
||||
|
||||
print "For 5000 masternodes with 1500 Byzantine nodes the chance of producing a malicious ChainLock is ", pcalc(5000,400,240,1500)
|
||||
print "Which means in a Zettayear the chances of a "
|
||||
print "malicious chainlock is ", ZettaYear(pcalc(5000,400,240,1500))
|
||||
|
||||
|
||||
print "In an extreme case the chance of quorum control with 40% of nodes:"
|
||||
print "5000 Masternodes ", pcalc(5000,400,240,2000)
|
||||
print "2000 Masternodes ", pcalc(2000,400,240,800)
|
||||
print "In an even more extreme case the chance of quorum control with 50% of nodes:"
|
||||
print "5000 Masternodes ", pcalc(5000,400,240,2500)
|
||||
print "2000 Masternodes ", pcalc(2000,400,240,1000)
|
||||
|
||||
print MegaYear(pcalc(5000,400,240,2000))
|
||||
print "For 2000 total nodes with 200 attacking , the chance of withholding a ChainLock is,", pcalc(2000,400,161,200)
|
Loading…
Add table
Add a link
Reference in a new issue