Merge branch 'crypto' of https://github.com/beardog108/Onionr into crypto
commit
36d9ebd4f1
|
@ -68,6 +68,8 @@ class API:
|
||||||
self.clientToken = config.get('client')['client_hmac']
|
self.clientToken = config.get('client')['client_hmac']
|
||||||
self.timeBypassToken = base64.b16encode(os.urandom(32)).decode()
|
self.timeBypassToken = base64.b16encode(os.urandom(32)).decode()
|
||||||
|
|
||||||
|
self.mimeType = 'text/plain'
|
||||||
|
|
||||||
with open('data/time-bypass.txt', 'w') as bypass:
|
with open('data/time-bypass.txt', 'w') as bypass:
|
||||||
bypass.write(self.timeBypassToken)
|
bypass.write(self.timeBypassToken)
|
||||||
|
|
||||||
|
@ -96,12 +98,17 @@ class API:
|
||||||
def afterReq(resp):
|
def afterReq(resp):
|
||||||
if not self.requestFailed:
|
if not self.requestFailed:
|
||||||
resp.headers['Access-Control-Allow-Origin'] = '*'
|
resp.headers['Access-Control-Allow-Origin'] = '*'
|
||||||
else:
|
#else:
|
||||||
resp.headers['server'] = 'Onionr'
|
# resp.headers['server'] = 'Onionr'
|
||||||
resp.headers['Content-Type'] = 'text/plain'
|
resp.headers['Content-Type'] = self.mimeType
|
||||||
resp.headers["Content-Security-Policy"] = "default-src 'none'"
|
resp.headers["Content-Security-Policy"] = "default-src 'none'; script-src 'none'; object-src 'none'; style-src data: 'unsafe-inline'; img-src data:; media-src 'none'; frame-src 'none'; font-src 'none'; connect-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"
|
||||||
|
resp.headers['server'] = 'Onionr'
|
||||||
|
|
||||||
|
# reset to text/plain to help prevent browser attacks
|
||||||
|
if self.mimeType != 'text/plain':
|
||||||
|
self.mimeType = 'text/plain'
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
@ -111,6 +118,11 @@ class API:
|
||||||
timingToken = ''
|
timingToken = ''
|
||||||
else:
|
else:
|
||||||
timingToken = request.args.get('timingToken')
|
timingToken = request.args.get('timingToken')
|
||||||
|
data = request.args.get('data')
|
||||||
|
try:
|
||||||
|
data = data
|
||||||
|
except:
|
||||||
|
data = ''
|
||||||
startTime = math.floor(time.time())
|
startTime = math.floor(time.time())
|
||||||
# we should keep a hash DB of requests (with hmac) to prevent replays
|
# we should keep a hash DB of requests (with hmac) to prevent replays
|
||||||
action = request.args.get('action')
|
action = request.args.get('action')
|
||||||
|
@ -129,6 +141,15 @@ class API:
|
||||||
resp = Response('pong')
|
resp = Response('pong')
|
||||||
elif action == 'stats':
|
elif action == 'stats':
|
||||||
resp = Response('me_irl')
|
resp = Response('me_irl')
|
||||||
|
elif action == 'site':
|
||||||
|
block = data
|
||||||
|
siteData = self._core.getData(data)
|
||||||
|
response = 'not found'
|
||||||
|
if siteData != '' and siteData != False:
|
||||||
|
self.mimeType = 'text/html'
|
||||||
|
response = siteData.split(b'-', 2)[-1]
|
||||||
|
resp = Response(response)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
resp = Response('(O_o) Dude what? (invalid command)')
|
resp = Response('(O_o) Dude what? (invalid command)')
|
||||||
endTime = math.floor(time.time())
|
endTime = math.floor(time.time())
|
||||||
|
@ -149,7 +170,7 @@ class API:
|
||||||
requestingPeer = request.args.get('myID')
|
requestingPeer = request.args.get('myID')
|
||||||
data = request.args.get('data')
|
data = request.args.get('data')
|
||||||
try:
|
try:
|
||||||
data
|
data = data
|
||||||
except:
|
except:
|
||||||
data = ''
|
data = ''
|
||||||
if action == 'firstConnect':
|
if action == 'firstConnect':
|
||||||
|
@ -175,7 +196,9 @@ class API:
|
||||||
resp = Response('')
|
resp = Response('')
|
||||||
# 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)
|
if self._utils.validateHash(data):
|
||||||
|
if not os.path.exists('data/blocks/' + data + '.db'):
|
||||||
|
resp = base64.b64encode(self._core.getData(data))
|
||||||
if resp == False:
|
if resp == False:
|
||||||
abort(404)
|
abort(404)
|
||||||
resp = ""
|
resp = ""
|
||||||
|
|
|
@ -19,7 +19,7 @@ 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, math, logger, urllib.parse, random
|
import sqlite3, requests, hmac, hashlib, time, sys, os, math, logger, urllib.parse, random, base64, binascii
|
||||||
import core, onionrutils, onionrcrypto, netcontroller, onionrproofs, btc, config, onionrplugins as plugins
|
import core, onionrutils, onionrcrypto, netcontroller, onionrproofs, btc, config, onionrplugins as plugins
|
||||||
|
|
||||||
class OnionrCommunicate:
|
class OnionrCommunicate:
|
||||||
|
@ -241,17 +241,21 @@ class OnionrCommunicate:
|
||||||
data = self.performGet('getData', i, hash)
|
data = self.performGet('getData', i, hash)
|
||||||
if data == False or len(data) > 10000000:
|
if data == False or len(data) > 10000000:
|
||||||
continue
|
continue
|
||||||
hasher.update(data.encode())
|
try:
|
||||||
|
data = base64.b64decode(data)
|
||||||
|
except binascii.Error:
|
||||||
|
data = b''
|
||||||
|
hasher.update(data)
|
||||||
digest = hasher.hexdigest()
|
digest = hasher.hexdigest()
|
||||||
if type(digest) is bytes:
|
if type(digest) is bytes:
|
||||||
digest = digest.decode()
|
digest = digest.decode()
|
||||||
if digest == hash.strip():
|
if digest == hash.strip():
|
||||||
self._core.setData(data)
|
self._core.setData(data)
|
||||||
if data.startswith('-txt-'):
|
|
||||||
self._core.setBlockType(hash, 'txt')
|
|
||||||
logger.info('Successfully obtained data for ' + hash, timestamp=True)
|
logger.info('Successfully obtained data for ' + hash, timestamp=True)
|
||||||
if len(data) < 120:
|
if data.startswith(b'-txt-'):
|
||||||
logger.debug('Block text:\n' + data)
|
self._core.setBlockType(hash, 'txt')
|
||||||
|
if len(data) < 120:
|
||||||
|
logger.debug('Block text:\n' + data.decode())
|
||||||
else:
|
else:
|
||||||
logger.warn("Failed to validate " + hash + " " + " hash calculated was " + digest)
|
logger.warn("Failed to validate " + hash + " " + " hash calculated was " + digest)
|
||||||
|
|
||||||
|
@ -263,7 +267,7 @@ class OnionrCommunicate:
|
||||||
'''
|
'''
|
||||||
return urllib.parse.quote_plus(data)
|
return urllib.parse.quote_plus(data)
|
||||||
|
|
||||||
def performGet(self, action, peer, data=None, skipHighFailureAddress=False, peerType='tor'):
|
def performGet(self, action, peer, data=None, skipHighFailureAddress=False, peerType='tor', selfCheck=True):
|
||||||
'''
|
'''
|
||||||
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)
|
||||||
'''
|
'''
|
||||||
|
@ -271,6 +275,13 @@ class OnionrCommunicate:
|
||||||
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')
|
||||||
|
|
||||||
|
if len(self._core.hsAdder.strip()) == 0:
|
||||||
|
raise Exception("Could not perform self address check in performGet due to not knowing our address")
|
||||||
|
if selfCheck:
|
||||||
|
if peer.replace('/', '') == self._core.hsAdder:
|
||||||
|
logger.warn('Tried to performget to own hidden service, but selfCheck was not set to false')
|
||||||
|
return
|
||||||
|
|
||||||
# Store peer in peerData dictionary (non permanent)
|
# Store peer in peerData dictionary (non permanent)
|
||||||
if not peer in self.peerData:
|
if not peer in self.peerData:
|
||||||
self.peerData[peer] = {'connectCount': 0, 'failCount': 0, 'lastConnectTime': math.floor(time.time())}
|
self.peerData[peer] = {'connectCount': 0, 'failCount': 0, 'lastConnectTime': math.floor(time.time())}
|
||||||
|
|
|
@ -44,6 +44,9 @@ class Core:
|
||||||
self.addressDB = 'data/address.db'
|
self.addressDB = 'data/address.db'
|
||||||
self.hsAdder = ''
|
self.hsAdder = ''
|
||||||
|
|
||||||
|
self.bootstrapFileLocation = 'static-data/bootstrap-nodes.txt'
|
||||||
|
self.bootstrapList = []
|
||||||
|
|
||||||
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/'):
|
||||||
|
@ -55,9 +58,19 @@ class Core:
|
||||||
with open('data/hs/hostname', 'r') as hs:
|
with open('data/hs/hostname', 'r') as hs:
|
||||||
self.hsAdder = hs.read()
|
self.hsAdder = hs.read()
|
||||||
|
|
||||||
|
# Load bootstrap address list
|
||||||
|
if os.path.exists(self.bootstrapFileLocation):
|
||||||
|
with open(self.bootstrapFileLocation, 'r') as bootstrap:
|
||||||
|
bootstrap = bootstrap.read()
|
||||||
|
for i in bootstrap.split('\n'):
|
||||||
|
self.bootstrapList.append(i)
|
||||||
|
else:
|
||||||
|
logger.warn('Warning: address bootstrap file not found ' + self.bootstrapFileLocation)
|
||||||
|
|
||||||
self._utils = onionrutils.OnionrUtils(self)
|
self._utils = onionrutils.OnionrUtils(self)
|
||||||
# Initialize the crypto object
|
# Initialize the crypto object
|
||||||
self._crypto = onionrcrypto.OnionrCrypto(self)
|
self._crypto = onionrcrypto.OnionrCrypto(self)
|
||||||
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.error('Failed to initialize core Onionr library.', error=error)
|
logger.error('Failed to initialize core Onionr library.', error=error)
|
||||||
logger.fatal('Cannot recover from error.')
|
logger.fatal('Cannot recover from error.')
|
||||||
|
@ -245,7 +258,7 @@ class Core:
|
||||||
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', 'rb')
|
||||||
data = dataFile.read()
|
data = dataFile.read()
|
||||||
dataFile.close()
|
dataFile.close()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
|
@ -257,8 +270,10 @@ class Core:
|
||||||
'''
|
'''
|
||||||
Set the data assciated with a hash
|
Set the data assciated with a hash
|
||||||
'''
|
'''
|
||||||
data = data.encode()
|
data = data
|
||||||
hasher = hashlib.sha3_256()
|
hasher = hashlib.sha3_256()
|
||||||
|
if not type(data) is bytes:
|
||||||
|
data = data.encode()
|
||||||
hasher.update(data)
|
hasher.update(data)
|
||||||
dataHash = hasher.hexdigest()
|
dataHash = hasher.hexdigest()
|
||||||
if type(dataHash) is bytes:
|
if type(dataHash) is bytes:
|
||||||
|
@ -268,8 +283,8 @@ class Core:
|
||||||
pass # TODO: 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, 'wb')
|
||||||
blockFile.write(data.decode())
|
blockFile.write(data)
|
||||||
blockFile.close()
|
blockFile.close()
|
||||||
|
|
||||||
conn = sqlite3.connect(self.blockDB)
|
conn = sqlite3.connect(self.blockDB)
|
||||||
|
@ -574,8 +589,10 @@ class Core:
|
||||||
announceAmount = 2
|
announceAmount = 2
|
||||||
nodeList = self.listAdders()
|
nodeList = self.listAdders()
|
||||||
if len(nodeList) == 0:
|
if len(nodeList) == 0:
|
||||||
self.addAddress('onionragxuddecmg.onion')
|
for i in self.bootstrapList:
|
||||||
nodeList.append('onionragxuddecmg.onion')
|
if self._utils.validateID(i):
|
||||||
|
self.addAddress(i)
|
||||||
|
nodeList.append(i)
|
||||||
if announceAmount > len(nodeList):
|
if announceAmount > len(nodeList):
|
||||||
announceAmount = len(nodeList)
|
announceAmount = len(nodeList)
|
||||||
for i in range(announceAmount):
|
for i in range(announceAmount):
|
||||||
|
|
|
@ -34,7 +34,7 @@ except ImportError:
|
||||||
|
|
||||||
ONIONR_TAGLINE = 'Anonymous P2P Platform - GPLv3 - https://Onionr.VoidNet.Tech'
|
ONIONR_TAGLINE = 'Anonymous P2P Platform - GPLv3 - https://Onionr.VoidNet.Tech'
|
||||||
ONIONR_VERSION = '0.0.0' # for debugging and stuff
|
ONIONR_VERSION = '0.0.0' # for debugging and stuff
|
||||||
API_VERSION = '1' # increments of 1; only change when something fundemental about how the API works changes. This way other nodes knows how to communicate without learning too much information about you.
|
API_VERSION = '2' # increments of 1; only change when something fundemental about how the API works changes. This way other nodes knows how to communicate without learning too much information about you.
|
||||||
|
|
||||||
class Onionr:
|
class Onionr:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -179,6 +179,7 @@ class Onionr:
|
||||||
'add-addr': self.addAddress,
|
'add-addr': self.addAddress,
|
||||||
'addaddr': self.addAddress,
|
'addaddr': self.addAddress,
|
||||||
'addaddress': self.addAddress,
|
'addaddress': self.addAddress,
|
||||||
|
'addfile': self.addFile,
|
||||||
|
|
||||||
'introduce': self.onionrCore.introduceNode,
|
'introduce': self.onionrCore.introduceNode,
|
||||||
'connect': self.addAddress
|
'connect': self.addAddress
|
||||||
|
@ -200,6 +201,7 @@ class Onionr:
|
||||||
'add-msg': 'Broadcasts a message to the Onionr network',
|
'add-msg': 'Broadcasts a message to the Onionr network',
|
||||||
'pm': 'Adds a private message to block',
|
'pm': 'Adds a private message to block',
|
||||||
'get-pms': 'Shows private messages sent to you',
|
'get-pms': 'Shows private messages sent to you',
|
||||||
|
'addfile': 'Create an Onionr block from a file',
|
||||||
'introduce': 'Introduce your node to the public Onionr network (DAEMON MUST BE RUNNING)',
|
'introduce': 'Introduce your node to the public Onionr network (DAEMON MUST BE RUNNING)',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,7 +375,7 @@ class Onionr:
|
||||||
addedHash = self.onionrCore.setData(messageToAdd)
|
addedHash = self.onionrCore.setData(messageToAdd)
|
||||||
self.onionrCore.addToBlockDB(addedHash, selfInsert=True)
|
self.onionrCore.addToBlockDB(addedHash, selfInsert=True)
|
||||||
self.onionrCore.setBlockType(addedHash, 'txt')
|
self.onionrCore.setBlockType(addedHash, 'txt')
|
||||||
|
logger.info("inserted your message as block: " + addedHash)
|
||||||
return
|
return
|
||||||
|
|
||||||
def getPMs(self):
|
def getPMs(self):
|
||||||
|
@ -557,8 +559,23 @@ class Onionr:
|
||||||
retVal = ''
|
retVal = ''
|
||||||
try:
|
try:
|
||||||
with open('./data/hs/hostname', 'r') as hostname:
|
with open('./data/hs/hostname', 'r') as hostname:
|
||||||
retval = retVal.read()
|
retVal = hostname.read()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return retVal
|
return retVal
|
||||||
|
|
||||||
|
def addFile(self):
|
||||||
|
'''command to add a file to the onionr network'''
|
||||||
|
if len(sys.argv) >= 2:
|
||||||
|
newFile = sys.argv[2]
|
||||||
|
logger.info('Attempting to add file...')
|
||||||
|
try:
|
||||||
|
with open(newFile, 'r') as new:
|
||||||
|
new = new.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.warn('That file does not exist. Improper path?')
|
||||||
|
else:
|
||||||
|
print(new)
|
||||||
|
self.onionrCore.insertBlock(new, header='bin')
|
||||||
|
|
||||||
|
|
||||||
Onionr()
|
Onionr()
|
||||||
|
|
|
@ -167,7 +167,7 @@ class OnionrCrypto:
|
||||||
return binascii.hexlify(nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE))
|
return binascii.hexlify(nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE))
|
||||||
|
|
||||||
def generatePubKey(self):
|
def generatePubKey(self):
|
||||||
'''Generate a Ed25519 public key pair, return tuple of base64encoded pubkey, privkey'''
|
'''Generate a Ed25519 public key pair, return tuple of base32encoded pubkey, privkey'''
|
||||||
private_key = nacl.signing.SigningKey.generate()
|
private_key = nacl.signing.SigningKey.generate()
|
||||||
public_key = private_key.verify_key.encode(encoder=nacl.encoding.Base32Encoder())
|
public_key = private_key.verify_key.encode(encoder=nacl.encoding.Base32Encoder())
|
||||||
return (public_key.decode(), private_key.encode(encoder=nacl.encoding.Base32Encoder()).decode())
|
return (public_key.decode(), private_key.encode(encoder=nacl.encoding.Base32Encoder()).decode())
|
|
@ -0,0 +1,3 @@
|
||||||
|
onionisrgccylxpr.onion
|
||||||
|
aaronk3mcmglj6qedwptg62yl3wxxjwba2ucpoobrn7iudcacdxtrfad.onion
|
||||||
|
onionragxuddecmg.onion
|
Loading…
Reference in New Issue