Added pycrypto fallback for RIPEMD160 hash function

This commit is contained in:
Dmitri Bogomolov 2019-01-31 17:42:22 +02:00
parent 4eec124225
commit e163137893
Signed by untrusted user: g1itch
GPG Key ID: 720A756F18DEED13
7 changed files with 118 additions and 62 deletions

View File

@ -1,2 +1,3 @@
python_prctl python_prctl
psutil psutil
pycrypto

View File

@ -15,6 +15,7 @@ import highlevelcrypto
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
from addresses import decodeAddress, encodeAddress, encodeVarint from addresses import decodeAddress, encodeAddress, encodeVarint
from fallback import RIPEMD160Hash
from helper_threading import StoppableThread from helper_threading import StoppableThread
@ -133,16 +134,17 @@ class addressGenerator(threading.Thread, StoppableThread):
potentialPrivEncryptionKey = OpenSSL.rand(32) potentialPrivEncryptionKey = OpenSSL.rand(32)
potentialPubEncryptionKey = highlevelcrypto.pointMult( potentialPubEncryptionKey = highlevelcrypto.pointMult(
potentialPrivEncryptionKey) potentialPrivEncryptionKey)
ripe = hashlib.new('ripemd160')
sha = hashlib.new('sha512') sha = hashlib.new('sha512')
sha.update( sha.update(
potentialPubSigningKey + potentialPubEncryptionKey) potentialPubSigningKey + potentialPubEncryptionKey)
ripe.update(sha.digest()) ripe = RIPEMD160Hash(sha.digest()).digest()
if ripe.digest()[:numberOfNullBytesDemandedOnFrontOfRipeHash] == '\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash: if (
ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash] ==
'\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash
):
break break
logger.info( logger.info(
'Generated address with ripe digest: %s', 'Generated address with ripe digest: %s', hexlify(ripe))
hexlify(ripe.digest()))
try: try:
logger.info( logger.info(
'Address generator calculated %s addresses at %s' 'Address generator calculated %s addresses at %s'
@ -156,7 +158,7 @@ class addressGenerator(threading.Thread, StoppableThread):
# time.time() - startTime equaled zero. # time.time() - startTime equaled zero.
pass pass
address = encodeAddress( address = encodeAddress(
addressVersionNumber, streamNumber, ripe.digest()) addressVersionNumber, streamNumber, ripe)
# An excellent way for us to store our keys # An excellent way for us to store our keys
# is in Wallet Import Format. Let us convert now. # is in Wallet Import Format. Let us convert now.
@ -203,7 +205,7 @@ class addressGenerator(threading.Thread, StoppableThread):
shared.reloadMyAddressHashes() shared.reloadMyAddressHashes()
if addressVersionNumber == 3: if addressVersionNumber == 3:
queues.workerQueue.put(( queues.workerQueue.put((
'sendOutOrStoreMyV3Pubkey', ripe.digest())) 'sendOutOrStoreMyV3Pubkey', ripe))
elif addressVersionNumber == 4: elif addressVersionNumber == 4:
queues.workerQueue.put(( queues.workerQueue.put((
'sendOutOrStoreMyV4Pubkey', address)) 'sendOutOrStoreMyV4Pubkey', address))
@ -255,17 +257,18 @@ class addressGenerator(threading.Thread, StoppableThread):
potentialPrivEncryptionKey) potentialPrivEncryptionKey)
signingKeyNonce += 2 signingKeyNonce += 2
encryptionKeyNonce += 2 encryptionKeyNonce += 2
ripe = hashlib.new('ripemd160')
sha = hashlib.new('sha512') sha = hashlib.new('sha512')
sha.update( sha.update(
potentialPubSigningKey + potentialPubEncryptionKey) potentialPubSigningKey + potentialPubEncryptionKey)
ripe.update(sha.digest()) ripe = RIPEMD160Hash(sha.digest()).digest()
if ripe.digest()[:numberOfNullBytesDemandedOnFrontOfRipeHash] == '\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash: if (
ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash] ==
'\x00' * numberOfNullBytesDemandedOnFrontOfRipeHash
):
break break
logger.info( logger.info(
'Generated address with ripe digest: %s', 'Generated address with ripe digest: %s', hexlify(ripe))
hexlify(ripe.digest()))
try: try:
logger.info( logger.info(
'Address generator calculated %s addresses' 'Address generator calculated %s addresses'
@ -280,7 +283,7 @@ class addressGenerator(threading.Thread, StoppableThread):
# time.time() - startTime equaled zero. # time.time() - startTime equaled zero.
pass pass
address = encodeAddress( address = encodeAddress(
addressVersionNumber, streamNumber, ripe.digest()) addressVersionNumber, streamNumber, ripe)
saveAddressToDisk = True saveAddressToDisk = True
# If we are joining an existing chan, let us check # If we are joining an existing chan, let us check
@ -357,13 +360,13 @@ class addressGenerator(threading.Thread, StoppableThread):
)) ))
listOfNewAddressesToSendOutThroughTheAPI.append( listOfNewAddressesToSendOutThroughTheAPI.append(
address) address)
shared.myECCryptorObjects[ripe.digest()] = \ shared.myECCryptorObjects[ripe] = \
highlevelcrypto.makeCryptor( highlevelcrypto.makeCryptor(
hexlify(potentialPrivEncryptionKey)) hexlify(potentialPrivEncryptionKey))
shared.myAddressesByHash[ripe.digest()] = address shared.myAddressesByHash[ripe] = address
tag = hashlib.sha512(hashlib.sha512( tag = hashlib.sha512(hashlib.sha512(
encodeVarint(addressVersionNumber) + encodeVarint(addressVersionNumber) +
encodeVarint(streamNumber) + ripe.digest() encodeVarint(streamNumber) + ripe
).digest()).digest()[32:] ).digest()).digest()[32:]
shared.myAddressesByTag[tag] = address shared.myAddressesByTag[tag] = address
if addressVersionNumber == 3: if addressVersionNumber == 3:
@ -371,7 +374,7 @@ class addressGenerator(threading.Thread, StoppableThread):
# the worker thread won't send out # the worker thread won't send out
# the pubkey over the network. # the pubkey over the network.
queues.workerQueue.put(( queues.workerQueue.put((
'sendOutOrStoreMyV3Pubkey', ripe.digest())) 'sendOutOrStoreMyV3Pubkey', ripe))
elif addressVersionNumber == 4: elif addressVersionNumber == 4:
queues.workerQueue.put(( queues.workerQueue.put((
'sendOutOrStoreMyV4Pubkey', address)) 'sendOutOrStoreMyV4Pubkey', address))

View File

@ -24,6 +24,7 @@ import queues
import state import state
import tr import tr
from debug import logger from debug import logger
from fallback import RIPEMD160Hash
import l10n import l10n
@ -288,9 +289,7 @@ class objectProcessor(threading.Thread):
sha = hashlib.new('sha512') sha = hashlib.new('sha512')
sha.update( sha.update(
'\x04' + publicSigningKey + '\x04' + publicEncryptionKey) '\x04' + publicSigningKey + '\x04' + publicEncryptionKey)
ripeHasher = hashlib.new('ripemd160') ripe = RIPEMD160Hash(sha.digest()).digest()
ripeHasher.update(sha.digest())
ripe = ripeHasher.digest()
logger.debug( logger.debug(
'within recpubkey, addressVersion: %s, streamNumber: %s' 'within recpubkey, addressVersion: %s, streamNumber: %s'
@ -354,9 +353,7 @@ class objectProcessor(threading.Thread):
sha = hashlib.new('sha512') sha = hashlib.new('sha512')
sha.update(publicSigningKey + publicEncryptionKey) sha.update(publicSigningKey + publicEncryptionKey)
ripeHasher = hashlib.new('ripemd160') ripe = RIPEMD160Hash(sha.digest()).digest()
ripeHasher.update(sha.digest())
ripe = ripeHasher.digest()
logger.debug( logger.debug(
'within recpubkey, addressVersion: %s, streamNumber: %s' 'within recpubkey, addressVersion: %s, streamNumber: %s'
@ -575,10 +572,9 @@ class objectProcessor(threading.Thread):
# calculate the fromRipe. # calculate the fromRipe.
sha = hashlib.new('sha512') sha = hashlib.new('sha512')
sha.update(pubSigningKey + pubEncryptionKey) sha.update(pubSigningKey + pubEncryptionKey)
ripe = hashlib.new('ripemd160') ripe = RIPEMD160Hash(sha.digest()).digest()
ripe.update(sha.digest())
fromAddress = encodeAddress( fromAddress = encodeAddress(
sendersAddressVersionNumber, sendersStreamNumber, ripe.digest()) sendersAddressVersionNumber, sendersStreamNumber, ripe)
# Let's store the public key in case we want to reply to this # Let's store the public key in case we want to reply to this
# person. # person.
@ -897,9 +893,7 @@ class objectProcessor(threading.Thread):
sha = hashlib.new('sha512') sha = hashlib.new('sha512')
sha.update(sendersPubSigningKey + sendersPubEncryptionKey) sha.update(sendersPubSigningKey + sendersPubEncryptionKey)
ripeHasher = hashlib.new('ripemd160') calculatedRipe = RIPEMD160Hash(sha.digest()).digest()
ripeHasher.update(sha.digest())
calculatedRipe = ripeHasher.digest()
if broadcastVersion == 4: if broadcastVersion == 4:
if toRipe != calculatedRipe: if toRipe != calculatedRipe:

View File

@ -162,37 +162,13 @@ def try_import(module, log_extra=False):
return False return False
# We need to check hashlib for RIPEMD-160, as it won't be available def check_ripemd160():
# if OpenSSL is not linked against or the linked OpenSSL has RIPEMD """Check availability of the RIPEMD160 hash function"""
# disabled.
def check_hashlib():
"""Do hashlib check.
The hashlib module check with version as if it included or not
in The Python Standard library, it's a module containing an
interface to the most popular hashing algorithms. hashlib
implements some of the algorithms, however if OpenSSL
installed, hashlib is able to use this algorithms as well.
"""
if sys.hexversion < 0x020500F0:
logger.error(
'The hashlib module is not included in this version of Python.')
return False
import hashlib
if '_hashlib' not in hashlib.__dict__:
logger.error(
'The RIPEMD-160 hash algorithm is not available.'
' The hashlib module is not linked against OpenSSL.')
return False
try: try:
hashlib.new('ripemd160') from fallback import RIPEMD160Hash
except ValueError: except ImportError:
logger.error(
'The RIPEMD-160 hash algorithm is not available.'
' The hashlib module utilizes an OpenSSL library with'
' RIPEMD disabled.')
return False return False
return True return RIPEMD160Hash is not None
def check_sqlite(): def check_sqlite():
@ -446,7 +422,7 @@ def check_dependencies(verbose=False, optional=False):
' or greater is required.') ' or greater is required.')
has_all_dependencies = False has_all_dependencies = False
check_functions = [check_hashlib, check_sqlite, check_openssl] check_functions = [check_ripemd160, check_sqlite, check_openssl]
if optional: if optional:
check_functions.extend([check_msgpack, check_pyqt, check_curses]) check_functions.extend([check_msgpack, check_pyqt, check_curses])

View File

@ -1,3 +1,26 @@
""" """
.. todo:: hello world .. todo:: hello world
""" """
import hashlib
# We need to check hashlib for RIPEMD-160, as it won't be available
# if OpenSSL is not linked against or the linked OpenSSL has RIPEMD
# disabled.
try:
hashlib.new('ripemd160')
except ValueError:
try:
from Crypto.Hash import RIPEMD
except ImportError:
RIPEMD160Hash = None
else:
RIPEMD160Hash = RIPEMD.RIPEMD160Hash
else:
def RIPEMD160Hash(data=None):
"""hashlib based RIPEMD160Hash"""
hasher = hashlib.new('ripemd160')
if data:
hasher.update(data)
return hasher

View File

@ -25,6 +25,7 @@ from addresses import (
encodeVarint, decodeVarint, decodeAddress, varintDecodeError) encodeVarint, decodeVarint, decodeAddress, varintDecodeError)
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
from fallback import RIPEMD160Hash
from helper_sql import sqlExecute from helper_sql import sqlExecute
from version import softwareVersion from version import softwareVersion
@ -411,9 +412,7 @@ def decryptAndCheckPubkeyPayload(data, address):
sha = hashlib.new('sha512') sha = hashlib.new('sha512')
sha.update(publicSigningKey + publicEncryptionKey) sha.update(publicSigningKey + publicEncryptionKey)
ripeHasher = hashlib.new('ripemd160') embeddedRipe = RIPEMD160Hash(sha.digest()).digest()
ripeHasher.update(sha.digest())
embeddedRipe = ripeHasher.digest()
if embeddedRipe != ripe: if embeddedRipe != ripe:
# Although this pubkey object had the tag were were looking for # Although this pubkey object had the tag were were looking for

60
src/tests/test_crypto.py Normal file
View File

@ -0,0 +1,60 @@
"""
Test the alternatives for crypto primitives
"""
import hashlib
import unittest
from abc import ABCMeta, abstractmethod
from binascii import hexlify, unhexlify
try:
from Crypto.Hash import RIPEMD
except ImportError:
RIPEMD = None
# These keys are from addresses test script
sample_pubsigningkey = unhexlify(
'044a367f049ec16cb6b6118eb734a9962d10b8db59c890cd08f210c43ff08bdf09d'
'16f502ca26cd0713f38988a1237f1fc8fa07b15653c996dc4013af6d15505ce')
sample_pubencryptionkey = unhexlify(
'044597d59177fc1d89555d38915f581b5ff2286b39d022ca0283d2bdd5c36be5d3c'
'e7b9b97792327851a562752e4b79475d1f51f5a71352482b241227f45ed36a9')
sample_ripe = '003cd097eb7f35c87b5dc8b4538c22cb55312a9f'
_sha = hashlib.new('sha512')
_sha.update(sample_pubsigningkey + sample_pubencryptionkey)
pubkey_sha = _sha.digest()
class RIPEMD160TestCase(object):
"""Base class for RIPEMD160 test case"""
__metaclass__ = ABCMeta
@abstractmethod
def _hashdigest(self, data):
"""RIPEMD160 digest implementation"""
pass
def test_hash_string(self):
"""Check RIPEMD160 hash function on string"""
self.assertEqual(hexlify(self._hashdigest(pubkey_sha)), sample_ripe)
class TestHashlib(RIPEMD160TestCase, unittest.TestCase):
"""RIPEMD160 test case for hashlib"""
@staticmethod
def _hashdigest(data):
hasher = hashlib.new('ripemd160')
hasher.update(data)
return hasher.digest()
@unittest.skipUnless(RIPEMD, 'pycrypto package not found')
class TestCrypto(RIPEMD160TestCase, unittest.TestCase):
"""RIPEMD160 test case for Crypto"""
@staticmethod
def _hashdigest(data):
return RIPEMD.RIPEMD160Hash(data).digest()