Merge branch 'crypto' of https://github.com/beardog108/onionr into crypto

This commit is contained in:
Arinerron 2018-02-21 22:42:04 -08:00
commit 005273b52c
14 changed files with 196 additions and 213 deletions

View file

@ -20,10 +20,10 @@
import flask
from flask import request, Response, abort
from multiprocessing import Process
import configparser, sys, random, threading, hmac, hashlib, base64, time, math, gnupg, os, logger
import configparser, sys, random, threading, hmac, hashlib, base64, time, math, os, logger
from core import Core
import onionrutils
import onionrutils, onionrcrypto
class API:
'''
Main HTTP API (Flask)
@ -47,7 +47,7 @@ class API:
if os.path.exists('dev-enabled'):
self._developmentMode = True
logger.set_level(logger.LEVEL_DEBUG)
logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
#logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
else:
self._developmentMode = False
logger.set_level(logger.LEVEL_INFO)
@ -56,6 +56,7 @@ class API:
self.debug = debug
self._privateDelayTime = 3
self._core = Core()
self._crypto = onionrcrypto.OnionrCrypto(self._core)
self._utils = onionrutils.OnionrUtils(self._core)
app = flask.Flask(__name__)
bindPort = int(self.config['CLIENT']['PORT'])
@ -131,14 +132,14 @@ class API:
pass
elif action == 'ping':
resp = Response("pong!")
elif action == 'setHMAC':
pass
elif action == 'getHMAC':
resp = Response(self._crypto.generateSymmetric())
elif action == 'getSymmetric':
resp = Response(self._crypto.generateSymmetric())
elif action == 'getDBHash':
resp = Response(self._utils.getBlockDBHash())
elif action == 'getBlockHashes':
resp = Response(self._core.getBlockList())
elif action == 'getPGP':
resp = Response(self._utils.exportMyPubkey())
# setData should be something the communicator initiates, not this api
elif action == 'getData':
resp = self._core.getData(data)
@ -171,9 +172,9 @@ class API:
resp = Response("Invalid request")
return resp
logger.info('Starting client on ' + self.host + ':' + str(bindPort) + '...')
logger.debug('Client token: ' + logger.colors.underline + self.clientToken)
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
logger.info('Starting client on ' + self.host + ':' + str(bindPort) + '...')
logger.debug('Client token: ' + logger.colors.underline + self.clientToken)
app.run(host=self.host, port=bindPort, debug=True, threaded=True)

View file

@ -20,7 +20,7 @@ and code to operate as a daemon, getting commands from the command queue databas
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import sqlite3, requests, hmac, hashlib, time, sys, os, math, logger, urllib.parse
import core, onionrutils
import core, onionrutils, onionrcrypto
class OnionrCommunicate:
def __init__(self, debug, developmentMode):
@ -31,6 +31,7 @@ class OnionrCommunicate:
'''
self._core = core.Core()
self._utils = onionrutils.OnionrUtils(self._core)
self._crypto = onionrcrypto.OnionrCrypto(self._core)
blockProcessTimer = 0
blockProcessAmount = 5
heartBeatTimer = 0
@ -40,13 +41,6 @@ class OnionrCommunicate:
self.peerData = {} # Session data for peers (recent reachability, speed, etc)
# get our own PGP fingerprint
fingerprintFile = 'data/own-fingerprint.txt'
if not os.path.exists(fingerprintFile):
self._core.generateMainPGP(torID)
with open(fingerprintFile,'r') as f:
self.pgpOwnFingerprint = f.read()
logger.info('My PGP fingerprint is ' + logger.colors.underline + self.pgpOwnFingerprint + logger.colors.reset + logger.colors.fg.green + '.')
if os.path.exists(self._core.queueDB):
self._core.clearDaemonQueue()
while True:
@ -69,40 +63,7 @@ class OnionrCommunicate:
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
'''
url = 'http://' + peerID + '/public/?action=getPGP'
r = requests.get(url, headers=headers)
response = r.text
return response
def shareHMAC(self, peerID, key):
'''
This function shares an HMAC key to a peer
'''
return
def getPeerProof(self, peerID):
'''
This function gets the current peer proof requirement
'''
return
def sendPeerProof(self, peerID, data):
'''
This function sends the proof result to a peer previously fetched with getPeerProof
'''
return
def lookupBlocks(self):
'''
Lookup blocks and merge new ones

View file

@ -17,12 +17,12 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import sqlite3, os, sys, time, math, gnupg, base64, tarfile, getpass, simplecrypt, hashlib, nacl, logger
from Crypto.Cipher import AES
from Crypto import Random
import sqlite3, os, sys, time, math, base64, tarfile, getpass, simplecrypt, hashlib, nacl, logger
#from Crypto.Cipher import AES
#from Crypto import Random
import netcontroller
import onionrutils
import onionrutils, onionrcrypto
if sys.version_info < (3, 6):
try:
@ -38,40 +38,20 @@ class Core:
'''
self.queueDB = 'data/queue.db'
self.peerDB = 'data/peers.db'
self.ownPGPID = ''
self.blockDB = 'data/blocks.db'
self.blockDataLocation = 'data/blocks/'
self._utils = onionrutils.OnionrUtils(self)
self.addressDB = 'data/address.db'
if not os.path.exists('data/'):
os.mkdir('data/')
if not os.path.exists('data/blocks/'):
os.mkdir('data/blocks/')
if not os.path.exists(self.blockDB):
self.createBlockDB()
return
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
'''
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)
key = gpg.gen_key(input_data)
logger.info("Generating PGP key, this will take some time..")
while key.status != "key created":
time.sleep(0.5)
print(key.status)
logger.info("Finished generating PGP key")
# Write the key
myFingerpintFile = open('data/own-fingerprint.txt', 'w')
myFingerpintFile.write(key.fingerprint)
myFingerpintFile.close()
self._utils = onionrutils.OnionrUtils(self)
# Initialize the crypto object
self._crypto = onionrcrypto.OnionrCrypto(self)
return
@ -82,7 +62,7 @@ class Core:
DOES NO SAFETY CHECKS if the ID is valid, but prepares the insertion
'''
# This function simply adds a peer to the DB
if not self._utils.validateID(peerID):
if not self._utils.validatePubKey(peerID):
return False
conn = sqlite3.connect(self.peerDB)
c = conn.cursor()
@ -91,6 +71,29 @@ class Core:
conn.commit()
conn.close()
return True
def createAddressDB(self):
'''
Generate the address database
types:
1: I2P b32 address
2: Tor v2 (like facebookcorewwwi.onion)
3: Tor v3
'''
conn = sqlite3.connect(self.addressDB)
c = conn.cursor()
c.execute('''CREATE TABLE adders(
address text,
type int,
knownPeer text,
speed int,
success int,
failure int
);
''')
conn.commit()
conn.close()
def createPeerDB(self):
'''
@ -102,8 +105,7 @@ class Core:
c.execute('''CREATE TABLE peers(
ID text not null,
name text,
pgpKey text,
hmacKey text,
adders text,
blockDBHash text,
forwardKey text,
dateSeen not null,
@ -112,7 +114,6 @@ class Core:
''')
conn.commit()
conn.close()
return
def createBlockDB(self):
@ -300,14 +301,6 @@ class Core:
return
def generateHMAC(self, length=32):
'''
Generate and return an HMAC key
'''
key = base64.b64encode(os.urandom(length))
return key
def listPeers(self, randomOrder=True):
'''
Return a list of peers
@ -322,7 +315,7 @@ class Core:
peers = c.execute('SELECT * FROM peers;')
peerList = []
for i in peers:
peerList.append(i[0])
peerList.append(i[2])
conn.close()
return peerList
@ -333,18 +326,17 @@ class Core:
id text 0
name text, 1
pgpKey text, 2
hmacKey text, 3
blockDBHash text, 4
forwardKey text, 5
dateSeen not null, 7
bytesStored int, 8
trust int 9
adders text, 2
blockDBHash text, 3
forwardKey text, 4
dateSeen not null, 5
bytesStored int, 6
trust int 7
'''
conn = sqlite3.connect(self.peerDB)
c = conn.cursor()
command = (peer,)
infoNumbers = {'id': 0, 'name': 1, 'pgpKey': 2, 'hmacKey': 3, 'blockDBHash': 4, 'forwardKey': 5, 'dateSeen': 6, 'bytesStored': 7, 'trust': 8}
infoNumbers = {'id': 0, 'name': 1, 'adders': 2, 'blockDBHash': 3, 'forwardKey': 4, 'dateSeen': 5, 'bytesStored': 6, 'trust': 7}
info = infoNumbers[info]
iterCount = 0
retVal = ''
@ -367,7 +359,7 @@ class Core:
c = conn.cursor()
command = (data, peer)
# TODO: validate key on whitelist
if key not in ('id', 'text', 'name', 'pgpKey', 'hmacKey', 'blockDBHash', 'forwardKey', 'dateSeen', 'bytesStored', 'trust'):
if key not in ('id', 'name', 'pubkey', 'blockDBHash', 'forwardKey', 'dateSeen', 'bytesStored', 'trust'):
raise Exception("Got invalid database key when setting peer info")
c.execute('UPDATE peers SET ' + key + ' = ? WHERE id=?', command)
conn.commit()

View file

@ -103,7 +103,12 @@ HiddenServicePort 80 127.0.0.1:''' + str(self.hsPort) + '''
int(pidN)
except:
return
os.kill(int(pidN), signal.SIGTERM)
os.remove('data/torPid.txt')
try:
os.kill(int(pidN), signal.SIGTERM)
os.remove('data/torPid.txt')
except ProcessLookupError:
pass
except FileNotFoundError:
pass
return

View file

@ -47,7 +47,6 @@ class Onionr:
if os.path.exists('dev-enabled'):
self._developmentMode = True
logger.set_level(logger.LEVEL_DEBUG)
logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
else:
self._developmentMode = False
logger.set_level(logger.LEVEL_INFO)
@ -128,6 +127,7 @@ class Onionr:
'addmessage': self.addMessage,
'add-msg': self.addMessage,
'add-message': self.addMessage,
'pm': self.sendEncrypt,
'gui': self.openGUI,
'addpeer': self.addPeer,
'add-peer': self.addPeer
@ -150,6 +150,19 @@ class Onionr:
logger.info('Onionr ' + ONIONR_VERSION + ' (' + platform.machine() + ') : API v' + API_VERSION)
logger.info('Running on ' + platform.platform() + ' ' + platform.release())
def sendEncrypt(self):
'''Create a private message and send it'''
while True:
peer = logger.readline('Peer to send to: ')
if self.onionrUtils.validateID(peer):
break
else:
logger.error('Invalid peer ID')
message = logger.readline("Enter a message: ")
logger.info("Sending message to " + peer)
self.onionrUtils.sendPM(peer, message)
def openGUI(self):
gui.OnionrGUI(self.onionrCore)
@ -197,11 +210,14 @@ class Onionr:
def daemon(self):
''' Start the Onionr communication daemon '''
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
if self._developmentMode:
logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
net = NetController(self.config['CLIENT']['PORT'])
logger.info('Tor is starting...')
if not net.startTor():
sys.exit(1)
logger.info('Started Tor .onion service: ' + logger.colors.underline + net.myID)
logger.info('Our Public key: ' + self.onionrCore._crypto.pubKey)
time.sleep(1)
subprocess.Popen(["./communicator.py", "run", str(net.socksPort)])
logger.debug('Started communicator')

View file

@ -17,17 +17,51 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import nacl
import nacl.signing, nacl.encoding, nacl.public, os
class OnionrCrypto:
def __init__(self):
def __init__(self, coreInstance):
self._core = coreInstance
self._keyFile = 'data/keys.txt'
self.pubKey = None
self.privKey = None
# Load our own pub/priv Ed25519 keys, gen & save them if they don't exist
if os.path.exists(self._keyFile):
with open('data/keys.txt', 'r') as keys:
keys = keys.read().split(',')
self.pubKey = keys[0]
self.privKey = keys[1]
else:
keys = self.generatePubKey()
self.pubKey = keys[0]
self.privKey = keys[1]
with open(self._keyFile, 'w') as keyfile:
keyfile.write(self.pubKey + ',' + self.privKey)
return
def symmetricPeerEncrypt(self, data, key):
def pubKeyEncrypt(self, data, peer):
'''Encrypt to a peers public key (Curve25519, taken from Ed25519 pubkey)'''
return
def symmetricPeerDecrypt(self, data, key):
def pubKeyEncrypt(self, data, peer):
'''pubkey decrypt (Curve25519, taken from Ed25519 pubkey)'''
return
def rsaEncrypt(self, peer, data):
def symmetricPeerEncrypt(self, data):
'''Salsa20 encrypt data to peer (with mac)'''
return
def symmetricPeerDecrypt(self, data, peer):
'''Salsa20 decrypt data from peer (with mac)'''
return
def generateSymmetric(self, data, peer):
'''Generate symmetric key'''
return
def generatePubKey(self):
'''Generate a Ed25519 public key pair, return tuple of base64encoded pubkey, privkey'''
private_key = nacl.signing.SigningKey.generate()
public_key = private_key.verify_key.encode(encoder=nacl.encoding.Base32Encoder())
return (public_key.decode(), private_key.encode(encoder=nacl.encoding.Base32Encoder()).decode())

View file

@ -18,7 +18,8 @@
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, configparser, os, socket, gnupg, hashlib, logger, sqlite3
import getpass, sys, requests, configparser, os, socket, hashlib, logger, sqlite3
import nacl.signing, nacl.encoding
if sys.version_info < (3, 6):
try:
import sha3
@ -93,19 +94,6 @@ class OnionrUtils:
else:
return True
def exportMyPubkey(self):
'''
Export our PGP key if it exists
'''
if not os.path.exists(self.fingerprintFile):
raise Exception("No fingerprint found, cannot export our PGP key.")
gpg = gnupg.GPG(homedir='./data/pgp/')
with open(self.fingerprintFile,'r') as f:
fingerprint = f.read()
ascii_armored_public_keys = gpg.export_keys(fingerprint)
return ascii_armored_public_keys
def getBlockDBHash(self):
'''
Return a sha3_256 hash of the blocks DB
@ -153,10 +141,22 @@ class OnionrUtils:
retVal = False
return retVal
def validatePubKey(self, key):
'''Validate if a string is a valid base32 encoded Ed25519 key'''
retVal = False
try:
nacl.signing.SigningKey(seed=key, encoder=nacl.encoding.Base32Encoder)
except nacl.exceptions.ValueError:
pass
else:
retVal = True
return retVal
def validateID(self, id):
'''
Validate if a user ID is a valid tor or i2p hidden service
Validate if an address is a valid tor or i2p hidden service
'''
idLength = len(id)
retVal = True
@ -196,4 +196,4 @@ class OnionrUtils:
if not idNoDomain.isalnum():
retVal = False
return retVal
return retVal

View file

@ -54,7 +54,7 @@ class OnionrTests(unittest.TestCase):
myCore = core.Core()
if not os.path.exists('data/peers.db'):
myCore.createPeerDB()
if myCore.addPeer('2ks5c5bm6zk3ejqg.onion') and not myCore.addPeer('invalidpeer.onion'):
if myCore.addPeer('6M5MXL237OK57ITHVYN5WGHANPGOMKS5C3PJLHBBNKFFJQOIDOJA====') and not myCore.addPeer('NFXHMYLMNFSAU==='):
self.assertTrue(True)
else:
self.assertTrue(False)
@ -85,33 +85,6 @@ class OnionrTests(unittest.TestCase):
else:
self.assertTrue(False)
def testPGPGen(self):
logger.debug('--------------------------')
logger.info('Running PGP key generation test...')
if os.path.exists('data/pgp/'):
self.assertTrue(True)
else:
import core, netcontroller
myCore = core.Core()
net = netcontroller.NetController(1337)
net.startTor()
torID = open('data/hs/hostname').read()
myCore.generateMainPGP(torID)
if os.path.exists('data/pgp/'):
self.assertTrue(True)
def testHMACGen(self):
logger.debug('--------------------------')
logger.info('Running HMAC generation test...')
# Test if hmac key generation is working
import core
myCore = core.Core()
key = myCore.generateHMAC()
if len(key) > 10:
self.assertTrue(True)
else:
self.assertTrue(False)
def testQueue(self):
logger.debug('--------------------------')
logger.info('Running daemon queue test...')