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)