Removed from shared the functions duplicating protocol:

decryptAndCheckPubkeyPayload, isBitSetWithinBitfield
This commit is contained in:
Dmitri Bogomolov 2019-01-30 11:14:42 +02:00
parent 5b5ec2b63d
commit 3adadd398f
Signed by untrusted user: g1itch
GPG Key ID: 720A756F18DEED13
4 changed files with 67 additions and 198 deletions

View File

@ -400,7 +400,7 @@ class objectProcessor(threading.Thread):
# Let us try to decrypt the pubkey # Let us try to decrypt the pubkey
toAddress, _ = state.neededPubkeys[tag] toAddress, _ = state.neededPubkeys[tag]
if shared.decryptAndCheckPubkeyPayload(data, toAddress) == \ if protocol.decryptAndCheckPubkeyPayload(data, toAddress) == \
'successful': 'successful':
# At this point we know that we have been waiting on this # At this point we know that we have been waiting on this
# pubkey. This function will command the workerThread # pubkey. This function will command the workerThread

View File

@ -758,7 +758,7 @@ class singleWorker(threading.Thread, StoppableThread):
for value in Inventory().by_type_and_tag(1, toTag): for value in Inventory().by_type_and_tag(1, toTag):
# if valid, this function also puts it # if valid, this function also puts it
# in the pubkeys table. # in the pubkeys table.
if shared.decryptAndCheckPubkeyPayload( if protocol.decryptAndCheckPubkeyPayload(
value.payload, toaddress value.payload, toaddress
) == 'successful': ) == 'successful':
needToRequestPubkey = False needToRequestPubkey = False
@ -860,7 +860,7 @@ class singleWorker(threading.Thread, StoppableThread):
# if receiver is a mobile device who expects that their # if receiver is a mobile device who expects that their
# address RIPE is included unencrypted on the front of # address RIPE is included unencrypted on the front of
# the message.. # the message..
if shared.isBitSetWithinBitfield(behaviorBitfield, 30): if protocol.isBitSetWithinBitfield(behaviorBitfield, 30):
# if we are Not willing to include the receiver's # if we are Not willing to include the receiver's
# RIPE hash on the message.. # RIPE hash on the message..
if not shared.BMConfigParser().safeGetBoolean( if not shared.BMConfigParser().safeGetBoolean(

View File

@ -9,21 +9,21 @@ Low-level protocol-related functions.
from __future__ import absolute_import from __future__ import absolute_import
import base64 import base64
from binascii import hexlify
import hashlib import hashlib
import os
import random import random
import socket import socket
import ssl import ssl
from struct import pack, unpack, Struct
import sys import sys
import time import time
import traceback import traceback
from binascii import hexlify
from struct import pack, unpack, Struct
import defaults import defaults
import highlevelcrypto import highlevelcrypto
import state import state
from addresses import encodeVarint, decodeVarint, decodeAddress, varintDecodeError from addresses import (
encodeVarint, decodeVarint, decodeAddress, varintDecodeError)
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
from helper_sql import sqlExecute from helper_sql import sqlExecute
@ -321,33 +321,41 @@ def assembleErrorMessage(fatal=0, banTime=0, inventoryVector='', errorText=''):
def decryptAndCheckPubkeyPayload(data, address): def decryptAndCheckPubkeyPayload(data, address):
""" """
Version 4 pubkeys are encrypted. This function is run when we already have the Version 4 pubkeys are encrypted. This function is run when we
address to which we want to try to send a message. The 'data' may come either already have the address to which we want to try to send a message.
off of the wire or we might have had it already in our inventory when we tried The 'data' may come either off of the wire or we might have had it
to send a msg to this particular address. already in our inventory when we tried to send a msg to this
particular address.
""" """
# pylint: disable=unused-variable
try: try:
status, addressVersion, streamNumber, ripe = decodeAddress(address) addressVersion, streamNumber, ripe = decodeAddress(address)[1:]
readPosition = 20 # bypass the nonce, time, and object type readPosition = 20 # bypass the nonce, time, and object type
embeddedAddressVersion, varintLength = decodeVarint(data[readPosition:readPosition + 10]) embeddedAddressVersion, varintLength = decodeVarint(
data[readPosition:readPosition + 10])
readPosition += varintLength readPosition += varintLength
embeddedStreamNumber, varintLength = decodeVarint(data[readPosition:readPosition + 10]) embeddedStreamNumber, varintLength = decodeVarint(
data[readPosition:readPosition + 10])
readPosition += varintLength readPosition += varintLength
# We'll store the address version and stream number (and some more) in the pubkeys table. # We'll store the address version and stream number
# (and some more) in the pubkeys table.
storedData = data[20:readPosition] storedData = data[20:readPosition]
if addressVersion != embeddedAddressVersion: if addressVersion != embeddedAddressVersion:
logger.info('Pubkey decryption was UNsuccessful due to address version mismatch.') logger.info(
'Pubkey decryption was UNsuccessful'
' due to address version mismatch.')
return 'failed' return 'failed'
if streamNumber != embeddedStreamNumber: if streamNumber != embeddedStreamNumber:
logger.info('Pubkey decryption was UNsuccessful due to stream number mismatch.') logger.info(
'Pubkey decryption was UNsuccessful'
' due to stream number mismatch.')
return 'failed' return 'failed'
tag = data[readPosition:readPosition + 32] tag = data[readPosition:readPosition + 32]
readPosition += 32 readPosition += 32
# the time through the tag. More data is appended onto signedData below after the decryption. # the time through the tag. More data is appended onto
# signedData below after the decryption.
signedData = data[8:readPosition] signedData = data[8:readPosition]
encryptedData = data[readPosition:] encryptedData = data[readPosition:]
@ -355,13 +363,15 @@ def decryptAndCheckPubkeyPayload(data, address):
toAddress, cryptorObject = state.neededPubkeys[tag] toAddress, cryptorObject = state.neededPubkeys[tag]
if toAddress != address: if toAddress != address:
logger.critical( logger.critical(
'decryptAndCheckPubkeyPayload failed due to toAddress mismatch.' 'decryptAndCheckPubkeyPayload failed due to toAddress'
' This is very peculiar. toAddress: %s, address %s', ' mismatch. This is very peculiar.'
toAddress, ' toAddress: %s, address %s',
address) 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 caught by the UI or API and an error given to # the only way I can think that this could happen
# the user. # is if someone encodes their address data two different ways.
# That sort of address-malleability should have been caught
# by the UI or API and an error given to the user.
return 'failed' return 'failed'
try: try:
decryptedData = cryptorObject.decrypt(encryptedData) decryptedData = cryptorObject.decrypt(encryptedData)
@ -372,17 +382,17 @@ def decryptAndCheckPubkeyPayload(data, address):
return 'failed' return 'failed'
readPosition = 0 readPosition = 0
bitfieldBehaviors = decryptedData[readPosition:readPosition + 4] # bitfieldBehaviors = decryptedData[readPosition:readPosition + 4]
readPosition += 4 readPosition += 4
publicSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64] publicSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64 readPosition += 64
publicEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64] publicEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64 readPosition += 64
specifiedNonceTrialsPerByte, specifiedNonceTrialsPerByteLength = decodeVarint( specifiedNonceTrialsPerByteLength = decodeVarint(
decryptedData[readPosition:readPosition + 10]) decryptedData[readPosition:readPosition + 10])[1]
readPosition += specifiedNonceTrialsPerByteLength readPosition += specifiedNonceTrialsPerByteLength
specifiedPayloadLengthExtraBytes, specifiedPayloadLengthExtraBytesLength = decodeVarint( specifiedPayloadLengthExtraBytesLength = decodeVarint(
decryptedData[readPosition:readPosition + 10]) decryptedData[readPosition:readPosition + 10])[1]
readPosition += specifiedPayloadLengthExtraBytesLength readPosition += specifiedPayloadLengthExtraBytesLength
storedData += decryptedData[:readPosition] storedData += decryptedData[:readPosition]
signedData += decryptedData[:readPosition] signedData += decryptedData[:readPosition]
@ -391,12 +401,15 @@ def decryptAndCheckPubkeyPayload(data, address):
readPosition += signatureLengthLength readPosition += signatureLengthLength
signature = decryptedData[readPosition:readPosition + signatureLength] signature = decryptedData[readPosition:readPosition + signatureLength]
if highlevelcrypto.verify(signedData, signature, hexlify(publicSigningKey)): if not highlevelcrypto.verify(
logger.info('ECDSA verify passed (within decryptAndCheckPubkeyPayload)') signedData, signature, hexlify(publicSigningKey)):
else: logger.info(
logger.info('ECDSA verify failed (within decryptAndCheckPubkeyPayload)') 'ECDSA verify failed (within decryptAndCheckPubkeyPayload)')
return 'failed' return 'failed'
logger.info(
'ECDSA verify passed (within decryptAndCheckPubkeyPayload)')
sha = hashlib.new('sha512') sha = hashlib.new('sha512')
sha.update(publicSigningKey + publicEncryptionKey) sha.update(publicSigningKey + publicEncryptionKey)
ripeHasher = hashlib.new('ripemd160') ripeHasher = hashlib.new('ripemd160')
@ -404,34 +417,37 @@ def decryptAndCheckPubkeyPayload(data, address):
embeddedRipe = ripeHasher.digest() embeddedRipe = ripeHasher.digest()
if embeddedRipe != ripe: if embeddedRipe != ripe:
# Although this pubkey object had the tag were were looking for and was # Although this pubkey object had the tag were were looking for
# encrypted with the correct encryption key, it doesn't contain the # and was encrypted with the correct encryption key,
# correct pubkeys. Someone is either being malicious or using buggy software. # it doesn't contain the correct pubkeys. Someone is
logger.info('Pubkey decryption was UNsuccessful due to RIPE mismatch.') # either being malicious or using buggy software.
logger.info(
'Pubkey decryption was UNsuccessful due to RIPE mismatch.')
return 'failed' return 'failed'
# Everything checked out. Insert it into the pubkeys table. # Everything checked out. Insert it into the pubkeys table.
logger.info( logger.info(
os.linesep.join([ 'within decryptAndCheckPubkeyPayload, '
'within decryptAndCheckPubkeyPayload,' 'addressVersion: %s, streamNumber: %s\nripe %s\n'
' addressVersion: %s, streamNumber: %s' % addressVersion, streamNumber, 'publicSigningKey in hex: %s\npublicEncryptionKey in hex: %s',
'ripe %s' % hexlify(ripe), addressVersion, streamNumber, hexlify(ripe),
'publicSigningKey in hex: %s' % hexlify(publicSigningKey), hexlify(publicSigningKey), hexlify(publicEncryptionKey)
'publicEncryptionKey in hex: %s' % hexlify(publicEncryptionKey),
])
) )
t = (address, addressVersion, storedData, int(time.time()), 'yes') t = (address, addressVersion, storedData, int(time.time()), 'yes')
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t) sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
return 'successful' return 'successful'
except varintDecodeError: except varintDecodeError:
logger.info('Pubkey decryption was UNsuccessful due to a malformed varint.') logger.info(
'Pubkey decryption was UNsuccessful due to a malformed varint.')
return 'failed' return 'failed'
except Exception: except Exception:
logger.critical( logger.critical(
'Pubkey decryption was UNsuccessful because of an unhandled exception! This is definitely a bug! \n%s', 'Pubkey decryption was UNsuccessful because of'
traceback.format_exc()) ' an unhandled exception! This is definitely a bug! \n%s',
traceback.format_exc()
)
return 'failed' return 'failed'

View File

@ -6,10 +6,8 @@ import sys
import stat import stat
import time import time
import threading import threading
import traceback
import hashlib import hashlib
import subprocess import subprocess
from struct import unpack
from binascii import hexlify from binascii import hexlify
from pyelliptic import arithmetic from pyelliptic import arithmetic
@ -18,10 +16,8 @@ import state
import highlevelcrypto import highlevelcrypto
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
from addresses import ( from addresses import decodeAddress, encodeVarint
decodeAddress, encodeVarint, decodeVarint, varintDecodeError from helper_sql import sqlQuery
)
from helper_sql import sqlQuery, sqlExecute
verbose = 1 verbose = 1
@ -281,149 +277,6 @@ def fixSensitiveFilePermissions(filename, hasEnabledKeys):
raise raise
def isBitSetWithinBitfield(fourByteString, n):
# Uses MSB 0 bit numbering across 4 bytes of data
n = 31 - n
x, = unpack('>L', fourByteString)
return x & 2**n != 0
def decryptAndCheckPubkeyPayload(data, address):
"""
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)
readPosition = 20 # bypass the nonce, time, and object type
embeddedAddressVersion, varintLength = \
decodeVarint(data[readPosition:readPosition + 10])
readPosition += varintLength
embeddedStreamNumber, varintLength = \
decodeVarint(data[readPosition:readPosition + 10])
readPosition += varintLength
# We'll store the address version and stream number
# (and some more) in the pubkeys table.
storedData = data[20:readPosition]
if addressVersion != embeddedAddressVersion:
logger.info(
'Pubkey decryption was UNsuccessful'
' due to address version mismatch.')
return 'failed'
if streamNumber != embeddedStreamNumber:
logger.info(
'Pubkey decryption was UNsuccessful'
' due to stream number mismatch.')
return 'failed'
tag = data[readPosition:readPosition + 32]
readPosition += 32
# the time through the tag. More data is appended onto
# signedData below after the decryption.
signedData = data[8:readPosition]
encryptedData = data[readPosition:]
# Let us try to decrypt the pubkey
toAddress, cryptorObject = state.neededPubkeys[tag]
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 caught
# by the UI or API and an error given to the user.
return 'failed'
try:
decryptedData = cryptorObject.decrypt(encryptedData)
except:
# Someone must have encrypted some data with a different key
# but tagged it with a tag for which we are watching.
logger.info('Pubkey decryption was unsuccessful.')
return 'failed'
readPosition = 0
# bitfieldBehaviors = decryptedData[readPosition:readPosition + 4]
readPosition += 4
publicSigningKey = \
'\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
publicEncryptionKey = \
'\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
specifiedNonceTrialsPerByte, specifiedNonceTrialsPerByteLength = \
decodeVarint(decryptedData[readPosition:readPosition + 10])
readPosition += specifiedNonceTrialsPerByteLength
specifiedPayloadLengthExtraBytes, \
specifiedPayloadLengthExtraBytesLength = \
decodeVarint(decryptedData[readPosition:readPosition + 10])
readPosition += specifiedPayloadLengthExtraBytesLength
storedData += decryptedData[:readPosition]
signedData += decryptedData[:readPosition]
signatureLength, signatureLengthLength = \
decodeVarint(decryptedData[readPosition:readPosition + 10])
readPosition += signatureLengthLength
signature = decryptedData[readPosition:readPosition + signatureLength]
if not highlevelcrypto.verify(
signedData, signature, hexlify(publicSigningKey)):
logger.info(
'ECDSA verify failed (within decryptAndCheckPubkeyPayload)')
return 'failed'
logger.info(
'ECDSA verify passed (within decryptAndCheckPubkeyPayload)')
sha = hashlib.new('sha512')
sha.update(publicSigningKey + publicEncryptionKey)
ripeHasher = hashlib.new('ripemd160')
ripeHasher.update(sha.digest())
embeddedRipe = ripeHasher.digest()
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 pubkeys. Someone is
# either being malicious or using buggy software.
logger.info(
'Pubkey decryption was UNsuccessful due to RIPE mismatch.')
return 'failed'
# Everything checked out. Insert it into the pubkeys table.
logger.info(
'within decryptAndCheckPubkeyPayload, '
'addressVersion: %s, streamNumber: %s\nripe %s\n'
'publicSigningKey in hex: %s\npublicEncryptionKey in hex: %s',
addressVersion, streamNumber, hexlify(ripe),
hexlify(publicSigningKey), hexlify(publicEncryptionKey)
)
t = (address, addressVersion, storedData, int(time.time()), 'yes')
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
return 'successful'
except varintDecodeError:
logger.info(
'Pubkey decryption was UNsuccessful due to a malformed varint.')
return 'failed'
except Exception:
logger.critical(
'Pubkey decryption was UNsuccessful because of'
' an unhandled exception! This is definitely a bug! \n%s' %
traceback.format_exc()
)
return 'failed'
def openKeysFile(): def openKeysFile():
if 'linux' in sys.platform: if 'linux' in sys.platform:
subprocess.call(["xdg-open", state.appdata + 'keys.dat']) subprocess.call(["xdg-open", state.appdata + 'keys.dat'])