work on pm and gui improvements & some bug fixes

This commit is contained in:
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
'''
if self.clientToken != token:
if not hmac.compare_digest(self.clientToken.strip(), token.strip()):
return False
else:
return True
@ -63,6 +64,11 @@ class API:
bindPort = int(config.get('client')['port'])
self.bindPort = bindPort
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":
logger.debug('Your HMAC token: ' + logger.colors.underline + self.clientToken)
@ -99,11 +105,16 @@ class API:
@app.route('/client/')
def private_handler():
if request.args.get('timingToken') is None:
timingToken = ''
else:
timingToken = request.args.get('timingToken')
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')
@ -112,14 +123,19 @@ class API:
elif action == 'shutdown':
request.environ.get('werkzeug.server.shutdown')()
resp = Response('Goodbye')
elif action == 'ping':
resp = Response('pong')
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)
# 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
@ -208,9 +224,11 @@ class API:
host = self.host
if hostType == 'private':
if not request.host.startswith('127') and not self._utils.checkIsIP(request.host):
print("WHAT")
abort(403)
elif hostType == 'public':
if not request.host.endswith('onion') and not request.host.endswith('i2p'):
print("WHAT2")
abort(403)
# Validate x-requested-with, to protect against CSRF/metadata leaks
if not self._developmentMode:

View File

@ -513,3 +513,17 @@ class Core:
conn.close()
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:
def __init__(self, myCore):
self.root = Tk()
self.myCore = myCore # onionr core
self.root.title("PyOnionr")
w = Label(self.root, text="Onionr", width=10)
w.config(font=("Sans-Serif", 22))
w.pack()
self.runningCheckDelay = 5
self.runningCheckDelayCount = 0
scrollbar = Scrollbar(self.root)
scrollbar.pack(side=RIGHT, fill=Y)
self.listedBlocks = []
self.nodeInfo = Frame(self.root)
self.keyInfo = Frame(self.root)
idText = open('./data/hs/hostname', 'r').read()
idLabel = Label(self.root, text="ID: " + idText)
idLabel.pack(pady=5)
#idLabel = Label(self.info, text="Node Address: " + idText)
#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)
sendBtn = Button(self.root, text='Send Message', command=self.sendMessage)
self.sendEntry.pack()
sendBtn.pack()
self.sendEntry.pack(side=TOP, pady=5)
sendBtn.pack(side=TOP)
self.listbox = Listbox(self.root, yscrollcommand=scrollbar.set, height=15)
#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)
self.root.after(2000, self.update)
@ -66,5 +91,12 @@ class OnionrGUI:
self.listbox.see(END)
blocksList = os.listdir('./data/blocks/') # dir is your directory path
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)

View File

@ -111,7 +111,7 @@ class Onionr:
randomPort = random.randint(1024, 65535)
if self.onionrUtils.checkPort(randomPort):
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.showHelpSuggestion,
@ -316,14 +316,15 @@ class Onionr:
return
def addMessage(self):
def addMessage(self, header="txt"):
'''
Broadcasts a message to the Onionr network
'''
while True:
messageToAdd = '-txt-' + logger.readline('Broadcast message to network: ')
if len(messageToAdd) >= 1:
if len(messageToAdd) - 5 >= 1:
break
addedHash = self.onionrCore.setData(messageToAdd)

View File

@ -18,7 +18,7 @@
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
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
if sys.version_info < (3, 6):
@ -35,7 +35,15 @@ class OnionrUtils:
def __init__(self, coreInstance):
self.fingerprintFile = 'data/own-fingerprint.txt'
self._core = coreInstance
self.timingToken = ''
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):
'''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:
# pass
encrypted = self._core._crypto.pubKeyEncrypt(message, pubkey, anonymous=True, encodedData=True)
logger.info(encrypted)
return
encrypted = self._core._crypto.pubKeyEncrypt(message, pubkey, anonymous=True, encodedData=True).decode()
block = self._core.insertBlock(encrypted, header='pm')
if block == '':
logger.error('Could not send PM')
else:
logger.info('Sent PM, hash: ' + block)
return
@ -99,11 +111,14 @@ class OnionrUtils:
'''
config.reload()
self.getTimeBypassToken()
# 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):
'''