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

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

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ onionr/*.pyc
onionr/*.log onionr/*.log
onionr/data/hs/hostname onionr/data/hs/hostname
onionr/data/* onionr/data/*
onionr/gnupg/*

12
ISSUE_TEMPLATE.md Normal file
View File

@ -0,0 +1,12 @@
# Expected Behavior
# Actual Behavior
# Steps to Reproduce
# Version Information
Onionr:
OS:
Python:
Tor:
I2P:

View File

@ -1,74 +1,50 @@
# Onionr Protocol Spec # Onionr Protocol Spec v2
A social network/microblogging platform for Tor & I2P A P2P platform for Tor & I2P
Draft Dec 25 2017
notes:
Use Blowfish in addition with AES?
# Overview # Overview
Onionr is an encrypted microblogging & mailing system designed in the spirit of Twitter. Onionr is an encrypted microblogging & mailing system designed in the spirit of Twitter.
There are no central servers and all traffic is peer to peer by default (routed via Tor or I2P). There are no central servers and all traffic is peer to peer by default (routed via Tor or I2P).
User IDs are simply Tor onion service/I2P host id + PGP fingerprint. User IDs are simply Tor onion service/I2P host id + Ed25519 key fingerprint.
Clients consolidate feeds from peers into 1 “timeline” using RSS format. Private blocks are only able to be read by the intended peer.
Private messages are only accessible by the intended peer based on the PGP id. All traffic is over Tor/I2P, connecting only to Tor onion and I2P hidden services.
Onionr is not intended to be a replacement for Ricochet, OnionShare, or Briar.
All traffic is over onion/I2P because if only some was, then that would make that traffic inherently suspicious.
## Goals: ## Goals:
• Selective sharing of information with friends & public • Selective sharing of information
• Secure & semi-anonymous direct messaging • Secure & semi-anonymous direct messaging
• Forward secrecy • Forward secrecy
• Defense in depth • Defense in depth
• Data should be secure for years to come, quantum safe (though not necessarily every “layer”) • Data should be secure for years to come
• Decentralization • Decentralization
* Avoid browser-based exploits that plague similar software * Avoid browser-based exploits that plague similar software
* Avoid timing attacks & unexpected metadata leaks * Avoid timing attacks & unexpected metadata leaks
## Assumptions:
• Tor & I2Ps transport protocols & AES-256 are not broken, sha3-512 2nd preimage attacks will remain infeasible indefinitely
• All traffic is logged indefinitely by powerful adversaries
## Protocol ## Protocol
Clients MUST use HTTP(s) to communicate with one another to maintain compatibility cross platform. HTTPS is recommended, but HTTP is acceptable because Tor & I2P provide transport layer security.
Onionr nodes use HTTP (over Tor/I2P) to exchange keys, metadata, and blocks. Blocks are identified by their sha3_256 hash. Nodes sync a table of blocks hashes and attempt to download blocks they do not yet have from random peers.
Blocks may be encrypted using Curve25519.
## Connections ## Connections
When a node first comes online, it attempts to bootstrap using a default list provided by a client.
When two peers connect, they exchange PGP public keys and then generate a shared AES-SHA3-512 HMAC token. These keys are stored in a peer database until expiry.
HMAC tokens are regenerated either every X many communications with a peer or every X minutes. Every 10 communications or every 24 hours is a recommended default.
All valid requests with HMAC should be recorded until used HMAC's expiry to prevent replay attacks.
Peer Types
* Friends:
* Encrypted friends only posts to one another
* Usually less strict rate & storage limits
* OPTIONALLY sign one anothers keys. Users may not want to do this in order to avoid exposing their entire friends list.
• Strangers:
* Used for storage of encrypted or public information
* Can only read public posts
* Usually stricter rate & storage limits
## Data Storage/Delivery
Posts (public or friends only) are stored across the network. When a node first comes online, it attempts to bootstrap using a default list provided by a client.
Private messages SHOULD be delivered directly if both peers are online, otherwise stored in the network. When two peers connect, they exchange Ed25519 keys (if applicable) then Salsa20 keys.
Data SHOULD be stored in an entirely encrypted state when a client is offline, including metadata. Data SHOULD be stored in a minimal size with garbage data to ensure some level of plausible deniablity.
Data SHOULD be stored as long as the nodes user prefers and only erased once disk quota is reached due to new data.
Posts
Posts can contain text and images. All posts MUST be time stamped.
Images SHOULD not be displayed by non-friends by default, to prevent unwanted viewing of offensive material & to reduce attack surface.
All received posts must be verified to be stored and/or displayed to the user.
All data being transfered MUST be encrypted to the end node receiving the data, then the data MUST be encrypted the node(s) transporting/storing the data, Salsa20 keys are regenerated either every X many communications with a peer or every X minutes.
Posts have two settings: Every 100kb or every 2 hours is a recommended default.
• Friends only:
◦ Posts MUST be encrypted to all trusted peers via AES256-HMAC-SHA256 and PGP signed (signed before encryption) and time stamped to prevent replaying. A temporary RSA key for use in every post (or message) is exchanged every X many configured post (or message), for use in addition with PGP and the HMAC.
• Public:
◦ Posts MUST be PGP signed, and MUST NOT use any encryption.
## Private Messages
Private messages are messages that can have attached images. They MUST be encrypted via AES256-HMAC-SHA256 and PGP signed (signed before encryption) and time stamped to prevent replaying. A temporary RSA key for use in every message is exchanged every X many configured messages (or posts), for use in addition with PGP and the HMAC. All valid requests with HMAC should be recorded until used HMAC's expiry to prevent replay attacks.
When both peers are online messages SHOULD be dispatched directly between peers. Peer Types
All messages must be verified prior to being displayed. * Friends:
* Encrypted friends only posts to one another
* Usually less strict rate & storage limits
* Strangers:
* Used for storage of encrypted or public information
* Can only read public posts
* Usually stricter rate & storage limits
Clients SHOULD allow configurable message padding.
## Spam mitigation ## Spam mitigation
To send or receive data, a node can optionally request that the other node generate a hash that when in hexadecimal representation contains a random string at a random location in the string. Clients will configure what difficulty to request, and what difficulty is acceptable for themselves to perform. Difficulty should correlate with recent network & disk usage and data size. Friends can be configured to have less strict (to non existent) limits, separately from strangers. (proof of work). To send or receive data, a node can optionally request that the other node generate a hash that when in hexadecimal representation contains a random string at a random location in the string. Clients will configure what difficulty to request, and what difficulty is acceptable for themselves to perform. Difficulty should correlate with recent network & disk usage and data size. Friends can be configured to have less strict (to non existent) limits, separately from strangers. (proof of work).

View File

@ -20,10 +20,10 @@
import flask import flask
from flask import request, Response, abort from flask import request, Response, abort
from multiprocessing import Process 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 from core import Core
import onionrutils import onionrutils, onionrcrypto
class API: class API:
''' '''
Main HTTP API (Flask) Main HTTP API (Flask)
@ -47,7 +47,7 @@ class API:
if os.path.exists('dev-enabled'): if os.path.exists('dev-enabled'):
self._developmentMode = True self._developmentMode = True
logger.set_level(logger.LEVEL_DEBUG) 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: else:
self._developmentMode = False self._developmentMode = False
logger.set_level(logger.LEVEL_INFO) logger.set_level(logger.LEVEL_INFO)
@ -56,6 +56,7 @@ class API:
self.debug = debug self.debug = debug
self._privateDelayTime = 3 self._privateDelayTime = 3
self._core = Core() self._core = Core()
self._crypto = onionrcrypto.OnionrCrypto(self._core)
self._utils = onionrutils.OnionrUtils(self._core) self._utils = onionrutils.OnionrUtils(self._core)
app = flask.Flask(__name__) app = flask.Flask(__name__)
bindPort = int(self.config['CLIENT']['PORT']) bindPort = int(self.config['CLIENT']['PORT'])
@ -131,14 +132,14 @@ class API:
pass pass
elif action == 'ping': elif action == 'ping':
resp = Response("pong!") resp = Response("pong!")
elif action == 'setHMAC': elif action == 'getHMAC':
pass resp = Response(self._crypto.generateSymmetric())
elif action == 'getSymmetric':
resp = Response(self._crypto.generateSymmetric())
elif action == 'getDBHash': elif action == 'getDBHash':
resp = Response(self._utils.getBlockDBHash()) resp = Response(self._utils.getBlockDBHash())
elif action == 'getBlockHashes': elif action == 'getBlockHashes':
resp = Response(self._core.getBlockList()) resp = Response(self._core.getBlockList())
elif action == 'getPGP':
resp = Response(self._utils.exportMyPubkey())
# setData should be something the communicator initiates, not this api # setData should be something the communicator initiates, not this api
elif action == 'getData': elif action == 'getData':
resp = self._core.getData(data) resp = self._core.getData(data)
@ -171,9 +172,9 @@ class API:
resp = Response("Invalid request") resp = Response("Invalid request")
return resp return resp
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
logger.info('Starting client on ' + self.host + ':' + str(bindPort) + '...') logger.info('Starting client on ' + self.host + ':' + str(bindPort) + '...')
logger.debug('Client token: ' + logger.colors.underline + self.clientToken) logger.debug('Client token: ' + logger.colors.underline + self.clientToken)
app.run(host=self.host, port=bindPort, debug=True, threaded=True) 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/>. 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 sqlite3, requests, hmac, hashlib, time, sys, os, math, logger, urllib.parse
import core, onionrutils import core, onionrutils, onionrcrypto
class OnionrCommunicate: class OnionrCommunicate:
def __init__(self, debug, developmentMode): def __init__(self, debug, developmentMode):
@ -31,6 +31,7 @@ class OnionrCommunicate:
''' '''
self._core = core.Core() self._core = core.Core()
self._utils = onionrutils.OnionrUtils(self._core) self._utils = onionrutils.OnionrUtils(self._core)
self._crypto = onionrcrypto.OnionrCrypto(self._core)
blockProcessTimer = 0 blockProcessTimer = 0
blockProcessAmount = 5 blockProcessAmount = 5
heartBeatTimer = 0 heartBeatTimer = 0
@ -40,13 +41,6 @@ class OnionrCommunicate:
self.peerData = {} # Session data for peers (recent reachability, speed, etc) 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): if os.path.exists(self._core.queueDB):
self._core.clearDaemonQueue() self._core.clearDaemonQueue()
while True: while True:
@ -70,39 +64,6 @@ class OnionrCommunicate:
return 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): def lookupBlocks(self):
''' '''
Lookup blocks and merge new ones Lookup blocks and merge new ones

View File

@ -17,12 +17,12 @@
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, os, sys, time, math, gnupg, base64, tarfile, getpass, simplecrypt, hashlib, nacl, logger import sqlite3, os, sys, time, math, base64, tarfile, getpass, simplecrypt, hashlib, nacl, logger
from Crypto.Cipher import AES #from Crypto.Cipher import AES
from Crypto import Random #from Crypto import Random
import netcontroller import netcontroller
import onionrutils import onionrutils, onionrcrypto
if sys.version_info < (3, 6): if sys.version_info < (3, 6):
try: try:
@ -38,40 +38,20 @@ class Core:
''' '''
self.queueDB = 'data/queue.db' self.queueDB = 'data/queue.db'
self.peerDB = 'data/peers.db' self.peerDB = 'data/peers.db'
self.ownPGPID = ''
self.blockDB = 'data/blocks.db' self.blockDB = 'data/blocks.db'
self.blockDataLocation = 'data/blocks/' self.blockDataLocation = 'data/blocks/'
self._utils = onionrutils.OnionrUtils(self) self.addressDB = 'data/address.db'
if not os.path.exists('data/'): if not os.path.exists('data/'):
os.mkdir('data/') os.mkdir('data/')
if not os.path.exists('data/blocks/'): if not os.path.exists('data/blocks/'):
os.mkdir('data/blocks/') os.mkdir('data/blocks/')
if not os.path.exists(self.blockDB): if not os.path.exists(self.blockDB):
self.createBlockDB() self.createBlockDB()
return self._utils = onionrutils.OnionrUtils(self)
# Initialize the crypto object
def generateMainPGP(self, myID): self._crypto = onionrcrypto.OnionrCrypto(self)
'''
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()
return return
@ -82,7 +62,7 @@ class Core:
DOES NO SAFETY CHECKS if the ID is valid, but prepares the insertion 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.validatePubKey(peerID):
return False return False
conn = sqlite3.connect(self.peerDB) conn = sqlite3.connect(self.peerDB)
c = conn.cursor() c = conn.cursor()
@ -92,6 +72,29 @@ class Core:
conn.close() conn.close()
return True 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): 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.
@ -102,8 +105,7 @@ class Core:
c.execute('''CREATE TABLE peers( c.execute('''CREATE TABLE peers(
ID text not null, ID text not null,
name text, name text,
pgpKey text, adders text,
hmacKey text,
blockDBHash text, blockDBHash text,
forwardKey text, forwardKey text,
dateSeen not null, dateSeen not null,
@ -112,7 +114,6 @@ class Core:
''') ''')
conn.commit() conn.commit()
conn.close() conn.close()
return return
def createBlockDB(self): def createBlockDB(self):
@ -300,14 +301,6 @@ class Core:
return 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): def listPeers(self, randomOrder=True):
''' '''
Return a list of peers Return a list of peers
@ -322,7 +315,7 @@ class Core:
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[2])
conn.close() conn.close()
return peerList return peerList
@ -333,18 +326,17 @@ class Core:
id text 0 id text 0
name text, 1 name text, 1
pgpKey text, 2 adders text, 2
hmacKey text, 3 blockDBHash text, 3
blockDBHash text, 4 forwardKey text, 4
forwardKey text, 5 dateSeen not null, 5
dateSeen not null, 7 bytesStored int, 6
bytesStored int, 8 trust int 7
trust int 9
''' '''
conn = sqlite3.connect(self.peerDB) conn = sqlite3.connect(self.peerDB)
c = conn.cursor() c = conn.cursor()
command = (peer,) 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] info = infoNumbers[info]
iterCount = 0 iterCount = 0
retVal = '' retVal = ''
@ -367,7 +359,7 @@ class Core:
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', 'name', 'pubkey', '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()

View File

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

View File

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

View File

@ -17,17 +17,51 @@
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 nacl import nacl.signing, nacl.encoding, nacl.public, os
class OnionrCrypto: 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 return
def symmetricPeerEncrypt(self, data, key): def pubKeyEncrypt(self, data, peer):
'''Encrypt to a peers public key (Curve25519, taken from Ed25519 pubkey)'''
return return
def symmetricPeerDecrypt(self, data, key): def pubKeyEncrypt(self, data, peer):
'''pubkey decrypt (Curve25519, taken from Ed25519 pubkey)'''
return return
def rsaEncrypt(self, peer, data): def symmetricPeerEncrypt(self, data):
'''Salsa20 encrypt data to peer (with mac)'''
return 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/>. 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, 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): if sys.version_info < (3, 6):
try: try:
import sha3 import sha3
@ -93,19 +94,6 @@ class OnionrUtils:
else: else:
return True 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): def getBlockDBHash(self):
''' '''
Return a sha3_256 hash of the blocks DB Return a sha3_256 hash of the blocks DB
@ -154,9 +142,21 @@ class OnionrUtils:
return retVal 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): 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) idLength = len(id)
retVal = True retVal = True

View File

@ -54,7 +54,7 @@ class OnionrTests(unittest.TestCase):
myCore = core.Core() myCore = core.Core()
if not os.path.exists('data/peers.db'): if not os.path.exists('data/peers.db'):
myCore.createPeerDB() 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) self.assertTrue(True)
else: else:
self.assertTrue(False) self.assertTrue(False)
@ -85,33 +85,6 @@ class OnionrTests(unittest.TestCase):
else: else:
self.assertTrue(False) 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): def testQueue(self):
logger.debug('--------------------------') logger.debug('--------------------------')
logger.info('Running daemon queue test...') logger.info('Running daemon queue test...')

View File

@ -2,17 +2,27 @@
[![Build Status](https://travis-ci.org/beardog108/onionr.svg?branch=master)](https://travis-ci.org/beardog108/onionr) [![Build Status](https://travis-ci.org/beardog108/onionr.svg?branch=master)](https://travis-ci.org/beardog108/onionr)
P2P microblogging platform and social network, using Tor & I2P. P2P platform, using Tor & I2P.
Major work in progress. Major work in progress.
***THIS SOFTWARE IS NOT USABLE OR SAFE YET.*** ***THIS SOFTWARE IS NOT USABLE OR SAFE YET.***
**Roadmap/features:**
* [X] Fully p2p/decentralized, no trackers or other single points of failure
* [X] High level of anonymity
* [ ] End to end encryption where applicable
* [X] Optional non-encrypted blocks, useful for blog posts or public file sharing
* [ ] Easy API system for integration to websites
# Development # Development
This software is in heavy development. If for some reason you want to get involved, get in touch first. This software is in heavy development. If for some reason you want to get involved, get in touch first.
**Onionr API and functionality is subject to non-backwards compatible change during development**
## Disclaimer ## Disclaimer
The Tor Project, I2P developers, and anyone else do not own, create, or endorse this project, and are not otherwise involved. The Tor Project, I2P developers, and anyone else do not own, create, or endorse this project, and are not otherwise involved.

View File

@ -1,10 +1,7 @@
PyNaCl==1.2.1 PyNaCl==1.2.1
gnupg==2.3.1 requests==2.12.4
Flask==0.12.2 Flask==0.12.2
requests==2.18.4
urllib3==1.22
simple_crypt==4.1.7 simple_crypt==4.1.7
urllib3==1.19.1
sha3==0.2.1 sha3==0.2.1
pycrypto==2.6.1
pynacl==1.2.1
PySocks==1.6.8 PySocks==1.6.8

5
reset.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
echo "RESETING ONIONR"
rm onionr/data/blocks/*.dat
rm onionr/data/peers.db
rm onionr/data/blocks.db