work on adding peers, improving docs, and minor bug fixes
parent
1006be971b
commit
097c57b97a
|
@ -1,4 +1,5 @@
|
||||||
__pycache__/
|
__pycache__/
|
||||||
data/config.ini
|
data/config.ini
|
||||||
data/*.db
|
data/*.db
|
||||||
|
data-old/*
|
||||||
dev-enabled
|
dev-enabled
|
|
@ -1,6 +1,11 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
'''
|
'''
|
||||||
Onionr - P2P Microblogging Platform & Social network. Run with 'help' for usage.
|
Onionr - P2P Microblogging Platform & Social network.
|
||||||
|
|
||||||
|
This file contains both the OnionrCommunicate class for communcating with peers
|
||||||
|
and code to operate as a daemon, getting commands from the command queue database (see core.Core.daemonQueue)
|
||||||
|
'''
|
||||||
|
'''
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
@ -14,24 +19,30 @@
|
||||||
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
|
import sqlite3, requests, hmac, hashlib, time, sys
|
||||||
import core
|
import core
|
||||||
class OnionrCommunicate:
|
class OnionrCommunicate:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
''' OnionrCommunicate
|
||||||
|
|
||||||
|
This class handles communication with nodes in the Onionr network.
|
||||||
|
'''
|
||||||
self._core = core.Core()
|
self._core = core.Core()
|
||||||
while True:
|
|
||||||
print('Onionr daemon running')
|
|
||||||
time.sleep(2)
|
|
||||||
return
|
return
|
||||||
def getRemotePeerKey(self, peerID):
|
def getRemotePeerKey(self, peerID):
|
||||||
'''This function contacts a peer and gets their main PGP key.
|
'''This function contacts a peer and gets their main PGP key.
|
||||||
This is safe because Tor or I2P is used, but it does not insure that the person is who they say they are
|
|
||||||
|
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
|
||||||
|
shouldRun = False
|
||||||
|
try:
|
||||||
|
if sys.argv[1] == 'run':
|
||||||
OnionrCommunicate()
|
shouldRun = True
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
if shouldRun:
|
||||||
|
OnionrCommunicate()
|
4
core.py
4
core.py
|
@ -37,8 +37,8 @@ class Core:
|
||||||
# This function simply adds a peer to the DB
|
# This function simply adds a peer to the DB
|
||||||
conn = sqlite3.connect(self.peerDB)
|
conn = sqlite3.connect(self.peerDB)
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
t = (peerID, name)
|
t = (peerID, name, 'unknown')
|
||||||
c.execute('Insert into users (id, name) values(?, ?);', t)
|
c.execute('Insert into users (id, name, dateSeen) values(?, ?, ?);', t)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
# Onionr Protocol Spec
|
||||||
|
|
||||||
|
A social network/microblogging platform for Tor & I2P
|
||||||
|
|
||||||
|
Draft Dec 25 2017
|
||||||
|
|
||||||
|
notes:
|
||||||
|
Use Blowfish in addition with AES?
|
||||||
|
|
||||||
|
# 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 or OnionShare.
|
||||||
|
All traffic is over onion/I2P because if only some was, then that would make that traffic inherently suspicious.
|
||||||
|
## Goals:
|
||||||
|
• Selective sharing of information with friends & public
|
||||||
|
• 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”)
|
||||||
|
• 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.
|
||||||
|
## 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 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.
|
||||||
|
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
|
||||||
|
|
||||||
|
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.
|
||||||
|
When both peers are online messages SHOULD be dispatched directly between peers.
|
||||||
|
All messages must be verified prior to being displayed.
|
||||||
|
|
||||||
|
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.
|
19
onionr.py
19
onionr.py
|
@ -1,6 +1,12 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
'''
|
'''
|
||||||
Onionr - P2P Microblogging Platform & Social network. Run with 'help' for usage.
|
Onionr - P2P Microblogging Platform & Social network.
|
||||||
|
|
||||||
|
Onionr is the name for both the protocol and the original/reference software.
|
||||||
|
|
||||||
|
Run with 'help' for usage.
|
||||||
|
'''
|
||||||
|
'''
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
@ -21,6 +27,10 @@ from colors import Colors
|
||||||
|
|
||||||
class Onionr:
|
class Onionr:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
'''Main Onionr class. This is for the CLI program, and does not handle much of the logic.
|
||||||
|
In general, external programs and plugins should not use this class.
|
||||||
|
|
||||||
|
'''
|
||||||
if os.path.exists('dev-enabled'):
|
if os.path.exists('dev-enabled'):
|
||||||
print('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
|
print('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
|
||||||
self._developmentMode = True
|
self._developmentMode = True
|
||||||
|
@ -35,8 +45,11 @@ class Onionr:
|
||||||
# Get configuration and Handle commands
|
# Get configuration and Handle commands
|
||||||
|
|
||||||
self.debug = False # Whole application debugging
|
self.debug = False # Whole application debugging
|
||||||
|
try:
|
||||||
|
os.chdir(sys.path[0])
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
os.chdir(sys.path[0])
|
|
||||||
if os.path.exists('data-encrypted.dat'):
|
if os.path.exists('data-encrypted.dat'):
|
||||||
while True:
|
while True:
|
||||||
print('Enter password to decrypt:')
|
print('Enter password to decrypt:')
|
||||||
|
@ -94,7 +107,7 @@ class Onionr:
|
||||||
return
|
return
|
||||||
def daemon(self):
|
def daemon(self):
|
||||||
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
|
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
|
||||||
subprocess.Popen(["./communicator.py"])
|
subprocess.Popen(["./communicator.py", "run"])
|
||||||
print('Started communicator')
|
print('Started communicator')
|
||||||
api.API(self.config, self.debug)
|
api.API(self.config, self.debug)
|
||||||
return
|
return
|
||||||
|
|
5
tests.py
5
tests.py
|
@ -49,9 +49,10 @@ class OnionrTests(unittest.TestCase):
|
||||||
print('Running peer db insertion test')
|
print('Running peer db insertion test')
|
||||||
import core
|
import core
|
||||||
myCore = core.Core()
|
myCore = core.Core()
|
||||||
myCore.createPeerDB()
|
if not os.path.exists('data/peers.db'):
|
||||||
|
myCore.createPeerDB()
|
||||||
if myCore.addPeer('test'):
|
if myCore.addPeer('test'):
|
||||||
self.asserTrue(True)
|
self.assertTrue(True)
|
||||||
else:
|
else:
|
||||||
self.assertTrue(False)
|
self.assertTrue(False)
|
||||||
def testData_b_Encrypt(self):
|
def testData_b_Encrypt(self):
|
||||||
|
|
Loading…
Reference in New Issue