working on preventing replay attacks with block content

This commit is contained in:
Kevin Froman 2018-08-16 00:01:40 -05:00
parent ba2e5d7da9
commit 06dc97869e
No known key found for this signature in database
GPG Key ID: 0D414D0FE405B63B
5 changed files with 37 additions and 77 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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:
@ -749,6 +683,12 @@ class Core:
metadata['sig'] = signature metadata['sig'] = signature
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)

View File

@ -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

View File

@ -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')