diff --git a/docs/onionr-draft.md b/docs/onionr-draft.md
index 26fbf18f..6fb79cbe 100644
--- a/docs/onionr-draft.md
+++ b/docs/onionr-draft.md
@@ -1,72 +1,49 @@
-# Onionr Protocol Spec
+# Onionr Protocol Spec v2
-A social network/microblogging platform for Tor & I2P
-
-Draft Dec 25 2017
+A P2P platform for Tor & I2P
# Overview
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).
-User IDs are simply Tor onion service/I2P host id + PGP fingerprint.
-Clients consolidate feeds from peers into 1 “timeline” using RSS format.
-Private messages are only accessible by the intended peer based on the PGP id.
-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.
+User IDs are simply Tor onion service/I2P host id + Ed25519 key fingerprint.
+Private blocks are only able to be read by the intended peer.
+All traffic is over Tor/I2P, connecting only to Tor onion and I2P hidden services.
+
## Goals:
- • Selective sharing of information with friends & public
+ • Selective sharing of information
• Secure & semi-anonymous direct messaging
• Forward secrecy
• 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
* Avoid browser-based exploits that plague similar software
* Avoid timing attacks & unexpected metadata leaks
-## Assumptions:
- • Tor & I2P’s 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
-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.
+
## 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 10MB or every 2 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 another’s 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.
- Private messages SHOULD be delivered directly if both peers are online, otherwise stored in the network.
- 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 node’s 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.
+When a node first comes online, it attempts to bootstrap using a default list provided by a client.
+When two peers connect, they exchange Ed25519 keys (if applicable) then Salsa20 keys.
- 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:
- • 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
+Every 100kb or every 2 hours is a recommended default.
- 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 EdDSA key for use in every message is exchanged every X many configured messages (or posts), for use in addition with PGP and the HMAC.
- When both peers are online messages SHOULD be dispatched directly between peers.
- All messages must be verified prior to being displayed.
+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
+ * 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
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).
-Rate limits can be strict, as Onionr is not intended to be an instant messaging application.
+Rate limits can be strict, as Onionr is not intended to be an instant messaging application.
\ No newline at end of file
diff --git a/onionr/api.py b/onionr/api.py
index 92ba30a1..73c65b48 100755
--- a/onionr/api.py
+++ b/onionr/api.py
@@ -20,7 +20,7 @@
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, onionrcrypto
@@ -140,8 +140,6 @@ class API:
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)
diff --git a/onionr/communicator.py b/onionr/communicator.py
index 2e383fb2..7fe5ecd1 100755
--- a/onionr/communicator.py
+++ b/onionr/communicator.py
@@ -40,13 +40,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:
diff --git a/onionr/core.py b/onionr/core.py
index 93a1bc7c..d1a03b31 100644
--- a/onionr/core.py
+++ b/onionr/core.py
@@ -17,7 +17,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
'''
-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 import Random
import netcontroller
@@ -38,10 +38,8 @@ class Core:
'''
self.queueDB = 'data/queue.db'
self.peerDB = 'data/peers.db'
- self.ownPGPID = ''
self.blockDB = 'data/blocks.db'
self.blockDataLocation = 'data/blocks/'
- self.gpgHome = './data/pgp/'
self._utils = onionrutils.OnionrUtils(self)
self._crypto = onionrcrypto.OnionrCrypto(self)
@@ -55,28 +53,6 @@ class Core:
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=self.gpgHome)
- 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
-
def addPeer(self, peerID, name=''):
'''
Add a peer by their ID, with an optional name, to the peer database
@@ -104,8 +80,7 @@ class Core:
c.execute('''CREATE TABLE peers(
ID text not null,
name text,
- pgpKey text,
- hmacKey text,
+ pubkey text,
blockDBHash text,
forwardKey text,
dateSeen not null,
@@ -335,7 +310,6 @@ class Core:
id text 0
name text, 1
- pgpKey text, 2
hmacKey text, 3
blockDBHash text, 4
forwardKey text, 5
@@ -346,7 +320,7 @@ class Core:
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, 'hmacKey': 3, 'blockDBHash': 4, 'forwardKey': 5, 'dateSeen': 6, 'bytesStored': 7, 'trust': 8}
info = infoNumbers[info]
iterCount = 0
retVal = ''
@@ -369,7 +343,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()
diff --git a/onionr/onionrcrypto.py b/onionr/onionrcrypto.py
index 79bd339e..e9f89c7c 100644
--- a/onionr/onionrcrypto.py
+++ b/onionr/onionrcrypto.py
@@ -17,7 +17,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
'''
-import nacl, gnupg
+import nacl
class OnionrCrypto:
def __init__(self, coreInstance):
@@ -29,15 +29,9 @@ class OnionrCrypto:
def symmetricPeerDecrypt(self, data, key):
return
-
- def rsaEncrypt(self, peer, data):
- return
-
- def verifyPGP(self, peer, signature):
- '''Verify PGP signed data'''
- gpg = gnupg.GPG(homedir=self._core.gpgHome)
def generateSymmetric():
return
+
def generateHMAC():
return
\ No newline at end of file
diff --git a/onionr/onionrutils.py b/onionr/onionrutils.py
index 619e5927..ba082b98 100644
--- a/onionr/onionrutils.py
+++ b/onionr/onionrutils.py
@@ -18,7 +18,7 @@
along with this program. If not, see .
'''
# 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
if sys.version_info < (3, 6):
try:
import sha3
@@ -93,19 +93,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,17 +140,6 @@ class OnionrUtils:
retVal = False
return retVal
-
- def getPeerPGPFingerprint(self, peer):
- '''
- Get peer's PGP fingerprint
- '''
- retData = ''
- gpg = gnupg.GPG(homedir=self._core.gpgHome)
- for i in gpg.list_keys():
- if peer in i['uids'][0]:
- retData = i['fingerprint']
- return retData
def validateID(self, id):
'''
diff --git a/onionr/tests.py b/onionr/tests.py
index 8babbdfd..557e8885 100755
--- a/onionr/tests.py
+++ b/onionr/tests.py
@@ -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...')
diff --git a/requirements.txt b/requirements.txt
index da4c8869..77e25537 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,10 +1,7 @@
PyNaCl==1.2.1
-gnupg==2.3.1
+requests==2.12.4
Flask==0.12.2
-requests==2.18.4
-urllib3==1.22
simple_crypt==4.1.7
+urllib3==1.19.1
sha3==0.2.1
-pycrypto==2.6.1
-pynacl==1.2.1
PySocks==1.6.8