/ src / class_singleWorker.py
class_singleWorker.py
   1  """
   2  Thread for performing PoW
   3  """
   4  # pylint: disable=protected-access,too-many-branches,too-many-statements
   5  # pylint: disable=no-self-use,too-many-lines,too-many-locals
   6  
   7  
   8  
   9  import hashlib
  10  import time
  11  from binascii import hexlify, unhexlify
  12  from struct import pack
  13  from subprocess import call  # nosec
  14  
  15  from six.moves import configparser, queue
  16  from six.moves.reprlib import repr
  17  
  18  from . import defaults
  19  from . import helper_inbox
  20  from . import helper_msgcoding
  21  from . import helper_random
  22  from . import helper_sql
  23  from . import highlevelcrypto
  24  from . import l10n
  25  from . import proofofwork
  26  from . import protocol
  27  from . import queues
  28  from . import shared
  29  from . import state
  30  from . import tr
  31  from .addresses import decodeAddress, decodeVarint, encodeVarint
  32  from .bmconfigparser import config
  33  from .helper_sql import sqlExecute, sqlQuery
  34  from .network import StoppableThread, invQueue, knownnodes
  35  
  36  
  37  def sizeof_fmt(num, suffix='h/s'):
  38      """Format hashes per seconds nicely (SI prefix)"""
  39  
  40      for unit in ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z']:
  41          if abs(num) < 1000.0:
  42              return "%3.1f%s%s" % (num, unit, suffix)
  43          num /= 1024.0
  44      return "%.1f%s%s" % (num, 'Yi', suffix)
  45  
  46  
  47  class singleWorker(StoppableThread):
  48      """Thread for performing PoW"""
  49  
  50      def __init__(self):
  51          super(singleWorker, self).__init__(name="singleWorker")
  52          self.digestAlg = config.safeGet(
  53              'bitmessagesettings', 'digestalg', 'sha256')
  54          proofofwork.init()
  55  
  56      def stopThread(self):
  57          """Signal through the queue that the thread should be stopped"""
  58  
  59          try:
  60              queues.workerQueue.put(("stopThread", "data"))
  61          except queue.Full:
  62              self.logger.error('workerQueue is Full')
  63          super(singleWorker, self).stopThread()
  64  
  65      def run(self):
  66          # pylint: disable=attribute-defined-outside-init
  67  
  68          while not helper_sql.sql_ready.wait(1.0) and state.shutdown == 0:
  69              self.stop.wait(1.0)
  70          if state.shutdown > 0:
  71              return
  72  
  73          # Initialize the neededPubkeys dictionary.
  74          queryreturn = sqlQuery(
  75              '''SELECT DISTINCT toaddress FROM sent'''
  76              ''' WHERE (status='awaitingpubkey' AND folder='sent')''')
  77          for toAddress, in queryreturn:
  78              toAddressVersionNumber, toStreamNumber, toRipe = \
  79                  decodeAddress(toAddress)[1:]
  80              if toAddressVersionNumber <= 3:
  81                  state.neededPubkeys[toAddress] = 0
  82              elif toAddressVersionNumber >= 4:
  83                  doubleHashOfAddressData = highlevelcrypto.double_sha512(
  84                      encodeVarint(toAddressVersionNumber)
  85                      + encodeVarint(toStreamNumber) + toRipe
  86                  )
  87                  # Note that this is the first half of the sha512 hash.
  88                  privEncryptionKey = doubleHashOfAddressData[:32]
  89                  tag = doubleHashOfAddressData[32:]
  90                  # We'll need this for when we receive a pubkey reply:
  91                  # it will be encrypted and we'll need to decrypt it.
  92                  state.neededPubkeys[tag] = (
  93                      toAddress,
  94                      highlevelcrypto.makeCryptor(
  95                          hexlify(privEncryptionKey))
  96                  )
  97  
  98          # Initialize the state.ackdataForWhichImWatching data structure
  99          queryreturn = sqlQuery(
 100              '''SELECT ackdata FROM sent WHERE status = 'msgsent' AND folder = 'sent' ''')
 101          for row in queryreturn:
 102              ackdata, = row
 103              self.logger.info('Watching for ackdata %s', hexlify(ackdata))
 104              state.ackdataForWhichImWatching[ackdata] = 0
 105  
 106          # Fix legacy (headerless) watched ackdata to include header
 107          for oldack in state.ackdataForWhichImWatching:
 108              if len(oldack) == 32:
 109                  # attach legacy header, always constant (msg/1/1)
 110                  newack = '\x00\x00\x00\x02\x01\x01' + oldack
 111                  state.ackdataForWhichImWatching[newack] = 0
 112                  sqlExecute(
 113                      '''UPDATE sent SET ackdata=? WHERE ackdata=? AND folder = 'sent' ''',
 114                      newack, oldack
 115                  )
 116                  del state.ackdataForWhichImWatching[oldack]
 117  
 118          # For the case if user deleted knownnodes
 119          # but is still having onionpeer objects in inventory
 120          if not knownnodes.knownNodesActual:
 121              for item in state.Inventory.by_type_and_tag(protocol.OBJECT_ONIONPEER):
 122                  queues.objectProcessorQueue.put((
 123                      protocol.OBJECT_ONIONPEER, item.payload
 124                  ))
 125                  # FIXME: should also delete from inventory
 126  
 127          # give some time for the GUI to start
 128          # before we start on existing POW tasks.
 129          self.stop.wait(10)
 130  
 131          if state.shutdown:
 132              return
 133  
 134          # just in case there are any pending tasks for msg
 135          # messages that have yet to be sent.
 136          queues.workerQueue.put(('sendmessage', ''))
 137          # just in case there are any tasks for Broadcasts
 138          # that have yet to be sent.
 139          queues.workerQueue.put(('sendbroadcast', ''))
 140  
 141          # send onionpeer object
 142          queues.workerQueue.put(('sendOnionPeerObj', ''))
 143  
 144          while state.shutdown == 0:
 145              self.busy = 0
 146              command, data = queues.workerQueue.get()
 147              self.busy = 1
 148              if command == 'sendmessage':
 149                  try:
 150                      self.sendMsg()
 151                  except:  # noqa:E722
 152                      self.logger.warning("sendMsg didn't work")
 153              elif command == 'sendbroadcast':
 154                  try:
 155                      self.sendBroadcast()
 156                  except:  # noqa:E722
 157                      self.logger.warning("sendBroadcast didn't work")
 158              elif command == 'doPOWForMyV2Pubkey':
 159                  try:
 160                      self.doPOWForMyV2Pubkey(data)
 161                  except:  # noqa:E722
 162                      self.logger.warning("doPOWForMyV2Pubkey didn't work")
 163              elif command == 'sendOutOrStoreMyV3Pubkey':
 164                  try:
 165                      self.sendOutOrStoreMyV3Pubkey(data)
 166                  except:  # noqa:E722
 167                      self.logger.warning("sendOutOrStoreMyV3Pubkey didn't work")
 168              elif command == 'sendOutOrStoreMyV4Pubkey':
 169                  try:
 170                      self.sendOutOrStoreMyV4Pubkey(data)
 171                  except:  # noqa:E722
 172                      self.logger.warning("sendOutOrStoreMyV4Pubkey didn't work")
 173              elif command == 'sendOnionPeerObj':
 174                  try:
 175                      self.sendOnionPeerObj(data)
 176                  except:  # noqa:E722
 177                      self.logger.warning("sendOnionPeerObj didn't work")
 178              elif command == 'resetPoW':
 179                  try:
 180                      proofofwork.resetPoW()
 181                  except:  # noqa:E722
 182                      self.logger.warning("proofofwork.resetPoW didn't work")
 183              elif command == 'stopThread':
 184                  self.busy = 0
 185                  return
 186              else:
 187                  self.logger.error(
 188                      'Probable programming error: The command sent'
 189                      ' to the workerThread is weird. It is: %s\n',
 190                      command
 191                  )
 192  
 193              queues.workerQueue.task_done()
 194          self.logger.info("Quitting...")
 195  
 196      def _getKeysForAddress(self, address):
 197          try:
 198              privSigningKeyBase58 = config.get(address, 'privsigningkey')
 199              privEncryptionKeyBase58 = config.get(address, 'privencryptionkey')
 200          except (configparser.NoSectionError, configparser.NoOptionError):
 201              self.logger.error(
 202                  'Could not read or decode privkey for address %s', address)
 203              raise ValueError
 204  
 205          privSigningKeyHex = hexlify(highlevelcrypto.decodeWalletImportFormat(
 206              privSigningKeyBase58.encode()))
 207          privEncryptionKeyHex = hexlify(
 208              highlevelcrypto.decodeWalletImportFormat(
 209                  privEncryptionKeyBase58.encode()))
 210  
 211          # The \x04 on the beginning of the public keys are not sent.
 212          # This way there is only one acceptable way to encode
 213          # and send a public key.
 214          pubSigningKey = unhexlify(highlevelcrypto.privToPub(
 215              privSigningKeyHex))[1:]
 216          pubEncryptionKey = unhexlify(highlevelcrypto.privToPub(
 217              privEncryptionKeyHex))[1:]
 218  
 219          return privSigningKeyHex, privEncryptionKeyHex, \
 220              pubSigningKey, pubEncryptionKey
 221  
 222      @classmethod
 223      def _doPOWDefaults(
 224          cls, payload, TTL,
 225          nonceTrialsPerByte=None, payloadLengthExtraBytes=None,
 226          log_prefix='', log_time=False
 227      ):
 228          if not nonceTrialsPerByte:
 229              nonceTrialsPerByte = \
 230                  defaults.networkDefaultProofOfWorkNonceTrialsPerByte
 231          if not payloadLengthExtraBytes:
 232              payloadLengthExtraBytes = \
 233                  defaults.networkDefaultPayloadLengthExtraBytes
 234          cls.logger.info(
 235              '%s Doing proof of work... TTL set to %s', log_prefix, TTL)
 236          if log_time:
 237              start_time = time.time()
 238          trialValue, nonce = proofofwork.calculate(
 239              payload, TTL, nonceTrialsPerByte, payloadLengthExtraBytes)
 240          cls.logger.info(
 241              '%s Found proof of work %s Nonce: %s',
 242              log_prefix, trialValue, nonce
 243          )
 244          try:
 245              delta = time.time() - start_time
 246              cls.logger.info(
 247                  'PoW took %.1f seconds, speed %s.',
 248                  delta, sizeof_fmt(nonce / delta)
 249              )
 250          except NameError:  # no start_time - no logging
 251              pass
 252          payload = pack('>Q', nonce) + payload
 253          return payload
 254  
 255      def doPOWForMyV2Pubkey(self, adressHash):
 256          """ This function also broadcasts out the pubkey
 257          message once it is done with the POW"""
 258          # Look up my stream number based on my address hash
 259          myAddress = shared.myAddressesByHash[adressHash]
 260          addressVersionNumber, streamNumber = decodeAddress(myAddress)[1:3]
 261  
 262          # 28 days from now plus or minus five minutes
 263          TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
 264          embeddedTime = int(time.time() + TTL)
 265          payload = pack('>Q', (embeddedTime))
 266          payload += '\x00\x00\x00\x01'  # object type: pubkey
 267          payload += encodeVarint(addressVersionNumber)  # Address version number
 268          payload += encodeVarint(streamNumber)
 269          # bitfield of features supported by me (see the wiki).
 270          payload += protocol.getBitfield(myAddress)
 271  
 272          try:
 273              pubSigningKey, pubEncryptionKey = self._getKeysForAddress(
 274                  myAddress)[2:]
 275          except ValueError:
 276              return
 277          except Exception:  # pylint:disable=broad-exception-caught
 278              self.logger.error(
 279                  'Error within doPOWForMyV2Pubkey. Could not read'
 280                  ' the keys from the keys.dat file for a requested'
 281                  ' address. %s\n', exc_info=True)
 282              return
 283  
 284          payload += pubSigningKey + pubEncryptionKey
 285  
 286          # Do the POW for this pubkey message
 287          payload = self._doPOWDefaults(
 288              payload, TTL, log_prefix='(For pubkey message)')
 289  
 290          inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
 291          objectType = 1
 292          state.Inventory[inventoryHash] = (
 293              objectType, streamNumber, payload, embeddedTime, '')
 294  
 295          self.logger.info(
 296              'broadcasting inv with hash: %s', hexlify(inventoryHash))
 297  
 298          invQueue.put((streamNumber, inventoryHash))
 299          queues.UISignalQueue.put(('updateStatusBar', ''))
 300          try:
 301              config.set(
 302                  myAddress, 'lastpubkeysendtime', str(int(time.time())))
 303              config.save()
 304          except configparser.NoSectionError:
 305              # The user deleted the address out of the keys.dat file
 306              # before this finished.
 307              pass
 308          except:  # noqa:E722
 309              self.logger.warning("config.set didn't work")
 310  
 311      def sendOutOrStoreMyV3Pubkey(self, adressHash):
 312          """
 313          If this isn't a chan address, this function assembles the pubkey data, does the necessary POW and sends it out.
 314          If it *is* a chan then it assembles the pubkey and stores is in the pubkey table so that we can send messages
 315          to "ourselves".
 316          """
 317          try:
 318              myAddress = shared.myAddressesByHash[adressHash]
 319          except KeyError:
 320              self.logger.warning(  # The address has been deleted.
 321                  "Can't find %s in myAddressByHash", hexlify(adressHash))
 322              return
 323          if config.safeGetBoolean(myAddress, 'chan'):
 324              self.logger.info('This is a chan address. Not sending pubkey.')
 325              return
 326          _, addressVersionNumber, streamNumber, adressHash = decodeAddress(
 327              myAddress)
 328  
 329          # 28 days from now plus or minus five minutes
 330          TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
 331          embeddedTime = int(time.time() + TTL)
 332  
 333          # signedTimeForProtocolV2 = embeddedTime - TTL
 334          # According to the protocol specification, the expiresTime
 335          # along with the pubkey information is signed. But to be
 336          # backwards compatible during the upgrade period, we shall sign
 337          # not the expiresTime but rather the current time. There must be
 338          # precisely a 28 day difference between the two. After the upgrade
 339          # period we'll switch to signing the whole payload with the
 340          # expiresTime time.
 341  
 342          payload = pack('>Q', (embeddedTime))
 343          payload += '\x00\x00\x00\x01'  # object type: pubkey
 344          payload += encodeVarint(addressVersionNumber)  # Address version number
 345          payload += encodeVarint(streamNumber)
 346          # bitfield of features supported by me (see the wiki).
 347          payload += protocol.getBitfield(myAddress)
 348  
 349          try:
 350              # , privEncryptionKeyHex
 351              privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \
 352                  self._getKeysForAddress(myAddress)
 353          except ValueError:
 354              return
 355          except Exception:  # pylint:disable=broad-exception-caught
 356              self.logger.error(
 357                  'Error within sendOutOrStoreMyV3Pubkey. Could not read'
 358                  ' the keys from the keys.dat file for a requested'
 359                  ' address. %s\n', exc_info=True)
 360              return
 361  
 362          payload += pubSigningKey + pubEncryptionKey
 363  
 364          payload += encodeVarint(config.getint(
 365              myAddress, 'noncetrialsperbyte'))
 366          payload += encodeVarint(config.getint(
 367              myAddress, 'payloadlengthextrabytes'))
 368  
 369          signature = highlevelcrypto.sign(
 370              payload, privSigningKeyHex, self.digestAlg)
 371          payload += encodeVarint(len(signature))
 372          payload += signature
 373  
 374          # Do the POW for this pubkey message
 375          payload = self._doPOWDefaults(
 376              payload, TTL, log_prefix='(For pubkey message)')
 377  
 378          inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
 379          objectType = 1
 380          state.Inventory[inventoryHash] = (
 381              objectType, streamNumber, payload, embeddedTime, '')
 382  
 383          self.logger.info(
 384              'broadcasting inv with hash: %s', hexlify(inventoryHash))
 385  
 386          invQueue.put((streamNumber, inventoryHash))
 387          queues.UISignalQueue.put(('updateStatusBar', ''))
 388          try:
 389              config.set(
 390                  myAddress, 'lastpubkeysendtime', str(int(time.time())))
 391              config.save()
 392          except configparser.NoSectionError:
 393              # The user deleted the address out of the keys.dat file
 394              # before this finished.
 395              pass
 396          except:  # noqa:E722
 397              self.logger.warning("BMConfigParser().set didn't work")
 398  
 399      def sendOutOrStoreMyV4Pubkey(self, myAddress):
 400          """
 401          It doesn't send directly anymore. It put is to a queue for another thread to send at an appropriate time,
 402          whereas in the past it directly appended it to the outgoing buffer, I think. Same with all the other methods in
 403          this class.
 404          """
 405          if not config.has_section(myAddress):
 406              # The address has been deleted.
 407              return
 408          if config.safeGetBoolean(myAddress, 'chan'):
 409              self.logger.info('This is a chan address. Not sending pubkey.')
 410              return
 411          _, addressVersionNumber, streamNumber, addressHash = decodeAddress(
 412              myAddress)
 413  
 414          # 28 days from now plus or minus five minutes
 415          TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
 416          embeddedTime = int(time.time() + TTL)
 417          payload = pack('>Q', (embeddedTime))
 418          payload += '\x00\x00\x00\x01'  # object type: pubkey
 419          payload += encodeVarint(addressVersionNumber)  # Address version number
 420          payload += encodeVarint(streamNumber)
 421          dataToEncrypt = protocol.getBitfield(myAddress)
 422  
 423          try:
 424              # , privEncryptionKeyHex
 425              privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \
 426                  self._getKeysForAddress(myAddress)
 427          except ValueError:
 428              return
 429          except Exception:  # pylint:disable=broad-exception-caught
 430              self.logger.error(
 431                  'Error within sendOutOrStoreMyV4Pubkey. Could not read'
 432                  ' the keys from the keys.dat file for a requested'
 433                  ' address. %s\n', exc_info=True)
 434              return
 435  
 436          dataToEncrypt += pubSigningKey + pubEncryptionKey
 437  
 438          dataToEncrypt += encodeVarint(config.getint(
 439              myAddress, 'noncetrialsperbyte'))
 440          dataToEncrypt += encodeVarint(config.getint(
 441              myAddress, 'payloadlengthextrabytes'))
 442  
 443          # When we encrypt, we'll use a hash of the data
 444          # contained in an address as a decryption key. This way
 445          # in order to read the public keys in a pubkey message,
 446          # a node must know the address first. We'll also tag,
 447          # unencrypted, the pubkey with part of the hash so that nodes
 448          # know which pubkey object to try to decrypt
 449          # when they want to send a message.
 450          doubleHashOfAddressData = highlevelcrypto.double_sha512(
 451              encodeVarint(addressVersionNumber)
 452              + encodeVarint(streamNumber) + addressHash
 453          )
 454          payload += doubleHashOfAddressData[32:]  # the tag
 455          signature = highlevelcrypto.sign(
 456              payload + dataToEncrypt, privSigningKeyHex, self.digestAlg)
 457          dataToEncrypt += encodeVarint(len(signature))
 458          dataToEncrypt += signature
 459  
 460          privEncryptionKey = doubleHashOfAddressData[:32]
 461          pubEncryptionKey = highlevelcrypto.pointMult(privEncryptionKey)
 462          payload += highlevelcrypto.encrypt(
 463              dataToEncrypt, hexlify(pubEncryptionKey))
 464  
 465          # Do the POW for this pubkey message
 466          payload = self._doPOWDefaults(
 467              payload, TTL, log_prefix='(For pubkey message)')
 468  
 469          inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
 470          objectType = 1
 471          state.Inventory[inventoryHash] = (
 472              objectType, streamNumber, payload, embeddedTime,
 473              doubleHashOfAddressData[32:]
 474          )
 475  
 476          self.logger.info(
 477              'broadcasting inv with hash: %s', hexlify(inventoryHash))
 478  
 479          invQueue.put((streamNumber, inventoryHash))
 480          queues.UISignalQueue.put(('updateStatusBar', ''))
 481          try:
 482              config.set(
 483                  myAddress, 'lastpubkeysendtime', str(int(time.time())))
 484              config.save()
 485          except Exception as err:
 486              self.logger.error(
 487                  'Error: Couldn\'t add the lastpubkeysendtime'
 488                  ' to the keys.dat file. Error message: %s', err
 489              )
 490  
 491      def sendOnionPeerObj(self, peer=None):
 492          """Send onionpeer object representing peer"""
 493          if not peer:  # find own onionhostname
 494              for peer in state.ownAddresses:
 495                  if peer.host.endswith('.onion'):
 496                      break
 497              else:
 498                  return
 499          TTL = int(7 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
 500          embeddedTime = int(time.time() + TTL)
 501          streamNumber = 1  # Don't know yet what should be here
 502          objectType = protocol.OBJECT_ONIONPEER
 503          # FIXME: ideally the objectPayload should be signed
 504          objectPayload = encodeVarint(peer.port) + protocol.encodeHost(peer.host)
 505          tag = highlevelcrypto.calculateInventoryHash(objectPayload)
 506  
 507          if state.Inventory.by_type_and_tag(objectType, tag):
 508              return  # not expired
 509  
 510          payload = pack('>Q', embeddedTime)
 511          payload += pack('>I', objectType)
 512          payload += encodeVarint(2 if len(peer.host) == 22 else 3)
 513          payload += encodeVarint(streamNumber)
 514          payload += objectPayload
 515  
 516          payload = self._doPOWDefaults(
 517              payload, TTL, log_prefix='(For onionpeer object)')
 518  
 519          inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
 520          state.Inventory[inventoryHash] = (
 521              objectType, streamNumber, buffer(payload),  # noqa: F821
 522              embeddedTime, buffer(tag)  # noqa: F821
 523          )
 524          self.logger.info(
 525              'sending inv (within sendOnionPeerObj function) for object: %s',
 526              hexlify(inventoryHash))
 527          invQueue.put((streamNumber, inventoryHash))
 528  
 529      def sendBroadcast(self):
 530          """Send a broadcast-type object (assemble the object, perform PoW and put it to the inv announcement queue)"""
 531          # Reset just in case
 532          sqlExecute(
 533              '''UPDATE sent SET status='broadcastqueued' '''
 534  
 535              '''WHERE status = 'doingbroadcastpow' AND folder = 'sent' ''')
 536          queryreturn = sqlQuery(
 537              '''SELECT fromaddress, subject, message, '''
 538              ''' ackdata, ttl, encodingtype FROM sent '''
 539              ''' WHERE status=? and folder='sent' ''', 'broadcastqueued')
 540  
 541          for row in queryreturn:
 542              fromaddress, subject, body, ackdata, TTL, encoding = row
 543              # status
 544              _, addressVersionNumber, streamNumber, ripe = \
 545                  decodeAddress(fromaddress)
 546              if addressVersionNumber <= 1:
 547                  self.logger.error(
 548                      'Error: In the singleWorker thread, the '
 549                      ' sendBroadcast function doesn\'t understand'
 550                      ' the address version.\n')
 551                  return
 552              # We need to convert our private keys to public keys in order
 553              # to include them.
 554              try:
 555                  # , privEncryptionKeyHex
 556                  privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \
 557                      self._getKeysForAddress(fromaddress)
 558              except ValueError:
 559                  queues.UISignalQueue.put((
 560                      'updateSentItemStatusByAckdata', (
 561                          ackdata,
 562                          tr._translate(
 563                              "MainWindow",
 564                              "Error! Could not find sender address"
 565                              " (your address) in the keys.dat file."))
 566                  ))
 567                  continue
 568              except Exception as err:
 569                  self.logger.error(
 570                      'Error within sendBroadcast. Could not read'
 571                      ' the keys from the keys.dat file for a requested'
 572                      ' address. %s\n', err
 573                  )
 574                  queues.UISignalQueue.put((
 575                      'updateSentItemStatusByAckdata', (
 576                          ackdata,
 577                          tr._translate(
 578                              "MainWindow",
 579                              "Error, can't send."))
 580                  ))
 581                  continue
 582  
 583              if not sqlExecute(
 584                      '''UPDATE sent SET status='doingbroadcastpow' '''
 585                      ''' WHERE ackdata=? AND status='broadcastqueued' '''
 586                      ''' AND folder='sent' ''',
 587                      ackdata):
 588                  continue
 589  
 590              # At this time these pubkeys are 65 bytes long
 591              # because they include the encoding byte which we won't
 592              # be sending in the broadcast message.
 593              # pubSigningKey = \
 594              #     highlevelcrypto.privToPub(privSigningKeyHex).decode('hex')
 595  
 596              if TTL > 28 * 24 * 60 * 60:
 597                  TTL = 28 * 24 * 60 * 60
 598              if TTL < 60 * 60:
 599                  TTL = 60 * 60
 600              # add some randomness to the TTL
 601              TTL = int(TTL + helper_random.randomrandrange(-300, 300))
 602              embeddedTime = int(time.time() + TTL)
 603              payload = pack('>Q', embeddedTime)
 604              payload += '\x00\x00\x00\x03'  # object type: broadcast
 605  
 606              if addressVersionNumber <= 3:
 607                  payload += encodeVarint(4)  # broadcast version
 608              else:
 609                  payload += encodeVarint(5)  # broadcast version
 610  
 611              payload += encodeVarint(streamNumber)
 612              if addressVersionNumber >= 4:
 613                  doubleHashOfAddressData = highlevelcrypto.double_sha512(
 614                      encodeVarint(addressVersionNumber)
 615                      + encodeVarint(streamNumber) + ripe
 616                  )
 617                  tag = doubleHashOfAddressData[32:]
 618                  payload += tag
 619              else:
 620                  tag = ''
 621  
 622              dataToEncrypt = encodeVarint(addressVersionNumber)
 623              dataToEncrypt += encodeVarint(streamNumber)
 624              # behavior bitfield
 625              dataToEncrypt += protocol.getBitfield(fromaddress)
 626              dataToEncrypt += pubSigningKey + pubEncryptionKey
 627              if addressVersionNumber >= 3:
 628                  dataToEncrypt += encodeVarint(config.getint(
 629                      fromaddress, 'noncetrialsperbyte'))
 630                  dataToEncrypt += encodeVarint(config.getint(
 631                      fromaddress, 'payloadlengthextrabytes'))
 632              # message encoding type
 633              dataToEncrypt += encodeVarint(encoding)
 634              encodedMessage = helper_msgcoding.MsgEncode(
 635                  {"subject": subject, "body": body}, encoding)
 636              dataToEncrypt += encodeVarint(encodedMessage.length)
 637              dataToEncrypt += encodedMessage.data
 638              dataToSign = payload + dataToEncrypt
 639  
 640              signature = highlevelcrypto.sign(
 641                  dataToSign, privSigningKeyHex, self.digestAlg)
 642              dataToEncrypt += encodeVarint(len(signature))
 643              dataToEncrypt += signature
 644  
 645              # Encrypt the broadcast with the information
 646              # contained in the broadcaster's address.
 647              # Anyone who knows the address can generate
 648              # the private encryption key to decrypt the broadcast.
 649              # This provides virtually no privacy; its purpose is to keep
 650              # questionable and illegal content from flowing through the
 651              # Internet connections and being stored on the disk of 3rd parties.
 652              if addressVersionNumber <= 3:
 653                  privEncryptionKey = hashlib.sha512(
 654                      encodeVarint(addressVersionNumber)
 655                      + encodeVarint(streamNumber) + ripe
 656                  ).digest()[:32]
 657              else:
 658                  privEncryptionKey = doubleHashOfAddressData[:32]
 659  
 660              pubEncryptionKey = highlevelcrypto.pointMult(privEncryptionKey)
 661              payload += highlevelcrypto.encrypt(
 662                  dataToEncrypt, hexlify(pubEncryptionKey))
 663  
 664              queues.UISignalQueue.put((
 665                  'updateSentItemStatusByAckdata', (
 666                      ackdata,
 667                      tr._translate(
 668                          "MainWindow",
 669                          "Doing work necessary to send broadcast..."))
 670              ))
 671              payload = self._doPOWDefaults(
 672                  payload, TTL, log_prefix='(For broadcast message)')
 673  
 674              # Sanity check. The payload size should never be larger
 675              # than 256 KiB. There should be checks elsewhere in the code
 676              # to not let the user try to send a message this large
 677              # until we implement message continuation.
 678              if len(payload) > 2 ** 18:  # 256 KiB
 679                  self.logger.critical(
 680                      'This broadcast object is too large to send.'
 681                      ' This should never happen. Object size: %s',
 682                      len(payload)
 683                  )
 684                  continue
 685  
 686              inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
 687              objectType = 3
 688              state.Inventory[inventoryHash] = (
 689                  objectType, streamNumber, payload, embeddedTime, tag)
 690              self.logger.info(
 691                  'sending inv (within sendBroadcast function)'
 692                  ' for object: %s',
 693                  hexlify(inventoryHash)
 694              )
 695              invQueue.put((streamNumber, inventoryHash))
 696  
 697              queues.UISignalQueue.put((
 698                  'updateSentItemStatusByAckdata', (
 699                      ackdata,
 700                      tr._translate(
 701                          "MainWindow",
 702                          "Broadcast sent on %1"
 703                      ).arg(l10n.formatTimestamp()))
 704              ))
 705  
 706              # Update the status of the message in the 'sent' table to have
 707              # a 'broadcastsent' status
 708              sqlExecute(
 709                  '''UPDATE sent SET msgid=?, status=?, lastactiontime=? '''
 710                  ''' WHERE ackdata=? AND folder='sent' ''',
 711                  inventoryHash, 'broadcastsent', int(time.time()), ackdata
 712              )
 713  
 714      def sendMsg(self):
 715          """Send a message-type object (assemble the object, perform PoW and put it to the inv announcement queue)"""
 716          # pylint: disable=too-many-nested-blocks
 717          # Reset just in case
 718          sqlExecute(
 719              '''UPDATE sent SET status='msgqueued' '''
 720              ''' WHERE status IN ('doingpubkeypow', 'doingmsgpow') '''
 721              ''' AND folder='sent' ''')
 722          queryreturn = sqlQuery(
 723              '''SELECT toaddress, fromaddress, subject, message, '''
 724              ''' ackdata, status, ttl, retrynumber, encodingtype FROM '''
 725              ''' sent WHERE (status='msgqueued' or status='forcepow') '''
 726              ''' and folder='sent' ''')
 727          # while we have a msg that needs some work
 728          for row in queryreturn:
 729              toaddress, fromaddress, subject, message, \
 730                  ackdata, status, TTL, retryNumber, encoding = row
 731              # toStatus
 732              _, toAddressVersionNumber, toStreamNumber, toRipe = \
 733                  decodeAddress(toaddress)
 734              # fromStatus, , ,fromRipe
 735              _, fromAddressVersionNumber, fromStreamNumber, _ = \
 736                  decodeAddress(fromaddress)
 737  
 738              # We may or may not already have the pubkey
 739              # for this toAddress. Let's check.
 740              if status == 'forcepow':
 741                  # if the status of this msg is 'forcepow'
 742                  # then clearly we have the pubkey already
 743                  # because the user could not have overridden the message
 744                  # about the POW being too difficult without knowing
 745                  # the required difficulty.
 746                  pass
 747              elif status == 'doingmsgpow':
 748                  # We wouldn't have set the status to doingmsgpow
 749                  # if we didn't already have the pubkey so let's assume
 750                  # that we have it.
 751                  pass
 752              # If we are sending a message to ourselves or a chan
 753              # then we won't need an entry in the pubkeys table;
 754              # we can calculate the needed pubkey using the private keys
 755              # in our keys.dat file.
 756              elif config.has_section(toaddress):
 757                  if not sqlExecute(
 758                      '''UPDATE sent SET status='doingmsgpow' '''
 759                      ''' WHERE toaddress=? AND status='msgqueued' AND folder='sent' ''',
 760                      toaddress
 761                  ):
 762                      continue
 763                  status = 'doingmsgpow'
 764              elif status == 'msgqueued':
 765                  # Let's see if we already have the pubkey in our pubkeys table
 766                  queryreturn = sqlQuery(
 767                      '''SELECT address FROM pubkeys WHERE address=?''',
 768                      toaddress
 769                  )
 770                  # If we have the needed pubkey in the pubkey table already,
 771                  if queryreturn != []:
 772                      # set the status of this msg to doingmsgpow
 773                      if not sqlExecute(
 774                          '''UPDATE sent SET status='doingmsgpow' '''
 775                          ''' WHERE toaddress=? AND status='msgqueued' AND folder='sent' ''',
 776                          toaddress
 777                      ):
 778                          continue
 779                      status = 'doingmsgpow'
 780                      # mark the pubkey as 'usedpersonally' so that
 781                      # we don't delete it later. If the pubkey version
 782                      # is >= 4 then usedpersonally will already be set
 783                      # to yes because we'll only ever have
 784                      # usedpersonally v4 pubkeys in the pubkeys table.
 785                      sqlExecute(
 786                          '''UPDATE pubkeys SET usedpersonally='yes' '''
 787                          ''' WHERE address=?''',
 788                          toaddress
 789                      )
 790                  # We don't have the needed pubkey in the pubkeys table already.
 791                  else:
 792                      if toAddressVersionNumber <= 3:
 793                          toTag = ''
 794                      else:
 795                          toTag = highlevelcrypto.double_sha512(
 796                              encodeVarint(toAddressVersionNumber)
 797                              + encodeVarint(toStreamNumber) + toRipe
 798                          )[32:]
 799                      if toaddress in state.neededPubkeys or \
 800                              toTag in state.neededPubkeys:
 801                          # We already sent a request for the pubkey
 802                          sqlExecute(
 803                              '''UPDATE sent SET status='awaitingpubkey', '''
 804                              ''' sleeptill=? WHERE toaddress=? '''
 805                              ''' AND status='msgqueued' ''',
 806                              int(time.time()) + 2.5 * 24 * 60 * 60,
 807                              toaddress
 808                          )
 809                          queues.UISignalQueue.put((
 810                              'updateSentItemStatusByToAddress', (
 811                                  toaddress,
 812                                  tr._translate(
 813                                      "MainWindow",
 814                                      "Encryption key was requested earlier."))
 815                          ))
 816                          # on with the next msg on which we can do some work
 817                          continue
 818                      else:
 819                          # We have not yet sent a request for the pubkey
 820                          needToRequestPubkey = True
 821                          # If we are trying to send to address
 822                          # version >= 4 then the needed pubkey might be
 823                          # encrypted in the inventory.
 824                          # If we have it we'll need to decrypt it
 825                          # and put it in the pubkeys table.
 826  
 827                          # The decryptAndCheckPubkeyPayload function
 828                          # expects that the shared.neededPubkeys dictionary
 829                          # already contains the toAddress and cryptor
 830                          # object associated with the tag for this toAddress.
 831                          if toAddressVersionNumber >= 4:
 832                              doubleHashOfToAddressData = \
 833                                  highlevelcrypto.double_sha512(
 834                                      encodeVarint(toAddressVersionNumber)
 835                                      + encodeVarint(toStreamNumber) + toRipe
 836                                  )
 837                              # The first half of the sha512 hash.
 838                              privEncryptionKey = doubleHashOfToAddressData[:32]
 839                              # The second half of the sha512 hash.
 840                              tag = doubleHashOfToAddressData[32:]
 841                              state.neededPubkeys[tag] = (
 842                                  toaddress,
 843                                  highlevelcrypto.makeCryptor(
 844                                      hexlify(privEncryptionKey))
 845                              )
 846  
 847                              for value in state.Inventory.by_type_and_tag(1, toTag):
 848                                  # if valid, this function also puts it
 849                                  # in the pubkeys table.
 850                                  if protocol.decryptAndCheckPubkeyPayload(
 851                                          value.payload, toaddress
 852                                  ) == 'successful':
 853                                      needToRequestPubkey = False
 854                                      sqlExecute(
 855                                          '''UPDATE sent SET '''
 856                                          ''' status='doingmsgpow', '''
 857                                          ''' retrynumber=0 WHERE '''
 858                                          ''' toaddress=? AND '''
 859                                          ''' (status='msgqueued' or '''
 860                                          ''' status='awaitingpubkey' or '''
 861                                          ''' status='doingpubkeypow') AND '''
 862                                          ''' folder='sent' ''',
 863                                          toaddress)
 864                                      del state.neededPubkeys[tag]
 865                                      break
 866                                  # else:
 867                                  # There was something wrong with this
 868                                  # pubkey object even though it had
 869                                  # the correct tag- almost certainly
 870                                  # because of malicious behavior or
 871                                  # a badly programmed client. If there are
 872                                  # any other pubkeys in our inventory
 873                                  # with the correct tag then we'll try
 874                                  # to decrypt those.
 875                          if needToRequestPubkey:
 876                              sqlExecute(
 877                                  '''UPDATE sent SET '''
 878                                  ''' status='doingpubkeypow' WHERE '''
 879                                  ''' toaddress=? AND status='msgqueued' AND folder='sent' ''',
 880                                  toaddress
 881                              )
 882                              queues.UISignalQueue.put((
 883                                  'updateSentItemStatusByToAddress', (
 884                                      toaddress,
 885                                      tr._translate(
 886                                          "MainWindow",
 887                                          "Sending a request for the"
 888                                          " recipient\'s encryption key."))
 889                              ))
 890                              self.requestPubKey(toaddress)
 891                              # on with the next msg on which we can do some work
 892                              continue
 893  
 894              # At this point we know that we have the necessary pubkey
 895              # in the pubkeys table.
 896  
 897              TTL *= 2**retryNumber
 898              if TTL > 28 * 24 * 60 * 60:
 899                  TTL = 28 * 24 * 60 * 60
 900              # add some randomness to the TTL
 901              TTL = int(TTL + helper_random.randomrandrange(-300, 300))
 902              embeddedTime = int(time.time() + TTL)
 903  
 904              # if we aren't sending this to ourselves or a chan
 905              if not config.has_section(toaddress):
 906                  state.ackdataForWhichImWatching[ackdata] = 0
 907                  queues.UISignalQueue.put((
 908                      'updateSentItemStatusByAckdata', (
 909                          ackdata,
 910                          tr._translate(
 911                              "MainWindow",
 912                              "Looking up the receiver\'s public key"))
 913                  ))
 914                  self.logger.info('Sending a message.')
 915                  self.logger.debug(
 916                      'First 150 characters of message: %s',
 917                      repr(message[:150])
 918                  )
 919  
 920                  # Let us fetch the recipient's public key out of
 921                  # our database. If the required proof of work difficulty
 922                  # is too hard then we'll abort.
 923                  queryreturn = sqlQuery(
 924                      'SELECT transmitdata FROM pubkeys WHERE address=?',
 925                      toaddress)
 926                  for row in queryreturn:  # pylint: disable=redefined-outer-name
 927                      pubkeyPayload, = row
 928  
 929                  # The pubkey message is stored with the following items
 930                  # all appended:
 931                  #    -address version
 932                  #    -stream number
 933                  #    -behavior bitfield
 934                  #    -pub signing key
 935                  #    -pub encryption key
 936                  #    -nonce trials per byte (if address version is >= 3)
 937                  #    -length extra bytes (if address version is >= 3)
 938  
 939                  # to bypass the address version whose length is definitely 1
 940                  readPosition = 1
 941                  _, streamNumberLength = decodeVarint(
 942                      pubkeyPayload[readPosition:readPosition + 10])
 943                  readPosition += streamNumberLength
 944                  behaviorBitfield = pubkeyPayload[readPosition:readPosition + 4]
 945                  # Mobile users may ask us to include their address's
 946                  # RIPE hash on a message unencrypted. Before we actually
 947                  # do it the sending human must check a box
 948                  # in the settings menu to allow it.
 949  
 950                  # if receiver is a mobile device who expects that their
 951                  # address RIPE is included unencrypted on the front of
 952                  # the message..
 953                  if protocol.isBitSetWithinBitfield(behaviorBitfield, 30):
 954                      # if we are Not willing to include the receiver's
 955                      # RIPE hash on the message..
 956                      if not config.safeGetBoolean(
 957                              'bitmessagesettings', 'willinglysendtomobile'
 958                      ):
 959                          self.logger.info(
 960                              'The receiver is a mobile user but the'
 961                              ' sender (you) has not selected that you'
 962                              ' are willing to send to mobiles. Aborting'
 963                              ' send.'
 964                          )
 965                          queues.UISignalQueue.put((
 966                              'updateSentItemStatusByAckdata', (
 967                                  ackdata,
 968                                  tr._translate(
 969                                      "MainWindow",
 970                                      "Problem: Destination is a mobile"
 971                                      " device who requests that the"
 972                                      " destination be included in the"
 973                                      " message but this is disallowed in"
 974                                      " your settings.  %1"
 975                                  ).arg(l10n.formatTimestamp()))
 976                          ))
 977                          # if the human changes their setting and then
 978                          # sends another message or restarts their client,
 979                          # this one will send at that time.
 980                          continue
 981                  readPosition += 4  # to bypass the bitfield of behaviors
 982                  # We don't use this key for anything here.
 983                  # pubSigningKeyBase256 =
 984                  # pubkeyPayload[readPosition:readPosition+64]
 985                  readPosition += 64
 986                  pubEncryptionKeyBase256 = pubkeyPayload[
 987                      readPosition:readPosition + 64]
 988                  readPosition += 64
 989  
 990                  # Let us fetch the amount of work required by the recipient.
 991                  if toAddressVersionNumber == 2:
 992                      requiredAverageProofOfWorkNonceTrialsPerByte = \
 993                          defaults.networkDefaultProofOfWorkNonceTrialsPerByte
 994                      requiredPayloadLengthExtraBytes = \
 995                          defaults.networkDefaultPayloadLengthExtraBytes
 996                      queues.UISignalQueue.put((
 997                          'updateSentItemStatusByAckdata', (
 998                              ackdata,
 999                              tr._translate(
1000                                  "MainWindow",
1001                                  "Doing work necessary to send message.\n"
1002                                  "There is no required difficulty for"
1003                                  " version 2 addresses like this."))
1004                      ))
1005                  elif toAddressVersionNumber >= 3:
1006                      requiredAverageProofOfWorkNonceTrialsPerByte, \
1007                          varintLength = decodeVarint(
1008                              pubkeyPayload[readPosition:readPosition + 10])
1009                      readPosition += varintLength
1010                      requiredPayloadLengthExtraBytes, varintLength = \
1011                          decodeVarint(
1012                              pubkeyPayload[readPosition:readPosition + 10])
1013                      readPosition += varintLength
1014                      # We still have to meet a minimum POW difficulty
1015                      # regardless of what they say is allowed in order
1016                      # to get our message to propagate through the network.
1017                      if requiredAverageProofOfWorkNonceTrialsPerByte < \
1018                              defaults.networkDefaultProofOfWorkNonceTrialsPerByte:
1019                          requiredAverageProofOfWorkNonceTrialsPerByte = \
1020                              defaults.networkDefaultProofOfWorkNonceTrialsPerByte
1021                      if requiredPayloadLengthExtraBytes < \
1022                              defaults.networkDefaultPayloadLengthExtraBytes:
1023                          requiredPayloadLengthExtraBytes = \
1024                              defaults.networkDefaultPayloadLengthExtraBytes
1025                      self.logger.debug(
1026                          'Using averageProofOfWorkNonceTrialsPerByte: %s'
1027                          ' and payloadLengthExtraBytes: %s.',
1028                          requiredAverageProofOfWorkNonceTrialsPerByte,
1029                          requiredPayloadLengthExtraBytes
1030                      )
1031  
1032                      queues.UISignalQueue.put(
1033                          (
1034                              'updateSentItemStatusByAckdata',
1035                              (
1036                                  ackdata,
1037                                  tr._translate(
1038                                      "MainWindow",
1039                                      "Doing work necessary to send message.\n"
1040                                      "Receiver\'s required difficulty: %1"
1041                                      " and %2"
1042                                  ).arg(
1043                                      str(
1044                                          float(requiredAverageProofOfWorkNonceTrialsPerByte)
1045                                          / defaults.networkDefaultProofOfWorkNonceTrialsPerByte
1046                                      )
1047                                  ).arg(
1048                                      str(
1049                                          float(requiredPayloadLengthExtraBytes)
1050                                          / defaults.networkDefaultPayloadLengthExtraBytes
1051                                      )
1052                                  )
1053                              )
1054                          )
1055                      )
1056  
1057                      if status != 'forcepow':
1058                          maxacceptablenoncetrialsperbyte = config.getint(
1059                              'bitmessagesettings', 'maxacceptablenoncetrialsperbyte')
1060                          maxacceptablepayloadlengthextrabytes = config.getint(
1061                              'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')
1062                          cond1 = maxacceptablenoncetrialsperbyte and \
1063                              requiredAverageProofOfWorkNonceTrialsPerByte > maxacceptablenoncetrialsperbyte
1064                          cond2 = maxacceptablepayloadlengthextrabytes and \
1065                              requiredPayloadLengthExtraBytes > maxacceptablepayloadlengthextrabytes
1066  
1067                          if cond1 or cond2:
1068                              # The demanded difficulty is more than
1069                              # we are willing to do.
1070                              sqlExecute(
1071                                  '''UPDATE sent SET status='toodifficult' '''
1072                                  ''' WHERE ackdata=? AND folder='sent' ''',
1073                                  ackdata)
1074                              queues.UISignalQueue.put((
1075                                  'updateSentItemStatusByAckdata', (
1076                                      ackdata,
1077                                      tr._translate(
1078                                          "MainWindow",
1079                                          "Problem: The work demanded by"
1080                                          " the recipient (%1 and %2) is"
1081                                          " more difficult than you are"
1082                                          " willing to do. %3"
1083                                      ).arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte)
1084                                            / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
1085                                            ).arg(str(float(requiredPayloadLengthExtraBytes)
1086                                                  / defaults.networkDefaultPayloadLengthExtraBytes)
1087                                                  ).arg(l10n.formatTimestamp()))))
1088                              continue
1089              else:  # if we are sending a message to ourselves or a chan..
1090                  self.logger.info('Sending a message.')
1091                  self.logger.debug(
1092                      'First 150 characters of message: %r', message[:150])
1093                  behaviorBitfield = protocol.getBitfield(fromaddress)
1094  
1095                  try:
1096                      privEncryptionKeyBase58 = config.get(
1097                          toaddress, 'privencryptionkey')
1098                  except (configparser.NoSectionError, configparser.NoOptionError) as err:
1099                      queues.UISignalQueue.put((
1100                          'updateSentItemStatusByAckdata', (
1101                              ackdata,
1102                              tr._translate(
1103                                  "MainWindow",
1104                                  "Problem: You are trying to send a"
1105                                  " message to yourself or a chan but your"
1106                                  " encryption key could not be found in"
1107                                  " the keys.dat file. Could not encrypt"
1108                                  " message. %1"
1109                              ).arg(l10n.formatTimestamp()))
1110                      ))
1111                      self.logger.error(
1112                          'Error within sendMsg. Could not read the keys'
1113                          ' from the keys.dat file for our own address. %s\n',
1114                          err)
1115                      continue
1116                  privEncryptionKeyHex = hexlify(
1117                      highlevelcrypto.decodeWalletImportFormat(
1118                          privEncryptionKeyBase58.encode()))
1119                  pubEncryptionKeyBase256 = unhexlify(highlevelcrypto.privToPub(
1120                      privEncryptionKeyHex))[1:]
1121                  requiredAverageProofOfWorkNonceTrialsPerByte = \
1122                      defaults.networkDefaultProofOfWorkNonceTrialsPerByte
1123                  requiredPayloadLengthExtraBytes = \
1124                      defaults.networkDefaultPayloadLengthExtraBytes
1125                  queues.UISignalQueue.put((
1126                      'updateSentItemStatusByAckdata', (
1127                          ackdata,
1128                          tr._translate(
1129                              "MainWindow",
1130                              "Doing work necessary to send message."))
1131                  ))
1132  
1133              # Now we can start to assemble our message.
1134              payload = encodeVarint(fromAddressVersionNumber)
1135              payload += encodeVarint(fromStreamNumber)
1136              # Bitfield of features and behaviors
1137              # that can be expected from me. (See
1138              # https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features)
1139              payload += protocol.getBitfield(fromaddress)
1140  
1141              # We need to convert our private keys to public keys in order
1142              # to include them.
1143              try:
1144                  privSigningKeyHex, privEncryptionKeyHex, \
1145                      pubSigningKey, pubEncryptionKey = self._getKeysForAddress(
1146                          fromaddress)
1147              except ValueError:
1148                  queues.UISignalQueue.put((
1149                      'updateSentItemStatusByAckdata', (
1150                          ackdata,
1151                          tr._translate(
1152                              "MainWindow",
1153                              "Error! Could not find sender address"
1154                              " (your address) in the keys.dat file."))
1155                  ))
1156                  continue
1157              except Exception as err:
1158                  self.logger.error(
1159                      'Error within sendMsg. Could not read'
1160                      ' the keys from the keys.dat file for a requested'
1161                      ' address. %s\n', err
1162                  )
1163                  queues.UISignalQueue.put((
1164                      'updateSentItemStatusByAckdata', (
1165                          ackdata,
1166                          tr._translate(
1167                              "MainWindow",
1168                              "Error, can't send."))
1169                  ))
1170                  continue
1171  
1172              payload += pubSigningKey + pubEncryptionKey
1173  
1174              if fromAddressVersionNumber >= 3:
1175                  # If the receiver of our message is in our address book,
1176                  # subscriptions list, or whitelist then we will allow them to
1177                  # do the network-minimum proof of work. Let us check to see if
1178                  # the receiver is in any of those lists.
1179                  if shared.isAddressInMyAddressBookSubscriptionsListOrWhitelist(
1180                          toaddress):
1181                      payload += encodeVarint(
1182                          defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
1183                      payload += encodeVarint(
1184                          defaults.networkDefaultPayloadLengthExtraBytes)
1185                  else:
1186                      payload += encodeVarint(config.getint(
1187                          fromaddress, 'noncetrialsperbyte'))
1188                      payload += encodeVarint(config.getint(
1189                          fromaddress, 'payloadlengthextrabytes'))
1190  
1191              # This hash will be checked by the receiver of the message
1192              # to verify that toRipe belongs to them. This prevents
1193              # a Surreptitious Forwarding Attack.
1194              payload += toRipe
1195              payload += encodeVarint(encoding)  # message encoding type
1196              encodedMessage = helper_msgcoding.MsgEncode(
1197                  {"subject": subject, "body": message}, encoding
1198              )
1199              payload += encodeVarint(encodedMessage.length)
1200              payload += encodedMessage.data
1201              if config.has_section(toaddress):
1202                  self.logger.info(
1203                      'Not bothering to include ackdata because we are'
1204                      ' sending to ourselves or a chan.'
1205                  )
1206                  fullAckPayload = ''
1207              elif not protocol.checkBitfield(
1208                      behaviorBitfield, protocol.BITFIELD_DOESACK):
1209                  self.logger.info(
1210                      'Not bothering to include ackdata because'
1211                      ' the receiver said that they won\'t relay it anyway.'
1212                  )
1213                  fullAckPayload = ''
1214              else:
1215                  # The fullAckPayload is a normal msg protocol message
1216                  # with the proof of work already completed that the
1217                  # receiver of this message can easily send out.
1218                  fullAckPayload = self.generateFullAckMessage(
1219                      ackdata, toStreamNumber, TTL)
1220              payload += encodeVarint(len(fullAckPayload))
1221              payload += fullAckPayload
1222              dataToSign = pack('>Q', embeddedTime) + '\x00\x00\x00\x02' + \
1223                  encodeVarint(1) + encodeVarint(toStreamNumber) + payload
1224              signature = highlevelcrypto.sign(
1225                  dataToSign, privSigningKeyHex, self.digestAlg)
1226              payload += encodeVarint(len(signature))
1227              payload += signature
1228  
1229              # We have assembled the data that will be encrypted.
1230              try:
1231                  encrypted = highlevelcrypto.encrypt(
1232                      payload, "04" + hexlify(pubEncryptionKeyBase256)
1233                  )
1234              except:  # noqa:E722
1235                  self.logger.warning("highlevelcrypto.encrypt didn't work")
1236                  sqlExecute(
1237                      '''UPDATE sent SET status='badkey' WHERE ackdata=? AND folder='sent' ''',
1238                      ackdata
1239                  )
1240                  queues.UISignalQueue.put((
1241                      'updateSentItemStatusByAckdata', (
1242                          ackdata,
1243                          tr._translate(
1244                              "MainWindow",
1245                              "Problem: The recipient\'s encryption key is"
1246                              " no good. Could not encrypt message. %1"
1247                          ).arg(l10n.formatTimestamp()))
1248                  ))
1249                  continue
1250  
1251              encryptedPayload = pack('>Q', embeddedTime)
1252              encryptedPayload += '\x00\x00\x00\x02'  # object type: msg
1253              encryptedPayload += encodeVarint(1)  # msg version
1254              encryptedPayload += encodeVarint(toStreamNumber) + encrypted
1255  
1256              encryptedPayload = self._doPOWDefaults(
1257                  encryptedPayload, TTL,
1258                  requiredAverageProofOfWorkNonceTrialsPerByte,
1259                  requiredPayloadLengthExtraBytes,
1260                  log_prefix='(For msg message)', log_time=True
1261              )
1262  
1263              # Sanity check. The encryptedPayload size should never be
1264              # larger than 256 KiB. There should be checks elsewhere
1265              # in the code to not let the user try to send a message
1266              # this large until we implement message continuation.
1267              if len(encryptedPayload) > 2 ** 18:  # 256 KiB
1268                  self.logger.critical(
1269                      'This msg object is too large to send. This should'
1270                      ' never happen. Object size: %i',
1271                      len(encryptedPayload)
1272                  )
1273                  continue
1274  
1275              inventoryHash = highlevelcrypto.calculateInventoryHash(encryptedPayload)
1276              objectType = 2
1277              state.Inventory[inventoryHash] = (
1278                  objectType, toStreamNumber, encryptedPayload, embeddedTime, '')
1279              if config.has_section(toaddress) or \
1280                 not protocol.checkBitfield(behaviorBitfield, protocol.BITFIELD_DOESACK):
1281                  queues.UISignalQueue.put((
1282                      'updateSentItemStatusByAckdata', (
1283                          ackdata,
1284                          tr._translate(
1285                              "MainWindow",
1286                              "Message sent. Sent at %1"
1287                          ).arg(l10n.formatTimestamp()))))
1288              else:
1289                  # not sending to a chan or one of my addresses
1290                  queues.UISignalQueue.put((
1291                      'updateSentItemStatusByAckdata', (
1292                          ackdata,
1293                          tr._translate(
1294                              "MainWindow",
1295                              "Message sent. Waiting for acknowledgement."
1296                              " Sent on %1"
1297                          ).arg(l10n.formatTimestamp()))
1298                  ))
1299              self.logger.info(
1300                  'Broadcasting inv for my msg(within sendmsg function): %s',
1301                  hexlify(inventoryHash)
1302              )
1303              invQueue.put((toStreamNumber, inventoryHash))
1304  
1305              # Update the sent message in the sent table with the
1306              # necessary information.
1307              if config.has_section(toaddress) or \
1308                 not protocol.checkBitfield(behaviorBitfield, protocol.BITFIELD_DOESACK):
1309                  newStatus = 'msgsentnoackexpected'
1310              else:
1311                  newStatus = 'msgsent'
1312              # wait 10% past expiration
1313              sleepTill = int(time.time() + TTL * 1.1)
1314              sqlExecute(
1315                  '''UPDATE sent SET msgid=?, status=?, retrynumber=?, '''
1316                  ''' sleeptill=?, lastactiontime=? WHERE ackdata=? AND folder='sent' ''',
1317                  inventoryHash, newStatus, retryNumber + 1,
1318                  sleepTill, int(time.time()), ackdata
1319              )
1320  
1321              # If we are sending to ourselves or a chan, let's put
1322              # the message in our own inbox.
1323              if config.has_section(toaddress):
1324                  # Used to detect and ignore duplicate messages in our inbox
1325                  sigHash = highlevelcrypto.double_sha512(signature)[32:]
1326                  t = (inventoryHash, toaddress, fromaddress, subject, int(
1327                      time.time()), message, 'inbox', encoding, 0, sigHash)
1328                  helper_inbox.insert(t)
1329  
1330                  queues.UISignalQueue.put(('displayNewInboxMessage', (
1331                      inventoryHash, toaddress, fromaddress, subject, message)))
1332  
1333                  # If we are behaving as an API then we might need to run an
1334                  # outside command to let some program know that a new message
1335                  # has arrived.
1336                  if config.safeGetBoolean(
1337                          'bitmessagesettings', 'apienabled'):
1338  
1339                      apiNotifyPath = config.safeGet(
1340                          'bitmessagesettings', 'apinotifypath')
1341  
1342                      if apiNotifyPath:
1343                          # There is no additional risk of remote exploitation or
1344                          # privilege escalation
1345                          call([apiNotifyPath, "newMessage"])  # nosec B603
1346  
1347      def requestPubKey(self, toAddress):
1348          """Send a getpubkey object"""
1349          toStatus, addressVersionNumber, streamNumber, ripe = decodeAddress(
1350              toAddress)
1351          if toStatus != 'success':
1352              self.logger.error(
1353                  'Very abnormal error occurred in requestPubKey.'
1354                  ' toAddress is: %r. Please report this error to Atheros.',
1355                  toAddress
1356              )
1357              return
1358  
1359          queryReturn = sqlQuery(
1360              '''SELECT retrynumber FROM sent WHERE toaddress=? '''
1361              ''' AND (status='doingpubkeypow' OR status='awaitingpubkey') '''
1362              ''' AND folder='sent' LIMIT 1''',
1363              toAddress
1364          )
1365          if not queryReturn:
1366              self.logger.critical(
1367                  'BUG: Why are we requesting the pubkey for %s'
1368                  ' if there are no messages in the sent folder'
1369                  ' to that address?', toAddress
1370              )
1371              return
1372          retryNumber = queryReturn[0][0]
1373  
1374          if addressVersionNumber <= 3:
1375              state.neededPubkeys[toAddress] = 0
1376          elif addressVersionNumber >= 4:
1377              # If the user just clicked 'send' then the tag
1378              # (and other information) will already be in the
1379              # neededPubkeys dictionary. But if we are recovering
1380              # from a restart of the client then we have to put it in now.
1381  
1382              doubleHashOfAddressData = highlevelcrypto.double_sha512(
1383                  encodeVarint(addressVersionNumber)
1384                  + encodeVarint(streamNumber) + ripe
1385              )
1386              privEncryptionKey = doubleHashOfAddressData[:32]
1387              # Note that this is the second half of the sha512 hash.
1388              tag = doubleHashOfAddressData[32:]
1389              if tag not in state.neededPubkeys:
1390                  # We'll need this for when we receive a pubkey reply:
1391                  # it will be encrypted and we'll need to decrypt it.
1392                  state.neededPubkeys[tag] = (
1393                      toAddress,
1394                      highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
1395                  )
1396  
1397          # 2.5 days. This was chosen fairly arbitrarily.
1398          TTL = 2.5 * 24 * 60 * 60
1399          TTL *= 2 ** retryNumber
1400          if TTL > 28 * 24 * 60 * 60:
1401              TTL = 28 * 24 * 60 * 60
1402          # add some randomness to the TTL
1403          TTL = TTL + helper_random.randomrandrange(-300, 300)
1404          embeddedTime = int(time.time() + TTL)
1405          payload = pack('>Q', embeddedTime)
1406          payload += '\x00\x00\x00\x00'  # object type: getpubkey
1407          payload += encodeVarint(addressVersionNumber)
1408          payload += encodeVarint(streamNumber)
1409          if addressVersionNumber <= 3:
1410              payload += ripe
1411              self.logger.info(
1412                  'making request for pubkey with ripe: %s', hexlify(ripe))
1413          else:
1414              payload += tag
1415              self.logger.info(
1416                  'making request for v4 pubkey with tag: %s', hexlify(tag))
1417  
1418          statusbar = 'Doing the computations necessary to request' +\
1419              ' the recipient\'s public key.'
1420          queues.UISignalQueue.put(('updateStatusBar', statusbar))
1421          queues.UISignalQueue.put((
1422              'updateSentItemStatusByToAddress', (
1423                  toAddress,
1424                  tr._translate(
1425                      "MainWindow",
1426                      "Doing work necessary to request encryption key."))
1427          ))
1428  
1429          payload = self._doPOWDefaults(payload, TTL)
1430  
1431          inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
1432          objectType = 1
1433          state.Inventory[inventoryHash] = (
1434              objectType, streamNumber, payload, embeddedTime, '')
1435          self.logger.info('sending inv (for the getpubkey message)')
1436          invQueue.put((streamNumber, inventoryHash))
1437  
1438          # wait 10% past expiration
1439          sleeptill = int(time.time() + TTL * 1.1)
1440          sqlExecute(
1441              '''UPDATE sent SET lastactiontime=?, '''
1442              ''' status='awaitingpubkey', retrynumber=?, sleeptill=? '''
1443              ''' WHERE toaddress=? AND (status='doingpubkeypow' OR '''
1444              ''' status='awaitingpubkey') AND folder='sent' ''',
1445              int(time.time()), retryNumber + 1, sleeptill, toAddress)
1446  
1447          queues.UISignalQueue.put((
1448              'updateStatusBar',
1449              tr._translate(
1450                  "MainWindow",
1451                  "Broadcasting the public key request. This program will"
1452                  " auto-retry if they are offline.")
1453          ))
1454          queues.UISignalQueue.put((
1455              'updateSentItemStatusByToAddress', (
1456                  toAddress,
1457                  tr._translate(
1458                      "MainWindow",
1459                      "Sending public key request. Waiting for reply."
1460                      " Requested at %1"
1461                  ).arg(l10n.formatTimestamp()))
1462          ))
1463  
1464      def generateFullAckMessage(self, ackdata, _, TTL):
1465          """
1466          It might be perfectly fine to just use the same TTL for the ackdata that we use for the message. But I would
1467          rather it be more difficult for attackers to associate ackData with the associated msg object. However, users
1468          would want the TTL of the acknowledgement to be about the same as they set for the message itself. So let's set
1469          the TTL of the acknowledgement to be in one of three 'buckets': 1 hour, 7 days, or 28 days, whichever is
1470          relatively close to what the user specified.
1471          """
1472          if TTL < 24 * 60 * 60:  # 1 day
1473              TTL = 24 * 60 * 60  # 1 day
1474          elif TTL < 7 * 24 * 60 * 60:  # 1 week
1475              TTL = 7 * 24 * 60 * 60  # 1 week
1476          else:
1477              TTL = 28 * 24 * 60 * 60  # 4 weeks
1478          # Add some randomness to the TTL
1479          TTL = int(TTL + helper_random.randomrandrange(-300, 300))
1480          embeddedTime = int(time.time() + TTL)
1481  
1482          # type/version/stream already included
1483          payload = pack('>Q', (embeddedTime)) + ackdata
1484  
1485          payload = self._doPOWDefaults(
1486              payload, TTL, log_prefix='(For ack message)', log_time=True)
1487  
1488          return protocol.CreatePacket('object', payload)