2013-06-21 23:32:22 +02:00
import threading
import shared
import time
2013-06-24 22:25:31 +02:00
from time import strftime , localtime , gmtime
2013-06-21 23:32:22 +02:00
import random
2013-09-30 05:01:56 +02:00
from subprocess import call # used when the API must execute an outside program
2013-06-21 23:32:22 +02:00
from addresses import *
import highlevelcrypto
import proofofwork
2013-06-23 21:52:39 +02:00
import sys
2013-06-24 21:51:01 +02:00
import tr
2013-08-08 21:37:48 +02:00
from debug import logger
2013-08-29 13:27:30 +02:00
from helper_sql import *
2013-09-30 05:01:56 +02:00
import helper_inbox
2014-05-02 16:46:36 +02:00
from helper_generic import addDataPadding
2014-08-06 04:01:01 +02:00
import l10n
2013-06-21 23:32:22 +02:00
# This thread, of which there is only one, does the heavy lifting:
# calculating POWs.
class singleWorker ( threading . Thread ) :
def __init__ ( self ) :
# QThread.__init__(self, parent)
threading . Thread . __init__ ( self )
def run ( self ) :
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
2014-08-27 09:14:32 +02:00
''' SELECT DISTINCT toaddress FROM sent WHERE (status= ' awaitingpubkey ' AND folder= ' sent ' ) ''' )
2013-06-21 23:32:22 +02:00
for row in queryreturn :
2014-08-27 09:14:32 +02:00
toAddress , = row
toStatus , toAddressVersionNumber , toStreamNumber , toRipe = decodeAddress ( toAddress )
2013-09-13 06:27:34 +02:00
if toAddressVersionNumber < = 3 :
2014-08-27 09:14:32 +02:00
shared . neededPubkeys [ toRipe ] = 0
2013-09-13 06:27:34 +02:00
elif toAddressVersionNumber > = 4 :
2013-09-15 03:06:26 +02:00
doubleHashOfAddressData = hashlib . sha512 ( hashlib . sha512 ( encodeVarint (
toAddressVersionNumber ) + encodeVarint ( toStreamNumber ) + toRipe ) . digest ( ) ) . digest ( )
privEncryptionKey = doubleHashOfAddressData [ : 32 ] # Note that this is the first half of the sha512 hash.
tag = doubleHashOfAddressData [ 32 : ]
2014-08-27 09:14:32 +02:00
shared . neededPubkeys [ tag ] = ( toAddress , highlevelcrypto . makeCryptor ( privEncryptionKey . encode ( ' hex ' ) ) ) # We'll need this for when we receive a pubkey reply: it will be encrypted and we'll need to decrypt it.
2013-06-21 23:32:22 +02:00
2014-08-27 09:14:32 +02:00
# Initialize the shared.ackdataForWhichImWatching data structure
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
2013-06-21 23:32:22 +02:00
''' SELECT ackdata FROM sent where (status= ' msgsent ' OR status= ' doingmsgpow ' ) ''' )
for row in queryreturn :
ackdata , = row
print ' Watching for ackdata ' , ackdata . encode ( ' hex ' )
2013-06-24 21:51:01 +02:00
shared . ackdataForWhichImWatching [ ackdata ] = 0
2013-06-21 23:32:22 +02:00
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
2013-06-21 23:32:22 +02:00
''' SELECT DISTINCT toaddress FROM sent WHERE (status= ' doingpubkeypow ' AND folder= ' sent ' ) ''' )
for row in queryreturn :
toaddress , = row
self . requestPubKey ( toaddress )
time . sleep (
10 ) # give some time for the GUI to start before we start on existing POW tasks.
self . sendMsg ( )
# just in case there are any pending tasks for msg
# messages that have yet to be sent.
self . sendBroadcast ( )
# just in case there are any tasks for Broadcasts
# that have yet to be sent.
while True :
command , data = shared . workerQueue . get ( )
if command == ' sendmessage ' :
self . sendMsg ( )
elif command == ' sendbroadcast ' :
self . sendBroadcast ( )
elif command == ' doPOWForMyV2Pubkey ' :
self . doPOWForMyV2Pubkey ( data )
2013-07-22 07:10:22 +02:00
elif command == ' sendOutOrStoreMyV3Pubkey ' :
self . sendOutOrStoreMyV3Pubkey ( data )
2013-09-13 06:27:34 +02:00
elif command == ' sendOutOrStoreMyV4Pubkey ' :
self . sendOutOrStoreMyV4Pubkey ( data )
2013-06-21 23:32:22 +02:00
else :
2013-06-29 19:29:35 +02:00
with shared . printLock :
sys . stderr . write (
' Probable programming error: The command sent to the workerThread is weird. It is: %s \n ' % command )
2013-06-21 23:32:22 +02:00
shared . workerQueue . task_done ( )
def doPOWForMyV2Pubkey ( self , hash ) : # This function also broadcasts out the pubkey message once it is done with the POW
# Look up my stream number based on my address hash
""" configSections = shared.config.sections()
for addressInKeysFile in configSections :
if addressInKeysFile < > ' bitmessagesettings ' :
status , addressVersionNumber , streamNumber , hashFromThisParticularAddress = decodeAddress ( addressInKeysFile )
if hash == hashFromThisParticularAddress :
myAddress = addressInKeysFile
break """
myAddress = shared . myAddressesByHash [ hash ]
status , addressVersionNumber , streamNumber , hash = decodeAddress (
myAddress )
2014-08-27 09:14:32 +02:00
TTL = 28 * 24 * 60 * 60 # 28 days
embeddedTime = int ( time . time ( ) + random . randrange ( - 300 , 300 ) + TTL ) # 28 days from now plus or minus five minutes
payload = pack ( ' >Q ' , ( embeddedTime ) )
payload + = ' \x00 \x00 \x00 \x01 ' # object type: pubkey
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( addressVersionNumber ) # Address version number
payload + = encodeVarint ( streamNumber )
payload + = ' \x00 \x00 \x00 \x01 ' # bitfield of features supported by me (see the wiki).
try :
privSigningKeyBase58 = shared . config . get (
myAddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
myAddress , ' privencryptionkey ' )
except Exception as err :
2013-06-29 19:29:35 +02:00
with shared . printLock :
sys . stderr . write (
' Error within doPOWForMyV2Pubkey. Could not read the keys from the keys.dat file for a requested address. %s \n ' % err )
2013-06-21 23:32:22 +02:00
return
privSigningKeyHex = shared . decodeWalletImportFormat (
privSigningKeyBase58 ) . encode ( ' hex ' )
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubSigningKey = highlevelcrypto . privToPub (
privSigningKeyHex ) . decode ( ' hex ' )
pubEncryptionKey = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' )
payload + = pubSigningKey [ 1 : ]
payload + = pubEncryptionKey [ 1 : ]
# Do the POW for this pubkey message
2014-08-27 09:14:32 +02:00
target = 2 * * 64 / ( shared . networkDefaultProofOfWorkNonceTrialsPerByte * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes + ( ( TTL * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes ) ) / ( 2 * * 16 ) ) ) )
2013-06-21 23:32:22 +02:00
print ' (For pubkey message) Doing proof of work... '
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
print ' (For pubkey message) Found proof of work ' , trialValue , ' Nonce: ' , nonce
payload = pack ( ' >Q ' , nonce ) + payload
inventoryHash = calculateInventoryHash ( payload )
2014-08-27 09:14:32 +02:00
objectType = 1
2013-06-21 23:32:22 +02:00
shared . inventory [ inventoryHash ] = (
2013-09-13 06:27:34 +02:00
objectType , streamNumber , payload , embeddedTime , ' ' )
2013-09-07 00:55:12 +02:00
shared . inventorySets [ streamNumber ] . add ( inventoryHash )
2013-06-21 23:32:22 +02:00
2013-06-29 19:29:35 +02:00
with shared . printLock :
print ' broadcasting inv with hash: ' , inventoryHash . encode ( ' hex ' )
2013-06-21 23:32:22 +02:00
shared . broadcastToSendDataQueues ( (
2013-09-07 00:55:12 +02:00
streamNumber , ' advertiseobject ' , inventoryHash ) )
2013-06-21 23:32:22 +02:00
shared . UISignalQueue . put ( ( ' updateStatusBar ' , ' ' ) )
2013-11-07 05:38:19 +01:00
try :
shared . config . set (
myAddress , ' lastpubkeysendtime ' , str ( int ( time . time ( ) ) ) )
with open ( shared . appdata + ' keys.dat ' , ' wb ' ) as configfile :
shared . config . write ( configfile )
except :
# The user deleted the address out of the keys.dat file before this
# finished.
pass
2013-06-21 23:32:22 +02:00
2013-07-22 07:10:22 +02:00
# If this isn't a chan address, this function assembles the pubkey data,
# does the necessary POW and sends it out. If it *is* a chan then it
# assembles the pubkey and stores is in the pubkey table so that we can
# send messages to "ourselves".
def sendOutOrStoreMyV3Pubkey ( self , hash ) :
2013-11-07 05:38:19 +01:00
try :
myAddress = shared . myAddressesByHash [ hash ]
except :
#The address has been deleted.
return
2013-09-30 01:24:27 +02:00
if shared . safeConfigGetBoolean ( myAddress , ' chan ' ) :
with shared . printLock :
print ' This is a chan address. Not sending pubkey. '
return
2013-06-21 23:32:22 +02:00
status , addressVersionNumber , streamNumber , hash = decodeAddress (
myAddress )
2014-08-27 09:14:32 +02:00
TTL = 28 * 24 * 60 * 60 # 28 days
embeddedTime = int ( time . time ( ) + random . randrange ( - 300 , 300 ) + TTL ) # 28 days from now plus or minus five minutes
signedTimeForProtocolV2 = embeddedTime - TTL
"""
According to the protocol specification , the expiresTime along with the pubkey information is
signed . But to be backwards compatible during the upgrade period , we shall sign not the
expiresTime but rather the current time . There must be precisely a 28 day difference
between the two . After the upgrade period we ' ll switch to signing the whole payload with the
expiresTime time .
"""
payload = pack ( ' >Q ' , ( embeddedTime ) )
payload + = ' \x00 \x00 \x00 \x01 ' # object type: pubkey
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( addressVersionNumber ) # Address version number
payload + = encodeVarint ( streamNumber )
payload + = ' \x00 \x00 \x00 \x01 ' # bitfield of features supported by me (see the wiki).
try :
privSigningKeyBase58 = shared . config . get (
myAddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
myAddress , ' privencryptionkey ' )
except Exception as err :
2013-06-29 19:29:35 +02:00
with shared . printLock :
sys . stderr . write (
2013-07-22 07:10:22 +02:00
' Error within sendOutOrStoreMyV3Pubkey. Could not read the keys from the keys.dat file for a requested address. %s \n ' % err )
2013-06-29 19:29:35 +02:00
2013-06-21 23:32:22 +02:00
return
privSigningKeyHex = shared . decodeWalletImportFormat (
privSigningKeyBase58 ) . encode ( ' hex ' )
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubSigningKey = highlevelcrypto . privToPub (
privSigningKeyHex ) . decode ( ' hex ' )
pubEncryptionKey = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' )
payload + = pubSigningKey [ 1 : ]
payload + = pubEncryptionKey [ 1 : ]
payload + = encodeVarint ( shared . config . getint (
myAddress , ' noncetrialsperbyte ' ) )
payload + = encodeVarint ( shared . config . getint (
myAddress , ' payloadlengthextrabytes ' ) )
2014-08-27 09:14:32 +02:00
if int ( time . time ( ) ) < 1416175200 : # Sun, 16 Nov 2014 22:00:00 GMT
signedData = pack ( ' >Q ' , signedTimeForProtocolV2 ) + payload [ 12 : ]
else :
signedData = payload
signature = highlevelcrypto . sign ( signedData , privSigningKeyHex )
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( len ( signature ) )
payload + = signature
2013-09-30 01:24:27 +02:00
# Do the POW for this pubkey message
2014-08-27 09:14:32 +02:00
target = 2 * * 64 / ( shared . networkDefaultProofOfWorkNonceTrialsPerByte * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes + ( ( TTL * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes ) ) / ( 2 * * 16 ) ) ) )
2013-09-30 01:24:27 +02:00
print ' (For pubkey message) Doing proof of work... '
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
print ' (For pubkey message) Found proof of work ' , trialValue , ' Nonce: ' , nonce
2013-06-21 23:32:22 +02:00
2013-09-30 01:24:27 +02:00
payload = pack ( ' >Q ' , nonce ) + payload
inventoryHash = calculateInventoryHash ( payload )
2014-08-27 09:14:32 +02:00
objectType = 1
2013-09-30 01:24:27 +02:00
shared . inventory [ inventoryHash ] = (
objectType , streamNumber , payload , embeddedTime , ' ' )
shared . inventorySets [ streamNumber ] . add ( inventoryHash )
2013-06-21 23:32:22 +02:00
2013-09-30 01:24:27 +02:00
with shared . printLock :
print ' broadcasting inv with hash: ' , inventoryHash . encode ( ' hex ' )
2013-06-29 19:29:35 +02:00
2013-09-30 01:24:27 +02:00
shared . broadcastToSendDataQueues ( (
streamNumber , ' advertiseobject ' , inventoryHash ) )
shared . UISignalQueue . put ( ( ' updateStatusBar ' , ' ' ) )
2013-11-07 05:38:19 +01:00
try :
shared . config . set (
myAddress , ' lastpubkeysendtime ' , str ( int ( time . time ( ) ) ) )
with open ( shared . appdata + ' keys.dat ' , ' wb ' ) as configfile :
shared . config . write ( configfile )
except :
# The user deleted the address out of the keys.dat file before this
# finished.
pass
2013-06-21 23:32:22 +02:00
2013-09-13 06:27:34 +02:00
# If this isn't a chan address, this function assembles the pubkey data,
2013-09-30 01:24:27 +02:00
# does the necessary POW and sends it out.
2013-09-13 06:27:34 +02:00
def sendOutOrStoreMyV4Pubkey ( self , myAddress ) :
2013-11-07 05:38:19 +01:00
if not shared . config . has_section ( myAddress ) :
#The address has been deleted.
return
2013-09-30 01:24:27 +02:00
if shared . safeConfigGetBoolean ( myAddress , ' chan ' ) :
with shared . printLock :
print ' This is a chan address. Not sending pubkey. '
return
2013-09-13 06:27:34 +02:00
status , addressVersionNumber , streamNumber , hash = decodeAddress (
myAddress )
2014-08-27 09:14:32 +02:00
TTL = 28 * 24 * 60 * 60 # 28 days
embeddedTime = int ( time . time ( ) + random . randrange ( - 300 , 300 ) + TTL ) # 28 days from now plus or minus five minutes
2013-09-13 06:27:34 +02:00
payload = pack ( ' >Q ' , ( embeddedTime ) )
2014-08-27 09:14:32 +02:00
payload + = ' \x00 \x00 \x00 \x01 ' # object type: pubkey
2013-09-13 06:27:34 +02:00
payload + = encodeVarint ( addressVersionNumber ) # Address version number
payload + = encodeVarint ( streamNumber )
dataToEncrypt = ' \x00 \x00 \x00 \x01 ' # bitfield of features supported by me (see the wiki).
try :
privSigningKeyBase58 = shared . config . get (
myAddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
myAddress , ' privencryptionkey ' )
except Exception as err :
with shared . printLock :
sys . stderr . write (
' Error within sendOutOrStoreMyV4Pubkey. Could not read the keys from the keys.dat file for a requested address. %s \n ' % err )
return
privSigningKeyHex = shared . decodeWalletImportFormat (
privSigningKeyBase58 ) . encode ( ' hex ' )
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubSigningKey = highlevelcrypto . privToPub (
privSigningKeyHex ) . decode ( ' hex ' )
pubEncryptionKey = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' )
dataToEncrypt + = pubSigningKey [ 1 : ]
dataToEncrypt + = pubEncryptionKey [ 1 : ]
dataToEncrypt + = encodeVarint ( shared . config . getint (
myAddress , ' noncetrialsperbyte ' ) )
dataToEncrypt + = encodeVarint ( shared . config . getint (
myAddress , ' payloadlengthextrabytes ' ) )
2014-08-27 09:14:32 +02:00
2013-09-18 06:04:01 +02:00
2013-09-13 06:27:34 +02:00
2014-08-27 09:14:32 +02:00
# When we encrypt, we'll use a hash of the data
2013-09-30 01:24:27 +02:00
# contained in an address as a decryption key. This way in order to
# read the public keys in a pubkey message, a node must know the address
# first. We'll also tag, unencrypted, the pubkey with part of the hash
# so that nodes know which pubkey object to try to decrypt when they
# want to send a message.
doubleHashOfAddressData = hashlib . sha512 ( hashlib . sha512 ( encodeVarint (
addressVersionNumber ) + encodeVarint ( streamNumber ) + hash ) . digest ( ) ) . digest ( )
payload + = doubleHashOfAddressData [ 32 : ] # the tag
2014-08-27 09:14:32 +02:00
"""
According to the protocol specification , the expiresTime along with the pubkey information is
signed . But to be backwards compatible during the upgrade period , we shall sign not the
expiresTime but rather the current time . There must be precisely a 28 day difference
between the two . After the upgrade period we ' ll switch to signing the whole payload from
above appended with dataToEncrypt .
"""
if int ( time . time ( ) ) < 1416175200 : # Sun, 16 Nov 2014 22:00:00 GMT
dataToSign = pack ( ' >Q ' , ( embeddedTime - TTL ) )
dataToSign + = encodeVarint ( addressVersionNumber ) # Address version number
dataToSign + = encodeVarint ( streamNumber )
dataToSign + = dataToEncrypt
else :
dataToSign = payload + dataToEncrypt
signature = highlevelcrypto . sign ( dataToSign , privSigningKeyHex )
dataToEncrypt + = encodeVarint ( len ( signature ) )
dataToEncrypt + = signature
2013-09-30 01:24:27 +02:00
privEncryptionKey = doubleHashOfAddressData [ : 32 ]
2014-05-21 12:15:07 +02:00
pubEncryptionKey = highlevelcrypto . pointMult ( privEncryptionKey )
2013-09-30 01:24:27 +02:00
payload + = highlevelcrypto . encrypt (
dataToEncrypt , pubEncryptionKey . encode ( ' hex ' ) )
2013-09-18 06:04:01 +02:00
2013-09-30 01:24:27 +02:00
# Do the POW for this pubkey message
2014-08-27 09:14:32 +02:00
target = 2 * * 64 / ( shared . networkDefaultProofOfWorkNonceTrialsPerByte * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes + ( ( TTL * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes ) ) / ( 2 * * 16 ) ) ) )
2013-09-30 01:24:27 +02:00
print ' (For pubkey message) Doing proof of work... '
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
print ' (For pubkey message) Found proof of work ' , trialValue , ' Nonce: ' , nonce
2013-09-13 06:27:34 +02:00
2013-09-30 01:24:27 +02:00
payload = pack ( ' >Q ' , nonce ) + payload
inventoryHash = calculateInventoryHash ( payload )
2014-08-27 09:14:32 +02:00
objectType = 1
2013-09-30 01:24:27 +02:00
shared . inventory [ inventoryHash ] = (
objectType , streamNumber , payload , embeddedTime , doubleHashOfAddressData [ 32 : ] )
shared . inventorySets [ streamNumber ] . add ( inventoryHash )
2013-09-13 06:27:34 +02:00
2013-09-30 01:24:27 +02:00
with shared . printLock :
print ' broadcasting inv with hash: ' , inventoryHash . encode ( ' hex ' )
2013-09-18 06:04:01 +02:00
2013-09-30 01:24:27 +02:00
shared . broadcastToSendDataQueues ( (
streamNumber , ' advertiseobject ' , inventoryHash ) )
shared . UISignalQueue . put ( ( ' updateStatusBar ' , ' ' ) )
2013-10-26 01:35:59 +02:00
try :
shared . config . set (
myAddress , ' lastpubkeysendtime ' , str ( int ( time . time ( ) ) ) )
with open ( shared . appdata + ' keys.dat ' , ' wb ' ) as configfile :
shared . config . write ( configfile )
2013-12-06 07:52:19 +01:00
except Exception as err :
logger . error ( ' Error: Couldn \' t add the lastpubkeysendtime to the keys.dat file. Error message: %s ' % err )
2013-09-13 06:27:34 +02:00
2013-06-21 23:32:22 +02:00
def sendBroadcast ( self ) :
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
''' SELECT fromaddress, subject, message, ackdata FROM sent WHERE status=? and folder= ' sent ' ''' , ' broadcastqueued ' )
2013-06-21 23:32:22 +02:00
for row in queryreturn :
fromaddress , subject , body , ackdata = row
status , addressVersionNumber , streamNumber , ripe = decodeAddress (
fromaddress )
2013-07-31 18:36:51 +02:00
if addressVersionNumber < = 1 :
2013-06-29 19:29:35 +02:00
with shared . printLock :
sys . stderr . write (
' Error: In the singleWorker thread, the sendBroadcast function doesn \' t understand the address version. \n ' )
2013-07-31 18:36:51 +02:00
return
# We need to convert our private keys to public keys in order
# to include them.
try :
privSigningKeyBase58 = shared . config . get (
fromaddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
fromaddress , ' privencryptionkey ' )
except :
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
ackdata , tr . translateText ( " MainWindow " , " Error! Could not find sender address (your address) in the keys.dat file. " ) ) ) )
continue
privSigningKeyHex = shared . decodeWalletImportFormat (
privSigningKeyBase58 ) . encode ( ' hex ' )
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubSigningKey = highlevelcrypto . privToPub ( privSigningKeyHex ) . decode (
' hex ' ) # At this time these pubkeys are 65 bytes long because they include the encoding byte which we won't be sending in the broadcast message.
pubEncryptionKey = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' )
2014-08-27 09:14:32 +02:00
TTL = 2.5 * 24 * 60 * 60 # 2.5 days
embeddedTime = int ( time . time ( ) + random . randrange ( - 300 , 300 ) + TTL )
payload = pack ( ' >Q ' , embeddedTime )
payload + = ' \x00 \x00 \x00 \x03 ' # object type: broadcast
if int ( time . time ( ) ) < 1416175200 : # Before Sun, 16 Nov 2014 22:00:00 GMT
if addressVersionNumber < = 3 :
payload + = encodeVarint ( 2 ) # broadcast version
else :
payload + = encodeVarint ( 3 ) # broadcast version
else : # After Sun, 16 Nov 2014 22:00:00 GMT
if addressVersionNumber < = 3 :
payload + = encodeVarint ( 4 ) # broadcast version
else :
payload + = encodeVarint ( 5 ) # broadcast version
2013-07-31 18:36:51 +02:00
payload + = encodeVarint ( streamNumber )
2013-09-15 03:06:26 +02:00
if addressVersionNumber > = 4 :
doubleHashOfAddressData = hashlib . sha512 ( hashlib . sha512 ( encodeVarint (
addressVersionNumber ) + encodeVarint ( streamNumber ) + ripe ) . digest ( ) ) . digest ( )
2013-12-01 06:45:37 +01:00
tag = doubleHashOfAddressData [ 32 : ]
payload + = tag
else :
tag = ' '
2013-07-31 18:36:51 +02:00
2014-08-27 09:14:32 +02:00
dataToEncrypt = " "
# the broadcast version is not included here after the end of the protocol v3 upgrade period
if int ( time . time ( ) ) < 1416175200 : # Sun, 16 Nov 2014 22:00:00 GMT
if addressVersionNumber < = 3 :
dataToEncrypt + = encodeVarint ( 2 ) # broadcast version
else :
dataToEncrypt + = encodeVarint ( 3 ) # broadcast version
2013-07-31 18:36:51 +02:00
dataToEncrypt + = encodeVarint ( addressVersionNumber )
dataToEncrypt + = encodeVarint ( streamNumber )
dataToEncrypt + = ' \x00 \x00 \x00 \x01 ' # behavior bitfield
dataToEncrypt + = pubSigningKey [ 1 : ]
dataToEncrypt + = pubEncryptionKey [ 1 : ]
if addressVersionNumber > = 3 :
dataToEncrypt + = encodeVarint ( shared . config . getint ( fromaddress , ' noncetrialsperbyte ' ) )
dataToEncrypt + = encodeVarint ( shared . config . getint ( fromaddress , ' payloadlengthextrabytes ' ) )
dataToEncrypt + = ' \x02 ' # message encoding type
dataToEncrypt + = encodeVarint ( len ( ' Subject: ' + subject + ' \n ' + ' Body: ' + body ) ) #Type 2 is simple UTF-8 message encoding per the documentation on the wiki.
dataToEncrypt + = ' Subject: ' + subject + ' \n ' + ' Body: ' + body
2014-08-27 09:14:32 +02:00
if int ( time . time ( ) ) < 1416175200 : # Sun, 16 Nov 2014 22:00:00 GMT
dataToSign = dataToEncrypt
else :
dataToSign = payload + dataToEncrypt
2013-07-31 18:36:51 +02:00
signature = highlevelcrypto . sign (
2014-08-27 09:14:32 +02:00
dataToSign , privSigningKeyHex )
2013-07-31 18:36:51 +02:00
dataToEncrypt + = encodeVarint ( len ( signature ) )
dataToEncrypt + = signature
# Encrypt the broadcast with the information contained in the broadcaster's address. Anyone who knows the address can generate
# the private encryption key to decrypt the broadcast. This provides virtually no privacy; its purpose is to keep questionable
# and illegal content from flowing through the Internet connections and being stored on the disk of 3rd parties.
2013-09-15 03:06:26 +02:00
if addressVersionNumber < = 3 :
privEncryptionKey = hashlib . sha512 ( encodeVarint (
addressVersionNumber ) + encodeVarint ( streamNumber ) + ripe ) . digest ( ) [ : 32 ]
else :
privEncryptionKey = doubleHashOfAddressData [ : 32 ]
2013-12-01 06:45:37 +01:00
2014-05-21 12:15:07 +02:00
pubEncryptionKey = highlevelcrypto . pointMult ( privEncryptionKey )
2013-07-31 18:36:51 +02:00
payload + = highlevelcrypto . encrypt (
dataToEncrypt , pubEncryptionKey . encode ( ' hex ' ) )
2014-08-27 09:14:32 +02:00
target = 2 * * 64 / ( shared . networkDefaultProofOfWorkNonceTrialsPerByte * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes + ( ( TTL * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes ) ) / ( 2 * * 16 ) ) ) )
2013-07-31 18:36:51 +02:00
print ' (For broadcast message) Doing proof of work... '
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
ackdata , tr . translateText ( " MainWindow " , " Doing work necessary to send broadcast... " ) ) ) )
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
print ' (For broadcast message) Found proof of work ' , trialValue , ' Nonce: ' , nonce
payload = pack ( ' >Q ' , nonce ) + payload
2014-08-27 09:14:32 +02:00
# Sanity check. The payload size should never be larger than 256 KiB. There should
# be checks elsewhere in the code to not let the user try to send a message this large
# until we implement message continuation.
if len ( payload ) > 2 * * 18 : # 256 KiB
logger . critical ( ' This broadcast object is too large to send. This should never happen. Object size: %s ' % len ( payload ) )
continue
2013-06-29 19:29:35 +02:00
2013-07-31 18:36:51 +02:00
inventoryHash = calculateInventoryHash ( payload )
2014-08-27 09:14:32 +02:00
objectType = 3
2013-07-31 18:36:51 +02:00
shared . inventory [ inventoryHash ] = (
2014-08-27 09:14:32 +02:00
objectType , streamNumber , payload , embeddedTime , tag )
2013-09-07 00:55:12 +02:00
shared . inventorySets [ streamNumber ] . add ( inventoryHash )
2013-07-31 18:36:51 +02:00
with shared . printLock :
print ' sending inv (within sendBroadcast function) for object: ' , inventoryHash . encode ( ' hex ' )
shared . broadcastToSendDataQueues ( (
2013-09-07 00:55:12 +02:00
streamNumber , ' advertiseobject ' , inventoryHash ) )
2013-07-31 18:36:51 +02:00
2014-08-06 04:01:01 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , " Broadcast sent on % 1 " ) . arg ( l10n . formatTimestamp ( ) ) ) ) )
2013-07-31 18:36:51 +02:00
# Update the status of the message in the 'sent' table to have
# a 'broadcastsent' status
2013-08-29 13:27:30 +02:00
sqlExecute (
' UPDATE sent SET msgid=?, status=?, lastactiontime=? WHERE ackdata=? ' ,
inventoryHash ,
' broadcastsent ' ,
int ( time . time ( ) ) ,
ackdata )
2013-07-31 18:36:51 +02:00
2013-06-21 23:32:22 +02:00
def sendMsg ( self ) :
2014-08-27 09:14:32 +02:00
while True : # while we have a msg that needs some work
# Select just one msg that needs work. We'll get a msg
# which is ready to be sent or a msg which we have sent in
# the last 28 days which were previously marked
# as 'toodifficult'. If the user has raised the maximum acceptable
# difficulty then those msgs may now be sendable.
2013-08-29 13:27:30 +02:00
queryreturn = sqlQuery (
2014-08-27 09:14:32 +02:00
''' SELECT toaddress, toripe, fromaddress, subject, message, ackdata, status FROM sent WHERE (status= ' msgqueued ' or status= ' doingmsgpow ' or status= ' forcepow ' or (status= ' toodifficult ' and lastactiontime>?)) and folder= ' sent ' LIMIT 1 ''' ,
int ( time . time ( ) ) - 2419200 )
if len ( queryreturn ) == 0 : # if there is no work to do then
break # break out of this sendMsg loop and
# wait for something to get put in the shared.workerQueue.
row = queryreturn [ 0 ]
toaddress , toripe , fromaddress , subject , message , ackdata , status = row
toStatus , toAddressVersionNumber , toStreamNumber , toRipe = decodeAddress (
toaddress )
fromStatus , fromAddressVersionNumber , fromStreamNumber , fromRipe = decodeAddress (
fromaddress )
# We may or may not already have the pubkey for this toAddress. Let's check.
if status == ' forcepow ' :
# if the status of this msg is 'forcepow' then clearly we have the pubkey already
# because the user could not have overridden the message about the POW being
# too difficult without knowing the required difficulty.
pass
# If we are sending a message to ourselves or a chan then we won't need an entry in the pubkeys table; we can calculate the needed pubkey using the private keys in our keys.dat file.
elif shared . config . has_section ( toaddress ) :
2013-08-29 13:27:30 +02:00
sqlExecute (
''' UPDATE sent SET status= ' doingmsgpow ' WHERE toaddress=? AND status= ' msgqueued ' ''' ,
toaddress )
2014-08-27 09:14:32 +02:00
status = ' doingmsgpow '
else :
# Let's see if we already have the pubkey in our pubkeys table
queryreturn = sqlQuery (
''' SELECT hash FROM pubkeys WHERE hash=? AND addressversion=? ''' , toRipe , toAddressVersionNumber )
if queryreturn != [ ] : # If we have the needed pubkey in the pubkey table already,
# set the status of this msg to doingmsgpow
2013-08-29 13:27:30 +02:00
sqlExecute (
2014-08-27 09:14:32 +02:00
''' UPDATE sent SET status= ' doingmsgpow ' WHERE toaddress=? AND status= ' msgqueued ' ''' ,
toaddress )
status = ' doingmsgpow '
# mark the pubkey as 'usedpersonally' so that we don't delete it later
sqlExecute (
''' UPDATE pubkeys SET usedpersonally= ' yes ' WHERE hash=? and addressversion=? ''' ,
toRipe ,
toAddressVersionNumber )
else : # We don't have the needed pubkey in the pubkeys table already.
if toAddressVersionNumber < = 3 :
toTag = ' '
else :
toTag = hashlib . sha512 ( hashlib . sha512 ( encodeVarint ( toAddressVersionNumber ) + encodeVarint ( toStreamNumber ) + toRipe ) . digest ( ) ) . digest ( ) [ 32 : ]
if toRipe in shared . neededPubkeys or toTag in shared . neededPubkeys :
# We already sent a request for the pubkey
2013-09-15 03:06:26 +02:00
sqlExecute (
2014-08-27 09:14:32 +02:00
''' UPDATE sent SET status= ' awaitingpubkey ' WHERE toaddress=? AND status= ' msgqueued ' ''' , toaddress )
2013-09-15 03:06:26 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByHash ' , (
2014-08-27 09:14:32 +02:00
toRipe , tr . translateText ( " MainWindow " , ' Encryption key was requested earlier. ' ) ) ) )
continue #on with the next msg on which we can do some work
else :
# We have not yet sent a request for the pubkey
needToRequestPubkey = True
if toAddressVersionNumber > = 4 : # If we are trying to send to address version >= 4 then
# the needed pubkey might be encrypted in the inventory.
# If we have it we'll need to decrypt it and put it in
# the pubkeys table.
# The decryptAndCheckPubkeyPayload function expects that the shared.neededPubkeys
# dictionary already contains the toAddress and cryptor object associated with
# the tag for this toAddress.
doubleHashOfToAddressData = hashlib . sha512 ( hashlib . sha512 ( encodeVarint (
toAddressVersionNumber ) + encodeVarint ( toStreamNumber ) + toRipe ) . digest ( ) ) . digest ( )
privEncryptionKey = doubleHashOfToAddressData [ : 32 ] # The first half of the sha512 hash.
tag = doubleHashOfToAddressData [ 32 : ] # The second half of the sha512 hash.
shared . neededPubkeys [ tag ] = ( toaddress , highlevelcrypto . makeCryptor ( privEncryptionKey . encode ( ' hex ' ) ) )
queryreturn = sqlQuery (
''' SELECT payload FROM inventory WHERE objecttype=1 and tag=? ''' , toTag )
if queryreturn != [ ] : # if there are any pubkeys in our inventory with the correct tag..
for row in queryreturn :
payload , = row
if shared . decryptAndCheckPubkeyPayload ( payload , toaddress ) == ' successful ' :
needToRequestPubkey = False
sqlExecute (
''' UPDATE sent SET status= ' doingmsgpow ' WHERE toaddress=? AND (status= ' msgqueued ' or status= ' awaitingpubkey ' or status= ' doingpubkeypow ' ) ''' ,
toaddress )
del shared . neededPubkeys [ tag ]
continue # We'll start back at the beginning, pick up this msg, mark the pubkey as 'usedpersonally', and then send the msg.
#else: # There was something wrong with this pubkey object even
# though it had the correct tag- almost certainly because
# of malicious behavior or a badly programmed client. If
# there are any other pubkeys in our inventory with the correct
# tag then we'll try to decrypt those.
if needToRequestPubkey : # Obviously we had no success looking in the sql inventory. Let's look through the memory inventory.
with shared . inventoryLock :
for hash , storedValue in shared . inventory . items ( ) :
objectType , streamNumber , payload , expiresTime , tag = storedValue
if objectType == 1 and tag == toTag :
if shared . decryptAndCheckPubkeyPayload ( payload , toaddress ) == ' successful ' : #if valid, this function also puts it in the pubkeys table.
needToRequestPubkey = False
sqlExecute (
''' UPDATE sent SET status= ' doingmsgpow ' WHERE toaddress=? AND (status= ' msgqueued ' or status= ' awaitingpubkey ' or status= ' doingpubkeypow ' ) ''' ,
toaddress )
del shared . neededPubkeys [ tag ]
continue # We'll start back at the beginning, pick up this msg, mark the pubkey as 'usedpersonally', and then send the msg.
if needToRequestPubkey :
sqlExecute (
''' UPDATE sent SET status= ' doingpubkeypow ' WHERE toaddress=? AND status= ' msgqueued ' ''' ,
toaddress )
shared . UISignalQueue . put ( ( ' updateSentItemStatusByHash ' , (
toRipe , tr . translateText ( " MainWindow " , ' Sending a request for the recipient \' s encryption key. ' ) ) ) )
self . requestPubKey ( toaddress )
continue #on with the next msg on which we can do some work
# At this point we know that we have the necessary pubkey in the pubkeys table.
TTL = 2.5 * 24 * 60 * 60 # 2.5 days
embeddedTime = int ( time . time ( ) + random . randrange ( - 300 , 300 ) + TTL ) # 2.5 days from now plus or minus five minutes
if not shared . config . has_section ( toaddress ) : # if we aren't sending this to ourselves or a chan
2013-09-30 01:24:27 +02:00
shared . ackdataForWhichImWatching [ ackdata ] = 0
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
ackdata , tr . translateText ( " MainWindow " , " Looking up the receiver \' s public key " ) ) ) )
2013-06-29 19:29:35 +02:00
with shared . printLock :
2013-09-30 01:24:27 +02:00
print ' Sending a message. First 150 characters of message: ' , repr ( message [ : 150 ] )
2013-06-29 19:29:35 +02:00
2013-09-30 01:24:27 +02:00
# Let us fetch the recipient's public key out of our database. If
# the required proof of work difficulty is too hard then we'll
# abort.
queryreturn = sqlQuery (
' SELECT transmitdata FROM pubkeys WHERE hash=? and addressversion=? ' ,
2014-08-27 09:14:32 +02:00
toRipe ,
2013-09-30 01:24:27 +02:00
toAddressVersionNumber )
for row in queryreturn :
pubkeyPayload , = row
# The pubkey message is stored the way we originally received it
2014-08-27 09:14:32 +02:00
# under protocol version 2
2013-09-30 01:24:27 +02:00
# which means that we need to read beyond things like the nonce and
# time to get to the actual public keys.
if toAddressVersionNumber < = 3 :
readPosition = 8 # to bypass the nonce
elif toAddressVersionNumber > = 4 :
readPosition = 0 # the nonce is not included here so we don't need to skip over it.
2013-06-21 23:32:22 +02:00
pubkeyEmbeddedTime , = unpack (
2013-09-30 01:24:27 +02:00
' >I ' , pubkeyPayload [ readPosition : readPosition + 4 ] )
# This section is used for the transition from 32 bit time to 64
# bit time in the protocol.
if pubkeyEmbeddedTime == 0 :
pubkeyEmbeddedTime , = unpack (
' >Q ' , pubkeyPayload [ readPosition : readPosition + 8 ] )
readPosition + = 8
else :
readPosition + = 4
readPosition + = 1 # to bypass the address version whose length is definitely 1
streamNumber , streamNumberLength = decodeVarint (
2013-06-21 23:32:22 +02:00
pubkeyPayload [ readPosition : readPosition + 10 ] )
2013-09-30 01:24:27 +02:00
readPosition + = streamNumberLength
behaviorBitfield = pubkeyPayload [ readPosition : readPosition + 4 ]
# Mobile users may ask us to include their address's RIPE hash on a message
# unencrypted. Before we actually do it the sending human must check a box
# in the settings menu to allow it.
if shared . isBitSetWithinBitfield ( behaviorBitfield , 30 ) : # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message..
if not shared . safeConfigGetBoolean ( ' bitmessagesettings ' , ' willinglysendtomobile ' ) : # if we are Not willing to include the receiver's RIPE hash on the message..
logger . info ( ' The receiver is a mobile user but the sender (you) has not selected that you are willing to send to mobiles. Aborting send. ' )
2014-08-06 04:01:01 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , ' Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. % 1 ' ) . arg ( l10n . formatTimestamp ( ) ) ) ) )
2013-09-30 01:24:27 +02:00
# if the human changes their setting and then sends another message or restarts their client, this one will send at that time.
continue
readPosition + = 4 # to bypass the bitfield of behaviors
# pubSigningKeyBase256 =
# pubkeyPayload[readPosition:readPosition+64] #We don't use this
# key for anything here.
readPosition + = 64
pubEncryptionKeyBase256 = pubkeyPayload [
readPosition : readPosition + 64 ]
readPosition + = 64
# Let us fetch the amount of work required by the recipient.
if toAddressVersionNumber == 2 :
2013-06-21 23:32:22 +02:00
requiredAverageProofOfWorkNonceTrialsPerByte = shared . networkDefaultProofOfWorkNonceTrialsPerByte
requiredPayloadLengthExtraBytes = shared . networkDefaultPayloadLengthExtraBytes
2013-09-30 01:24:27 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
ackdata , tr . translateText ( " MainWindow " , " Doing work necessary to send message. \n There is no required difficulty for version 2 addresses like this. " ) ) ) )
elif toAddressVersionNumber > = 3 :
requiredAverageProofOfWorkNonceTrialsPerByte , varintLength = decodeVarint (
pubkeyPayload [ readPosition : readPosition + 10 ] )
readPosition + = varintLength
requiredPayloadLengthExtraBytes , varintLength = decodeVarint (
pubkeyPayload [ readPosition : readPosition + 10 ] )
readPosition + = varintLength
if requiredAverageProofOfWorkNonceTrialsPerByte < shared . networkDefaultProofOfWorkNonceTrialsPerByte : # We still have to meet a minimum POW difficulty regardless of what they say is allowed in order to get our message to propagate through the network.
requiredAverageProofOfWorkNonceTrialsPerByte = shared . networkDefaultProofOfWorkNonceTrialsPerByte
if requiredPayloadLengthExtraBytes < shared . networkDefaultPayloadLengthExtraBytes :
requiredPayloadLengthExtraBytes = shared . networkDefaultPayloadLengthExtraBytes
2014-08-27 09:14:32 +02:00
logger . debug ( ' Using averageProofOfWorkNonceTrialsPerByte: %s and payloadLengthExtraBytes: %s . ' % ( requiredAverageProofOfWorkNonceTrialsPerByte , requiredPayloadLengthExtraBytes ) )
2013-09-30 01:24:27 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , " Doing work necessary to send message. \n Receiver \' s required difficulty: % 1 and % 2 " ) . arg ( str ( float (
requiredAverageProofOfWorkNonceTrialsPerByte ) / shared . networkDefaultProofOfWorkNonceTrialsPerByte ) ) . arg ( str ( float ( requiredPayloadLengthExtraBytes ) / shared . networkDefaultPayloadLengthExtraBytes ) ) ) ) )
if status != ' forcepow ' :
if ( requiredAverageProofOfWorkNonceTrialsPerByte > shared . config . getint ( ' bitmessagesettings ' , ' maxacceptablenoncetrialsperbyte ' ) and shared . config . getint ( ' bitmessagesettings ' , ' maxacceptablenoncetrialsperbyte ' ) != 0 ) or ( requiredPayloadLengthExtraBytes > shared . config . getint ( ' bitmessagesettings ' , ' maxacceptablepayloadlengthextrabytes ' ) and shared . config . getint ( ' bitmessagesettings ' , ' maxacceptablepayloadlengthextrabytes ' ) != 0 ) :
# The demanded difficulty is more than we are willing
# to do.
sqlExecute (
''' UPDATE sent SET status= ' toodifficult ' WHERE ackdata=? ''' ,
ackdata )
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , " Problem: The work demanded by the recipient ( % 1 and % 2) is more difficult than you are willing to do. " ) . arg ( str ( float ( requiredAverageProofOfWorkNonceTrialsPerByte ) / shared . networkDefaultProofOfWorkNonceTrialsPerByte ) ) . arg ( str ( float (
2014-08-06 04:01:01 +02:00
requiredPayloadLengthExtraBytes ) / shared . networkDefaultPayloadLengthExtraBytes ) ) . arg ( l10n . formatTimestamp ( ) ) ) ) )
2013-09-30 01:24:27 +02:00
continue
else : # if we are sending a message to ourselves or a chan..
with shared . printLock :
print ' Sending a message. First 150 characters of message: ' , repr ( message [ : 150 ] )
behaviorBitfield = ' \x00 \x00 \x00 \x01 '
2013-06-21 23:32:22 +02:00
2013-09-30 01:24:27 +02:00
try :
privEncryptionKeyBase58 = shared . config . get (
toaddress , ' privencryptionkey ' )
except Exception as err :
2014-08-06 04:01:01 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , ' Problem: You are trying to send a message to yourself or a chan but your encryption key could not be found in the keys.dat file. Could not encrypt message. % 1 ' ) . arg ( l10n . formatTimestamp ( ) ) ) ) )
2013-09-30 01:24:27 +02:00
with shared . printLock :
sys . stderr . write (
' Error within sendMsg. Could not read the keys from the keys.dat file for our own address. %s \n ' % err )
continue
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubEncryptionKeyBase256 = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' ) [ 1 : ]
requiredAverageProofOfWorkNonceTrialsPerByte = shared . networkDefaultProofOfWorkNonceTrialsPerByte
requiredPayloadLengthExtraBytes = shared . networkDefaultPayloadLengthExtraBytes
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
ackdata , tr . translateText ( " MainWindow " , " Doing work necessary to send message. " ) ) ) )
2013-08-26 21:23:12 +02:00
2013-06-21 23:32:22 +02:00
if fromAddressVersionNumber == 2 :
2014-08-27 09:14:32 +02:00
payload = " "
if int ( time . time ( ) ) < 1416175200 : # Sun, 16 Nov 2014 22:00:00 GMT
payload + = ' \x01 ' # Message version.
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( fromAddressVersionNumber )
payload + = encodeVarint ( fromStreamNumber )
payload + = ' \x00 \x00 \x00 \x01 ' # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features )
# We need to convert our private keys to public keys in order
# to include them.
try :
privSigningKeyBase58 = shared . config . get (
fromaddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
fromaddress , ' privencryptionkey ' )
except :
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
2013-06-24 21:51:01 +02:00
ackdata , tr . translateText ( " MainWindow " , " Error! Could not find sender address (your address) in the keys.dat file. " ) ) ) )
2013-06-21 23:32:22 +02:00
continue
privSigningKeyHex = shared . decodeWalletImportFormat (
privSigningKeyBase58 ) . encode ( ' hex ' )
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubSigningKey = highlevelcrypto . privToPub (
privSigningKeyHex ) . decode ( ' hex ' )
pubEncryptionKey = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' )
payload + = pubSigningKey [
1 : ] # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
payload + = pubEncryptionKey [ 1 : ]
2014-08-27 09:14:32 +02:00
payload + = toRipe # This hash will be checked by the receiver of the message to verify that toRipe belongs to them. This prevents a Surreptitious Forwarding Attack.
2013-06-21 23:32:22 +02:00
payload + = ' \x02 ' # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
messageToTransmit = ' Subject: ' + \
subject + ' \n ' + ' Body: ' + message
payload + = encodeVarint ( len ( messageToTransmit ) )
payload + = messageToTransmit
fullAckPayload = self . generateFullAckMessage (
2013-12-06 07:52:19 +01:00
ackdata , toStreamNumber ) # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( len ( fullAckPayload ) )
payload + = fullAckPayload
2014-08-27 09:14:32 +02:00
if int ( time . time ( ) ) < 1416175200 : # Sun, 16 Nov 2014 22:00:00 GMT
dataToSign = payload
else :
2014-10-02 01:55:23 +02:00
dataToSign = pack ( ' >Q ' , embeddedTime ) + ' \x00 \x00 \x00 \x02 ' + encodeVarint ( 1 ) + encodeVarint ( toStreamNumber ) + payload
2014-08-27 09:14:32 +02:00
signature = highlevelcrypto . sign ( dataToSign , privSigningKeyHex )
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( len ( signature ) )
payload + = signature
2013-09-15 03:06:26 +02:00
if fromAddressVersionNumber > = 3 :
2014-08-27 09:14:32 +02:00
payload = " "
if int ( time . time ( ) ) < 1416175200 : # Sun, 16 Nov 2014 22:00:00 GMT
payload + = ' \x01 ' # Message version.
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( fromAddressVersionNumber )
payload + = encodeVarint ( fromStreamNumber )
payload + = ' \x00 \x00 \x00 \x01 ' # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features )
# We need to convert our private keys to public keys in order
# to include them.
try :
privSigningKeyBase58 = shared . config . get (
fromaddress , ' privsigningkey ' )
privEncryptionKeyBase58 = shared . config . get (
fromaddress , ' privencryptionkey ' )
except :
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , (
2013-06-24 21:51:01 +02:00
ackdata , tr . translateText ( " MainWindow " , " Error! Could not find sender address (your address) in the keys.dat file. " ) ) ) )
2013-06-21 23:32:22 +02:00
continue
privSigningKeyHex = shared . decodeWalletImportFormat (
privSigningKeyBase58 ) . encode ( ' hex ' )
privEncryptionKeyHex = shared . decodeWalletImportFormat (
privEncryptionKeyBase58 ) . encode ( ' hex ' )
pubSigningKey = highlevelcrypto . privToPub (
privSigningKeyHex ) . decode ( ' hex ' )
pubEncryptionKey = highlevelcrypto . privToPub (
privEncryptionKeyHex ) . decode ( ' hex ' )
payload + = pubSigningKey [
1 : ] # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
payload + = pubEncryptionKey [ 1 : ]
# If the receiver of our message is in our address book,
# subscriptions list, or whitelist then we will allow them to
# do the network-minimum proof of work. Let us check to see if
# the receiver is in any of those lists.
if shared . isAddressInMyAddressBookSubscriptionsListOrWhitelist ( toaddress ) :
payload + = encodeVarint (
shared . networkDefaultProofOfWorkNonceTrialsPerByte )
payload + = encodeVarint (
shared . networkDefaultPayloadLengthExtraBytes )
else :
payload + = encodeVarint ( shared . config . getint (
fromaddress , ' noncetrialsperbyte ' ) )
payload + = encodeVarint ( shared . config . getint (
fromaddress , ' payloadlengthextrabytes ' ) )
2014-08-27 09:14:32 +02:00
payload + = toRipe # This hash will be checked by the receiver of the message to verify that toRipe belongs to them. This prevents a Surreptitious Forwarding Attack.
2013-06-21 23:32:22 +02:00
payload + = ' \x02 ' # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
messageToTransmit = ' Subject: ' + \
subject + ' \n ' + ' Body: ' + message
payload + = encodeVarint ( len ( messageToTransmit ) )
payload + = messageToTransmit
2013-09-30 05:01:56 +02:00
if shared . config . has_section ( toaddress ) :
2013-07-22 07:10:22 +02:00
with shared . printLock :
2013-09-30 05:01:56 +02:00
print ' Not bothering to include ackdata because we are sending to ourselves or a chan. '
2013-07-22 07:10:22 +02:00
fullAckPayload = ' '
2013-08-08 21:37:48 +02:00
elif not shared . isBitSetWithinBitfield ( behaviorBitfield , 31 ) :
with shared . printLock :
2013-09-30 01:24:27 +02:00
print ' Not bothering to include ackdata because the receiver said that they won \' t relay it anyway. '
2013-08-08 21:37:48 +02:00
fullAckPayload = ' '
2013-07-22 07:10:22 +02:00
else :
fullAckPayload = self . generateFullAckMessage (
2013-12-06 07:52:19 +01:00
ackdata , toStreamNumber ) # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out.
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( len ( fullAckPayload ) )
payload + = fullAckPayload
2014-08-27 09:14:32 +02:00
if int ( time . time ( ) ) < 1416175200 : # Sun, 16 Nov 2014 22:00:00 GMT
dataToSign = payload
else :
dataToSign = pack ( ' >Q ' , embeddedTime ) + ' \x00 \x00 \x00 \x02 ' + encodeVarint ( 1 ) + encodeVarint ( toStreamNumber ) + payload
signature = highlevelcrypto . sign ( dataToSign , privSigningKeyHex )
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( len ( signature ) )
payload + = signature
2013-09-18 06:04:01 +02:00
print ' using pubEncryptionKey: ' , pubEncryptionKeyBase256 . encode ( ' hex ' )
2013-06-21 23:32:22 +02:00
# We have assembled the data that will be encrypted.
try :
encrypted = highlevelcrypto . encrypt ( payload , " 04 " + pubEncryptionKeyBase256 . encode ( ' hex ' ) )
except :
2013-08-29 13:27:30 +02:00
sqlExecute ( ''' UPDATE sent SET status= ' badkey ' WHERE ackdata=? ''' , ackdata )
2014-08-06 04:01:01 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , ' Problem: The recipient \' s encryption key is no good. Could not encrypt message. % 1 ' ) . arg ( l10n . formatTimestamp ( ) ) ) ) )
2013-06-21 23:32:22 +02:00
continue
2014-08-27 09:14:32 +02:00
encryptedPayload = pack ( ' >Q ' , embeddedTime )
encryptedPayload + = ' \x00 \x00 \x00 \x02 ' # object type: msg
if int ( time . time ( ) ) > = 1416175200 : # Sun, 16 Nov 2014 22:00:00 GMT
encryptedPayload + = encodeVarint ( 1 ) # msg version
encryptedPayload + = encodeVarint ( toStreamNumber ) + encrypted
target = 2 * * 64 / ( requiredAverageProofOfWorkNonceTrialsPerByte * ( len ( encryptedPayload ) + 8 + requiredPayloadLengthExtraBytes + ( ( TTL * ( len ( encryptedPayload ) + 8 + requiredPayloadLengthExtraBytes ) ) / ( 2 * * 16 ) ) ) )
2013-06-29 19:29:35 +02:00
with shared . printLock :
print ' (For msg message) Doing proof of work. Total required difficulty: ' , float ( requiredAverageProofOfWorkNonceTrialsPerByte ) / shared . networkDefaultProofOfWorkNonceTrialsPerByte , ' Required small message difficulty: ' , float ( requiredPayloadLengthExtraBytes ) / shared . networkDefaultPayloadLengthExtraBytes
2013-06-21 23:32:22 +02:00
powStartTime = time . time ( )
initialHash = hashlib . sha512 ( encryptedPayload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
2013-06-29 19:29:35 +02:00
with shared . printLock :
print ' (For msg message) Found proof of work ' , trialValue , ' Nonce: ' , nonce
try :
print ' POW took ' , int ( time . time ( ) - powStartTime ) , ' seconds. ' , nonce / ( time . time ( ) - powStartTime ) , ' nonce trials per second. '
except :
pass
2013-06-21 23:32:22 +02:00
encryptedPayload = pack ( ' >Q ' , nonce ) + encryptedPayload
2014-08-27 09:14:32 +02:00
# Sanity check. The encryptedPayload size should never be larger than 256 KiB. There should
# be checks elsewhere in the code to not let the user try to send a message this large
# until we implement message continuation.
if len ( encryptedPayload ) > 2 * * 18 : # 256 KiB
logger . critical ( ' This msg object is too large to send. This should never happen. Object size: %s ' % len ( encryptedPayload ) )
continue
2013-06-21 23:32:22 +02:00
inventoryHash = calculateInventoryHash ( encryptedPayload )
2014-08-27 09:14:32 +02:00
objectType = 2
2013-06-21 23:32:22 +02:00
shared . inventory [ inventoryHash ] = (
2014-08-27 09:14:32 +02:00
objectType , toStreamNumber , encryptedPayload , embeddedTime , ' ' )
2013-09-07 00:55:12 +02:00
shared . inventorySets [ toStreamNumber ] . add ( inventoryHash )
2013-09-30 01:24:27 +02:00
if shared . config . has_section ( toaddress ) :
2014-08-06 04:01:01 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , " Message sent. Sent on % 1 " ) . arg ( l10n . formatTimestamp ( ) ) ) ) )
2013-07-22 07:10:22 +02:00
else :
2013-09-30 01:24:27 +02:00
# not sending to a chan or one of my addresses
2014-08-06 04:01:01 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByAckdata ' , ( ackdata , tr . translateText ( " MainWindow " , " Message sent. Waiting for acknowledgement. Sent on % 1 " ) . arg ( l10n . formatTimestamp ( ) ) ) ) )
2013-06-21 23:32:22 +02:00
print ' Broadcasting inv for my msg(within sendmsg function): ' , inventoryHash . encode ( ' hex ' )
shared . broadcastToSendDataQueues ( (
2013-09-30 05:01:56 +02:00
toStreamNumber , ' advertiseobject ' , inventoryHash ) )
2013-06-21 23:32:22 +02:00
# Update the status of the message in the 'sent' table to have a
2014-08-27 09:14:32 +02:00
# 'msgsent' or 'msgsentnoackexpected' status.
2013-10-02 04:44:00 +02:00
if shared . config . has_section ( toaddress ) :
2013-07-22 07:10:22 +02:00
newStatus = ' msgsentnoackexpected '
else :
newStatus = ' msgsent '
2013-08-29 13:27:30 +02:00
sqlExecute ( ''' UPDATE sent SET msgid=?, status=? WHERE ackdata=? ''' ,
inventoryHash , newStatus , ackdata )
2013-06-21 23:32:22 +02:00
2013-09-30 05:01:56 +02:00
# If we are sending to ourselves or a chan, let's put the message in
# our own inbox.
if shared . config . has_section ( toaddress ) :
t = ( inventoryHash , toaddress , fromaddress , subject , int (
time . time ( ) ) , message , ' inbox ' , 2 , 0 )
helper_inbox . insert ( t )
shared . UISignalQueue . put ( ( ' displayNewInboxMessage ' , (
inventoryHash , toaddress , fromaddress , subject , message ) ) )
# If we are behaving as an API then we might need to run an
# outside command to let some program know that a new message
# has arrived.
if shared . safeConfigGetBoolean ( ' bitmessagesettings ' , ' apienabled ' ) :
try :
apiNotifyPath = shared . config . get (
' bitmessagesettings ' , ' apinotifypath ' )
except :
apiNotifyPath = ' '
if apiNotifyPath != ' ' :
call ( [ apiNotifyPath , " newMessage " ] )
2013-06-21 23:32:22 +02:00
def requestPubKey ( self , toAddress ) :
toStatus , addressVersionNumber , streamNumber , ripe = decodeAddress (
toAddress )
if toStatus != ' success ' :
2013-06-29 19:29:35 +02:00
with shared . printLock :
sys . stderr . write ( ' Very abnormal error occurred in requestPubKey. toAddress is: ' + repr (
toAddress ) + ' . Please report this error to Atheros. ' )
2013-06-21 23:32:22 +02:00
return
2013-09-13 06:27:34 +02:00
if addressVersionNumber < = 3 :
shared . neededPubkeys [ ripe ] = 0
elif addressVersionNumber > = 4 :
2014-08-27 09:14:32 +02:00
# If the user just clicked 'send' then the tag (and other information) will already
# be in the neededPubkeys dictionary. But if we are recovering from a restart
# of the client then we have to put it in now.
2013-09-13 06:27:34 +02:00
privEncryptionKey = hashlib . sha512 ( hashlib . sha512 ( encodeVarint ( addressVersionNumber ) + encodeVarint ( streamNumber ) + ripe ) . digest ( ) ) . digest ( ) [ : 32 ] # Note that this is the first half of the sha512 hash.
2013-09-15 03:06:26 +02:00
tag = hashlib . sha512 ( hashlib . sha512 ( encodeVarint ( addressVersionNumber ) + encodeVarint ( streamNumber ) + ripe ) . digest ( ) ) . digest ( ) [ 32 : ] # Note that this is the second half of the sha512 hash.
2014-08-27 09:14:32 +02:00
if tag not in shared . neededPubkeys :
shared . neededPubkeys [ tag ] = ( toAddress , highlevelcrypto . makeCryptor ( privEncryptionKey . encode ( ' hex ' ) ) ) # We'll need this for when we receive a pubkey reply: it will be encrypted and we'll need to decrypt it.
TTL = 2.5 * 24 * 60 * 60 # 2.5 days
embeddedTime = int ( time . time ( ) + random . randrange ( - 300 , 300 ) + TTL ) # 2.5 days from now plus or minus five minutes
payload = pack ( ' >Q ' , embeddedTime )
payload + = ' \x00 \x00 \x00 \x00 ' # object type: getpubkey
2013-06-21 23:32:22 +02:00
payload + = encodeVarint ( addressVersionNumber )
payload + = encodeVarint ( streamNumber )
2013-09-13 06:27:34 +02:00
if addressVersionNumber < = 3 :
payload + = ripe
with shared . printLock :
print ' making request for pubkey with ripe: ' , ripe . encode ( ' hex ' )
else :
2013-09-15 03:06:26 +02:00
payload + = tag
2013-09-13 06:27:34 +02:00
with shared . printLock :
2013-09-15 03:06:26 +02:00
print ' making request for v4 pubkey with tag: ' , tag . encode ( ' hex ' )
2013-06-29 19:29:35 +02:00
2013-06-21 23:32:22 +02:00
# print 'trial value', trialValue
statusbar = ' Doing the computations necessary to request the recipient \' s public key. '
shared . UISignalQueue . put ( ( ' updateStatusBar ' , statusbar ) )
shared . UISignalQueue . put ( ( ' updateSentItemStatusByHash ' , (
2013-06-24 21:51:01 +02:00
ripe , tr . translateText ( " MainWindow " , ' Doing work necessary to request encryption key. ' ) ) ) )
2014-08-27 09:14:32 +02:00
TTL = 2.5 * 24 * 60 * 60 # 2.5 days
target = 2 * * 64 / ( shared . networkDefaultProofOfWorkNonceTrialsPerByte * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes + ( ( TTL * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes ) ) / ( 2 * * 16 ) ) ) )
2013-06-21 23:32:22 +02:00
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
2013-06-29 19:29:35 +02:00
with shared . printLock :
print ' Found proof of work ' , trialValue , ' Nonce: ' , nonce
2013-06-21 23:32:22 +02:00
payload = pack ( ' >Q ' , nonce ) + payload
inventoryHash = calculateInventoryHash ( payload )
2014-08-27 09:14:32 +02:00
objectType = 1
2013-06-21 23:32:22 +02:00
shared . inventory [ inventoryHash ] = (
2014-08-27 09:14:32 +02:00
objectType , streamNumber , payload , embeddedTime , ' ' )
2013-09-07 00:55:12 +02:00
shared . inventorySets [ streamNumber ] . add ( inventoryHash )
2013-06-21 23:32:22 +02:00
print ' sending inv (for the getpubkey message) '
shared . broadcastToSendDataQueues ( (
2013-09-07 00:55:12 +02:00
streamNumber , ' advertiseobject ' , inventoryHash ) )
2013-06-21 23:32:22 +02:00
2013-08-29 13:27:30 +02:00
sqlExecute (
''' UPDATE sent SET status= ' awaitingpubkey ' WHERE toaddress=? AND status= ' doingpubkeypow ' ''' ,
toAddress )
2013-06-21 23:32:22 +02:00
shared . UISignalQueue . put ( (
2013-06-24 21:51:01 +02:00
' updateStatusBar ' , tr . translateText ( " MainWindow " , ' Broacasting the public key request. This program will auto-retry if they are offline. ' ) ) )
2014-08-06 04:01:01 +02:00
shared . UISignalQueue . put ( ( ' updateSentItemStatusByHash ' , ( ripe , tr . translateText ( " MainWindow " , ' Sending public key request. Waiting for reply. Requested at % 1 ' ) . arg ( l10n . formatTimestamp ( ) ) ) ) )
2013-06-21 23:32:22 +02:00
2013-12-06 07:52:19 +01:00
def generateFullAckMessage ( self , ackdata , toStreamNumber ) :
2014-08-27 09:14:32 +02:00
embeddedTime = int ( time . time ( ) + random . randrange ( - 300 , 300 ) ) # the current time plus or minus five minutes.
payload = pack ( ' >Q ' , ( embeddedTime ) )
payload + = ' \x00 \x00 \x00 \x02 ' # object type: msg
if int ( time . time ( ) ) > = 1416175200 : # Sun, 16 Nov 2014 22:00:00 GMT
payload + = encodeVarint ( 1 ) # msg version
payload + = encodeVarint ( toStreamNumber ) + ackdata
TTL = 2.5 * 24 * 60 * 60 # 2.5 days
target = 2 * * 64 / ( shared . networkDefaultProofOfWorkNonceTrialsPerByte * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes + ( ( TTL * ( len ( payload ) + 8 + shared . networkDefaultPayloadLengthExtraBytes ) ) / ( 2 * * 16 ) ) ) )
2013-06-29 19:29:35 +02:00
with shared . printLock :
print ' (For ack message) Doing proof of work... '
2013-06-21 23:32:22 +02:00
powStartTime = time . time ( )
initialHash = hashlib . sha512 ( payload ) . digest ( )
trialValue , nonce = proofofwork . run ( target , initialHash )
2013-06-29 19:29:35 +02:00
with shared . printLock :
print ' (For ack message) Found proof of work ' , trialValue , ' Nonce: ' , nonce
try :
print ' POW took ' , int ( time . time ( ) - powStartTime ) , ' seconds. ' , nonce / ( time . time ( ) - powStartTime ) , ' nonce trials per second. '
except :
pass
2013-06-21 23:32:22 +02:00
payload = pack ( ' >Q ' , nonce ) + payload
2014-08-27 09:14:32 +02:00
return shared . CreatePacket ( ' object ' , payload )