Chan fix: initial work completed

This commit is contained in:
Jonathan Warren 2013-09-29 19:24:27 -04:00
parent 7e07d7bc7e
commit 498928405b
5 changed files with 249 additions and 232 deletions

View File

@ -1564,15 +1564,6 @@ class MyForm(QtGui.QMainWindow):
"MainWindow", "Error: You must specify a From address. If you don\'t have one, go to the \'Your Identities\' tab.")) "MainWindow", "Error: You must specify a From address. If you don\'t have one, go to the \'Your Identities\' tab."))
else: else:
toAddress = addBMIfNotPresent(toAddress) toAddress = addBMIfNotPresent(toAddress)
try:
shared.config.get(toAddress, 'enabled')
# The toAddress is one owned by me.
if not shared.safeConfigGetBoolean(toAddress, 'chan'):
QMessageBox.about(self, _translate("MainWindow", "Sending to your address"), _translate(
"MainWindow", "Error: One of the addresses to which you are sending a message, %1, is yours. Unfortunately the Bitmessage client cannot process its own messages. Please try running a second client on a different computer or within a VM.").arg(toAddress))
continue
except:
pass
if addressVersionNumber > 4 or addressVersionNumber <= 1: if addressVersionNumber > 4 or addressVersionNumber <= 1:
QMessageBox.about(self, _translate("MainWindow", "Address version number"), _translate( QMessageBox.about(self, _translate("MainWindow", "Address version number"), _translate(
"MainWindow", "Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version.").arg(toAddress).arg(str(addressVersionNumber))) "MainWindow", "Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version.").arg(toAddress).arg(str(addressVersionNumber)))

View File

@ -1,4 +1,4 @@
doTimingAttackMitigation = True doTimingAttackMitigation = False
import time import time
import threading import threading
@ -514,8 +514,9 @@ class receiveDataThread(threading.Thread):
# the proof of work ourselves, which this program is programmed # the proof of work ourselves, which this program is programmed
# to not do.) # to not do.)
sqlExecute( sqlExecute(
'''INSERT INTO pubkeys VALUES (?,?,?,?)''', '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
ripe.digest(), ripe.digest(),
sendersAddressVersion,
'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + data[beginningOfPubkeyPosition:endOfPubkeyPosition], '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + data[beginningOfPubkeyPosition:endOfPubkeyPosition],
int(time.time()), int(time.time()),
'yes') 'yes')
@ -655,8 +656,9 @@ class receiveDataThread(threading.Thread):
# Let's store the public key in case we want to reply to this # Let's store the public key in case we want to reply to this
# person. # person.
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''', sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
ripe.digest(), ripe.digest(),
sendersAddressVersion,
'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition], '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition],
int(time.time()), int(time.time()),
'yes') 'yes')
@ -806,8 +808,9 @@ class receiveDataThread(threading.Thread):
# Let's store the public key in case we want to reply to this person. # Let's store the public key in case we want to reply to this person.
sqlExecute( sqlExecute(
'''INSERT INTO pubkeys VALUES (?,?,?,?)''', '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
calculatedRipe, calculatedRipe,
sendersAddressVersion,
'\x00\x00\x00\x00\x00\x00\x00\x01' + decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition], '\x00\x00\x00\x00\x00\x00\x00\x01' + decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition],
int(time.time()), int(time.time()),
'yes') 'yes')
@ -1070,8 +1073,9 @@ class receiveDataThread(threading.Thread):
# person. # person.
if sendersAddressVersionNumber <= 3: if sendersAddressVersionNumber <= 3:
sqlExecute( sqlExecute(
'''INSERT INTO pubkeys VALUES (?,?,?,?)''', '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
ripe.digest(), ripe.digest(),
sendersAddressVersionNumber,
'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[messageVersionLength:endOfThePublicKeyPosition], '\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[messageVersionLength:endOfThePublicKeyPosition],
int(time.time()), int(time.time()),
'yes') 'yes')
@ -1081,8 +1085,9 @@ class receiveDataThread(threading.Thread):
self.possibleNewPubkey(ripe=ripe.digest()) self.possibleNewPubkey(ripe=ripe.digest())
elif sendersAddressVersionNumber >= 4: elif sendersAddressVersionNumber >= 4:
sqlExecute( sqlExecute(
'''INSERT INTO pubkeys VALUES (?,?,?,?)''', '''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
ripe.digest(), ripe.digest(),
sendersAddressVersionNumber,
'\x00\x00\x00\x00\x00\x00\x00\x01' + decryptedData[messageVersionLength:endOfThePublicKeyPosition], '\x00\x00\x00\x00\x00\x00\x00\x01' + decryptedData[messageVersionLength:endOfThePublicKeyPosition],
int(time.time()), int(time.time()),
'yes') 'yes')
@ -1409,15 +1414,15 @@ class receiveDataThread(threading.Thread):
queryreturn = sqlQuery( queryreturn = sqlQuery(
'''SELECT usedpersonally FROM pubkeys WHERE hash=? AND usedpersonally='yes' ''', ripe) '''SELECT usedpersonally FROM pubkeys WHERE hash=? AND addressversion=? AND usedpersonally='yes' ''', ripe, addressVersion)
if queryreturn != []: # if this pubkey is already in our database and if we have used it personally: if queryreturn != []: # if this pubkey is already in our database and if we have used it personally:
print 'We HAVE used this pubkey personally. Updating time.' print 'We HAVE used this pubkey personally. Updating time.'
t = (ripe, data, embeddedTime, 'yes') t = (ripe, addressVersion, data, embeddedTime, 'yes')
else: else:
print 'We have NOT used this pubkey personally. Inserting in database.' print 'We have NOT used this pubkey personally. Inserting in database.'
t = (ripe, data, embeddedTime, 'no') t = (ripe, addressVersion, data, embeddedTime, 'no')
# This will also update the embeddedTime. # This will also update the embeddedTime.
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''', *t) sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
# shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) # shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe)))
self.possibleNewPubkey(ripe = ripe) self.possibleNewPubkey(ripe = ripe)
if addressVersion == 3: if addressVersion == 3:
@ -1466,19 +1471,18 @@ class receiveDataThread(threading.Thread):
print 'publicEncryptionKey in hex:', publicEncryptionKey.encode('hex') print 'publicEncryptionKey in hex:', publicEncryptionKey.encode('hex')
queryreturn = sqlQuery('''SELECT usedpersonally FROM pubkeys WHERE hash=? AND usedpersonally='yes' ''', ripe) queryreturn = sqlQuery('''SELECT usedpersonally FROM pubkeys WHERE hash=? AND addressversion=? AND usedpersonally='yes' ''', ripe, addressVersion)
if queryreturn != []: # if this pubkey is already in our database and if we have used it personally: if queryreturn != []: # if this pubkey is already in our database and if we have used it personally:
print 'We HAVE used this pubkey personally. Updating time.' print 'We HAVE used this pubkey personally. Updating time.'
t = (ripe, data, embeddedTime, 'yes') t = (ripe, addressVersion, data, embeddedTime, 'yes')
else: else:
print 'We have NOT used this pubkey personally. Inserting in database.' print 'We have NOT used this pubkey personally. Inserting in database.'
t = (ripe, data, embeddedTime, 'no') t = (ripe, addressVersion, data, embeddedTime, 'no')
# This will also update the embeddedTime. # This will also update the embeddedTime.
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''', *t) sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
self.possibleNewPubkey(ripe = ripe) self.possibleNewPubkey(ripe = ripe)
if addressVersion == 4: if addressVersion == 4:
print 'length of v4 pubkey:', len(data)
if len(data) < 350: # sanity check. if len(data) < 350: # sanity check.
print '(within processpubkey) payloadLength less than 350. Sanity check failed.' print '(within processpubkey) payloadLength less than 350. Sanity check failed.'
return return
@ -1554,8 +1558,8 @@ class receiveDataThread(threading.Thread):
print 'publicSigningKey in hex:', publicSigningKey.encode('hex') print 'publicSigningKey in hex:', publicSigningKey.encode('hex')
print 'publicEncryptionKey in hex:', publicEncryptionKey.encode('hex') print 'publicEncryptionKey in hex:', publicEncryptionKey.encode('hex')
t = (ripe, signedData, embeddedTime, 'yes') t = (ripe, addressVersion, signedData, embeddedTime, 'yes')
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''', *t) sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
fromAddress = encodeAddress(addressVersion, streamNumber, ripe) fromAddress = encodeAddress(addressVersion, streamNumber, ripe)
# That this point we know that we have been waiting on this pubkey. # That this point we know that we have been waiting on this pubkey.

View File

@ -156,6 +156,10 @@ class singleWorker(threading.Thread):
# send messages to "ourselves". # send messages to "ourselves".
def sendOutOrStoreMyV3Pubkey(self, hash): def sendOutOrStoreMyV3Pubkey(self, hash):
myAddress = shared.myAddressesByHash[hash] myAddress = shared.myAddressesByHash[hash]
if shared.safeConfigGetBoolean(myAddress, 'chan'):
with shared.printLock:
print 'This is a chan address. Not sending pubkey.'
return
status, addressVersionNumber, streamNumber, hash = decodeAddress( status, addressVersionNumber, streamNumber, hash = decodeAddress(
myAddress) myAddress)
embeddedTime = int(time.time() + random.randrange( embeddedTime = int(time.time() + random.randrange(
@ -197,49 +201,39 @@ class singleWorker(threading.Thread):
payload += encodeVarint(len(signature)) payload += encodeVarint(len(signature))
payload += signature payload += signature
if not shared.safeConfigGetBoolean(myAddress, 'chan'): # Do the POW for this pubkey message
# Do the POW for this pubkey message target = 2 ** 64 / ((len(payload) + shared.networkDefaultPayloadLengthExtraBytes +
target = 2 ** 64 / ((len(payload) + shared.networkDefaultPayloadLengthExtraBytes + 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte)
8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) print '(For pubkey message) Doing proof of work...'
print '(For pubkey message) Doing proof of work...' initialHash = hashlib.sha512(payload).digest()
initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash)
trialValue, nonce = proofofwork.run(target, initialHash) print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce
print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce
payload = pack('>Q', nonce) + payload payload = pack('>Q', nonce) + payload
inventoryHash = calculateInventoryHash(payload) inventoryHash = calculateInventoryHash(payload)
objectType = 'pubkey' objectType = 'pubkey'
shared.inventory[inventoryHash] = ( shared.inventory[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime,'') objectType, streamNumber, payload, embeddedTime,'')
shared.inventorySets[streamNumber].add(inventoryHash) shared.inventorySets[streamNumber].add(inventoryHash)
with shared.printLock: with shared.printLock:
print 'broadcasting inv with hash:', inventoryHash.encode('hex') print 'broadcasting inv with hash:', inventoryHash.encode('hex')
shared.broadcastToSendDataQueues(( shared.broadcastToSendDataQueues((
streamNumber, 'advertiseobject', inventoryHash)) streamNumber, 'advertiseobject', inventoryHash))
shared.UISignalQueue.put(('updateStatusBar', '')) shared.UISignalQueue.put(('updateStatusBar', ''))
# If this is a chan address then we won't send out the pubkey over the
# network but rather will only store it in our pubkeys table so that
# we can send messages to "ourselves".
if shared.safeConfigGetBoolean(myAddress, 'chan'):
payload = '\x00' * 8 + payload # Attach a fake nonce on the front
# just so that it is in the correct format.
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''',
hash,
payload,
embeddedTime,
'yes')
shared.config.set( shared.config.set(
myAddress, 'lastpubkeysendtime', str(int(time.time()))) myAddress, 'lastpubkeysendtime', str(int(time.time())))
with open(shared.appdata + 'keys.dat', 'wb') as configfile: with open(shared.appdata + 'keys.dat', 'wb') as configfile:
shared.config.write(configfile) shared.config.write(configfile)
# If this isn't a chan address, this function assembles the pubkey data, # If this isn't a chan address, this function assembles the pubkey data,
# does the necessary POW and sends it out. If it *is* a chan then it # does the necessary POW and sends it out.
# assembles the pubkey and stores is in the pubkey table so that we can
# send messages to "ourselves".
def sendOutOrStoreMyV4Pubkey(self, myAddress): def sendOutOrStoreMyV4Pubkey(self, myAddress):
if shared.safeConfigGetBoolean(myAddress, 'chan'):
with shared.printLock:
print 'This is a chan address. Not sending pubkey.'
return
status, addressVersionNumber, streamNumber, hash = decodeAddress( status, addressVersionNumber, streamNumber, hash = decodeAddress(
myAddress) myAddress)
embeddedTime = int(time.time() + random.randrange( embeddedTime = int(time.time() + random.randrange(
@ -284,52 +278,41 @@ class singleWorker(threading.Thread):
dataToEncrypt += encodeVarint(len(signature)) dataToEncrypt += encodeVarint(len(signature))
dataToEncrypt += signature dataToEncrypt += signature
if not shared.safeConfigGetBoolean(myAddress, 'chan'): # Let us encrypt the necessary data. We will use a hash of the data
# Let us encrypt the necessary data. We will use a hash of the data # contained in an address as a decryption key. This way in order to
# contained in an address as a decryption key. This way in order to # read the public keys in a pubkey message, a node must know the address
# read the public keys in a pubkey message, a node must know the address # first. We'll also tag, unencrypted, the pubkey with part of the hash
# first. We'll also tag, unencrypted, the pubkey with part of the hash # so that nodes know which pubkey object to try to decrypt when they
# so that nodes know which pubkey object to try to decrypt when they # want to send a message.
# want to send a message. doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint(
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint( addressVersionNumber) + encodeVarint(streamNumber) + hash).digest()).digest()
addressVersionNumber) + encodeVarint(streamNumber) + hash).digest()).digest() payload += doubleHashOfAddressData[32:] # the tag
payload += doubleHashOfAddressData[32:] # the tag privEncryptionKey = doubleHashOfAddressData[:32]
privEncryptionKey = doubleHashOfAddressData[:32] pubEncryptionKey = pointMult(privEncryptionKey)
pubEncryptionKey = pointMult(privEncryptionKey) payload += highlevelcrypto.encrypt(
payload += highlevelcrypto.encrypt( dataToEncrypt, pubEncryptionKey.encode('hex'))
dataToEncrypt, pubEncryptionKey.encode('hex'))
# Do the POW for this pubkey message # Do the POW for this pubkey message
target = 2 ** 64 / ((len(payload) + shared.networkDefaultPayloadLengthExtraBytes + target = 2 ** 64 / ((len(payload) + shared.networkDefaultPayloadLengthExtraBytes +
8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte)
print '(For pubkey message) Doing proof of work...' print '(For pubkey message) Doing proof of work...'
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
trialValue, nonce = proofofwork.run(target, initialHash) trialValue, nonce = proofofwork.run(target, initialHash)
print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce
payload = pack('>Q', nonce) + payload payload = pack('>Q', nonce) + payload
inventoryHash = calculateInventoryHash(payload) inventoryHash = calculateInventoryHash(payload)
objectType = 'pubkey' objectType = 'pubkey'
shared.inventory[inventoryHash] = ( shared.inventory[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, doubleHashOfAddressData[32:]) objectType, streamNumber, payload, embeddedTime, doubleHashOfAddressData[32:])
shared.inventorySets[streamNumber].add(inventoryHash) shared.inventorySets[streamNumber].add(inventoryHash)
with shared.printLock: with shared.printLock:
print 'broadcasting inv with hash:', inventoryHash.encode('hex') print 'broadcasting inv with hash:', inventoryHash.encode('hex')
shared.broadcastToSendDataQueues(( shared.broadcastToSendDataQueues((
streamNumber, 'advertiseobject', inventoryHash)) streamNumber, 'advertiseobject', inventoryHash))
shared.UISignalQueue.put(('updateStatusBar', '')) shared.UISignalQueue.put(('updateStatusBar', ''))
# If this is a chan address then we won't send out the pubkey over the
# network but rather will only store it in our pubkeys table so that
# we can send messages to "ourselves".
if shared.safeConfigGetBoolean(myAddress, 'chan'):
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''',
hash,
dataToStoreInOurPubkeysTable,
embeddedTime,
'yes')
shared.config.set( shared.config.set(
myAddress, 'lastpubkeysendtime', str(int(time.time()))) myAddress, 'lastpubkeysendtime', str(int(time.time())))
with open(shared.appdata + 'keys.dat', 'wb') as configfile: with open(shared.appdata + 'keys.dat', 'wb') as configfile:
@ -369,8 +352,6 @@ class singleWorker(threading.Thread):
pubEncryptionKey = highlevelcrypto.privToPub( pubEncryptionKey = highlevelcrypto.privToPub(
privEncryptionKeyHex).decode('hex') privEncryptionKeyHex).decode('hex')
print 'embedding pubEncryptionKey:', pubEncryptionKey.encode('hex')
payload = pack('>Q', (int(time.time()) + random.randrange( payload = pack('>Q', (int(time.time()) + random.randrange(
-300, 300))) # the current time plus or minus five minutes -300, 300))) # the current time plus or minus five minutes
if addressVersionNumber <= 3: if addressVersionNumber <= 3:
@ -382,8 +363,6 @@ class singleWorker(threading.Thread):
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint( doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint(
addressVersionNumber) + encodeVarint(streamNumber) + ripe).digest()).digest() addressVersionNumber) + encodeVarint(streamNumber) + ripe).digest()).digest()
payload += doubleHashOfAddressData[32:] # the tag payload += doubleHashOfAddressData[32:] # the tag
print 'embeddedTag is', doubleHashOfAddressData[32:].encode('hex')
print 'embeddedTag is', repr(doubleHashOfAddressData[32:])
if addressVersionNumber <= 3: if addressVersionNumber <= 3:
dataToEncrypt = encodeVarint(2) # broadcast version dataToEncrypt = encodeVarint(2) # broadcast version
@ -458,8 +437,11 @@ class singleWorker(threading.Thread):
for row in queryreturn: # For each address to which we need to send a message, check to see if we have its pubkey already. for row in queryreturn: # For each address to which we need to send a message, check to see if we have its pubkey already.
toaddress, = row toaddress, = row
status, toAddressVersion, toStreamNumber, toRipe = decodeAddress(toaddress) status, toAddressVersion, toStreamNumber, toRipe = decodeAddress(toaddress)
# If we are sending a message to ourselves or a chan then we won't need an entry in the pubkeys table; we can calculate the needed pubkey using the private keys in our keys.dat file.
if shared.config.has_section(toaddress):
continue
queryreturn = sqlQuery( queryreturn = sqlQuery(
'''SELECT hash FROM pubkeys WHERE hash=? ''', toRipe) '''SELECT hash FROM pubkeys WHERE hash=? AND addressversion=?''', toRipe, toAddressVersion)
if queryreturn != []: # If we have the needed pubkey in the pubkey table already, set the status to doingmsgpow (we'll do it further down) if queryreturn != []: # If we have the needed pubkey in the pubkey table already, set the status to doingmsgpow (we'll do it further down)
sqlExecute( sqlExecute(
'''UPDATE sent SET status='doingmsgpow' WHERE toaddress=? AND status='msgqueued' ''', '''UPDATE sent SET status='doingmsgpow' WHERE toaddress=? AND status='msgqueued' ''',
@ -523,130 +505,154 @@ class singleWorker(threading.Thread):
int(time.time()) - 2419200) int(time.time()) - 2419200)
for row in queryreturn: # For each message we need to send.. for row in queryreturn: # For each message we need to send..
toaddress, toripe, fromaddress, subject, message, ackdata, status = row toaddress, toripe, fromaddress, subject, message, ackdata, status = row
# There is a remote possibility that we may no longer have the
# recipient's pubkey. Let us make sure we still have it or else the
# sendMsg function will appear to freeze. This can happen if the
# user sends a message but doesn't let the POW function finish,
# then leaves their client off for a long time which could cause
# the needed pubkey to expire and be deleted.
queryreturn = sqlQuery(
'''SELECT hash FROM pubkeys WHERE hash=? ''',
toripe)
if queryreturn == [] and toripe not in shared.neededPubkeys:
# We no longer have the needed pubkey and we haven't requested
# it.
with shared.printLock:
sys.stderr.write(
'For some reason, the status of a message in our outbox is \'doingmsgpow\' even though we lack the pubkey. Here is the RIPE hash of the needed pubkey: %s\n' % toripe.encode('hex'))
sqlExecute(
'''UPDATE sent SET status='msgqueued' WHERE toaddress=? AND status='doingmsgpow' ''', toaddress)
shared.UISignalQueue.put(('updateSentItemStatusByHash', (
toripe, tr.translateText("MainWindow",'Sending a request for the recipient\'s encryption key.'))))
self.requestPubKey(toaddress)
continue
shared.ackdataForWhichImWatching[ackdata] = 0
toStatus, toAddressVersionNumber, toStreamNumber, toHash = decodeAddress( toStatus, toAddressVersionNumber, toStreamNumber, toHash = decodeAddress(
toaddress) toaddress)
fromStatus, fromAddressVersionNumber, fromStreamNumber, fromHash = decodeAddress( fromStatus, fromAddressVersionNumber, fromStreamNumber, fromHash = decodeAddress(
fromaddress) fromaddress)
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
ackdata, tr.translateText("MainWindow", "Looking up the receiver\'s public key"))))
with shared.printLock:
print 'Found a message in our database that needs to be sent with this pubkey.'
print 'First 150 characters of message:', repr(message[:150])
if not shared.config.has_section(toaddress):
# mark the pubkey as 'usedpersonally' so that we don't ever delete # There is a remote possibility that we may no longer have the
# it. # recipient's pubkey. Let us make sure we still have it or else the
sqlExecute( # sendMsg function will appear to freeze. This can happen if the
'''UPDATE pubkeys SET usedpersonally='yes' WHERE hash=?''', # user sends a message but doesn't let the POW function finish,
toripe) # then leaves their client off for a long time which could cause
# Let us fetch the recipient's public key out of our database. If # the needed pubkey to expire and be deleted.
# the required proof of work difficulty is too hard then we'll queryreturn = sqlQuery(
# abort. '''SELECT hash FROM pubkeys WHERE hash=? AND addressversion=?''',
queryreturn = sqlQuery( toripe,
'SELECT transmitdata FROM pubkeys WHERE hash=?', toAddressVersionNumber)
toripe) if queryreturn == [] and toripe not in shared.neededPubkeys:
if queryreturn == []: # We no longer have the needed pubkey and we haven't requested
with shared.printLock: # it.
sys.stderr.write( with shared.printLock:
'(within sendMsg) The needed pubkey was not found. This should never happen. Aborting send.\n') sys.stderr.write(
'For some reason, the status of a message in our outbox is \'doingmsgpow\' even though we lack the pubkey. Here is the RIPE hash of the needed pubkey: %s\n' % toripe.encode('hex'))
return sqlExecute(
for row in queryreturn: '''UPDATE sent SET status='msgqueued' WHERE toaddress=? AND status='doingmsgpow' ''', toaddress)
pubkeyPayload, = row shared.UISignalQueue.put(('updateSentItemStatusByHash', (
toripe, tr.translateText("MainWindow",'Sending a request for the recipient\'s encryption key.'))))
# The pubkey message is stored the way we originally received it self.requestPubKey(toaddress)
# which means that we need to read beyond things like the nonce and
# time to get to the actual public keys.
if toAddressVersionNumber <= 3:
readPosition = 8 # to bypass the nonce
elif toAddressVersionNumber >= 4:
readPosition = 0 # the nonce is not included here so we don't need to skip over it.
pubkeyEmbeddedTime, = unpack(
'>I', pubkeyPayload[readPosition:readPosition + 4])
# This section is used for the transition from 32 bit time to 64
# bit time in the protocol.
if pubkeyEmbeddedTime == 0:
pubkeyEmbeddedTime, = unpack(
'>Q', pubkeyPayload[readPosition:readPosition + 8])
readPosition += 8
else:
readPosition += 4
readPosition += 1 # to bypass the address version whose length is definitely 1
streamNumber, streamNumberLength = decodeVarint(
pubkeyPayload[readPosition:readPosition + 10])
readPosition += streamNumberLength
behaviorBitfield = pubkeyPayload[readPosition:readPosition + 4]
# Mobile users may ask us to include their address's RIPE hash on a message
# unencrypted. Before we actually do it the sending human must check a box
# in the settings menu to allow it.
if shared.isBitSetWithinBitfield(behaviorBitfield,30): # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message..
if not shared.safeConfigGetBoolean('bitmessagesettings','willinglysendtomobile'): # if we are Not willing to include the receiver's RIPE hash on the message..
logger.info('The receiver is a mobile user but the sender (you) has not selected that you are willing to send to mobiles. Aborting send.')
shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))))
# if the human changes their setting and then sends another message or restarts their client, this one will send at that time.
continue continue
readPosition += 4 # to bypass the bitfield of behaviors shared.ackdataForWhichImWatching[ackdata] = 0
# pubSigningKeyBase256 = shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
# pubkeyPayload[readPosition:readPosition+64] #We don't use this ackdata, tr.translateText("MainWindow", "Looking up the receiver\'s public key"))))
# key for anything here. with shared.printLock:
readPosition += 64 print 'Sending a message. First 150 characters of message:', repr(message[:150])
pubEncryptionKeyBase256 = pubkeyPayload[
readPosition:readPosition + 64]
readPosition += 64 # mark the pubkey as 'usedpersonally' so that we don't ever delete
# it.
# Let us fetch the amount of work required by the recipient. sqlExecute(
if toAddressVersionNumber == 2: '''UPDATE pubkeys SET usedpersonally='yes' WHERE hash=? and addressversion=?''',
toripe,
toAddressVersionNumber)
# Let us fetch the recipient's public key out of our database. If
# the required proof of work difficulty is too hard then we'll
# abort.
queryreturn = sqlQuery(
'SELECT transmitdata FROM pubkeys WHERE hash=? and addressversion=?',
toripe,
toAddressVersionNumber)
if queryreturn == []:
with shared.printLock:
sys.stderr.write(
'(within sendMsg) The needed pubkey was not found. This should never happen. Aborting send.\n')
return
for row in queryreturn:
pubkeyPayload, = row
# The pubkey message is stored the way we originally received it
# which means that we need to read beyond things like the nonce and
# time to get to the actual public keys.
if toAddressVersionNumber <= 3:
readPosition = 8 # to bypass the nonce
elif toAddressVersionNumber >= 4:
readPosition = 0 # the nonce is not included here so we don't need to skip over it.
pubkeyEmbeddedTime, = unpack(
'>I', pubkeyPayload[readPosition:readPosition + 4])
# This section is used for the transition from 32 bit time to 64
# bit time in the protocol.
if pubkeyEmbeddedTime == 0:
pubkeyEmbeddedTime, = unpack(
'>Q', pubkeyPayload[readPosition:readPosition + 8])
readPosition += 8
else:
readPosition += 4
readPosition += 1 # to bypass the address version whose length is definitely 1
streamNumber, streamNumberLength = decodeVarint(
pubkeyPayload[readPosition:readPosition + 10])
readPosition += streamNumberLength
behaviorBitfield = pubkeyPayload[readPosition:readPosition + 4]
# Mobile users may ask us to include their address's RIPE hash on a message
# unencrypted. Before we actually do it the sending human must check a box
# in the settings menu to allow it.
if shared.isBitSetWithinBitfield(behaviorBitfield,30): # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message..
if not shared.safeConfigGetBoolean('bitmessagesettings','willinglysendtomobile'): # if we are Not willing to include the receiver's RIPE hash on the message..
logger.info('The receiver is a mobile user but the sender (you) has not selected that you are willing to send to mobiles. Aborting send.')
shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))))
# if the human changes their setting and then sends another message or restarts their client, this one will send at that time.
continue
readPosition += 4 # to bypass the bitfield of behaviors
# pubSigningKeyBase256 =
# pubkeyPayload[readPosition:readPosition+64] #We don't use this
# key for anything here.
readPosition += 64
pubEncryptionKeyBase256 = pubkeyPayload[
readPosition:readPosition + 64]
readPosition += 64
# Let us fetch the amount of work required by the recipient.
if toAddressVersionNumber == 2:
requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nThere is no required difficulty for version 2 addresses like this."))))
elif toAddressVersionNumber >= 3:
requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(
pubkeyPayload[readPosition:readPosition + 10])
readPosition += varintLength
requiredPayloadLengthExtraBytes, varintLength = decodeVarint(
pubkeyPayload[readPosition:readPosition + 10])
readPosition += varintLength
if requiredAverageProofOfWorkNonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte: # We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network.
requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes:
requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nReceiver\'s required difficulty: %1 and %2").arg(str(float(
requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(str(float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)))))
if status != 'forcepow':
if (requiredAverageProofOfWorkNonceTrialsPerByte > shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte') and shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte') != 0) or (requiredPayloadLengthExtraBytes > shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') and shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') != 0):
# The demanded difficulty is more than we are willing
# to do.
sqlExecute(
'''UPDATE sent SET status='toodifficult' WHERE ackdata=? ''',
ackdata)
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do.").arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(str(float(
requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
continue
else: # if we are sending a message to ourselves or a chan..
with shared.printLock:
print 'Sending a message. First 150 characters of message:', repr(message[:150])
behaviorBitfield = '\x00\x00\x00\x01'
try:
privEncryptionKeyBase58 = shared.config.get(
toaddress, 'privencryptionkey')
except Exception as err:
shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))))
with shared.printLock:
sys.stderr.write(
'Error within sendMsg. Could not read the keys from the keys.dat file for our own address. %s\n' % err)
continue
privEncryptionKeyHex = shared.decodeWalletImportFormat(
privEncryptionKeyBase58).encode('hex')
pubEncryptionKeyBase256 = highlevelcrypto.privToPub(
privEncryptionKeyHex).decode('hex')[1:]
requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nThere is no required difficulty for version 2 addresses like this.")))) ackdata, tr.translateText("MainWindow", "Doing work necessary to send message."))))
elif toAddressVersionNumber >= 3:
requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(
pubkeyPayload[readPosition:readPosition + 10])
readPosition += varintLength
requiredPayloadLengthExtraBytes, varintLength = decodeVarint(
pubkeyPayload[readPosition:readPosition + 10])
readPosition += varintLength
if requiredAverageProofOfWorkNonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte: # We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network.
requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes:
requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nReceiver\'s required difficulty: %1 and %2").arg(str(float(
requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(str(float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)))))
if status != 'forcepow':
if (requiredAverageProofOfWorkNonceTrialsPerByte > shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte') and shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte') != 0) or (requiredPayloadLengthExtraBytes > shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') and shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') != 0):
# The demanded difficulty is more than we are willing
# to do.
sqlExecute(
'''UPDATE sent SET status='toodifficult' WHERE ackdata=? ''',
ackdata)
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do.").arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(str(float(
requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
continue
embeddedTime = pack('>Q', (int(time.time()) + random.randrange( embeddedTime = pack('>Q', (int(time.time()) + random.randrange(
-300, 300))) # the current time plus or minus five minutes. We will use this time both for our message and for the ackdata packed within our message. -300, 300))) # the current time plus or minus five minutes. We will use this time both for our message and for the ackdata packed within our message.
@ -750,11 +756,11 @@ class singleWorker(threading.Thread):
payload += messageToTransmit payload += messageToTransmit
if shared.safeConfigGetBoolean(toaddress, 'chan'): if shared.safeConfigGetBoolean(toaddress, 'chan'):
with shared.printLock: with shared.printLock:
print 'Not bothering to generate ackdata because we are sending to a chan.' print 'Not bothering to include ackdata because we are sending to a chan.'
fullAckPayload = '' fullAckPayload = ''
elif not shared.isBitSetWithinBitfield(behaviorBitfield,31): elif not shared.isBitSetWithinBitfield(behaviorBitfield,31):
with shared.printLock: with shared.printLock:
print 'Not bothering to generate ackdata because the receiver said that they won\'t relay it anyway.' print 'Not bothering to include ackdata because the receiver said that they won\'t relay it anyway.'
fullAckPayload = '' fullAckPayload = ''
else: else:
fullAckPayload = self.generateFullAckMessage( fullAckPayload = self.generateFullAckMessage(
@ -795,11 +801,11 @@ class singleWorker(threading.Thread):
shared.inventory[inventoryHash] = ( shared.inventory[inventoryHash] = (
objectType, toStreamNumber, encryptedPayload, int(time.time()),'') objectType, toStreamNumber, encryptedPayload, int(time.time()),'')
shared.inventorySets[toStreamNumber].add(inventoryHash) shared.inventorySets[toStreamNumber].add(inventoryHash)
if shared.safeConfigGetBoolean(toaddress, 'chan'): if shared.config.has_section(toaddress):
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Sent on %1").arg(unicode( shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Sent on %1").arg(unicode(
strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
else: else:
# not sending to a chan # not sending to a chan or one of my addresses
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Waiting on acknowledgement. Sent on %1").arg(unicode( shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Waiting on acknowledgement. Sent on %1").arg(unicode(
strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
print 'Broadcasting inv for my msg(within sendmsg function):', inventoryHash.encode('hex') print 'Broadcasting inv for my msg(within sendmsg function):', inventoryHash.encode('hex')
@ -808,7 +814,7 @@ class singleWorker(threading.Thread):
# Update the status of the message in the 'sent' table to have a # Update the status of the message in the 'sent' table to have a
# 'msgsent' status or 'msgsentnoackexpected' status. # 'msgsent' status or 'msgsentnoackexpected' status.
if shared.safeConfigGetBoolean(toaddress, 'chan'): if not shared.config.has_section(toaddress):
newStatus = 'msgsentnoackexpected' newStatus = 'msgsentnoackexpected'
else: else:
newStatus = 'msgsent' newStatus = 'msgsent'

View File

@ -46,7 +46,7 @@ class sqlThread(threading.Thread):
# because we may need more flexability in the future and it doesn't # because we may need more flexability in the future and it doesn't
# take up much more space anyway. # take up much more space anyway.
self.cur.execute( self.cur.execute(
'''CREATE TABLE pubkeys (hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE)''' ) '''CREATE TABLE pubkeys (hash blob, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(hash, addressversion) ON CONFLICT REPLACE)''' )
self.cur.execute( self.cur.execute(
'''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, tag blob, UNIQUE(hash) ON CONFLICT REPLACE)''' ) '''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, tag blob, UNIQUE(hash) ON CONFLICT REPLACE)''' )
self.cur.execute( self.cur.execute(
@ -57,7 +57,7 @@ class sqlThread(threading.Thread):
'''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''') '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''')
self.cur.execute( self.cur.execute(
'''CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)''' ) '''CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)''' )
self.cur.execute( '''INSERT INTO settings VALUES('version','4')''') self.cur.execute( '''INSERT INTO settings VALUES('version','5')''')
self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', ( self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', (
int(time.time()),)) int(time.time()),))
self.conn.commit() self.conn.commit()
@ -253,13 +253,29 @@ class sqlThread(threading.Thread):
with open(shared.appdata + 'keys.dat', 'wb') as configfile: with open(shared.appdata + 'keys.dat', 'wb') as configfile:
shared.config.write(configfile) shared.config.write(configfile)
# Add a new column to the pubkeys table to store the address version.
# We're going to trash all of our pubkeys and let them be redownloaded.
item = '''SELECT value FROM settings WHERE key='version';'''
parameters = ''
self.cur.execute(item, parameters)
currentVersion = int(self.cur.fetchall()[0][0])
if currentVersion == 4:
self.cur.execute( '''DROP TABLE pubkeys''')
self.cur.execute(
'''CREATE TABLE pubkeys (hash blob, addressversion int, transmitdata blob, time int, usedpersonally text, UNIQUE(hash, addressversion) ON CONFLICT REPLACE)''' )
self.cur.execute(
'''delete from inventory where objecttype = 'pubkey';''')
item = '''update settings set value=? WHERE key='version';'''
parameters = (5,)
self.cur.execute(item, parameters)
# Are you hoping to add a new option to the keys.dat file of existing # Are you hoping to add a new option to the keys.dat file of existing
# Bitmessage users? Add it right above this line! # Bitmessage users? Add it right above this line!
try: try:
testpayload = '\x00\x00' testpayload = '\x00\x00'
t = ('1234', testpayload, '12345678', 'no') t = ('1234', 1, testpayload, '12345678', 'no')
self.cur.execute( '''INSERT INTO pubkeys VALUES(?,?,?,?)''', t) self.cur.execute( '''INSERT INTO pubkeys VALUES(?,?,?,?,?)''', t)
self.conn.commit() self.conn.commit()
self.cur.execute( self.cur.execute(
'''SELECT transmitdata FROM pubkeys WHERE hash='1234' ''') '''SELECT transmitdata FROM pubkeys WHERE hash='1234' ''')

View File

@ -469,8 +469,8 @@ def decryptAndCheckPubkeyPayload(payload, address):
print 'Pubkey decryption was UNsuccessful due to RIPE mismatch. This shouldn\'t have happened.' print 'Pubkey decryption was UNsuccessful due to RIPE mismatch. This shouldn\'t have happened.'
return 'failed' return 'failed'
t = (ripe, signedData, int(time.time()), 'yes') t = (ripe, addressVersion, signedData, int(time.time()), 'yes')
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''', *t) sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
return 'successful' return 'successful'
Peer = collections.namedtuple('Peer', ['host', 'port']) Peer = collections.namedtuple('Peer', ['host', 'port'])