work on pm and gui improvements & some bug fixes

master
Kevin Froman 2018-04-15 21:22:19 -05:00
parent 3a2efce637
commit a3aa8e3ae6
No known key found for this signature in database
GPG Key ID: 0D414D0FE405B63B
5 changed files with 101 additions and 21 deletions

View File

@ -32,7 +32,8 @@ class API:
''' '''
Validate that the client token (hmac) matches the given token Validate that the client token (hmac) matches the given token
''' '''
if self.clientToken != token:
if not hmac.compare_digest(self.clientToken.strip(), token.strip()):
return False return False
else: else:
return True return True
@ -63,6 +64,11 @@ class API:
bindPort = int(config.get('client')['port']) bindPort = int(config.get('client')['port'])
self.bindPort = bindPort self.bindPort = bindPort
self.clientToken = config.get('client')['client_hmac'] self.clientToken = config.get('client')['client_hmac']
self.timeBypassToken = base64.b16encode(os.urandom(32)).decode()
with open('data/time-bypass.txt', 'w') as bypass:
bypass.write(self.timeBypassToken)
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true": if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
logger.debug('Your HMAC token: ' + logger.colors.underline + self.clientToken) logger.debug('Your HMAC token: ' + logger.colors.underline + self.clientToken)
@ -99,11 +105,16 @@ class API:
@app.route('/client/') @app.route('/client/')
def private_handler(): def private_handler():
if request.args.get('timingToken') is None:
timingToken = ''
else:
timingToken = request.args.get('timingToken')
startTime = math.floor(time.time()) startTime = math.floor(time.time())
# we should keep a hash DB of requests (with hmac) to prevent replays # we should keep a hash DB of requests (with hmac) to prevent replays
action = request.args.get('action') action = request.args.get('action')
#if not self.debug: #if not self.debug:
token = request.args.get('token') token = request.args.get('token')
if not self.validateToken(token): if not self.validateToken(token):
abort(403) abort(403)
self.validateHost('private') self.validateHost('private')
@ -112,12 +123,17 @@ class API:
elif action == 'shutdown': elif action == 'shutdown':
request.environ.get('werkzeug.server.shutdown')() request.environ.get('werkzeug.server.shutdown')()
resp = Response('Goodbye') resp = Response('Goodbye')
elif action == 'ping':
resp = Response('pong')
elif action == 'stats': elif action == 'stats':
resp = Response('me_irl') resp = Response('me_irl')
else: else:
resp = Response('(O_o) Dude what? (invalid command)') resp = Response('(O_o) Dude what? (invalid command)')
endTime = math.floor(time.time()) endTime = math.floor(time.time())
elapsed = endTime - startTime 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: if elapsed < self._privateDelayTime:
time.sleep(self._privateDelayTime - elapsed) time.sleep(self._privateDelayTime - elapsed)
@ -208,9 +224,11 @@ class API:
host = self.host host = self.host
if hostType == 'private': if hostType == 'private':
if not request.host.startswith('127') and not self._utils.checkIsIP(request.host): if not request.host.startswith('127') and not self._utils.checkIsIP(request.host):
print("WHAT")
abort(403) abort(403)
elif hostType == 'public': elif hostType == 'public':
if not request.host.endswith('onion') and not request.host.endswith('i2p'): if not request.host.endswith('onion') and not request.host.endswith('i2p'):
print("WHAT2")
abort(403) abort(403)
# Validate x-requested-with, to protect against CSRF/metadata leaks # Validate x-requested-with, to protect against CSRF/metadata leaks
if not self._developmentMode: if not self._developmentMode:

View File

@ -513,3 +513,17 @@ class Core:
conn.close() conn.close()
return return
def insertBlock(self, data, header='txt'):
'''
Inserts a block into the network
'''
retData = ''
if len(data) == 0:
logger.error('Will not insert empty block')
else:
addedHash = self.setData('-' + header + '-' + data)
self.addToBlockDB(addedHash, selfInsert=True)
self.setBlockType(addedHash, header)
retData = addedHash
return retData

View File

@ -19,30 +19,55 @@ import os, sqlite3, core
class OnionrGUI: class OnionrGUI:
def __init__(self, myCore): def __init__(self, myCore):
self.root = Tk() self.root = Tk()
self.myCore = myCore # onionr core self.myCore = myCore # onionr core
self.root.title("PyOnionr") self.root.title("PyOnionr")
w = Label(self.root, text="Onionr", width=10) self.runningCheckDelay = 5
w.config(font=("Sans-Serif", 22)) self.runningCheckDelayCount = 0
w.pack()
scrollbar = Scrollbar(self.root) scrollbar = Scrollbar(self.root)
scrollbar.pack(side=RIGHT, fill=Y) scrollbar.pack(side=RIGHT, fill=Y)
self.listedBlocks = [] self.listedBlocks = []
self.nodeInfo = Frame(self.root)
self.keyInfo = Frame(self.root)
idText = open('./data/hs/hostname', 'r').read() idText = open('./data/hs/hostname', 'r').read()
idLabel = Label(self.root, text="ID: " + idText) #idLabel = Label(self.info, text="Node Address: " + idText)
idLabel.pack(pady=5) #idLabel.pack(pady=5)
idEntry = Entry(self.nodeInfo)
Label(self.nodeInfo, text="Node Address: ").pack(side=LEFT)
idEntry.pack()
idEntry.insert(0, idText.strip())
idEntry.configure(state="readonly")
self.nodeInfo.pack()
pubKeyEntry = Entry(self.keyInfo)
Label(self.keyInfo, text="Public key: ").pack(side=LEFT)
pubKeyEntry.pack()
pubKeyEntry.insert(0, self.myCore._crypto.pubKey)
pubKeyEntry.configure(state="readonly")
self.keyInfo.pack()
self.sendEntry = Entry(self.root) self.sendEntry = Entry(self.root)
sendBtn = Button(self.root, text='Send Message', command=self.sendMessage) sendBtn = Button(self.root, text='Send Message', command=self.sendMessage)
self.sendEntry.pack() self.sendEntry.pack(side=TOP, pady=5)
sendBtn.pack() sendBtn.pack(side=TOP)
self.listbox = Listbox(self.root, yscrollcommand=scrollbar.set, height=15) self.listbox = Listbox(self.root, yscrollcommand=scrollbar.set, height=15)
#listbox.insert(END, str(i)) #listbox.insert(END, str(i))
self.listbox.pack(fill=BOTH) self.listbox.pack(fill=BOTH, pady=25)
self.daemonStatus = Label(self.root, text="Onionr Daemon Status: unknown")
self.daemonStatus.pack()
scrollbar.config(command=self.listbox.yview) scrollbar.config(command=self.listbox.yview)
self.root.after(2000, self.update) self.root.after(2000, self.update)
@ -66,5 +91,12 @@ class OnionrGUI:
self.listbox.see(END) self.listbox.see(END)
blocksList = os.listdir('./data/blocks/') # dir is your directory path blocksList = os.listdir('./data/blocks/') # dir is your directory path
number_blocks = len(blocksList) number_blocks = len(blocksList)
self.runningCheckDelayCount += 1
if self.runningCheckDelayCount == self.runningCheckDelay:
if self.myCore._utils.localCommand('ping') == 'pong':
self.daemonStatus.config(text="Onionr Daemon Status: Running")
else:
self.daemonStatus.config(text="Onionr Daemon Status: Not Running")
self.runningCheckDelayCount = 0
self.root.after(10000, self.update) self.root.after(10000, self.update)

View File

@ -111,7 +111,7 @@ class Onionr:
randomPort = random.randint(1024, 65535) randomPort = random.randint(1024, 65535)
if self.onionrUtils.checkPort(randomPort): if self.onionrUtils.checkPort(randomPort):
break break
config.set('client', {'participate': 'true', 'client_hmac': base64.b64encode(os.urandom(32)).decode('utf-8'), 'port': randomPort, 'api_version': API_VERSION}, True) config.set('client', {'participate': 'true', 'client_hmac': base64.b16encode(os.urandom(32)).decode('utf-8'), 'port': randomPort, 'api_version': API_VERSION}, True)
self.cmds = { self.cmds = {
'': self.showHelpSuggestion, '': self.showHelpSuggestion,
@ -316,14 +316,15 @@ class Onionr:
return return
def addMessage(self): def addMessage(self, header="txt"):
''' '''
Broadcasts a message to the Onionr network Broadcasts a message to the Onionr network
''' '''
while True: while True:
messageToAdd = '-txt-' + logger.readline('Broadcast message to network: ') messageToAdd = '-txt-' + logger.readline('Broadcast message to network: ')
if len(messageToAdd) >= 1: if len(messageToAdd) - 5 >= 1:
break break
addedHash = self.onionrCore.setData(messageToAdd) addedHash = self.onionrCore.setData(messageToAdd)

View File

@ -18,7 +18,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
''' '''
# Misc functions that do not fit in the main api, but are useful # Misc functions that do not fit in the main api, but are useful
import getpass, sys, requests, os, socket, hashlib, logger, sqlite3, config, binascii import getpass, sys, requests, os, socket, hashlib, logger, sqlite3, config, binascii, time
import nacl.signing, nacl.encoding import nacl.signing, nacl.encoding
if sys.version_info < (3, 6): if sys.version_info < (3, 6):
@ -35,8 +35,16 @@ class OnionrUtils:
def __init__(self, coreInstance): def __init__(self, coreInstance):
self.fingerprintFile = 'data/own-fingerprint.txt' self.fingerprintFile = 'data/own-fingerprint.txt'
self._core = coreInstance self._core = coreInstance
self.timingToken = ''
return return
def getTimeBypassToken(self):
if os.path.exists('data/time-bypass.txt'):
with open('data/time-bypass.txt', 'r') as bypass:
self.timingToken = bypass.read()
def sendPM(self, pubkey, message): def sendPM(self, pubkey, message):
'''High level function to encrypt a message to a peer and insert it as a block''' '''High level function to encrypt a message to a peer and insert it as a block'''
@ -44,9 +52,13 @@ class OnionrUtils:
#if self._core.getPeerInfo(pubkey, 'pubkeyExchanged') == 1: #if self._core.getPeerInfo(pubkey, 'pubkeyExchanged') == 1:
# pass # pass
encrypted = self._core._crypto.pubKeyEncrypt(message, pubkey, anonymous=True, encodedData=True) encrypted = self._core._crypto.pubKeyEncrypt(message, pubkey, anonymous=True, encodedData=True).decode()
logger.info(encrypted) block = self._core.insertBlock(encrypted, header='pm')
return
if block == '':
logger.error('Could not send PM')
else:
logger.info('Sent PM, hash: ' + block)
return return
@ -99,11 +111,14 @@ class OnionrUtils:
''' '''
config.reload() config.reload()
self.getTimeBypassToken()
# TODO: URL encode parameters, just as an extra measure. May not be needed, but should be added regardless. # TODO: URL encode parameters, just as an extra measure. May not be needed, but should be added regardless.
requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config.get('client')['port']) + '/client/?action=' + command + '&token=' + str(config.get('client')['client_hmac'])) try:
retData = requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config.get('client')['port']) + '/client/?action=' + command + '&token=' + str(config.get('client')['client_hmac']) + '&timingToken=' + self.timingToken).text
except requests.ConnectionError:
retData = False
return return retData
def getPassword(self, message='Enter password: ', confirm = True): def getPassword(self, message='Enter password: ', confirm = True):
''' '''