progress in removing core

This commit is contained in:
Kevin Froman 2019-07-19 19:01:16 -05:00
parent e12781a49d
commit e1676ef168
21 changed files with 152 additions and 477 deletions

View File

@ -41,7 +41,6 @@ class PrivateAPI:
self.config = config self.config = config
self.debug = debug self.debug = debug
self.startTime = epoch.get_epoch() self.startTime = epoch.get_epoch()
self._crypto = onionrInst.onionrCrypto
app = flask.Flask(__name__) app = flask.Flask(__name__)
bindPort = int(config.get('client.client.port', 59496)) bindPort = int(config.get('client.client.port', 59496))
self.bindPort = bindPort self.bindPort = bindPort

View File

@ -34,7 +34,7 @@ class PublicAPI:
self.i2pEnabled = config.get('i2p.host', False) self.i2pEnabled = config.get('i2p.host', False)
self.hideBlocks = [] # Blocks to be denied sharing self.hideBlocks = [] # Blocks to be denied sharing
self.host = apiutils.setbindip.set_bind_IP(filepaths.public_API_host_file) self.host = apiutils.setbindip.set_bind_IP(filepaths.public_API_host_file)
self.torAdder = gettransports.get_transports[0] self.torAdder = gettransports.transports[0]
self.bindPort = config.get('client.public.port') self.bindPort = config.get('client.public.port')
self.lastRequest = 0 self.lastRequest = 0
self.hitCount = 0 # total rec requests to public api since server started self.hitCount = 0 # total rec requests to public api since server started

View File

@ -1,351 +0,0 @@
'''
Onionr - Private P2P Communication
Core Onionr library, useful for external programs. Handles peer & data processing
'''
'''
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
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 os, sys, json
import logger, netcontroller, config
from onionrblockapi import Block
import coredb
import deadsimplekv as simplekv
import onionrcrypto, onionrproofs, onionrevents as events, onionrexceptions
import onionrblacklist
from onionrusers import onionrusers
from onionrstorage import removeblock, setdata
import dbcreator, onionrstorage, serializeddata, subprocesspow
from etc import onionrvalues, powchoice
from onionrutils import localcommand, stringvalidators, bytesconverter, epoch
from onionrutils import blockmetadata
from utils import identifyhome
import storagecounter
class Core:
def __init__(self, torPort=0):
'''
Initialize Core Onionr library
'''
# set data dir
self.dataDir = identifyhome.identify_home()
self.usageFile = self.dataDir + 'disk-usage.txt'
self.config = config
self.maxBlockSize = 10000000 # max block size in bytes
self.onionrInst = None
self.hsAddress = ''
self.i2pAddress = config.get('i2p.own_addr', None)
self.bootstrapFileLocation = 'static-data/bootstrap-nodes.txt'
self.bootstrapList = []
self.requirements = onionrvalues.OnionrValues()
self.torPort = torPort
self.dataNonceFile = self.dataDir + 'block-nonces.dat'
self.forwardKeysFile = self.dataDir + 'forward-keys.db'
self.keyStore = simplekv.DeadSimpleKV(self.dataDir + 'cachedstorage.dat', refresh_seconds=5)
self.storage_counter = storagecounter.StorageCounter(self)
# Socket data, defined here because of multithreading constraints with gevent
self.killSockets = False
self.startSocket = {}
self.socketServerConnData = {}
self.socketReasons = {}
self.socketServerResponseData = {}
if os.path.exists(self.dataDir + '/hs/hostname'):
with open(self.dataDir + '/hs/hostname', 'r') as hs:
self.hsAddress = hs.read().strip()
# 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.use_subprocess = powchoice.use_subprocess(self)
# Initialize the crypto object
self._crypto = onionrcrypto.OnionrCrypto(self)
self._blacklist = onionrblacklist.OnionrBlackList(self)
self.serializer = serializeddata.SerializedData(self)
def addPeer(self, peerID, name=''):
'''
Adds a public key to the key database (misleading function name)
'''
return coredb.keydb.addkeys.add_peer(self, peerID, name)
def addAddress(self, address):
'''
Add an address to the address database (only tor currently)
'''
return coredb.keydb.addkeys.add_address(self, address)
def removeAddress(self, address):
'''
Remove an address from the address database
'''
return coredb.keydb.removekeys.remove_address(self, address)
def removeBlock(self, block):
'''
remove a block from this node (does not automatically blacklist)
**You may want blacklist.addToDB(blockHash)
'''
removeblock.remove_block(self, block)
def createAddressDB(self):
'''
Generate the address database
'''
self.dbCreate.createAddressDB()
def createPeerDB(self):
'''
Generate the peer sqlite3 database and populate it with the peers table.
'''
self.dbCreate.createPeerDB()
def createBlockDB(self):
'''
Create a database for blocks
'''
self.dbCreate.createBlockDB()
def setData(self, data):
'''
Set the data assciated with a hash
'''
return onionrstorage.setdata.set_data(self, data)
def getData(self, hash):
'''
Simply return the data associated to a hash
'''
return onionrstorage.getData(self, hash)
def listAdders(self, randomOrder=True, i2p=True, recent=0):
'''
Return a list of addresses
'''
return coredb.keydb.listkeys.list_adders(self, randomOrder, i2p, recent)
def listPeers(self, randomOrder=True, getPow=False, trust=0):
'''
Return a list of public keys (misleading function name)
randomOrder determines if the list should be in a random order
trust sets the minimum trust to list
'''
return coredb.keydb.listkeys.list_peers(self, randomOrder, getPow, trust)
def getPeerInfo(self, peer, info):
'''
Get info about a peer from their database entry
id text 0
name text, 1
adders text, 2
dateSeen not null, 3
trust int 4
hashID text 5
'''
return coredb.keydb.userinfo.get_user_info(self, peer, info)
def setPeerInfo(self, peer, key, data):
'''
Update a peer for a key
'''
return coredb.keydb.userinfo.set_peer_info(self, peer, key, data)
def getAddressInfo(self, address, info):
'''
Get info about an address from its database entry
address text, 0
type int, 1
knownPeer text, 2
speed int, 3
success int, 4
powValue 5
failure int 6
lastConnect 7
trust 8
introduced 9
'''
return coredb.keydb.transportinfo.get_address_info(self, address, info)
def setAddressInfo(self, address, key, data):
'''
Update an address for a key
'''
return coredb.keydb.transportinfo.set_address_info(self, address, key, data)
def insertBlock(self, data, header='txt', sign=False, encryptType='', symKey='', asymPeer='', meta = {}, expire=None, disableForward=False):
'''
Inserts a block into the network
encryptType must be specified to encrypt a block
'''
allocationReachedMessage = 'Cannot insert block, disk allocation reached.'
if self.storage_counter.isFull():
logger.error(allocationReachedMessage)
return False
retData = False
if type(data) is None:
raise ValueError('Data cannot be none')
createTime = epoch.get_epoch()
dataNonce = bytesconverter.bytes_to_str(self._crypto.sha3Hash(data))
try:
with open(self.dataNonceFile, 'r') as nonces:
if dataNonce in nonces:
return retData
except FileNotFoundError:
pass
# record nonce
with open(self.dataNonceFile, 'a') as nonceFile:
nonceFile.write(dataNonce + '\n')
if type(data) is bytes:
data = data.decode()
data = str(data)
plaintext = data
plaintextMeta = {}
plaintextPeer = asymPeer
retData = ''
signature = ''
signer = ''
metadata = {}
# metadata is full block metadata, meta is internal, user specified metadata
# only use header if not set in provided meta
meta['type'] = str(header)
if encryptType in ('asym', 'sym', ''):
metadata['encryptType'] = encryptType
else:
raise onionrexceptions.InvalidMetadata('encryptType must be asym or sym, or blank')
try:
data = data.encode()
except AttributeError:
pass
if encryptType == 'asym':
meta['rply'] = createTime # Duplicate the time in encrypted messages to prevent replays
if not disableForward and sign and asymPeer != self._crypto.pubKey:
try:
forwardEncrypted = onionrusers.OnionrUser(self, asymPeer).forwardEncrypt(data)
data = forwardEncrypted[0]
meta['forwardEnc'] = True
expire = forwardEncrypted[2] # Expire time of key. no sense keeping block after that
except onionrexceptions.InvalidPubkey:
pass
#onionrusers.OnionrUser(self, asymPeer).generateForwardKey()
fsKey = onionrusers.OnionrUser(self, asymPeer).generateForwardKey()
#fsKey = onionrusers.OnionrUser(self, asymPeer).getGeneratedForwardKeys().reverse()
meta['newFSKey'] = fsKey
jsonMeta = json.dumps(meta)
plaintextMeta = jsonMeta
if sign:
signature = self._crypto.edSign(jsonMeta.encode() + data, key=self._crypto.privKey, encodeResult=True)
signer = self._crypto.pubKey
if len(jsonMeta) > 1000:
raise onionrexceptions.InvalidMetadata('meta in json encoded form must not exceed 1000 bytes')
user = onionrusers.OnionrUser(self, symKey)
# encrypt block metadata/sig/content
if encryptType == 'sym':
if len(symKey) < self.requirements.passwordLength:
raise onionrexceptions.SecurityError('Weak encryption key')
jsonMeta = self._crypto.symmetricEncrypt(jsonMeta, key=symKey, returnEncoded=True).decode()
data = self._crypto.symmetricEncrypt(data, key=symKey, returnEncoded=True).decode()
signature = self._crypto.symmetricEncrypt(signature, key=symKey, returnEncoded=True).decode()
signer = self._crypto.symmetricEncrypt(signer, key=symKey, returnEncoded=True).decode()
elif encryptType == 'asym':
if stringvalidators.validate_pub_key(asymPeer):
# Encrypt block data with forward secrecy key first, but not meta
jsonMeta = json.dumps(meta)
jsonMeta = self._crypto.pubKeyEncrypt(jsonMeta, asymPeer, encodedData=True).decode()
data = self._crypto.pubKeyEncrypt(data, asymPeer, encodedData=True).decode()
signature = self._crypto.pubKeyEncrypt(signature, asymPeer, encodedData=True).decode()
signer = self._crypto.pubKeyEncrypt(signer, asymPeer, encodedData=True).decode()
try:
onionrusers.OnionrUser(self, asymPeer, saveUser=True)
except ValueError:
# if peer is already known
pass
else:
raise onionrexceptions.InvalidPubkey(asymPeer + ' is not a valid base32 encoded ed25519 key')
# compile metadata
metadata['meta'] = jsonMeta
metadata['sig'] = signature
metadata['signer'] = signer
metadata['time'] = createTime
# ensure expire is integer and of sane length
if type(expire) is not type(None):
assert len(str(int(expire))) < 14
metadata['expire'] = expire
# send block data (and metadata) to POW module to get tokenized block data
if self.use_subprocess:
payload = subprocesspow.SubprocessPOW(data, metadata, self).start()
else:
payload = onionrproofs.POW(metadata, data).waitForResult()
if payload != False:
try:
retData = self.setData(payload)
except onionrexceptions.DiskAllocationReached:
logger.error(allocationReachedMessage)
retData = False
else:
# Tell the api server through localCommand to wait for the daemon to upload this block to make statistical analysis more difficult
if localcommand.local_command(self, '/ping', maxWait=10) == 'pong!':
if self.config.get('general.security_level', 1) == 0:
localcommand.local_command(self, '/waitforshare/' + retData, post=True, maxWait=5)
coredb.daemonqueue.daemon_queue_add('uploadBlock', retData)
else:
pass
coredb.blockmetadb.add_to_block_DB(retData, selfInsert=True, dataSaved=True)
coredb.blockmetadata.process_block_metadata(self, retData)
if retData != False:
if plaintextPeer == onionrvalues.DENIABLE_PEER_ADDRESS:
events.event('insertdeniable', {'content': plaintext, 'meta': plaintextMeta, 'hash': retData, 'peer': bytesconverter.bytes_to_str(asymPeer)}, onionr = self.onionrInst, threaded = True)
else:
events.event('insertblock', {'content': plaintext, 'meta': plaintextMeta, 'hash': retData, 'peer': bytesconverter.bytes_to_str(asymPeer)}, onionr = self.onionrInst, threaded = True)
return retData
def introduceNode(self):
'''
Introduces our node into the network by telling X many nodes our HS address
'''
if localcommand.local_command(self, '/ping', maxWait=10) == 'pong!':
coredb.daemonqueue.daemon_queue_add('announceNode')
logger.info('Introduction command will be processed.', terminal=True)
else:
logger.warn('No running node detected. Cannot introduce.', terminal=True)

View File

@ -2,8 +2,8 @@ import json
import onionrblockapi import onionrblockapi
from onionrutils import bytesconverter, stringvalidators from onionrutils import bytesconverter, stringvalidators
class GetBlockData: class GetBlockData:
def __init__(self, client_api_inst): def __init__(self, client_api_inst=None):
self.client_api_inst = client_api_inst return
def get_block_data(self, bHash, decrypt=False, raw=False, headerOnly=False): def get_block_data(self, bHash, decrypt=False, raw=False, headerOnly=False):
assert stringvalidators.validate_hash(bHash) assert stringvalidators.validate_hash(bHash)

View File

@ -19,7 +19,7 @@
''' '''
from flask import Blueprint, Response, abort from flask import Blueprint, Response, abort
import onionrblockapi import onionrblockapi
from httpapi import apiutils from .. import apiutils
from onionrutils import stringvalidators from onionrutils import stringvalidators
from coredb import blockmetadb from coredb import blockmetadb

View File

@ -129,7 +129,7 @@ HiddenServicePort 80 ''' + self.apiServerIP + ''':''' + str(self.hsPort)
logger.fatal('Failed to start Tor. Maybe a stray instance of Tor used by Onionr is still running? This can also be a result of file permissions being too open', terminal=True) logger.fatal('Failed to start Tor. Maybe a stray instance of Tor used by Onionr is still running? This can also be a result of file permissions being too open', terminal=True)
return False return False
except KeyboardInterrupt: except KeyboardInterrupt:
logger.fatal('Got keyboard interrupt. Onionr will exit soon.', timestamp = False, level = logger.LEVEL_IMPORTANT, terminal=True) logger.fatal('Got keyboard interrupt. Onionr will exit soon.', timestamp = False, terminal=True)
return False return False
logger.info('Finished starting Tor.', terminal=True) logger.info('Finished starting Tor.', terminal=True)

View File

@ -19,14 +19,14 @@
''' '''
import logger, config, onionrexceptions, nacl.exceptions import logger, config, onionrexceptions, nacl.exceptions
import json, os, sys, datetime, base64, onionrstorage, onionrcrypto import json, os, sys, datetime, base64, onionrstorage
from onionrusers import onionrusers from onionrusers import onionrusers
from onionrutils import stringvalidators, epoch from onionrutils import stringvalidators, epoch
from coredb import blockmetadb from coredb import blockmetadb
from onionrstorage import removeblock from onionrstorage import removeblock
import onionrblocks import onionrblocks
from onionrcrypto import encryption, cryptoutils as cryptoutils, signing
class Block: class Block:
crypto = onionrcrypto.OnionrCrypto()
blockCacheOrder = list() # NEVER write your own code that writes to this! blockCacheOrder = list() # NEVER write your own code that writes to this!
blockCache = dict() # should never be accessed directly, look at Block.getCache() blockCache = dict() # should never be accessed directly, look at Block.getCache()
@ -71,23 +71,23 @@ class Block:
# decrypt data # decrypt data
if self.getHeader('encryptType') == 'asym': if self.getHeader('encryptType') == 'asym':
try: try:
self.bcontent = crypto.pubKeyDecrypt(self.bcontent, encodedData=encodedData) self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=encodedData)
bmeta = crypto.pubKeyDecrypt(self.bmetadata, encodedData=encodedData) bmeta = encryption.pub_key_decrypt(self.bmetadata, encodedData=encodedData)
try: try:
bmeta = bmeta.decode() bmeta = bmeta.decode()
except AttributeError: except AttributeError:
# yet another bytes fix # yet another bytes fix
pass pass
self.bmetadata = json.loads(bmeta) self.bmetadata = json.loads(bmeta)
self.signature = crypto.pubKeyDecrypt(self.signature, encodedData=encodedData) self.signature = encryption.pub_key_decrypt(self.signature, encodedData=encodedData)
self.signer = crypto.pubKeyDecrypt(self.signer, encodedData=encodedData) self.signer = encryption.pub_key_decrypt(self.signer, encodedData=encodedData)
self.bheader['signer'] = self.signer.decode() self.bheader['signer'] = self.signer.decode()
self.signedData = json.dumps(self.bmetadata) + self.bcontent.decode() self.signedData = json.dumps(self.bmetadata) + self.bcontent.decode()
# Check for replay attacks # Check for replay attacks
try: try:
if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) > 60: if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) > 60:
assert self.crypto.replayTimestampValidation(self.bmetadata['rply']) assert cryptoutils.replay_validator(self.bmetadata['rply'])
except (AssertionError, KeyError, TypeError) as e: except (AssertionError, KeyError, TypeError) as e:
if not self.bypassReplayCheck: if not self.bypassReplayCheck:
# Zero out variables to prevent reading of replays # Zero out variables to prevent reading of replays
@ -124,7 +124,7 @@ class Block:
Verify if a block's signature is signed by its claimed signer Verify if a block's signature is signed by its claimed signer
''' '''
if crypto.edVerify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True): if signing.ed_verify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True):
self.validSig = True self.validSig = True
else: else:
self.validSig = False self.validSig = False
@ -425,7 +425,7 @@ class Block:
if (not self.isSigned()) or (not stringvalidators.validate_pub_key(signer)): if (not self.isSigned()) or (not stringvalidators.validate_pub_key(signer)):
return False return False
return bool(crypto.edVerify(self.getSignedData(), signer, self.getSignature(), encodedData = encodedData)) return bool(signing.ed_verify(self.getSignedData(), signer, self.getSignature(), encodedData = encodedData))
except: except:
return False return False

View File

@ -26,7 +26,7 @@ from netcontroller import NetController
from onionrutils import localcommand from onionrutils import localcommand
import filepaths import filepaths
from coredb import daemonqueue from coredb import daemonqueue
from onionrcrypto import getourkeypair
def _proper_shutdown(o_inst): def _proper_shutdown(o_inst):
localcommand.local_command('shutdown') localcommand.local_command('shutdown')
sys.exit(1) sys.exit(1)
@ -72,7 +72,7 @@ def daemon(o_inst):
logger.debug('Started .onion service: %s' % (logger.colors.underline + net.myID)) logger.debug('Started .onion service: %s' % (logger.colors.underline + net.myID))
else: else:
logger.debug('.onion service disabled') logger.debug('.onion service disabled')
logger.info('Using public key: %s' % (logger.colors.underline + o_inst._crypto.pubKey[:52]), terminal=True) logger.info('Using public key: %s' % (logger.colors.underline + getourkeypair.get_keypair()[0][:52]), terminal=True)
try: try:
time.sleep(1) time.sleep(1)

View File

@ -22,6 +22,7 @@ import sys, getpass
import logger, onionrexceptions import logger, onionrexceptions
from onionrutils import stringvalidators, bytesconverter from onionrutils import stringvalidators, bytesconverter
from onionrusers import onionrusers, contactmanager from onionrusers import onionrusers, contactmanager
from coredb import keydb
import unpaddedbase32 import unpaddedbase32
def add_ID(o_inst): def add_ID(o_inst):
try: try:
@ -82,22 +83,22 @@ def friend_command(o_inst):
action = action.lower() action = action.lower()
if action == 'list': if action == 'list':
# List out peers marked as our friend # List out peers marked as our friend
for friend in contactmanager.ContactManager.list_friends(o_inst.): for friend in contactmanager.ContactManager.list_friends():
logger.info(friend.publicKey + ' - ' + friend.get_info('name'), terminal=True) logger.info(friend.publicKey + ' - ' + friend.get_info('name'), terminal=True)
elif action in ('add', 'remove'): elif action in ('add', 'remove'):
try: try:
friend = sys.argv[3] friend = sys.argv[3]
if not stringvalidators.validate_pub_key(friend): if not stringvalidators.validate_pub_key(friend):
raise onionrexceptions.InvalidPubkey('Public key is invalid') raise onionrexceptions.InvalidPubkey('Public key is invalid')
if friend not in o_inst..listPeers(): if friend not in keydb.listkeys.list_peers():
raise onionrexceptions.KeyNotKnown raise onionrexceptions.KeyNotKnown
friend = onionrusers.OnionrUser(o_inst., friend) friend = onionrusers.OnionrUser(friend)
except IndexError: except IndexError:
logger.warn('Friend ID is required.', terminal=True) logger.warn('Friend ID is required.', terminal=True)
action = 'error' # set to 'error' so that the finally block does not process anything action = 'error' # set to 'error' so that the finally block does not process anything
except onionrexceptions.KeyNotKnown: except onionrexceptions.KeyNotKnown:
o_inst..addPeer(friend) o_inst.addPeer(friend)
friend = onionrusers.OnionrUser(o_inst., friend) friend = onionrusers.OnionrUser(friend)
finally: finally:
if action == 'add': if action == 'add':
friend.setTrust(1) friend.setTrust(1)

View File

@ -36,7 +36,7 @@ class OnionrCrypto:
self.secrets = secrets self.secrets = secrets
self.deterministicRequirement = 25 # Min deterministic password/phrase length self.deterministicRequirement = 25 # Min deterministic password/phrase length
self.HASH_ID_ROUNDS = 2000 self.HASH_ID_ROUNDS = 2000
self.keyManager = keymanager.KeyManager(self) self.keyManager = keymanager.KeyManager()
# Load our own pub/priv Ed25519 keys, gen & save them if they don't exist # Load our own pub/priv Ed25519 keys, gen & save them if they don't exist
if os.path.exists(self._keyFile): if os.path.exists(self._keyFile):
@ -52,47 +52,6 @@ class OnionrCrypto:
self.keyManager.addKey(self.pubKey, self.privKey) self.keyManager.addKey(self.pubKey, self.privKey)
return return
def edVerify(self, data, key, sig, encodedData=True):
'''Verify signed data (combined in nacl) to an ed25519 key'''
try:
key = nacl.signing.VerifyKey(key=key, encoder=nacl.encoding.Base32Encoder)
except nacl.exceptions.ValueError:
#logger.debug('Signature by unknown key (cannot reverse hash)')
return False
except binascii.Error:
logger.warn('Could not load key for verification, invalid padding')
return False
retData = False
sig = base64.b64decode(sig)
try:
data = data.encode()
except AttributeError:
pass
if encodedData:
try:
retData = key.verify(data, sig) # .encode() is not the same as nacl.encoding
except nacl.exceptions.BadSignatureError:
pass
else:
try:
retData = key.verify(data, sig)
except nacl.exceptions.BadSignatureError:
pass
return retData
def edSign(self, data, key, encodeResult=False):
'''Ed25519 sign data'''
try:
data = data.encode()
except AttributeError:
pass
key = nacl.signing.SigningKey(seed=key, encoder=nacl.encoding.Base32Encoder)
retData = ''
if encodeResult:
retData = key.sign(data, encoder=nacl.encoding.Base64Encoder).signature.decode() # .encode() is not the same as nacl.encoding
else:
retData = key.sign(data).signature
return retData
def pubKeyEncrypt(self, data, pubkey, encodedData=False): def pubKeyEncrypt(self, data, pubkey, encodedData=False):
'''Encrypt to a public key (Curve25519, taken from base32 Ed25519 pubkey)''' '''Encrypt to a public key (Curve25519, taken from base32 Ed25519 pubkey)'''
@ -113,25 +72,6 @@ class OnionrCrypto:
return retVal return retVal
def pubKeyDecrypt(self, data, pubkey='', privkey='', encodedData=False):
'''pubkey decrypt (Curve25519, taken from Ed25519 pubkey)'''
decrypted = False
if encodedData:
encoding = nacl.encoding.Base64Encoder
else:
encoding = nacl.encoding.RawEncoder
if privkey == '':
privkey = self.privKey
ownKey = nacl.signing.SigningKey(seed=privkey, encoder=nacl.encoding.Base32Encoder()).to_curve25519_private_key()
if stringvalidators.validate_pub_key(privkey):
privkey = nacl.signing.SigningKey(seed=privkey, encoder=nacl.encoding.Base32Encoder()).to_curve25519_private_key()
anonBox = nacl.public.SealedBox(privkey)
else:
anonBox = nacl.public.SealedBox(ownKey)
decrypted = anonBox.decrypt(data, encoder=encoding)
return decrypted
def symmetricEncrypt(self, data, key, encodedKey=False, returnEncoded=True): def symmetricEncrypt(self, data, key, encodedKey=False, returnEncoded=True):
'''Encrypt data with a 32-byte key (Salsa20-Poly1305 MAC)''' '''Encrypt data with a 32-byte key (Salsa20-Poly1305 MAC)'''
if encodedKey: if encodedKey:
@ -252,37 +192,3 @@ class OnionrCrypto:
logger.debug("Invalid token, bad proof") logger.debug("Invalid token, bad proof")
return retData return retData
@staticmethod
def replayTimestampValidation(timestamp):
if epoch.get_epoch() - int(timestamp) > 2419200:
return False
else:
return True
@staticmethod
def safeCompare(one, two):
# Do encode here to avoid spawning core
try:
one = one.encode()
except AttributeError:
pass
try:
two = two.encode()
except AttributeError:
pass
return hmac.compare_digest(one, two)
@staticmethod
def randomShuffle(theList):
myList = list(theList)
shuffledList = []
myListLength = len(myList) + 1
while myListLength > 0:
removed = secrets.randbelow(myListLength)
try:
shuffledList.append(myList.pop(removed))
except IndexError:
pass
myListLength = len(myList)
return shuffledList

View File

@ -0,0 +1,5 @@
from . import safecompare, replayvalidation, randomshuffle
replay_validator = replayvalidation.replay_timestamp_validation
random_shuffle = randomshuffle.random_shuffle
safe_compare = safecompare.safe_compare

View File

@ -0,0 +1,13 @@
import secrets
def random_shuffle(theList):
myList = list(theList)
shuffledList = []
myListLength = len(myList) + 1
while myListLength > 0:
removed = secrets.randbelow(myListLength)
try:
shuffledList.append(myList.pop(removed))
except IndexError:
pass
myListLength = len(myList)
return shuffledList

View File

@ -0,0 +1,6 @@
import utils # onionr utils epoch, not this utils
def replay_timestamp_validation(timestamp):
if utils.epoch.get_epoch() - int(timestamp) > 2419200:
return False
else:
return True

View File

@ -0,0 +1,12 @@
import hmac
def safe_compare(one, two):
# Do encode here to avoid spawning core
try:
one = one.encode()
except AttributeError:
pass
try:
two = two.encode()
except AttributeError:
pass
return hmac.compare_digest(one, two)

View File

@ -0,0 +1,24 @@
import nacl.encoding, nacl.public, nacl.signing
from .. import getourkeypair
pair = getourkeypair.get_keypair()
our_pub_key = pair[0]
our_priv_key = pair[1]
def pub_key_decrypt(data, pubkey='', privkey='', encodedData=False):
'''pubkey decrypt (Curve25519, taken from Ed25519 pubkey)'''
decrypted = False
if encodedData:
encoding = nacl.encoding.Base64Encoder
else:
encoding = nacl.encoding.RawEncoder
if privkey == '':
privkey = our_priv_key
ownKey = nacl.signing.SigningKey(seed=privkey, encoder=nacl.encoding.Base32Encoder()).to_curve25519_private_key()
if stringvalidators.validate_pub_key(privkey):
privkey = nacl.signing.SigningKey(seed=privkey, encoder=nacl.encoding.Base32Encoder()).to_curve25519_private_key()
anonBox = nacl.public.SealedBox(privkey)
else:
anonBox = nacl.public.SealedBox(ownKey)
decrypted = anonBox.decrypt(data, encoder=encoding)
return decrypted

View File

@ -0,0 +1,17 @@
import os
import keymanager, config, filepaths
from . import generate
def get_keypair():
key_m = keymanager.KeyManager()
if os.path.exists(filepaths.keys_file):
if len(config.get('general.public_key', '')) > 0:
pubKey = config.get('general.public_key')
else:
pubKey = key_m.getPubkeyList()[0]
privKey = key_m.getPrivkey(pubKey)
else:
keys = generate.generate_pub_key()
pubKey = keys[0]
privKey = keys[1]
key_m.addKey(pubKey, privKey)
return (pubKey, privKey)

View File

@ -0,0 +1,44 @@
import base64, binascii
import nacl.encoding, nacl.signing, nacl.exceptions
import logger
def ed_sign(data, key, encodeResult=False):
'''Ed25519 sign data'''
try:
data = data.encode()
except AttributeError:
pass
key = nacl.signing.SigningKey(seed=key, encoder=nacl.encoding.Base32Encoder)
retData = ''
if encodeResult:
retData = key.sign(data, encoder=nacl.encoding.Base64Encoder).signature.decode() # .encode() is not the same as nacl.encoding
else:
retData = key.sign(data).signature
return retData
def ed_verify(data, key, sig, encodedData=True):
'''Verify signed data (combined in nacl) to an ed25519 key'''
try:
key = nacl.signing.VerifyKey(key=key, encoder=nacl.encoding.Base32Encoder)
except nacl.exceptions.ValueError:
#logger.debug('Signature by unknown key (cannot reverse hash)')
return False
except binascii.Error:
logger.warn('Could not load key for verification, invalid padding')
return False
retData = False
sig = base64.b64decode(sig)
try:
data = data.encode()
except AttributeError:
pass
if encodedData:
try:
retData = key.verify(data, sig) # .encode() is not the same as nacl.encoding
except nacl.exceptions.BadSignatureError:
pass
else:
try:
retData = key.verify(data, sig)
except nacl.exceptions.BadSignatureError:
pass
return retData

View File

@ -18,11 +18,10 @@
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 multiprocessing, nacl.encoding, nacl.hash, nacl.utils, time, math, threading, binascii, sys, json import multiprocessing, nacl.encoding, nacl.hash, nacl.utils, time, math, threading, binascii, sys, json
import config, logger, onionrblockapi, storagecounter, onionrcrypto import config, logger, onionrblockapi, storagecounter
from onionrutils import bytesconverter from onionrutils import bytesconverter
from onionrcrypto import hashers
config.reload() config.reload()
crypto = onionrcrypto.OnionrCrypto()
def getDifficultyModifier(): def getDifficultyModifier():
'''returns the difficulty modifier for block storage based '''returns the difficulty modifier for block storage based
on a variety of factors, currently only disk use. on a variety of factors, currently only disk use.
@ -227,7 +226,7 @@ class POW:
#token = nacl.hash.blake2b(rand + self.data).decode() #token = nacl.hash.blake2b(rand + self.data).decode()
self.metadata['pow'] = nonce self.metadata['pow'] = nonce
payload = json.dumps(self.metadata).encode() + b'\n' + self.data payload = json.dumps(self.metadata).encode() + b'\n' + self.data
token = crypto.sha3Hash(payload) token = hashers.sha3_hash(payload)
try: try:
# on some versions, token is bytes # on some versions, token is bytes
token = token.decode() token = token.decode()

View File

@ -21,7 +21,7 @@ import json
import logger, onionrexceptions import logger, onionrexceptions
from etc import onionrvalues from etc import onionrvalues
from onionrutils import stringvalidators, epoch, bytesconverter from onionrutils import stringvalidators, epoch, bytesconverter
import config, onionrvalues, filepaths, onionrcrypto import config, filepaths, onionrcrypto
def validate_metadata(metadata, blockData): def validate_metadata(metadata, blockData):
'''Validate metadata meets onionr spec (does not validate proof value computation), take in either dictionary or json string''' '''Validate metadata meets onionr spec (does not validate proof value computation), take in either dictionary or json string'''
# TODO, make this check sane sizes # TODO, make this check sane sizes

View File

@ -49,9 +49,9 @@ class SubprocessPOW:
self.data = bytesconverter.str_to_bytes(data) self.data = bytesconverter.str_to_bytes(data)
# Calculate difficulty. Dumb for now, may use good algorithm in the future. # Calculate difficulty. Dumb for now, may use good algorithm in the future.
self.difficulty = onionrproofs.getDifficultyForNewBlock(bytes(json_metadata + b'\n' + self.data) self.difficulty = onionrproofs.getDifficultyForNewBlock(bytes(json_metadata + b'\n' + self.data))
logger.info('Computing POW (difficulty: %s)...' % self.difficulty) logger.info('Computing POW (difficulty: %s)...' % (self.difficulty,))
self.mainHash = '0' * 64 self.mainHash = '0' * 64
self.puzzle = self.mainHash[0:min(self.difficulty, len(self.mainHash))] self.puzzle = self.mainHash[0:min(self.difficulty, len(self.mainHash))]

View File

@ -1,6 +1,6 @@
import os import os
def read_static(file, ret_bin=False): def read_static(file, ret_bin=False):
static_file = os.path.realpath(__file__) + '../static-data/' + file static_file = os.path.dirname(os.path.realpath(__file__)) + '/../static-data/' + file
if ret_bin: if ret_bin:
mode = 'rb' mode = 'rb'