From 2898dbaafabc109798ff13c410d6f27c20f2ae04 Mon Sep 17 00:00:00 2001 From: Kevin Froman Date: Fri, 5 Jan 2018 03:16:21 -0600 Subject: [PATCH] timing attack identity correlation prevention added, python3 test, more fingerprinting prevention, began work on PGP core --- LICENSE | 4 ++++ api.py | 47 +++++++++++++++++++++++++++++++++++++---------- core.py | 7 ++++++- onionr.py | 4 +++- readme.md | 4 ++++ tests.py | 6 ++++++ 6 files changed, 60 insertions(+), 12 deletions(-) diff --git a/LICENSE b/LICENSE index f288702d..fdc16448 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,7 @@ +Onionr Logo is licensed under Creative Commons Attribution-Share Alike 3.0 Unported +https://creativecommons.org/licenses/by-sa/4.0/ + + GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 diff --git a/api.py b/api.py index 6e450e61..fb676e75 100755 --- a/api.py +++ b/api.py @@ -15,7 +15,7 @@ ''' import flask from flask import request, Response, abort -import configparser, sys, random, threading, hmac, hashlib, base64 +import configparser, sys, random, threading, hmac, hashlib, base64, time, math, gnupg from core import Core ''' @@ -31,6 +31,8 @@ class API: def __init__(self, config, debug): self.config = config self.debug = debug + self._privateDelayTime = 3 + self._core = Core() app = flask.Flask(__name__) bindPort = int(self.config['CLIENT']['PORT']) self.bindPort = bindPort @@ -44,12 +46,15 @@ class API: @app.before_request def beforeReq(): + self.requestFailed = False return @app.after_request def afterReq(resp): - resp.headers['Access-Control-Allow-Origin'] = '*' - resp.headers['server'] = 'Onionr' + 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' @@ -57,29 +62,47 @@ class API: @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() + self.validateHost('private') if action == 'hello': resp = Response('Hello, World! ' + request.host) elif action == 'stats': - resp =Response('something') + resp = Response('something') + elif action == 'init': + # generate PGP key + pass + 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') + + @app.errorhandler(404) def notfound(err): - resp = Response("\_(0_0)_/ I got nothin") + self.requestFailed = True + resp = Response("") #resp.headers = getHeaders(resp) return resp @app.errorhandler(403) def authFail(err): - resp = Response("Auth required/security failure") + self.requestFailed = True + resp = Response("403") return resp print('Starting client on ' + self.host + ':' + str(bindPort)) @@ -87,13 +110,17 @@ class API: app.run(host=self.host, port=bindPort, debug=True, threaded=True) - def validateHost(self): + def validateHost(self, hostType): if self.debug: return # Validate host header, to protect against DNS rebinding attacks host = self.host - if not request.host.startswith('127'): - abort(403) + if hostType == 'private': + if not request.host.startswith('127'): + abort(403) + elif hostType == 'public': + if not request.host.endswith('onion') and not request.hosst.endswith('i2p'): + abort(403) # Validate x-requested-with, to protect against CSRF/metadata leaks ''' try: diff --git a/core.py b/core.py index 4ee9eeb7..b157285f 100644 --- a/core.py +++ b/core.py @@ -13,13 +13,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ''' -import sqlite3, os, time, math +import sqlite3, os, time, math, gnupg class Core: def __init__(self): self.queueDB = 'data/queue.db' #self.daemonQueue() # Call to create the DB if it doesn't exist return + def generateMainPGP(self): + # Generate main pgp key + gpg = gnupg.GPG(gnupghome='data/pgp/') + return + def daemonQueue(self): # This function intended to be used by the client # Queue to exchange data between "client" and server. diff --git a/onionr.py b/onionr.py index e3174ac7..4ec4ba90 100755 --- a/onionr.py +++ b/onionr.py @@ -43,7 +43,7 @@ class Onionr: randomPort = 8080 else: randomPort = random.randint(1024, 65535) - self.config['CLIENT'] = {'CLIENT HMAC': base64.b64encode(os.urandom(32)).decode('utf-8'), 'PORT': randomPort} + self.config['CLIENT'] = {'CLIENT HMAC': base64.b64encode(os.urandom(32)).decode('utf-8'), 'PORT': randomPort, 'API VERSION': '0.0.0'} with open('data/config.ini', 'w') as configfile: self.config.write(configfile) command = '' @@ -67,6 +67,8 @@ class Onionr: return return def daemon(self): + os.system('./communicator.py') + print('Started communicator') api.API(self.config, self.debug) return def killDaemon(self): diff --git a/readme.md b/readme.md index 2ea823dd..29ff9e85 100644 --- a/readme.md +++ b/readme.md @@ -3,3 +3,7 @@ P2P microblogging platform and social network, using Tor & I2P. Major work in progress. *NOT USABLE OR SAFE YET* + + +# Development + diff --git a/tests.py b/tests.py index 6561a9fa..c0b3f79f 100755 --- a/tests.py +++ b/tests.py @@ -17,6 +17,12 @@ import unittest, sys, os class OnionrTests(unittest.TestCase): + def testPython3(self): + if sys.version_info.major != 3: + print(sys.version_info.major) + self.assertTrue(False) + else: + self.assertTrue(True) def testNone(self): print('--------------------------') print('Running simple program run test')