示例#1
0
    def processMessageWithPOWStatus(self):
        ret = sqlQuery(
            '''SELECT toaddress, fromaddress, subject, message, ackdata, status, ttl, retrynumber FROM sent WHERE (status='doingmsgpow' or status='forcepow') and folder='sent' '''
        )
        for row in ret:
            # Decode data from query
            toaddress, fromaddress, subject, message, ackdata, status, TTL, retryNumber = row
            toStatus, toAddressVersionNumber, toStreamNumber, toRipe = decodeAddress(
                toaddress)
            fromStatus, fromAddressVersionNumber, fromStreamNumber, fromRipe = decodeAddress(
                fromaddress)

            # Set TTL
            if retryNumber == 0:
                if TTL > 28 * 24 * 60 * 60:
                    TTL = 28 * 24 * 60 * 60
            else:
                TTL = 28 * 24 * 60 * 60
            TTL = int(
                TTL +
                random.randrange(-300, 300))  # add some randomness to the TTL
            embeddedTime = int(time.time() + TTL)
            # Get pubkey
            if not shared.config.has_section(
                    toaddress
            ):  # if we aren't sending this to ourselves or a chan
                shared.ackdataForWhichImWatching[ackdata] = 0
                # Let us fetch the recipient's public key out of our database. If
                # the required proof of work difficulty is too hard then we'll
                # abort.
                pubkeyPayload = shared.hadPubkeys[toaddress][2]

                # The pubkey message is stored with the following items all appended:
                #    -address version
                #    -stream number
                #    -behavior bitfield
                #    -pub signing key
                #    -pub encryption key
                #    -nonce trials per byte (if address version is >= 3)
                #    -length extra bytes (if address version is >= 3)

                readPosition = 1  # to bypass the address version whose length is definitely 1
                streamNumber, streamNumberLength = decodeVarint(
                    pubkeyPayload[readPosition:readPosition + 10])
                readPosition += streamNumberLength
                behaviorBitfield = pubkeyPayload[readPosition:readPosition + 4]
                # Mobile users may ask us to include their address's RIPE hash on a message
                # unencrypted. Before we actually do it the sending human must check a box
                # in the settings menu to allow it.
                if shared.isBitSetWithinBitfield(
                        behaviorBitfield, 30
                ):  # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message..
                    if not shared.safeConfigGetBoolean(
                            'bitmessagesettings', 'willinglysendtomobile'
                    ):  # if we are Not willing to include the receiver's RIPE hash on the message..
                        # if the human changes their setting and then sends another message or restarts their client, this one will send at that time.
                        continue
                readPosition += 4  # to bypass the bitfield of behaviors
                # pubSigningKeyBase256 = pubkeyPayload[readPosition:readPosition+64] # We don't use this key for anything here.
                readPosition += 64
                pubEncryptionKeyBase256 = pubkeyPayload[
                    readPosition:readPosition + 64]
                readPosition += 64

                # Let us fetch the amount of work required by the recipient.
                if toAddressVersionNumber == 2:
                    requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
                    requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
                elif toAddressVersionNumber >= 3:
                    requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(
                        pubkeyPayload[readPosition:readPosition + 10])
                    readPosition += varintLength
                    requiredPayloadLengthExtraBytes, varintLength = decodeVarint(
                        pubkeyPayload[readPosition:readPosition + 10])
                    readPosition += varintLength
                    if requiredAverageProofOfWorkNonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte:  # We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network.
                        requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
                    if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes:
                        requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
                    if status != 'forcepow':
                        if (requiredAverageProofOfWorkNonceTrialsPerByte >
                                shared.config.getint(
                                    'bitmessagesettings',
                                    'maxacceptablenoncetrialsperbyte')
                                and shared.config.getint(
                                    'bitmessagesettings',
                                    'maxacceptablenoncetrialsperbyte') != 0
                            ) or (requiredPayloadLengthExtraBytes >
                                  shared.config.getint(
                                      'bitmessagesettings',
                                      'maxacceptablepayloadlengthextrabytes')
                                  and shared.config.getint(
                                      'bitmessagesettings',
                                      'maxacceptablepayloadlengthextrabytes')
                                  != 0):
                            # The demanded difficulty is more than we are willing
                            # to do.
                            sqlExecute(
                                '''UPDATE sent SET status='toodifficult' WHERE ackdata=? ''',
                                ackdata)
                            continue
            else:  # if we are sending a message to ourselves or a chan..
                behaviorBitfield = '\x00\x00\x00\x01'

                try:
                    privEncryptionKeyBase58 = shared.config.get(
                        toaddress, 'privencryptionkey')
                except Exception as err:
                    continue
                privEncryptionKeyHex = shared.decodeWalletImportFormat(
                    privEncryptionKeyBase58).encode('hex')
                pubEncryptionKeyBase256 = highlevelcrypto.privToPub(
                    privEncryptionKeyHex).decode('hex')[1:]
                requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
                requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
            # Now we can start to assemble our message.
            payload = encodeVarint(fromAddressVersionNumber)
            payload += encodeVarint(fromStreamNumber)
            payload += '\x00\x00\x00\x01'  # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features  )

            # We need to convert our private keys to public keys in order
            # to include them.
            try:
                privSigningKeyBase58 = shared.config.get(
                    fromaddress, 'privsigningkey')
                privEncryptionKeyBase58 = shared.config.get(
                    fromaddress, 'privencryptionkey')
            except:
                continue

            privSigningKeyHex = shared.decodeWalletImportFormat(
                privSigningKeyBase58).encode('hex')
            privEncryptionKeyHex = shared.decodeWalletImportFormat(
                privEncryptionKeyBase58).encode('hex')

            pubSigningKey = highlevelcrypto.privToPub(
                privSigningKeyHex).decode('hex')
            pubEncryptionKey = highlevelcrypto.privToPub(
                privEncryptionKeyHex).decode('hex')

            payload += pubSigningKey[
                1:]  # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
            payload += pubEncryptionKey[1:]

            if fromAddressVersionNumber >= 3:
                # If the receiver of our message is in our address book,
                # subscriptions list, or whitelist then we will allow them to
                # do the network-minimum proof of work. Let us check to see if
                # the receiver is in any of those lists.
                payload += encodeVarint(
                    shared.config.getint(fromaddress, 'noncetrialsperbyte'))
                payload += encodeVarint(
                    shared.config.getint(fromaddress,
                                         'payloadlengthextrabytes'))

            payload += toRipe  # This hash will be checked by the receiver of the message to verify that toRipe belongs to them. This prevents a Surreptitious Forwarding Attack.
            payload += '\x02'  # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
            messageToTransmit = 'Subject:' + subject + '\n'
            messageToTransmit += 'Body:' + message
            payload += encodeVarint(len(messageToTransmit))
            payload += messageToTransmit
            if shared.config.has_section(toaddress):
                fullAckPayload = ''
            elif not shared.isBitSetWithinBitfield(behaviorBitfield, 31):
                fullAckPayload = ''
            else:
                fullAckPayload = self.generateFullAckMessage(
                    ackdata, toStreamNumber, TTL
                )  # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
            payload += encodeVarint(len(fullAckPayload))
            payload += fullAckPayload
            dataToSign = pack(
                '>Q', embeddedTime) + '\x00\x00\x00\x02' + encodeVarint(
                    1) + encodeVarint(toStreamNumber) + payload
            signature = highlevelcrypto.sign(dataToSign, privSigningKeyHex)
            payload += encodeVarint(len(signature))
            payload += signature

            # We have assembled the data that will be encrypted.
            try:
                encrypted = highlevelcrypto.encrypt(
                    payload, "04" + pubEncryptionKeyBase256.encode('hex'))
            except:
                sqlExecute(
                    '''UPDATE sent SET status='badkey' WHERE ackdata=?''',
                    ackdata)
                continue

            encryptedPayload = pack('>Q', embeddedTime)
            encryptedPayload += '\x00\x00\x00\x02'  # object type: msg
            encryptedPayload += encodeVarint(1)  # msg version
            encryptedPayload += encodeVarint(toStreamNumber) + encrypted
            target = 2**64 / (
                requiredAverageProofOfWorkNonceTrialsPerByte *
                (len(encryptedPayload) + 8 + requiredPayloadLengthExtraBytes +
                 ((TTL * (len(encryptedPayload) + 8 +
                          requiredPayloadLengthExtraBytes)) / (2**16))))
            powStartTime = time.time()
            initialHash = hashlib.sha512(encryptedPayload).digest()
            trialValue, nonce = proofofwork.run(target, initialHash)

            encryptedPayload = pack('>Q', nonce) + encryptedPayload

            # Sanity check. The encryptedPayload size should never be larger than 256 KiB. There should
            # be checks elsewhere in the code to not let the user try to send a message this large
            # until we implement message continuation.
            if len(encryptedPayload) > 2**18:  # 256 KiB
                print "The payload is lager than 256KiB,So the loop will continue"
                continue

            inventoryHash = calculateInventoryHash(encryptedPayload)
            objectType = 2
            shared.inventory[inventoryHash] = (objectType, toStreamNumber,
                                               encryptedPayload, embeddedTime,
                                               '')
            shared.inventorySets[toStreamNumber].add(inventoryHash)
            shared.broadcastToSendDataQueues(
                (toStreamNumber, 'advertiseobject', inventoryHash))

            # Update the sent message in the sent table with the necessary information.
            if shared.config.has_section(toaddress):
                newStatus = 'msgsentnoackexpected'
            else:
                newStatus = 'msgsent'
            if retryNumber == 0:
                sleepTill = int(time.time()) + TTL
            else:
                sleepTill = int(
                    time.time()) + 28 * 24 * 60 * 60 * 2**retryNumber
            sqlExecute(
                '''UPDATE sent SET msgid=?, status=?, retrynumber=?, sleeptill=?, lastactiontime=? WHERE ackdata=?''',
                inventoryHash, newStatus, retryNumber + 1, sleepTill,
                int(time.time()), ackdata)

            # If we are sending to ourselves or a chan, let's put the message in our own inbox.
            if shared.config.has_section(toaddress):
                sigHash = hashlib.sha512(hashlib.sha512(signature).digest(
                )).digest(
                )[32:]  # Used to detect and ignore duplicate messages in our inbox
                t = (inventoryHash, toaddress, fromaddress, subject,
                     int(time.time()), message, 'inbox', 2, 0, sigHash)
                shared.messages.append(t)
示例#2
0
    def processMessageWithPOWStatus(self):
        ret = sqlQuery(
                '''SELECT toaddress, fromaddress, subject, message, ackdata, status, ttl, retrynumber FROM sent WHERE (status='doingmsgpow' or status='forcepow') and folder='sent' ''')
        for row in ret:
            # Decode data from query
            toaddress, fromaddress, subject, message, ackdata, status, TTL, retryNumber = row 
            toStatus, toAddressVersionNumber, toStreamNumber, toRipe = decodeAddress(toaddress)
            fromStatus, fromAddressVersionNumber, fromStreamNumber, fromRipe = decodeAddress(fromaddress)
            
            # Set TTL
            if retryNumber == 0:
                if TTL > 28 * 24 * 60 * 60:
                    TTL = 28 * 24 * 60 * 60
            else:
                TTL = 28 * 24 * 60 * 60 
            TTL = int(TTL + random.randrange(-300, 300))# add some randomness to the TTL
            embeddedTime = int(time.time() + TTL)
            # Get pubkey 
            if not shared.config.has_section(toaddress): # if we aren't sending this to ourselves or a chan
                shared.ackdataForWhichImWatching[ackdata] = 0
                # Let us fetch the recipient's public key out of our database. If
                # the required proof of work difficulty is too hard then we'll
                # abort.
		pubkeyPayload = shared.hadPubkeys[toaddress][2]

                # The pubkey message is stored with the following items all appended:
                #    -address version
                #    -stream number
                #    -behavior bitfield
                #    -pub signing key
                #    -pub encryption key
                #    -nonce trials per byte (if address version is >= 3) 
                #    -length extra bytes (if address version is >= 3)

                readPosition = 1  # to bypass the address version whose length is definitely 1
                streamNumber, streamNumberLength = decodeVarint(
                    pubkeyPayload[readPosition:readPosition + 10])
                readPosition += streamNumberLength
                behaviorBitfield = pubkeyPayload[readPosition:readPosition + 4]
                # Mobile users may ask us to include their address's RIPE hash on a message
                # unencrypted. Before we actually do it the sending human must check a box
                # in the settings menu to allow it.
                if shared.isBitSetWithinBitfield(behaviorBitfield,30): # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message..
                    if not shared.safeConfigGetBoolean('bitmessagesettings','willinglysendtomobile'): # if we are Not willing to include the receiver's RIPE hash on the message..
                        # if the human changes their setting and then sends another message or restarts their client, this one will send at that time.
                        continue
                readPosition += 4  # to bypass the bitfield of behaviors
                # pubSigningKeyBase256 = pubkeyPayload[readPosition:readPosition+64] # We don't use this key for anything here.
                readPosition += 64
                pubEncryptionKeyBase256 = pubkeyPayload[readPosition:readPosition + 64]
                readPosition += 64

                # Let us fetch the amount of work required by the recipient.
                if toAddressVersionNumber == 2:
                    requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
                    requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
                elif toAddressVersionNumber >= 3:
                    requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(
                        pubkeyPayload[readPosition:readPosition + 10])
                    readPosition += varintLength
                    requiredPayloadLengthExtraBytes, varintLength = decodeVarint(
                        pubkeyPayload[readPosition:readPosition + 10])
                    readPosition += varintLength
                    if requiredAverageProofOfWorkNonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte:  # We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network.
                        requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
                    if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes:
                        requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
                    if status != 'forcepow':
                        if (requiredAverageProofOfWorkNonceTrialsPerByte > shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte') and shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte') != 0) or (requiredPayloadLengthExtraBytes > shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') and shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') != 0):
                            # The demanded difficulty is more than we are willing
                            # to do.
                            sqlExecute(
                                '''UPDATE sent SET status='toodifficult' WHERE ackdata=? ''',
                                ackdata)
                            continue
            else: # if we are sending a message to ourselves or a chan..
                behaviorBitfield = '\x00\x00\x00\x01'

                try:
                    privEncryptionKeyBase58 = shared.config.get(
                        toaddress, 'privencryptionkey')
                except Exception as err:
                    continue
                privEncryptionKeyHex = shared.decodeWalletImportFormat(
                    privEncryptionKeyBase58).encode('hex')
                pubEncryptionKeyBase256 = highlevelcrypto.privToPub(
                    privEncryptionKeyHex).decode('hex')[1:]
                requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
                requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
            # Now we can start to assemble our message.
            payload = encodeVarint(fromAddressVersionNumber)
            payload += encodeVarint(fromStreamNumber)
            payload += '\x00\x00\x00\x01'  # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features  )

            # We need to convert our private keys to public keys in order
            # to include them.
            try:
                privSigningKeyBase58 = shared.config.get(
                    fromaddress, 'privsigningkey')
                privEncryptionKeyBase58 = shared.config.get(
                    fromaddress, 'privencryptionkey')
            except:
                continue

            privSigningKeyHex = shared.decodeWalletImportFormat(
                privSigningKeyBase58).encode('hex')
            privEncryptionKeyHex = shared.decodeWalletImportFormat(
                privEncryptionKeyBase58).encode('hex')

            pubSigningKey = highlevelcrypto.privToPub(
                privSigningKeyHex).decode('hex')
            pubEncryptionKey = highlevelcrypto.privToPub(
                privEncryptionKeyHex).decode('hex')

            payload += pubSigningKey[
                1:]  # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
            payload += pubEncryptionKey[1:]

            if fromAddressVersionNumber >= 3:
                # If the receiver of our message is in our address book,
                # subscriptions list, or whitelist then we will allow them to
                # do the network-minimum proof of work. Let us check to see if
                # the receiver is in any of those lists.
                payload += encodeVarint(shared.config.getint(
                    fromaddress, 'noncetrialsperbyte'))
                payload += encodeVarint(shared.config.getint(
                    fromaddress, 'payloadlengthextrabytes'))

            payload += toRipe  # This hash will be checked by the receiver of the message to verify that toRipe belongs to them. This prevents a Surreptitious Forwarding Attack.
            payload += '\x02'  # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
            messageToTransmit = 'Subject:' + subject + '\n' 
            messageToTransmit+= 'Body:' + message
            payload += encodeVarint(len(messageToTransmit))
            payload += messageToTransmit
            if shared.config.has_section(toaddress):
                fullAckPayload = ''
            elif not shared.isBitSetWithinBitfield(behaviorBitfield,31):
                fullAckPayload = ''                    
            else:
                fullAckPayload = self.generateFullAckMessage(
                    ackdata, toStreamNumber, TTL)  # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
            payload += encodeVarint(len(fullAckPayload))
            payload += fullAckPayload
            dataToSign = pack('>Q', embeddedTime) + '\x00\x00\x00\x02' + encodeVarint(1) + encodeVarint(toStreamNumber) + payload 
            signature = highlevelcrypto.sign(dataToSign, privSigningKeyHex)
            payload += encodeVarint(len(signature))
            payload += signature

            # We have assembled the data that will be encrypted.
            try:
                encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex'))
            except:
                sqlExecute('''UPDATE sent SET status='badkey' WHERE ackdata=?''', ackdata)
                continue
            
            encryptedPayload = pack('>Q', embeddedTime)
            encryptedPayload += '\x00\x00\x00\x02' # object type: msg
            encryptedPayload += encodeVarint(1) # msg version
            encryptedPayload += encodeVarint(toStreamNumber) + encrypted
            target = 2 ** 64 / (requiredAverageProofOfWorkNonceTrialsPerByte*(len(encryptedPayload) + 8 + requiredPayloadLengthExtraBytes + ((TTL*(len(encryptedPayload)+8+requiredPayloadLengthExtraBytes))/(2 ** 16))))
            powStartTime = time.time()
            initialHash = hashlib.sha512(encryptedPayload).digest()
            trialValue, nonce = proofofwork.run(target, initialHash)

            encryptedPayload = pack('>Q', nonce) + encryptedPayload
            
            # Sanity check. The encryptedPayload size should never be larger than 256 KiB. There should
            # be checks elsewhere in the code to not let the user try to send a message this large
            # until we implement message continuation. 
            if len(encryptedPayload) > 2 ** 18: # 256 KiB
                print "The payload is lager than 256KiB,So the loop will continue"
                continue

            inventoryHash = calculateInventoryHash(encryptedPayload)
            objectType = 2
            shared.inventory[inventoryHash] = (
                objectType, toStreamNumber, encryptedPayload, embeddedTime, '')
            shared.inventorySets[toStreamNumber].add(inventoryHash)
            shared.broadcastToSendDataQueues((
                toStreamNumber, 'advertiseobject', inventoryHash))

            # Update the sent message in the sent table with the necessary information.
            if shared.config.has_section(toaddress):
                newStatus = 'msgsentnoackexpected'
            else:
                newStatus = 'msgsent'
            if retryNumber == 0:
                sleepTill = int(time.time()) + TTL
            else:
                sleepTill = int(time.time()) + 28*24*60*60 * 2**retryNumber
            sqlExecute('''UPDATE sent SET msgid=?, status=?, retrynumber=?, sleeptill=?, lastactiontime=? WHERE ackdata=?''',
                       inventoryHash,
                       newStatus,
                       retryNumber+1,
                       sleepTill,
                       int(time.time()),
                       ackdata)

            # If we are sending to ourselves or a chan, let's put the message in our own inbox.
            if shared.config.has_section(toaddress):
                sigHash = hashlib.sha512(hashlib.sha512(signature).digest()).digest()[32:] # Used to detect and ignore duplicate messages in our inbox
                t = (inventoryHash, toaddress, fromaddress, subject, int(
                    time.time()), message, 'inbox', 2, 0, sigHash)
                shared.messages.append(t)
示例#3
0
    def sendMsg(self):
        # Check to see if there are any messages queued to be sent
        shared.sqlLock.acquire()
        shared.sqlSubmitQueue.put(
            '''SELECT DISTINCT toaddress FROM sent WHERE (status='msgqueued' AND folder='sent')''')
        shared.sqlSubmitQueue.put('')
        queryreturn = shared.sqlReturnQueue.get()
        shared.sqlLock.release()
        for row in queryreturn:  # For each address to which we need to send a message, check to see if we have its pubkey already.
            toaddress, = row
            toripe = decodeAddress(toaddress)[3]
            shared.sqlLock.acquire()
            shared.sqlSubmitQueue.put(
                '''SELECT hash FROM pubkeys WHERE hash=? ''')
            shared.sqlSubmitQueue.put((toripe,))
            queryreturn = shared.sqlReturnQueue.get()
            shared.sqlLock.release()
            if queryreturn != []:  # If we have the needed pubkey, set the status to doingmsgpow (we'll do it further down)
                t = (toaddress,)
                shared.sqlLock.acquire()
                shared.sqlSubmitQueue.put(
                    '''UPDATE sent SET status='doingmsgpow' WHERE toaddress=? AND status='msgqueued' ''')
                shared.sqlSubmitQueue.put(t)
                shared.sqlReturnQueue.get()
                shared.sqlSubmitQueue.put('commit')
                shared.sqlLock.release()
            else:  # We don't have the needed pubkey. Set the status to 'awaitingpubkey' and request it if we haven't already
                if toripe in shared.neededPubkeys:
                    # We already sent a request for the pubkey
                    t = (toaddress,)
                    shared.sqlLock.acquire()
                    shared.sqlSubmitQueue.put(
                        '''UPDATE sent SET status='awaitingpubkey' WHERE toaddress=? AND status='msgqueued' ''')
                    shared.sqlSubmitQueue.put(t)
                    shared.sqlReturnQueue.get()
                    shared.sqlSubmitQueue.put('commit')
                    shared.sqlLock.release()
                    shared.UISignalQueue.put(('updateSentItemStatusByHash', (
                        toripe, tr.translateText("MainWindow",'Encryption key was requested earlier.'))))
                else:
                    # We have not yet sent a request for the pubkey
                    t = (toaddress,)
                    shared.sqlLock.acquire()
                    shared.sqlSubmitQueue.put(
                        '''UPDATE sent SET status='doingpubkeypow' WHERE toaddress=? AND status='msgqueued' ''')
                    shared.sqlSubmitQueue.put(t)
                    shared.sqlReturnQueue.get()
                    shared.sqlSubmitQueue.put('commit')
                    shared.sqlLock.release()
                    shared.UISignalQueue.put(('updateSentItemStatusByHash', (
                        toripe, tr.translateText("MainWindow",'Sending a request for the recipient\'s encryption key.'))))
                    self.requestPubKey(toaddress)
        shared.sqlLock.acquire()
        # Get all messages that are ready to be sent, and also all messages
        # which we have sent in the last 28 days which were previously marked
        # as 'toodifficult'. If the user as raised the maximum acceptable
        # difficulty then those messages may now be sendable.
        shared.sqlSubmitQueue.put(
            '''SELECT toaddress, toripe, fromaddress, subject, message, ackdata, status FROM sent WHERE (status='doingmsgpow' or status='forcepow' or (status='toodifficult' and lastactiontime>?)) and folder='sent' ''')
        shared.sqlSubmitQueue.put((int(time.time()) - 2419200,))
        queryreturn = shared.sqlReturnQueue.get()
        shared.sqlLock.release()
        for row in queryreturn:  # For each message we need to send..
            toaddress, toripe, fromaddress, subject, message, ackdata, status = row
            # There is a remote possibility that we may no longer have the
            # recipient's pubkey. Let us make sure we still have it or else the
            # sendMsg function will appear to freeze. This can happen if the
            # user sends a message but doesn't let the POW function finish,
            # then leaves their client off for a long time which could cause
            # the needed pubkey to expire and be deleted.
            shared.sqlLock.acquire()
            shared.sqlSubmitQueue.put(
                '''SELECT hash FROM pubkeys WHERE hash=? ''')
            shared.sqlSubmitQueue.put((toripe,))
            queryreturn = shared.sqlReturnQueue.get()
            shared.sqlLock.release()
            if queryreturn == [] and toripe not in shared.neededPubkeys:
                # We no longer have the needed pubkey and we haven't requested
                # it.
                with shared.printLock:
                    sys.stderr.write(
                        'For some reason, the status of a message in our outbox is \'doingmsgpow\' even though we lack the pubkey. Here is the RIPE hash of the needed pubkey: %s\n' % toripe.encode('hex'))

                t = (toaddress,)
                shared.sqlLock.acquire()
                shared.sqlSubmitQueue.put(
                    '''UPDATE sent SET status='msgqueued' WHERE toaddress=? AND status='doingmsgpow' ''')
                shared.sqlSubmitQueue.put(t)
                shared.sqlReturnQueue.get()
                shared.sqlSubmitQueue.put('commit')
                shared.sqlLock.release()
                shared.UISignalQueue.put(('updateSentItemStatusByHash', (
                    toripe, tr.translateText("MainWindow",'Sending a request for the recipient\'s encryption key.'))))
                self.requestPubKey(toaddress)
                continue
            shared.ackdataForWhichImWatching[ackdata] = 0
            toStatus, toAddressVersionNumber, toStreamNumber, toHash = decodeAddress(
                toaddress)
            fromStatus, fromAddressVersionNumber, fromStreamNumber, fromHash = decodeAddress(
                fromaddress)
            shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                ackdata, tr.translateText("MainWindow", "Looking up the receiver\'s public key"))))
            with shared.printLock:
                print 'Found a message in our database that needs to be sent with this pubkey.'
                print 'First 150 characters of message:', repr(message[:150])


            # mark the pubkey as 'usedpersonally' so that we don't ever delete
            # it.
            shared.sqlLock.acquire()
            t = (toripe,)
            shared.sqlSubmitQueue.put(
                '''UPDATE pubkeys SET usedpersonally='yes' WHERE hash=?''')
            shared.sqlSubmitQueue.put(t)
            shared.sqlReturnQueue.get()
            shared.sqlSubmitQueue.put('commit')
            # Let us fetch the recipient's public key out of our database. If
            # the required proof of work difficulty is too hard then we'll
            # abort.
            shared.sqlSubmitQueue.put(
                'SELECT transmitdata FROM pubkeys WHERE hash=?')
            shared.sqlSubmitQueue.put((toripe,))
            queryreturn = shared.sqlReturnQueue.get()
            shared.sqlLock.release()
            if queryreturn == []:
                with shared.printLock:
                    sys.stderr.write(
                        '(within sendMsg) The needed pubkey was not found. This should never happen. Aborting send.\n')

                return
            for row in queryreturn:
                pubkeyPayload, = row

            # The pubkey message is stored the way we originally received it
            # which means that we need to read beyond things like the nonce and
            # time to get to the actual public keys.
            readPosition = 8  # to bypass the nonce
            pubkeyEmbeddedTime, = unpack(
                '>I', pubkeyPayload[readPosition:readPosition + 4])
            # This section is used for the transition from 32 bit time to 64
            # bit time in the protocol.
            if pubkeyEmbeddedTime == 0:
                pubkeyEmbeddedTime, = unpack(
                    '>Q', pubkeyPayload[readPosition:readPosition + 8])
                readPosition += 8
            else:
                readPosition += 4
            readPosition += 1  # to bypass the address version whose length is definitely 1
            streamNumber, streamNumberLength = decodeVarint(
                pubkeyPayload[readPosition:readPosition + 10])
            readPosition += streamNumberLength
            behaviorBitfield = pubkeyPayload[readPosition:readPosition + 4]
            # Mobile users may ask us to include their address's RIPE hash on a message
            # unencrypted. Before we actually do it the sending human must check a box
            # in the settings menu to allow it.
            if shared.isBitSetWithinBitfield(behaviorBitfield,30): # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message..
                if not shared.safeConfigGetBoolean('bitmessagesettings','willinglysendtomobile'): # if we are Not willing to include the receiver's RIPE hash on the message..
                    logger.info('The receiver is a mobile user but the sender (you) has not selected that you are willing to send to mobiles. Aborting send.')
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings.  %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))))
                    # if the human changes their setting and then sends another message or restarts their client, this one will send at that time.
                    continue
            readPosition += 4  # to bypass the bitfield of behaviors
            # pubSigningKeyBase256 =
            # pubkeyPayload[readPosition:readPosition+64] #We don't use this
            # key for anything here.
            readPosition += 64
            pubEncryptionKeyBase256 = pubkeyPayload[
                readPosition:readPosition + 64]
            readPosition += 64
            
            # Let us fetch the amount of work required by the recipient.
            if toAddressVersionNumber == 2:
                requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
                requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                    ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nThere is no required difficulty for version 2 addresses like this."))))
            elif toAddressVersionNumber == 3:
                requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(
                    pubkeyPayload[readPosition:readPosition + 10])
                readPosition += varintLength
                requiredPayloadLengthExtraBytes, varintLength = decodeVarint(
                    pubkeyPayload[readPosition:readPosition + 10])
                readPosition += varintLength
                if requiredAverageProofOfWorkNonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte:  # We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network.
                    requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
                if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes:
                    requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nReceiver\'s required difficulty: %1 and %2").arg(str(float(
                    requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(str(float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)))))
                if status != 'forcepow':
                    if (requiredAverageProofOfWorkNonceTrialsPerByte > shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte') and shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte') != 0) or (requiredPayloadLengthExtraBytes > shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') and shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') != 0):
                        # The demanded difficulty is more than we are willing
                        # to do.
                        shared.sqlLock.acquire()
                        t = (ackdata,)
                        shared.sqlSubmitQueue.put(
                            '''UPDATE sent SET status='toodifficult' WHERE ackdata=? ''')
                        shared.sqlSubmitQueue.put(t)
                        shared.sqlReturnQueue.get()
                        shared.sqlSubmitQueue.put('commit')
                        shared.sqlLock.release()
                        shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do.").arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(str(float(
                            requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
                        continue


            embeddedTime = pack('>Q', (int(time.time()) + random.randrange(
                -300, 300)))  # the current time plus or minus five minutes. We will use this time both for our message and for the ackdata packed within our message.
            if fromAddressVersionNumber == 2:
                payload = '\x01'  # Message version.
                payload += encodeVarint(fromAddressVersionNumber)
                payload += encodeVarint(fromStreamNumber)
                payload += '\x00\x00\x00\x01'  # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features  )

                # We need to convert our private keys to public keys in order
                # to include them.
                try:
                    privSigningKeyBase58 = shared.config.get(
                        fromaddress, 'privsigningkey')
                    privEncryptionKeyBase58 = shared.config.get(
                        fromaddress, 'privencryptionkey')
                except:
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                        ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file."))))
                    continue

                privSigningKeyHex = shared.decodeWalletImportFormat(
                    privSigningKeyBase58).encode('hex')
                privEncryptionKeyHex = shared.decodeWalletImportFormat(
                    privEncryptionKeyBase58).encode('hex')

                pubSigningKey = highlevelcrypto.privToPub(
                    privSigningKeyHex).decode('hex')
                pubEncryptionKey = highlevelcrypto.privToPub(
                    privEncryptionKeyHex).decode('hex')

                payload += pubSigningKey[
                    1:]  # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
                payload += pubEncryptionKey[1:]

                payload += toHash  # This hash will be checked by the receiver of the message to verify that toHash belongs to them. This prevents a Surreptitious Forwarding Attack.
                payload += '\x02'  # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
                messageToTransmit = 'Subject:' + \
                    subject + '\n' + 'Body:' + message
                payload += encodeVarint(len(messageToTransmit))
                payload += messageToTransmit
                fullAckPayload = self.generateFullAckMessage(
                    ackdata, toStreamNumber, embeddedTime)  # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
                payload += encodeVarint(len(fullAckPayload))
                payload += fullAckPayload
                signature = highlevelcrypto.sign(payload, privSigningKeyHex)
                payload += encodeVarint(len(signature))
                payload += signature

            if fromAddressVersionNumber == 3:
                payload = '\x01'  # Message version.
                payload += encodeVarint(fromAddressVersionNumber)
                payload += encodeVarint(fromStreamNumber)
                payload += '\x00\x00\x00\x01'  # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features  )

                # We need to convert our private keys to public keys in order
                # to include them.
                try:
                    privSigningKeyBase58 = shared.config.get(
                        fromaddress, 'privsigningkey')
                    privEncryptionKeyBase58 = shared.config.get(
                        fromaddress, 'privencryptionkey')
                except:
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                        ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file."))))
                    continue

                privSigningKeyHex = shared.decodeWalletImportFormat(
                    privSigningKeyBase58).encode('hex')
                privEncryptionKeyHex = shared.decodeWalletImportFormat(
                    privEncryptionKeyBase58).encode('hex')

                pubSigningKey = highlevelcrypto.privToPub(
                    privSigningKeyHex).decode('hex')
                pubEncryptionKey = highlevelcrypto.privToPub(
                    privEncryptionKeyHex).decode('hex')

                payload += pubSigningKey[
                    1:]  # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
                payload += pubEncryptionKey[1:]
                # If the receiver of our message is in our address book,
                # subscriptions list, or whitelist then we will allow them to
                # do the network-minimum proof of work. Let us check to see if
                # the receiver is in any of those lists.
                if shared.isAddressInMyAddressBookSubscriptionsListOrWhitelist(toaddress):
                    payload += encodeVarint(
                        shared.networkDefaultProofOfWorkNonceTrialsPerByte)
                    payload += encodeVarint(
                        shared.networkDefaultPayloadLengthExtraBytes)
                else:
                    payload += encodeVarint(shared.config.getint(
                        fromaddress, 'noncetrialsperbyte'))
                    payload += encodeVarint(shared.config.getint(
                        fromaddress, 'payloadlengthextrabytes'))

                payload += toHash  # This hash will be checked by the receiver of the message to verify that toHash belongs to them. This prevents a Surreptitious Forwarding Attack.
                payload += '\x02'  # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
                messageToTransmit = 'Subject:' + \
                    subject + '\n' + 'Body:' + message
                payload += encodeVarint(len(messageToTransmit))
                payload += messageToTransmit
                if shared.safeConfigGetBoolean(toaddress, 'chan'):
                    with shared.printLock:
                        print 'Not bothering to generate ackdata because we are sending to a chan.'
                    fullAckPayload = ''
                elif not shared.isBitSetWithinBitfield(behaviorBitfield,31):
                    with shared.printLock:
                        print 'Not bothering to generate ackdata because the receiver said that they won\'t relay it anyway.'
                    fullAckPayload = ''                    
                else:
                    fullAckPayload = self.generateFullAckMessage(
                        ackdata, toStreamNumber, embeddedTime)  # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
                payload += encodeVarint(len(fullAckPayload))
                payload += fullAckPayload
                signature = highlevelcrypto.sign(payload, privSigningKeyHex)
                payload += encodeVarint(len(signature))
                payload += signature


            # We have assembled the data that will be encrypted.
            try:
                encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex'))
            except:
                shared.sqlLock.acquire()
                t = (ackdata,)
                shared.sqlSubmitQueue.put('''UPDATE sent SET status='badkey' WHERE ackdata=?''')
                shared.sqlSubmitQueue.put(t)
                queryreturn = shared.sqlReturnQueue.get()
                shared.sqlSubmitQueue.put('commit')
                shared.sqlLock.release()
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))))
                continue
            encryptedPayload = embeddedTime + encodeVarint(toStreamNumber) + encrypted
            target = 2**64 / ((len(encryptedPayload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte)
            with shared.printLock:
                print '(For msg message) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes

            powStartTime = time.time()
            initialHash = hashlib.sha512(encryptedPayload).digest()
            trialValue, nonce = proofofwork.run(target, initialHash)
            with shared.printLock:
                print '(For msg message) Found proof of work', trialValue, 'Nonce:', nonce
                try:
                    print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.'
                except:
                    pass

            encryptedPayload = pack('>Q', nonce) + encryptedPayload

            inventoryHash = calculateInventoryHash(encryptedPayload)
            objectType = 'msg'
            shared.inventory[inventoryHash] = (
                objectType, toStreamNumber, encryptedPayload, int(time.time()))
            if shared.safeConfigGetBoolean(toaddress, 'chan'):
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Sent on %1").arg(unicode(
                    strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
            else:
                # not sending to a chan
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Waiting on acknowledgement. Sent on %1").arg(unicode(
                    strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
            print 'Broadcasting inv for my msg(within sendmsg function):', inventoryHash.encode('hex')
            shared.broadcastToSendDataQueues((
                streamNumber, 'sendinv', inventoryHash))

            # Update the status of the message in the 'sent' table to have a
            # 'msgsent' status or 'msgsentnoackexpected' status.
            if shared.safeConfigGetBoolean(toaddress, 'chan'):
                newStatus = 'msgsentnoackexpected'
            else:
                newStatus = 'msgsent'
            shared.sqlLock.acquire()
            t = (inventoryHash,newStatus,ackdata,)
            shared.sqlSubmitQueue.put('''UPDATE sent SET msgid=?, status=? WHERE ackdata=?''')
            shared.sqlSubmitQueue.put(t)
            queryreturn = shared.sqlReturnQueue.get()
            shared.sqlSubmitQueue.put('commit')
            shared.sqlLock.release()
示例#4
0
def decode_pubkey_payload(pubkeyPayload, addressVersion):
    """
    Returns a tuple ( pubEncryptionKey, pubsigningKey,
                      requiredAverageProofOfWorkNonceTrialsPerByte,
                      requiredPayloadLengthExtraBytes,
                      behaviorBitfield )
    by decoding the payload of a pubkey message.
    Can also return "mobile-user-disallowed"
    
    The keys are in binary format (base 256)
    """
    requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
    requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
    if addressVersion <= 3:
        readPosition = 8  # to bypass the nonce
    elif addressVersion >= 4:
        readPosition = 0  # the nonce is not included here so we don't need to skip over it.
    pubkeyEmbeddedTime, = struct.unpack(
        '>I', pubkeyPayload[readPosition:readPosition + 4])
    # This section is used for the transition from 32 bit time to 64
    # bit time in the protocol.
    if pubkeyEmbeddedTime == 0:
        pubkeyEmbeddedTime, = struct.unpack(
            '>Q', pubkeyPayload[readPosition:readPosition + 8])
        readPosition += 8
    else:
        readPosition += 4
    readPosition += 1  # to bypass the address version whose length is definitely 1
    _, streamNumberLength = shared.decodeVarint(
        pubkeyPayload[readPosition:readPosition + 10])
    readPosition += streamNumberLength
    behaviorBitfield = pubkeyPayload[readPosition:readPosition + 4]
    # Mobile users may ask us to include their address's RIPE hash on a message
    # unencrypted. Before we actually do it the sending human must check a box
    # in the settings menu to allow it.
    if shared.isBitSetWithinBitfield(
            behaviorBitfield, 30
    ):  # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message..
        if not shared.safeConfigGetBoolean(
                'bitmessagesettings', 'willinglysendtomobile'
        ):  # if we are Not willing to include the receiver's RIPE hash on the message..
            #                logger.info('The receiver is a mobile user but the sender (you) has not selected that you are willing to send to mobiles. Aborting send.')
            #                shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings.  %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))))
            # if the human changes their setting and then sends another message or restarts their client, this one will send at that time.
            return "mobile-user-disallowed"
    readPosition += 4  # to bypass the bitfield of behaviors
    pubSigningKeyBase256 = pubkeyPayload[readPosition:readPosition + 64]
    readPosition += 64
    pubEncryptionKeyBase256 = pubkeyPayload[readPosition:readPosition + 64]
    readPosition += 64

    requiredAverageProofOfWorkNonceTrialsPerByte

    # Let us fetch the amount of work required by the recipient.
    if addressVersion >= 3:
        requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = shared.decodeVarint(
            pubkeyPayload[readPosition:readPosition + 10])
        readPosition += varintLength
        requiredPayloadLengthExtraBytes, varintLength = shared.decodeVarint(
            pubkeyPayload[readPosition:readPosition + 10])
        readPosition += varintLength
        if requiredAverageProofOfWorkNonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte:  # We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network.
            requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
        if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes:
            requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
    return (pubEncryptionKeyBase256, pubSigningKeyBase256,
            requiredAverageProofOfWorkNonceTrialsPerByte,
            requiredPayloadLengthExtraBytes, behaviorBitfield)
示例#5
0
    def sendMsg(self):
        # Check to see if there are any messages queued to be sent
        queryreturn = sqlQuery(
            '''SELECT DISTINCT toaddress FROM sent WHERE (status='msgqueued' AND folder='sent')''')
        for row in queryreturn:  # For each address to which we need to send a message, check to see if we have its pubkey already.
            toaddress, = row
            toripe = decodeAddress(toaddress)[3]
            queryreturn = sqlQuery(
                '''SELECT hash FROM pubkeys WHERE hash=? ''', toripe)
            if queryreturn != []:  # If we have the needed pubkey, set the status to doingmsgpow (we'll do it further down)
                sqlExecute(
                    '''UPDATE sent SET status='doingmsgpow' WHERE toaddress=? AND status='msgqueued' ''',
                    toaddress)
            else:  # We don't have the needed pubkey. Set the status to 'awaitingpubkey' and request it if we haven't already
                if toripe in shared.neededPubkeys:
                    # We already sent a request for the pubkey
                    sqlExecute(
                        '''UPDATE sent SET status='awaitingpubkey' WHERE toaddress=? AND status='msgqueued' ''', toaddress)
                    shared.UISignalQueue.put(('updateSentItemStatusByHash', (
                        toripe, tr.translateText("MainWindow",'Encryption key was requested earlier.'))))
                else:
                    # We have not yet sent a request for the pubkey
                    sqlExecute(
                        '''UPDATE sent SET status='doingpubkeypow' WHERE toaddress=? AND status='msgqueued' ''',
                        toaddress)
                    shared.UISignalQueue.put(('updateSentItemStatusByHash', (
                        toripe, tr.translateText("MainWindow",'Sending a request for the recipient\'s encryption key.'))))
                    self.requestPubKey(toaddress)
        # Get all messages that are ready to be sent, and also all messages
        # which we have sent in the last 28 days which were previously marked
        # as 'toodifficult'. If the user as raised the maximum acceptable
        # difficulty then those messages may now be sendable.
        queryreturn = sqlQuery(
            '''SELECT toaddress, toripe, fromaddress, subject, message, ackdata, status FROM sent WHERE (status='doingmsgpow' or status='forcepow' or (status='toodifficult' and lastactiontime>?)) and folder='sent' ''',
            int(time.time()) - 2419200)
        for row in queryreturn:  # For each message we need to send..
            toaddress, toripe, fromaddress, subject, message, ackdata, status = row
            # There is a remote possibility that we may no longer have the
            # recipient's pubkey. Let us make sure we still have it or else the
            # sendMsg function will appear to freeze. This can happen if the
            # user sends a message but doesn't let the POW function finish,
            # then leaves their client off for a long time which could cause
            # the needed pubkey to expire and be deleted.
            queryreturn = sqlQuery(
                '''SELECT hash FROM pubkeys WHERE hash=? ''',
                toripe)
            if queryreturn == [] and toripe not in shared.neededPubkeys:
                # We no longer have the needed pubkey and we haven't requested
                # it.
                with shared.printLock:
                    sys.stderr.write(
                        'For some reason, the status of a message in our outbox is \'doingmsgpow\' even though we lack the pubkey. Here is the RIPE hash of the needed pubkey: %s\n' % toripe.encode('hex'))

                sqlExecute(
                    '''UPDATE sent SET status='msgqueued' WHERE toaddress=? AND status='doingmsgpow' ''', toaddress)
                shared.UISignalQueue.put(('updateSentItemStatusByHash', (
                    toripe, tr.translateText("MainWindow",'Sending a request for the recipient\'s encryption key.'))))
                self.requestPubKey(toaddress)
                continue
            shared.ackdataForWhichImWatching[ackdata] = 0
            toStatus, toAddressVersionNumber, toStreamNumber, toHash = decodeAddress(
                toaddress)
            fromStatus, fromAddressVersionNumber, fromStreamNumber, fromHash = decodeAddress(
                fromaddress)
            shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                ackdata, tr.translateText("MainWindow", "Looking up the receiver\'s public key"))))
            with shared.printLock:
                print 'Found a message in our database that needs to be sent with this pubkey.'
                print 'First 150 characters of message:', repr(message[:150])


            # mark the pubkey as 'usedpersonally' so that we don't ever delete
            # it.
            sqlExecute(
                '''UPDATE pubkeys SET usedpersonally='yes' WHERE hash=?''',
                toripe)
            # Let us fetch the recipient's public key out of our database. If
            # the required proof of work difficulty is too hard then we'll
            # abort.
            queryreturn = sqlQuery(
                'SELECT transmitdata FROM pubkeys WHERE hash=?',
                toripe)
            if queryreturn == []:
                with shared.printLock:
                    sys.stderr.write(
                        '(within sendMsg) The needed pubkey was not found. This should never happen. Aborting send.\n')

                return
            for row in queryreturn:
                pubkeyPayload, = row

            # The pubkey message is stored the way we originally received it
            # which means that we need to read beyond things like the nonce and
            # time to get to the actual public keys.
            readPosition = 8  # to bypass the nonce
            pubkeyEmbeddedTime, = unpack(
                '>I', pubkeyPayload[readPosition:readPosition + 4])
            # This section is used for the transition from 32 bit time to 64
            # bit time in the protocol.
            if pubkeyEmbeddedTime == 0:
                pubkeyEmbeddedTime, = unpack(
                    '>Q', pubkeyPayload[readPosition:readPosition + 8])
                readPosition += 8
            else:
                readPosition += 4
            readPosition += 1  # to bypass the address version whose length is definitely 1
            streamNumber, streamNumberLength = decodeVarint(
                pubkeyPayload[readPosition:readPosition + 10])
            readPosition += streamNumberLength
            behaviorBitfield = pubkeyPayload[readPosition:readPosition + 4]
            # Mobile users may ask us to include their address's RIPE hash on a message
            # unencrypted. Before we actually do it the sending human must check a box
            # in the settings menu to allow it.
            if shared.isBitSetWithinBitfield(behaviorBitfield,30): # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message..
                if not shared.safeConfigGetBoolean('bitmessagesettings','willinglysendtomobile'): # if we are Not willing to include the receiver's RIPE hash on the message..
                    logger.info('The receiver is a mobile user but the sender (you) has not selected that you are willing to send to mobiles. Aborting send.')
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings.  %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))))
                    # if the human changes their setting and then sends another message or restarts their client, this one will send at that time.
                    continue
            readPosition += 4  # to bypass the bitfield of behaviors
            # pubSigningKeyBase256 =
            # pubkeyPayload[readPosition:readPosition+64] #We don't use this
            # key for anything here.
            readPosition += 64
            pubEncryptionKeyBase256 = pubkeyPayload[
                readPosition:readPosition + 64]
            readPosition += 64
            
            # Let us fetch the amount of work required by the recipient.
            if toAddressVersionNumber == 2:
                requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
                requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                    ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nThere is no required difficulty for version 2 addresses like this."))))
            elif toAddressVersionNumber == 3:
                requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(
                    pubkeyPayload[readPosition:readPosition + 10])
                readPosition += varintLength
                requiredPayloadLengthExtraBytes, varintLength = decodeVarint(
                    pubkeyPayload[readPosition:readPosition + 10])
                readPosition += varintLength
                if requiredAverageProofOfWorkNonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte:  # We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network.
                    requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
                if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes:
                    requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Doing work necessary to send message.\nReceiver\'s required difficulty: %1 and %2").arg(str(float(
                    requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(str(float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)))))
                if status != 'forcepow':
                    if (requiredAverageProofOfWorkNonceTrialsPerByte > shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte') and shared.config.getint('bitmessagesettings', 'maxacceptablenoncetrialsperbyte') != 0) or (requiredPayloadLengthExtraBytes > shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') and shared.config.getint('bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') != 0):
                        # The demanded difficulty is more than we are willing
                        # to do.
                        sqlExecute(
                            '''UPDATE sent SET status='toodifficult' WHERE ackdata=? ''',
                            ackdata)
                        shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do.").arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(str(float(
                            requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes)).arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
                        continue


            embeddedTime = pack('>Q', (int(time.time()) + random.randrange(
                -300, 300)))  # the current time plus or minus five minutes. We will use this time both for our message and for the ackdata packed within our message.
            if fromAddressVersionNumber == 2:
                payload = '\x01'  # Message version.
                payload += encodeVarint(fromAddressVersionNumber)
                payload += encodeVarint(fromStreamNumber)
                payload += '\x00\x00\x00\x01'  # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features  )

                # We need to convert our private keys to public keys in order
                # to include them.
                try:
                    privSigningKeyBase58 = shared.config.get(
                        fromaddress, 'privsigningkey')
                    privEncryptionKeyBase58 = shared.config.get(
                        fromaddress, 'privencryptionkey')
                except:
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                        ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file."))))
                    continue

                privSigningKeyHex = shared.decodeWalletImportFormat(
                    privSigningKeyBase58).encode('hex')
                privEncryptionKeyHex = shared.decodeWalletImportFormat(
                    privEncryptionKeyBase58).encode('hex')

                pubSigningKey = highlevelcrypto.privToPub(
                    privSigningKeyHex).decode('hex')
                pubEncryptionKey = highlevelcrypto.privToPub(
                    privEncryptionKeyHex).decode('hex')

                payload += pubSigningKey[
                    1:]  # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
                payload += pubEncryptionKey[1:]

                payload += toHash  # This hash will be checked by the receiver of the message to verify that toHash belongs to them. This prevents a Surreptitious Forwarding Attack.
                payload += '\x02'  # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
                messageToTransmit = 'Subject:' + \
                    subject + '\n' + 'Body:' + message
                payload += encodeVarint(len(messageToTransmit))
                payload += messageToTransmit
                fullAckPayload = self.generateFullAckMessage(
                    ackdata, toStreamNumber, embeddedTime)  # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
                payload += encodeVarint(len(fullAckPayload))
                payload += fullAckPayload
                signature = highlevelcrypto.sign(payload, privSigningKeyHex)
                payload += encodeVarint(len(signature))
                payload += signature

            if fromAddressVersionNumber == 3:
                payload = '\x01'  # Message version.
                payload += encodeVarint(fromAddressVersionNumber)
                payload += encodeVarint(fromStreamNumber)
                payload += '\x00\x00\x00\x01'  # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features  )

                # We need to convert our private keys to public keys in order
                # to include them.
                try:
                    privSigningKeyBase58 = shared.config.get(
                        fromaddress, 'privsigningkey')
                    privEncryptionKeyBase58 = shared.config.get(
                        fromaddress, 'privencryptionkey')
                except:
                    shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
                        ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file."))))
                    continue

                privSigningKeyHex = shared.decodeWalletImportFormat(
                    privSigningKeyBase58).encode('hex')
                privEncryptionKeyHex = shared.decodeWalletImportFormat(
                    privEncryptionKeyBase58).encode('hex')

                pubSigningKey = highlevelcrypto.privToPub(
                    privSigningKeyHex).decode('hex')
                pubEncryptionKey = highlevelcrypto.privToPub(
                    privEncryptionKeyHex).decode('hex')

                payload += pubSigningKey[
                    1:]  # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
                payload += pubEncryptionKey[1:]
                # If the receiver of our message is in our address book,
                # subscriptions list, or whitelist then we will allow them to
                # do the network-minimum proof of work. Let us check to see if
                # the receiver is in any of those lists.
                if shared.isAddressInMyAddressBookSubscriptionsListOrWhitelist(toaddress):
                    payload += encodeVarint(
                        shared.networkDefaultProofOfWorkNonceTrialsPerByte)
                    payload += encodeVarint(
                        shared.networkDefaultPayloadLengthExtraBytes)
                else:
                    payload += encodeVarint(shared.config.getint(
                        fromaddress, 'noncetrialsperbyte'))
                    payload += encodeVarint(shared.config.getint(
                        fromaddress, 'payloadlengthextrabytes'))

                payload += toHash  # This hash will be checked by the receiver of the message to verify that toHash belongs to them. This prevents a Surreptitious Forwarding Attack.
                payload += '\x02'  # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
                messageToTransmit = 'Subject:' + \
                    subject + '\n' + 'Body:' + message
                payload += encodeVarint(len(messageToTransmit))
                payload += messageToTransmit
                if shared.safeConfigGetBoolean(toaddress, 'chan'):
                    with shared.printLock:
                        print 'Not bothering to generate ackdata because we are sending to a chan.'
                    fullAckPayload = ''
                elif not shared.isBitSetWithinBitfield(behaviorBitfield,31):
                    with shared.printLock:
                        print 'Not bothering to generate ackdata because the receiver said that they won\'t relay it anyway.'
                    fullAckPayload = ''                    
                else:
                    fullAckPayload = self.generateFullAckMessage(
                        ackdata, toStreamNumber, embeddedTime)  # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
                payload += encodeVarint(len(fullAckPayload))
                payload += fullAckPayload
                signature = highlevelcrypto.sign(payload, privSigningKeyHex)
                payload += encodeVarint(len(signature))
                payload += signature


            # We have assembled the data that will be encrypted.
            try:
                encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex'))
            except:
                sqlExecute('''UPDATE sent SET status='badkey' WHERE ackdata=?''', ackdata)
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: The recipient\'s encryption key is no good. Could not encrypt message. %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))))
                continue
            encryptedPayload = embeddedTime + encodeVarint(toStreamNumber) + encrypted
            target = 2**64 / ((len(encryptedPayload)+requiredPayloadLengthExtraBytes+8) * requiredAverageProofOfWorkNonceTrialsPerByte)
            with shared.printLock:
                print '(For msg message) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / shared.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / shared.networkDefaultPayloadLengthExtraBytes

            powStartTime = time.time()
            initialHash = hashlib.sha512(encryptedPayload).digest()
            trialValue, nonce = proofofwork.run(target, initialHash)
            with shared.printLock:
                print '(For msg message) Found proof of work', trialValue, 'Nonce:', nonce
                try:
                    print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.'
                except:
                    pass

            encryptedPayload = pack('>Q', nonce) + encryptedPayload

            inventoryHash = calculateInventoryHash(encryptedPayload)
            objectType = 'msg'
            shared.inventory[inventoryHash] = (
                objectType, toStreamNumber, encryptedPayload, int(time.time()))
            if shared.safeConfigGetBoolean(toaddress, 'chan'):
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Sent on %1").arg(unicode(
                    strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
            else:
                # not sending to a chan
                shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Message sent. Waiting on acknowledgement. Sent on %1").arg(unicode(
                    strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8')))))
            print 'Broadcasting inv for my msg(within sendmsg function):', inventoryHash.encode('hex')
            shared.broadcastToSendDataQueues((
                streamNumber, 'sendinv', inventoryHash))

            # Update the status of the message in the 'sent' table to have a
            # 'msgsent' status or 'msgsentnoackexpected' status.
            if shared.safeConfigGetBoolean(toaddress, 'chan'):
                newStatus = 'msgsentnoackexpected'
            else:
                newStatus = 'msgsent'
            sqlExecute('''UPDATE sent SET msgid=?, status=? WHERE ackdata=?''',
                       inventoryHash,newStatus,ackdata)
def decode_pubkey_payload(pubkeyPayload, addressVersion):
    """
    Returns a tuple ( pubEncryptionKey, pubsigningKey,
                      requiredAverageProofOfWorkNonceTrialsPerByte,
                      requiredPayloadLengthExtraBytes,
                      behaviorBitfield )
    by decoding the payload of a pubkey message.
    Can also return "mobile-user-disallowed"
    
    The keys are in binary format (base 256)
    """
    requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
    requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
    if addressVersion <= 3:
        readPosition = 8  # to bypass the nonce
    elif addressVersion >= 4:
        readPosition = 0 # the nonce is not included here so we don't need to skip over it.
    pubkeyEmbeddedTime, = struct.unpack(
        '>I', pubkeyPayload[readPosition:readPosition + 4])
    # This section is used for the transition from 32 bit time to 64
    # bit time in the protocol.
    if pubkeyEmbeddedTime == 0:
        pubkeyEmbeddedTime, = struct.unpack(
            '>Q', pubkeyPayload[readPosition:readPosition + 8])
        readPosition += 8
    else:
        readPosition += 4
    readPosition += 1  # to bypass the address version whose length is definitely 1
    _, streamNumberLength = shared.decodeVarint(
        pubkeyPayload[readPosition:readPosition + 10])
    readPosition += streamNumberLength
    behaviorBitfield = pubkeyPayload[readPosition:readPosition + 4]
    # Mobile users may ask us to include their address's RIPE hash on a message
    # unencrypted. Before we actually do it the sending human must check a box
    # in the settings menu to allow it.
    if shared.isBitSetWithinBitfield(behaviorBitfield,30): # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message..
        if not shared.safeConfigGetBoolean('bitmessagesettings','willinglysendtomobile'): # if we are Not willing to include the receiver's RIPE hash on the message..
#                logger.info('The receiver is a mobile user but the sender (you) has not selected that you are willing to send to mobiles. Aborting send.')
#                shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings.  %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8')))))
            # if the human changes their setting and then sends another message or restarts their client, this one will send at that time.
            return "mobile-user-disallowed"
    readPosition += 4  # to bypass the bitfield of behaviors
    pubSigningKeyBase256 = pubkeyPayload[readPosition:readPosition+64]
    readPosition += 64
    pubEncryptionKeyBase256 = pubkeyPayload[readPosition:readPosition+64]
    readPosition += 64
    
    requiredAverageProofOfWorkNonceTrialsPerByte
    
    # Let us fetch the amount of work required by the recipient.
    if addressVersion >= 3:
        requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = shared.decodeVarint(
            pubkeyPayload[readPosition:readPosition + 10])
        readPosition += varintLength
        requiredPayloadLengthExtraBytes, varintLength = shared.decodeVarint(
            pubkeyPayload[readPosition:readPosition + 10])
        readPosition += varintLength
        if requiredAverageProofOfWorkNonceTrialsPerByte < shared.networkDefaultProofOfWorkNonceTrialsPerByte:  # We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network.
            requiredAverageProofOfWorkNonceTrialsPerByte = shared.networkDefaultProofOfWorkNonceTrialsPerByte
        if requiredPayloadLengthExtraBytes < shared.networkDefaultPayloadLengthExtraBytes:
            requiredPayloadLengthExtraBytes = shared.networkDefaultPayloadLengthExtraBytes
    return ( pubEncryptionKeyBase256, pubSigningKeyBase256,
             requiredAverageProofOfWorkNonceTrialsPerByte,
             requiredPayloadLengthExtraBytes,
             behaviorBitfield )