Refactor using of crypto functions #1796

Closed
g1itch wants to merge 14 commits from crypto into v0.6
20 changed files with 436 additions and 303 deletions

View File

@ -2,11 +2,16 @@
Operations with addresses Operations with addresses
""" """
# pylint: disable=inconsistent-return-statements # pylint: disable=inconsistent-return-statements
import hashlib
import logging import logging
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from struct import pack, unpack from struct import pack, unpack
try:
from highlevelcrypto import double_sha512
except ImportError:
from .highlevelcrypto import double_sha512
logger = logging.getLogger('default') logger = logging.getLogger('default')
@ -134,15 +139,6 @@ def decodeVarint(data):
return (encodedValue, 9) return (encodedValue, 9)
def calculateInventoryHash(data):
"""Calculate inventory hash from object data"""
sha = hashlib.new('sha512')
sha2 = hashlib.new('sha512')
sha.update(data)
sha2.update(sha.digest())
return sha2.digest()[0:32]
def encodeAddress(version, stream, ripe): def encodeAddress(version, stream, ripe):
"""Convert ripe to address""" """Convert ripe to address"""
if version >= 2 and version < 4: if version >= 2 and version < 4:
@ -166,12 +162,7 @@ def encodeAddress(version, stream, ripe):
storedBinaryData = encodeVarint(version) + encodeVarint(stream) + ripe storedBinaryData = encodeVarint(version) + encodeVarint(stream) + ripe
# Generate the checksum # Generate the checksum
sha = hashlib.new('sha512') checksum = double_sha512(storedBinaryData)[0:4]
sha.update(storedBinaryData)
currentHash = sha.digest()
sha = hashlib.new('sha512')
sha.update(currentHash)
checksum = sha.digest()[0:4]
# FIXME: encodeBase58 should take binary data, to reduce conversions # FIXME: encodeBase58 should take binary data, to reduce conversions
# encodeBase58(storedBinaryData + checksum) # encodeBase58(storedBinaryData + checksum)
@ -207,13 +198,7 @@ def decodeAddress(address):
data = unhexlify(hexdata) data = unhexlify(hexdata)
checksum = data[-4:] checksum = data[-4:]
sha = hashlib.new('sha512') if checksum != double_sha512(data[:-4])[0:4]:
sha.update(data[:-4])
currentHash = sha.digest()
sha = hashlib.new('sha512')
sha.update(currentHash)
if checksum != sha.digest()[0:4]:
status = 'checksumfailed' status = 'checksumfailed'
return status, 0, 0, '' return status, 0, 0, ''

View File

@ -71,6 +71,8 @@ from binascii import hexlify, unhexlify
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
from struct import pack from struct import pack
from six.moves import queue
import defaults import defaults
import helper_inbox import helper_inbox
import helper_sent import helper_sent
@ -82,17 +84,17 @@ import shutdown
import state import state
from addresses import ( from addresses import (
addBMIfNotPresent, addBMIfNotPresent,
calculateInventoryHash,
decodeAddress, decodeAddress,
decodeVarint, decodeVarint,
varintDecodeError varintDecodeError
) )
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
from helper_sql import SqlBulkExecute, sqlExecute, sqlQuery, sqlStoredProcedure, sql_ready from helper_sql import (
SqlBulkExecute, sqlExecute, sqlQuery, sql_ready, sqlStoredProcedure)
from highlevelcrypto import calculateInventoryHash
from inventory import Inventory from inventory import Inventory
from network.threads import StoppableThread from network.threads import StoppableThread
from six.moves import queue
from version import softwareVersion from version import softwareVersion
try: # TODO: write tests for XML vulnerabilities try: # TODO: write tests for XML vulnerabilities

View File

@ -15,8 +15,6 @@ from addresses import decodeAddress, encodeAddress, encodeVarint
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from fallback import RIPEMD160Hash from fallback import RIPEMD160Hash
from network import StoppableThread from network import StoppableThread
from pyelliptic import arithmetic
from pyelliptic.openssl import OpenSSL
from six.moves import configparser, queue from six.moves import configparser, queue
@ -129,17 +127,13 @@ class addressGenerator(StoppableThread):
# the \x00 or \x00\x00 bytes thus making the address shorter. # the \x00 or \x00\x00 bytes thus making the address shorter.
startTime = time.time() startTime = time.time()
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0 numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0
potentialPrivSigningKey = OpenSSL.rand(32) privSigningKey, pubSigningKey = highlevelcrypto.random_keys()
potentialPubSigningKey = highlevelcrypto.pointMult(
potentialPrivSigningKey)
while True: while True:
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
potentialPrivEncryptionKey = OpenSSL.rand(32) potentialPrivEncryptionKey, potentialPubEncryptionKey = \
potentialPubEncryptionKey = highlevelcrypto.pointMult( highlevelcrypto.random_keys()
potentialPrivEncryptionKey)
sha = hashlib.new('sha512') sha = hashlib.new('sha512')
sha.update( sha.update(pubSigningKey + potentialPubEncryptionKey)
potentialPubSigningKey + potentialPubEncryptionKey)
ripe = RIPEMD160Hash(sha.digest()).digest() ripe = RIPEMD160Hash(sha.digest()).digest()
if ( if (
ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash] ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash]
@ -163,20 +157,10 @@ class addressGenerator(StoppableThread):
address = encodeAddress( address = encodeAddress(
addressVersionNumber, streamNumber, ripe) addressVersionNumber, streamNumber, ripe)
# An excellent way for us to store our keys privSigningKeyWIF = highlevelcrypto.encodeWalletImportFormat(
# is in Wallet Import Format. Let us convert now. privSigningKey)
# https://en.bitcoin.it/wiki/Wallet_import_format privEncryptionKeyWIF = highlevelcrypto.encodeWalletImportFormat(
privSigningKey = b'\x80' + potentialPrivSigningKey potentialPrivEncryptionKey)
checksum = hashlib.sha256(hashlib.sha256(
privSigningKey).digest()).digest()[0:4]
privSigningKeyWIF = arithmetic.changebase(
privSigningKey + checksum, 256, 58)
privEncryptionKey = b'\x80' + potentialPrivEncryptionKey
checksum = hashlib.sha256(hashlib.sha256(
privEncryptionKey).digest()).digest()[0:4]
privEncryptionKeyWIF = arithmetic.changebase(
privEncryptionKey + checksum, 256, 58)
BMConfigParser().add_section(address) BMConfigParser().add_section(address)
BMConfigParser().set(address, 'label', label) BMConfigParser().set(address, 'label', label)
@ -246,18 +230,15 @@ class addressGenerator(StoppableThread):
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0 numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0
while True: while True:
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1
potentialPrivSigningKey = hashlib.sha512( potentialPrivSigningKey, potentialPubSigningKey = \
deterministicPassphrase highlevelcrypto.deterministic_keys(
+ encodeVarint(signingKeyNonce) deterministicPassphrase,
).digest()[:32] encodeVarint(signingKeyNonce))
potentialPrivEncryptionKey = hashlib.sha512( potentialPrivEncryptionKey, potentialPubEncryptionKey = \
deterministicPassphrase highlevelcrypto.deterministic_keys(
+ encodeVarint(encryptionKeyNonce) deterministicPassphrase,
).digest()[:32] encodeVarint(encryptionKeyNonce))
potentialPubSigningKey = highlevelcrypto.pointMult(
potentialPrivSigningKey)
potentialPubEncryptionKey = highlevelcrypto.pointMult(
potentialPrivEncryptionKey)
signingKeyNonce += 2 signingKeyNonce += 2
encryptionKeyNonce += 2 encryptionKeyNonce += 2
sha = hashlib.new('sha512') sha = hashlib.new('sha512')
@ -300,21 +281,12 @@ class addressGenerator(StoppableThread):
saveAddressToDisk = False saveAddressToDisk = False
if saveAddressToDisk and live: if saveAddressToDisk and live:
# An excellent way for us to store our keys is privSigningKeyWIF = \
# in Wallet Import Format. Let us convert now. highlevelcrypto.encodeWalletImportFormat(
# https://en.bitcoin.it/wiki/Wallet_import_format potentialPrivSigningKey)
privSigningKey = b'\x80' + potentialPrivSigningKey privEncryptionKeyWIF = \
checksum = hashlib.sha256(hashlib.sha256( highlevelcrypto.encodeWalletImportFormat(
privSigningKey).digest()).digest()[0:4] potentialPrivEncryptionKey)
privSigningKeyWIF = arithmetic.changebase(
privSigningKey + checksum, 256, 58)
privEncryptionKey = b'\x80' + \
potentialPrivEncryptionKey
checksum = hashlib.sha256(hashlib.sha256(
privEncryptionKey).digest()).digest()[0:4]
privEncryptionKeyWIF = arithmetic.changebase(
privEncryptionKey + checksum, 256, 58)
try: try:
BMConfigParser().add_section(address) BMConfigParser().add_section(address)
@ -367,10 +339,10 @@ class addressGenerator(StoppableThread):
highlevelcrypto.makeCryptor( highlevelcrypto.makeCryptor(
hexlify(potentialPrivEncryptionKey)) hexlify(potentialPrivEncryptionKey))
shared.myAddressesByHash[ripe] = address shared.myAddressesByHash[ripe] = address
tag = hashlib.sha512(hashlib.sha512( tag = highlevelcrypto.double_sha512(
encodeVarint(addressVersionNumber) encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + ripe + encodeVarint(streamNumber) + ripe
).digest()).digest()[32:] )[32:]
shared.myAddressesByTag[tag] = address shared.myAddressesByTag[tag] = address
if addressVersionNumber == 3: if addressVersionNumber == 3:
# If this is a chan address, # If this is a chan address,

View File

@ -23,7 +23,7 @@ import queues
import shared import shared
import state import state
from addresses import ( from addresses import (
calculateInventoryHash, decodeAddress, decodeVarint, decodeAddress, decodeVarint,
encodeAddress, encodeVarint, varintDecodeError encodeAddress, encodeVarint, varintDecodeError
) )
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
@ -450,7 +450,7 @@ class objectProcessor(threading.Thread):
streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = \ streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = \
decodeVarint(data[readPosition:readPosition + 9]) decodeVarint(data[readPosition:readPosition + 9])
readPosition += streamNumberAsClaimedByMsgLength readPosition += streamNumberAsClaimedByMsgLength
inventoryHash = calculateInventoryHash(data) inventoryHash = highlevelcrypto.calculateInventoryHash(data)
initialDecryptionSuccessful = False initialDecryptionSuccessful = False
# This is not an acknowledgement bound for me. See if it is a message # This is not an acknowledgement bound for me. See if it is a message
@ -580,8 +580,7 @@ class objectProcessor(threading.Thread):
helper_bitcoin.calculateTestnetAddressFromPubkey(pubSigningKey) helper_bitcoin.calculateTestnetAddressFromPubkey(pubSigningKey)
) )
# Used to detect and ignore duplicate messages in our inbox # Used to detect and ignore duplicate messages in our inbox
sigHash = hashlib.sha512( sigHash = highlevelcrypto.double_sha512(signature)[32:]
hashlib.sha512(signature).digest()).digest()[32:]
# calculate the fromRipe. # calculate the fromRipe.
sha = hashlib.new('sha512') sha = hashlib.new('sha512')
@ -751,7 +750,7 @@ class objectProcessor(threading.Thread):
state.numberOfBroadcastsProcessed += 1 state.numberOfBroadcastsProcessed += 1
queues.UISignalQueue.put(( queues.UISignalQueue.put((
'updateNumberOfBroadcastsProcessed', 'no data')) 'updateNumberOfBroadcastsProcessed', 'no data'))
inventoryHash = calculateInventoryHash(data) inventoryHash = highlevelcrypto.calculateInventoryHash(data)
readPosition = 20 # bypass the nonce, time, and object type readPosition = 20 # bypass the nonce, time, and object type
broadcastVersion, broadcastVersionLength = decodeVarint( broadcastVersion, broadcastVersionLength = decodeVarint(
data[readPosition:readPosition + 9]) data[readPosition:readPosition + 9])
@ -885,10 +884,10 @@ class objectProcessor(threading.Thread):
' itself. Ignoring message.' ' itself. Ignoring message.'
) )
elif broadcastVersion == 5: elif broadcastVersion == 5:
calculatedTag = hashlib.sha512(hashlib.sha512( calculatedTag = highlevelcrypto.double_sha512(
encodeVarint(sendersAddressVersion) encodeVarint(sendersAddressVersion)
+ encodeVarint(sendersStream) + calculatedRipe + encodeVarint(sendersStream) + calculatedRipe
).digest()).digest()[32:] )[32:]
if calculatedTag != embeddedTag: if calculatedTag != embeddedTag:
return logger.debug( return logger.debug(
'The tag and encryption key used to encrypt this' 'The tag and encryption key used to encrypt this'
@ -918,8 +917,7 @@ class objectProcessor(threading.Thread):
return return
logger.debug('ECDSA verify passed') logger.debug('ECDSA verify passed')
# Used to detect and ignore duplicate messages in our inbox # Used to detect and ignore duplicate messages in our inbox
sigHash = hashlib.sha512( sigHash = highlevelcrypto.double_sha512(signature)[32:]
hashlib.sha512(signature).digest()).digest()[32:]
fromAddress = encodeAddress( fromAddress = encodeAddress(
sendersAddressVersion, sendersStream, calculatedRipe) sendersAddressVersion, sendersStream, calculatedRipe)
@ -993,10 +991,10 @@ class objectProcessor(threading.Thread):
# Let us create the tag from the address and see if we were waiting # Let us create the tag from the address and see if we were waiting
# for it. # for it.
elif addressVersion >= 4: elif addressVersion >= 4:
tag = hashlib.sha512(hashlib.sha512( tag = highlevelcrypto.double_sha512(
encodeVarint(addressVersion) + encodeVarint(streamNumber) encodeVarint(addressVersion) + encodeVarint(streamNumber)
+ ripe + ripe
).digest()).digest()[32:] )[32:]
if tag in state.neededPubkeys: if tag in state.neededPubkeys:
del state.neededPubkeys[tag] del state.neededPubkeys[tag]
self.sendMessages(address) self.sendMessages(address)

View File

@ -25,9 +25,7 @@ import queues
import shared import shared
import state import state
import tr import tr
from addresses import ( from addresses import decodeAddress, decodeVarint, encodeVarint
calculateInventoryHash, decodeAddress, decodeVarint, encodeVarint
)
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from helper_sql import sqlExecute, sqlQuery from helper_sql import sqlExecute, sqlQuery
from inventory import Inventory from inventory import Inventory
@ -50,6 +48,8 @@ class singleWorker(StoppableThread):
def __init__(self): def __init__(self):
super(singleWorker, self).__init__(name="singleWorker") super(singleWorker, self).__init__(name="singleWorker")
self.digestAlg = BMConfigParser().safeGet(
'bitmessagesettings', 'digestalg', 'sha256')
proofofwork.init() proofofwork.init()
def stopThread(self): def stopThread(self):
@ -73,18 +73,16 @@ class singleWorker(StoppableThread):
queryreturn = sqlQuery( queryreturn = sqlQuery(
'''SELECT DISTINCT toaddress FROM sent''' '''SELECT DISTINCT toaddress FROM sent'''
''' WHERE (status='awaitingpubkey' AND folder='sent')''') ''' WHERE (status='awaitingpubkey' AND folder='sent')''')
for row in queryreturn: for toAddress, in queryreturn:
toAddress, = row toAddressVersionNumber, toStreamNumber, toRipe = \
# toStatus decodeAddress(toAddress)[1:]
_, toAddressVersionNumber, toStreamNumber, toRipe = \
decodeAddress(toAddress)
if toAddressVersionNumber <= 3: if toAddressVersionNumber <= 3:
state.neededPubkeys[toAddress] = 0 state.neededPubkeys[toAddress] = 0
elif toAddressVersionNumber >= 4: elif toAddressVersionNumber >= 4:
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( doubleHashOfAddressData = highlevelcrypto.double_sha512(
encodeVarint(toAddressVersionNumber) encodeVarint(toAddressVersionNumber)
+ encodeVarint(toStreamNumber) + toRipe + encodeVarint(toStreamNumber) + toRipe
).digest()).digest() )
# Note that this is the first half of the sha512 hash. # Note that this is the first half of the sha512 hash.
privEncryptionKey = doubleHashOfAddressData[:32] privEncryptionKey = doubleHashOfAddressData[:32]
tag = doubleHashOfAddressData[32:] tag = doubleHashOfAddressData[32:]
@ -195,15 +193,20 @@ class singleWorker(StoppableThread):
self.logger.info("Quitting...") self.logger.info("Quitting...")
def _getKeysForAddress(self, address): def _getKeysForAddress(self, address):
try:
privSigningKeyBase58 = BMConfigParser().get( privSigningKeyBase58 = BMConfigParser().get(
address, 'privsigningkey') address, 'privsigningkey')
privEncryptionKeyBase58 = BMConfigParser().get( privEncryptionKeyBase58 = BMConfigParser().get(
address, 'privencryptionkey') address, 'privencryptionkey')
except (configparser.NoSectionError, configparser.NoOptionError):
self.logger.error(
'Could not read or decode privkey for address %s', address)
raise ValueError
privSigningKeyHex = hexlify(shared.decodeWalletImportFormat( privSigningKeyHex = hexlify(
privSigningKeyBase58)) highlevelcrypto.decodeWalletImportFormat(privSigningKeyBase58))
privEncryptionKeyHex = hexlify(shared.decodeWalletImportFormat( privEncryptionKeyHex = hexlify(
privEncryptionKeyBase58)) highlevelcrypto.decodeWalletImportFormat(privEncryptionKeyBase58))
# The \x04 on the beginning of the public keys are not sent. # The \x04 on the beginning of the public keys are not sent.
# This way there is only one acceptable way to encode # This way there is only one acceptable way to encode
@ -254,9 +257,7 @@ class singleWorker(StoppableThread):
message once it is done with the POW""" message once it is done with the POW"""
# Look up my stream number based on my address hash # Look up my stream number based on my address hash
myAddress = shared.myAddressesByHash[adressHash] myAddress = shared.myAddressesByHash[adressHash]
# status addressVersionNumber, streamNumber = decodeAddress(myAddress)[1:3]
_, addressVersionNumber, streamNumber, adressHash = (
decodeAddress(myAddress))
# 28 days from now plus or minus five minutes # 28 days from now plus or minus five minutes
TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300)) TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
@ -269,18 +270,15 @@ class singleWorker(StoppableThread):
payload += protocol.getBitfield(myAddress) payload += protocol.getBitfield(myAddress)
try: try:
# privSigningKeyHex, privEncryptionKeyHex pubSigningKey, pubEncryptionKey = self._getKeysForAddress(
_, _, pubSigningKey, pubEncryptionKey = \ myAddress)[2:]
self._getKeysForAddress(myAddress) except ValueError:
except (configparser.NoSectionError, configparser.NoOptionError) as err: return
self.logger.warning("Section or Option did not found: %s", err) except Exception:
except Exception as err: return self.logger.error(
self.logger.error(
'Error within doPOWForMyV2Pubkey. Could not read' 'Error within doPOWForMyV2Pubkey. Could not read'
' the keys from the keys.dat file for a requested' ' the keys from the keys.dat file for a requested'
' address. %s\n', err ' address. %s\n', exc_info=True)
)
return
payload += pubSigningKey + pubEncryptionKey payload += pubSigningKey + pubEncryptionKey
@ -288,7 +286,7 @@ class singleWorker(StoppableThread):
payload = self._doPOWDefaults( payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For pubkey message)') payload, TTL, log_prefix='(For pubkey message)')
inventoryHash = calculateInventoryHash(payload) inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
objectType = 1 objectType = 1
Inventory()[inventoryHash] = ( Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, '') objectType, streamNumber, payload, embeddedTime, '')
@ -318,9 +316,8 @@ class singleWorker(StoppableThread):
try: try:
myAddress = shared.myAddressesByHash[adressHash] myAddress = shared.myAddressesByHash[adressHash]
except KeyError: except KeyError:
# The address has been deleted. return self.logger.warning( # The address has been deleted.
self.logger.warning("Can't find %s in myAddressByHash", hexlify(adressHash)) "Can't find %s in myAddressByHash", hexlify(adressHash))
return
if BMConfigParser().safeGetBoolean(myAddress, 'chan'): if BMConfigParser().safeGetBoolean(myAddress, 'chan'):
self.logger.info('This is a chan address. Not sending pubkey.') self.logger.info('This is a chan address. Not sending pubkey.')
return return
@ -351,15 +348,13 @@ class singleWorker(StoppableThread):
# , privEncryptionKeyHex # , privEncryptionKeyHex
privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \ privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \
self._getKeysForAddress(myAddress) self._getKeysForAddress(myAddress)
except (configparser.NoSectionError, configparser.NoOptionError) as err: except ValueError:
self.logger.warning("Section or Option did not found: %s", err) return
except Exception as err: except Exception:
self.logger.error( return self.logger.error(
'Error within sendOutOrStoreMyV3Pubkey. Could not read' 'Error within sendOutOrStoreMyV3Pubkey. Could not read'
' the keys from the keys.dat file for a requested' ' the keys from the keys.dat file for a requested'
' address. %s\n', err ' address. %s\n', exc_info=True)
)
return
payload += pubSigningKey + pubEncryptionKey payload += pubSigningKey + pubEncryptionKey
@ -368,7 +363,8 @@ class singleWorker(StoppableThread):
payload += encodeVarint(BMConfigParser().getint( payload += encodeVarint(BMConfigParser().getint(
myAddress, 'payloadlengthextrabytes')) myAddress, 'payloadlengthextrabytes'))
signature = highlevelcrypto.sign(payload, privSigningKeyHex) signature = highlevelcrypto.sign(
payload, privSigningKeyHex, self.digestAlg)
payload += encodeVarint(len(signature)) payload += encodeVarint(len(signature))
payload += signature payload += signature
@ -376,7 +372,7 @@ class singleWorker(StoppableThread):
payload = self._doPOWDefaults( payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For pubkey message)') payload, TTL, log_prefix='(For pubkey message)')
inventoryHash = calculateInventoryHash(payload) inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
objectType = 1 objectType = 1
Inventory()[inventoryHash] = ( Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, '') objectType, streamNumber, payload, embeddedTime, '')
@ -425,15 +421,13 @@ class singleWorker(StoppableThread):
# , privEncryptionKeyHex # , privEncryptionKeyHex
privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \ privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \
self._getKeysForAddress(myAddress) self._getKeysForAddress(myAddress)
except (configparser.NoSectionError, configparser.NoOptionError) as err: except ValueError:
self.logger.warning("Section or Option did not found: %s", err) return
except Exception as err: except Exception:
self.logger.error( return self.logger.error(
'Error within sendOutOrStoreMyV4Pubkey. Could not read' 'Error within sendOutOrStoreMyV4Pubkey. Could not read'
' the keys from the keys.dat file for a requested' ' the keys from the keys.dat file for a requested'
' address. %s\n', err ' address. %s\n', exc_info=True)
)
return
dataToEncrypt += pubSigningKey + pubEncryptionKey dataToEncrypt += pubSigningKey + pubEncryptionKey
@ -449,14 +443,13 @@ class singleWorker(StoppableThread):
# unencrypted, the pubkey with part of the hash so that nodes # unencrypted, the pubkey with part of the hash so that nodes
# know which pubkey object to try to decrypt # know which pubkey object to try to decrypt
# when they want to send a message. # when they want to send a message.
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( doubleHashOfAddressData = highlevelcrypto.double_sha512(
encodeVarint(addressVersionNumber) encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + addressHash + encodeVarint(streamNumber) + addressHash
).digest()).digest() )
payload += doubleHashOfAddressData[32:] # the tag payload += doubleHashOfAddressData[32:] # the tag
signature = highlevelcrypto.sign( signature = highlevelcrypto.sign(
payload + dataToEncrypt, privSigningKeyHex payload + dataToEncrypt, privSigningKeyHex, self.digestAlg)
)
dataToEncrypt += encodeVarint(len(signature)) dataToEncrypt += encodeVarint(len(signature))
dataToEncrypt += signature dataToEncrypt += signature
@ -469,7 +462,7 @@ class singleWorker(StoppableThread):
payload = self._doPOWDefaults( payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For pubkey message)') payload, TTL, log_prefix='(For pubkey message)')
inventoryHash = calculateInventoryHash(payload) inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
objectType = 1 objectType = 1
Inventory()[inventoryHash] = ( Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, objectType, streamNumber, payload, embeddedTime,
@ -505,7 +498,7 @@ class singleWorker(StoppableThread):
objectType = protocol.OBJECT_ONIONPEER objectType = protocol.OBJECT_ONIONPEER
# FIXME: ideally the objectPayload should be signed # FIXME: ideally the objectPayload should be signed
objectPayload = encodeVarint(peer.port) + protocol.encodeHost(peer.host) objectPayload = encodeVarint(peer.port) + protocol.encodeHost(peer.host)
tag = calculateInventoryHash(objectPayload) tag = highlevelcrypto.calculateInventoryHash(objectPayload)
if Inventory().by_type_and_tag(objectType, tag): if Inventory().by_type_and_tag(objectType, tag):
return # not expired return # not expired
@ -519,7 +512,7 @@ class singleWorker(StoppableThread):
payload = self._doPOWDefaults( payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For onionpeer object)') payload, TTL, log_prefix='(For onionpeer object)')
inventoryHash = calculateInventoryHash(payload) inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
Inventory()[inventoryHash] = ( Inventory()[inventoryHash] = (
objectType, streamNumber, buffer(payload), objectType, streamNumber, buffer(payload),
embeddedTime, buffer(tag) embeddedTime, buffer(tag)
@ -613,10 +606,10 @@ class singleWorker(StoppableThread):
payload += encodeVarint(streamNumber) payload += encodeVarint(streamNumber)
if addressVersionNumber >= 4: if addressVersionNumber >= 4:
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( doubleHashOfAddressData = highlevelcrypto.double_sha512(
encodeVarint(addressVersionNumber) encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + ripe + encodeVarint(streamNumber) + ripe
).digest()).digest() )
tag = doubleHashOfAddressData[32:] tag = doubleHashOfAddressData[32:]
payload += tag payload += tag
else: else:
@ -641,7 +634,7 @@ class singleWorker(StoppableThread):
dataToSign = payload + dataToEncrypt dataToSign = payload + dataToEncrypt
signature = highlevelcrypto.sign( signature = highlevelcrypto.sign(
dataToSign, privSigningKeyHex) dataToSign, privSigningKeyHex, self.digestAlg)
dataToEncrypt += encodeVarint(len(signature)) dataToEncrypt += encodeVarint(len(signature))
dataToEncrypt += signature dataToEncrypt += signature
@ -686,7 +679,7 @@ class singleWorker(StoppableThread):
) )
continue continue
inventoryHash = calculateInventoryHash(payload) inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
objectType = 3 objectType = 3
Inventory()[inventoryHash] = ( Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, tag) objectType, streamNumber, payload, embeddedTime, tag)
@ -795,10 +788,10 @@ class singleWorker(StoppableThread):
if toAddressVersionNumber <= 3: if toAddressVersionNumber <= 3:
toTag = '' toTag = ''
else: else:
toTag = hashlib.sha512(hashlib.sha512( toTag = highlevelcrypto.double_sha512(
encodeVarint(toAddressVersionNumber) encodeVarint(toAddressVersionNumber)
+ encodeVarint(toStreamNumber) + toRipe + encodeVarint(toStreamNumber) + toRipe
).digest()).digest()[32:] )[32:]
if toaddress in state.neededPubkeys or \ if toaddress in state.neededPubkeys or \
toTag in state.neededPubkeys: toTag in state.neededPubkeys:
# We already sent a request for the pubkey # We already sent a request for the pubkey
@ -832,11 +825,11 @@ class singleWorker(StoppableThread):
# already contains the toAddress and cryptor # already contains the toAddress and cryptor
# object associated with the tag for this toAddress. # object associated with the tag for this toAddress.
if toAddressVersionNumber >= 4: if toAddressVersionNumber >= 4:
doubleHashOfToAddressData = hashlib.sha512( doubleHashOfToAddressData = \
hashlib.sha512( highlevelcrypto.double_sha512(
encodeVarint(toAddressVersionNumber) + encodeVarint(toStreamNumber) + toRipe encodeVarint(toAddressVersionNumber)
).digest() + encodeVarint(toStreamNumber) + toRipe
).digest() )
# The first half of the sha512 hash. # The first half of the sha512 hash.
privEncryptionKey = doubleHashOfToAddressData[:32] privEncryptionKey = doubleHashOfToAddressData[:32]
# The second half of the sha512 hash. # The second half of the sha512 hash.
@ -1116,7 +1109,8 @@ class singleWorker(StoppableThread):
' from the keys.dat file for our own address. %s\n', ' from the keys.dat file for our own address. %s\n',
err) err)
continue continue
privEncryptionKeyHex = hexlify(shared.decodeWalletImportFormat( privEncryptionKeyHex = hexlify(
highlevelcrypto.decodeWalletImportFormat(
privEncryptionKeyBase58)) privEncryptionKeyBase58))
pubEncryptionKeyBase256 = unhexlify(highlevelcrypto.privToPub( pubEncryptionKeyBase256 = unhexlify(highlevelcrypto.privToPub(
privEncryptionKeyHex))[1:] privEncryptionKeyHex))[1:]
@ -1223,7 +1217,8 @@ class singleWorker(StoppableThread):
payload += fullAckPayload payload += fullAckPayload
dataToSign = pack('>Q', embeddedTime) + '\x00\x00\x00\x02' + \ dataToSign = pack('>Q', embeddedTime) + '\x00\x00\x00\x02' + \
encodeVarint(1) + encodeVarint(toStreamNumber) + payload encodeVarint(1) + encodeVarint(toStreamNumber) + payload
signature = highlevelcrypto.sign(dataToSign, privSigningKeyHex) signature = highlevelcrypto.sign(
dataToSign, privSigningKeyHex, self.digestAlg)
payload += encodeVarint(len(signature)) payload += encodeVarint(len(signature))
payload += signature payload += signature
@ -1301,7 +1296,7 @@ class singleWorker(StoppableThread):
) )
continue continue
inventoryHash = calculateInventoryHash(encryptedPayload) inventoryHash = highlevelcrypto.calculateInventoryHash(encryptedPayload)
objectType = 2 objectType = 2
Inventory()[inventoryHash] = ( Inventory()[inventoryHash] = (
objectType, toStreamNumber, encryptedPayload, embeddedTime, '') objectType, toStreamNumber, encryptedPayload, embeddedTime, '')
@ -1351,8 +1346,7 @@ class singleWorker(StoppableThread):
# the message in our own inbox. # the message in our own inbox.
if BMConfigParser().has_section(toaddress): if BMConfigParser().has_section(toaddress):
# Used to detect and ignore duplicate messages in our inbox # Used to detect and ignore duplicate messages in our inbox
sigHash = hashlib.sha512(hashlib.sha512( sigHash = highlevelcrypto.double_sha512(signature)[32:]
signature).digest()).digest()[32:]
t = (inventoryHash, toaddress, fromaddress, subject, int( t = (inventoryHash, toaddress, fromaddress, subject, int(
time.time()), message, 'inbox', encoding, 0, sigHash) time.time()), message, 'inbox', encoding, 0, sigHash)
helper_inbox.insert(t) helper_inbox.insert(t)
@ -1407,16 +1401,13 @@ class singleWorker(StoppableThread):
# neededPubkeys dictionary. But if we are recovering # neededPubkeys dictionary. But if we are recovering
# from a restart of the client then we have to put it in now. # from a restart of the client then we have to put it in now.
# Note that this is the first half of the sha512 hash. doubleHashOfAddressData = highlevelcrypto.double_sha512(
privEncryptionKey = hashlib.sha512(hashlib.sha512(
encodeVarint(addressVersionNumber) encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + ripe + encodeVarint(streamNumber) + ripe
).digest()).digest()[:32] )
privEncryptionKey = doubleHashOfAddressData[:32]
# Note that this is the second half of the sha512 hash. # Note that this is the second half of the sha512 hash.
tag = hashlib.sha512(hashlib.sha512( tag = doubleHashOfAddressData[32:]
encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + ripe
).digest()).digest()[32:]
if tag not in state.neededPubkeys: if tag not in state.neededPubkeys:
# We'll need this for when we receive a pubkey reply: # We'll need this for when we receive a pubkey reply:
# it will be encrypted and we'll need to decrypt it. # it will be encrypted and we'll need to decrypt it.
@ -1459,7 +1450,7 @@ class singleWorker(StoppableThread):
payload = self._doPOWDefaults(payload, TTL) payload = self._doPOWDefaults(payload, TTL)
inventoryHash = calculateInventoryHash(payload) inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
objectType = 1 objectType = 1
Inventory()[inventoryHash] = ( Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, '') objectType, streamNumber, payload, embeddedTime, '')

View File

@ -25,10 +25,10 @@ def genAckPayload(streamNumber=1, stealthLevel=0):
if stealthLevel == 2: # Generate privacy-enhanced payload if stealthLevel == 2: # Generate privacy-enhanced payload
# Generate a dummy privkey and derive the pubkey # Generate a dummy privkey and derive the pubkey
dummyPubKeyHex = highlevelcrypto.privToPub( dummyPubKeyHex = highlevelcrypto.privToPub(
hexlify(helper_random.randomBytes(32))) hexlify(highlevelcrypto.randomBytes(32)))
# Generate a dummy message of random length # Generate a dummy message of random length
# (the smallest possible standard-formatted message is 234 bytes) # (the smallest possible standard-formatted message is 234 bytes)
dummyMessage = helper_random.randomBytes( dummyMessage = highlevelcrypto.randomBytes(
helper_random.randomrandrange(234, 801)) helper_random.randomrandrange(234, 801))
# Encrypt the message using standard BM encryption (ECIES) # Encrypt the message using standard BM encryption (ECIES)
ackdata = highlevelcrypto.encrypt(dummyMessage, dummyPubKeyHex) ackdata = highlevelcrypto.encrypt(dummyMessage, dummyPubKeyHex)
@ -36,12 +36,12 @@ def genAckPayload(streamNumber=1, stealthLevel=0):
version = 1 version = 1
elif stealthLevel == 1: # Basic privacy payload (random getpubkey) elif stealthLevel == 1: # Basic privacy payload (random getpubkey)
ackdata = helper_random.randomBytes(32) ackdata = highlevelcrypto.randomBytes(32)
acktype = 0 # getpubkey acktype = 0 # getpubkey
version = 4 version = 4
else: # Minimum viable payload (non stealth) else: # Minimum viable payload (non stealth)
ackdata = helper_random.randomBytes(32) ackdata = highlevelcrypto.randomBytes(32)
acktype = 2 # message acktype = 2 # message
version = 1 version = 1

View File

@ -1,12 +1,7 @@
"""Convenience functions for random operations. Not suitable for security / cryptography operations.""" """Convenience functions for random operations. Not suitable for security / cryptography operations."""
import os
import random import random
try:
from pyelliptic.openssl import OpenSSL
except ImportError:
from .pyelliptic.openssl import OpenSSL
NoneType = type(None) NoneType = type(None)
@ -16,14 +11,6 @@ def seed():
random.seed() random.seed()
def randomBytes(n):
"""Method randomBytes."""
try:
return os.urandom(n)
except NotImplementedError:
return OpenSSL.rand(n)
def randomshuffle(population): def randomshuffle(population):
"""Method randomShuffle. """Method randomShuffle.

View File

@ -7,17 +7,85 @@ High level cryptographic functions based on `.pyelliptic` OpenSSL bindings.
`More discussion. <https://github.com/yann2192/pyelliptic/issues/32>`_ `More discussion. <https://github.com/yann2192/pyelliptic/issues/32>`_
""" """
import hashlib
import os
from binascii import hexlify from binascii import hexlify
import pyelliptic import pyelliptic
from pyelliptic import OpenSSL from pyelliptic import OpenSSL
from pyelliptic import arithmetic as a from pyelliptic import arithmetic as a
from bmconfigparser import BMConfigParser
__all__ = ['encrypt', 'makeCryptor', 'pointMult', 'privToPub', 'sign', 'verify'] __all__ = ['encrypt', 'makeCryptor', 'pointMult', 'privToPub', 'sign', 'verify']
# Hashes
def double_sha512(data):
"""Binary double SHA512 digest"""
return hashlib.sha512(hashlib.sha512(data).digest()).digest()
def calculateInventoryHash(data):
"""Calculate inventory hash from object data"""
return double_sha512(data)[:32]
# WIF (uses arithmetic ):
def decodeWalletImportFormat(WIFstring):
"""
Convert private key from base58 that's used in the config file to
8-bit binary string.
"""
fullString = a.changebase(WIFstring, 58, 256)
privkey = fullString[:-4]
if fullString[-4:] != \
hashlib.sha256(hashlib.sha256(privkey).digest()).digest()[:4]:
raise ValueError('Checksum failed')
elif privkey[0:1] == b'\x80': # checksum passed
return privkey[1:]
raise ValueError('No hex 80 prefix')
# An excellent way for us to store our keys
# is in Wallet Import Format. Let us convert now.
# https://en.bitcoin.it/wiki/Wallet_import_format
def encodeWalletImportFormat(privKey):
"""
Convert private key from binary 8-bit string into base58check WIF string.
"""
privKey = b'\x80' + privKey
checksum = hashlib.sha256(hashlib.sha256(privKey).digest()).digest()[0:4]
return a.changebase(privKey + checksum, 256, 58)
# Random
def randomBytes(n):
"""Get n random bytes"""
try:
return os.urandom(n)
except NotImplementedError:
return OpenSSL.rand(n)
# Keys
def random_keys():
"""Return a pair of keys, private and public"""
priv = randomBytes(32)
pub = pointMult(priv)
return priv, pub
def deterministic_keys(passphrase, nonce):
"""Generate keys from *passphrase* and *nonce* (encoded as varint)"""
priv = hashlib.sha512(passphrase + nonce).digest()[:32]
pub = pointMult(priv)
return priv, pub
g1itch commented 2021-12-10 18:02:14 +01:00 (Migrated from github.com)
Review

I'm not sure if these functions should return the pair or only the private key.

I'm not sure if these functions should return the pair or only the private key.
def makeCryptor(privkey): def makeCryptor(privkey):
"""Return a private `.pyelliptic.ECC` instance""" """Return a private `.pyelliptic.ECC` instance"""
private_key = a.changebase(privkey, 16, 256, minlen=32) private_key = a.changebase(privkey, 16, 256, minlen=32)
@ -67,22 +135,17 @@ def decryptFast(msg, cryptor):
return cryptor.decrypt(msg) return cryptor.decrypt(msg)
def sign(msg, hexPrivkey): def sign(msg, hexPrivkey, digestAlg="sha256"):
""" """
Signs with hex private key using SHA1 or SHA256 depending on Signs with hex private key using SHA1 or SHA256 depending on
"digestalg" setting *digestAlg* keyword.
""" """
digestAlg = BMConfigParser().safeGet( if digestAlg not in ("sha1", "sha256"):
'bitmessagesettings', 'digestalg', 'sha256') raise ValueError("Unknown digest algorithm %s" % digestAlg)
if digestAlg == "sha1":
# SHA1, this will eventually be deprecated # SHA1, this will eventually be deprecated
return makeCryptor(hexPrivkey).sign( return makeCryptor(hexPrivkey).sign(
msg, digest_alg=OpenSSL.digest_ecdsa_sha1) msg, digest_alg=OpenSSL.digest_ecdsa_sha1
elif digestAlg == "sha256": if digestAlg == "sha1" else OpenSSL.EVP_sha256)
# SHA256. Eventually this will become the default
return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.EVP_sha256)
else:
raise ValueError("Unknown digest algorithm %s" % digestAlg)
def verify(msg, sig, hexPubkey): def verify(msg, sig, hexPubkey):

View File

@ -6,7 +6,7 @@ import time
import protocol import protocol
import state import state
from addresses import calculateInventoryHash from highlevelcrypto import calculateInventoryHash
from inventory import Inventory from inventory import Inventory
from network.dandelion import Dandelion from network.dandelion import Dandelion

View File

@ -17,7 +17,7 @@ import knownnodes
import protocol import protocol
import state import state
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from helper_random import randomBytes from highlevelcrypto import randomBytes
from inventory import Inventory from inventory import Inventory
from network.advanceddispatcher import AdvancedDispatcher from network.advanceddispatcher import AdvancedDispatcher
from network.assemble import assemble_addr from network.assemble import assemble_addr

View File

@ -4,7 +4,6 @@ Proof of work calculation
""" """
import ctypes import ctypes
import hashlib
import os import os
import sys import sys
import tempfile import tempfile
@ -12,6 +11,7 @@ import time
from struct import pack, unpack from struct import pack, unpack
from subprocess import call from subprocess import call
import highlevelcrypto
import openclpow import openclpow
import paths import paths
import queues import queues
@ -87,13 +87,20 @@ def _set_idle():
pass pass
def trial_value(nonce, initialHash):
"""Calculate PoW trial value"""
trialValue, = unpack(
'>Q', highlevelcrypto.double_sha512(
pack('>Q', nonce) + initialHash)[0:8])
return trialValue
def _pool_worker(nonce, initialHash, target, pool_size): def _pool_worker(nonce, initialHash, target, pool_size):
_set_idle() _set_idle()
trialValue = float('inf') trialValue = float('inf')
while trialValue > target: while trialValue > target:
nonce += pool_size nonce += pool_size
trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512( trialValue = trial_value(nonce, initialHash)
pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
return [trialValue, nonce] return [trialValue, nonce]
@ -103,10 +110,9 @@ def _doSafePoW(target, initialHash):
trialValue = float('inf') trialValue = float('inf')
while trialValue > target and state.shutdown == 0: while trialValue > target and state.shutdown == 0:
nonce += 1 nonce += 1
trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512( trialValue = trial_value(nonce, initialHash)
pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
if state.shutdown != 0: if state.shutdown != 0:
raise StopIteration("Interrupted") # pylint: misplaced-bare-raise raise StopIteration("Interrupted")
logger.debug("Safe PoW done") logger.debug("Safe PoW done")
return [trialValue, nonce] return [trialValue, nonce]
@ -163,7 +169,7 @@ def _doCPoW(target, initialHash):
logger.debug("C PoW start") logger.debug("C PoW start")
nonce = bmpow(out_h, out_m) nonce = bmpow(out_h, out_m)
trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512(pack('>Q', nonce) + initialHash).digest()).digest()[0:8]) trialValue = trial_value(nonce, initialHash)
if state.shutdown != 0: if state.shutdown != 0:
raise StopIteration("Interrupted") raise StopIteration("Interrupted")
logger.debug("C PoW done") logger.debug("C PoW done")
@ -173,7 +179,7 @@ def _doCPoW(target, initialHash):
def _doGPUPoW(target, initialHash): def _doGPUPoW(target, initialHash):
logger.debug("GPU PoW start") logger.debug("GPU PoW start")
nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target) nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target)
trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512(pack('>Q', nonce) + initialHash).digest()).digest()[0:8]) trialValue = trial_value(nonce, initialHash)
if trialValue > target: if trialValue > target:
deviceNames = ", ".join(gpu.name for gpu in openclpow.enabledGpus) deviceNames = ", ".join(gpu.name for gpu in openclpow.enabledGpus)
queues.UISignalQueue.put(( queues.UISignalQueue.put((

View File

@ -269,12 +269,11 @@ def isProofOfWorkSufficient(
if payloadLengthExtraBytes < defaults.networkDefaultPayloadLengthExtraBytes: if payloadLengthExtraBytes < defaults.networkDefaultPayloadLengthExtraBytes:
payloadLengthExtraBytes = defaults.networkDefaultPayloadLengthExtraBytes payloadLengthExtraBytes = defaults.networkDefaultPayloadLengthExtraBytes
endOfLifeTime, = unpack('>Q', data[8:16]) endOfLifeTime, = unpack('>Q', data[8:16])
TTL = endOfLifeTime - (int(recvTime) if recvTime else int(time.time())) TTL = endOfLifeTime - int(recvTime if recvTime else time.time())
if TTL < 300: if TTL < 300:
TTL = 300 TTL = 300
POW, = unpack('>Q', hashlib.sha512(hashlib.sha512( POW, = unpack('>Q', highlevelcrypto.double_sha512(
data[:8] + hashlib.sha512(data[8:]).digest() data[:8] + hashlib.sha512(data[8:]).digest())[0:8])
).digest()).digest()[0:8])
return POW <= 2 ** 64 / ( return POW <= 2 ** 64 / (
nonceTrialsPerByte * ( nonceTrialsPerByte * (
len(data) + payloadLengthExtraBytes len(data) + payloadLengthExtraBytes

View File

@ -23,6 +23,12 @@ sample_privsigningkey = \
sample_privencryptionkey = \ sample_privencryptionkey = \
b'4b0b73a54e19b059dc274ab69df095fe699f43b17397bca26fdf40f4d7400a3a' b'4b0b73a54e19b059dc274ab69df095fe699f43b17397bca26fdf40f4d7400a3a'
# [chan] bitmessage
sample_wif_privsigningkey = \
b'a2e8b841a531c1c558ee0680c396789c7a2ea3ac4795ae3f000caf9fe367d144'
sample_wif_privencryptionkey = \
b'114ec0e2dca24a826a0eed064b0405b0ac148abc3b1d52729697f4d7b873fdc6'
sample_factor = \ sample_factor = \
66858749573256452658262553961707680376751171096153613379801854825275240965733 66858749573256452658262553961707680376751171096153613379801854825275240965733
# G * sample_factor # G * sample_factor
@ -40,6 +46,38 @@ class TestArithmetic(unittest.TestCase):
sample_point, sample_point,
arithmetic.base10_multiply(arithmetic.G, sample_factor)) arithmetic.base10_multiply(arithmetic.G, sample_factor))
def test_base58(self):
"""Test encoding/decoding base58 using arithmetic functions"""
self.assertEqual(
arithmetic.decode(arithmetic.changebase(
b'2cWzSnwjJ7yRP3nLEWUV5LisTZyREWSzUK', 58, 256), 256),
25152821841976547050350277460563089811513157529113201589004)
self.assertEqual(
arithmetic.decode(arithmetic.changebase(
b'2DBPTgeSawWYZceFD69AbDT5q4iUWtj1ZN', 58, 256), 256),
18875720106589866286514488037355423395410802084648916523381)
self.assertEqual(
arithmetic.changebase(arithmetic.encode(
25152821841976547050350277460563089811513157529113201589004,
256), 256, 58), b'2cWzSnwjJ7yRP3nLEWUV5LisTZyREWSzUK')
self.assertEqual(
arithmetic.changebase(arithmetic.encode(
18875720106589866286514488037355423395410802084648916523381,
256), 256, 58), b'2DBPTgeSawWYZceFD69AbDT5q4iUWtj1ZN')
def test_wif(self):
"""Decode WIFs of [chan] bitmessage and check the keys"""
self.assertEqual(
sample_wif_privsigningkey,
arithmetic.changebase(arithmetic.changebase(
b'5K42shDERM5g7Kbi3JT5vsAWpXMqRhWZpX835M2pdSoqQQpJMYm', 58, 256
)[1:-4], 256, 16))
self.assertEqual(
sample_wif_privencryptionkey,
arithmetic.changebase(arithmetic.changebase(
b'5HwugVWm31gnxtoYcvcK7oywH2ezYTh6Y4tzRxsndAeMi6NHqpA', 58, 256
)[1:-4], 256, 16))
def test_decode(self): def test_decode(self):
"""Decode sample privsigningkey from hex to int and compare to factor""" """Decode sample privsigningkey from hex to int and compare to factor"""
self.assertEqual( self.assertEqual(

View File

@ -4,9 +4,10 @@ Test if OpenSSL is working correctly
import unittest import unittest
try: try:
from pyelliptic.ecc import ECC
from pyelliptic.openssl import OpenSSL from pyelliptic.openssl import OpenSSL
except ImportError: except ImportError:
from pybitmessage.pyelliptic import OpenSSL from pybitmessage.pyelliptic import ECC, OpenSSL
try: try:
OpenSSL.BN_bn2binpad OpenSSL.BN_bn2binpad
@ -55,3 +56,10 @@ class TestOpenSSL(unittest.TestCase):
if b.raw != c.raw.rjust(OpenSSL.BN_num_bytes(n), b'\x00'): if b.raw != c.raw.rjust(OpenSSL.BN_num_bytes(n), b'\x00'):
bad += 1 bad += 1
self.assertEqual(bad, 0) self.assertEqual(bad, 0)
def test_random_keys(self):
"""A dummy test for random keys in ECC object"""
eccobj = ECC(curve='secp256k1')
self.assertEqual(len(eccobj.privkey), 32)
pubkey = eccobj.get_pubkey()
self.assertEqual(pubkey[:4], b'\x02\xca\x00\x20')

View File

@ -23,8 +23,6 @@ from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
from helper_sql import sqlQuery from helper_sql import sqlQuery
from pyelliptic import arithmetic
myECCryptorObjects = {} myECCryptorObjects = {}
MyECSubscriptionCryptorObjects = {} MyECSubscriptionCryptorObjects = {}
@ -76,35 +74,6 @@ def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
return False return False
def decodeWalletImportFormat(WIFstring):
# pylint: disable=inconsistent-return-statements
"""
Convert private key from base58 that's used in the config file to
8-bit binary string
"""
fullString = arithmetic.changebase(WIFstring, 58, 256)
privkey = fullString[:-4]
if fullString[-4:] != \
hashlib.sha256(hashlib.sha256(privkey).digest()).digest()[:4]:
logger.critical(
'Major problem! When trying to decode one of your'
' private keys, the checksum failed. Here are the first'
' 6 characters of the PRIVATE key: %s',
str(WIFstring)[:6]
)
os._exit(0) # pylint: disable=protected-access
# return ""
elif privkey[0] == '\x80': # checksum passed
return privkey[1:]
logger.critical(
'Major problem! When trying to decode one of your private keys,'
' the checksum passed but the key doesn\'t begin with hex 80.'
' Here is the PRIVATE key: %s', WIFstring
)
os._exit(0) # pylint: disable=protected-access
def reloadMyAddressHashes(): def reloadMyAddressHashes():
"""Reload keys for user's addresses from the config file""" """Reload keys for user's addresses from the config file"""
logger.debug('reloading keys from keys.dat file') logger.debug('reloading keys from keys.dat file')
@ -118,29 +87,39 @@ def reloadMyAddressHashes():
hasEnabledKeys = False hasEnabledKeys = False
for addressInKeysFile in BMConfigParser().addresses(): for addressInKeysFile in BMConfigParser().addresses():
isEnabled = BMConfigParser().getboolean(addressInKeysFile, 'enabled') isEnabled = BMConfigParser().getboolean(addressInKeysFile, 'enabled')
if isEnabled: if not isEnabled:
continue
hasEnabledKeys = True hasEnabledKeys = True
# status
addressVersionNumber, streamNumber, hashobj = decodeAddress(addressInKeysFile)[1:] addressVersionNumber, streamNumber, hashobj = decodeAddress(
if addressVersionNumber in (2, 3, 4): addressInKeysFile)[1:]
# Returns a simple 32 bytes of information encoded if addressVersionNumber not in (2, 3, 4):
# in 64 Hex characters, or null if there was an error. logger.error(
privEncryptionKey = hexlify(decodeWalletImportFormat( 'Error in reloadMyAddressHashes: Can\'t handle'
BMConfigParser().get(addressInKeysFile, 'privencryptionkey'))) ' address versions other than 2, 3, or 4.')
continue
# Returns a simple 32 bytes of information encoded in 64 Hex characters.
try:
privEncryptionKey = hexlify(
highlevelcrypto.decodeWalletImportFormat(
BMConfigParser().get(addressInKeysFile, 'privencryptionkey')
))
except ValueError:
logger.error(
'Error in reloadMyAddressHashes: failed to decode'
' one of the private keys for address %s', addressInKeysFile)
continue
# It is 32 bytes encoded as 64 hex characters # It is 32 bytes encoded as 64 hex characters
if len(privEncryptionKey) == 64: if len(privEncryptionKey) == 64:
myECCryptorObjects[hashobj] = \ myECCryptorObjects[hashobj] = \
highlevelcrypto.makeCryptor(privEncryptionKey) highlevelcrypto.makeCryptor(privEncryptionKey)
myAddressesByHash[hashobj] = addressInKeysFile myAddressesByHash[hashobj] = addressInKeysFile
tag = hashlib.sha512(hashlib.sha512( tag = highlevelcrypto.double_sha512(
encodeVarint(addressVersionNumber) encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + hashobj).digest()).digest()[32:] + encodeVarint(streamNumber) + hashobj)[32:]
myAddressesByTag[tag] = addressInKeysFile myAddressesByTag[tag] = addressInKeysFile
else:
logger.error(
'Error in reloadMyAddressHashes: Can\'t handle'
' address versions other than 2, 3, or 4.'
)
if not keyfileSecure: if not keyfileSecure:
fixSensitiveFilePermissions(os.path.join( fixSensitiveFilePermissions(os.path.join(
@ -174,10 +153,10 @@ def reloadBroadcastSendersForWhichImWatching():
MyECSubscriptionCryptorObjects[hashobj] = \ MyECSubscriptionCryptorObjects[hashobj] = \
highlevelcrypto.makeCryptor(hexlify(privEncryptionKey)) highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
else: else:
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( doubleHashOfAddressData = highlevelcrypto.double_sha512(
encodeVarint(addressVersionNumber) encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + hashobj + encodeVarint(streamNumber) + hashobj
).digest()).digest() )
tag = doubleHashOfAddressData[32:] tag = doubleHashOfAddressData[32:]
privEncryptionKey = doubleHashOfAddressData[:32] privEncryptionKey = doubleHashOfAddressData[:32]
MyECSubscriptionCryptorObjects[tag] = \ MyECSubscriptionCryptorObjects[tag] = \

View File

@ -2,6 +2,11 @@
from binascii import unhexlify from binascii import unhexlify
# hello, page 1 of the Specification
sample_double_sha512 = unhexlify(
'0592a10584ffabf96539f3d780d776828c67da1ab5b169e9e8aed838aaecc9ed36d49ff14'
'23c55f019e050c66c6324f53588be88894fef4dcffdb74b98e2b200')
magic = 0xE9BEB4D9 magic = 0xE9BEB4D9
@ -28,17 +33,39 @@ sample_point = (
94730058721143827257669456336351159718085716196507891067256111928318063085006 94730058721143827257669456336351159718085716196507891067256111928318063085006
) )
sample_seed = 'TIGER, tiger, burning bright. In the forests of the night' sample_seed = b'TIGER, tiger, burning bright. In the forests of the night'
# Deterministic addresses with stream 1 and versions 3, 4 # RIPE hash on step 22 with signing key nonce 42
sample_deterministic_ripe = b'00cfb69416ae76f68a81c459de4e13460c7d17eb' sample_deterministic_ripe = b'00cfb69416ae76f68a81c459de4e13460c7d17eb'
# Deterministic addresses with stream 1 and versions 3, 4
sample_deterministic_addr3 = 'BM-2DBPTgeSawWYZceFD69AbDT5q4iUWtj1ZN' sample_deterministic_addr3 = 'BM-2DBPTgeSawWYZceFD69AbDT5q4iUWtj1ZN'
sample_deterministic_addr4 = 'BM-2cWzSnwjJ7yRP3nLEWUV5LisTZyREWSzUK' sample_deterministic_addr4 = 'BM-2cWzSnwjJ7yRP3nLEWUV5LisTZyREWSzUK'
sample_daddr3_512 = 18875720106589866286514488037355423395410802084648916523381 sample_daddr3_512 = 18875720106589866286514488037355423395410802084648916523381
sample_daddr4_512 = 25152821841976547050350277460563089811513157529113201589004 sample_daddr4_512 = 25152821841976547050350277460563089811513157529113201589004
sample_statusbar_msg = "new status bar message" sample_statusbar_msg = 'new status bar message'
sample_inbox_msg_ids = ['27e644765a3e4b2e973ee7ccf958ea20', '51fc5531-3989-4d69-bbb5-68d64b756f5b', sample_inbox_msg_ids = [
'27e644765a3e4b2e973ee7ccf958ea20', '51fc5531-3989-4d69-bbb5-68d64b756f5b',
'2c975c515f8b414db5eea60ba57ba455', 'bc1f2d8a-681c-4cc0-9a12-6067c7e1ac24'] '2c975c515f8b414db5eea60ba57ba455', 'bc1f2d8a-681c-4cc0-9a12-6067c7e1ac24']
# second address in sample_test_subscription_address is for the announcement broadcast # second address in sample_subscription_addresses
sample_test_subscription_address = ['BM-2cWQLCBGorT9pUGkYSuGGVr9LzE4mRnQaq', 'BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw'] # is for the announcement broadcast
sample_subscription_addresses = [
'BM-2cWQLCBGorT9pUGkYSuGGVr9LzE4mRnQaq',
'BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw']
sample_subscription_name = 'test sub' sample_subscription_name = 'test sub'
sample_msg = unhexlify(
'0592a10584ffabf96539f3d780d776828c67da1ab5b169e9e8aed838aaecc9ed36d49ff'
'1423c55f019e050c66c6324f53588be88894fef4dcffdb74b98e2b200')
sample_sig = unhexlify(
'304402202302475351db6b822de15d922e29397541f10d8a19780ba2ca4a920b1035f075'
'02205e5bba40d5f07a24c23a89ba5f01a3828371dfbb685dd5375fa1c29095fd232b')
sample_sig_sha1 = unhexlify(
'304502203b50123af78b4e40f5f819ae5b8786f48826e56d0f3e65744708a493f5b65de1'
'0221009ddce2981ea143c0ac70404a535327e774adce8eebbae2d35104f1d326255f9a')
# [chan] bitmessage
sample_wif_privsigningkey = unhexlify(
b'a2e8b841a531c1c558ee0680c396789c7a2ea3ac4795ae3f000caf9fe367d144')
sample_wif_privencryptionkey = unhexlify(
b'114ec0e2dca24a826a0eed064b0405b0ac148abc3b1d52729697f4d7b873fdc6')

View File

@ -2,12 +2,13 @@
import unittest import unittest
from binascii import unhexlify from binascii import unhexlify
from pybitmessage import addresses from pybitmessage import addresses, highlevelcrypto
from .samples import ( from .samples import (
sample_address, sample_daddr3_512, sample_daddr4_512, sample_address, sample_daddr3_512, sample_daddr4_512,
sample_deterministic_addr4, sample_deterministic_addr3, sample_deterministic_addr4, sample_deterministic_addr3,
sample_deterministic_ripe, sample_ripe) sample_deterministic_ripe, sample_ripe,
sample_wif_privsigningkey, sample_wif_privencryptionkey)
sample_addr3 = sample_deterministic_addr3.split('-')[1] sample_addr3 = sample_deterministic_addr3.split('-')[1]
sample_addr4 = sample_deterministic_addr4.split('-')[1] sample_addr4 = sample_deterministic_addr4.split('-')[1]
@ -59,3 +60,26 @@ class TestAddresses(unittest.TestCase):
sample_addr4, addresses.encodeBase58(sample_daddr4_512)) sample_addr4, addresses.encodeBase58(sample_daddr4_512))
PeterSurda commented 2021-11-15 08:23:35 +01:00 (Migrated from github.com)
Review

these binary data should also be a variable in samples.

these binary data should also be a variable in samples.
self.assertEqual( self.assertEqual(
sample_addr3, addresses.encodeBase58(sample_daddr3_512)) sample_addr3, addresses.encodeBase58(sample_daddr3_512))
def test_wif(self):
"""Decode WIFs of [chan] bitmessage and check the keys"""
self.assertEqual(
sample_wif_privsigningkey,
highlevelcrypto.decodeWalletImportFormat(
b'5K42shDERM5g7Kbi3JT5vsAWpXMqRhWZpX835M2pdSoqQQpJMYm'))
self.assertEqual(
sample_wif_privencryptionkey,
highlevelcrypto.decodeWalletImportFormat(
b'5HwugVWm31gnxtoYcvcK7oywH2ezYTh6Y4tzRxsndAeMi6NHqpA'))
self.assertEqual(
b'5K42shDERM5g7Kbi3JT5vsAWpXMqRhWZpX835M2pdSoqQQpJMYm',
highlevelcrypto.encodeWalletImportFormat(
sample_wif_privsigningkey))
self.assertEqual(
b'5HwugVWm31gnxtoYcvcK7oywH2ezYTh6Y4tzRxsndAeMi6NHqpA',
highlevelcrypto.encodeWalletImportFormat(
sample_wif_privencryptionkey))
with self.assertRaises(ValueError):
highlevelcrypto.decodeWalletImportFormat(
b'5HwugVWm31gnxtoYcvcK7oywH2ezYTh6Y4tzRxsndAeMi6NHq')

View File

@ -12,8 +12,10 @@ from six.moves import xmlrpc_client # nosec
import psutil import psutil
from .samples import ( from .samples import (
sample_seed, sample_deterministic_addr3, sample_deterministic_addr4, sample_statusbar_msg, sample_seed, sample_deterministic_addr3, sample_deterministic_addr4,
sample_inbox_msg_ids, sample_test_subscription_address, sample_subscription_name) sample_statusbar_msg, sample_inbox_msg_ids, sample_subscription_addresses,
sample_subscription_name
)
from .test_process import TestProcessProto from .test_process import TestProcessProto
@ -263,9 +265,10 @@ class TestAPI(TestAPIProto):
def test_subscriptions(self): def test_subscriptions(self):
"""Testing the API commands related to subscriptions""" """Testing the API commands related to subscriptions"""
self.assertEqual( self.assertEqual(
self.api.addSubscription(sample_test_subscription_address[0], sample_subscription_name.encode('base64')), self.api.addSubscription(
sample_subscription_addresses[0],
sample_subscription_name.encode('base64')),
'Added subscription.' 'Added subscription.'
) )
@ -273,18 +276,19 @@ class TestAPI(TestAPIProto):
# check_address # check_address
for sub in json.loads(self.api.listSubscriptions())['subscriptions']: for sub in json.loads(self.api.listSubscriptions())['subscriptions']:
# special address, added when sqlThread starts # special address, added when sqlThread starts
if sub['address'] == sample_test_subscription_address[0]: if sub['address'] == sample_subscription_addresses[0]:
added_subscription = sub added_subscription = sub
break break
self.assertEqual( self.assertEqual(
base64.decodestring(added_subscription['label']) if added_subscription['label'] else None, base64.decodestring(added_subscription['label'])
if added_subscription['label'] else None,
sample_subscription_name) sample_subscription_name)
self.assertTrue(added_subscription['enabled']) self.assertTrue(added_subscription['enabled'])
for s in json.loads(self.api.listSubscriptions())['subscriptions']: for s in json.loads(self.api.listSubscriptions())['subscriptions']:
# special address, added when sqlThread starts # special address, added when sqlThread starts
if s['address'] == sample_test_subscription_address[1]: if s['address'] == sample_subscription_addresses[1]:
self.assertEqual( self.assertEqual(
base64.decodestring(s['label']), base64.decodestring(s['label']),
'Bitmessage new releases/announcements') 'Bitmessage new releases/announcements')
@ -295,10 +299,10 @@ class TestAPI(TestAPIProto):
'Could not find Bitmessage new releases/announcements' 'Could not find Bitmessage new releases/announcements'
' in subscriptions') ' in subscriptions')
self.assertEqual( self.assertEqual(
self.api.deleteSubscription(sample_test_subscription_address[0]), self.api.deleteSubscription(sample_subscription_addresses[0]),
'Deleted subscription if it existed.') 'Deleted subscription if it existed.')
self.assertEqual( self.assertEqual(
self.api.deleteSubscription(sample_test_subscription_address[1]), self.api.deleteSubscription(sample_subscription_addresses[1]),
'Deleted subscription if it existed.') 'Deleted subscription if it existed.')
self.assertEqual( self.assertEqual(
json.loads(self.api.listSubscriptions())['subscriptions'], []) json.loads(self.api.listSubscriptions())['subscriptions'], [])

View File

@ -16,8 +16,10 @@ except ImportError:
RIPEMD = None RIPEMD = None
from .samples import ( from .samples import (
sample_pubsigningkey, sample_pubencryptionkey, sample_deterministic_ripe, sample_double_sha512,
sample_privsigningkey, sample_privencryptionkey, sample_ripe sample_msg, sample_pubsigningkey, sample_pubencryptionkey,
sample_privsigningkey, sample_privencryptionkey, sample_ripe,
sample_seed, sample_sig, sample_sig_sha1
) )
@ -62,6 +64,56 @@ class TestCrypto(RIPEMD160TestCase, unittest.TestCase):
class TestHighlevelcrypto(unittest.TestCase): class TestHighlevelcrypto(unittest.TestCase):
"""Test highlevelcrypto public functions""" """Test highlevelcrypto public functions"""
# def test_sign(self):
# """Check the signature of the sample_msg created with sample key"""
# self.assertEqual(
# highlevelcrypto.sign(sample_msg, sample_privsigningkey), sample_sig)
def test_double_sha512(self):
"""Reproduce the example on page 1 of the Specification"""
self.assertEqual(
highlevelcrypto.double_sha512(b'hello'), sample_double_sha512)
def test_randomBytes(self):
"""Dummy checks for random bytes"""
for n in (8, 32, 64):
data = highlevelcrypto.randomBytes(n)
self.assertEqual(len(data), n)
self.assertNotEqual(len(set(data)), 1)
self.assertNotEqual(data, highlevelcrypto.randomBytes(n))
def test_random_keys(self):
"""Dummy checks for random keys"""
priv, pub = highlevelcrypto.random_keys()
self.assertEqual(len(priv), 32)
PeterSurda commented 2021-11-15 08:24:01 +01:00 (Migrated from github.com)
Review

ideally b'hello' should also be a variable in samples

ideally `b'hello'` should also be a variable in samples
g1itch commented 2021-11-15 13:10:44 +01:00 (Migrated from github.com)
Review
It's from here: https://pybitmessage-test.readthedocs.io/en/doc/protocol.html#hashes
self.assertEqual(highlevelcrypto.pointMult(priv), pub)
def test_deterministic_keys(self):
"""Generate deterministic keys, make ripe and compare it to sample"""
# encodeVarint(42) = b'*'
sigkey = highlevelcrypto.deterministic_keys(sample_seed, b'*')[1]
enkey = highlevelcrypto.deterministic_keys(sample_seed, b'+')[1]
self.assertEqual(
sample_deterministic_ripe,
hexlify(TestHashlib._hashdigest(
hashlib.sha512(sigkey + enkey).digest())))
def test_verify(self):
"""Verify sample signatures and newly generated ones"""
pubkey_hex = hexlify(sample_pubsigningkey)
# pregenerated signatures
self.assertTrue(
highlevelcrypto.verify(sample_msg, sample_sig, pubkey_hex))
self.assertTrue(
highlevelcrypto.verify(sample_msg, sample_sig_sha1, pubkey_hex))
# new signatures
sig256 = highlevelcrypto.sign(sample_msg, sample_privsigningkey)
sig1 = highlevelcrypto.sign(sample_msg, sample_privsigningkey, "sha1")
self.assertTrue(
highlevelcrypto.verify(sample_msg, sig256, pubkey_hex))
self.assertTrue(
highlevelcrypto.verify(sample_msg, sig1, pubkey_hex))
def test_privtopub(self): def test_privtopub(self):
"""Generate public keys and check the result""" """Generate public keys and check the result"""
self.assertEqual( self.assertEqual(

View File

@ -1,10 +1,10 @@
""" """
Tests for openclpow module Tests for openclpow module
""" """
import hashlib
import unittest import unittest
from struct import pack, unpack
from pybitmessage import openclpow from pybitmessage import openclpow, proofofwork
class TestOpenClPow(unittest.TestCase): class TestOpenClPow(unittest.TestCase):
@ -25,7 +25,5 @@ class TestOpenClPow(unittest.TestCase):
"b93f3ffeba0ef2fd08a8dc2f87b68ae5a0dc819ab57f22ad2c4c9c8618a43b3" "b93f3ffeba0ef2fd08a8dc2f87b68ae5a0dc819ab57f22ad2c4c9c8618a43b3"
).decode("hex") ).decode("hex")
nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target_) nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target_)
trialValue, = unpack( self.assertLess(
'>Q', hashlib.sha512(hashlib.sha512( nonce - proofofwork.trial_value(nonce, initialHash), target_)
pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
self.assertLess((nonce - trialValue), target_)