work on pm and gui improvements & some bug fixes
parent
3a2efce637
commit
a3aa8e3ae6
|
@ -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,14 +123,19 @@ 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 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
|
return resp
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
|
|
@ -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
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
'''
|
'''
|
||||||
|
|
Loading…
Reference in New Issue