Merge pull request #750 from Atheros1/master

Removed obsolete Protocol v2 code
This commit is contained in:
Jonathan Warren 2014-12-25 19:16:37 -05:00
commit 607acf74f5
6 changed files with 326 additions and 643 deletions

View File

@ -184,13 +184,15 @@ class objectProcessor(threading.Thread):
readPosition += 4
publicSigningKey = data[readPosition:readPosition + 64]
# Is it possible for a public key to be invalid such that trying to
# encrypt or sign with it will cause an error? If it is, we should
# probably test these keys here.
# encrypt or sign with it will cause an error? If it is, it would
# be easiest to test them here.
readPosition += 64
publicEncryptionKey = data[readPosition:readPosition + 64]
if len(publicEncryptionKey) < 64:
logger.debug('publicEncryptionKey length less than 64. Sanity check failed.')
return
readPosition += 64
dataToStore = data[20:readPosition] # The data we'll store in the pubkeys table.
sha = hashlib.new('sha512')
sha.update(
'\x04' + publicSigningKey + '\x04' + publicEncryptionKey)
@ -213,17 +215,6 @@ class objectProcessor(threading.Thread):
queryreturn = sqlQuery(
'''SELECT usedpersonally FROM pubkeys WHERE hash=? AND addressversion=? AND usedpersonally='yes' ''', ripe, addressVersion)
"""
With the changes in protocol v3, we have to be careful to store pubkey data
in the database the same way we did before to maintain backwards compatibility
with what is in people's databases already. This means that for v2 keys, we
must store the nonce, the time, and then everything else starting with the
address version.
"""
dataToStore = '\x00' * 8 # fake nonce
dataToStore += data[8:16] # the time
dataToStore += data[20:] # everything else
if queryreturn != []: # if this pubkey is already in our database and if we have used it personally:
logger.info('We HAVE used this pubkey personally. Updating time.')
t = (ripe, addressVersion, dataToStore, int(time.time()), 'yes')
@ -249,35 +240,15 @@ class objectProcessor(threading.Thread):
data[readPosition:readPosition + 10])
readPosition += specifiedPayloadLengthExtraBytesLength
endOfSignedDataPosition = readPosition
dataToStore = data[20:readPosition] # The data we'll store in the pubkeys table.
signatureLength, signatureLengthLength = decodeVarint(
data[readPosition:readPosition + 10])
readPosition += signatureLengthLength
signature = data[readPosition:readPosition + signatureLength]
"""
With the changes in protocol v3, to maintain backwards compatibility, signatures will be sent
the 'old' way during an upgrade period and then a 'new' simpler way after that. We will therefore
check the sig both ways.
Old way:
signedData = timePubkeyWasSigned(4 bytes) + addressVersion through extra_bytes
New way:
signedData = all of the payload data, from the time down through the extra_bytes
The timePubkeyWasSigned will be calculated by subtracting 28 days form the embedded expiresTime.
"""
expiresTime, = unpack('>Q', data[8:16])
TTL = 28 * 24 * 60 * 60
signedData = pack('>I', (expiresTime - TTL)) # the time that the pubkey was signed. 4 bytes.
signedData += data[20:endOfSignedDataPosition] # the address version down through the payloadLengthExtraBytes
if highlevelcrypto.verify(signedData, signature, publicSigningKey.encode('hex')):
logger.info('ECDSA verify passed (within processpubkey, old method)')
else:
logger.warning('ECDSA verify failed (within processpubkey, old method)')
# let us try the newer signature method
if highlevelcrypto.verify(data[8:endOfSignedDataPosition], signature, publicSigningKey.encode('hex')):
logger.info('ECDSA verify passed (within processpubkey, new method)')
logger.debug('ECDSA verify passed (within processpubkey)')
else:
logger.warning('ECDSA verify failed (within processpubkey, new method)')
logger.warning('ECDSA verify failed (within processpubkey)')
return
sha = hashlib.new('sha512')
@ -299,17 +270,6 @@ class objectProcessor(threading.Thread):
)
"""
With the changes in protocol v3, we have to be careful to store pubkey data
in the database the same way we did before to maintain backwards compatibility
with what is in people's databases already. This means that for v3 keys, we
must store the nonce, the time, and then everything else starting with the
address version.
"""
dataToStore = '\x00' * 8 # fake nonce
dataToStore += data[8:16] # the time
dataToStore += data[20:] # everything else
queryreturn = sqlQuery('''SELECT usedpersonally FROM pubkeys WHERE hash=? AND addressversion=? AND usedpersonally='yes' ''', ripe, addressVersion)
if queryreturn != []: # if this pubkey is already in our database and if we have used it personally:
logger.info('We HAVE used this pubkey personally. Updating time.')
@ -350,14 +310,11 @@ class objectProcessor(threading.Thread):
shared.UISignalQueue.put((
'updateNumberOfMessagesProcessed', 'no data'))
readPosition = 20 # bypass the nonce, time, and object type
"""
In protocol v2, the next byte(s) was the streamNumber. But starting after
the protocol v3 upgrade period, the next byte(s) will be a msg version
number followed by the streamNumber.
"""
#msgVersionOutsideEncryption, msgVersionOutsideEncryptionLength = decodeVarint(data[readPosition:readPosition + 9])
#readPosition += msgVersionOutsideEncryptionLength
msgVersion, msgVersionLength = decodeVarint(data[readPosition:readPosition + 9])
if msgVersion != 1:
logger.info('Cannot understand message versions other than one. Ignoring message.')
return
readPosition += msgVersionLength
streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = decodeVarint(
data[readPosition:readPosition + 9])
@ -379,31 +336,13 @@ class objectProcessor(threading.Thread):
# This is not an acknowledgement bound for me. See if it is a message
# bound for me by trying to decrypt it with my private keys.
# This can be simplified quite a bit after 1416175200: # Sun, 16 Nov 2014 22:00:00 GMT
for key, cryptorObject in shared.myECCryptorObjects.items():
try:
decryptedData = cryptorObject.decrypt(data[readPosition:])
toRipe = key # This is the RIPE hash of my pubkeys. We need this below to compare to the destination_ripe included in the encrypted data.
initialDecryptionSuccessful = True
logger.info('EC decryption successful using key associated with ripe hash: %s. msg did NOT specify version.' % key.encode('hex'))
# We didn't bypass a msg version above as it is commented out.
# But the decryption was successful. Which means that there
# wasn't a msg version byte include in this msg.
msgObjectContainedVersion = False
break
except Exception as err:
# What if a client sent us a msg with
# a msg version included? We didn't bypass it above. So
# let's try to decrypt the msg assuming that it is present.
try:
decryptedData = cryptorObject.decrypt(data[readPosition+1:]) # notice that we offset by 1 byte compared to the attempt above.
toRipe = key # This is the RIPE hash of my pubkeys. We need this below to compare to the destination_ripe included in the encrypted data.
initialDecryptionSuccessful = True
logger.info('EC decryption successful using key associated with ripe hash: %s. msg DID specifiy version.' % key.encode('hex'))
# There IS a msg version byte include in this msg.
msgObjectContainedVersion = True
logger.info('EC decryption successful using key associated with ripe hash: %s.' % key.encode('hex'))
#msgObjectContainedVersion = False
break
except Exception as err:
pass
@ -416,15 +355,6 @@ class objectProcessor(threading.Thread):
toAddress = shared.myAddressesByHash[
toRipe] # Look up my address based on the RIPE hash.
readPosition = 0
if not msgObjectContainedVersion: # by which I mean "if the msg object didn't have the msg version outside of the encryption". This confusingness will be removed after the protocol v3 upgrade period.
messageVersionWithinEncryption, messageVersionWithinEncryptionLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += messageVersionWithinEncryptionLength
if messageVersionWithinEncryption != 1:
logger.info('Cannot understand message versions other than one. Ignoring message.')
return
else:
messageVersionWithinEncryptionLength = 0 # This variable can disappear after the protocol v3 upgrade period is complete.
sendersAddressVersionNumber, sendersAddressVersionNumberLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += sendersAddressVersionNumberLength
@ -489,11 +419,6 @@ class objectProcessor(threading.Thread):
readPosition += signatureLengthLength
signature = decryptedData[
readPosition:readPosition + signatureLength]
if not msgObjectContainedVersion:
# protocol v2. This can be removed after the end of the protocol v3 upgrade period.
signedData = decryptedData[:positionOfBottomOfAckData]
else:
# protocol v3
signedData = data[8:20] + encodeVarint(1) + encodeVarint(streamNumberAsClaimedByMsg) + decryptedData[:positionOfBottomOfAckData]
if not highlevelcrypto.verify(signedData, signature, pubSigningKey.encode('hex')):
@ -511,32 +436,25 @@ class objectProcessor(threading.Thread):
ripe.update(sha.digest())
fromAddress = encodeAddress(
sendersAddressVersionNumber, sendersStreamNumber, ripe.digest())
# Let's store the public key in case we want to reply to this
# person.
if sendersAddressVersionNumber <= 3:
sqlExecute(
'''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
ripe.digest(),
sendersAddressVersionNumber,
'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[messageVersionWithinEncryptionLength:endOfThePublicKeyPosition],
decryptedData[:endOfThePublicKeyPosition],
int(time.time()),
'yes')
# This will check to see whether we happen to be awaiting this
# Check to see whether we happen to be awaiting this
# pubkey in order to send a message. If we are, it will do the POW
# and send it.
if sendersAddressVersionNumber <= 3:
self.possibleNewPubkey(ripe=ripe.digest())
elif sendersAddressVersionNumber >= 4:
sqlExecute(
'''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
ripe.digest(),
sendersAddressVersionNumber,
'\x00\x00\x00\x00\x00\x00\x00\x01' + decryptedData[messageVersionWithinEncryptionLength:endOfThePublicKeyPosition],
int(time.time()),
'yes')
# This will check to see whether we happen to be awaiting this
# pubkey in order to send a message. If we are, it will do the POW
# and send it.
self.possibleNewPubkey(address = fromAddress)
# If this message is bound for one of my version 3 addresses (or
# higher), then we must check to make sure it meets our demanded
# proof of work requirement. If this is bound for one of my chan
@ -665,22 +583,19 @@ class objectProcessor(threading.Thread):
broadcastVersion, broadcastVersionLength = decodeVarint(
data[readPosition:readPosition + 9])
readPosition += broadcastVersionLength
if broadcastVersion < 1 or broadcastVersion > 5:
logger.info('Cannot decode incoming broadcast versions higher than 5. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.')
if broadcastVersion < 4 or broadcastVersion > 5:
logger.info('Cannot decode incoming broadcast versions less than 4 or higher than 5. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.')
return
if broadcastVersion == 1:
logger.info('Version 1 broadcasts are no longer supported. Not processing it at all.')
if broadcastVersion in [2,4]:
"""
v2 (and later v4) broadcasts are encrypted the same way the msgs were encrypted. To see if we are interested in a
v2 broadcast, we try to decrypt it. This was replaced with v3 (and later v5) broadcasts which include a tag which
we check instead, just like we do with v4 pubkeys.
v2 and v3 broadcasts should be completely obsolete after the protocol v3 upgrade period and some code can be simplified.
"""
cleartextStreamNumber, cleartextStreamNumberLength = decodeVarint(
data[readPosition:readPosition + 10])
readPosition += cleartextStreamNumberLength
signedData = data[8:readPosition] # This doesn't end up being used if the broadcastVersion is 2
if broadcastVersion == 4:
"""
v4 broadcasts are encrypted the same way the msgs are encrypted. To see if we are interested in a
v4 broadcast, we try to decrypt it. This was replaced with v5 broadcasts which include a tag which
we check instead, just like we do with v4 pubkeys.
"""
signedData = data[8:readPosition]
initialDecryptionSuccessful = False
for key, cryptorObject in shared.MyECSubscriptionCryptorObjects.items():
try:
@ -694,21 +609,35 @@ class objectProcessor(threading.Thread):
# print 'cryptorObject.decrypt Exception:', err
if not initialDecryptionSuccessful:
# This is not a broadcast I am interested in.
logger.debug('Length of time program spent failing to decrypt this v2 broadcast: %s seconds.' % (time.time() - messageProcessingStartTime,))
logger.debug('Length of time program spent failing to decrypt this v4 broadcast: %s seconds.' % (time.time() - messageProcessingStartTime,))
return
# At this point this is a broadcast I have decrypted and thus am
elif broadcastVersion == 5:
embeddedTag = data[readPosition:readPosition+32]
readPosition += 32
if embeddedTag not in shared.MyECSubscriptionCryptorObjects:
logger.debug('We\'re not interested in this broadcast.')
return
# We are interested in this broadcast because of its tag.
signedData = data[8:readPosition] # We're going to add some more data which is signed further down.
cryptorObject = shared.MyECSubscriptionCryptorObjects[embeddedTag]
try:
decryptedData = cryptorObject.decrypt(data[readPosition:])
logger.debug('EC decryption successful')
except Exception as err:
logger.debug('Broadcast version %s decryption Unsuccessful.' % broadcastVersion)
return
# At this point this is a broadcast I have decrypted and am
# interested in.
readPosition = 0
if broadcastVersion == 2:
signedBroadcastVersion, signedBroadcastVersionLength = decodeVarint(
decryptedData[:10])
readPosition += signedBroadcastVersionLength
beginningOfPubkeyPosition = readPosition # used when we add the pubkey to our pubkey table. This variable can be disposed of after the protocol v3 upgrade period because it will necessarily be at the beginning of the decryptedData; ie it will definitely equal 0
sendersAddressVersion, sendersAddressVersionLength = decodeVarint(
decryptedData[readPosition:readPosition + 9])
if broadcastVersion == 4:
if sendersAddressVersion < 2 or sendersAddressVersion > 3:
logger.info('Cannot decode senderAddressVersion other than 2 or 3. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.')
logger.warning('Cannot decode senderAddressVersion other than 2 or 3. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.')
return
elif broadcastVersion == 5:
if sendersAddressVersion < 4:
logger.info('Cannot decode senderAddressVersion less than 4 for broadcast version number 5. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.')
return
readPosition += sendersAddressVersionLength
sendersStream, sendersStreamLength = decodeVarint(
@ -738,12 +667,20 @@ class objectProcessor(threading.Thread):
sha = hashlib.new('sha512')
sha.update(sendersPubSigningKey + sendersPubEncryptionKey)
ripe = hashlib.new('ripemd160')
ripe.update(sha.digest())
ripeHasher = hashlib.new('ripemd160')
ripeHasher.update(sha.digest())
calculatedRipe = ripeHasher.digest()
if toRipe != ripe.digest():
if broadcastVersion == 4:
if toRipe != calculatedRipe:
logger.info('The encryption key used to encrypt this message doesn\'t match the keys inbedded in the message itself. Ignoring message.')
return
elif broadcastVersion == 5:
calculatedTag = hashlib.sha512(hashlib.sha512(encodeVarint(
sendersAddressVersion) + encodeVarint(sendersStream) + calculatedRipe).digest()).digest()[32:]
if calculatedTag != embeddedTag:
logger.debug('The tag and encryption key used to encrypt this message doesn\'t match the keys inbedded in the message itself. Ignoring message.')
return
messageEncodingType, messageEncodingTypeLength = decodeVarint(
decryptedData[readPosition:readPosition + 9])
if messageEncodingType == 0:
@ -760,32 +697,34 @@ class objectProcessor(threading.Thread):
readPosition += signatureLengthLength
signature = decryptedData[
readPosition:readPosition + signatureLength]
if broadcastVersion == 2: # this can be removed after the protocol v3 upgrade period
signedData = decryptedData[:readPositionAtBottomOfMessage]
else:
signedData += decryptedData[:readPositionAtBottomOfMessage]
if not highlevelcrypto.verify(signedData, signature, sendersPubSigningKey.encode('hex')):
logger.debug('ECDSA verify failed')
return
logger.debug('ECDSA verify passed')
# verify passed
# Let's store the public key in case we want to reply to this
# person.
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
ripe.digest(),
sendersAddressVersion,
'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF' + '\xFF\xFF\xFF\xFF' + decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition],
int(time.time()),
'yes')
# shared.workerQueue.put(('newpubkey',(sendersAddressVersion,sendersStream,ripe.digest())))
# This will check to see whether we happen to be awaiting this
# pubkey in order to send a message. If we are, it will do the POW
# and send it.
self.possibleNewPubkey(ripe=ripe.digest())
fromAddress = encodeAddress(
sendersAddressVersion, sendersStream, ripe.digest())
sendersAddressVersion, sendersStream, calculatedRipe)
logger.info('fromAddress: %s' % fromAddress)
# Let's store the public key in case we want to reply to this person.
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
calculatedRipe,
sendersAddressVersion,
decryptedData[:endOfPubkeyPosition],
int(time.time()),
'yes')
# Check to see whether we happen to be awaiting this
# pubkey in order to send a message. If we are, it will do the POW
# and send it.
if broadcastVersion == 4:
self.possibleNewPubkey(ripe=calculatedRipe)
elif broadcastVersion == 5:
self.possibleNewPubkey(address=fromAddress)
fromAddress = encodeAddress(
sendersAddressVersion, sendersStream, calculatedRipe)
with shared.printLock:
print 'fromAddress:', fromAddress
@ -828,172 +767,20 @@ class objectProcessor(threading.Thread):
# Display timing data
logger.info('Time spent processing this interesting broadcast: %s' % (time.time() - messageProcessingStartTime,))
if broadcastVersion in [3,5]:
# broadcast version 3 should be completely obsolete after the end of the protocol v3 upgrade period
cleartextStreamNumber, cleartextStreamNumberLength = decodeVarint(
data[readPosition:readPosition + 10])
readPosition += cleartextStreamNumberLength
embeddedTag = data[readPosition:readPosition+32]
readPosition += 32
if embeddedTag not in shared.MyECSubscriptionCryptorObjects:
logger.debug('We\'re not interested in this broadcast.')
return
# We are interested in this broadcast because of its tag.
signedData = data[8:readPosition] # We're going to add some more data which is signed further down.
cryptorObject = shared.MyECSubscriptionCryptorObjects[embeddedTag]
try:
decryptedData = cryptorObject.decrypt(data[readPosition:])
logger.debug('EC decryption successful')
except Exception as err:
logger.debug('Broadcast version %s decryption Unsuccessful.' % broadcastVersion)
return
# broadcast version 3 includes the broadcast version at the beginning
# of the decryptedData. Broadcast version 5 doesn't.
readPosition = 0
if broadcastVersion == 3: # This section can be removed after the protocol v3 upgrade period
signedBroadcastVersion, signedBroadcastVersionLength = decodeVarint(
decryptedData[:10])
readPosition += signedBroadcastVersionLength
beginningOfPubkeyPosition = readPosition # used when we add the pubkey to our pubkey table
sendersAddressVersion, sendersAddressVersionLength = decodeVarint(
decryptedData[readPosition:readPosition + 9])
if sendersAddressVersion < 4:
logger.info('Cannot decode senderAddressVersion less than 4 for broadcast version number 3 or 4. Assuming the sender isn\'t being silly, you should upgrade Bitmessage because this message shall be ignored.')
return
readPosition += sendersAddressVersionLength
sendersStream, sendersStreamLength = decodeVarint(
decryptedData[readPosition:readPosition + 9])
if sendersStream != cleartextStreamNumber:
logger.info('The stream number outside of the encryption on which the POW was completed doesn\'t match the stream number inside the encryption. Ignoring broadcast.')
return
readPosition += sendersStreamLength
behaviorBitfield = decryptedData[readPosition:readPosition + 4]
readPosition += 4
sendersPubSigningKey = '\x04' + \
decryptedData[readPosition:readPosition + 64]
readPosition += 64
sendersPubEncryptionKey = '\x04' + \
decryptedData[readPosition:readPosition + 64]
readPosition += 64
if sendersAddressVersion >= 3:
requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += varintLength
logger.debug('sender\'s requiredAverageProofOfWorkNonceTrialsPerByte is %s' % requiredAverageProofOfWorkNonceTrialsPerByte)
requiredPayloadLengthExtraBytes, varintLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += varintLength
logger.debug('sender\'s requiredPayloadLengthExtraBytes is %s' % requiredPayloadLengthExtraBytes)
endOfPubkeyPosition = readPosition
sha = hashlib.new('sha512')
sha.update(sendersPubSigningKey + sendersPubEncryptionKey)
ripeHasher = hashlib.new('ripemd160')
ripeHasher.update(sha.digest())
calculatedRipe = ripeHasher.digest()
calculatedTag = hashlib.sha512(hashlib.sha512(encodeVarint(
sendersAddressVersion) + encodeVarint(sendersStream) + calculatedRipe).digest()).digest()[32:]
if calculatedTag != embeddedTag:
logger.debug('The tag and encryption key used to encrypt this message doesn\'t match the keys inbedded in the message itself. Ignoring message.')
return
messageEncodingType, messageEncodingTypeLength = decodeVarint(
decryptedData[readPosition:readPosition + 9])
if messageEncodingType == 0:
return
readPosition += messageEncodingTypeLength
messageLength, messageLengthLength = decodeVarint(
decryptedData[readPosition:readPosition + 9])
readPosition += messageLengthLength
message = decryptedData[readPosition:readPosition + messageLength]
readPosition += messageLength
readPositionAtBottomOfMessage = readPosition
signatureLength, signatureLengthLength = decodeVarint(
decryptedData[readPosition:readPosition + 9])
readPosition += signatureLengthLength
signature = decryptedData[
readPosition:readPosition + signatureLength]
if broadcastVersion == 3: # broadcastVersion 3 should be completely unused after the end of the protocol v3 upgrade period
signedData = decryptedData[:readPositionAtBottomOfMessage]
elif broadcastVersion == 5:
signedData += decryptedData[:readPositionAtBottomOfMessage]
if not highlevelcrypto.verify(signedData, signature, sendersPubSigningKey.encode('hex')):
logger.debug('ECDSA verify failed')
return
logger.debug('ECDSA verify passed')
# verify passed
fromAddress = encodeAddress(
sendersAddressVersion, sendersStream, calculatedRipe)
logger.info('fromAddress: %s' % fromAddress)
# Let's store the public key in case we want to reply to this person.
sqlExecute(
'''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
calculatedRipe,
sendersAddressVersion,
'\x00\x00\x00\x00\x00\x00\x00\x01' + decryptedData[beginningOfPubkeyPosition:endOfPubkeyPosition],
int(time.time()),
'yes')
# This will check to see whether we happen to be awaiting this
# pubkey in order to send a message. If we are, it will do the
# POW and send it.
self.possibleNewPubkey(address = fromAddress)
if messageEncodingType == 2:
subject, body = self.decodeType2Message(message)
logger.info('Broadcast subject (first 100 characters): %s' % repr(subject)[:100])
elif messageEncodingType == 1:
body = message
subject = ''
elif messageEncodingType == 0:
logger.info('messageEncodingType == 0. Doing nothing with the message.')
else:
body = 'Unknown encoding type.\n\n' + repr(message)
subject = ''
toAddress = '[Broadcast subscribers]'
if messageEncodingType != 0:
if helper_inbox.isMessageAlreadyInInbox(toAddress, fromAddress, subject, body, messageEncodingType):
logger.info('This broadcast is already in our inbox. Ignoring it.')
else:
t = (inventoryHash, toAddress, fromAddress, subject, int(
time.time()), body, 'inbox', messageEncodingType, 0)
helper_inbox.insert(t)
shared.UISignalQueue.put(('displayNewInboxMessage', (
inventoryHash, toAddress, fromAddress, subject, body)))
# If we are behaving as an API then we might need to run an
# outside command to let some program know that a new message
# has arrived.
if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'):
try:
apiNotifyPath = shared.config.get(
'bitmessagesettings', 'apinotifypath')
except:
apiNotifyPath = ''
if apiNotifyPath != '':
call([apiNotifyPath, "newBroadcast"])
# Display timing data
logger.debug('Time spent processing this interesting broadcast: %s' % (time.time() - messageProcessingStartTime,))
# We have inserted a pubkey into our pubkey table which we received from a
# pubkey, msg, or broadcast message. It might be one that we have been
# waiting for. Let's check.
def possibleNewPubkey(self, ripe=None, address=None):
"""
We have put a new pubkey in our pubkeys table. Let's see if we have been
awaiting it.
"""
# For address versions <= 3, we wait on a key with the correct ripe hash
if ripe != None:
if ripe in shared.neededPubkeys:
logger.info('We have been awaiting the arrival of this pubkey.')
del shared.neededPubkeys[ripe]
sqlExecute(
'''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND (status='awaitingpubkey' or status='doingpubkeypow') and folder='sent' ''',
ripe)
shared.workerQueue.put(('sendmessage', ''))
self.sendMessages(ripe)
else:
logger.debug('We don\'t need this pub key. We didn\'t ask for it. Pubkey hash: %s' % ripe.encode('hex'))
# For address versions >= 4, we wait on a pubkey with the correct tag.
@ -1004,8 +791,16 @@ class objectProcessor(threading.Thread):
tag = hashlib.sha512(hashlib.sha512(encodeVarint(
addressVersion) + encodeVarint(streamNumber) + ripe).digest()).digest()[32:]
if tag in shared.neededPubkeys:
logger.info('We have been awaiting the arrival of this pubkey.')
del shared.neededPubkeys[tag]
self.sendMessages(ripe)
def sendMessages(self, ripe):
"""
This function is called by the possibleNewPubkey function when
that function sees that we now have the necessary pubkey
to send one or more messages.
"""
logger.info('We have been awaiting the arrival of this pubkey.')
sqlExecute(
'''UPDATE sent SET status='doingmsgpow' WHERE toripe=? AND (status='awaitingpubkey' or status='doingpubkeypow') and folder='sent' ''',
ripe)
@ -1040,22 +835,6 @@ class objectProcessor(threading.Thread):
return False
return True
def decodeType2Message(self, message):
bodyPositionIndex = string.find(message, '\nBody:')
if bodyPositionIndex > 1:
subject = message[8:bodyPositionIndex]
# Only save and show the first 500 characters of the subject.
# Any more is probably an attack.
subject = subject[:500]
body = message[bodyPositionIndex + 6:]
else:
subject = ''
body = message
# Throw away any extra lines (headers) after the subject.
if subject:
subject = subject.splitlines()[0]
return subject, body
def addMailingListNameToSubject(self, subject, mailingListName):
subject = subject.strip()
if subject[:3] == 'Re:' or subject[:3] == 'RE:':

View File

@ -219,12 +219,7 @@ class singleWorker(threading.Thread):
payload += encodeVarint(shared.config.getint(
myAddress, 'payloadlengthextrabytes'))
if int(time.time()) < 1416175200: # Sun, 16 Nov 2014 22:00:00 GMT
signedData = pack('>I', signedTimeForProtocolV2) + payload[12:]
else:
signedData = payload
signature = highlevelcrypto.sign(signedData, privSigningKeyHex)
signature = highlevelcrypto.sign(payload, privSigningKeyHex)
payload += encodeVarint(len(signature))
payload += signature
@ -309,8 +304,6 @@ class singleWorker(threading.Thread):
dataToEncrypt += encodeVarint(shared.config.getint(
myAddress, 'payloadlengthextrabytes'))
# When we encrypt, we'll use a hash of the data
# 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
@ -320,23 +313,7 @@ class singleWorker(threading.Thread):
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(encodeVarint(
addressVersionNumber) + encodeVarint(streamNumber) + hash).digest()).digest()
payload += doubleHashOfAddressData[32:] # the tag
"""
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)
signature = highlevelcrypto.sign(payload + dataToEncrypt, privSigningKeyHex)
dataToEncrypt += encodeVarint(len(signature))
dataToEncrypt += signature
@ -412,12 +389,6 @@ class singleWorker(threading.Thread):
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:
@ -432,14 +403,7 @@ class singleWorker(threading.Thread):
else:
tag = ''
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
dataToEncrypt += encodeVarint(addressVersionNumber)
dataToEncrypt = encodeVarint(addressVersionNumber)
dataToEncrypt += encodeVarint(streamNumber)
dataToEncrypt += '\x00\x00\x00\x01' # behavior bitfield
dataToEncrypt += pubSigningKey[1:]
@ -450,9 +414,6 @@ class singleWorker(threading.Thread):
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
if int(time.time()) < 1416175200: # Sun, 16 Nov 2014 22:00:00 GMT
dataToSign = dataToEncrypt
else:
dataToSign = payload + dataToEncrypt
signature = highlevelcrypto.sign(
@ -646,25 +607,16 @@ class singleWorker(threading.Thread):
for row in queryreturn:
pubkeyPayload, = row
# The pubkey message is stored the way we originally received it
# under protocol version 2
# 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.
pubkeyEmbeddedTime, = unpack(
'>I', pubkeyPayload[readPosition:readPosition + 4])
# This section is used for the transition from 32 bit time to 64
# bit time in the protocol.
if pubkeyEmbeddedTime == 0:
pubkeyEmbeddedTime, = unpack(
'>Q', pubkeyPayload[readPosition:readPosition + 8])
readPosition += 8
else:
readPosition += 4
readPosition += 1 # to bypass the address version whose length is definitely 1
# The pubkey message is stored with the following items all appended:
# -address version
# -stream number
# -behavior bitfield
# -pub signing key
# -pub encryption key
# -nonce trials per byte (if address version is >= 3)
# -length extra bytes (if address version is >= 3)
readPosition = 1 # to bypass the address version whose length is definitely 1
streamNumber, streamNumberLength = decodeVarint(
pubkeyPayload[readPosition:readPosition + 10])
readPosition += streamNumberLength
@ -679,9 +631,7 @@ class singleWorker(threading.Thread):
# 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.
# pubSigningKeyBase256 = pubkeyPayload[readPosition:readPosition+64] # We don't use this key for anything here.
readPosition += 64
pubEncryptionKeyBase256 = pubkeyPayload[
readPosition:readPosition + 64]
@ -740,11 +690,8 @@ class singleWorker(threading.Thread):
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
ackdata, tr.translateText("MainWindow", "Doing work necessary to send message."))))
if fromAddressVersionNumber == 2:
payload = ""
if int(time.time()) < 1416175200: # Sun, 16 Nov 2014 22:00:00 GMT
payload += '\x01' # Message version.
payload += encodeVarint(fromAddressVersionNumber)
# Now we can start to assemble our message.
payload = encodeVarint(fromAddressVersionNumber)
payload += encodeVarint(fromStreamNumber)
payload += '\x00\x00\x00\x01' # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features )
@ -774,57 +721,7 @@ class singleWorker(threading.Thread):
1:] # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
payload += pubEncryptionKey[1:]
payload += toRipe # This hash will be checked by the receiver of the message to verify that toRipe belongs to them. This prevents a Surreptitious Forwarding Attack.
payload += '\x02' # Type 2 is simple UTF-8 message encoding as specified on the Protocol Specification on the Bitmessage Wiki.
messageToTransmit = 'Subject:' + \
subject + '\n' + 'Body:' + message
payload += encodeVarint(len(messageToTransmit))
payload += messageToTransmit
fullAckPayload = self.generateFullAckMessage(
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.
payload += encodeVarint(len(fullAckPayload))
payload += fullAckPayload
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)
payload += encodeVarint(len(signature))
payload += signature
if fromAddressVersionNumber >= 3:
payload = ""
if int(time.time()) < 1416175200: # Sun, 16 Nov 2014 22:00:00 GMT
payload += '\x01' # Message version.
payload += encodeVarint(fromAddressVersionNumber)
payload += encodeVarint(fromStreamNumber)
payload += '\x00\x00\x00\x01' # Bitfield of features and behaviors that can be expected from me. (See https://bitmessage.org/wiki/Protocol_specification#Pubkey_bitfield_features )
# We need to convert our private keys to public keys in order
# to include them.
try:
privSigningKeyBase58 = shared.config.get(
fromaddress, 'privsigningkey')
privEncryptionKeyBase58 = shared.config.get(
fromaddress, 'privencryptionkey')
except:
shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (
ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file."))))
continue
privSigningKeyHex = shared.decodeWalletImportFormat(
privSigningKeyBase58).encode('hex')
privEncryptionKeyHex = shared.decodeWalletImportFormat(
privEncryptionKeyBase58).encode('hex')
pubSigningKey = highlevelcrypto.privToPub(
privSigningKeyHex).decode('hex')
pubEncryptionKey = highlevelcrypto.privToPub(
privEncryptionKeyHex).decode('hex')
payload += pubSigningKey[
1:] # The \x04 on the beginning of the public keys are not sent. This way there is only one acceptable way to encode and send a public key.
payload += pubEncryptionKey[1:]
# If the receiver of our message is in our address book,
# subscriptions list, or whitelist then we will allow them to
# do the network-minimum proof of work. Let us check to see if
@ -859,15 +756,11 @@ class singleWorker(threading.Thread):
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.
payload += encodeVarint(len(fullAckPayload))
payload += fullAckPayload
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)
payload += encodeVarint(len(signature))
payload += signature
print 'using pubEncryptionKey:', pubEncryptionKeyBase256.encode('hex')
# We have assembled the data that will be encrypted.
try:
encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex'))
@ -878,7 +771,6 @@ class singleWorker(threading.Thread):
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))))
@ -1019,7 +911,6 @@ class singleWorker(threading.Thread):
embeddedTime = int(time.time() + TTL)
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

View File

@ -348,6 +348,28 @@ class sqlThread(threading.Thread):
shared.config.write(configfile)
# The format of data stored in the pubkeys table has changed. Let's
# clear it, and the pubkeys from inventory, so that they'll be re-downloaded.
item = '''SELECT value FROM settings WHERE key='version';'''
parameters = ''
self.cur.execute(item, parameters)
currentVersion = int(self.cur.fetchall()[0][0])
if currentVersion == 7:
logger.debug('In messages.dat database, clearing pubkeys table because the data format has been updated.')
self.cur.execute(
'''delete from inventory where objecttype = 1;''')
self.cur.execute(
'''delete from pubkeys;''')
# Any sending messages for which we *thought* that we had the pubkey must
# be rechecked.
self.cur.execute(
'''UPDATE sent SET status='msgqueued' WHERE status='doingmsgpow' or status='badkey';''')
query = '''update settings set value=? WHERE key='version';'''
parameters = (8,)
self.cur.execute(query, parameters)
logger.debug('Finished clearing currently held pubkeys.')
# Are you hoping to add a new option to the keys.dat file of existing
# Bitmessage users or modify the SQLite database? Add it right above this line!

View File

@ -7,7 +7,7 @@
from hashlib import sha512
from pyelliptic.openssl import OpenSSL
from pyelliptic.cipher import Cipher
from pyelliptic.hash import hmac_sha256
from pyelliptic.hash import hmac_sha256, equals
from struct import pack, unpack
@ -436,16 +436,9 @@ class ECC:
pubkey = ephem.get_pubkey()
iv = OpenSSL.rand(OpenSSL.get_cipher(ciphername).get_blocksize())
ctx = Cipher(key_e, iv, 1, ciphername)
import time
if int(time.time()) < 1416175200: # Sun, 16 Nov 2014 22:00:00 GMT
ciphertext = ctx.ciphering(data)
else:
ciphertext = iv + pubkey + ctx.ciphering(data) # Everyone should be using this line after the Bitmessage protocol v3 upgrade period
ciphertext = iv + pubkey + ctx.ciphering(data)
mac = hmac_sha256(key_m, ciphertext)
if int(time.time()) < 1416175200: # Sun, 16 Nov 2014 22:00:00 GMT
return iv + pubkey + ciphertext + mac
else:
return ciphertext + mac # Everyone should be using this line after the Bitmessage protocol v3 upgrade period
return ciphertext + mac
def decrypt(self, data, ciphername='aes-256-cbc'):
"""
@ -461,14 +454,7 @@ class ECC:
mac = data[i:]
key = sha512(self.raw_get_ecdh_key(pubkey_x, pubkey_y)).digest()
key_e, key_m = key[:32], key[32:]
"""
pyelliptic was changed slightly so that the hmac covers the
iv and pubkey. So let's have an upgrade period where we support
both the old and the new hmac'ing algorithms.
https://github.com/yann2192/pyelliptic/issues/17
"""
if hmac_sha256(key_m, ciphertext) != mac:
if hmac_sha256(key_m, data[:len(data) - 32]) != mac:
if not equals(hmac_sha256(key_m, data[:len(data) - 32]), mac):
raise RuntimeError("Fail to verify data")
ctx = Cipher(key_e, iv, 0, ciphername)
return ctx.ciphering(ciphertext)

View File

@ -7,6 +7,32 @@
from pyelliptic.openssl import OpenSSL
# For python3
def _equals_bytes(a, b):
if len(a) != len(b):
return False
result = 0
for x, y in zip(a, b):
result |= x ^ y
return result == 0
def _equals_str(a, b):
if len(a) != len(b):
return False
result = 0
for x, y in zip(a, b):
result |= ord(x) ^ ord(y)
return result == 0
def equals(a, b):
if isinstance(a, str):
return _equals_str(a, b)
else:
return _equals_bytes(a, b)
def hmac_sha256(k, m):
"""
Compute the key and the message with HMAC SHA5256

View File

@ -472,20 +472,10 @@ def isBitSetWithinBitfield(fourByteString, n):
def decryptAndCheckPubkeyPayload(data, address):
"""
With the changes in protocol v3, to maintain backwards compatibility, signatures will be sent
the 'old' way during an upgrade period and then a 'new' simpler way after that. We will therefore
check the sig both ways.
Old way:
signedData = timePubkeyWasSigned(8 bytes) + addressVersion + streamNumber + the decrypted data down through the payloadLengthExtraBytes
New way:
signedData = all of the payload data from the time to the tag + the decrypted data down through the payloadLengthExtraBytes
The timePubkeyWasSigned will be calculated by subtracting 28 days form the embedded expiresTime.
"""
"""
The time, address version, and stream number are not encrypted so let's
keep that data here for now.
Version 4 pubkeys are encrypted. This function is run when we already have the
address to which we want to try to send a message. The 'data' may come either
off of the wire or we might have had it already in our inventory when we tried
to send a msg to this particular address.
"""
try:
status, addressVersion, streamNumber, ripe = decodeAddress(address)
@ -495,6 +485,7 @@ def decryptAndCheckPubkeyPayload(data, address):
readPosition += varintLength
embeddedStreamNumber, varintLength = decodeVarint(data[readPosition:readPosition + 10])
readPosition += varintLength
storedData = data[20:readPosition] # We'll store the address version and stream number (and some more) in the pubkeys table.
if addressVersion != embeddedAddressVersion:
logger.info('Pubkey decryption was UNsuccessful due to address version mismatch.')
@ -503,16 +494,9 @@ def decryptAndCheckPubkeyPayload(data, address):
logger.info('Pubkey decryption was UNsuccessful due to stream number mismatch.')
return 'failed'
expiresTime, = unpack('>Q', data[8:16])
TTL = 28 * 24 * 60 * 60
signedDataOldMethod = pack('>Q', (expiresTime - TTL)) # the time that the pubkey was signed. 8 bytes.
signedDataOldMethod += data[20:readPosition] # the address version and stream number
tag = data[readPosition:readPosition + 32]
readPosition += 32
signedDataNewMethod = data[8:readPosition] # the time through the tag
signedData = data[8:readPosition] # the time through the tag. More data is appended onto signedData below after the decryption.
encryptedData = data[readPosition:]
# Let us try to decrypt the pubkey
@ -520,7 +504,7 @@ def decryptAndCheckPubkeyPayload(data, address):
if toAddress != address:
logger.critical('decryptAndCheckPubkeyPayload failed due to toAddress mismatch. This is very peculiar. toAddress: %s, address %s' % (toAddress, address))
# the only way I can think that this could happen is if someone encodes their address data two different ways.
# That sort of address-malleability should have been prevented earlier.
# That sort of address-malleability should have been caught by the UI or API and an error given to the user.
return 'failed'
try:
decryptedData = cryptorObject.decrypt(encryptedData)
@ -543,22 +527,17 @@ def decryptAndCheckPubkeyPayload(data, address):
specifiedPayloadLengthExtraBytes, specifiedPayloadLengthExtraBytesLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += specifiedPayloadLengthExtraBytesLength
signedDataOldMethod += decryptedData[:readPosition]
signedDataNewMethod += decryptedData[:readPosition]
storedData += decryptedData[:readPosition]
signedData += decryptedData[:readPosition]
signatureLength, signatureLengthLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])
readPosition += signatureLengthLength
signature = decryptedData[readPosition:readPosition + signatureLength]
if highlevelcrypto.verify(signedDataOldMethod, signature, publicSigningKey.encode('hex')):
logger.info('ECDSA verify passed (within decryptAndCheckPubkeyPayload, old method)')
if highlevelcrypto.verify(signedData, signature, publicSigningKey.encode('hex')):
logger.info('ECDSA verify passed (within decryptAndCheckPubkeyPayload)')
else:
logger.info('ECDSA verify failed (within decryptAndCheckPubkeyPayload, old method)')
# Try the protocol v3 signing method
if highlevelcrypto.verify(signedDataNewMethod, signature, publicSigningKey.encode('hex')):
logger.info('ECDSA verify passed (within decryptAndCheckPubkeyPayload, new method)')
else:
logger.info('ECDSA verify failed (within decryptAndCheckPubkeyPayload, new method)')
logger.info('ECDSA verify failed (within decryptAndCheckPubkeyPayload)')
return 'failed'
sha = hashlib.new('sha512')
@ -570,7 +549,7 @@ def decryptAndCheckPubkeyPayload(data, address):
if embeddedRipe != ripe:
# Although this pubkey object had the tag were were looking for and was
# encrypted with the correct encryption key, it doesn't contain the
# correct keys. Someone is either being malicious or using buggy software.
# correct pubkeys. Someone is either being malicious or using buggy software.
logger.info('Pubkey decryption was UNsuccessful due to RIPE mismatch.')
return 'failed'
@ -587,7 +566,7 @@ def decryptAndCheckPubkeyPayload(data, address):
)
)
t = (ripe, addressVersion, signedDataOldMethod, int(time.time()), 'yes')
t = (ripe, addressVersion, storedData, int(time.time()), 'yes')
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
return 'successful'
except varintDecodeError as e: