diff --git a/onionr/api.py b/onionr/api.py index b43908e1..9acdf0e2 100755 --- a/onionr/api.py +++ b/onionr/api.py @@ -68,6 +68,8 @@ class API: self.clientToken = config.get('client')['client_hmac'] self.timeBypassToken = base64.b16encode(os.urandom(32)).decode() + self.mimeType = 'text/plain' + with open('data/time-bypass.txt', 'w') as bypass: bypass.write(self.timeBypassToken) @@ -96,12 +98,17 @@ class API: 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'" + #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 @@ -111,6 +118,11 @@ class API: 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') @@ -129,6 +141,15 @@ class API: resp = Response('pong') elif action == 'stats': resp = Response('me_irl') + 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()) @@ -149,7 +170,7 @@ class API: requestingPeer = request.args.get('myID') data = request.args.get('data') try: - data + data = data except: data = '' if action == 'firstConnect': @@ -175,7 +196,9 @@ class API: resp = Response('') # setData should be something the communicator initiates, not this api elif action == 'getData': - resp = self._core.getData(data) + if self._utils.validateHash(data): + if not os.path.exists('data/blocks/' + data + '.db'): + resp = base64.b64encode(self._core.getData(data)) if resp == False: abort(404) resp = "" diff --git a/onionr/communicator.py b/onionr/communicator.py index e8a27b24..a99664bf 100755 --- a/onionr/communicator.py +++ b/onionr/communicator.py @@ -19,7 +19,7 @@ and code to operate as a daemon, getting commands from the command queue databas You should have received a copy of the GNU General Public License along with this program. If not, see . ''' -import sqlite3, requests, hmac, hashlib, time, sys, os, math, logger, urllib.parse, random +import sqlite3, requests, hmac, hashlib, time, sys, os, math, logger, urllib.parse, random, base64, binascii import core, onionrutils, onionrcrypto, netcontroller, onionrproofs, btc, config, onionrplugins as plugins class OnionrCommunicate: @@ -241,17 +241,21 @@ class OnionrCommunicate: data = self.performGet('getData', i, hash) if data == False or len(data) > 10000000: continue - hasher.update(data.encode()) + try: + data = base64.b64decode(data) + except binascii.Error: + data = b'' + hasher.update(data) digest = hasher.hexdigest() if type(digest) is bytes: digest = digest.decode() if digest == hash.strip(): self._core.setData(data) - if data.startswith('-txt-'): - self._core.setBlockType(hash, 'txt') logger.info('Successfully obtained data for ' + hash, timestamp=True) - if len(data) < 120: - logger.debug('Block text:\n' + data) + if data.startswith(b'-txt-'): + self._core.setBlockType(hash, 'txt') + if len(data) < 120: + logger.debug('Block text:\n' + data.decode()) else: logger.warn("Failed to validate " + hash + " " + " hash calculated was " + digest) @@ -263,13 +267,20 @@ class OnionrCommunicate: ''' return urllib.parse.quote_plus(data) - def performGet(self, action, peer, data=None, skipHighFailureAddress=False, peerType='tor'): + def performGet(self, action, peer, data=None, skipHighFailureAddress=False, peerType='tor', selfCheck=True): ''' Performs a request to a peer through Tor or i2p (currently only Tor) ''' if not peer.endswith('.onion') and not peer.endswith('.onion/'): raise PeerError('Currently only Tor .onion peers are supported. You must manually specify .onion') + + if len(self._core.hsAdder.strip()) == 0: + raise Exception("Could not perform self address check in performGet due to not knowing our address") + if selfCheck: + if peer.replace('/', '') == self._core.hsAdder: + logger.warn('Tried to performget to own hidden service, but selfCheck was not set to false') + return # Store peer in peerData dictionary (non permanent) if not peer in self.peerData: diff --git a/onionr/core.py b/onionr/core.py index f363d8bc..a1348279 100644 --- a/onionr/core.py +++ b/onionr/core.py @@ -44,6 +44,9 @@ class Core: self.addressDB = 'data/address.db' self.hsAdder = '' + self.bootstrapFileLocation = 'static-data/bootstrap-nodes.txt' + self.bootstrapList = [] + if not os.path.exists('data/'): os.mkdir('data/') if not os.path.exists('data/blocks/'): @@ -55,9 +58,19 @@ class Core: with open('data/hs/hostname', 'r') as hs: self.hsAdder = hs.read() + # 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._utils = onionrutils.OnionrUtils(self) # Initialize the crypto object self._crypto = onionrcrypto.OnionrCrypto(self) + except Exception as error: logger.error('Failed to initialize core Onionr library.', error=error) logger.fatal('Cannot recover from error.') @@ -245,7 +258,7 @@ class Core: Simply return the data associated to a hash ''' try: - dataFile = open(self.blockDataLocation + hash + '.dat') + dataFile = open(self.blockDataLocation + hash + '.dat', 'rb') data = dataFile.read() dataFile.close() except FileNotFoundError: @@ -257,8 +270,10 @@ class Core: ''' Set the data assciated with a hash ''' - data = data.encode() + data = data hasher = hashlib.sha3_256() + if not type(data) is bytes: + data = data.encode() hasher.update(data) dataHash = hasher.hexdigest() if type(dataHash) is bytes: @@ -268,8 +283,8 @@ class Core: pass # TODO: properly check if block is already saved elsewhere #raise Exception("Data is already set for " + dataHash) else: - blockFile = open(blockFileName, 'w') - blockFile.write(data.decode()) + blockFile = open(blockFileName, 'wb') + blockFile.write(data) blockFile.close() conn = sqlite3.connect(self.blockDB) @@ -574,8 +589,10 @@ class Core: announceAmount = 2 nodeList = self.listAdders() if len(nodeList) == 0: - self.addAddress('onionragxuddecmg.onion') - nodeList.append('onionragxuddecmg.onion') + for i in self.bootstrapList: + if self._utils.validateID(i): + self.addAddress(i) + nodeList.append(i) if announceAmount > len(nodeList): announceAmount = len(nodeList) for i in range(announceAmount): diff --git a/onionr/onionr.py b/onionr/onionr.py index 7a941d0b..bd3fd952 100755 --- a/onionr/onionr.py +++ b/onionr/onionr.py @@ -34,7 +34,7 @@ except ImportError: ONIONR_TAGLINE = 'Anonymous P2P Platform - GPLv3 - https://Onionr.VoidNet.Tech' ONIONR_VERSION = '0.0.0' # for debugging and stuff -API_VERSION = '1' # increments of 1; only change when something fundemental about how the API works changes. This way other nodes knows how to communicate without learning too much information about you. +API_VERSION = '2' # increments of 1; only change when something fundemental about how the API works changes. This way other nodes knows how to communicate without learning too much information about you. class Onionr: def __init__(self): @@ -179,6 +179,7 @@ class Onionr: 'add-addr': self.addAddress, 'addaddr': self.addAddress, 'addaddress': self.addAddress, + 'addfile': self.addFile, 'introduce': self.onionrCore.introduceNode, 'connect': self.addAddress @@ -200,6 +201,7 @@ class Onionr: 'add-msg': 'Broadcasts a message to the Onionr network', 'pm': 'Adds a private message to block', 'get-pms': 'Shows private messages sent to you', + 'addfile': 'Create an Onionr block from a file', 'introduce': 'Introduce your node to the public Onionr network (DAEMON MUST BE RUNNING)', } @@ -373,7 +375,7 @@ class Onionr: addedHash = self.onionrCore.setData(messageToAdd) self.onionrCore.addToBlockDB(addedHash, selfInsert=True) self.onionrCore.setBlockType(addedHash, 'txt') - + logger.info("inserted your message as block: " + addedHash) return def getPMs(self): @@ -557,8 +559,23 @@ class Onionr: retVal = '' try: with open('./data/hs/hostname', 'r') as hostname: - retval = retVal.read() + retVal = hostname.read() except FileNotFoundError: return retVal + + def addFile(self): + '''command to add a file to the onionr network''' + if len(sys.argv) >= 2: + newFile = sys.argv[2] + logger.info('Attempting to add file...') + try: + with open(newFile, 'r') as new: + new = new.read() + except FileNotFoundError: + logger.warn('That file does not exist. Improper path?') + else: + print(new) + self.onionrCore.insertBlock(new, header='bin') + Onionr() diff --git a/onionr/onionrcrypto.py b/onionr/onionrcrypto.py index 98bf9230..00e6d520 100644 --- a/onionr/onionrcrypto.py +++ b/onionr/onionrcrypto.py @@ -167,7 +167,7 @@ class OnionrCrypto: return binascii.hexlify(nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE)) def generatePubKey(self): - '''Generate a Ed25519 public key pair, return tuple of base64encoded pubkey, privkey''' + '''Generate a Ed25519 public key pair, return tuple of base32encoded pubkey, privkey''' private_key = nacl.signing.SigningKey.generate() public_key = private_key.verify_key.encode(encoder=nacl.encoding.Base32Encoder()) return (public_key.decode(), private_key.encode(encoder=nacl.encoding.Base32Encoder()).decode()) \ No newline at end of file diff --git a/onionr/static-data/bootstrap-nodes.txt b/onionr/static-data/bootstrap-nodes.txt new file mode 100644 index 00000000..64215eb7 --- /dev/null +++ b/onionr/static-data/bootstrap-nodes.txt @@ -0,0 +1,3 @@ +onionisrgccylxpr.onion +aaronk3mcmglj6qedwptg62yl3wxxjwba2ucpoobrn7iudcacdxtrfad.onion +onionragxuddecmg.onion