prevent replay of very old encrypted data
This commit is contained in:
parent
31039861c2
commit
651fc8c43c
12 changed files with 119 additions and 24 deletions
|
@ -689,6 +689,8 @@ class Core:
|
|||
return False
|
||||
retData = False
|
||||
|
||||
createTime = self._utils.getRoundedEpoch()
|
||||
|
||||
# check nonce
|
||||
dataNonce = self._utils.bytesToStr(self._crypto.sha3Hash(data))
|
||||
try:
|
||||
|
@ -706,10 +708,7 @@ class Core:
|
|||
data = str(data)
|
||||
plaintext = data
|
||||
plaintextMeta = {}
|
||||
|
||||
# Convert asym peer human readable key to base32 if set
|
||||
if ' ' in asymPeer.strip():
|
||||
asymPeer = self._utils.convertHumanReadableID(asymPeer)
|
||||
plaintextPeer = asymPeer
|
||||
|
||||
retData = ''
|
||||
signature = ''
|
||||
|
@ -732,6 +731,7 @@ class Core:
|
|||
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)
|
||||
|
@ -779,7 +779,7 @@ class Core:
|
|||
metadata['meta'] = jsonMeta
|
||||
metadata['sig'] = signature
|
||||
metadata['signer'] = signer
|
||||
metadata['time'] = self._utils.getRoundedEpoch()
|
||||
metadata['time'] = createTime
|
||||
|
||||
# ensure expire is integer and of sane length
|
||||
if type(expire) is not type(None):
|
||||
|
@ -804,7 +804,10 @@ class Core:
|
|||
self.daemonQueueAdd('uploadBlock', retData)
|
||||
|
||||
if retData != False:
|
||||
events.event('insertblock', {'content': plaintext, 'meta': plaintextMeta, 'hash': retData, 'peer': self._utils.bytesToStr(asymPeer)}, onionr = self.onionrInst, threaded = True)
|
||||
if plaintextPeer == 'OVPCZLOXD6DC5JHX4EQ3PSOGAZ3T24F75HQLIUZSDSMYPEOXCPFA====':
|
||||
events.event('insertdeniable', {'content': plaintext, 'meta': plaintextMeta, 'hash': retData, 'peer': self._utils.bytesToStr(asymPeer)}, onionr = self.onionrInst, threaded = True)
|
||||
else:
|
||||
events.event('insertblock', {'content': plaintext, 'meta': plaintextMeta, 'hash': retData, 'peer': self._utils.bytesToStr(asymPeer)}, onionr = self.onionrInst, threaded = True)
|
||||
return retData
|
||||
|
||||
def introduceNode(self):
|
||||
|
|
|
@ -26,7 +26,7 @@ class Block:
|
|||
blockCacheOrder = list() # NEVER write your own code that writes to this!
|
||||
blockCache = dict() # should never be accessed directly, look at Block.getCache()
|
||||
|
||||
def __init__(self, hash = None, core = None, type = None, content = None, expire=None, decrypt=False):
|
||||
def __init__(self, hash = None, core = None, type = None, content = None, expire=None, decrypt=False, bypassReplayCheck=False):
|
||||
# take from arguments
|
||||
# sometimes people input a bytes object instead of str in `hash`
|
||||
if (not hash is None) and isinstance(hash, bytes):
|
||||
|
@ -37,6 +37,7 @@ class Block:
|
|||
self.btype = type
|
||||
self.bcontent = content
|
||||
self.expire = expire
|
||||
self.bypassReplayCheck = bypassReplayCheck
|
||||
|
||||
# initialize variables
|
||||
self.valid = True
|
||||
|
@ -84,6 +85,19 @@ class Block:
|
|||
self.signer = core._crypto.pubKeyDecrypt(self.signer, encodedData=encodedData)
|
||||
self.bheader['signer'] = self.signer.decode()
|
||||
self.signedData = json.dumps(self.bmetadata) + self.bcontent.decode()
|
||||
|
||||
# Check for replay attacks
|
||||
try:
|
||||
assert self.core._crypto.replayTimestampValidation(self.bmetadata['rply'])
|
||||
except (AssertionError, KeyError) as e:
|
||||
if not self.bypassReplayCheck:
|
||||
# Zero out variables to prevent reading of replays
|
||||
self.bmetadata = {}
|
||||
self.signer = ''
|
||||
self.bheader['signer'] = ''
|
||||
self.signedData = ''
|
||||
self.signature = ''
|
||||
raise onionrexceptions.ReplayAttack('Signature is too old. possible replay attack')
|
||||
try:
|
||||
assert self.bmetadata['forwardEnc'] is True
|
||||
except (AssertionError, KeyError) as e:
|
||||
|
@ -97,6 +111,8 @@ class Block:
|
|||
except nacl.exceptions.CryptoError:
|
||||
pass
|
||||
#logger.debug('Could not decrypt block. Either invalid key or corrupted data')
|
||||
except onionrexceptions.ReplayAttack:
|
||||
logger.warn('%s is possibly a replay attack' % (self.hash,))
|
||||
else:
|
||||
retData = True
|
||||
self.decrypted = True
|
||||
|
|
|
@ -264,6 +264,13 @@ class OnionrCrypto:
|
|||
|
||||
return retData
|
||||
|
||||
@staticmethod
|
||||
def replayTimestampValidation(timestamp):
|
||||
if core.Core()._utils.getEpoch() - int(timestamp) > 2419200:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def safeCompare(one, two):
|
||||
# Do encode here to avoid spawning core
|
||||
|
|
|
@ -198,8 +198,8 @@ class DaemonTools:
|
|||
fakePeer = ''
|
||||
chance = 10
|
||||
if secrets.randbelow(chance) == (chance - 1):
|
||||
fakePeer = self.daemon._core._crypto.generatePubKey()[0]
|
||||
fakePeer = 'OVPCZLOXD6DC5JHX4EQ3PSOGAZ3T24F75HQLIUZSDSMYPEOXCPFA===='
|
||||
data = secrets.token_hex(secrets.randbelow(500) + 1)
|
||||
self.daemon._core.insertBlock(data, header='db', encryptType='asym', asymPeer=fakePeer, meta={'subject': 'foo'})
|
||||
self.daemon._core.insertBlock(data, header='pm', encryptType='asym', asymPeer=fakePeer, meta={'subject': 'foo'})
|
||||
self.daemon.decrementThreadCount('insertDeniableBlock')
|
||||
return
|
|
@ -45,6 +45,9 @@ class PasswordStrengthError(Exception):
|
|||
|
||||
# block exceptions
|
||||
|
||||
class ReplayAttack(Exception):
|
||||
pass
|
||||
|
||||
class DifficultyTooLarge(Exception):
|
||||
pass
|
||||
|
||||
|
|
|
@ -284,6 +284,12 @@ class OnionrUtils:
|
|||
except AssertionError:
|
||||
logger.warn('Block is expired')
|
||||
break
|
||||
elif i == 'encryptType':
|
||||
try:
|
||||
assert metadata[i] in ('asym', 'sym', '')
|
||||
except AssertionError:
|
||||
logger.warn('Invalid encryption mode')
|
||||
break
|
||||
else:
|
||||
# if metadata loop gets no errors, it does not break, therefore metadata is valid
|
||||
# make sure we do not have another block with the same data content (prevent data duplication and replay attacks)
|
||||
|
|
|
@ -43,7 +43,6 @@ def draw_border(text):
|
|||
res.append('└' + '─' * width + '┘')
|
||||
return '\n'.join(res)
|
||||
|
||||
|
||||
class MailStrings:
|
||||
def __init__(self, mailInstance):
|
||||
self.mailInstance = mailInstance
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
</div>
|
||||
<div id='sendMessage' class='overlay'>
|
||||
<div class='overlayContent'>
|
||||
<label>Select friend: <select id='friendSelect'></select></label>
|
||||
<form method='post' action='/apipoints/mail/send' id='sendForm' enctype="application/x-www-form-urlencoded">
|
||||
<span class='closeOverlay' overlay='sendMessage'></span>
|
||||
To: <input id='draftID' type='text' name='to' placeholder='pubkey' required>
|
||||
|
|
|
@ -78,7 +78,7 @@ function loadInboxEntrys(bHash){
|
|||
var metadata = resp['metadata']
|
||||
humanDate.setUTCSeconds(resp['meta']['time'])
|
||||
if (resp['meta']['signer'] != ''){
|
||||
senderInput.value = httpGet('/getHumanReadable/' + resp['meta']['signer'])
|
||||
senderInput.value = httpGet('/friends/getinfo/' + resp['meta']['signer'] + '/name')
|
||||
}
|
||||
if (resp['meta']['validSig']){
|
||||
validSig.innerText = 'Signature Validity: Good'
|
||||
|
@ -88,7 +88,7 @@ function loadInboxEntrys(bHash){
|
|||
validSig.style.color = 'red'
|
||||
}
|
||||
if (senderInput.value == ''){
|
||||
senderInput.value = 'Anonymous'
|
||||
senderInput.value = resp['meta']['signer']
|
||||
}
|
||||
bHashDisplay.innerText = bHash.substring(0, 10)
|
||||
entry.setAttribute('hash', bHash)
|
||||
|
@ -195,17 +195,18 @@ tabBtns.onclick = function(event){
|
|||
setActiveTab(event.target.innerText.toLowerCase())
|
||||
}
|
||||
|
||||
|
||||
var idStrings = document.getElementsByClassName('myPub')
|
||||
var myHumanReadable = httpGet('/getHumanReadable/' + myPub)
|
||||
for (var i = 0; i < idStrings.length; i++){
|
||||
if (idStrings[i].tagName.toLowerCase() == 'input'){
|
||||
idStrings[i].value = myHumanReadable
|
||||
idStrings[i].value = myPub
|
||||
}
|
||||
else{
|
||||
idStrings[i].innerText = myHumanReadable
|
||||
idStrings[i].innerText = myPub
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (var i = 0; i < document.getElementsByClassName('refresh').length; i++){
|
||||
document.getElementsByClassName('refresh')[i].style.float = 'right'
|
||||
}
|
||||
|
@ -216,3 +217,34 @@ for (var i = 0; i < document.getElementsByClassName('closeOverlay').length; i++)
|
|||
}
|
||||
}
|
||||
|
||||
fetch('/friends/list', {
|
||||
headers: {
|
||||
"token": webpass
|
||||
}})
|
||||
.then((resp) => resp.json()) // Transform the data into json
|
||||
.then(function(resp) {
|
||||
var friendSelectParent = document.getElementById('friendSelect')
|
||||
var keys = [];
|
||||
var friend
|
||||
for(var k in resp) keys.push(k);
|
||||
|
||||
friendSelectParent.appendChild(document.createElement('option'))
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var option = document.createElement("option")
|
||||
var name = resp[keys[i]]['name']
|
||||
option.value = keys[i]
|
||||
if (name.length == 0){
|
||||
option.text = keys[i]
|
||||
}
|
||||
else{
|
||||
option.text = name
|
||||
}
|
||||
friendSelectParent.appendChild(option)
|
||||
}
|
||||
|
||||
for (var i = 0; i < keys.length; i++){
|
||||
|
||||
//friendSelectParent
|
||||
//alert(resp[keys[i]]['name'])
|
||||
}
|
||||
})
|
|
@ -18,6 +18,10 @@
|
|||
*/
|
||||
|
||||
var sendbutton = document.getElementById('sendMail')
|
||||
messageContent = document.getElementById('draftText')
|
||||
to = document.getElementById('draftID')
|
||||
subject = document.getElementById('draftSubject')
|
||||
friendPicker = document.getElementById('friendSelect')
|
||||
|
||||
function sendMail(to, message, subject){
|
||||
//postData = {"postData": '{"to": "' + to + '", "message": "' + message + '"}'} // galaxy brain
|
||||
|
@ -35,11 +39,19 @@ function sendMail(to, message, subject){
|
|||
})
|
||||
}
|
||||
|
||||
sendForm.onsubmit = function(){
|
||||
var messageContent = document.getElementById('draftText')
|
||||
var to = document.getElementById('draftID')
|
||||
var subject = document.getElementById('draftSubject')
|
||||
|
||||
sendMail(to.value, messageContent.value, subject.value)
|
||||
return false;
|
||||
var friendPicker = document.getElementById('friendSelect')
|
||||
friendPicker.onchange = function(){
|
||||
to.value = friendPicker.value
|
||||
}
|
||||
|
||||
sendForm.onsubmit = function(){
|
||||
if (friendPicker.value.trim().length !== 0 && to.value.trim().length !== 0){
|
||||
if (friendPicker.value !== to.value){
|
||||
alert('You have selected both a friend and entered a public key manually.')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
sendMail(to.value, messageContent.value, subject.value)
|
||||
return false
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue