Onionr/onionr/onionrblocks/insert.py

146 lines
6.1 KiB
Python
Raw Normal View History

2019-07-18 23:07:18 +00:00
import json
from onionrutils import bytesconverter, epoch
2019-07-19 19:49:56 +00:00
import storagecounter, filepaths, onionrstorage
2019-07-18 23:07:18 +00:00
import onionrevents as events
2019-07-19 19:49:56 +00:00
from etc import powchoice, onionrvalues
2019-07-20 15:52:03 +00:00
import config, onionrcrypto as crypto, subprocesspow, onionrexceptions
from onionrusers import onionrusers
2019-07-21 16:15:20 +00:00
from onionrutils import localcommand, blockmetadata, stringvalidators
2019-07-20 15:52:03 +00:00
import coredb
def insert_block(data, header='txt', sign=False, encryptType='', symKey='', asymPeer='', meta = {}, expire=None, disableForward=False):
2019-07-18 23:07:18 +00:00
'''
Inserts a block into the network
encryptType must be specified to encrypt a block
'''
2019-07-20 15:52:03 +00:00
use_subprocess = powchoice.use_subprocess(config)
2019-07-18 23:07:18 +00:00
storage_counter = storagecounter.StorageCounter()
allocationReachedMessage = 'Cannot insert block, disk allocation reached.'
if 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()
2019-07-20 15:52:03 +00:00
dataNonce = bytesconverter.bytes_to_str(crypto.hashers.sha3_hash(data))
2019-07-18 23:07:18 +00:00
try:
with open(filepaths.data_nonce_file, 'r') as nonces:
if dataNonce in nonces:
return retData
except FileNotFoundError:
pass
# record nonce
with open(filepaths.data_nonce_file, '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
2019-07-21 16:15:20 +00:00
if not disableForward and sign and asymPeer != crypto.pub_key:
2019-07-18 23:07:18 +00:00
try:
forwardEncrypted = onionrusers.OnionrUser(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(asymPeer).generateForwardKey()
#fsKey = onionrusers.OnionrUser(self, asymPeer).getGeneratedForwardKeys().reverse()
meta['newFSKey'] = fsKey
jsonMeta = json.dumps(meta)
plaintextMeta = jsonMeta
if sign:
2019-07-20 15:52:03 +00:00
signature = crypto.signing.ed_sign(jsonMeta.encode() + data, key=crypto.priv_key, encodeResult=True)
2019-07-21 16:15:20 +00:00
signer = crypto.pub_key
2019-07-18 23:07:18 +00:00
if len(jsonMeta) > 1000:
raise onionrexceptions.InvalidMetadata('meta in json encoded form must not exceed 1000 bytes')
# encrypt block metadata/sig/content
if encryptType == 'sym':
2019-07-20 15:52:03 +00:00
raise NotImplementedError("not yet implemented")
2019-07-18 23:07:18 +00:00
elif encryptType == 'asym':
if stringvalidators.validate_pub_key(asymPeer):
# Encrypt block data with forward secrecy key first, but not meta
jsonMeta = json.dumps(meta)
2019-07-20 15:52:03 +00:00
jsonMeta = crypto.encryption.pub_key_encrypt(jsonMeta, asymPeer, encodedData=True).decode()
data = crypto.encryption.pub_key_encrypt(data, asymPeer, encodedData=True).decode()
2019-07-21 16:15:20 +00:00
signature = crypto.encryption.pub_key_encrypt(signature, asymPeer, encodedData=True).decode()
signer = crypto.encryption.pub_key_encrypt(signer, asymPeer, encodedData=True).decode()
2019-07-18 23:07:18 +00:00
try:
onionrusers.OnionrUser(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 use_subprocess:
payload = subprocesspow.SubprocessPOW(data, metadata).start()
else:
payload = onionrproofs.POW(metadata, data).waitForResult()
if payload != False:
try:
2019-07-24 17:22:19 +00:00
retData = onionrstorage.set_data(payload)
2019-07-18 23:07:18 +00:00
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('/ping', maxWait=10) == 'pong!':
if config.get('general.security_level', 1) == 0:
localcommand.local_command('/waitforshare/' + retData, post=True, maxWait=5)
coredb.daemonqueue.daemon_queue_add('uploadBlock', retData)
else:
pass
2019-07-20 15:52:03 +00:00
coredb.blockmetadb.add.add_to_block_DB(retData, selfInsert=True, dataSaved=True)
blockmetadata.process_block_metadata(retData)
2019-07-24 18:23:31 +00:00
2019-07-18 23:07:18 +00:00
if retData != False:
if plaintextPeer == onionrvalues.DENIABLE_PEER_ADDRESS:
2019-07-24 18:23:31 +00:00
events.event('insertdeniable', {'content': plaintext, 'meta': plaintextMeta, 'hash': retData, 'peer': bytesconverter.bytes_to_str(asymPeer)}, threaded = True)
2019-07-18 23:07:18 +00:00
else:
2019-07-24 18:23:31 +00:00
events.event('insertblock', {'content': plaintext, 'meta': plaintextMeta, 'hash': retData, 'peer': bytesconverter.bytes_to_str(asymPeer)}, threaded = True)
2019-07-18 23:07:18 +00:00
return retData