From 56168e82b50fb74172defb148dab4c10640ef5f8 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 13 Sep 2013 00:27:34 -0400 Subject: [PATCH] most initial work on v4 pubkeys completed --- src/addresses.py | 51 ----- src/bitmessagemain.py | 7 +- src/bitmessageqt/__init__.py | 12 +- src/class_addressGenerator.py | 357 +++++++++++++++++---------------- src/class_receiveDataThread.py | 198 +++++++++++++----- src/class_singleCleaner.py | 4 +- src/class_singleListener.py | 2 +- src/class_singleWorker.py | 167 ++++++++++++--- src/class_sqlThread.py | 17 +- src/defaultKnownNodes.py | 5 +- src/shared.py | 8 +- 11 files changed, 503 insertions(+), 325 deletions(-) diff --git a/src/addresses.py b/src/addresses.py index 1db2105d..5b092ca5 100644 --- a/src/addresses.py +++ b/src/addresses.py @@ -204,7 +204,6 @@ def decodeAddress(address): else: x00string = '\x00' * (20 - len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4])) return status,addressVersionNumber,streamNumber,x00string+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4] - def addBMIfNotPresent(address): address = str(address).strip() @@ -213,56 +212,6 @@ def addBMIfNotPresent(address): else: return address -def addressStream(address): - #returns the stream number of an address or False if there is a problem with the address. - - #check for the BM- at the front of the address. If it isn't there, this address might be for a different version of Bitmessage - if address[:3] != 'BM-': - status = 'missingbm' - return False - #here we take off the BM- - integer = decodeBase58(address[3:]) - #after converting to hex, the string will be prepended with a 0x and appended with a L - hexdata = hex(integer)[2:-1] - - if len(hexdata) % 2 != 0: - hexdata = '0' + hexdata - - #print 'hexdata', hexdata - - data = hexdata.decode('hex') - checksum = data[-4:] - - sha = hashlib.new('sha512') - sha.update(data[:-4]) - currentHash = sha.digest() - #print 'sha after first hashing: ', sha.hexdigest() - sha = hashlib.new('sha512') - sha.update(currentHash) - #print 'sha after second hashing: ', sha.hexdigest() - - if checksum != sha.digest()[0:4]: - print 'checksum failed' - status = 'checksumfailed' - return False - #else: - # print 'checksum PASSED' - - addressVersionNumber, bytesUsedByVersionNumber = decodeVarint(data[:9]) - #print 'addressVersionNumber', addressVersionNumber - #print 'bytesUsedByVersionNumber', bytesUsedByVersionNumber - - if addressVersionNumber < 1: - print 'cannot decode version address version numbers this high' - status = 'versiontoohigh' - return False - - streamNumber, bytesUsedByStreamNumber = decodeVarint(data[bytesUsedByVersionNumber:9+bytesUsedByVersionNumber]) - #print streamNumber - status = 'success' - return streamNumber - - if __name__ == "__main__": print 'Let us make an address from scratch. Suppose we generate two random 32 byte values and call the first one the signing key and the second one the encryption key:' privateSigningKey = '93d0b61371a54b53df143b954035d612f8efa8a3ed1cf842c2186bfd8f876665' diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 88538297..ab146923 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -712,7 +712,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): inventoryHash = calculateInventoryHash(encryptedPayload) objectType = 'msg' shared.inventory[inventoryHash] = ( - objectType, toStreamNumber, encryptedPayload, int(time.time())) + objectType, toStreamNumber, encryptedPayload, int(time.time()),'') shared.inventorySets[toStreamNumber].add(inventoryHash) with shared.printLock: print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex') @@ -748,8 +748,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): pubkeyStreamNumber = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10])[0] inventoryHash = calculateInventoryHash(payload) objectType = 'pubkey' + #todo: support v4 pubkeys shared.inventory[inventoryHash] = ( - objectType, pubkeyStreamNumber, payload, int(time.time())) + objectType, pubkeyStreamNumber, payload, int(time.time()),'') shared.inventorySets[pubkeyStreamNumber].add(inventoryHash) with shared.printLock: print 'broadcasting inv within API command disseminatePubkey with hash:', inventoryHash.encode('hex') @@ -861,7 +862,7 @@ class Main: def start(self, daemon=False): shared.daemon = daemon # is the application already running? If yes then exit. - thisapp = singleton.singleinstance() + #thisapp = singleton.singleinstance() #todo: renable after testing. signal.signal(signal.SIGINT, helper_generic.signal_handler) # signal.signal(signal.SIGINT, signal.SIG_DFL) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 54e10adf..33de2fbf 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -334,7 +334,7 @@ class MyForm(QtGui.QMainWindow): newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta self.ui.tableWidgetYourIdentities.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(str( - addressStream(addressInKeysFile))) + decodeAddress(addressInKeysFile)[2])) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not isEnabled: @@ -1566,7 +1566,7 @@ class MyForm(QtGui.QMainWindow): continue except: pass - if addressVersionNumber > 3 or addressVersionNumber <= 1: + if addressVersionNumber > 4 or addressVersionNumber <= 1: 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))) continue @@ -2200,9 +2200,9 @@ class MyForm(QtGui.QMainWindow): else: # User selected 'Use the same stream as an existing # address.' - streamNumberForAddress = addressStream( - self.dialog.ui.comboBoxExisting.currentText()) - shared.addressGeneratorQueue.put(('createRandomAddress', 3, streamNumberForAddress, str( + streamNumberForAddress = decodeAddress( + self.dialog.ui.comboBoxExisting.currentText())[2] + shared.addressGeneratorQueue.put(('createRandomAddress', 4, streamNumberForAddress, str( self.dialog.ui.newaddresslabel.text().toUtf8()), 1, "", self.dialog.ui.checkBoxEighteenByteRipe.isChecked())) else: if self.dialog.ui.lineEditPassphrase.text() != self.dialog.ui.lineEditPassphraseAgain.text(): @@ -2213,7 +2213,7 @@ class MyForm(QtGui.QMainWindow): "MainWindow", "Choose a passphrase"), _translate("MainWindow", "You really do need a passphrase.")) else: streamNumberForAddress = 1 # this will eventually have to be replaced by logic to determine the most available stream number. - shared.addressGeneratorQueue.put(('createDeterministicAddresses', 3, streamNumberForAddress, "unused deterministic address", self.dialog.ui.spinBoxNumberOfAddressesToMake.value( + shared.addressGeneratorQueue.put(('createDeterministicAddresses', 4, streamNumberForAddress, "unused deterministic address", self.dialog.ui.spinBoxNumberOfAddressesToMake.value( ), self.dialog.ui.lineEditPassphrase.text().toUtf8(), self.dialog.ui.checkBoxEighteenByteRipe.isChecked())) else: print 'new address dialog box rejected' diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py index 54582541..0b03ac2d 100644 --- a/src/class_addressGenerator.py +++ b/src/class_addressGenerator.py @@ -38,7 +38,7 @@ class addressGenerator(threading.Thread): else: sys.stderr.write( 'Programming error: A structure with the wrong number of values was passed into the addressGeneratorQueue. Here is the queueValue: %s\n' % repr(queueValue)) - if addressVersionNumber < 3 or addressVersionNumber > 3: + if addressVersionNumber < 3 or addressVersionNumber > 4: sys.stderr.write( 'Program error: For some reason the address generator queue has been given a request to create at least one version %s address which it cannot do.\n' % addressVersionNumber) if nonceTrialsPerByte == 0: @@ -51,26 +51,125 @@ class addressGenerator(threading.Thread): 'bitmessagesettings', 'defaultpayloadlengthextrabytes') if payloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes: payloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes - if addressVersionNumber == 3: # currently the only one supported. - if command == 'createRandomAddress': + if command == 'createRandomAddress': + shared.UISignalQueue.put(( + 'updateStatusBar', tr.translateText("MainWindow", "Generating one new address"))) + # This next section is a little bit strange. We're going to generate keys over and over until we + # find one that starts with either \x00 or \x00\x00. Then when we pack them into a Bitmessage address, + # we won't store the \x00 or \x00\x00 bytes thus making the + # address shorter. + startTime = time.time() + numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0 + potentialPrivSigningKey = OpenSSL.rand(32) + potentialPubSigningKey = pointMult(potentialPrivSigningKey) + while True: + numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 + potentialPrivEncryptionKey = OpenSSL.rand(32) + potentialPubEncryptionKey = pointMult( + potentialPrivEncryptionKey) + # print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex') + # print 'potentialPubEncryptionKey', + # potentialPubEncryptionKey.encode('hex') + ripe = hashlib.new('ripemd160') + sha = hashlib.new('sha512') + sha.update( + potentialPubSigningKey + potentialPubEncryptionKey) + ripe.update(sha.digest()) + # print 'potential ripe.digest', + # ripe.digest().encode('hex') + if eighteenByteRipe: + if ripe.digest()[:2] == '\x00\x00': + break + else: + if ripe.digest()[:1] == '\x00': + break + print 'Generated address with ripe digest:', ripe.digest().encode('hex') + print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime), 'addresses per second before finding one with the correct ripe-prefix.' + address = encodeAddress(addressVersionNumber, streamNumber, ripe.digest()) + + # An excellent way for us to store our keys is in Wallet Import Format. Let us convert now. + # https://en.bitcoin.it/wiki/Wallet_import_format + privSigningKey = '\x80' + potentialPrivSigningKey + checksum = hashlib.sha256(hashlib.sha256( + privSigningKey).digest()).digest()[0:4] + privSigningKeyWIF = arithmetic.changebase( + privSigningKey + checksum, 256, 58) + # print 'privSigningKeyWIF',privSigningKeyWIF + + privEncryptionKey = '\x80' + potentialPrivEncryptionKey + checksum = hashlib.sha256(hashlib.sha256( + privEncryptionKey).digest()).digest()[0:4] + privEncryptionKeyWIF = arithmetic.changebase( + privEncryptionKey + checksum, 256, 58) + # print 'privEncryptionKeyWIF',privEncryptionKeyWIF + + shared.config.add_section(address) + shared.config.set(address, 'label', label) + shared.config.set(address, 'enabled', 'true') + shared.config.set(address, 'decoy', 'false') + shared.config.set(address, 'noncetrialsperbyte', str( + nonceTrialsPerByte)) + shared.config.set(address, 'payloadlengthextrabytes', str( + payloadLengthExtraBytes)) + shared.config.set( + address, 'privSigningKey', privSigningKeyWIF) + shared.config.set( + address, 'privEncryptionKey', privEncryptionKeyWIF) + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) + + # The API and the join and create Chan functionality + # both need information back from the address generator. + shared.apiAddressGeneratorReturnQueue.put(address) + + shared.UISignalQueue.put(( + 'updateStatusBar', tr.translateText("MainWindow", "Done generating address. Doing work necessary to broadcast it..."))) + shared.UISignalQueue.put(('writeNewAddressToTable', ( + label, address, streamNumber))) + shared.reloadMyAddressHashes() + if addressVersionNumber == 3: + shared.workerQueue.put(( + 'sendOutOrStoreMyV3Pubkey', ripe.digest())) + elif addressVersionNumber == 4: + shared.workerQueue.put(( + 'sendOutOrStoreMyV4Pubkey', address)) + + elif command == 'createDeterministicAddresses' or command == 'getDeterministicAddress' or command == 'createChan' or command == 'joinChan': + if len(deterministicPassphrase) == 0: + sys.stderr.write( + 'WARNING: You are creating deterministic address(es) using a blank passphrase. Bitmessage will do it but it is rather stupid.') + if command == 'createDeterministicAddresses': + statusbar = 'Generating ' + str( + numberOfAddressesToMake) + ' new addresses.' shared.UISignalQueue.put(( - 'updateStatusBar', tr.translateText("MainWindow", "Generating one new address"))) + 'updateStatusBar', statusbar)) + signingKeyNonce = 0 + encryptionKeyNonce = 1 + listOfNewAddressesToSendOutThroughTheAPI = [ + ] # We fill out this list no matter what although we only need it if we end up passing the info to the API. + + for i in range(numberOfAddressesToMake): # This next section is a little bit strange. We're going to generate keys over and over until we - # find one that starts with either \x00 or \x00\x00. Then when we pack them into a Bitmessage address, - # we won't store the \x00 or \x00\x00 bytes thus making the - # address shorter. + # find one that has a RIPEMD hash that starts with either \x00 or \x00\x00. Then when we pack them + # into a Bitmessage address, we won't store the \x00 or + # \x00\x00 bytes thus making the address shorter. startTime = time.time() numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0 - potentialPrivSigningKey = OpenSSL.rand(32) - potentialPubSigningKey = pointMult(potentialPrivSigningKey) while True: numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 - potentialPrivEncryptionKey = OpenSSL.rand(32) + potentialPrivSigningKey = hashlib.sha512( + deterministicPassphrase + encodeVarint(signingKeyNonce)).digest()[:32] + potentialPrivEncryptionKey = hashlib.sha512( + deterministicPassphrase + encodeVarint(encryptionKeyNonce)).digest()[:32] + potentialPubSigningKey = pointMult( + potentialPrivSigningKey) potentialPubEncryptionKey = pointMult( potentialPrivEncryptionKey) # print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex') # print 'potentialPubEncryptionKey', # potentialPubEncryptionKey.encode('hex') + signingKeyNonce += 2 + encryptionKeyNonce += 2 ripe = hashlib.new('ripemd160') sha = hashlib.new('sha512') sha.update( @@ -84,187 +183,93 @@ class addressGenerator(threading.Thread): else: if ripe.digest()[:1] == '\x00': break - print 'Generated address with ripe digest:', ripe.digest().encode('hex') - print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime), 'addresses per second before finding one with the correct ripe-prefix.' - address = encodeAddress(3, streamNumber, ripe.digest()) - # An excellent way for us to store our keys is in Wallet Import Format. Let us convert now. - # https://en.bitcoin.it/wiki/Wallet_import_format - privSigningKey = '\x80' + potentialPrivSigningKey - checksum = hashlib.sha256(hashlib.sha256( - privSigningKey).digest()).digest()[0:4] - privSigningKeyWIF = arithmetic.changebase( - privSigningKey + checksum, 256, 58) - # print 'privSigningKeyWIF',privSigningKeyWIF + print 'ripe.digest', ripe.digest().encode('hex') + print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime), 'keys per second.' + address = encodeAddress(addressVersionNumber, streamNumber, ripe.digest()) - privEncryptionKey = '\x80' + potentialPrivEncryptionKey - checksum = hashlib.sha256(hashlib.sha256( - privEncryptionKey).digest()).digest()[0:4] - privEncryptionKeyWIF = arithmetic.changebase( - privEncryptionKey + checksum, 256, 58) - # print 'privEncryptionKeyWIF',privEncryptionKeyWIF - - shared.config.add_section(address) - shared.config.set(address, 'label', label) - shared.config.set(address, 'enabled', 'true') - shared.config.set(address, 'decoy', 'false') - shared.config.set(address, 'noncetrialsperbyte', str( - nonceTrialsPerByte)) - shared.config.set(address, 'payloadlengthextrabytes', str( - payloadLengthExtraBytes)) - shared.config.set( - address, 'privSigningKey', privSigningKeyWIF) - shared.config.set( - address, 'privEncryptionKey', privEncryptionKeyWIF) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) - - # The API and the join and create Chan functionality - # both need information back from the address generator. - shared.apiAddressGeneratorReturnQueue.put(address) - - shared.UISignalQueue.put(( - 'updateStatusBar', tr.translateText("MainWindow", "Done generating address. Doing work necessary to broadcast it..."))) - shared.UISignalQueue.put(('writeNewAddressToTable', ( - label, address, streamNumber))) - shared.reloadMyAddressHashes() - shared.workerQueue.put(( - 'sendOutOrStoreMyV3Pubkey', ripe.digest())) - - elif command == 'createDeterministicAddresses' or command == 'getDeterministicAddress' or command == 'createChan' or command == 'joinChan': - if len(deterministicPassphrase) == 0: - sys.stderr.write( - 'WARNING: You are creating deterministic address(es) using a blank passphrase. Bitmessage will do it but it is rather stupid.') - if command == 'createDeterministicAddresses': - statusbar = 'Generating ' + str( - numberOfAddressesToMake) + ' new addresses.' - shared.UISignalQueue.put(( - 'updateStatusBar', statusbar)) - signingKeyNonce = 0 - encryptionKeyNonce = 1 - listOfNewAddressesToSendOutThroughTheAPI = [ - ] # We fill out this list no matter what although we only need it if we end up passing the info to the API. - - for i in range(numberOfAddressesToMake): - # This next section is a little bit strange. We're going to generate keys over and over until we - # find one that has a RIPEMD hash that starts with either \x00 or \x00\x00. Then when we pack them - # into a Bitmessage address, we won't store the \x00 or - # \x00\x00 bytes thus making the address shorter. - startTime = time.time() - numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0 - while True: - numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 - potentialPrivSigningKey = hashlib.sha512( - deterministicPassphrase + encodeVarint(signingKeyNonce)).digest()[:32] - potentialPrivEncryptionKey = hashlib.sha512( - deterministicPassphrase + encodeVarint(encryptionKeyNonce)).digest()[:32] - potentialPubSigningKey = pointMult( - potentialPrivSigningKey) - potentialPubEncryptionKey = pointMult( - potentialPrivEncryptionKey) - # print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex') - # print 'potentialPubEncryptionKey', - # potentialPubEncryptionKey.encode('hex') - signingKeyNonce += 2 - encryptionKeyNonce += 2 - ripe = hashlib.new('ripemd160') - sha = hashlib.new('sha512') - sha.update( - potentialPubSigningKey + potentialPubEncryptionKey) - ripe.update(sha.digest()) - # print 'potential ripe.digest', - # ripe.digest().encode('hex') - if eighteenByteRipe: - if ripe.digest()[:2] == '\x00\x00': - break - else: - if ripe.digest()[:1] == '\x00': - break - - print 'ripe.digest', ripe.digest().encode('hex') - print 'Address generator calculated', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, 'addresses at', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime), 'keys per second.' - address = encodeAddress(3, streamNumber, ripe.digest()) - - saveAddressToDisk = True - # If we are joining an existing chan, let us check to make sure it matches the provided Bitmessage address - if command == 'joinChan': - if address != chanAddress: - shared.apiAddressGeneratorReturnQueue.put('chan name does not match address') - saveAddressToDisk = False - if command == 'getDeterministicAddress': + saveAddressToDisk = True + # If we are joining an existing chan, let us check to make sure it matches the provided Bitmessage address + if command == 'joinChan': + if address != chanAddress: + shared.apiAddressGeneratorReturnQueue.put('chan name does not match address') saveAddressToDisk = False + if command == 'getDeterministicAddress': + saveAddressToDisk = False - if saveAddressToDisk: - # An excellent way for us to store our keys is in Wallet Import Format. Let us convert now. - # https://en.bitcoin.it/wiki/Wallet_import_format - privSigningKey = '\x80' + potentialPrivSigningKey - checksum = hashlib.sha256(hashlib.sha256( - privSigningKey).digest()).digest()[0:4] - privSigningKeyWIF = arithmetic.changebase( - privSigningKey + checksum, 256, 58) + if saveAddressToDisk: + # An excellent way for us to store our keys is in Wallet Import Format. Let us convert now. + # https://en.bitcoin.it/wiki/Wallet_import_format + privSigningKey = '\x80' + potentialPrivSigningKey + checksum = hashlib.sha256(hashlib.sha256( + privSigningKey).digest()).digest()[0:4] + privSigningKeyWIF = arithmetic.changebase( + privSigningKey + checksum, 256, 58) - privEncryptionKey = '\x80' + \ - potentialPrivEncryptionKey - checksum = hashlib.sha256(hashlib.sha256( - privEncryptionKey).digest()).digest()[0:4] - privEncryptionKeyWIF = arithmetic.changebase( - privEncryptionKey + checksum, 256, 58) + privEncryptionKey = '\x80' + \ + potentialPrivEncryptionKey + checksum = hashlib.sha256(hashlib.sha256( + privEncryptionKey).digest()).digest()[0:4] + privEncryptionKeyWIF = arithmetic.changebase( + privEncryptionKey + checksum, 256, 58) - addressAlreadyExists = False - try: - shared.config.add_section(address) - except: - print address, 'already exists. Not adding it again.' - addressAlreadyExists = True - if not addressAlreadyExists: - print 'label:', label - shared.config.set(address, 'label', label) - shared.config.set(address, 'enabled', 'true') - shared.config.set(address, 'decoy', 'false') - if command == 'joinChan' or command == 'createChan': - shared.config.set(address, 'chan', 'true') - shared.config.set(address, 'noncetrialsperbyte', str( - nonceTrialsPerByte)) - shared.config.set(address, 'payloadlengthextrabytes', str( - payloadLengthExtraBytes)) - shared.config.set( - address, 'privSigningKey', privSigningKeyWIF) - shared.config.set( - address, 'privEncryptionKey', privEncryptionKeyWIF) - with open(shared.appdata + 'keys.dat', 'wb') as configfile: - shared.config.write(configfile) + addressAlreadyExists = False + try: + shared.config.add_section(address) + except: + print address, 'already exists. Not adding it again.' + addressAlreadyExists = True + if not addressAlreadyExists: + print 'label:', label + shared.config.set(address, 'label', label) + shared.config.set(address, 'enabled', 'true') + shared.config.set(address, 'decoy', 'false') + if command == 'joinChan' or command == 'createChan': + shared.config.set(address, 'chan', 'true') + shared.config.set(address, 'noncetrialsperbyte', str( + nonceTrialsPerByte)) + shared.config.set(address, 'payloadlengthextrabytes', str( + payloadLengthExtraBytes)) + shared.config.set( + address, 'privSigningKey', privSigningKeyWIF) + shared.config.set( + address, 'privEncryptionKey', privEncryptionKeyWIF) + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) - shared.UISignalQueue.put(('writeNewAddressToTable', ( - label, address, str(streamNumber)))) - listOfNewAddressesToSendOutThroughTheAPI.append( - address) - shared.myECCryptorObjects[ripe.digest()] = highlevelcrypto.makeCryptor( - potentialPrivEncryptionKey.encode('hex')) - shared.myAddressesByHash[ - ripe.digest()] = address + shared.UISignalQueue.put(('writeNewAddressToTable', ( + label, address, str(streamNumber)))) + listOfNewAddressesToSendOutThroughTheAPI.append( + address) + shared.myECCryptorObjects[ripe.digest()] = highlevelcrypto.makeCryptor( + potentialPrivEncryptionKey.encode('hex')) + shared.myAddressesByHash[ + ripe.digest()] = address + if addressVersionNumber == 3: shared.workerQueue.put(( 'sendOutOrStoreMyV3Pubkey', ripe.digest())) # If this is a chan address, # the worker thread won't send out the pubkey over the network. + elif addressVersionNumber == 4: + shared.workerQueue.put(( + 'sendOutOrStoreMyV4Pubkey', address)) - # Done generating addresses. - if command == 'createDeterministicAddresses' or command == 'joinChan' or command == 'createChan': - shared.apiAddressGeneratorReturnQueue.put( - listOfNewAddressesToSendOutThroughTheAPI) - shared.UISignalQueue.put(( - 'updateStatusBar', tr.translateText("MainWindow", "Done generating address"))) - # shared.reloadMyAddressHashes() - elif command == 'getDeterministicAddress': - shared.apiAddressGeneratorReturnQueue.put(address) - #todo: return things to the API if createChan or joinChan assuming saveAddressToDisk - else: - raise Exception( - "Error in the addressGenerator thread. Thread was given a command it could not understand: " + command) + # Done generating addresses. + if command == 'createDeterministicAddresses' or command == 'joinChan' or command == 'createChan': + shared.apiAddressGeneratorReturnQueue.put( + listOfNewAddressesToSendOutThroughTheAPI) + shared.UISignalQueue.put(( + 'updateStatusBar', tr.translateText("MainWindow", "Done generating address"))) + # shared.reloadMyAddressHashes() + elif command == 'getDeterministicAddress': + shared.apiAddressGeneratorReturnQueue.put(address) + #todo: return things to the API if createChan or joinChan assuming saveAddressToDisk + else: + raise Exception( + "Error in the addressGenerator thread. Thread was given a command it could not understand: " + command) # Does an EC point multiplication; turns a private key into a public key. - - def pointMult(secret): # ctx = OpenSSL.BN_CTX_new() #This value proved to cause Seg Faults on # Linux. It turns out that it really didn't speed up EC_POINT_mul anyway. diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 643185fa..83a73d66 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -300,7 +300,7 @@ class receiveDataThread(threading.Thread): with shared.inventoryLock: for hash, storedValue in shared.inventory.items(): if hash not in self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware: - objectType, streamNumber, payload, receivedTime = storedValue + objectType, streamNumber, payload, receivedTime, tag = storedValue if streamNumber == self.streamNumber and receivedTime > int(time.time()) - shared.maximumAgeOfObjectsThatIAdvertiseToOthers: bigInvList[hash] = 0 numberOfObjectsInInvMessage = 0 @@ -391,7 +391,7 @@ class receiveDataThread(threading.Thread): # It is valid so far. Let's let our peers know about it. objectType = 'broadcast' shared.inventory[self.inventoryHash] = ( - objectType, self.streamNumber, data, embeddedTime) + objectType, self.streamNumber, data, embeddedTime,'') shared.inventorySets[self.streamNumber].add(self.inventoryHash) shared.inventoryLock.release() self.broadcastinv(self.inventoryHash) @@ -755,7 +755,7 @@ class receiveDataThread(threading.Thread): # This msg message is valid. Let's let our peers know about it. objectType = 'msg' shared.inventory[self.inventoryHash] = ( - objectType, self.streamNumber, data, embeddedTime) + objectType, self.streamNumber, data, embeddedTime,'') shared.inventorySets[self.streamNumber].add(self.inventoryHash) shared.inventoryLock.release() self.broadcastinv(self.inventoryHash) @@ -1103,7 +1103,7 @@ class receiveDataThread(threading.Thread): # We have received a pubkey def recpubkey(self, data): self.pubkeyProcessingStartTime = time.time() - if len(data) < 146 or len(data) > 600: # sanity check + if len(data) < 146 or len(data) > 420: # sanity check return # We must check to make sure the proof of work is sufficient. if not self.isProofOfWorkSufficient(data): @@ -1140,6 +1140,10 @@ class receiveDataThread(threading.Thread): if self.streamNumber != streamNumber: print 'stream number embedded in this pubkey doesn\'t match our stream number. Ignoring.' return + if addressVersion >= 4: + tag = data[readPosition:readPosition + 32] + else: + tag = '' shared.numberOfInventoryLookupsPerformed += 1 inventoryHash = calculateInventoryHash(data) @@ -1154,7 +1158,7 @@ class receiveDataThread(threading.Thread): return objectType = 'pubkey' shared.inventory[inventoryHash] = ( - objectType, self.streamNumber, data, embeddedTime) + objectType, self.streamNumber, data, embeddedTime, tag) shared.inventorySets[self.streamNumber].add(inventoryHash) shared.inventoryLock.release() self.broadcastinv(inventoryHash) @@ -1194,10 +1198,11 @@ class receiveDataThread(threading.Thread): streamNumber, varintLength = decodeVarint( data[readPosition:readPosition + 10]) readPosition += varintLength + signedData = data[8:readPosition] # Used only for v4 or higher pubkeys if addressVersion == 0: print '(Within processpubkey) addressVersion of 0 doesn\'t make sense.' return - if addressVersion > 3 or addressVersion == 1: + if addressVersion > 4 or addressVersion == 1: with shared.printLock: print 'This version of Bitmessage cannot handle version', addressVersion, 'addresses.' @@ -1298,9 +1303,95 @@ class receiveDataThread(threading.Thread): t = (ripe, data, embeddedTime, 'no') # This will also update the embeddedTime. sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''', *t) - # shared.workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) self.possibleNewPubkey(ripe) + if addressVersion == 4: + print 'length of v4 pubkey:', len(data) + if len(data) < 350: # sanity check. + print '(within processpubkey) payloadLength less than 350. Sanity check failed.' + return + tag = data[readPosition:readPosition + 32] + readPosition += 32 + encryptedData = data[readPosition:] + if tag not in shared.neededPubkeys: + with shared.printLock: + print 'We don\'t need this v4 pubkey. We didn\'t ask for it.' + return + + with shared.printLock: + print 'We have been awaiting the arrival of this pubkey.' + + # Let us try to decrypt the pubkey + cryptorObject = shared.neededPubkeys[tag] + try: + decryptedData = cryptorObject.decrypt(encryptedData) + except: + # Someone must have encrypted some data with a different key + # but tagged it with a tag for which we are watching. + with shared.printLock: + print 'Pubkey decryption was unsuccessful.' + return + + + readPosition = 0 + bitfieldBehaviors = decryptedData[readPosition:readPosition + 4] + readPosition += 4 + publicSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64] + # Is it possible for a public key to be invalid such that trying to + # encrypt or sign with it will cause an error? If it is, we should + # probably test these keys here. + readPosition += 64 + publicEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64] + readPosition += 64 + specifiedNonceTrialsPerByte, specifiedNonceTrialsPerByteLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += specifiedNonceTrialsPerByteLength + specifiedPayloadLengthExtraBytes, specifiedPayloadLengthExtraBytesLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += specifiedPayloadLengthExtraBytesLength + signedData += decryptedData[:readPosition] + signatureLength, signatureLengthLength = decodeVarint( + decryptedData[readPosition:readPosition + 10]) + readPosition += signatureLengthLength + signature = decryptedData[readPosition:readPosition + signatureLength] + try: + if not highlevelcrypto.verify(signedData, signature, publicSigningKey.encode('hex')): + print 'ECDSA verify failed (within processpubkey)' + return + print 'ECDSA verify passed (within processpubkey)' + except Exception as err: + print 'ECDSA verify failed (within processpubkey)', err + return + + sha = hashlib.new('sha512') + sha.update(publicSigningKey + publicEncryptionKey) + ripeHasher = hashlib.new('ripemd160') + ripeHasher.update(sha.digest()) + ripe = ripeHasher.digest() + + # We need to make sure that the tag on the outside of the encryption + # is the one generated from hashing these particular keys. + if tag != hashlib.sha512(hashlib.sha512(encodeVarint(addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest()[32:]: + with shared.printLock: + print 'Someone was trying to act malicious: tag doesn\'t match the keys in this pubkey message. Ignoring it.' + return + else: + print 'Tag successfully matches keys in pubkey message' # testing. Will remove soon. + + with shared.printLock: + print 'within recpubkey, addressVersion:', addressVersion, ', streamNumber:', streamNumber + print 'ripe', ripe.encode('hex') + print 'publicSigningKey in hex:', publicSigningKey.encode('hex') + print 'publicEncryptionKey in hex:', publicEncryptionKey.encode('hex') + + t = (ripe, signedData, embeddedTime, 'yes') + sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?)''', *t) + + sqlExecute( + '''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND (status='awaitingpubkey' or status='doingpubkeypow') and folder='sent' ''', + toRipe) + shared.workerQueue.put(('sendmessage', '')) + # We have received a getpubkey message def recgetpubkey(self, data): if not self.isProofOfWorkSufficient(data): @@ -1350,7 +1441,7 @@ class receiveDataThread(threading.Thread): objectType = 'getpubkey' shared.inventory[inventoryHash] = ( - objectType, self.streamNumber, data, embeddedTime) + objectType, self.streamNumber, data, embeddedTime,'') shared.inventorySets[self.streamNumber].add(inventoryHash) shared.inventoryLock.release() # This getpubkey request is valid so far. Forward to peers. @@ -1362,50 +1453,65 @@ class receiveDataThread(threading.Thread): elif requestedAddressVersionNumber == 1: print 'The requestedAddressVersionNumber of the pubkey request is 1 which isn\'t supported anymore. Ignoring it.' return - elif requestedAddressVersionNumber > 3: + elif requestedAddressVersionNumber > 4: print 'The requestedAddressVersionNumber of the pubkey request is too high. Can\'t understand. Ignoring it.' return - requestedHash = data[readPosition:readPosition + 20] - if len(requestedHash) != 20: - print 'The length of the requested hash is not 20 bytes. Something is wrong. Ignoring.' - return - with shared.printLock: - print 'the hash requested in this getpubkey request is:', requestedHash.encode('hex') - - if requestedHash in shared.myAddressesByHash: # if this address hash is one of mine - if decodeAddress(shared.myAddressesByHash[requestedHash])[1] != requestedAddressVersionNumber: - with shared.printLock: - sys.stderr.write( - '(Within the recgetpubkey function) Someone requested one of my pubkeys but the requestedAddressVersionNumber doesn\'t match my actual address version number. They shouldn\'t have done that. Ignoring.\n') + myAddress = '' + if requestedAddressVersionNumber <= 3 : + requestedHash = data[readPosition:readPosition + 20] + if len(requestedHash) != 20: + print 'The length of the requested hash is not 20 bytes. Something is wrong. Ignoring.' return - if shared.safeConfigGetBoolean(shared.myAddressesByHash[requestedHash], 'chan'): - with shared.printLock: - print 'Ignoring getpubkey request because it is for one of my chan addresses. The other party should already have the pubkey.' - return - try: - lastPubkeySendTime = int(shared.config.get( - shared.myAddressesByHash[requestedHash], 'lastpubkeysendtime')) - except: - lastPubkeySendTime = 0 - if lastPubkeySendTime > time.time() - shared.lengthOfTimeToHoldOnToAllPubkeys: # If the last time we sent our pubkey was more recent than 28 days ago... - with shared.printLock: - print 'Found getpubkey-requested-hash in my list of EC hashes BUT we already sent it recently. Ignoring request. The lastPubkeySendTime is:', lastPubkeySendTime - return - with shared.printLock: - print 'Found getpubkey-requested-hash in my list of EC hashes. Telling Worker thread to do the POW for a pubkey message and send it out.' - if requestedAddressVersionNumber == 2: - shared.workerQueue.put(( - 'doPOWForMyV2Pubkey', requestedHash)) - elif requestedAddressVersionNumber == 3: - shared.workerQueue.put(( - 'sendOutOrStoreMyV3Pubkey', requestedHash)) - - - else: + print 'the hash requested in this getpubkey request is:', requestedHash.encode('hex') + if requestedHash in shared.myAddressesByHash: # if this address hash is one of mine + myAddress = shared.myAddressesByHash[requestedHash] + elif requestedAddressVersionNumber >= 4: + requestedTag = data[readPosition:readPosition + 32] + if len(requestedTag) != 32: + print 'The length of the requested tag is not 32 bytes. Something is wrong. Ignoring.' + return + with shared.printLock: + print 'the tag requested in this getpubkey request is:', requestedTag.encode('hex') + if requestedTag in shared.myAddressesByTag[requestedTag]: + myAddress = shared.myAddressesByTag[requestedTag] + + if myAddress == '': with shared.printLock: print 'This getpubkey request is not for any of my keys.' + return + + if decodeAddress(myAddress)[1] != requestedAddressVersionNumber: + with shared.printLock: + sys.stderr.write( + '(Within the recgetpubkey function) Someone requested one of my pubkeys but the requestedAddressVersionNumber doesn\'t match my actual address version number. They shouldn\'t have done that. Ignoring.\n') + return + if shared.safeConfigGetBoolean(myAddress, 'chan'): + with shared.printLock: + print 'Ignoring getpubkey request because it is for one of my chan addresses. The other party should already have the pubkey.' + return + try: + lastPubkeySendTime = int(shared.config.get( + myAddress, 'lastpubkeysendtime')) + except: + lastPubkeySendTime = 0 + if lastPubkeySendTime > time.time() - shared.lengthOfTimeToHoldOnToAllPubkeys: # If the last time we sent our pubkey was more recent than 28 days ago... + with shared.printLock: + print 'Found getpubkey-requested-item in my list of EC hashes BUT we already sent it recently. Ignoring request. The lastPubkeySendTime is:', lastPubkeySendTime + return + + with shared.printLock: + print 'Found getpubkey-requested-hash in my list of EC hashes. Telling Worker thread to do the POW for a pubkey message and send it out.' + if requestedAddressVersionNumber == 2: + shared.workerQueue.put(( + 'doPOWForMyV2Pubkey', requestedHash)) + elif requestedAddressVersionNumber == 3: + shared.workerQueue.put(( + 'sendOutOrStoreMyV3Pubkey', requestedHash)) + elif requestedAddressVersionNumber == 4: + shared.workerQueue.put(( + 'sendOutOrStoreMyV4Pubkey', myAddress)) # We have received an inv message @@ -1498,7 +1604,7 @@ class receiveDataThread(threading.Thread): shared.numberOfInventoryLookupsPerformed += 1 shared.inventoryLock.acquire() if hash in shared.inventory: - objectType, streamNumber, payload, receivedTime = shared.inventory[ + objectType, streamNumber, payload, receivedTime, tag = shared.inventory[ hash] shared.inventoryLock.release() self.sendData(objectType, payload) diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 44cb893c..9658f81b 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -39,7 +39,7 @@ class singleCleaner(threading.Thread): with shared.inventoryLock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock. with SqlBulkExecute() as sql: for hash, storedValue in shared.inventory.items(): - objectType, streamNumber, payload, receivedTime = storedValue + objectType, streamNumber, payload, receivedTime, tag = storedValue if int(time.time()) - 3600 > receivedTime: sql.execute( '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''', @@ -48,7 +48,7 @@ class singleCleaner(threading.Thread): streamNumber, payload, receivedTime, - '') + tag) del shared.inventory[hash] shared.UISignalQueue.put(('updateStatusBar', '')) shared.broadcastToSendDataQueues(( diff --git a/src/class_singleListener.py b/src/class_singleListener.py index 3890447a..aa382e5b 100644 --- a/src/class_singleListener.py +++ b/src/class_singleListener.py @@ -24,7 +24,7 @@ class singleListener(threading.Thread): def run(self): while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): time.sleep(1) - helper_bootstrap.dns() + #helper_bootstrap.dns() # We typically don't want to accept incoming connections if the user is using a # SOCKS proxy, unless they have configured otherwise. If they eventually select # proxy 'none' or configure SOCKS listening then this will start listening for diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index d8990ede..943f995d 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -24,10 +24,15 @@ class singleWorker(threading.Thread): def run(self): queryreturn = sqlQuery( - '''SELECT toripe FROM sent WHERE ((status='awaitingpubkey' OR status='doingpubkeypow') AND folder='sent')''') + '''SELECT toripe, toaddress FROM sent WHERE ((status='awaitingpubkey' OR status='doingpubkeypow') AND folder='sent')''') for row in queryreturn: - toripe, = row - shared.neededPubkeys[toripe] = 0 + toripe, toaddress = row + toStatus, toAddressVersionNumber, toStreamNumber, toRipe = decodeAddress(toaddress)[1] + if toAddressVersionNumber <= 3 : + shared.neededPubkeys[toripe] = 0 + elif toAddressVersionNumber >= 4: + privEncryptionKey = hashlib.sha512(hashlib.sha512(encodeVarint(toAddressVersionNumber)+encodeVarint(toStreamNumber)+toRipe).digest()).digest()[:32] # Note that this is the first half of the sha512 hash. + shared.neededPubkeys[toripe] = highlevelcrypto.makeCryptor(privEncryptionKey.encode('hex')) # We'll need this for when we receive a pubkey reply: it will be encrypted and we'll need to decrypt it. # Initialize the shared.ackdataForWhichImWatching data structure using data # from the sql database. @@ -64,23 +69,8 @@ class singleWorker(threading.Thread): self.doPOWForMyV2Pubkey(data) elif command == 'sendOutOrStoreMyV3Pubkey': self.sendOutOrStoreMyV3Pubkey(data) - """elif command == 'newpubkey': - toAddressVersion,toStreamNumber,toRipe = data - if toRipe in shared.neededPubkeys: - print 'We have been awaiting the arrival of this pubkey.' - del shared.neededPubkeys[toRipe] - t = (toRipe,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND status='awaitingpubkey' and folder='sent' ''') - shared.sqlSubmitQueue.put(t) - shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() - self.sendMsg() - else: - with shared.printLock: - print 'We don\'t need this pub key. We didn\'t ask for it. Pubkey hash:', toRipe.encode('hex') - """ + elif command == 'sendOutOrStoreMyV4Pubkey': + self.sendOutOrStoreMyV4Pubkey(data) else: with shared.printLock: sys.stderr.write( @@ -150,7 +140,7 @@ class singleWorker(threading.Thread): inventoryHash = calculateInventoryHash(payload) objectType = 'pubkey' shared.inventory[inventoryHash] = ( - objectType, streamNumber, payload, embeddedTime) + objectType, streamNumber, payload, embeddedTime,'') shared.inventorySets[streamNumber].add(inventoryHash) with shared.printLock: @@ -224,7 +214,7 @@ class singleWorker(threading.Thread): inventoryHash = calculateInventoryHash(payload) objectType = 'pubkey' shared.inventory[inventoryHash] = ( - objectType, streamNumber, payload, embeddedTime) + objectType, streamNumber, payload, embeddedTime,'') shared.inventorySets[streamNumber].add(inventoryHash) with shared.printLock: @@ -249,6 +239,107 @@ class singleWorker(threading.Thread): with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) + # 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 + # assembles the pubkey and stores is in the pubkey table so that we can + # send messages to "ourselves". + def sendOutOrStoreMyV4Pubkey(self, myAddress): + status, addressVersionNumber, streamNumber, hash = decodeAddress( + myAddress) + embeddedTime = int(time.time() + random.randrange( + -300, 300)) # the current time plus or minus five minutes + payload = pack('>Q', (embeddedTime)) + payload += encodeVarint(addressVersionNumber) # Address version number + payload += encodeVarint(streamNumber) + + + dataToEncrypt = '\x00\x00\x00\x01' # bitfield of features supported by me (see the wiki). + + try: + privSigningKeyBase58 = shared.config.get( + myAddress, 'privsigningkey') + privEncryptionKeyBase58 = shared.config.get( + myAddress, 'privencryptionkey') + except Exception as err: + with shared.printLock: + sys.stderr.write( + 'Error within sendOutOrStoreMyV4Pubkey. Could not read the keys from the keys.dat file for a requested address. %s\n' % err) + + return + + privSigningKeyHex = shared.decodeWalletImportFormat( + privSigningKeyBase58).encode('hex') + privEncryptionKeyHex = shared.decodeWalletImportFormat( + privEncryptionKeyBase58).encode('hex') + pubSigningKey = highlevelcrypto.privToPub( + privSigningKeyHex).decode('hex') + pubEncryptionKey = highlevelcrypto.privToPub( + privEncryptionKeyHex).decode('hex') + + dataToEncrypt += pubSigningKey[1:] + dataToEncrypt += pubEncryptionKey[1:] + + dataToEncrypt += encodeVarint(shared.config.getint( + myAddress, 'noncetrialsperbyte')) + dataToEncrypt += encodeVarint(shared.config.getint( + myAddress, 'payloadlengthextrabytes')) + signature = highlevelcrypto.sign(payload + dataToEncrypt, privSigningKeyHex) + dataToEncrypt += encodeVarint(len(signature)) + dataToEncrypt += signature + + # 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 + # 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 + # so that nodes know which pubkey object to try to decrypt when they + # want to send a message. + doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint( + addressVersionNumber) + encodeVarint(streamNumber) + hash).digest()).digest() + privEncryptionKey = doubleHashOfAddressData[:32] + pubEncryptionKey = pointMult(privEncryptionKey) + payload += doubleHashOfAddressData[32:] # the tag + payload += highlevelcrypto.encrypt( + dataToEncrypt, pubEncryptionKey.encode('hex')) + + #################### + + + if not shared.safeConfigGetBoolean(myAddress, 'chan'): + # Do the POW for this pubkey message + target = 2 ** 64 / ((len(payload) + shared.networkDefaultPayloadLengthExtraBytes + + 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) + print '(For pubkey message) Doing proof of work...' + initialHash = hashlib.sha512(payload).digest() + trialValue, nonce = proofofwork.run(target, initialHash) + print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce + + payload = pack('>Q', nonce) + payload + inventoryHash = calculateInventoryHash(payload) + objectType = 'pubkey' + shared.inventory[inventoryHash] = ( + objectType, streamNumber, payload, embeddedTime, doubleHashOfAddressData[32:]) + shared.inventorySets[streamNumber].add(inventoryHash) + + with shared.printLock: + print 'broadcasting inv with hash:', inventoryHash.encode('hex') + + shared.broadcastToSendDataQueues(( + streamNumber, 'advertiseobject', inventoryHash)) + 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, + payload, + embeddedTime, + 'yes') + shared.config.set( + myAddress, 'lastpubkeysendtime', str(int(time.time()))) + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) + def sendBroadcast(self): queryreturn = sqlQuery( '''SELECT fromaddress, subject, message, ackdata FROM sent WHERE status=? and folder='sent' ''', 'broadcastqueued') @@ -328,7 +419,7 @@ class singleWorker(threading.Thread): inventoryHash = calculateInventoryHash(payload) objectType = 'broadcast' shared.inventory[inventoryHash] = ( - objectType, streamNumber, payload, int(time.time())) + objectType, streamNumber, payload, int(time.time()),'') shared.inventorySets[streamNumber].add(inventoryHash) with shared.printLock: print 'sending inv (within sendBroadcast function) for object:', inventoryHash.encode('hex') @@ -439,10 +530,13 @@ class singleWorker(threading.Thread): for row in queryreturn: pubkeyPayload, = row - # The pubkey message is stored the way we originally received it + # The v3 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. - readPosition = 8 # to bypass the nonce + 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 @@ -482,7 +576,7 @@ class singleWorker(threading.Thread): 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: + elif toAddressVersionNumber >= 3: requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint( pubkeyPayload[readPosition:readPosition + 10]) readPosition += varintLength @@ -652,7 +746,7 @@ class singleWorker(threading.Thread): inventoryHash = calculateInventoryHash(encryptedPayload) objectType = 'msg' shared.inventory[inventoryHash] = ( - objectType, toStreamNumber, encryptedPayload, int(time.time())) + objectType, toStreamNumber, encryptedPayload, int(time.time()),'') shared.inventorySets[toStreamNumber].add(inventoryHash) if shared.safeConfigGetBoolean(toaddress, 'chan'): shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Sent on %1").arg(unicode( @@ -683,14 +777,23 @@ class singleWorker(threading.Thread): toAddress) + '. Please report this error to Atheros.') return - shared.neededPubkeys[ripe] = 0 + if addressVersionNumber <= 3: + shared.neededPubkeys[ripe] = 0 + elif addressVersionNumber >= 4: + privEncryptionKey = hashlib.sha512(hashlib.sha512(encodeVarint(addressVersionNumber)+encodeVarint(streamNumber)+ripe).digest()).digest()[:32] # Note that this is the first half of the sha512 hash. + shared.neededPubkeys[ripe] = highlevelcrypto.makeCryptor(privEncryptionKey.encode('hex')) # We'll need this for when we receive a pubkey reply: it will be encrypted and we'll need to decrypt it. payload = pack('>Q', (int(time.time()) + random.randrange( -300, 300))) # the current time plus or minus five minutes. payload += encodeVarint(addressVersionNumber) payload += encodeVarint(streamNumber) - payload += ripe - with shared.printLock: - print 'making request for pubkey with ripe:', ripe.encode('hex') + if addressVersionNumber <= 3: + payload += ripe + with shared.printLock: + print 'making request for pubkey with ripe:', ripe.encode('hex') + else: + payload += hashlib.sha512(hashlib.sha512(encodeVarint(addressVersionNumber)+encodeVarint(streamNumber)+ripe).digest()).digest()[32:] # Note that this is the second half of the sha512 hash. + with shared.printLock: + print 'making request for v4 pubkey with ripe:', ripe.encode('hex') # print 'trial value', trialValue statusbar = 'Doing the computations necessary to request the recipient\'s public key.' @@ -709,7 +812,7 @@ class singleWorker(threading.Thread): inventoryHash = calculateInventoryHash(payload) objectType = 'getpubkey' shared.inventory[inventoryHash] = ( - objectType, streamNumber, payload, int(time.time())) + objectType, streamNumber, payload, int(time.time()),'') shared.inventorySets[streamNumber].add(inventoryHash) print 'sending inv (for the getpubkey message)' shared.broadcastToSendDataQueues(( diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index 34109172..7e0740a9 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -48,7 +48,7 @@ class sqlThread(threading.Thread): self.cur.execute( '''CREATE TABLE pubkeys (hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE)''' ) self.cur.execute( - '''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, first20bytesofencryptedmessage blob, UNIQUE(hash) ON CONFLICT REPLACE)''' ) + '''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, first20bytesofencryptedmessage blob, tag blob, UNIQUE(hash) ON CONFLICT REPLACE)''' ) self.cur.execute( '''CREATE TABLE knownnodes (timelastseen int, stream int, services blob, host blob, port blob, UNIQUE(host, stream, port) ON CONFLICT REPLACE)''' ) # This table isn't used in the program yet but I @@ -57,7 +57,7 @@ class sqlThread(threading.Thread): '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''') self.cur.execute( '''CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)''' ) - self.cur.execute( '''INSERT INTO settings VALUES('version','2')''') + self.cur.execute( '''INSERT INTO settings VALUES('version','3')''') self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', ( int(time.time()),)) self.conn.commit() @@ -206,6 +206,19 @@ class sqlThread(threading.Thread): parameters = (2,) self.cur.execute(item, parameters) + # Add a new column to the inventory table to store pubkeys' tags. + item = '''SELECT value FROM settings WHERE key='version';''' + parameters = '' + self.cur.execute(item, parameters) + if int(self.cur.fetchall()[0][0]) == 2: + print 'upgrading database' + item = '''ALTER TABLE inventory ADD tag blob DEFAULT '' ''' + parameters = '' + self.cur.execute(item, parameters) + item = '''update settings set value=? WHERE key='version';''' + parameters = (3,) + self.cur.execute(item, parameters) + if not shared.config.has_option('bitmessagesettings', 'userlocale'): shared.config.set('bitmessagesettings', 'userlocale', 'system') if not shared.config.has_option('bitmessagesettings', 'sendoutgoingconnections'): diff --git a/src/defaultKnownNodes.py b/src/defaultKnownNodes.py index f481581c..6916218c 100644 --- a/src/defaultKnownNodes.py +++ b/src/defaultKnownNodes.py @@ -11,14 +11,15 @@ def createDefaultKnownNodes(appdata): ############## Stream 1 ################ stream1 = {} - stream1[shared.Peer('85.171.174.131', 8444)] = int(time.time()) + """stream1[shared.Peer('85.171.174.131', 8444)] = int(time.time()) stream1[shared.Peer('23.28.68.159', 8444)] = int(time.time()) stream1[shared.Peer('66.108.210.240', 8444)] = int(time.time()) stream1[shared.Peer('204.236.246.212', 8444)] = int(time.time()) stream1[shared.Peer('78.81.56.239', 8444)] = int(time.time()) stream1[shared.Peer('122.60.235.157', 8444)] = int(time.time()) stream1[shared.Peer('204.236.246.212', 8444)] = int(time.time()) - stream1[shared.Peer('24.98.219.109', 8444)] = int(time.time()) + stream1[shared.Peer('24.98.219.109', 8444)] = int(time.time())""" + stream1[shared.Peer('12.34.56.78', 8444)] = int(time.time()) ############# Stream 2 ################# diff --git a/src/shared.py b/src/shared.py index 22a6d890..dffa2e44 100644 --- a/src/shared.py +++ b/src/shared.py @@ -5,7 +5,7 @@ lengthOfTimeToLeaveObjectsInInventory = 237600 # Equals two days and 18 hours. lengthOfTimeToHoldOnToAllPubkeys = 2419200 # Equals 4 weeks. You could make this longer if you want but making it shorter would not be advisable because there is a very small possibility that it could keep you from obtaining a needed pubkey for a period of time. maximumAgeOfObjectsThatIAdvertiseToOthers = 216000 # Equals two days and 12 hours maximumAgeOfNodesThatIAdvertiseToOthers = 10800 # Equals three hours -useVeryEasyProofOfWorkForTesting = False # If you set this to True while on the normal network, you won't be able to send or sometimes receive messages. +useVeryEasyProofOfWorkForTesting = True # If you set this to True while on the normal network, you won't be able to send or sometimes receive messages. # Libraries. @@ -311,9 +311,9 @@ def flushInventory(): #Note that the singleCleanerThread clears out the inventory dictionary from time to time, although it only clears things that have been in the dictionary for a long time. This clears the inventory dictionary Now. with SqlBulkExecute() as sql: for hash, storedValue in inventory.items(): - objectType, streamNumber, payload, receivedTime = storedValue - sql.execute('''INSERT INTO inventory VALUES (?,?,?,?,?,?)''', - hash,objectType,streamNumber,payload,receivedTime,'') + objectType, streamNumber, payload, receivedTime, tag = storedValue + sql.execute('''INSERT INTO inventory VALUES (?,?,?,?,?,?,?)''', + hash,objectType,streamNumber,payload,receivedTime,'',tag) del inventory[hash] def fixPotentiallyInvalidUTF8Data(text):