def testSymmetric(self): dataString = "this is a secret message" dataBytes = dataString.encode() myCore = core.Core() crypto = onionrcrypto.OnionrCrypto(myCore) key = key = b"tttttttttttttttttttttttttttttttt" logger.info("Encrypting: " + dataString, timestamp=True) encrypted = crypto.symmetricEncrypt(dataString, key, returnEncoded=True) logger.info(encrypted, timestamp=True) logger.info('Decrypting encrypted string:', timestamp=True) decrypted = crypto.symmetricDecrypt(encrypted, key, encodedMessage=True) logger.info(decrypted, timestamp=True) self.assertTrue(True)
def __init__(self): ''' Initialize Core Onionr library ''' self.queueDB = 'data/queue.db' self.peerDB = 'data/peers.db' self.blockDB = 'data/blocks.db' self.blockDataLocation = 'data/blocks/' self.addressDB = 'data/address.db' if not os.path.exists('data/'): os.mkdir('data/') if not os.path.exists('data/blocks/'): os.mkdir('data/blocks/') if not os.path.exists(self.blockDB): self.createBlockDB() self._utils = onionrutils.OnionrUtils(self) # Initialize the crypto object self._crypto = onionrcrypto.OnionrCrypto(self) return
def __init__(self, torPort=0): ''' Initialize Core Onionr library ''' # set data dir self.dataDir = os.environ.get('ONIONR_HOME', os.environ.get('DATA_DIR', 'data/')) if not self.dataDir.endswith('/'): self.dataDir += '/' try: self.onionrInst = None self.queueDB = self.dataDir + 'queue.db' self.peerDB = self.dataDir + 'peers.db' self.blockDB = self.dataDir + 'blocks.db' self.blockDataLocation = self.dataDir + 'blocks/' self.blockDataDB = self.blockDataLocation + 'block-data.db' self.publicApiHostFile = self.dataDir + 'public-host.txt' self.privateApiHostFile = self.dataDir + 'private-host.txt' self.addressDB = self.dataDir + 'address.db' self.hsAddress = '' self.i2pAddress = config.get('i2p.own_addr', None) self.bootstrapFileLocation = 'static-data/bootstrap-nodes.txt' self.bootstrapList = [] self.requirements = onionrvalues.OnionrValues() self.torPort = torPort self.dataNonceFile = self.dataDir + 'block-nonces.dat' self.dbCreate = dbcreator.DBCreator(self) self.forwardKeysFile = self.dataDir + 'forward-keys.db' self.keyStore = simplekv.DeadSimpleKV(self.dataDir + 'cachedstorage.dat', refresh_seconds=5) # Socket data, defined here because of multithreading constraints with gevent self.killSockets = False self.startSocket = {} self.socketServerConnData = {} self.socketReasons = {} self.socketServerResponseData = {} self.usageFile = self.dataDir + 'disk-usage.txt' self.config = config self.maxBlockSize = 10000000 # max block size in bytes if not os.path.exists(self.dataDir): os.mkdir(self.dataDir) if not os.path.exists(self.dataDir + 'blocks/'): os.mkdir(self.dataDir + 'blocks/') if not os.path.exists(self.blockDB): self.createBlockDB() if not os.path.exists(self.forwardKeysFile): self.dbCreate.createForwardKeyDB() if not os.path.exists(self.peerDB): self.createPeerDB() if not os.path.exists(self.addressDB): self.createAddressDB() if os.path.exists(self.dataDir + '/hs/hostname'): with open(self.dataDir + '/hs/hostname', 'r') as hs: self.hsAddress = hs.read().strip() # Load bootstrap address list if os.path.exists(self.bootstrapFileLocation): with open(self.bootstrapFileLocation, 'r') as bootstrap: bootstrap = bootstrap.read() for i in bootstrap.split('\n'): self.bootstrapList.append(i) else: logger.warn('Warning: address bootstrap file not found ' + self.bootstrapFileLocation) self.use_subprocess = powchoice.use_subprocess(self) self._utils = onionrutils.OnionrUtils(self) # Initialize the crypto object self._crypto = onionrcrypto.OnionrCrypto(self) self._blacklist = onionrblacklist.OnionrBlackList(self) self.serializer = serializeddata.SerializedData(self) except Exception as error: logger.error('Failed to initialize core Onionr library.', error=error) logger.fatal('Cannot recover from error.') sys.exit(1) return
def __init__(self, debug, developmentMode): ''' OnionrCommunicate This class handles communication with nodes in the Onionr network. ''' self._core = core.Core() self._utils = onionrutils.OnionrUtils(self._core) self._crypto = onionrcrypto.OnionrCrypto(self._core) self._netController = netcontroller.NetController( 0) # arg is the HS port but not needed rn in this file self.newHashes = { } # use this to not keep hashes around too long if we cant get their data self.keepNewHash = 12 self.ignoredHashes = [] self.highFailureAmount = 7 self.communicatorThreads = 0 self.maxThreads = 75 self.processBlocksThreads = 0 self.lookupBlocksThreads = 0 self.blocksProcessing = [ ] # list of blocks currently processing, to avoid trying a block twice at once in 2 seperate threads self.peerStatus = { } # network actions (active requests) for peers used mainly to prevent conflicting actions in threads self.communicatorTimers = { } # communicator timers, name: rate (in seconds) self.communicatorTimerCounts = {} self.communicatorTimerFuncs = {} self.registerTimer('blockProcess', 20) self.registerTimer('highFailure', 10) self.registerTimer('heartBeat', 10) self.registerTimer('pex', 120) logger.debug('Communicator debugging enabled.') with open('data/hs/hostname', 'r') as torID: todID = torID.read() apiRunningCheckRate = 10 apiRunningCheckCount = 0 self.peerData = { } # Session data for peers (recent reachability, speed, etc) if os.path.exists(self._core.queueDB): self._core.clearDaemonQueue() # Loads in and starts the enabled plugins plugins.reload() while True: command = self._core.daemonQueue() # Process blocks based on a timer self.timerTick() # TODO: migrate below if statements to be own functions which are called in the above timerTick() function if self.communicatorTimers[ 'highFailure'] == self.communicatorTimerCounts[ 'highFailure']: self.communicatorTimerCounts['highFailure'] = 0 for i in self.peerData: if self.peerData[i]['failCount'] >= self.highFailureAmount: self.peerData[i]['failCount'] -= 1 if self.communicatorTimers['pex'] == self.communicatorTimerCounts[ 'pex']: pT1 = threading.Thread(target=self.getNewPeers, name="pT1") pT1.start() pT2 = threading.Thread(target=self.getNewPeers, name="pT2") pT2.start() self.communicatorTimerCounts[ 'pex'] = 0 # TODO: do not reset timer if low peer count if self.communicatorTimers[ 'heartBeat'] == self.communicatorTimerCounts['heartBeat']: logger.debug('Communicator heartbeat') self.communicatorTimerCounts['heartBeat'] = 0 if self.communicatorTimers[ 'blockProcess'] == self.communicatorTimerCounts[ 'blockProcess']: lT1 = threading.Thread(target=self.lookupBlocks, name="lt1", args=(True, )) lT2 = threading.Thread(target=self.lookupBlocks, name="lt2", args=(True, )) lT3 = threading.Thread(target=self.lookupBlocks, name="lt3", args=(True, )) lT4 = threading.Thread(target=self.lookupBlocks, name="lt4", args=(True, )) pbT1 = threading.Thread(target=self.processBlocks, name='pbT1', args=(True, )) pbT2 = threading.Thread(target=self.processBlocks, name='pbT2', args=(True, )) pbT3 = threading.Thread(target=self.processBlocks, name='pbT3', args=(True, )) pbT4 = threading.Thread(target=self.processBlocks, name='pbT4', args=(True, )) if (self.maxThreads - 8) >= threading.active_count(): lT1.start() lT2.start() lT3.start() lT4.start() pbT1.start() pbT2.start() pbT3.start() pbT4.start() self.communicatorTimerCounts['blockProcess'] = 0 else: logger.debug(threading.active_count()) logger.debug('Too many threads.') if command != False: if command[0] == 'shutdown': logger.info('Daemon received exit command.', timestamp=True) break elif command[0] == 'announceNode': announceAttempts = 3 announceAttemptCount = 0 announceVal = False logger.info('Announcing node to ' + command[1], timestamp=True) while not announceVal: announceAttemptCount += 1 announceVal = self.performGet( 'announce', command[1], data=self._core.hsAdder.replace('\n', ''), skipHighFailureAddress=True) logger.info(announceVal) if announceAttemptCount >= announceAttempts: logger.warn('Unable to announce to ' + command[1]) break elif command[0] == 'runCheck': logger.info('Status check; looks good.') open('data/.runcheck', 'w+').close() elif command[0] == 'kex': self.pexCount = pexTimer - 1 elif command[0] == 'event': # todo pass elif command[0] == 'checkCallbacks': try: data = json.loads(command[1]) logger.info( 'Checking for callbacks with connection %s...' % data['id']) self.check_callbacks( data, config.get('dc_execcallbacks', True)) events.event('incoming_direct_connection', data={ 'callback': True, 'communicator': self, 'data': data }) except Exception as e: logger.error( 'Failed to interpret callbacks for checking', e) elif command[0] == 'incomingDirectConnection': try: data = json.loads(command[1]) logger.info('Handling incoming connection %s...' % data['id']) self.incoming_direct_connection(data) events.event('incoming_direct_connection', data={ 'callback': False, 'communicator': self, 'data': data }) except Exception as e: logger.error('Failed to handle callbacks for checking', e) apiRunningCheckCount += 1 # check if local API is up if apiRunningCheckCount > apiRunningCheckRate: if self._core._utils.localCommand('ping') != 'pong': for i in range(4): if self._utils.localCommand('ping') == 'pong': apiRunningCheckCount = 0 break # break for loop time.sleep(1) else: # This executes if the api is NOT detected to be running logger.error( 'Daemon detected API crash (or otherwise unable to reach API after long time), stopping...' ) break # break main daemon loop apiRunningCheckCount = 0 time.sleep(1) self._netController.killTor() return
def __init__(self, debug): ''' Initialize the api server, preping variables for later use This initilization defines all of the API entry points and handlers for the endpoints and errors This also saves the used host (random localhost IP address) to the data folder in host.txt ''' config.reload() if config.get('devmode', True): self._developmentMode = True logger.set_level(logger.LEVEL_DEBUG) else: self._developmentMode = False logger.set_level(logger.LEVEL_INFO) self.debug = debug self._privateDelayTime = 3 self._core = Core() self._crypto = onionrcrypto.OnionrCrypto(self._core) self._utils = onionrutils.OnionrUtils(self._core) app = flask.Flask(__name__) bindPort = int(config.get('client')['port']) self.bindPort = bindPort self.clientToken = config.get('client')['client_hmac'] if not os.environ.get("WERKZEUG_RUN_MAIN") == "true": logger.debug('Your HMAC token: ' + logger.colors.underline + self.clientToken) if not debug and not self._developmentMode: hostNums = [ random.randint(1, 255), random.randint(1, 255), random.randint(1, 255) ] self.host = '127.' + str(hostNums[0]) + '.' + str( hostNums[1]) + '.' + str(hostNums[2]) else: self.host = '127.0.0.1' hostFile = open('data/host.txt', 'w') hostFile.write(self.host) hostFile.close() @app.before_request def beforeReq(): ''' Simply define the request as not having yet failed, before every request. ''' self.requestFailed = False return @app.after_request def afterReq(resp): if not self.requestFailed: resp.headers['Access-Control-Allow-Origin'] = '*' else: resp.headers['server'] = 'Onionr' resp.headers['Content-Type'] = 'text/plain' resp.headers["Content-Security-Policy"] = "default-src 'none'" resp.headers['X-Frame-Options'] = 'deny' resp.headers['X-Content-Type-Options'] = "nosniff" return resp @app.route('/client/') def private_handler(): startTime = math.floor(time.time()) # we should keep a hash DB of requests (with hmac) to prevent replays action = request.args.get('action') #if not self.debug: token = request.args.get('token') if not self.validateToken(token): abort(403) self.validateHost('private') if action == 'hello': resp = Response('Hello, World! ' + request.host) elif action == 'shutdown': request.environ.get('werkzeug.server.shutdown')() resp = Response('Goodbye') elif action == 'stats': resp = Response('me_irl') else: resp = Response('(O_o) Dude what? (invalid command)') endTime = math.floor(time.time()) elapsed = endTime - startTime if elapsed < self._privateDelayTime: time.sleep(self._privateDelayTime - elapsed) return resp @app.route('/public/') def public_handler(): # Public means it is publicly network accessible self.validateHost('public') action = request.args.get('action') requestingPeer = request.args.get('myID') data = request.args.get('data') if action == 'firstConnect': pass elif action == 'ping': resp = Response("pong!") elif action == 'getHMAC': resp = Response(self._crypto.generateSymmetric()) elif action == 'getSymmetric': resp = Response(self._crypto.generateSymmetric()) elif action == 'getDBHash': resp = Response(self._utils.getBlockDBHash()) elif action == 'getBlockHashes': resp = Response(self._core.getBlockList()) # setData should be something the communicator initiates, not this api elif action == 'getData': resp = self._core.getData(data) if resp == False: abort(404) resp = "" resp = Response(resp) elif action == 'pex': response = ','.join(self._core.listAdders()) if len(response) == 0: response = 'none' resp = Response(response) elif action == 'kex': response = ','.join(self._core.listPeers()) if len(response) == 0: response = 'none' resp = Response(response) else: resp = Response("") return resp @app.errorhandler(404) def notfound(err): self.requestFailed = True resp = Response("") return resp @app.errorhandler(403) def authFail(err): self.requestFailed = True resp = Response("403") return resp @app.errorhandler(401) def clientError(err): self.requestFailed = True resp = Response("Invalid request") return resp if not os.environ.get("WERKZEUG_RUN_MAIN") == "true": logger.info('Starting client on ' + self.host + ':' + str(bindPort) + '...') try: app.run(host=self.host, port=bindPort, debug=True, threaded=True) except Exception as e: logger.error(str(e)) logger.fatal('Failed to start client on ' + self.host + ':' + str(bindPort) + ', exiting...') exit(1)
def __init__(self, debug, developmentMode): ''' OnionrCommunicate This class handles communication with nodes in the Onionr network. ''' self._core = core.Core() self._utils = onionrutils.OnionrUtils(self._core) self._crypto = onionrcrypto.OnionrCrypto(self._core) self.highFailureAmount = 7 ''' logger.info('Starting Bitcoin Node... with Tor socks port:' + str(sys.argv[2])) try: self.bitcoin = btc.OnionrBTC(torP=int(sys.argv[2])) except _gdbm.error: pass logger.info('Bitcoin Node started, on block: ' + self.bitcoin.node.getBlockHash(self.bitcoin.node.getLastBlockHeight())) ''' #except: #logger.fatal('Failed to start Bitcoin Node, exiting...') #exit(1) blockProcessTimer = 0 blockProcessAmount = 5 highFailureTimer = 0 highFailureRate = 10 heartBeatTimer = 0 heartBeatRate = 5 pexTimer = 5 # How often we should check for new peers pexCount = 0 logger.debug('Communicator debugging enabled.') torID = open('data/hs/hostname').read() self.peerData = { } # Session data for peers (recent reachability, speed, etc) if os.path.exists(self._core.queueDB): self._core.clearDaemonQueue() # Loads in and starts the enabled plugins plugins.reload() while True: command = self._core.daemonQueue() # Process blocks based on a timer blockProcessTimer += 1 heartBeatTimer += 1 pexCount += 1 if highFailureTimer == highFailureRate: highFailureTimer = 0 for i in self.peerData: if self.peerData[i]['failCount'] == self.highFailureAmount: self.peerData[i]['failCount'] -= 1 if pexTimer == pexCount: self.getNewPeers() pexCount = 0 if heartBeatRate == heartBeatTimer: logger.debug('Communicator heartbeat') heartBeatTimer = 0 if blockProcessTimer == blockProcessAmount: self.lookupBlocks() self.processBlocks() blockProcessTimer = 0 if command != False: if command[0] == 'shutdown': logger.info('Daemon recieved exit command.') break time.sleep(1) return
def __init__(self, debug): ''' Initialize the api server, preping variables for later use This initilization defines all of the API entry points and handlers for the endpoints and errors This also saves the used host (random localhost IP address) to the data folder in host.txt ''' config.reload() if config.get('devmode', True): self._developmentMode = True logger.set_level(logger.LEVEL_DEBUG) else: self._developmentMode = False logger.set_level(logger.LEVEL_INFO) self.debug = debug self._privateDelayTime = 3 self._core = Core() self._crypto = onionrcrypto.OnionrCrypto(self._core) self._utils = onionrutils.OnionrUtils(self._core) app = flask.Flask(__name__) bindPort = int(config.get('client')['port']) self.bindPort = bindPort self.clientToken = config.get('client')['client_hmac'] self.timeBypassToken = base64.b16encode(os.urandom(32)).decode() self.i2pEnabled = config.get('i2p')['host'] self.mimeType = 'text/plain' with open('data/time-bypass.txt', 'w') as bypass: bypass.write(self.timeBypassToken) if not os.environ.get("WERKZEUG_RUN_MAIN") == "true": logger.debug('Your web password (KEEP SECRET): ' + logger.colors.underline + self.clientToken) if not debug and not self._developmentMode: hostNums = [ random.randint(1, 255), random.randint(1, 255), random.randint(1, 255) ] self.host = '127.' + str(hostNums[0]) + '.' + str( hostNums[1]) + '.' + str(hostNums[2]) else: self.host = '127.0.0.1' hostFile = open('data/host.txt', 'w') hostFile.write(self.host) hostFile.close() @app.before_request def beforeReq(): ''' Simply define the request as not having yet failed, before every request. ''' self.requestFailed = False return @app.after_request def afterReq(resp): if not self.requestFailed: resp.headers['Access-Control-Allow-Origin'] = '*' #else: # resp.headers['server'] = 'Onionr' resp.headers['Content-Type'] = self.mimeType resp.headers[ "Content-Security-Policy"] = "default-src 'none'; script-src 'none'; object-src 'none'; style-src data: 'unsafe-inline'; img-src data:; media-src 'none'; frame-src 'none'; font-src 'none'; connect-src 'none'" resp.headers['X-Frame-Options'] = 'deny' resp.headers['X-Content-Type-Options'] = "nosniff" resp.headers['server'] = 'Onionr' # reset to text/plain to help prevent browser attacks if self.mimeType != 'text/plain': self.mimeType = 'text/plain' return resp @app.route('/client/') def private_handler(): if request.args.get('timingToken') is None: timingToken = '' else: timingToken = request.args.get('timingToken') data = request.args.get('data') try: data = data except: data = '' startTime = math.floor(time.time()) # we should keep a hash DB of requests (with hmac) to prevent replays action = request.args.get('action') #if not self.debug: token = request.args.get('token') if not self.validateToken(token): abort(403) self.validateHost('private') if action == 'hello': resp = Response('Hello, World! ' + request.host) elif action == 'shutdown': # request.environ.get('werkzeug.server.shutdown')() self.http_server.stop() resp = Response('Goodbye') elif action == 'ping': resp = Response('pong') elif action == 'stats': resp = Response('me_irl') raise Exception elif action == 'site': block = data siteData = self._core.getData(data) response = 'not found' if siteData != '' and siteData != False: self.mimeType = 'text/html' response = siteData.split(b'-', 2)[-1] resp = Response(response) else: resp = Response('(O_o) Dude what? (invalid command)') endTime = math.floor(time.time()) elapsed = endTime - startTime # if bypass token not used, delay response to prevent timing attacks if not hmac.compare_digest(timingToken, self.timeBypassToken): if elapsed < self._privateDelayTime: time.sleep(self._privateDelayTime - elapsed) return resp @app.route('/') def banner(): self.mimeType = 'text/html' self.validateHost('public') try: with open('static-data/index.html', 'r') as html: resp = Response(html.read()) except FileNotFoundError: resp = Response("") return resp @app.route('/public/') def public_handler(): # Public means it is publicly network accessible self.validateHost('public') action = request.args.get('action') requestingPeer = request.args.get('myID') data = request.args.get('data') try: data = data except: data = '' if action == 'firstConnect': pass elif action == 'ping': resp = Response("pong!") elif action == 'getHMAC': resp = Response(self._crypto.generateSymmetric()) elif action == 'getSymmetric': resp = Response(self._crypto.generateSymmetric()) elif action == 'getDBHash': resp = Response(self._utils.getBlockDBHash()) elif action == 'getBlockHashes': resp = Response('\n'.join(self._core.getBlockList())) elif action == 'directMessage': resp = Response(self._core.handle_direct_connection(data)) elif action == 'announce': if data != '': # TODO: require POW for this if self._core.addAddress(data): resp = Response('Success') else: resp = Response('') else: resp = Response('') # setData should be something the communicator initiates, not this api elif action == 'getData': if self._utils.validateHash(data): if not os.path.exists('data/blocks/' + data + '.db'): try: resp = base64.b64encode(self._core.getData(data)) except TypeError: resp = "" if resp == False: abort(404) resp = "" resp = Response(resp) elif action == 'pex': response = ','.join(self._core.listAdders()) if len(response) == 0: response = 'none' resp = Response(response) elif action == 'kex': peers = self._core.listPeers(getPow=True) response = ','.join(peers) resp = Response(response) else: resp = Response("") return resp @app.errorhandler(404) def notfound(err): self.requestFailed = True resp = Response("") return resp @app.errorhandler(403) def authFail(err): self.requestFailed = True resp = Response("403") return resp @app.errorhandler(401) def clientError(err): self.requestFailed = True resp = Response("Invalid request") return resp if not os.environ.get("WERKZEUG_RUN_MAIN") == "true": logger.info('Starting client on ' + self.host + ':' + str(bindPort) + '...', timestamp=True) try: self.http_server = WSGIServer((self.host, bindPort), app) self.http_server.serve_forever() except KeyboardInterrupt: pass #app.run(host=self.host, port=bindPort, debug=False, threaded=True) except Exception as e: logger.error(str(e)) logger.fatal('Failed to start client on ' + self.host + ':' + str(bindPort) + ', exiting...') exit(1)