Add a 'trustedpeer' option to keys.dat #639

Merged
bpeel merged 1 commits from wip/trusted-peer into master 2014-04-30 23:42:28 +02:00
5 changed files with 59 additions and 20 deletions

View File

@ -22,26 +22,37 @@ class outgoingSynSender(threading.Thread):
self.streamNumber = streamNumber
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):
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
time.sleep(2)
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)
if shared.shutdown:
break
random.seed()
shared.knownNodesLock.acquire()
peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
shared.knownNodesLock.release()
peer = self._getPeer()
shared.alreadyAttemptedConnectionsListLock.acquire()
while peer in shared.alreadyAttemptedConnectionsList or peer.host in shared.connectedHostsList:
shared.alreadyAttemptedConnectionsListLock.release()
# print 'choosing new sample'
random.seed()
shared.knownNodesLock.acquire()
peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
shared.knownNodesLock.release()
peer = self._getPeer()
time.sleep(1)
# Clear out the shared.alreadyAttemptedConnectionsList every half
# hour so that this program will again attempt a connection

View File

@ -313,6 +313,14 @@ class receiveDataThread(threading.Thread):
print 'Sending huge inv message with', numberOfObjects, 'objects to just this one peer'
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
def recbroadcast(self, data):
@ -341,10 +349,7 @@ class receiveDataThread(threading.Thread):
sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \
(time.time() - self.messageProcessingStartTime)
if sleepTime > 0 and doTimingAttackMitigation:
with shared.printLock:
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
time.sleep(sleepTime)
self._sleepForTimingAttackMitigation(sleepTime)
# We have received a msg message.
def recmsg(self, data):
@ -373,10 +378,7 @@ class receiveDataThread(threading.Thread):
sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \
(time.time() - self.messageProcessingStartTime)
if sleepTime > 0 and doTimingAttackMitigation:
with shared.printLock:
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
time.sleep(sleepTime)
self._sleepForTimingAttackMitigation(sleepTime)
# We have received a pubkey
def recpubkey(self, data):
@ -387,11 +389,7 @@ class receiveDataThread(threading.Thread):
lengthOfTimeWeShouldUseToProcessThisMessage = .1
sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \
(time.time() - self.pubkeyProcessingStartTime)
if sleepTime > 0 and doTimingAttackMitigation:
with shared.printLock:
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
time.sleep(sleepTime)
self._sleepForTimingAttackMitigation(sleepTime)
# We have received an inv message
def recinv(self, data):

View File

@ -22,6 +22,11 @@ class singleListener(threading.Thread):
self.selfInitiatedConnections = selfInitiatedConnections
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'):
time.sleep(1)
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.
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():
if shared.appdata:
shared.config.read(shared.appdata + 'keys.dat')
@ -122,6 +133,8 @@ def loadConfig():
with open(shared.appdata + 'keys.dat', 'wb') as configfile:
shared.config.write(configfile)
_loadTrustedPeer()
def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections():
try:
VER_THIS=StrictVersion(platform.version())

View File

@ -92,6 +92,18 @@ namecoinDefaultRpcPort = "8336"
# binary distributions vs source distributions.
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):
queryreturn = sqlQuery('''select hash from inventory where hash=?''', hash)
return queryreturn != []