working on preventing replay attacks with block content
parent
ba2e5d7da9
commit
06dc97869e
|
@ -36,7 +36,7 @@ def importBlockFromData(content, coreInst):
|
||||||
|
|
||||||
metas = coreInst._utils.getBlockMetadataFromData(content) # returns tuple(metadata, meta), meta is also in metadata
|
metas = coreInst._utils.getBlockMetadataFromData(content) # returns tuple(metadata, meta), meta is also in metadata
|
||||||
metadata = metas[0]
|
metadata = metas[0]
|
||||||
if coreInst._utils.validateMetadata(metadata): # check if metadata is valid
|
if coreInst._utils.validateMetadata(metadata, metas[2]): # check if metadata is valid
|
||||||
if coreInst._crypto.verifyPow(content): # check if POW is enough/correct
|
if coreInst._crypto.verifyPow(content): # check if POW is enough/correct
|
||||||
logger.info('Block passed proof, saving.')
|
logger.info('Block passed proof, saving.')
|
||||||
blockHash = coreInst.setData(content)
|
blockHash = coreInst.setData(content)
|
||||||
|
|
|
@ -206,7 +206,7 @@ class OnionrCommunicatorDaemon:
|
||||||
metas = self._core._utils.getBlockMetadataFromData(content) # returns tuple(metadata, meta), meta is also in metadata
|
metas = self._core._utils.getBlockMetadataFromData(content) # returns tuple(metadata, meta), meta is also in metadata
|
||||||
metadata = metas[0]
|
metadata = metas[0]
|
||||||
#meta = metas[1]
|
#meta = metas[1]
|
||||||
if self._core._utils.validateMetadata(metadata): # check if metadata is valid
|
if self._core._utils.validateMetadata(metadata, metas[2]): # check if metadata is valid, and verify nonce
|
||||||
if self._core._crypto.verifyPow(content): # check if POW is enough/correct
|
if self._core._crypto.verifyPow(content): # check if POW is enough/correct
|
||||||
logger.info('Block passed proof, saving.')
|
logger.info('Block passed proof, saving.')
|
||||||
self._core.setData(content)
|
self._core.setData(content)
|
||||||
|
|
|
@ -22,7 +22,7 @@ from onionrblockapi import Block
|
||||||
|
|
||||||
import onionrutils, onionrcrypto, onionrproofs, onionrevents as events, onionrexceptions, onionrvalues
|
import onionrutils, onionrcrypto, onionrproofs, onionrevents as events, onionrexceptions, onionrvalues
|
||||||
import onionrblacklist
|
import onionrblacklist
|
||||||
|
import dbcreator
|
||||||
if sys.version_info < (3, 6):
|
if sys.version_info < (3, 6):
|
||||||
try:
|
try:
|
||||||
import sha3
|
import sha3
|
||||||
|
@ -46,6 +46,8 @@ class Core:
|
||||||
self.bootstrapList = []
|
self.bootstrapList = []
|
||||||
self.requirements = onionrvalues.OnionrValues()
|
self.requirements = onionrvalues.OnionrValues()
|
||||||
self.torPort = torPort
|
self.torPort = torPort
|
||||||
|
self.dataNonceFile = 'data/block-nonces.dat'
|
||||||
|
self.dbCreate = dbcreator.DBCreator(self)
|
||||||
|
|
||||||
self.usageFile = 'data/disk-usage.txt'
|
self.usageFile = 'data/disk-usage.txt'
|
||||||
|
|
||||||
|
@ -188,89 +190,20 @@ class Core:
|
||||||
def createAddressDB(self):
|
def createAddressDB(self):
|
||||||
'''
|
'''
|
||||||
Generate the address database
|
Generate the address database
|
||||||
|
|
||||||
types:
|
|
||||||
1: I2P b32 address
|
|
||||||
2: Tor v2 (like facebookcorewwwi.onion)
|
|
||||||
3: Tor v3
|
|
||||||
'''
|
'''
|
||||||
conn = sqlite3.connect(self.addressDB)
|
self.dbCreate.createAddressDB()
|
||||||
c = conn.cursor()
|
|
||||||
c.execute('''CREATE TABLE adders(
|
|
||||||
address text,
|
|
||||||
type int,
|
|
||||||
knownPeer text,
|
|
||||||
speed int,
|
|
||||||
success int,
|
|
||||||
DBHash text,
|
|
||||||
powValue text,
|
|
||||||
failure int,
|
|
||||||
lastConnect int,
|
|
||||||
lastConnectAttempt int,
|
|
||||||
trust int
|
|
||||||
);
|
|
||||||
''')
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
def createPeerDB(self):
|
def createPeerDB(self):
|
||||||
'''
|
'''
|
||||||
Generate the peer sqlite3 database and populate it with the peers table.
|
Generate the peer sqlite3 database and populate it with the peers table.
|
||||||
'''
|
'''
|
||||||
# generate the peer database
|
self.dbCreate.createPeerDB()
|
||||||
conn = sqlite3.connect(self.peerDB)
|
|
||||||
c = conn.cursor()
|
|
||||||
c.execute('''CREATE TABLE peers(
|
|
||||||
ID text not null,
|
|
||||||
name text,
|
|
||||||
adders text,
|
|
||||||
blockDBHash text,
|
|
||||||
forwardKey text,
|
|
||||||
dateSeen not null,
|
|
||||||
bytesStored int,
|
|
||||||
trust int,
|
|
||||||
pubkeyExchanged int,
|
|
||||||
hashID text,
|
|
||||||
pow text not null);
|
|
||||||
''')
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
return
|
|
||||||
|
|
||||||
def createBlockDB(self):
|
def createBlockDB(self):
|
||||||
'''
|
'''
|
||||||
Create a database for blocks
|
Create a database for blocks
|
||||||
|
|
||||||
hash - the hash of a block
|
|
||||||
dateReceived - the date the block was recieved, not necessarily when it was created
|
|
||||||
decrypted - if we can successfully decrypt the block (does not describe its current state)
|
|
||||||
dataType - data type of the block
|
|
||||||
dataFound - if the data has been found for the block
|
|
||||||
dataSaved - if the data has been saved for the block
|
|
||||||
sig - optional signature by the author (not optional if author is specified)
|
|
||||||
author - multi-round partial sha3-256 hash of authors public key
|
|
||||||
dateClaimed - timestamp claimed inside the block, only as trustworthy as the block author is
|
|
||||||
'''
|
'''
|
||||||
if os.path.exists(self.blockDB):
|
self.dbCreate.createBlockDB()
|
||||||
raise Exception("Block database already exists")
|
|
||||||
conn = sqlite3.connect(self.blockDB)
|
|
||||||
c = conn.cursor()
|
|
||||||
c.execute('''CREATE TABLE hashes(
|
|
||||||
hash text not null,
|
|
||||||
dateReceived int,
|
|
||||||
decrypted int,
|
|
||||||
dataType text,
|
|
||||||
dataFound int,
|
|
||||||
dataSaved int,
|
|
||||||
sig text,
|
|
||||||
author text,
|
|
||||||
dateClaimed int
|
|
||||||
);
|
|
||||||
''')
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def addToBlockDB(self, newHash, selfInsert=False, dataSaved=False):
|
def addToBlockDB(self, newHash, selfInsert=False, dataSaved=False):
|
||||||
'''
|
'''
|
||||||
|
@ -702,6 +635,7 @@ class Core:
|
||||||
signature = ''
|
signature = ''
|
||||||
signer = ''
|
signer = ''
|
||||||
metadata = {}
|
metadata = {}
|
||||||
|
# metadata is full block metadata, meta is internal, user specified metadata
|
||||||
|
|
||||||
# only use header if not set in provided meta
|
# only use header if not set in provided meta
|
||||||
if not header is None:
|
if not header is None:
|
||||||
|
@ -750,6 +684,12 @@ class Core:
|
||||||
metadata['signer'] = signer
|
metadata['signer'] = signer
|
||||||
metadata['time'] = str(self._utils.getEpoch())
|
metadata['time'] = str(self._utils.getEpoch())
|
||||||
|
|
||||||
|
nonce = self._utils.bytesToStr(self._crypto.sha3Hash(data))
|
||||||
|
|
||||||
|
# TODO check in advance
|
||||||
|
with open(self.dataNonceFile, 'a') as nonceFile:
|
||||||
|
nonceFile.write(nonce + '\n')
|
||||||
|
|
||||||
# send block data (and metadata) to POW module to get tokenized block data
|
# send block data (and metadata) to POW module to get tokenized block data
|
||||||
proof = onionrproofs.POW(metadata, data)
|
proof = onionrproofs.POW(metadata, data)
|
||||||
payload = proof.waitForResult()
|
payload = proof.waitForResult()
|
||||||
|
|
|
@ -41,6 +41,9 @@ class InvalidMetadata(Exception):
|
||||||
class BlacklistedBlock(Exception):
|
class BlacklistedBlock(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class DataExists(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
class InvalidHexHash(Exception):
|
class InvalidHexHash(Exception):
|
||||||
'''When a string is not a valid hex string of appropriate length for a hash value'''
|
'''When a string is not a valid hex string of appropriate length for a hash value'''
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -334,7 +334,7 @@ class OnionrUtils:
|
||||||
|
|
||||||
return retVal
|
return retVal
|
||||||
|
|
||||||
def validateMetadata(self, metadata):
|
def validateMetadata(self, 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
|
||||||
retData = False
|
retData = False
|
||||||
|
@ -364,7 +364,24 @@ class OnionrUtils:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# if metadata loop gets no errors, it does not break, therefore metadata is valid
|
# if metadata loop gets no errors, it does not break, therefore metadata is valid
|
||||||
retData = True
|
# make sure we do not have another block with the same data content (prevent data duplication and replay attacks)
|
||||||
|
try:
|
||||||
|
with open(self._core.dataNonceFile, 'r') as nonceFile:
|
||||||
|
nonce = self._core._utils.bytesToStr(self._core._crypto.sha3Hash(blockData))
|
||||||
|
if nonce in nonceFile.read():
|
||||||
|
retData = False # we've seen that nonce before, so we can't pass metadata
|
||||||
|
raise onionrexceptions.DataExists
|
||||||
|
except FileNotFoundError:
|
||||||
|
retData = True
|
||||||
|
except onionrexceptions.DataExists:
|
||||||
|
# do not set retData to True, because nonce has been seen before
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
retData = True
|
||||||
|
if retData:
|
||||||
|
# Executes if data not seen
|
||||||
|
with open(self._core.dataNonceFile, 'a') as nonceFile:
|
||||||
|
nonceFile.write(nonce + '\n')
|
||||||
else:
|
else:
|
||||||
logger.warn('In call to utils.validateMetadata, metadata must be JSON string or a dictionary object')
|
logger.warn('In call to utils.validateMetadata, metadata must be JSON string or a dictionary object')
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue