implement stealth ack objects #1062
|
@ -35,6 +35,7 @@ import network.stats
|
||||||
|
|
||||||
# Classes
|
# Classes
|
||||||
from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute,sqlStoredProcedure
|
from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute,sqlStoredProcedure
|
||||||
|
from helper_ackPayload import genAckPayload
|
||||||
from debug import logger
|
from debug import logger
|
||||||
from inventory import Inventory
|
from inventory import Inventory
|
||||||
from version import softwareVersion
|
from version import softwareVersion
|
||||||
|
@ -679,7 +680,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
if not fromAddressEnabled:
|
if not fromAddressEnabled:
|
||||||
raise APIError(14, 'Your fromAddress is disabled. Cannot send.')
|
raise APIError(14, 'Your fromAddress is disabled. Cannot send.')
|
||||||
|
|
||||||
ackdata = OpenSSL.rand(32)
|
stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel')
|
||||||
|
ackdata = genAckPayload(streamNumber, stealthLevel)
|
||||||
|
|
||||||
t = ('',
|
t = ('',
|
||||||
toAddress,
|
toAddress,
|
||||||
|
@ -740,7 +742,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
fromAddress, 'enabled')
|
fromAddress, 'enabled')
|
||||||
except:
|
except:
|
||||||
raise APIError(13, 'could not find your fromAddress in the keys.dat file.')
|
raise APIError(13, 'could not find your fromAddress in the keys.dat file.')
|
||||||
ackdata = OpenSSL.rand(32)
|
ackdata = genAckPayload(streamNumber, 0)
|
||||||
toAddress = '[Broadcast subscribers]'
|
toAddress = '[Broadcast subscribers]'
|
||||||
ripe = ''
|
ripe = ''
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import curses
|
||||||
import dialog
|
import dialog
|
||||||
from dialog import Dialog
|
from dialog import Dialog
|
||||||
from helper_sql import *
|
from helper_sql import *
|
||||||
|
from helper_ackPayload import genAckPayload
|
||||||
|
|
||||||
from addresses import *
|
from addresses import *
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
@ -778,7 +779,8 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
|
||||||
if len(shared.connectedHostsList) == 0:
|
if len(shared.connectedHostsList) == 0:
|
||||||
set_background_title(d, "Not connected warning")
|
set_background_title(d, "Not connected warning")
|
||||||
scrollbox(d, unicode("Because you are not currently connected to the network, "))
|
scrollbox(d, unicode("Because you are not currently connected to the network, "))
|
||||||
ackdata = OpenSSL.rand(32)
|
stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel')
|
||||||
|
ackdata = genAckPayload(streamNumber, stealthLevel)
|
||||||
sqlExecute(
|
sqlExecute(
|
||||||
"INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
"INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||||
"",
|
"",
|
||||||
|
@ -802,7 +804,8 @@ def sendMessage(sender="", recv="", broadcast=None, subject="", body="", reply=F
|
||||||
set_background_title(d, "Empty sender error")
|
set_background_title(d, "Empty sender error")
|
||||||
scrollbox(d, unicode("You must specify an address to send the message from."))
|
scrollbox(d, unicode("You must specify an address to send the message from."))
|
||||||
else:
|
else:
|
||||||
ackdata = OpenSSL.rand(32)
|
# dummy ackdata, no need for stealth
|
||||||
|
ackdata = genAckPayload(streamNumber, 0)
|
||||||
recv = BROADCAST_STR
|
recv = BROADCAST_STR
|
||||||
ripe = ""
|
ripe = ""
|
||||||
sqlExecute(
|
sqlExecute(
|
||||||
|
|
|
@ -52,6 +52,7 @@ import random
|
||||||
import string
|
import string
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from helper_sql import *
|
from helper_sql import *
|
||||||
|
from helper_ackPayload import genAckPayload
|
||||||
import helper_search
|
import helper_search
|
||||||
import l10n
|
import l10n
|
||||||
import openclpow
|
import openclpow
|
||||||
|
@ -1879,7 +1880,8 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
if shared.statusIconColor == 'red':
|
if shared.statusIconColor == 'red':
|
||||||
self.statusBar().showMessage(_translate(
|
self.statusBar().showMessage(_translate(
|
||||||
"MainWindow", "Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect."))
|
"MainWindow", "Warning: You are currently not connected. Bitmessage will do the work necessary to send the message but it won\'t send until you connect."))
|
||||||
ackdata = OpenSSL.rand(32)
|
stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel')
|
||||||
|
ackdata = genAckPayload(streamNumber, stealthLevel)
|
||||||
t = ()
|
t = ()
|
||||||
sqlExecute(
|
sqlExecute(
|
||||||
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
|
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
|
||||||
|
@ -1933,7 +1935,7 @@ class MyForm(settingsmixin.SMainWindow):
|
||||||
# We don't actually need the ackdata for acknowledgement since
|
# We don't actually need the ackdata for acknowledgement since
|
||||||
# this is a broadcast message, but we can use it to update the
|
# this is a broadcast message, but we can use it to update the
|
||||||
# user interface when the POW is done generating.
|
# user interface when the POW is done generating.
|
||||||
ackdata = OpenSSL.rand(32)
|
ackdata = genAckPayload(streamNumber, 0)
|
||||||
toAddress = str_broadcast_subscribers
|
toAddress = str_broadcast_subscribers
|
||||||
ripe = ''
|
ripe = ''
|
||||||
t = ('', # msgid. We don't know what this will be until the POW is done.
|
t = ('', # msgid. We don't know what this will be until the POW is done.
|
||||||
|
|
|
@ -5,6 +5,7 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import inspect
|
import inspect
|
||||||
from helper_sql import *
|
from helper_sql import *
|
||||||
|
from helper_ackPayload import genAckPayload
|
||||||
from addresses import decodeAddress
|
from addresses import decodeAddress
|
||||||
from bmconfigparser import BMConfigParser
|
from bmconfigparser import BMConfigParser
|
||||||
from foldertree import AccountMixin
|
from foldertree import AccountMixin
|
||||||
|
@ -166,7 +167,8 @@ class GatewayAccount(BMAccount):
|
||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
status, addressVersionNumber, streamNumber, ripe = decodeAddress(self.toAddress)
|
status, addressVersionNumber, streamNumber, ripe = decodeAddress(self.toAddress)
|
||||||
ackdata = OpenSSL.rand(32)
|
stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel')
|
||||||
|
ackdata = genAckPayload(streamNumber, stealthLevel)
|
||||||
t = ()
|
t = ()
|
||||||
sqlExecute(
|
sqlExecute(
|
||||||
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
|
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
|
||||||
|
|
|
@ -21,6 +21,7 @@ import helper_inbox
|
||||||
import helper_msgcoding
|
import helper_msgcoding
|
||||||
import helper_sent
|
import helper_sent
|
||||||
from helper_sql import *
|
from helper_sql import *
|
||||||
|
from helper_ackPayload import genAckPayload
|
||||||
import protocol
|
import protocol
|
||||||
import queues
|
import queues
|
||||||
import state
|
import state
|
||||||
|
@ -97,15 +98,9 @@ class objectProcessor(threading.Thread):
|
||||||
# Let's check whether this is a message acknowledgement bound for us.
|
# Let's check whether this is a message acknowledgement bound for us.
|
||||||
if len(data) < 32:
|
if len(data) < 32:
|
||||||
return
|
return
|
||||||
readPosition = 20 # bypass the nonce, time, and object type
|
|
||||||
# chomp version number
|
# bypass nonce and time, retain object type/version/stream + body
|
||||||
versionNumber, varIntLength = decodeVarint(
|
readPosition = 16
|
||||||
data[readPosition:readPosition + 10])
|
|
||||||
readPosition += varIntLength
|
|
||||||
# chomp stream number
|
|
||||||
streamNumber, varIntLength = decodeVarint(
|
|
||||||
data[readPosition:readPosition + 10])
|
|
||||||
readPosition += varIntLength
|
|
||||||
|
|
||||||
if data[readPosition:] in shared.ackdataForWhichImWatching:
|
if data[readPosition:] in shared.ackdataForWhichImWatching:
|
||||||
logger.info('This object is an acknowledgement bound for me.')
|
logger.info('This object is an acknowledgement bound for me.')
|
||||||
|
@ -558,8 +553,8 @@ class objectProcessor(threading.Thread):
|
||||||
message = time.strftime("%a, %Y-%m-%d %H:%M:%S UTC", time.gmtime(
|
message = time.strftime("%a, %Y-%m-%d %H:%M:%S UTC", time.gmtime(
|
||||||
)) + ' Message ostensibly from ' + fromAddress + ':\n\n' + body
|
)) + ' Message ostensibly from ' + fromAddress + ':\n\n' + body
|
||||||
fromAddress = toAddress # The fromAddress for the broadcast that we are about to send is the toAddress (my address) for the msg message we are currently processing.
|
fromAddress = toAddress # The fromAddress for the broadcast that we are about to send is the toAddress (my address) for the msg message we are currently processing.
|
||||||
ackdataForBroadcast = OpenSSL.rand(
|
# We don't actually need the ackdataForBroadcast for acknowledgement since this is a broadcast message but we can use it to update the user interface when the POW is done generating.
|
||||||
32) # We don't actually need the ackdataForBroadcast for acknowledgement since this is a broadcast message but we can use it to update the user interface when the POW is done generating.
|
ackdata = genAckPayload(streamNumber, 0)
|
||||||
toAddress = '[Broadcast subscribers]'
|
toAddress = '[Broadcast subscribers]'
|
||||||
ripe = ''
|
ripe = ''
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,16 @@ class singleWorker(threading.Thread, StoppableThread):
|
||||||
logger.info('Watching for ackdata ' + hexlify(ackdata))
|
logger.info('Watching for ackdata ' + hexlify(ackdata))
|
||||||
shared.ackdataForWhichImWatching[ackdata] = 0
|
shared.ackdataForWhichImWatching[ackdata] = 0
|
||||||
|
|
||||||
|
# Fix legacy (headerless) watched ackdata to include header
|
||||||
|
for oldack in shared.ackdataForWhichImWatching.keys():
|
||||||
|
if (len(oldack)==32):
|
||||||
|
# attach legacy header, always constant (msg/1/1)
|
||||||
|
newack = '\x00\x00\x00\x02\x01\x01' + oldack
|
||||||
|
shared.ackdataForWhichImWatching[newack] = 0
|
||||||
|
sqlExecute('UPDATE sent SET ackdata=? WHERE ackdata=?',
|
||||||
|
newack, oldack )
|
||||||
|
del shared.ackdataForWhichImWatching[oldack]
|
||||||
|
|
||||||
self.stop.wait(
|
self.stop.wait(
|
||||||
10) # give some time for the GUI to start before we start on existing POW tasks.
|
10) # give some time for the GUI to start before we start on existing POW tasks.
|
||||||
|
|
||||||
|
@ -967,11 +977,10 @@ class singleWorker(threading.Thread, StoppableThread):
|
||||||
TTL = 28*24*60*60 # 4 weeks
|
TTL = 28*24*60*60 # 4 weeks
|
||||||
TTL = int(TTL + random.randrange(-300, 300)) # Add some randomness to the TTL
|
TTL = int(TTL + random.randrange(-300, 300)) # Add some randomness to the TTL
|
||||||
embeddedTime = int(time.time() + TTL)
|
embeddedTime = int(time.time() + TTL)
|
||||||
payload = pack('>Q', (embeddedTime))
|
|
||||||
payload += '\x00\x00\x00\x02' # object type: msg
|
# type/version/stream already included
|
||||||
payload += encodeVarint(1) # msg version
|
payload = pack('>Q', (embeddedTime)) + ackdata
|
||||||
payload += encodeVarint(toStreamNumber) + ackdata
|
|
||||||
|
|
||||||
target = 2 ** 64 / (defaults.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + defaults.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+defaults.networkDefaultPayloadLengthExtraBytes))/(2 ** 16))))
|
target = 2 ** 64 / (defaults.networkDefaultProofOfWorkNonceTrialsPerByte*(len(payload) + 8 + defaults.networkDefaultPayloadLengthExtraBytes + ((TTL*(len(payload)+8+defaults.networkDefaultPayloadLengthExtraBytes))/(2 ** 16))))
|
||||||
logger.info('(For ack message) Doing proof of work. TTL set to ' + str(TTL))
|
logger.info('(For ack message) Doing proof of work. TTL set to ' + str(TTL))
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ from addresses import decodeAddress
|
||||||
from bmconfigparser import BMConfigParser
|
from bmconfigparser import BMConfigParser
|
||||||
from debug import logger
|
from debug import logger
|
||||||
from helper_sql import sqlExecute
|
from helper_sql import sqlExecute
|
||||||
|
from helper_ackPayload import genAckPayload
|
||||||
from helper_threading import StoppableThread
|
from helper_threading import StoppableThread
|
||||||
from pyelliptic.openssl import OpenSSL
|
from pyelliptic.openssl import OpenSSL
|
||||||
import queues
|
import queues
|
||||||
|
@ -65,7 +66,8 @@ class smtpServerPyBitmessage(smtpd.SMTPServer):
|
||||||
|
|
||||||
def send(self, fromAddress, toAddress, subject, message):
|
def send(self, fromAddress, toAddress, subject, message):
|
||||||
status, addressVersionNumber, streamNumber, ripe = decodeAddress(toAddress)
|
status, addressVersionNumber, streamNumber, ripe = decodeAddress(toAddress)
|
||||||
ackdata = OpenSSL.rand(32)
|
stealthLevel = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel')
|
||||||
|
ackdata = genAckPayload(streamNumber, stealthLevel)
|
||||||
t = ()
|
t = ()
|
||||||
sqlExecute(
|
sqlExecute(
|
||||||
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
|
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
|
||||||
|
|
40
src/helper_ackPayload.py
Normal file
40
src/helper_ackPayload.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import hashlib
|
||||||
|
import highlevelcrypto
|
||||||
|
import random
|
||||||
|
import helper_random
|
||||||
|
from binascii import hexlify, unhexlify
|
||||||
|
from struct import pack, unpack
|
||||||
|
from addresses import encodeVarint
|
||||||
|
|
||||||
|
# This function generates payload objects for message acknowledgements
|
||||||
|
# Several stealth levels are available depending on the privacy needs;
|
||||||
|
# a higher level means better stealth, but also higher cost (size+POW)
|
||||||
|
# - level 0: a random 32-byte sequence with a message header appended
|
||||||
|
# - level 1: a getpubkey request for a (random) dummy key hash
|
||||||
|
# - level 2: a standard message, encrypted to a random pubkey
|
||||||
|
|
||||||
|
def genAckPayload(streamNumber=1, stealthLevel=0):
|
||||||
|
if (stealthLevel==2): # Generate privacy-enhanced payload
|
||||||
|
# Generate a dummy privkey and derive the pubkey
|
||||||
|
dummyPubKeyHex = highlevelcrypto.privToPub(hexlify(helper_random.randomBytes(32)))
|
||||||
|
# Generate a dummy message of random length
|
||||||
|
# (the smallest possible standard-formatted message is 234 bytes)
|
||||||
|
dummyMessage = helper_random.randomBytes(random.randint(234, 800))
|
||||||
|
# Encrypt the message using standard BM encryption (ECIES)
|
||||||
|
ackdata = highlevelcrypto.encrypt(dummyMessage, dummyPubKeyHex)
|
||||||
|
acktype = 2 # message
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
elif (stealthLevel==1): # Basic privacy payload (random getpubkey)
|
||||||
|
ackdata = helper_random.randomBytes(32)
|
||||||
|
acktype = 0 # getpubkey
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
else: # Minimum viable payload (non stealth)
|
||||||
|
ackdata = helper_random.randomBytes(32)
|
||||||
|
acktype = 2 # message
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
ackobject = pack('>I', acktype) + encodeVarint(version) + encodeVarint(streamNumber) + ackdata
|
||||||
|
|
||||||
|
return ackobject
|
Reference in New Issue
Block a user