Merge pull request #639 from bpeel/wip/trusted-peer

Add a 'trustedpeer' option to keys.dat
This commit is contained in:
Jonathan Warren 2014-04-30 17:42:28 -04:00
commit 38d7db24ad
5 changed files with 59 additions and 20 deletions

View File

@ -22,26 +22,37 @@ class outgoingSynSender(threading.Thread):
self.streamNumber = streamNumber self.streamNumber = streamNumber
self.selfInitiatedConnections = selfInitiatedConnections self.selfInitiatedConnections = selfInitiatedConnections
def _getPeer(self):
# If the user has specified a trusted peer then we'll only
# ever connect to that. Otherwise we'll pick a random one from
# the known nodes
shared.knownNodesLock.acquire()
if shared.trustedPeer:
peer = shared.trustedPeer
shared.knownNodes[self.streamNumber][peer] = time.time()
else:
peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
shared.knownNodesLock.release()
return peer
def run(self): def run(self):
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
time.sleep(2) time.sleep(2)
while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections'): while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections'):
while len(self.selfInitiatedConnections[self.streamNumber]) >= 8: # maximum number of outgoing connections = 8 maximumConnections = 1 if shared.trustedPeer else 8 # maximum number of outgoing connections = 8
while len(self.selfInitiatedConnections[self.streamNumber]) >= maximumConnections:
time.sleep(10) time.sleep(10)
if shared.shutdown: if shared.shutdown:
break break
random.seed() random.seed()
shared.knownNodesLock.acquire() peer = self._getPeer()
peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
shared.knownNodesLock.release()
shared.alreadyAttemptedConnectionsListLock.acquire() shared.alreadyAttemptedConnectionsListLock.acquire()
while peer in shared.alreadyAttemptedConnectionsList or peer.host in shared.connectedHostsList: while peer in shared.alreadyAttemptedConnectionsList or peer.host in shared.connectedHostsList:
shared.alreadyAttemptedConnectionsListLock.release() shared.alreadyAttemptedConnectionsListLock.release()
# print 'choosing new sample' # print 'choosing new sample'
random.seed() random.seed()
shared.knownNodesLock.acquire() peer = self._getPeer()
peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
shared.knownNodesLock.release()
time.sleep(1) time.sleep(1)
# Clear out the shared.alreadyAttemptedConnectionsList every half # Clear out the shared.alreadyAttemptedConnectionsList every half
# hour so that this program will again attempt a connection # hour so that this program will again attempt a connection

View File

@ -319,6 +319,14 @@ class receiveDataThread(threading.Thread):
print 'Sending huge inv message with', numberOfObjects, 'objects to just this one peer' print 'Sending huge inv message with', numberOfObjects, 'objects to just this one peer'
self.sendDataThreadQueue.put((0, 'sendRawData', headerData + payload)) self.sendDataThreadQueue.put((0, 'sendRawData', headerData + payload))
def _sleepForTimingAttackMitigation(self, sleepTime):
# We don't need to do the timing attack mitigation if we are
# only connected to the trusted peer because we can trust the
# peer not to attack
if sleepTime > 0 and doTimingAttackMitigation and shared.trustedPeer == None:
with shared.printLock:
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
time.sleep(sleepTime)
# We have received a broadcast message # We have received a broadcast message
def recbroadcast(self, data): def recbroadcast(self, data):
@ -347,10 +355,7 @@ class receiveDataThread(threading.Thread):
sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \ sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \
(time.time() - self.messageProcessingStartTime) (time.time() - self.messageProcessingStartTime)
if sleepTime > 0 and doTimingAttackMitigation: self._sleepForTimingAttackMitigation(sleepTime)
with shared.printLock:
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
time.sleep(sleepTime)
# We have received a msg message. # We have received a msg message.
def recmsg(self, data): def recmsg(self, data):
@ -379,10 +384,7 @@ class receiveDataThread(threading.Thread):
sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \ sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \
(time.time() - self.messageProcessingStartTime) (time.time() - self.messageProcessingStartTime)
if sleepTime > 0 and doTimingAttackMitigation: self._sleepForTimingAttackMitigation(sleepTime)
with shared.printLock:
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
time.sleep(sleepTime)
# We have received a pubkey # We have received a pubkey
def recpubkey(self, data): def recpubkey(self, data):
@ -393,11 +395,7 @@ class receiveDataThread(threading.Thread):
lengthOfTimeWeShouldUseToProcessThisMessage = .1 lengthOfTimeWeShouldUseToProcessThisMessage = .1
sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \ sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \
(time.time() - self.pubkeyProcessingStartTime) (time.time() - self.pubkeyProcessingStartTime)
if sleepTime > 0 and doTimingAttackMitigation: self._sleepForTimingAttackMitigation(sleepTime)
with shared.printLock:
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
time.sleep(sleepTime)
# We have received an inv message # We have received an inv message
def recinv(self, data): def recinv(self, data):

View File

@ -39,6 +39,11 @@ class singleListener(threading.Thread):
return sock return sock
def run(self): def run(self):
# If there is a trusted peer then we don't want to accept
# incoming connections so we'll just abandon the thread
if shared.trustedPeer:
return
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
time.sleep(1) time.sleep(1)
helper_bootstrap.dns() helper_bootstrap.dns()

View File

@ -12,6 +12,17 @@ from namecoin import ensureNamecoinOptions
storeConfigFilesInSameDirectoryAsProgramByDefault = False # The user may de-select Portable Mode in the settings if they want the config files to stay in the application data folder. storeConfigFilesInSameDirectoryAsProgramByDefault = False # The user may de-select Portable Mode in the settings if they want the config files to stay in the application data folder.
def _loadTrustedPeer():
try:
trustedPeer = shared.config.get('bitmessagesettings', 'trustedpeer')
except ConfigParser.Error:
# This probably means the trusted peer wasn't specified so we
# can just leave it as None
return
host, port = trustedPeer.split(':')
shared.trustedPeer = shared.Peer(host, int(port))
def loadConfig(): def loadConfig():
if shared.appdata: if shared.appdata:
shared.config.read(shared.appdata + 'keys.dat') shared.config.read(shared.appdata + 'keys.dat')
@ -123,6 +134,8 @@ def loadConfig():
with open(shared.appdata + 'keys.dat', 'wb') as configfile: with open(shared.appdata + 'keys.dat', 'wb') as configfile:
shared.config.write(configfile) shared.config.write(configfile)
_loadTrustedPeer()
def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections(): def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections():
try: try:
VER_THIS=StrictVersion(platform.version()) VER_THIS=StrictVersion(platform.version())

View File

@ -92,6 +92,18 @@ namecoinDefaultRpcPort = "8336"
# binary distributions vs source distributions. # binary distributions vs source distributions.
frozen = getattr(sys,'frozen', None) frozen = getattr(sys,'frozen', None)
# If the trustedpeer option is specified in keys.dat then this will
# contain a Peer which will be connected to instead of using the
# addresses advertised by other peers. The client will only connect to
# this peer and the timing attack mitigation will be disabled in order
# to download data faster. The expected use case is where the user has
# a fast connection to a trusted server where they run a BitMessage
# daemon permanently. If they then run a second instance of the client
# on a local machine periodically when they want to check for messages
# it will sync with the network a lot faster without compromising
# security.
trustedPeer = None
def isInSqlInventory(hash): def isInSqlInventory(hash):
queryreturn = sqlQuery('''select hash from inventory where hash=?''', hash) queryreturn = sqlQuery('''select hash from inventory where hash=?''', hash)
return queryreturn != [] return queryreturn != []