Code consistency updates

- Improved formatting
- Added comments
- URL encoded values in netcontroller.performGET
- Kept SQL statement case consistency
This commit is contained in:
Arinerron 2018-02-03 19:44:29 -08:00
parent bdd1d9697b
commit 62cad7a6ea
No known key found for this signature in database
GPG Key ID: 99383627861C62F0
10 changed files with 275 additions and 147 deletions

View File

@ -25,10 +25,12 @@ import configparser, sys, random, threading, hmac, hashlib, base64, time, math,
from core import Core from core import Core
import onionrutils import onionrutils
class API: class API:
''' Main http api (flask)''' '''
Main HTTP API (Flask)
'''
def validateToken(self, token): def validateToken(self, token):
''' '''
Validate if the client token (hmac) matches the given token Validate that the client token (hmac) matches the given token
''' '''
if self.clientToken != token: if self.clientToken != token:
return False return False
@ -36,10 +38,11 @@ class API:
return True return True
def __init__(self, config, debug): def __init__(self, config, debug):
''' Initialize the api server, preping variables for later use '''
This initilization defines all of the API entry points and handlers for the endpoints and errors Initialize the api server, preping variables for later use
This also saves the used host (random localhost IP address) to the data folder in host.txt This initilization defines all of the API entry points and handlers for the endpoints and errors
This also saves the used host (random localhost IP address) to the data folder in host.txt
''' '''
if os.path.exists('dev-enabled'): if os.path.exists('dev-enabled'):
self._developmentMode = True self._developmentMode = True
@ -72,9 +75,10 @@ class API:
@app.before_request @app.before_request
def beforeReq(): def beforeReq():
''' '''
Simply define the request as not having yet failed, before every request. Simply define the request as not having yet failed, before every request.
''' '''
self.requestFailed = False self.requestFailed = False
return return
@app.after_request @app.after_request
@ -87,6 +91,7 @@ class API:
resp.headers["Content-Security-Policy"] = "default-src 'none'" resp.headers["Content-Security-Policy"] = "default-src 'none'"
resp.headers['X-Frame-Options'] = 'deny' resp.headers['X-Frame-Options'] = 'deny'
resp.headers['X-Content-Type-Options'] = "nosniff" resp.headers['X-Content-Type-Options'] = "nosniff"
return resp return resp
@app.route('/client/') @app.route('/client/')
@ -112,6 +117,7 @@ class API:
elapsed = endTime - startTime elapsed = endTime - startTime
if elapsed < self._privateDelayTime: if elapsed < self._privateDelayTime:
time.sleep(self._privateDelayTime - elapsed) time.sleep(self._privateDelayTime - elapsed)
return resp return resp
@app.route('/public/') @app.route('/public/')
@ -149,17 +155,21 @@ class API:
def notfound(err): def notfound(err):
self.requestFailed = True self.requestFailed = True
resp = Response("") resp = Response("")
#resp.headers = getHeaders(resp)
return resp return resp
@app.errorhandler(403) @app.errorhandler(403)
def authFail(err): def authFail(err):
self.requestFailed = True self.requestFailed = True
resp = Response("403") resp = Response("403")
return resp return resp
@app.errorhandler(401) @app.errorhandler(401)
def clientError(err): def clientError(err):
self.requestFailed = True self.requestFailed = True
resp = Response("Invalid request") resp = Response("Invalid request")
return resp return resp
logger.info('Starting client on ' + self.host + ':' + str(bindPort) + '...') logger.info('Starting client on ' + self.host + ':' + str(bindPort) + '...')
@ -168,7 +178,9 @@ class API:
app.run(host=self.host, port=bindPort, debug=True, threaded=True) app.run(host=self.host, port=bindPort, debug=True, threaded=True)
def validateHost(self, hostType): def validateHost(self, hostType):
''' Validate various features of the request including: '''
Validate various features of the request including:
If private (/client/), is the host header local? If private (/client/), is the host header local?
If public (/public/), is the host header onion or i2p? If public (/public/), is the host header onion or i2p?

View File

@ -1,23 +0,0 @@
'''
Simply define terminal control codes (mainly colors)
'''
class Colors:
def __init__(self):
'''
PURPLE='\033[95m'
BLUE='\033[94m'
GREEN='\033[92m'
YELLOW='\033[93m'
RED='\033[91m'
BOLD='\033[1m'
UNDERLINE='\033[4m'
RESET="\x1B[m"
'''
self.PURPLE='\033[95m'
self.BLUE='\033[94m'
self.GREEN='\033[92m'
self.YELLOW='\033[93m'
self.RED='\033[91m'
self.BOLD='\033[1m'
self.UNDERLINE='\033[4m'
self.RESET="\x1B[m"

View File

@ -19,13 +19,15 @@ 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 You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
''' '''
import sqlite3, requests, hmac, hashlib, time, sys, os, logger import sqlite3, requests, hmac, hashlib, time, sys, os, logger, urllib.parse
import core, onionrutils import core, onionrutils
class OnionrCommunicate: class OnionrCommunicate:
def __init__(self, debug, developmentMode): def __init__(self, debug, developmentMode):
''' OnionrCommunicate '''
OnionrCommunicate
This class handles communication with nodes in the Onionr network. This class handles communication with nodes in the Onionr network.
''' '''
self._core = core.Core() self._core = core.Core()
self._utils = onionrutils.OnionrUtils(self._core) self._utils = onionrutils.OnionrUtils(self._core)
@ -63,29 +65,46 @@ class OnionrCommunicate:
logger.warn('Daemon recieved exit command.') logger.warn('Daemon recieved exit command.')
break break
time.sleep(1) time.sleep(1)
return
def getRemotePeerKey(self, peerID):
'''This function contacts a peer and gets their main PGP key.
This is safe because Tor or I2P is used, but it does not ensure that the person is who they say they are return
def getRemotePeerKey(self, peerID):
'''
This function contacts a peer and gets their main PGP key.
This is safe because Tor or I2P is used, but it does not ensure that the person is who they say they are
''' '''
url = 'http://' + peerID + '/public/?action=getPGP' url = 'http://' + peerID + '/public/?action=getPGP'
r = requests.get(url, headers=headers) r = requests.get(url, headers=headers)
response = r.text response = r.text
return response return response
def shareHMAC(self, peerID, key): def shareHMAC(self, peerID, key):
'''This function shares an HMAC key to a peer
''' '''
This function shares an HMAC key to a peer
'''
return return
def getPeerProof(self, peerID): def getPeerProof(self, peerID):
'''This function gets the current peer proof requirement''' '''
This function gets the current peer proof requirement
'''
return return
def sendPeerProof(self, peerID, data): def sendPeerProof(self, peerID, data):
'''This function sends the proof result to a peer previously fetched with getPeerProof''' '''
This function sends the proof result to a peer previously fetched with getPeerProof
'''
return return
def lookupBlocks(self): def lookupBlocks(self):
'''Lookup blocks and merge new ones''' '''
Lookup blocks and merge new ones
'''
peerList = self._core.listPeers() peerList = self._core.listPeers()
blocks = '' blocks = ''
for i in peerList: for i in peerList:
@ -120,20 +139,26 @@ class OnionrCommunicate:
else: else:
logger.debug('Adding ' + i + ' to hash database...') logger.debug('Adding ' + i + ' to hash database...')
self._core.addToBlockDB(i) self._core.addToBlockDB(i)
return return
def processBlocks(self): def processBlocks(self):
''' '''
Work with the block database and download any missing blocks Work with the block database and download any missing blocks
This is meant to be called from the communicator daemon on its timer.
This is meant to be called from the communicator daemon on its timer.
''' '''
for i in self._core.getBlockList(True).split("\n"): for i in self._core.getBlockList(True).split("\n"):
if i != "": if i != "":
logger.warn('UNSAVED BLOCK: ' + i) logger.warn('UNSAVED BLOCK: ' + i)
data = self.downloadBlock(i) data = self.downloadBlock(i)
return return
def downloadBlock(self, hash): def downloadBlock(self, hash):
'''download a block from random order of peers''' '''
Download a block from random order of peers
'''
peerList = self._core.listPeers() peerList = self._core.listPeers()
blocks = '' blocks = ''
for i in peerList: for i in peerList:
@ -155,22 +180,33 @@ class OnionrCommunicate:
else: else:
logger.warn("Failed to validate " + hash) logger.warn("Failed to validate " + hash)
return
def urlencode(self, data):
'''
URL encodes the data
'''
return urllib.parse.quote_plus(data)
def performGet(self, action, peer, data=None, type='tor'): def performGet(self, action, peer, data=None, type='tor'):
'''Performs a request to a peer through Tor or i2p (currently only tor)''' '''
Performs a request to a peer through Tor or i2p (currently only Tor)
'''
if not peer.endswith('.onion') and not peer.endswith('.onion/'): if not peer.endswith('.onion') and not peer.endswith('.onion/'):
raise PeerError('Currently only Tor .onion peers are supported. You must manually specify .onion') raise PeerError('Currently only Tor .onion peers are supported. You must manually specify .onion')
socksPort = sys.argv[2] socksPort = sys.argv[2]
'''We use socks5h to use tor as DNS''' '''We use socks5h to use tor as DNS'''
proxies = {'http': 'socks5h://127.0.0.1:' + str(socksPort), 'https': 'socks5h://127.0.0.1:' + str(socksPort)} proxies = {'http': 'socks5h://127.0.0.1:' + str(socksPort), 'https': 'socks5h://127.0.0.1:' + str(socksPort)}
headers = {'user-agent': 'PyOnionr'} headers = {'user-agent': 'PyOnionr'}
url = 'http://' + peer + '/public/?action=' + action url = 'http://' + peer + '/public/?action=' + urlencode(action)
if data != None: if data != None:
url = url + '&data=' + data url = url + '&data=' + urlencode(data)
try: try:
r = requests.get(url, headers=headers, proxies=proxies, timeout=(15, 30)) r = requests.get(url, headers=headers, proxies=proxies, timeout=(15, 30))
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
logger.warn(action + " failed with peer " + peer + ": " + str(e)) logger.warn(action + " failed with peer " + peer + ": " + str(e))
return False return False
return r.text return r.text

View File

@ -34,7 +34,7 @@ if sys.version_info < (3, 6):
class Core: class Core:
def __init__(self): def __init__(self):
''' '''
Initialize Core Onionr library Initialize Core Onionr library
''' '''
self.queueDB = 'data/queue.db' self.queueDB = 'data/queue.db'
self.peerDB = 'data/peers.db' self.peerDB = 'data/peers.db'
@ -54,87 +54,101 @@ class Core:
return return
def generateMainPGP(self, myID): def generateMainPGP(self, myID):
''' Generate the main PGP key for our client. Should not be done often. '''
Uses own PGP home folder in the data/ directory. ''' Generate the main PGP key for our client. Should not be done often.
# Generate main pgp key
Uses own PGP home folder in the data/ directory
'''
gpg = gnupg.GPG(homedir='./data/pgp/') gpg = gnupg.GPG(homedir='./data/pgp/')
input_data = gpg.gen_key_input(key_type="RSA", key_length=1024, name_real=myID, name_email='anon@onionr', testing=True) input_data = gpg.gen_key_input(key_type="RSA", key_length=1024, name_real=myID, name_email='anon@onionr', testing=True)
#input_data = gpg.gen_key_input(key_type="RSA", key_length=1024)
key = gpg.gen_key(input_data) key = gpg.gen_key(input_data)
logger.info("Generating PGP key, this will take some time..") logger.info("Generating PGP key, this will take some time..")
while key.status != "key created": while key.status != "key created":
time.sleep(0.5) time.sleep(0.5)
print(key.status) print(key.status)
logger.info("Finished generating PGP key") logger.info("Finished generating PGP key")
# Write the key # Write the key
myFingerpintFile = open('data/own-fingerprint.txt', 'w') myFingerpintFile = open('data/own-fingerprint.txt', 'w')
myFingerpintFile.write(key.fingerprint) myFingerpintFile.write(key.fingerprint)
myFingerpintFile.close() myFingerpintFile.close()
return return
def addPeer(self, peerID, name=''): def addPeer(self, peerID, name=''):
''' Add a peer by their ID, with an optional name, to the peer database.''' '''
''' DOES NO SAFETY CHECKS if the ID is valid, but prepares the insertion. ''' Add a peer by their ID, with an optional name, to the peer database
DOES NO SAFETY CHECKS if the ID is valid, but prepares the insertion
'''
# This function simply adds a peer to the DB # This function simply adds a peer to the DB
if not self._utils.validateID(peerID): if not self._utils.validateID(peerID):
return False return False
conn = sqlite3.connect(self.peerDB) conn = sqlite3.connect(self.peerDB)
c = conn.cursor() c = conn.cursor()
t = (peerID, name, 'unknown') t = (peerID, name, 'unknown')
c.execute('insert into peers (id, name, dateSeen) values(?, ?, ?);', t) c.execute('INSERT INTO peers (id, name, dateSeen) VALUES(?, ?, ?);', t)
conn.commit() conn.commit()
conn.close() conn.close()
return True return True
def createPeerDB(self): def createPeerDB(self):
''' '''
Generate the peer sqlite3 database and populate it with the peers table. Generate the peer sqlite3 database and populate it with the peers table.
''' '''
# generate the peer database # generate the peer database
conn = sqlite3.connect(self.peerDB) conn = sqlite3.connect(self.peerDB)
c = conn.cursor() c = conn.cursor()
c.execute(''' c.execute('''CREATE TABLE peers(
create table peers( ID text not null,
ID text not null, name text,
name text, pgpKey text,
pgpKey text, hmacKey text,
hmacKey text, blockDBHash text,
blockDBHash text, forwardKey text,
forwardKey text, dateSeen not null,
dateSeen not null, bytesStored int,
bytesStored int, trust int);
trust int);
''') ''')
conn.commit() conn.commit()
conn.close() conn.close()
return
def createBlockDB(self): def createBlockDB(self):
''' '''
Create a database for blocks Create a database for blocks
hash - the hash of a block hash - the hash of a block
dateReceived - the date the block was recieved, not necessarily when it was created dateReceived - the date the block was recieved, not necessarily when it was created
decrypted - if we can successfully decrypt the block (does not describe its current state) decrypted - if we can successfully decrypt the block (does not describe its current state)
dataType - data type of the block dataType - data type of the block
dataFound - if the data has been found for the block dataFound - if the data has been found for the block
dataSaved - if the data has been saved for the block dataSaved - if the data has been saved for the block
''' '''
if os.path.exists(self.blockDB): if os.path.exists(self.blockDB):
raise Exception("Block database already exists") raise Exception("Block database already exists")
conn = sqlite3.connect(self.blockDB) conn = sqlite3.connect(self.blockDB)
c = conn.cursor() c = conn.cursor()
c.execute('''create table hashes( c.execute('''CREATE TABLE hashes(
hash text not null, hash text not null,
dateReceived int, dateReceived int,
decrypted int, decrypted int,
dataType text, dataType text,
dataFound int, dataFound int,
dataSaved int dataSaved int);
);
''') ''')
conn.commit() conn.commit()
conn.close() conn.close()
return
def addToBlockDB(self, newHash, selfInsert=False): def addToBlockDB(self, newHash, selfInsert=False):
'''add a hash value to the block db (should be in hex format)''' '''
Add a hash value to the block db
Should be in hex format!
'''
if not os.path.exists(self.blockDB): if not os.path.exists(self.blockDB):
raise Exception('Block db does not exist') raise Exception('Block db does not exist')
if self._utils.hasBlock(newHash): if self._utils.hasBlock(newHash):
@ -147,22 +161,29 @@ class Core:
else: else:
selfInsert = 0 selfInsert = 0
data = (newHash, currentTime, 0, '', 0, selfInsert) data = (newHash, currentTime, 0, '', 0, selfInsert)
c.execute('INSERT into hashes values(?, ?, ?, ?, ?, ?);', data) c.execute('INSERT INTO hashes VALUES(?, ?, ?, ?, ?, ?);', data)
conn.commit() conn.commit()
conn.close() conn.close()
return
def getData(self,hash): def getData(self,hash):
'''simply return the data associated to a hash''' '''
Simply return the data associated to a hash
'''
try: try:
dataFile = open(self.blockDataLocation + hash + '.dat') dataFile = open(self.blockDataLocation + hash + '.dat')
data = dataFile.read() data = dataFile.read()
dataFile.close() dataFile.close()
except FileNotFoundError: except FileNotFoundError:
data = False data = False
return data return data
def setData(self, data): def setData(self, data):
'''set the data assciated with a hash''' '''
Set the data assciated with a hash
'''
data = data.encode() data = data.encode()
hasher = hashlib.sha3_256() hasher = hashlib.sha3_256()
hasher.update(data) hasher.update(data)
@ -171,7 +192,7 @@ class Core:
dataHash = dataHash.decode() dataHash = dataHash.decode()
blockFileName = self.blockDataLocation + dataHash + '.dat' blockFileName = self.blockDataLocation + dataHash + '.dat'
if os.path.exists(blockFileName): if os.path.exists(blockFileName):
pass # to do, properly check if block is already saved elsewhere pass # TODO: properly check if block is already saved elsewhere
#raise Exception("Data is already set for " + dataHash) #raise Exception("Data is already set for " + dataHash)
else: else:
blockFile = open(blockFileName, 'w') blockFile = open(blockFileName, 'w')
@ -180,7 +201,7 @@ class Core:
conn = sqlite3.connect(self.blockDB) conn = sqlite3.connect(self.blockDB)
c = conn.cursor() c = conn.cursor()
c.execute("UPDATE hashes set dataSaved=1 where hash = '" + dataHash + "';") c.execute("UPDATE hashes SET dataSaved=1 WHERE hash = '" + dataHash + "';")
conn.commit() conn.commit()
conn.close() conn.close()
@ -188,9 +209,8 @@ class Core:
def dataDirEncrypt(self, password): def dataDirEncrypt(self, password):
''' '''
Encrypt the data directory on Onionr shutdown Encrypt the data directory on Onionr shutdown
''' '''
# Encrypt data directory (don't delete it in this function)
if os.path.exists('data.tar'): if os.path.exists('data.tar'):
os.remove('data.tar') os.remove('data.tar')
tar = tarfile.open("data.tar", "w") tar = tarfile.open("data.tar", "w")
@ -201,12 +221,13 @@ class Core:
encrypted = simplecrypt.encrypt(password, tarData) encrypted = simplecrypt.encrypt(password, tarData)
open('data-encrypted.dat', 'wb').write(encrypted) open('data-encrypted.dat', 'wb').write(encrypted)
os.remove('data.tar') os.remove('data.tar')
return return
def dataDirDecrypt(self, password): def dataDirDecrypt(self, password):
''' '''
Decrypt the data directory on startup Decrypt the data directory on startup
''' '''
# Decrypt data directory
if not os.path.exists('data-encrypted.dat'): if not os.path.exists('data-encrypted.dat'):
return (False, 'encrypted archive does not exist') return (False, 'encrypted archive does not exist')
data = open('data-encrypted.dat', 'rb').read() data = open('data-encrypted.dat', 'rb').read()
@ -219,13 +240,15 @@ class Core:
tar = tarfile.open('data.tar') tar = tarfile.open('data.tar')
tar.extractall() tar.extractall()
tar.close() tar.close()
return (True, '') return (True, '')
def daemonQueue(self): def daemonQueue(self):
''' '''
Gives commands to the communication proccess/daemon by reading an sqlite3 database Gives commands to the communication proccess/daemon by reading an sqlite3 database
This function intended to be used by the client. Queue to exchange data between "client" and server.
''' '''
# This function intended to be used by the client
# Queue to exchange data between "client" and server.
retData = False retData = False
if not os.path.exists(self.queueDB): if not os.path.exists(self.queueDB):
conn = sqlite3.connect(self.queueDB) conn = sqlite3.connect(self.queueDB)
@ -241,7 +264,7 @@ class Core:
retData = row retData = row
break break
if retData != False: if retData != False:
c.execute('delete from commands where id = ?', (retData[3],)) c.execute('DELETE FROM commands WHERE id=?;', (retData[3],))
conn.commit() conn.commit()
conn.close() conn.close()
@ -249,19 +272,23 @@ class Core:
def daemonQueueAdd(self, command, data=''): def daemonQueueAdd(self, command, data=''):
''' '''
Add a command to the daemon queue, used by the communication daemon (communicator.py) Add a command to the daemon queue, used by the communication daemon (communicator.py)
''' '''
# Intended to be used by the web server # Intended to be used by the web server
date = math.floor(time.time()) date = math.floor(time.time())
conn = sqlite3.connect(self.queueDB) conn = sqlite3.connect(self.queueDB)
c = conn.cursor() c = conn.cursor()
t = (command, data, date) t = (command, data, date)
c.execute('INSERT into commands (command, data, date) values (?, ?, ?)', t) c.execute('INSERT INTO commands (command, data, date) VALUES(?, ?, ?)', t)
conn.commit() conn.commit()
conn.close() conn.close()
return return
def clearDaemonQueue(self): def clearDaemonQueue(self):
'''clear the daemon queue (somewhat dangerousous)''' '''
Clear the daemon queue (somewhat dangerous)
'''
conn = sqlite3.connect(self.queueDB) conn = sqlite3.connect(self.queueDB)
c = conn.cursor() c = conn.cursor()
try: try:
@ -271,45 +298,49 @@ class Core:
pass pass
conn.close() conn.close()
def generateHMAC(self): return
def generateHMAC(self, length=32):
''' '''
generate and return an HMAC key Generate and return an HMAC key
''' '''
key = base64.b64encode(os.urandom(32)) key = base64.b64encode(os.urandom(length))
return key return key
def listPeers(self, randomOrder=True): def listPeers(self, randomOrder=True):
'''Return a list of peers '''
Return a list of peers
randomOrder determines if the list should be in a random order randomOrder determines if the list should be in a random order
''' '''
conn = sqlite3.connect(self.peerDB) conn = sqlite3.connect(self.peerDB)
c = conn.cursor() c = conn.cursor()
if randomOrder: if randomOrder:
peers = c.execute('SELECT * FROM peers order by RANDOM();') peers = c.execute('SELECT * FROM peers ORDER BY RANDOM();')
else: else:
peers = c.execute('SELECT * FROM peers;') peers = c.execute('SELECT * FROM peers;')
peerList = [] peerList = []
for i in peers: for i in peers:
peerList.append(i[0]) peerList.append(i[0])
conn.close() conn.close()
return peerList return peerList
def getPeerInfo(self, peer, info): def getPeerInfo(self, peer, info):
''' '''
get info about a peer Get info about a peer from their database entry
id text 0 id text 0
name text, 1 name text, 1
pgpKey text, 2 pgpKey text, 2
hmacKey text, 3 hmacKey text, 3
blockDBHash text, 4 blockDBHash text, 4
forwardKey text, 5 forwardKey text, 5
dateSeen not null, 7 dateSeen not null, 7
bytesStored int, 8 bytesStored int, 8
trust int 9 trust int 9
''' '''
# Lookup something about a peer from their database entry
conn = sqlite3.connect(self.peerDB) conn = sqlite3.connect(self.peerDB)
c = conn.cursor() c = conn.cursor()
command = (peer,) command = (peer,)
@ -325,21 +356,29 @@ class Core:
else: else:
iterCount += 1 iterCount += 1
conn.close() conn.close()
return retVal return retVal
def setPeerInfo(self, peer, key, data): def setPeerInfo(self, peer, key, data):
'''update a peer for a key''' '''
Update a peer for a key
'''
conn = sqlite3.connect(self.peerDB) conn = sqlite3.connect(self.peerDB)
c = conn.cursor() c = conn.cursor()
command = (data, peer) command = (data, peer)
# TODO: validate key on whitelist # TODO: validate key on whitelist
if key not in ('id', 'text', 'name', 'pgpKey', 'hmacKey', 'blockDBHash', 'forwardKey', 'dateSeen', 'bytesStored', 'trust'): if key not in ('id', 'text', 'name', 'pgpKey', 'hmacKey', 'blockDBHash', 'forwardKey', 'dateSeen', 'bytesStored', 'trust'):
raise Exception("Got invalid database key when setting peer info") raise Exception("Got invalid database key when setting peer info")
c.execute('UPDATE peers SET ' + key + ' = ? where id=?', command) c.execute('UPDATE peers SET ' + key + ' = ? WHERE id=?', command)
conn.commit() conn.commit()
conn.close() conn.close()
return
def getBlockList(self, unsaved=False): def getBlockList(self, unsaved=False):
'''get list of our blocks''' '''
Get list of our blocks
'''
conn = sqlite3.connect(self.blockDB) conn = sqlite3.connect(self.blockDB)
c = conn.cursor() c = conn.cursor()
retData = '' retData = ''
@ -350,24 +389,33 @@ class Core:
for row in c.execute(execute): for row in c.execute(execute):
for i in row: for i in row:
retData += i + "\n" retData += i + "\n"
return retData return retData
def getBlocksByType(self, blockType): def getBlocksByType(self, blockType):
'''
Returns a list of blocks by the type
'''
conn = sqlite3.connect(self.blockDB) conn = sqlite3.connect(self.blockDB)
c = conn.cursor() c = conn.cursor()
retData = '' retData = ''
execute = 'SELECT hash FROM hashes where dataType=?' execute = 'SELECT hash FROM hashes WHERE dataType=?;'
args = (blockType,) args = (blockType,)
for row in c.execute(execute, args): for row in c.execute(execute, args):
for i in row: for i in row:
retData += i + "\n" retData += i + "\n"
return retData.split('\n') return retData.split('\n')
def setBlockType(self, hash, blockType): def setBlockType(self, hash, blockType):
'''
Sets the type of block
'''
conn = sqlite3.connect(self.blockDB) conn = sqlite3.connect(self.blockDB)
c = conn.cursor() c = conn.cursor()
#if blockType not in ("txt"):
# return
c.execute("UPDATE hashes SET dataType='" + blockType + "' WHERE hash = '" + hash + "';") c.execute("UPDATE hashes SET dataType='" + blockType + "' WHERE hash = '" + hash + "';")
conn.commit() conn.commit()
conn.close() conn.close()
return

View File

@ -19,8 +19,8 @@
''' '''
import subprocess, os, random, sys, logger, time, signal import subprocess, os, random, sys, logger, time, signal
class NetController: class NetController:
'''NetController '''
This class handles hidden service setup on Tor and I2P This class handles hidden service setup on Tor and I2P
''' '''
def __init__(self, hsPort): def __init__(self, hsPort):
self.torConfigLocation = 'data/torrc' self.torConfigLocation = 'data/torrc'
@ -30,15 +30,19 @@ class NetController:
self._torInstnace = '' self._torInstnace = ''
self.myID = '' self.myID = ''
''' '''
if os.path.exists(self.torConfigLocation): if os.path.exists(self.torConfigLocation):
torrc = open(self.torConfigLocation, 'r') torrc = open(self.torConfigLocation, 'r')
if not str(self.hsPort) in torrc.read(): if not str(self.hsPort) in torrc.read():
os.remove(self.torConfigLocation) os.remove(self.torConfigLocation)
torrc.close() torrc.close()
''' '''
return return
def generateTorrc(self): def generateTorrc(self):
'''generate a torrc file for our tor instance''' '''
Generate a torrc file for our tor instance
'''
if os.path.exists(self.torConfigLocation): if os.path.exists(self.torConfigLocation):
os.remove(self.torConfigLocation) os.remove(self.torConfigLocation)
torrcData = '''SocksPort ''' + str(self.socksPort) + ''' torrcData = '''SocksPort ''' + str(self.socksPort) + '''
@ -48,10 +52,12 @@ HiddenServicePort 80 127.0.0.1:''' + str(self.hsPort) + '''
torrc = open(self.torConfigLocation, 'w') torrc = open(self.torConfigLocation, 'w')
torrc.write(torrcData) torrc.write(torrcData)
torrc.close() torrc.close()
return return
def startTor(self): def startTor(self):
'''Start Tor with onion service on port 80 & socks proxy on random port '''
Start Tor with onion service on port 80 & socks proxy on random port
''' '''
self.generateTorrc() self.generateTorrc()
if os.path.exists('./tor'): if os.path.exists('./tor'):
@ -80,9 +86,13 @@ HiddenServicePort 80 127.0.0.1:''' + str(self.hsPort) + '''
torPidFile = open('data/torPid.txt', 'w') torPidFile = open('data/torPid.txt', 'w')
torPidFile.write(str(tor.pid)) torPidFile.write(str(tor.pid))
torPidFile.close() torPidFile.close()
return True return True
def killTor(self): def killTor(self):
'''properly kill tor based on pid saved to file''' '''
Properly kill tor based on pid saved to file
'''
try: try:
pid = open('data/torPid.txt', 'r') pid = open('data/torPid.txt', 'r')
pidN = pid.read() pidN = pid.read()
@ -95,3 +105,5 @@ HiddenServicePort 80 127.0.0.1:''' + str(self.hsPort) + '''
return return
os.kill(int(pidN), signal.SIGTERM) os.kill(int(pidN), signal.SIGTERM)
os.remove('data/torPid.txt') os.remove('data/torPid.txt')
return

View File

@ -234,4 +234,3 @@ class Onionr:
return return
Onionr() Onionr()

View File

@ -22,9 +22,12 @@ import nacl
class OnionrCrypto: class OnionrCrypto:
def __init__(self): def __init__(self):
return return
def symmetricPeerEncrypt(self, data, key): def symmetricPeerEncrypt(self, data, key):
return return
def symmetricPeerDecrypt(self, data, key): def symmetricPeerDecrypt(self, data, key):
return return
def rsaEncrypt(self, peer, data): def rsaEncrypt(self, peer, data):
return return

View File

@ -32,15 +32,22 @@ class OnionrUtils:
self._core = coreInstance self._core = coreInstance
return return
def localCommand(self, command): def localCommand(self, command):
'''Send a command to the local http API server, securely. Intended for local clients, DO NOT USE for remote peers.''' '''
Send a command to the local http API server, securely. Intended for local clients, DO NOT USE for remote peers.
'''
config = configparser.ConfigParser() config = configparser.ConfigParser()
if os.path.exists('data/config.ini'): if os.path.exists('data/config.ini'):
config.read('data/config.ini') config.read('data/config.ini')
else: else:
return return
requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config['CLIENT']['PORT']) + '/client/?action=' + command + '&token=' + config['CLIENT']['CLIENT HMAC']) requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config['CLIENT']['PORT']) + '/client/?action=' + command + '&token=' + config['CLIENT']['CLIENT HMAC'])
return
def getPassword(self, message='Enter password: ', confirm = True): def getPassword(self, message='Enter password: ', confirm = True):
'''Get a password without showing the users typing and confirm the input''' '''
Get a password without showing the users typing and confirm the input
'''
# Get a password safely with confirmation and return it # Get a password safely with confirmation and return it
while True: while True:
print(message) print(message)
@ -55,9 +62,13 @@ class OnionrUtils:
break break
else: else:
break break
return pass1 return pass1
def checkPort(self, port, host = ''):
'''Checks if a port is available, returns bool''' def checkPort(self, port, host=''):
'''
Checks if a port is available, returns bool
'''
# inspired by https://www.reddit.com/r/learnpython/comments/2i4qrj/how_to_write_a_python_script_that_checks_to_see/ckzarux/ # inspired by https://www.reddit.com/r/learnpython/comments/2i4qrj/how_to_write_a_python_script_that_checks_to_see/ckzarux/
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
retVal = False retVal = False
@ -68,36 +79,49 @@ class OnionrUtils:
retVal = True retVal = True
finally: finally:
sock.close() sock.close()
return retVal return retVal
def checkIsIP(self, ip): def checkIsIP(self, ip):
'''Check if a string is a valid ipv4 address''' '''
Check if a string is a valid IPv4 address
'''
try: try:
socket.inet_aton(ip) socket.inet_aton(ip)
except: except:
return False return False
else: else:
return True return True
def exportMyPubkey(self): def exportMyPubkey(self):
'''Export our PGP key if it exists''' '''
Export our PGP key if it exists
'''
if not os.path.exists(self.fingerprintFile): if not os.path.exists(self.fingerprintFile):
raise Exception("No fingerprint found, cannot export our PGP key.") raise Exception("No fingerprint found, cannot export our PGP key.")
gpg = gnupg.GPG(homedir='./data/pgp/') gpg = gnupg.GPG(homedir='./data/pgp/')
with open(self.fingerprintFile,'r') as f: with open(self.fingerprintFile,'r') as f:
fingerprint = f.read() fingerprint = f.read()
ascii_armored_public_keys = gpg.export_keys(fingerprint) ascii_armored_public_keys = gpg.export_keys(fingerprint)
return ascii_armored_public_keys return ascii_armored_public_keys
def getBlockDBHash(self): def getBlockDBHash(self):
'''Return a sha3_256 hash of the blocks DB''' '''
Return a sha3_256 hash of the blocks DB
'''
with open(self._core.blockDB, 'rb') as data: with open(self._core.blockDB, 'rb') as data:
data = data.read() data = data.read()
hasher = hashlib.sha3_256() hasher = hashlib.sha3_256()
hasher.update(data) hasher.update(data)
dataHash = hasher.hexdigest() dataHash = hasher.hexdigest()
return dataHash return dataHash
def hasBlock(self, hash): def hasBlock(self, hash):
'''detect if we have a block in the list or not''' '''
Check for new block in the list
'''
conn = sqlite3.connect(self._core.blockDB) conn = sqlite3.connect(self._core.blockDB)
c = conn.cursor() c = conn.cursor()
if not self.validateHash(hash): if not self.validateHash(hash):
@ -113,7 +137,9 @@ class OnionrUtils:
return False return False
def validateHash(self, data, length=64): def validateHash(self, data, length=64):
'''Validate if a string is a valid hex formatted hash''' '''
Validate if a string is a valid hex formatted hash
'''
retVal = True retVal = True
if data == False or data == True: if data == False or data == True:
return False return False
@ -125,9 +151,13 @@ class OnionrUtils:
int(data, 16) int(data, 16)
except ValueError: except ValueError:
retVal = False retVal = False
return retVal return retVal
def validateID(self, id): def validateID(self, id):
'''validate if a user ID is a valid tor or i2p hidden service''' '''
Validate if a user ID is a valid tor or i2p hidden service
'''
idLength = len(id) idLength = len(id)
retVal = True retVal = True
idNoDomain = '' idNoDomain = ''
@ -165,4 +195,5 @@ class OnionrUtils:
retVal = False retVal = False
if not idNoDomain.isalnum(): if not idNoDomain.isalnum():
retVal = False retVal = False
return retVal return retVal

View File

@ -23,6 +23,7 @@ class OnionrTests(unittest.TestCase):
self.assertTrue(False) self.assertTrue(False)
else: else:
self.assertTrue(True) self.assertTrue(True)
def testNone(self): def testNone(self):
logger.debug('--------------------------') logger.debug('--------------------------')
logger.info('Running simple program run test...') logger.info('Running simple program run test...')
@ -32,6 +33,7 @@ class OnionrTests(unittest.TestCase):
self.assertTrue(False) self.assertTrue(False)
else: else:
self.assertTrue(True) self.assertTrue(True)
def testPeer_a_DBCreation(self): def testPeer_a_DBCreation(self):
logger.debug('--------------------------') logger.debug('--------------------------')
logger.info('Running peer db creation test...') logger.info('Running peer db creation test...')
@ -44,6 +46,7 @@ class OnionrTests(unittest.TestCase):
self.assertTrue(True) self.assertTrue(True)
else: else:
self.assertTrue(False) self.assertTrue(False)
def testPeer_b_addPeerToDB(self): def testPeer_b_addPeerToDB(self):
logger.debug('--------------------------') logger.debug('--------------------------')
logger.info('Running peer db insertion test...') logger.info('Running peer db insertion test...')
@ -55,6 +58,7 @@ class OnionrTests(unittest.TestCase):
self.assertTrue(True) self.assertTrue(True)
else: else:
self.assertTrue(False) self.assertTrue(False)
def testData_b_Encrypt(self): def testData_b_Encrypt(self):
self.assertTrue(True) self.assertTrue(True)
return return
@ -67,6 +71,7 @@ class OnionrTests(unittest.TestCase):
self.assertTrue(True) self.assertTrue(True)
else: else:
self.assertTrue(False) self.assertTrue(False)
def testData_a_Decrypt(self): def testData_a_Decrypt(self):
self.assertTrue(True) self.assertTrue(True)
return return
@ -79,6 +84,7 @@ class OnionrTests(unittest.TestCase):
self.assertTrue(True) self.assertTrue(True)
else: else:
self.assertTrue(False) self.assertTrue(False)
def testPGPGen(self): def testPGPGen(self):
logger.debug('--------------------------') logger.debug('--------------------------')
logger.info('Running PGP key generation test...') logger.info('Running PGP key generation test...')
@ -93,6 +99,7 @@ class OnionrTests(unittest.TestCase):
myCore.generateMainPGP(torID) myCore.generateMainPGP(torID)
if os.path.exists('data/pgp/'): if os.path.exists('data/pgp/'):
self.assertTrue(True) self.assertTrue(True)
def testHMACGen(self): def testHMACGen(self):
logger.debug('--------------------------') logger.debug('--------------------------')
logger.info('Running HMAC generation test...') logger.info('Running HMAC generation test...')
@ -104,6 +111,7 @@ class OnionrTests(unittest.TestCase):
self.assertTrue(True) self.assertTrue(True)
else: else:
self.assertTrue(False) self.assertTrue(False)
def testQueue(self): def testQueue(self):
logger.debug('--------------------------') logger.debug('--------------------------')
logger.info('Running daemon queue test...') logger.info('Running daemon queue test...')
@ -124,4 +132,5 @@ class OnionrTests(unittest.TestCase):
if command[0] == 'testCommand': if command[0] == 'testCommand':
if myCore.daemonQueue() == False: if myCore.daemonQueue() == False:
logger.info('Succesfully added and read command') logger.info('Succesfully added and read command')
unittest.main() unittest.main()

View File

@ -16,12 +16,12 @@ import hmac, base64, time, math
class TimedHMAC: class TimedHMAC:
def __init__(self, base64Key, data, hashAlgo): def __init__(self, base64Key, data, hashAlgo):
''' '''
base64Key = base64 encoded key base64Key = base64 encoded key
data = data to hash data = data to hash
expire = time expiry in epoch expire = time expiry in epoch
hashAlgo = string in hashlib.algorithms_available hashAlgo = string in hashlib.algorithms_available
Maximum of 10 seconds grace period Maximum of 10 seconds grace period
''' '''
self.data = data self.data = data
self.expire = math.floor(time.time()) self.expire = math.floor(time.time())
@ -30,6 +30,7 @@ class TimedHMAC:
generatedHMAC = hmac.HMAC(base64.b64decode(base64Key).decode(), digestmod=self.hashAlgo) generatedHMAC = hmac.HMAC(base64.b64decode(base64Key).decode(), digestmod=self.hashAlgo)
generatedHMAC.update(data + expire) generatedHMAC.update(data + expire)
self.HMACResult = generatedHMAC.hexdigest() self.HMACResult = generatedHMAC.hexdigest()
return return
def check(self, data): def check(self, data):