def run(self): while state.shutdown == 0: queueValue = queues.addressGeneratorQueue.get() nonceTrialsPerByte = 0 payloadLengthExtraBytes = 0 live = True if queueValue[0] == 'createChan': command, addressVersionNumber, streamNumber, label, \ deterministicPassphrase, live = queueValue eighteenByteRipe = False numberOfAddressesToMake = 1 numberOfNullBytesDemandedOnFrontOfRipeHash = 1 elif queueValue[0] == 'joinChan': command, chanAddress, label, deterministicPassphrase, \ live = queueValue eighteenByteRipe = False addressVersionNumber = decodeAddress(chanAddress)[1] streamNumber = decodeAddress(chanAddress)[2] numberOfAddressesToMake = 1 numberOfNullBytesDemandedOnFrontOfRipeHash = 1 elif len(queueValue) == 7: command, addressVersionNumber, streamNumber, label, \ numberOfAddressesToMake, deterministicPassphrase, \ eighteenByteRipe = queueValue try: numberOfNullBytesDemandedOnFrontOfRipeHash = \ BMConfigParser().getint( 'bitmessagesettings', 'numberofnullbytesonaddress' ) except: if eighteenByteRipe: numberOfNullBytesDemandedOnFrontOfRipeHash = 2 else: # the default numberOfNullBytesDemandedOnFrontOfRipeHash = 1 elif len(queueValue) == 9: command, addressVersionNumber, streamNumber, label, \ numberOfAddressesToMake, deterministicPassphrase, \ eighteenByteRipe, nonceTrialsPerByte, \ payloadLengthExtraBytes = queueValue try: numberOfNullBytesDemandedOnFrontOfRipeHash = \ BMConfigParser().getint( 'bitmessagesettings', 'numberofnullbytesonaddress' ) except: if eighteenByteRipe: numberOfNullBytesDemandedOnFrontOfRipeHash = 2 else: # the default numberOfNullBytesDemandedOnFrontOfRipeHash = 1 elif queueValue[0] == 'stopThread': break else: logger.error( 'Programming error: A structure with the wrong number' ' of values was passed into the addressGeneratorQueue.' ' Here is the queueValue: %r\n', queueValue) if addressVersionNumber < 3 or addressVersionNumber > 4: logger.error( '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: nonceTrialsPerByte = BMConfigParser().getint( 'bitmessagesettings', 'defaultnoncetrialsperbyte') if nonceTrialsPerByte < \ defaults.networkDefaultProofOfWorkNonceTrialsPerByte: nonceTrialsPerByte = \ defaults.networkDefaultProofOfWorkNonceTrialsPerByte if payloadLengthExtraBytes == 0: payloadLengthExtraBytes = BMConfigParser().getint( 'bitmessagesettings', 'defaultpayloadlengthextrabytes') if payloadLengthExtraBytes < \ defaults.networkDefaultPayloadLengthExtraBytes: payloadLengthExtraBytes = \ defaults.networkDefaultPayloadLengthExtraBytes if command == 'createRandomAddress': queues.UISignalQueue.put( ('updateStatusBar', tr._translate("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 = highlevelcrypto.pointMult( potentialPrivSigningKey) while True: numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 potentialPrivEncryptionKey = OpenSSL.rand(32) potentialPubEncryptionKey = highlevelcrypto.pointMult( potentialPrivEncryptionKey) sha = hashlib.new('sha512') sha.update(potentialPubSigningKey + potentialPubEncryptionKey) ripe = RIPEMD160Hash(sha.digest()).digest() if (ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash] == '\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash): break logger.info('Generated address with ripe digest: %s', hexlify(ripe)) try: logger.info( 'Address generator calculated %s addresses at %s' ' addresses per second before finding one with' ' the correct ripe-prefix.', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime)) except ZeroDivisionError: # The user must have a pretty fast computer. # time.time() - startTime equaled zero. pass address = encodeAddress(addressVersionNumber, streamNumber, ripe) # 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) BMConfigParser().add_section(address) BMConfigParser().set(address, 'label', label) BMConfigParser().set(address, 'enabled', 'true') BMConfigParser().set(address, 'decoy', 'false') BMConfigParser().set(address, 'noncetrialsperbyte', str(nonceTrialsPerByte)) BMConfigParser().set(address, 'payloadlengthextrabytes', str(payloadLengthExtraBytes)) BMConfigParser().set(address, 'privsigningkey', privSigningKeyWIF) BMConfigParser().set(address, 'privencryptionkey', privEncryptionKeyWIF) BMConfigParser().save() # The API and the join and create Chan functionality # both need information back from the address generator. queues.apiAddressGeneratorReturnQueue.put(address) queues.UISignalQueue.put( ('updateStatusBar', tr._translate( "MainWindow", "Done generating address. Doing work necessary" " to broadcast it..."))) queues.UISignalQueue.put( ('writeNewAddressToTable', (label, address, streamNumber))) shared.reloadMyAddressHashes() if addressVersionNumber == 3: queues.workerQueue.put(('sendOutOrStoreMyV3Pubkey', ripe)) elif addressVersionNumber == 4: queues.workerQueue.put( ('sendOutOrStoreMyV4Pubkey', address)) elif command == 'createDeterministicAddresses' \ or command == 'getDeterministicAddress' \ or command == 'createChan' or command == 'joinChan': if len(deterministicPassphrase) == 0: logger.warning( 'You are creating deterministic' ' address(es) using a blank passphrase.' ' Bitmessage will do it but it is rather stupid.') if command == 'createDeterministicAddresses': queues.UISignalQueue.put( ('updateStatusBar', tr._translate("MainWindow", "Generating %1 new addresses.").arg( str(numberOfAddressesToMake)))) signingKeyNonce = 0 encryptionKeyNonce = 1 # We fill out this list no matter what although we only # need it if we end up passing the info to the API. listOfNewAddressesToSendOutThroughTheAPI = [] for _ 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 = highlevelcrypto.pointMult( potentialPrivSigningKey) potentialPubEncryptionKey = highlevelcrypto.pointMult( potentialPrivEncryptionKey) signingKeyNonce += 2 encryptionKeyNonce += 2 sha = hashlib.new('sha512') sha.update(potentialPubSigningKey + potentialPubEncryptionKey) ripe = RIPEMD160Hash(sha.digest()).digest() if (ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash] == '\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash): break logger.info('Generated address with ripe digest: %s', hexlify(ripe)) try: logger.info( 'Address generator calculated %s addresses' ' at %s addresses per second before finding' ' one with the correct ripe-prefix.', numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix, numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix / (time.time() - startTime)) except ZeroDivisionError: # The user must have a pretty fast computer. # time.time() - startTime equaled zero. pass address = encodeAddress(addressVersionNumber, streamNumber, ripe) 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: listOfNewAddressesToSendOutThroughTheAPI.append( 'chan name does not match address') saveAddressToDisk = False if command == 'getDeterministicAddress': saveAddressToDisk = False if saveAddressToDisk and live: # 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) try: BMConfigParser().add_section(address) addressAlreadyExists = False except: addressAlreadyExists = True if addressAlreadyExists: logger.info( '%s already exists. Not adding it again.', address) queues.UISignalQueue.put( ('updateStatusBar', tr._translate( "MainWindow", "%1 is already in 'Your Identities'." " Not adding it again.").arg(address))) else: logger.debug('label: %s', label) BMConfigParser().set(address, 'label', label) BMConfigParser().set(address, 'enabled', 'true') BMConfigParser().set(address, 'decoy', 'false') if command == 'joinChan' \ or command == 'createChan': BMConfigParser().set(address, 'chan', 'true') BMConfigParser().set(address, 'noncetrialsperbyte', str(nonceTrialsPerByte)) BMConfigParser().set(address, 'payloadlengthextrabytes', str(payloadLengthExtraBytes)) BMConfigParser().set(address, 'privSigningKey', privSigningKeyWIF) BMConfigParser().set(address, 'privEncryptionKey', privEncryptionKeyWIF) BMConfigParser().save() queues.UISignalQueue.put( ('writeNewAddressToTable', (label, address, str(streamNumber)))) listOfNewAddressesToSendOutThroughTheAPI.append( address) shared.myECCryptorObjects[ripe] = \ highlevelcrypto.makeCryptor( hexlify(potentialPrivEncryptionKey)) shared.myAddressesByHash[ripe] = address tag = hashlib.sha512( hashlib.sha512( encodeVarint(addressVersionNumber) + encodeVarint(streamNumber) + ripe).digest()).digest()[32:] shared.myAddressesByTag[tag] = address if addressVersionNumber == 3: # If this is a chan address, # the worker thread won't send out # the pubkey over the network. queues.workerQueue.put( ('sendOutOrStoreMyV3Pubkey', ripe)) elif addressVersionNumber == 4: queues.workerQueue.put( ('sendOutOrStoreMyV4Pubkey', address)) queues.UISignalQueue.put( ('updateStatusBar', tr._translate("MainWindow", "Done generating address"))) elif saveAddressToDisk and not live \ and not BMConfigParser().has_section(address): listOfNewAddressesToSendOutThroughTheAPI.append( address) # Done generating addresses. if command == 'createDeterministicAddresses' \ or command == 'joinChan' or command == 'createChan': queues.apiAddressGeneratorReturnQueue.put( listOfNewAddressesToSendOutThroughTheAPI) elif command == 'getDeterministicAddress': queues.apiAddressGeneratorReturnQueue.put(address) else: raise Exception( "Error in the addressGenerator thread. Thread was" + " given a command it could not understand: " + command) queues.addressGeneratorQueue.task_done()
def processbroadcast(self, data): messageProcessingStartTime = time.time() shared.numberOfBroadcastsProcessed += 1 queues.UISignalQueue.put( ('updateNumberOfBroadcastsProcessed', 'no data')) inventoryHash = calculateInventoryHash(data) readPosition = 20 # bypass the nonce, time, and object type broadcastVersion, broadcastVersionLength = decodeVarint( data[readPosition:readPosition + 9]) readPosition += broadcastVersionLength if broadcastVersion < 4 or broadcastVersion > 5: logger.info( 'Cannot decode incoming broadcast versions less than 4' ' or higher than 5. Assuming the sender isn\'t being silly,' ' you should upgrade Bitmessage because this message shall' ' be ignored.') return cleartextStreamNumber, cleartextStreamNumberLength = decodeVarint( data[readPosition:readPosition + 10]) readPosition += cleartextStreamNumberLength if broadcastVersion == 4: # v4 broadcasts are encrypted the same way the msgs are # encrypted. To see if we are interested in a v4 broadcast, # we try to decrypt it. This was replaced with v5 broadcasts # which include a tag which we check instead, just like we do # with v4 pubkeys. signedData = data[8:readPosition] initialDecryptionSuccessful = False for key, cryptorObject in sorted( shared.MyECSubscriptionCryptorObjects.items(), key=lambda x: random.random()): try: # continue decryption attempts to avoid timing attacks if initialDecryptionSuccessful: cryptorObject.decrypt(data[readPosition:]) else: decryptedData = cryptorObject.decrypt( data[readPosition:]) # This is the RIPE hash of the sender's pubkey. # We need this below to compare to the RIPE hash # of the sender's address to verify that it was # encrypted by with their key rather than some # other key. toRipe = key initialDecryptionSuccessful = True logger.info( 'EC decryption successful using key associated' ' with ripe hash: %s', hexlify(key)) except Exception: logger.debug('cryptorObject.decrypt Exception:', exc_info=True) if not initialDecryptionSuccessful: # This is not a broadcast I am interested in. logger.debug( 'Length of time program spent failing to decrypt this' ' v4 broadcast: %s seconds.', time.time() - messageProcessingStartTime) return elif broadcastVersion == 5: embeddedTag = data[readPosition:readPosition + 32] readPosition += 32 if embeddedTag not in shared.MyECSubscriptionCryptorObjects: logger.debug('We\'re not interested in this broadcast.') return # We are interested in this broadcast because of its tag. # We're going to add some more data which is signed further down. signedData = data[8:readPosition] cryptorObject = shared.MyECSubscriptionCryptorObjects[embeddedTag] try: decryptedData = cryptorObject.decrypt(data[readPosition:]) logger.debug('EC decryption successful') except Exception: logger.debug('Broadcast version %s decryption Unsuccessful.', broadcastVersion) return # At this point this is a broadcast I have decrypted and am # interested in. readPosition = 0 sendersAddressVersion, sendersAddressVersionLength = decodeVarint( decryptedData[readPosition:readPosition + 9]) if broadcastVersion == 4: if sendersAddressVersion < 2 or sendersAddressVersion > 3: logger.warning( 'Cannot decode senderAddressVersion other than 2 or 3.' ' Assuming the sender isn\'t being silly, you should' ' upgrade Bitmessage because this message shall be' ' ignored.') return elif broadcastVersion == 5: if sendersAddressVersion < 4: logger.info( 'Cannot decode senderAddressVersion less than 4 for' ' broadcast version number 5. Assuming the sender' ' isn\'t being silly, you should upgrade Bitmessage' ' because this message shall be ignored.') return readPosition += sendersAddressVersionLength sendersStream, sendersStreamLength = decodeVarint( decryptedData[readPosition:readPosition + 9]) if sendersStream != cleartextStreamNumber: logger.info( 'The stream number outside of the encryption on which the' ' POW was completed doesn\'t match the stream number' ' inside the encryption. Ignoring broadcast.') return readPosition += sendersStreamLength readPosition += 4 sendersPubSigningKey = '\x04' + \ decryptedData[readPosition:readPosition + 64] readPosition += 64 sendersPubEncryptionKey = '\x04' + \ decryptedData[readPosition:readPosition + 64] readPosition += 64 if sendersAddressVersion >= 3: requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = \ decodeVarint(decryptedData[readPosition:readPosition + 10]) readPosition += varintLength logger.debug( 'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte' ' is %s', requiredAverageProofOfWorkNonceTrialsPerByte) requiredPayloadLengthExtraBytes, varintLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += varintLength logger.debug('sender\'s requiredPayloadLengthExtraBytes is %s', requiredPayloadLengthExtraBytes) endOfPubkeyPosition = readPosition sha = hashlib.new('sha512') sha.update(sendersPubSigningKey + sendersPubEncryptionKey) calculatedRipe = RIPEMD160Hash(sha.digest()).digest() if broadcastVersion == 4: if toRipe != calculatedRipe: logger.info('The encryption key used to encrypt this message' ' doesn\'t match the keys inbedded in the message' ' itself. Ignoring message.') return elif broadcastVersion == 5: calculatedTag = hashlib.sha512( hashlib.sha512( encodeVarint(sendersAddressVersion) + encodeVarint(sendersStream) + calculatedRipe).digest()).digest()[32:] if calculatedTag != embeddedTag: logger.debug('The tag and encryption key used to encrypt this' ' message doesn\'t match the keys inbedded in the' ' message itself. Ignoring message.') return messageEncodingType, messageEncodingTypeLength = decodeVarint( decryptedData[readPosition:readPosition + 9]) if messageEncodingType == 0: return readPosition += messageEncodingTypeLength messageLength, messageLengthLength = decodeVarint( decryptedData[readPosition:readPosition + 9]) readPosition += messageLengthLength message = decryptedData[readPosition:readPosition + messageLength] readPosition += messageLength readPositionAtBottomOfMessage = readPosition signatureLength, signatureLengthLength = decodeVarint( decryptedData[readPosition:readPosition + 9]) readPosition += signatureLengthLength signature = decryptedData[readPosition:readPosition + signatureLength] signedData += decryptedData[:readPositionAtBottomOfMessage] if not highlevelcrypto.verify(signedData, signature, hexlify(sendersPubSigningKey)): logger.debug('ECDSA verify failed') return logger.debug('ECDSA verify passed') # Used to detect and ignore duplicate messages in our inbox sigHash = hashlib.sha512( hashlib.sha512(signature).digest()).digest()[32:] fromAddress = encodeAddress(sendersAddressVersion, sendersStream, calculatedRipe) logger.info('fromAddress: %s' % fromAddress) # Let's store the public key in case we want to reply to this person. sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', fromAddress, sendersAddressVersion, decryptedData[:endOfPubkeyPosition], int(time.time()), 'yes') # Check to see whether we happen to be awaiting this # pubkey in order to send a message. If we are, it will do the POW # and send it. self.possibleNewPubkey(fromAddress) fromAddress = encodeAddress(sendersAddressVersion, sendersStream, calculatedRipe) logger.debug('fromAddress: ' + fromAddress) try: decodedMessage = helper_msgcoding.MsgDecode( messageEncodingType, message) except helper_msgcoding.MsgDecodeException: return subject = decodedMessage.subject body = decodedMessage.body toAddress = '[Broadcast subscribers]' if helper_inbox.isMessageAlreadyInInbox(sigHash): logger.info('This broadcast is already in our inbox. Ignoring it.') return t = (inventoryHash, toAddress, fromAddress, subject, int(time.time()), body, 'inbox', messageEncodingType, 0, sigHash) helper_inbox.insert(t) queues.UISignalQueue.put( ('displayNewInboxMessage', (inventoryHash, toAddress, fromAddress, subject, body))) # If we are behaving as an API then we might need to run an # outside command to let some program know that a new message # has arrived. if BMConfigParser().safeGetBoolean('bitmessagesettings', 'apienabled'): try: apiNotifyPath = BMConfigParser().get('bitmessagesettings', 'apinotifypath') except: apiNotifyPath = '' if apiNotifyPath != '': call([apiNotifyPath, "newBroadcast"]) # Display timing data logger.info('Time spent processing this interesting broadcast: %s', time.time() - messageProcessingStartTime)
def processpubkey(self, data): pubkeyProcessingStartTime = time.time() shared.numberOfPubkeysProcessed += 1 queues.UISignalQueue.put(('updateNumberOfPubkeysProcessed', 'no data')) readPosition = 20 # bypass the nonce, time, and object type addressVersion, varintLength = decodeVarint( data[readPosition:readPosition + 10]) readPosition += varintLength streamNumber, varintLength = decodeVarint( data[readPosition:readPosition + 10]) readPosition += varintLength if addressVersion == 0: logger.debug('(Within processpubkey) addressVersion of 0 doesn\'t' ' make sense.') return if addressVersion > 4 or addressVersion == 1: logger.info( 'This version of Bitmessage cannot handle version %s' ' addresses.', addressVersion) return if addressVersion == 2: # sanity check. This is the minimum possible length. if len(data) < 146: logger.debug( '(within processpubkey) payloadLength less than 146.' ' Sanity check failed.') return readPosition += 4 publicSigningKey = data[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, it would # be easiest to test them here. readPosition += 64 publicEncryptionKey = data[readPosition:readPosition + 64] if len(publicEncryptionKey) < 64: logger.debug( 'publicEncryptionKey length less than 64. Sanity check' ' failed.') return readPosition += 64 # The data we'll store in the pubkeys table. dataToStore = data[20:readPosition] sha = hashlib.new('sha512') sha.update('\x04' + publicSigningKey + '\x04' + publicEncryptionKey) ripe = RIPEMD160Hash(sha.digest()).digest() logger.debug( 'within recpubkey, addressVersion: %s, streamNumber: %s' '\nripe %s\npublicSigningKey in hex: %s' '\npublicEncryptionKey in hex: %s', addressVersion, streamNumber, hexlify(ripe), hexlify(publicSigningKey), hexlify(publicEncryptionKey)) address = encodeAddress(addressVersion, streamNumber, ripe) queryreturn = sqlQuery( "SELECT usedpersonally FROM pubkeys WHERE address=?" " AND usedpersonally='yes'", address) # if this pubkey is already in our database and if we have # used it personally: if queryreturn != []: logger.info( 'We HAVE used this pubkey personally. Updating time.') t = (address, addressVersion, dataToStore, int(time.time()), 'yes') else: logger.info( 'We have NOT used this pubkey personally. Inserting' ' in database.') t = (address, addressVersion, dataToStore, int(time.time()), 'no') sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t) self.possibleNewPubkey(address) if addressVersion == 3: if len(data) < 170: # sanity check. logger.warning( '(within processpubkey) payloadLength less than 170.' ' Sanity check failed.') return readPosition += 4 publicSigningKey = '\x04' + data[readPosition:readPosition + 64] readPosition += 64 publicEncryptionKey = '\x04' + data[readPosition:readPosition + 64] readPosition += 64 _, specifiedNonceTrialsPerByteLength = decodeVarint( data[readPosition:readPosition + 10]) readPosition += specifiedNonceTrialsPerByteLength _, specifiedPayloadLengthExtraBytesLength = decodeVarint( data[readPosition:readPosition + 10]) readPosition += specifiedPayloadLengthExtraBytesLength endOfSignedDataPosition = readPosition # The data we'll store in the pubkeys table. dataToStore = data[20:readPosition] signatureLength, signatureLengthLength = decodeVarint( data[readPosition:readPosition + 10]) readPosition += signatureLengthLength signature = data[readPosition:readPosition + signatureLength] if highlevelcrypto.verify(data[8:endOfSignedDataPosition], signature, hexlify(publicSigningKey)): logger.debug('ECDSA verify passed (within processpubkey)') else: logger.warning('ECDSA verify failed (within processpubkey)') return sha = hashlib.new('sha512') sha.update(publicSigningKey + publicEncryptionKey) ripe = RIPEMD160Hash(sha.digest()).digest() logger.debug( 'within recpubkey, addressVersion: %s, streamNumber: %s' '\nripe %s\npublicSigningKey in hex: %s' '\npublicEncryptionKey in hex: %s', addressVersion, streamNumber, hexlify(ripe), hexlify(publicSigningKey), hexlify(publicEncryptionKey)) address = encodeAddress(addressVersion, streamNumber, ripe) queryreturn = sqlQuery( "SELECT usedpersonally FROM pubkeys WHERE address=?" " AND usedpersonally='yes'", address) # if this pubkey is already in our database and if we have # used it personally: if queryreturn != []: logger.info( 'We HAVE used this pubkey personally. Updating time.') t = (address, addressVersion, dataToStore, int(time.time()), 'yes') else: logger.info( 'We have NOT used this pubkey personally. Inserting' ' in database.') t = (address, addressVersion, dataToStore, int(time.time()), 'no') sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t) self.possibleNewPubkey(address) if addressVersion == 4: if len(data) < 350: # sanity check. logger.debug( '(within processpubkey) payloadLength less than 350.' ' Sanity check failed.') return tag = data[readPosition:readPosition + 32] if tag not in state.neededPubkeys: logger.info( 'We don\'t need this v4 pubkey. We didn\'t ask for it.') return # Let us try to decrypt the pubkey toAddress, _ = state.neededPubkeys[tag] if protocol.decryptAndCheckPubkeyPayload(data, toAddress) == \ 'successful': # At this point we know that we have been waiting on this # pubkey. This function will command the workerThread # to start work on the messages that require it. self.possibleNewPubkey(toAddress) # Display timing data timeRequiredToProcessPubkey = time.time() - pubkeyProcessingStartTime logger.debug('Time required to process this pubkey: %s', timeRequiredToProcessPubkey)
def processmsg(self, data): messageProcessingStartTime = time.time() shared.numberOfMessagesProcessed += 1 queues.UISignalQueue.put( ('updateNumberOfMessagesProcessed', 'no data')) readPosition = 20 # bypass the nonce, time, and object type msgVersion, msgVersionLength = decodeVarint( data[readPosition:readPosition + 9]) if msgVersion != 1: logger.info('Cannot understand message versions other than one.' ' Ignoring message.') return readPosition += msgVersionLength streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = \ decodeVarint(data[readPosition:readPosition + 9]) readPosition += streamNumberAsClaimedByMsgLength inventoryHash = calculateInventoryHash(data) initialDecryptionSuccessful = False # This is not an acknowledgement bound for me. See if it is a message # bound for me by trying to decrypt it with my private keys. for key, cryptorObject in sorted(shared.myECCryptorObjects.items(), key=lambda x: random.random()): try: # continue decryption attempts to avoid timing attacks if initialDecryptionSuccessful: cryptorObject.decrypt(data[readPosition:]) else: decryptedData = cryptorObject.decrypt(data[readPosition:]) # This is the RIPE hash of my pubkeys. We need this # below to compare to the destination_ripe included # in the encrypted data. toRipe = key initialDecryptionSuccessful = True logger.info( 'EC decryption successful using key associated' ' with ripe hash: %s.', hexlify(key)) except Exception: pass if not initialDecryptionSuccessful: # This is not a message bound for me. logger.info( 'Length of time program spent failing to decrypt this' ' message: %s seconds.', time.time() - messageProcessingStartTime) return # This is a message bound for me. # Look up my address based on the RIPE hash. toAddress = shared.myAddressesByHash[toRipe] readPosition = 0 sendersAddressVersionNumber, sendersAddressVersionNumberLength = \ decodeVarint(decryptedData[readPosition:readPosition + 10]) readPosition += sendersAddressVersionNumberLength if sendersAddressVersionNumber == 0: logger.info('Cannot understand sendersAddressVersionNumber = 0.' ' Ignoring message.') return if sendersAddressVersionNumber > 4: logger.info( 'Sender\'s address version number %s not yet supported.' ' Ignoring message.', sendersAddressVersionNumber) return if len(decryptedData) < 170: logger.info('Length of the unencrypted data is unreasonably short.' ' Sanity check failed. Ignoring message.') return sendersStreamNumber, sendersStreamNumberLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) if sendersStreamNumber == 0: logger.info('sender\'s stream number is 0. Ignoring message.') return readPosition += sendersStreamNumberLength readPosition += 4 pubSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64] readPosition += 64 pubEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64] readPosition += 64 if sendersAddressVersionNumber >= 3: requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = \ decodeVarint(decryptedData[readPosition:readPosition + 10]) readPosition += varintLength logger.info( 'sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is %s', requiredAverageProofOfWorkNonceTrialsPerByte) requiredPayloadLengthExtraBytes, varintLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += varintLength logger.info('sender\'s requiredPayloadLengthExtraBytes is %s', requiredPayloadLengthExtraBytes) # needed for when we store the pubkey in our database of pubkeys # for later use. endOfThePublicKeyPosition = readPosition if toRipe != decryptedData[readPosition:readPosition + 20]: logger.info( 'The original sender of this message did not send it to' ' you. Someone is attempting a Surreptitious Forwarding' ' Attack.\nSee: ' 'http://world.std.com/~dtd/sign_encrypt/sign_encrypt7.html' '\nyour toRipe: %s\nembedded destination toRipe: %s', hexlify(toRipe), hexlify(decryptedData[readPosition:readPosition + 20])) return readPosition += 20 messageEncodingType, messageEncodingTypeLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += messageEncodingTypeLength messageLength, messageLengthLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += messageLengthLength message = decryptedData[readPosition:readPosition + messageLength] # print 'First 150 characters of message:', repr(message[:150]) readPosition += messageLength ackLength, ackLengthLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += ackLengthLength ackData = decryptedData[readPosition:readPosition + ackLength] readPosition += ackLength # needed to mark the end of what is covered by the signature positionOfBottomOfAckData = readPosition signatureLength, signatureLengthLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += signatureLengthLength signature = decryptedData[readPosition:readPosition + signatureLength] signedData = data[8:20] + encodeVarint( 1) + encodeVarint(streamNumberAsClaimedByMsg ) + decryptedData[:positionOfBottomOfAckData] if not highlevelcrypto.verify(signedData, signature, hexlify(pubSigningKey)): logger.debug('ECDSA verify failed') return logger.debug('ECDSA verify passed') logger.debug( 'As a matter of intellectual curiosity, here is the Bitcoin' ' address associated with the keys owned by the other person:' ' %s ..and here is the testnet address: %s. The other person' ' must take their private signing key from Bitmessage and' ' import it into Bitcoin (or a service like Blockchain.info)' ' for it to be of any use. Do not use this unless you know' ' what you are doing.', helper_bitcoin.calculateBitcoinAddressFromPubkey(pubSigningKey), helper_bitcoin.calculateTestnetAddressFromPubkey(pubSigningKey)) # Used to detect and ignore duplicate messages in our inbox sigHash = hashlib.sha512( hashlib.sha512(signature).digest()).digest()[32:] # calculate the fromRipe. sha = hashlib.new('sha512') sha.update(pubSigningKey + pubEncryptionKey) ripe = RIPEMD160Hash(sha.digest()).digest() fromAddress = encodeAddress(sendersAddressVersionNumber, sendersStreamNumber, ripe) # Let's store the public key in case we want to reply to this # person. sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', fromAddress, sendersAddressVersionNumber, decryptedData[:endOfThePublicKeyPosition], int(time.time()), 'yes') # Check to see whether we happen to be awaiting this # pubkey in order to send a message. If we are, it will do the POW # and send it. self.possibleNewPubkey(fromAddress) # If this message is bound for one of my version 3 addresses (or # higher), then we must check to make sure it meets our demanded # proof of work requirement. If this is bound for one of my chan # addresses then we skip this check; the minimum network POW is # fine. # If the toAddress version number is 3 or higher and not one of # my chan addresses: if decodeAddress(toAddress)[1] >= 3 \ and not BMConfigParser().safeGetBoolean(toAddress, 'chan'): # If I'm not friendly with this person: if not shared.isAddressInMyAddressBookSubscriptionsListOrWhitelist( fromAddress): requiredNonceTrialsPerByte = BMConfigParser().getint( toAddress, 'noncetrialsperbyte') requiredPayloadLengthExtraBytes = BMConfigParser().getint( toAddress, 'payloadlengthextrabytes') if not protocol.isProofOfWorkSufficient( data, requiredNonceTrialsPerByte, requiredPayloadLengthExtraBytes): logger.info( 'Proof of work in msg is insufficient only because' ' it does not meet our higher requirement.') return # Gets set to True if the user shouldn't see the message according # to black or white lists. blockMessage = False # If we are using a blacklist if BMConfigParser().get('bitmessagesettings', 'blackwhitelist') == 'black': queryreturn = sqlQuery( "SELECT label FROM blacklist where address=? and enabled='1'", fromAddress) if queryreturn != []: logger.info('Message ignored because address is in blacklist.') blockMessage = True else: # We're using a whitelist queryreturn = sqlQuery( "SELECT label FROM whitelist where address=? and enabled='1'", fromAddress) if queryreturn == []: logger.info( 'Message ignored because address not in whitelist.') blockMessage = True toLabel = BMConfigParser().get(toAddress, 'label') if toLabel == '': toLabel = toAddress try: decodedMessage = helper_msgcoding.MsgDecode( messageEncodingType, message) except helper_msgcoding.MsgDecodeException: return subject = decodedMessage.subject body = decodedMessage.body # Let us make sure that we haven't already received this message if helper_inbox.isMessageAlreadyInInbox(sigHash): logger.info('This msg is already in our inbox. Ignoring it.') blockMessage = True if not blockMessage: if messageEncodingType != 0: t = (inventoryHash, toAddress, fromAddress, subject, int(time.time()), body, 'inbox', messageEncodingType, 0, sigHash) helper_inbox.insert(t) queues.UISignalQueue.put( ('displayNewInboxMessage', (inventoryHash, toAddress, fromAddress, subject, body))) # If we are behaving as an API then we might need to run an # outside command to let some program know that a new message # has arrived. if BMConfigParser().safeGetBoolean('bitmessagesettings', 'apienabled'): try: apiNotifyPath = BMConfigParser().get( 'bitmessagesettings', 'apinotifypath') except: apiNotifyPath = '' if apiNotifyPath != '': call([apiNotifyPath, "newMessage"]) # Let us now check and see whether our receiving address is # behaving as a mailing list if BMConfigParser().safeGetBoolean(toAddress, 'mailinglist') \ and messageEncodingType != 0: try: mailingListName = BMConfigParser().get( toAddress, 'mailinglistname') except: mailingListName = '' # Let us send out this message as a broadcast subject = self.addMailingListNameToSubject( subject, mailingListName) # Let us now send this message out as a broadcast message = time.strftime( "%a, %Y-%m-%d %H:%M:%S UTC", time.gmtime() ) + ' Message ostensibly from ' + fromAddress \ + ':\n\n' + body # The fromAddress for the broadcast that we are about to # send is the toAddress (my address) for the msg message # we are currently processing. fromAddress = toAddress # We don't actually need the ackdata for acknowledgement # since this is a broadcast message but we can use it to # update the user interface when the POW is done generating. streamNumber = decodeAddress(fromAddress)[2] ackdata = genAckPayload(streamNumber, 0) toAddress = '[Broadcast subscribers]' ripe = '' # We really should have a discussion about how to # set the TTL for mailing list broadcasts. This is obviously # hard-coded. TTL = 2 * 7 * 24 * 60 * 60 # 2 weeks t = ( '', toAddress, ripe, fromAddress, subject, message, ackdata, int(time.time()), # sentTime (this doesn't change) int(time.time()), # lastActionTime 0, 'broadcastqueued', 0, 'sent', messageEncodingType, TTL) helper_sent.insert(t) queues.UISignalQueue.put( ('displayNewSentMessage', (toAddress, '[Broadcast subscribers]', fromAddress, subject, message, ackdata))) queues.workerQueue.put(('sendbroadcast', '')) # Don't send ACK if invalid, blacklisted senders, invisible # messages, disabled or chan if (self.ackDataHasAValidHeader(ackData) and not blockMessage and messageEncodingType != 0 and not BMConfigParser().safeGetBoolean(toAddress, 'dontsendack') and not BMConfigParser().safeGetBoolean(toAddress, 'chan')): self._ack_obj.send_data(ackData[24:]) # Display timing data timeRequiredToAttemptToDecryptMessage = time.time( ) - messageProcessingStartTime self.successfullyDecryptMessageTimings.append( timeRequiredToAttemptToDecryptMessage) timing_sum = 0 for item in self.successfullyDecryptMessageTimings: timing_sum += item logger.debug( 'Time to decrypt this message successfully: %s' '\nAverage time for all message decryption successes since' ' startup: %s.', timeRequiredToAttemptToDecryptMessage, timing_sum / len(self.successfullyDecryptMessageTimings))
def decryptAndCheckPubkeyPayload(data, address): """ Version 4 pubkeys are encrypted. This function is run when we already have the address to which we want to try to send a message. The 'data' may come either off of the wire or we might have had it already in our inventory when we tried to send a msg to this particular address. """ try: addressVersion, streamNumber, ripe = decodeAddress(address)[1:] readPosition = 20 # bypass the nonce, time, and object type embeddedAddressVersion, varintLength = decodeVarint( data[readPosition:readPosition + 10]) readPosition += varintLength embeddedStreamNumber, varintLength = decodeVarint( data[readPosition:readPosition + 10]) readPosition += varintLength # We'll store the address version and stream number # (and some more) in the pubkeys table. storedData = data[20:readPosition] if addressVersion != embeddedAddressVersion: logger.info('Pubkey decryption was UNsuccessful' ' due to address version mismatch.') return 'failed' if streamNumber != embeddedStreamNumber: logger.info('Pubkey decryption was UNsuccessful' ' due to stream number mismatch.') return 'failed' tag = data[readPosition:readPosition + 32] readPosition += 32 # the time through the tag. More data is appended onto # signedData below after the decryption. signedData = data[8:readPosition] encryptedData = data[readPosition:] # Let us try to decrypt the pubkey toAddress, cryptorObject = state.neededPubkeys[tag] if toAddress != address: logger.critical( 'decryptAndCheckPubkeyPayload failed due to toAddress' ' mismatch. This is very peculiar.' ' toAddress: %s, address %s', toAddress, address) # the only way I can think that this could happen # is if someone encodes their address data two different ways. # That sort of address-malleability should have been caught # by the UI or API and an error given to the user. return 'failed' 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. logger.info('Pubkey decryption was unsuccessful.') return 'failed' readPosition = 0 # bitfieldBehaviors = decryptedData[readPosition:readPosition + 4] readPosition += 4 publicSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64] readPosition += 64 publicEncryptionKey = '\x04' + decryptedData[ readPosition:readPosition + 64] readPosition += 64 specifiedNonceTrialsPerByteLength = decodeVarint( decryptedData[readPosition:readPosition + 10])[1] readPosition += specifiedNonceTrialsPerByteLength specifiedPayloadLengthExtraBytesLength = decodeVarint( decryptedData[readPosition:readPosition + 10])[1] readPosition += specifiedPayloadLengthExtraBytesLength storedData += decryptedData[:readPosition] signedData += decryptedData[:readPosition] signatureLength, signatureLengthLength = decodeVarint( decryptedData[readPosition:readPosition + 10]) readPosition += signatureLengthLength signature = decryptedData[readPosition:readPosition + signatureLength] if not highlevelcrypto.verify(signedData, signature, hexlify(publicSigningKey)): logger.info( 'ECDSA verify failed (within decryptAndCheckPubkeyPayload)') return 'failed' logger.info( 'ECDSA verify passed (within decryptAndCheckPubkeyPayload)') sha = hashlib.new('sha512') sha.update(publicSigningKey + publicEncryptionKey) embeddedRipe = RIPEMD160Hash(sha.digest()).digest() if embeddedRipe != ripe: # Although this pubkey object had the tag were were looking for # and was encrypted with the correct encryption key, # it doesn't contain the correct pubkeys. Someone is # either being malicious or using buggy software. logger.info( 'Pubkey decryption was UNsuccessful due to RIPE mismatch.') return 'failed' # Everything checked out. Insert it into the pubkeys table. logger.info( 'within decryptAndCheckPubkeyPayload, ' 'addressVersion: %s, streamNumber: %s\nripe %s\n' 'publicSigningKey in hex: %s\npublicEncryptionKey in hex: %s', addressVersion, streamNumber, hexlify(ripe), hexlify(publicSigningKey), hexlify(publicEncryptionKey)) t = (address, addressVersion, storedData, int(time.time()), 'yes') sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t) return 'successful' except varintDecodeError: logger.info( 'Pubkey decryption was UNsuccessful due to a malformed varint.') return 'failed' except Exception: logger.critical( 'Pubkey decryption was UNsuccessful because of' ' an unhandled exception! This is definitely a bug!', exc_info=True) return 'failed'