From 4b3c3876346b896244b180aa28cb1179a5a2ea53 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 27 Jul 2021 22:21:20 +0300 Subject: [PATCH 01/14] Add a test for sign() and verify() --- src/tests/samples.py | 24 +++++++++++++++++++----- src/tests/test_api.py | 22 +++++++++++++--------- src/tests/test_crypto.py | 26 ++++++++++++++++++++++++-- 3 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/tests/samples.py b/src/tests/samples.py index e1a3e676..fd7393e1 100644 --- a/src/tests/samples.py +++ b/src/tests/samples.py @@ -36,9 +36,23 @@ sample_deterministic_addr4 = 'BM-2cWzSnwjJ7yRP3nLEWUV5LisTZyREWSzUK' sample_daddr3_512 = 18875720106589866286514488037355423395410802084648916523381 sample_daddr4_512 = 25152821841976547050350277460563089811513157529113201589004 -sample_statusbar_msg = "new status bar message" -sample_inbox_msg_ids = ['27e644765a3e4b2e973ee7ccf958ea20', '51fc5531-3989-4d69-bbb5-68d64b756f5b', - '2c975c515f8b414db5eea60ba57ba455', 'bc1f2d8a-681c-4cc0-9a12-6067c7e1ac24'] -# second address in sample_test_subscription_address is for the announcement broadcast -sample_test_subscription_address = ['BM-2cWQLCBGorT9pUGkYSuGGVr9LzE4mRnQaq', 'BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw'] +sample_statusbar_msg = 'new status bar message' +sample_inbox_msg_ids = [ + '27e644765a3e4b2e973ee7ccf958ea20', '51fc5531-3989-4d69-bbb5-68d64b756f5b', + '2c975c515f8b414db5eea60ba57ba455', 'bc1f2d8a-681c-4cc0-9a12-6067c7e1ac24'] +# second address in sample_subscription_addresses +# is for the announcement broadcast +sample_subscription_addresses = [ + 'BM-2cWQLCBGorT9pUGkYSuGGVr9LzE4mRnQaq', + 'BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw'] sample_subscription_name = 'test sub' + +sample_msg = unhexlify( + '0592a10584ffabf96539f3d780d776828c67da1ab5b169e9e8aed838aaecc9ed36d49ff' + '1423c55f019e050c66c6324f53588be88894fef4dcffdb74b98e2b200') +sample_sig = unhexlify( + '304402202302475351db6b822de15d922e29397541f10d8a19780ba2ca4a920b1035f075' + '02205e5bba40d5f07a24c23a89ba5f01a3828371dfbb685dd5375fa1c29095fd232b') +sample_sig_sha1 = unhexlify( + '304502203b50123af78b4e40f5f819ae5b8786f48826e56d0f3e65744708a493f5b65de1' + '0221009ddce2981ea143c0ac70404a535327e774adce8eebbae2d35104f1d326255f9a') diff --git a/src/tests/test_api.py b/src/tests/test_api.py index 835b4afb..41a3c911 100644 --- a/src/tests/test_api.py +++ b/src/tests/test_api.py @@ -12,8 +12,10 @@ from six.moves import xmlrpc_client # nosec import psutil from .samples import ( - sample_seed, sample_deterministic_addr3, sample_deterministic_addr4, sample_statusbar_msg, - sample_inbox_msg_ids, sample_test_subscription_address, sample_subscription_name) + sample_seed, sample_deterministic_addr3, sample_deterministic_addr4, + sample_statusbar_msg, sample_inbox_msg_ids, sample_subscription_addresses, + sample_subscription_name +) from .test_process import TestProcessProto @@ -263,9 +265,10 @@ class TestAPI(TestAPIProto): def test_subscriptions(self): """Testing the API commands related to subscriptions""" - 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.' ) @@ -273,18 +276,19 @@ class TestAPI(TestAPIProto): # check_address for sub in json.loads(self.api.listSubscriptions())['subscriptions']: # special address, added when sqlThread starts - if sub['address'] == sample_test_subscription_address[0]: + if sub['address'] == sample_subscription_addresses[0]: added_subscription = sub break 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) self.assertTrue(added_subscription['enabled']) for s in json.loads(self.api.listSubscriptions())['subscriptions']: # special address, added when sqlThread starts - if s['address'] == sample_test_subscription_address[1]: + if s['address'] == sample_subscription_addresses[1]: self.assertEqual( base64.decodestring(s['label']), 'Bitmessage new releases/announcements') @@ -295,10 +299,10 @@ class TestAPI(TestAPIProto): 'Could not find Bitmessage new releases/announcements' ' in subscriptions') self.assertEqual( - self.api.deleteSubscription(sample_test_subscription_address[0]), + self.api.deleteSubscription(sample_subscription_addresses[0]), 'Deleted subscription if it existed.') self.assertEqual( - self.api.deleteSubscription(sample_test_subscription_address[1]), + self.api.deleteSubscription(sample_subscription_addresses[1]), 'Deleted subscription if it existed.') self.assertEqual( json.loads(self.api.listSubscriptions())['subscriptions'], []) diff --git a/src/tests/test_crypto.py b/src/tests/test_crypto.py index 38410359..e5bb600a 100644 --- a/src/tests/test_crypto.py +++ b/src/tests/test_crypto.py @@ -16,8 +16,9 @@ except ImportError: RIPEMD = None from .samples import ( - sample_pubsigningkey, sample_pubencryptionkey, - sample_privsigningkey, sample_privencryptionkey, sample_ripe + sample_msg, sample_pubsigningkey, sample_pubencryptionkey, + sample_privsigningkey, sample_privencryptionkey, sample_ripe, + sample_sig, sample_sig_sha1 ) @@ -62,6 +63,27 @@ class TestCrypto(RIPEMD160TestCase, unittest.TestCase): class TestHighlevelcrypto(unittest.TestCase): """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_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): """Generate public keys and check the result""" self.assertEqual( -- 2.45.1 From 32e7e863f8acbd3afb0abc8c4a389d536155e6fa Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Wed, 28 Jul 2021 00:44:19 +0300 Subject: [PATCH 02/14] Don't use BMConfigParser in highlevelcrypto, instead use digestAlg kwarg --- src/class_singleWorker.py | 13 ++++++++----- src/highlevelcrypto.py | 20 +++++++------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index fea842ea..49c41c07 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -50,6 +50,8 @@ class singleWorker(StoppableThread): def __init__(self): super(singleWorker, self).__init__(name="singleWorker") + self.digestAlg = BMConfigParser().safeGet( + 'bitmessagesettings', 'digestalg', 'sha256') proofofwork.init() def stopThread(self): @@ -368,7 +370,8 @@ class singleWorker(StoppableThread): payload += encodeVarint(BMConfigParser().getint( myAddress, 'payloadlengthextrabytes')) - signature = highlevelcrypto.sign(payload, privSigningKeyHex) + signature = highlevelcrypto.sign( + payload, privSigningKeyHex, self.digestAlg) payload += encodeVarint(len(signature)) payload += signature @@ -455,8 +458,7 @@ class singleWorker(StoppableThread): ).digest()).digest() payload += doubleHashOfAddressData[32:] # the tag signature = highlevelcrypto.sign( - payload + dataToEncrypt, privSigningKeyHex - ) + payload + dataToEncrypt, privSigningKeyHex, self.digestAlg) dataToEncrypt += encodeVarint(len(signature)) dataToEncrypt += signature @@ -641,7 +643,7 @@ class singleWorker(StoppableThread): dataToSign = payload + dataToEncrypt signature = highlevelcrypto.sign( - dataToSign, privSigningKeyHex) + dataToSign, privSigningKeyHex, self.digestAlg) dataToEncrypt += encodeVarint(len(signature)) dataToEncrypt += signature @@ -1223,7 +1225,8 @@ class singleWorker(StoppableThread): payload += fullAckPayload dataToSign = pack('>Q', embeddedTime) + '\x00\x00\x00\x02' + \ encodeVarint(1) + encodeVarint(toStreamNumber) + payload - signature = highlevelcrypto.sign(dataToSign, privSigningKeyHex) + signature = highlevelcrypto.sign( + dataToSign, privSigningKeyHex, self.digestAlg) payload += encodeVarint(len(signature)) payload += signature diff --git a/src/highlevelcrypto.py b/src/highlevelcrypto.py index 82743acf..9a31ad97 100644 --- a/src/highlevelcrypto.py +++ b/src/highlevelcrypto.py @@ -13,7 +13,6 @@ import pyelliptic from pyelliptic import OpenSSL from pyelliptic import arithmetic as a -from bmconfigparser import BMConfigParser __all__ = ['encrypt', 'makeCryptor', 'pointMult', 'privToPub', 'sign', 'verify'] @@ -67,22 +66,17 @@ def decryptFast(msg, cryptor): 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 - "digestalg" setting + *digestAlg* keyword. """ - digestAlg = BMConfigParser().safeGet( - 'bitmessagesettings', 'digestalg', 'sha256') - if digestAlg == "sha1": - # SHA1, this will eventually be deprecated - return makeCryptor(hexPrivkey).sign( - msg, digest_alg=OpenSSL.digest_ecdsa_sha1) - elif digestAlg == "sha256": - # SHA256. Eventually this will become the default - return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.EVP_sha256) - else: + if digestAlg not in ("sha1", "sha256"): raise ValueError("Unknown digest algorithm %s" % digestAlg) + # SHA1, this will eventually be deprecated + return makeCryptor(hexPrivkey).sign( + msg, digest_alg=OpenSSL.digest_ecdsa_sha1 + if digestAlg == "sha1" else OpenSSL.EVP_sha256) def verify(msg, sig, hexPubkey): -- 2.45.1 From 0290607538ef5379230f0bf8485a22c57761483a Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 29 Jul 2021 14:47:23 +0300 Subject: [PATCH 03/14] Add tests for base58 and WIF decoding using pyelliptic.arithmetic --- src/pyelliptic/tests/test_arithmetic.py | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/pyelliptic/tests/test_arithmetic.py b/src/pyelliptic/tests/test_arithmetic.py index 7b5c59b1..dbbceeda 100644 --- a/src/pyelliptic/tests/test_arithmetic.py +++ b/src/pyelliptic/tests/test_arithmetic.py @@ -23,6 +23,12 @@ sample_privsigningkey = \ sample_privencryptionkey = \ b'4b0b73a54e19b059dc274ab69df095fe699f43b17397bca26fdf40f4d7400a3a' +# [chan] bitmessage +sample_wif_privsigningkey = \ + b'a2e8b841a531c1c558ee0680c396789c7a2ea3ac4795ae3f000caf9fe367d144' +sample_wif_privencryptionkey = \ + b'114ec0e2dca24a826a0eed064b0405b0ac148abc3b1d52729697f4d7b873fdc6' + sample_factor = \ 66858749573256452658262553961707680376751171096153613379801854825275240965733 # G * sample_factor @@ -40,6 +46,38 @@ class TestArithmetic(unittest.TestCase): sample_point, 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): """Decode sample privsigningkey from hex to int and compare to factor""" self.assertEqual( -- 2.45.1 From 07f815c28bfa418dd7f6802046f1ed410b876b99 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 29 Jul 2021 16:33:33 +0300 Subject: [PATCH 04/14] Add a test for WIF decoding --- src/tests/samples.py | 7 +++++++ src/tests/test_addresses.py | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/tests/samples.py b/src/tests/samples.py index fd7393e1..57510c3f 100644 --- a/src/tests/samples.py +++ b/src/tests/samples.py @@ -56,3 +56,10 @@ sample_sig = unhexlify( sample_sig_sha1 = unhexlify( '304502203b50123af78b4e40f5f819ae5b8786f48826e56d0f3e65744708a493f5b65de1' '0221009ddce2981ea143c0ac70404a535327e774adce8eebbae2d35104f1d326255f9a') + + +# [chan] bitmessage +sample_wif_privsigningkey = unhexlify( + b'a2e8b841a531c1c558ee0680c396789c7a2ea3ac4795ae3f000caf9fe367d144') +sample_wif_privencryptionkey = unhexlify( + b'114ec0e2dca24a826a0eed064b0405b0ac148abc3b1d52729697f4d7b873fdc6') diff --git a/src/tests/test_addresses.py b/src/tests/test_addresses.py index 8f9c283d..43b4884f 100644 --- a/src/tests/test_addresses.py +++ b/src/tests/test_addresses.py @@ -2,12 +2,13 @@ import unittest from binascii import unhexlify -from pybitmessage import addresses +from pybitmessage import addresses, shared from .samples import ( sample_address, sample_daddr3_512, sample_daddr4_512, 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_addr4 = sample_deterministic_addr4.split('-')[1] @@ -59,3 +60,14 @@ class TestAddresses(unittest.TestCase): sample_addr4, addresses.encodeBase58(sample_daddr4_512)) self.assertEqual( 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, + shared.decodeWalletImportFormat( + b'5K42shDERM5g7Kbi3JT5vsAWpXMqRhWZpX835M2pdSoqQQpJMYm')) + self.assertEqual( + sample_wif_privencryptionkey, + shared.decodeWalletImportFormat( + b'5HwugVWm31gnxtoYcvcK7oywH2ezYTh6Y4tzRxsndAeMi6NHqpA')) -- 2.45.1 From ba2d0e26878a3e43d8771cd3a998d940863290fc Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 29 Jul 2021 22:16:37 +0300 Subject: [PATCH 05/14] Start adding hashes with double SHA512 --- src/addresses.py | 31 +++++----------- src/api.py | 8 +++-- src/class_addressGenerator.py | 4 +-- src/class_objectProcessor.py | 20 +++++------ src/class_singleWorker.py | 68 ++++++++++++++++------------------- src/highlevelcrypto.py | 13 +++++++ src/network/bmobject.py | 2 +- src/proofofwork.py | 22 +++++++----- src/protocol.py | 7 ++-- src/shared.py | 11 +++--- 10 files changed, 91 insertions(+), 95 deletions(-) diff --git a/src/addresses.py b/src/addresses.py index e48873a1..885c1f64 100644 --- a/src/addresses.py +++ b/src/addresses.py @@ -2,11 +2,16 @@ Operations with addresses """ # pylint: disable=inconsistent-return-statements -import hashlib + import logging from binascii import hexlify, unhexlify from struct import pack, unpack +try: + from highlevelcrypto import double_sha512 +except ImportError: + from .highlevelcrypto import double_sha512 + logger = logging.getLogger('default') @@ -134,15 +139,6 @@ def decodeVarint(data): 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): """Convert ripe to address""" if version >= 2 and version < 4: @@ -166,12 +162,7 @@ def encodeAddress(version, stream, ripe): storedBinaryData = encodeVarint(version) + encodeVarint(stream) + ripe # Generate the checksum - sha = hashlib.new('sha512') - sha.update(storedBinaryData) - currentHash = sha.digest() - sha = hashlib.new('sha512') - sha.update(currentHash) - checksum = sha.digest()[0:4] + checksum = double_sha512(storedBinaryData)[0:4] # FIXME: encodeBase58 should take binary data, to reduce conversions # encodeBase58(storedBinaryData + checksum) @@ -207,13 +198,7 @@ def decodeAddress(address): data = unhexlify(hexdata) checksum = data[-4:] - sha = hashlib.new('sha512') - sha.update(data[:-4]) - currentHash = sha.digest() - sha = hashlib.new('sha512') - sha.update(currentHash) - - if checksum != sha.digest()[0:4]: + if checksum != double_sha512(data[:-4])[0:4]: status = 'checksumfailed' return status, 0, 0, '' diff --git a/src/api.py b/src/api.py index de220cc4..536768e8 100644 --- a/src/api.py +++ b/src/api.py @@ -71,6 +71,8 @@ from binascii import hexlify, unhexlify from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer from struct import pack +from six.moves import queue + import defaults import helper_inbox import helper_sent @@ -82,17 +84,17 @@ import shutdown import state from addresses import ( addBMIfNotPresent, - calculateInventoryHash, decodeAddress, decodeVarint, varintDecodeError ) from bmconfigparser import BMConfigParser 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 network.threads import StoppableThread -from six.moves import queue from version import softwareVersion try: # TODO: write tests for XML vulnerabilities diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py index 25b0c5df..f4a0f008 100644 --- a/src/class_addressGenerator.py +++ b/src/class_addressGenerator.py @@ -367,10 +367,10 @@ class addressGenerator(StoppableThread): highlevelcrypto.makeCryptor( hexlify(potentialPrivEncryptionKey)) shared.myAddressesByHash[ripe] = address - tag = hashlib.sha512(hashlib.sha512( + tag = highlevelcrypto.double_sha512( encodeVarint(addressVersionNumber) + encodeVarint(streamNumber) + ripe - ).digest()).digest()[32:] + )[32:] shared.myAddressesByTag[tag] = address if addressVersionNumber == 3: # If this is a chan address, diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py index 1bacf639..86ed2bcc 100644 --- a/src/class_objectProcessor.py +++ b/src/class_objectProcessor.py @@ -23,7 +23,7 @@ import queues import shared import state from addresses import ( - calculateInventoryHash, decodeAddress, decodeVarint, + decodeAddress, decodeVarint, encodeAddress, encodeVarint, varintDecodeError ) from bmconfigparser import BMConfigParser @@ -450,7 +450,7 @@ class objectProcessor(threading.Thread): streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = \ decodeVarint(data[readPosition:readPosition + 9]) readPosition += streamNumberAsClaimedByMsgLength - inventoryHash = calculateInventoryHash(data) + inventoryHash = highlevelcrypto.calculateInventoryHash(data) initialDecryptionSuccessful = False # 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) ) # Used to detect and ignore duplicate messages in our inbox - sigHash = hashlib.sha512( - hashlib.sha512(signature).digest()).digest()[32:] + sigHash = highlevelcrypto.double_sha512(signature)[32:] # calculate the fromRipe. sha = hashlib.new('sha512') @@ -751,7 +750,7 @@ class objectProcessor(threading.Thread): state.numberOfBroadcastsProcessed += 1 queues.UISignalQueue.put(( 'updateNumberOfBroadcastsProcessed', 'no data')) - inventoryHash = calculateInventoryHash(data) + inventoryHash = highlevelcrypto.calculateInventoryHash(data) readPosition = 20 # bypass the nonce, time, and object type broadcastVersion, broadcastVersionLength = decodeVarint( data[readPosition:readPosition + 9]) @@ -885,10 +884,10 @@ class objectProcessor(threading.Thread): ' itself. Ignoring message.' ) elif broadcastVersion == 5: - calculatedTag = hashlib.sha512(hashlib.sha512( + calculatedTag = highlevelcrypto.double_sha512( encodeVarint(sendersAddressVersion) + encodeVarint(sendersStream) + calculatedRipe - ).digest()).digest()[32:] + )[32:] if calculatedTag != embeddedTag: return logger.debug( 'The tag and encryption key used to encrypt this' @@ -918,8 +917,7 @@ class objectProcessor(threading.Thread): return logger.debug('ECDSA verify passed') # Used to detect and ignore duplicate messages in our inbox - sigHash = hashlib.sha512( - hashlib.sha512(signature).digest()).digest()[32:] + sigHash = highlevelcrypto.double_sha512(signature)[32:] fromAddress = encodeAddress( 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 # for it. elif addressVersion >= 4: - tag = hashlib.sha512(hashlib.sha512( + tag = highlevelcrypto.double_sha512( encodeVarint(addressVersion) + encodeVarint(streamNumber) + ripe - ).digest()).digest()[32:] + )[32:] if tag in state.neededPubkeys: del state.neededPubkeys[tag] self.sendMessages(address) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 49c41c07..23631d71 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -25,9 +25,7 @@ import queues import shared import state import tr -from addresses import ( - calculateInventoryHash, decodeAddress, decodeVarint, encodeVarint -) +from addresses import decodeAddress, decodeVarint, encodeVarint from bmconfigparser import BMConfigParser from helper_sql import sqlExecute, sqlQuery from inventory import Inventory @@ -75,18 +73,16 @@ class singleWorker(StoppableThread): queryreturn = sqlQuery( '''SELECT DISTINCT toaddress FROM sent''' ''' WHERE (status='awaitingpubkey' AND folder='sent')''') - for row in queryreturn: - toAddress, = row - # toStatus - _, toAddressVersionNumber, toStreamNumber, toRipe = \ - decodeAddress(toAddress) + for toAddress, in queryreturn: + toAddressVersionNumber, toStreamNumber, toRipe = \ + decodeAddress(toAddress)[1:] if toAddressVersionNumber <= 3: state.neededPubkeys[toAddress] = 0 elif toAddressVersionNumber >= 4: - doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( + doubleHashOfAddressData = highlevelcrypto.double_sha512( encodeVarint(toAddressVersionNumber) + encodeVarint(toStreamNumber) + toRipe - ).digest()).digest() + ) # Note that this is the first half of the sha512 hash. privEncryptionKey = doubleHashOfAddressData[:32] tag = doubleHashOfAddressData[32:] @@ -290,7 +286,7 @@ class singleWorker(StoppableThread): payload = self._doPOWDefaults( payload, TTL, log_prefix='(For pubkey message)') - inventoryHash = calculateInventoryHash(payload) + inventoryHash = highlevelcrypto.calculateInventoryHash(payload) objectType = 1 Inventory()[inventoryHash] = ( objectType, streamNumber, payload, embeddedTime, '') @@ -379,7 +375,7 @@ class singleWorker(StoppableThread): payload = self._doPOWDefaults( payload, TTL, log_prefix='(For pubkey message)') - inventoryHash = calculateInventoryHash(payload) + inventoryHash = highlevelcrypto.calculateInventoryHash(payload) objectType = 1 Inventory()[inventoryHash] = ( objectType, streamNumber, payload, embeddedTime, '') @@ -452,10 +448,10 @@ class singleWorker(StoppableThread): # unencrypted, the pubkey with part of the hash so that nodes # know which pubkey object to try to decrypt # when they want to send a message. - doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( + doubleHashOfAddressData = highlevelcrypto.double_sha512( encodeVarint(addressVersionNumber) + encodeVarint(streamNumber) + addressHash - ).digest()).digest() + ) payload += doubleHashOfAddressData[32:] # the tag signature = highlevelcrypto.sign( payload + dataToEncrypt, privSigningKeyHex, self.digestAlg) @@ -471,7 +467,7 @@ class singleWorker(StoppableThread): payload = self._doPOWDefaults( payload, TTL, log_prefix='(For pubkey message)') - inventoryHash = calculateInventoryHash(payload) + inventoryHash = highlevelcrypto.calculateInventoryHash(payload) objectType = 1 Inventory()[inventoryHash] = ( objectType, streamNumber, payload, embeddedTime, @@ -507,7 +503,7 @@ class singleWorker(StoppableThread): objectType = protocol.OBJECT_ONIONPEER # FIXME: ideally the objectPayload should be signed objectPayload = encodeVarint(peer.port) + protocol.encodeHost(peer.host) - tag = calculateInventoryHash(objectPayload) + tag = highlevelcrypto.calculateInventoryHash(objectPayload) if Inventory().by_type_and_tag(objectType, tag): return # not expired @@ -521,7 +517,7 @@ class singleWorker(StoppableThread): payload = self._doPOWDefaults( payload, TTL, log_prefix='(For onionpeer object)') - inventoryHash = calculateInventoryHash(payload) + inventoryHash = highlevelcrypto.calculateInventoryHash(payload) Inventory()[inventoryHash] = ( objectType, streamNumber, buffer(payload), embeddedTime, buffer(tag) @@ -615,10 +611,10 @@ class singleWorker(StoppableThread): payload += encodeVarint(streamNumber) if addressVersionNumber >= 4: - doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( + doubleHashOfAddressData = highlevelcrypto.double_sha512( encodeVarint(addressVersionNumber) + encodeVarint(streamNumber) + ripe - ).digest()).digest() + ) tag = doubleHashOfAddressData[32:] payload += tag else: @@ -688,7 +684,7 @@ class singleWorker(StoppableThread): ) continue - inventoryHash = calculateInventoryHash(payload) + inventoryHash = highlevelcrypto.calculateInventoryHash(payload) objectType = 3 Inventory()[inventoryHash] = ( objectType, streamNumber, payload, embeddedTime, tag) @@ -797,10 +793,10 @@ class singleWorker(StoppableThread): if toAddressVersionNumber <= 3: toTag = '' else: - toTag = hashlib.sha512(hashlib.sha512( + toTag = highlevelcrypto.double_sha512( encodeVarint(toAddressVersionNumber) + encodeVarint(toStreamNumber) + toRipe - ).digest()).digest()[32:] + )[32:] if toaddress in state.neededPubkeys or \ toTag in state.neededPubkeys: # We already sent a request for the pubkey @@ -834,11 +830,11 @@ class singleWorker(StoppableThread): # already contains the toAddress and cryptor # object associated with the tag for this toAddress. if toAddressVersionNumber >= 4: - doubleHashOfToAddressData = hashlib.sha512( - hashlib.sha512( - encodeVarint(toAddressVersionNumber) + encodeVarint(toStreamNumber) + toRipe - ).digest() - ).digest() + doubleHashOfToAddressData = \ + highlevelcrypto.double_sha512( + encodeVarint(toAddressVersionNumber) + + encodeVarint(toStreamNumber) + toRipe + ) # The first half of the sha512 hash. privEncryptionKey = doubleHashOfToAddressData[:32] # The second half of the sha512 hash. @@ -1304,7 +1300,7 @@ class singleWorker(StoppableThread): ) continue - inventoryHash = calculateInventoryHash(encryptedPayload) + inventoryHash = highlevelcrypto.calculateInventoryHash(encryptedPayload) objectType = 2 Inventory()[inventoryHash] = ( objectType, toStreamNumber, encryptedPayload, embeddedTime, '') @@ -1354,8 +1350,7 @@ class singleWorker(StoppableThread): # the message in our own inbox. if BMConfigParser().has_section(toaddress): # Used to detect and ignore duplicate messages in our inbox - sigHash = hashlib.sha512(hashlib.sha512( - signature).digest()).digest()[32:] + sigHash = highlevelcrypto.double_sha512(signature)[32:] t = (inventoryHash, toaddress, fromaddress, subject, int( time.time()), message, 'inbox', encoding, 0, sigHash) helper_inbox.insert(t) @@ -1410,16 +1405,13 @@ class singleWorker(StoppableThread): # neededPubkeys dictionary. But if we are recovering # 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. - privEncryptionKey = hashlib.sha512(hashlib.sha512( + doubleHashOfAddressData = highlevelcrypto.double_sha512( encodeVarint(addressVersionNumber) + encodeVarint(streamNumber) + ripe - ).digest()).digest()[:32] + ) + privEncryptionKey = doubleHashOfAddressData[:32] # Note that this is the second half of the sha512 hash. - tag = hashlib.sha512(hashlib.sha512( - encodeVarint(addressVersionNumber) - + encodeVarint(streamNumber) + ripe - ).digest()).digest()[32:] + tag = doubleHashOfAddressData[32:] if tag not in state.neededPubkeys: # We'll need this for when we receive a pubkey reply: # it will be encrypted and we'll need to decrypt it. @@ -1462,7 +1454,7 @@ class singleWorker(StoppableThread): payload = self._doPOWDefaults(payload, TTL) - inventoryHash = calculateInventoryHash(payload) + inventoryHash = highlevelcrypto.calculateInventoryHash(payload) objectType = 1 Inventory()[inventoryHash] = ( objectType, streamNumber, payload, embeddedTime, '') diff --git a/src/highlevelcrypto.py b/src/highlevelcrypto.py index 9a31ad97..3c084b12 100644 --- a/src/highlevelcrypto.py +++ b/src/highlevelcrypto.py @@ -7,6 +7,7 @@ High level cryptographic functions based on `.pyelliptic` OpenSSL bindings. `More discussion. `_ """ +import hashlib from binascii import hexlify import pyelliptic @@ -17,6 +18,18 @@ from pyelliptic import arithmetic as a __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] + + def makeCryptor(privkey): """Return a private `.pyelliptic.ECC` instance""" private_key = a.changebase(privkey, 16, 256, minlen=32) diff --git a/src/network/bmobject.py b/src/network/bmobject.py index 12b997d7..49e3c2de 100644 --- a/src/network/bmobject.py +++ b/src/network/bmobject.py @@ -6,7 +6,7 @@ import time import protocol import state -from addresses import calculateInventoryHash +from highlevelcrypto import calculateInventoryHash from inventory import Inventory from network.dandelion import Dandelion diff --git a/src/proofofwork.py b/src/proofofwork.py index 148d6734..69b04e2d 100644 --- a/src/proofofwork.py +++ b/src/proofofwork.py @@ -4,7 +4,6 @@ Proof of work calculation """ import ctypes -import hashlib import os import sys import tempfile @@ -12,6 +11,7 @@ import time from struct import pack, unpack from subprocess import call +import highlevelcrypto import openclpow import paths import queues @@ -87,13 +87,20 @@ def _set_idle(): 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): _set_idle() trialValue = float('inf') while trialValue > target: nonce += pool_size - trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512( - pack('>Q', nonce) + initialHash).digest()).digest()[0:8]) + trialValue = trial_value(nonce, initialHash) return [trialValue, nonce] @@ -103,10 +110,9 @@ def _doSafePoW(target, initialHash): trialValue = float('inf') while trialValue > target and state.shutdown == 0: nonce += 1 - trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512( - pack('>Q', nonce) + initialHash).digest()).digest()[0:8]) + trialValue = trial_value(nonce, initialHash) if state.shutdown != 0: - raise StopIteration("Interrupted") # pylint: misplaced-bare-raise + raise StopIteration("Interrupted") logger.debug("Safe PoW done") return [trialValue, nonce] @@ -163,7 +169,7 @@ def _doCPoW(target, initialHash): logger.debug("C PoW start") 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: raise StopIteration("Interrupted") logger.debug("C PoW done") @@ -173,7 +179,7 @@ def _doCPoW(target, initialHash): def _doGPUPoW(target, initialHash): logger.debug("GPU PoW start") 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: deviceNames = ", ".join(gpu.name for gpu in openclpow.enabledGpus) queues.UISignalQueue.put(( diff --git a/src/protocol.py b/src/protocol.py index 1934d9cc..ac00c1e2 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -269,12 +269,11 @@ def isProofOfWorkSufficient( if payloadLengthExtraBytes < defaults.networkDefaultPayloadLengthExtraBytes: payloadLengthExtraBytes = defaults.networkDefaultPayloadLengthExtraBytes 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: TTL = 300 - POW, = unpack('>Q', hashlib.sha512(hashlib.sha512( - data[:8] + hashlib.sha512(data[8:]).digest() - ).digest()).digest()[0:8]) + POW, = unpack('>Q', highlevelcrypto.double_sha512( + data[:8] + hashlib.sha512(data[8:]).digest())[0:8]) return POW <= 2 ** 64 / ( nonceTrialsPerByte * ( len(data) + payloadLengthExtraBytes diff --git a/src/shared.py b/src/shared.py index 4a654932..8adb73b2 100644 --- a/src/shared.py +++ b/src/shared.py @@ -121,7 +121,8 @@ def reloadMyAddressHashes(): if isEnabled: hasEnabledKeys = True # status - addressVersionNumber, streamNumber, hashobj = decodeAddress(addressInKeysFile)[1:] + addressVersionNumber, streamNumber, hashobj = decodeAddress( + addressInKeysFile)[1:] if addressVersionNumber in (2, 3, 4): # Returns a simple 32 bytes of information encoded # in 64 Hex characters, or null if there was an error. @@ -132,9 +133,9 @@ def reloadMyAddressHashes(): myECCryptorObjects[hashobj] = \ highlevelcrypto.makeCryptor(privEncryptionKey) myAddressesByHash[hashobj] = addressInKeysFile - tag = hashlib.sha512(hashlib.sha512( + tag = highlevelcrypto.double_sha512( encodeVarint(addressVersionNumber) - + encodeVarint(streamNumber) + hashobj).digest()).digest()[32:] + + encodeVarint(streamNumber) + hashobj)[32:] myAddressesByTag[tag] = addressInKeysFile else: logger.error( @@ -174,10 +175,10 @@ def reloadBroadcastSendersForWhichImWatching(): MyECSubscriptionCryptorObjects[hashobj] = \ highlevelcrypto.makeCryptor(hexlify(privEncryptionKey)) else: - doubleHashOfAddressData = hashlib.sha512(hashlib.sha512( + doubleHashOfAddressData = highlevelcrypto.double_sha512( encodeVarint(addressVersionNumber) + encodeVarint(streamNumber) + hashobj - ).digest()).digest() + ) tag = doubleHashOfAddressData[32:] privEncryptionKey = doubleHashOfAddressData[:32] MyECSubscriptionCryptorObjects[tag] = \ -- 2.45.1 From 15039cea9400ad225019846a09b999dc974d6d04 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 29 Jul 2021 22:18:16 +0300 Subject: [PATCH 06/14] The test --- src/tests/samples.py | 5 +++++ src/tests/test_crypto.py | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/tests/samples.py b/src/tests/samples.py index 57510c3f..8efae597 100644 --- a/src/tests/samples.py +++ b/src/tests/samples.py @@ -2,6 +2,11 @@ from binascii import unhexlify +# hello, page 1 of the Specification +sample_double_sha512 = unhexlify( + '0592a10584ffabf96539f3d780d776828c67da1ab5b169e9e8aed838aaecc9ed36d49ff14' + '23c55f019e050c66c6324f53588be88894fef4dcffdb74b98e2b200') + magic = 0xE9BEB4D9 diff --git a/src/tests/test_crypto.py b/src/tests/test_crypto.py index e5bb600a..3563098d 100644 --- a/src/tests/test_crypto.py +++ b/src/tests/test_crypto.py @@ -16,6 +16,7 @@ except ImportError: RIPEMD = None from .samples import ( + sample_double_sha512, sample_msg, sample_pubsigningkey, sample_pubencryptionkey, sample_privsigningkey, sample_privencryptionkey, sample_ripe, sample_sig, sample_sig_sha1 @@ -68,6 +69,11 @@ class TestHighlevelcrypto(unittest.TestCase): # 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_verify(self): """Verify sample signatures and newly generated ones""" pubkey_hex = hexlify(sample_pubsigningkey) -- 2.45.1 From 8942f63d0611f108076bfef406454fc0313c0a2f Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 6 Aug 2021 15:44:35 +0300 Subject: [PATCH 07/14] Moved decodeWalletImportFormat() from shared to highlevelcrypto, not addresses, where it's supposed to be because it uses pyelliptic.arithmetic, addresses.decodeBase58() returns int which needs to be encoded. Defined encodeWalletImportFormat() and replaced all uses. --- src/class_addressGenerator.py | 40 ++++------------ src/class_singleWorker.py | 76 ++++++++++++++---------------- src/highlevelcrypto.py | 29 ++++++++++++ src/shared.py | 88 +++++++++++++---------------------- 4 files changed, 108 insertions(+), 125 deletions(-) diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py index f4a0f008..4a2c20ec 100644 --- a/src/class_addressGenerator.py +++ b/src/class_addressGenerator.py @@ -15,7 +15,6 @@ from addresses import decodeAddress, encodeAddress, encodeVarint from bmconfigparser import BMConfigParser from fallback import RIPEMD160Hash from network import StoppableThread -from pyelliptic import arithmetic from pyelliptic.openssl import OpenSSL from six.moves import configparser, queue @@ -163,20 +162,10 @@ class addressGenerator(StoppableThread): address = encodeAddress( addressVersionNumber, streamNumber, ripe) - # 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 - privSigningKey = b'\x80' + potentialPrivSigningKey - 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) + privSigningKeyWIF = highlevelcrypto.encodeWalletImportFormat( + potentialPrivSigningKey) + privEncryptionKeyWIF = highlevelcrypto.encodeWalletImportFormat( + potentialPrivEncryptionKey) BMConfigParser().add_section(address) BMConfigParser().set(address, 'label', label) @@ -300,21 +289,12 @@ class addressGenerator(StoppableThread): saveAddressToDisk = False if saveAddressToDisk and live: - # 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 - privSigningKey = b'\x80' + potentialPrivSigningKey - 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) + privSigningKeyWIF = \ + highlevelcrypto.encodeWalletImportFormat( + potentialPrivSigningKey) + privEncryptionKeyWIF = \ + highlevelcrypto.encodeWalletImportFormat( + potentialPrivEncryptionKey) try: BMConfigParser().add_section(address) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 23631d71..c3230107 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -193,15 +193,20 @@ class singleWorker(StoppableThread): self.logger.info("Quitting...") def _getKeysForAddress(self, address): - privSigningKeyBase58 = BMConfigParser().get( - address, 'privsigningkey') - privEncryptionKeyBase58 = BMConfigParser().get( - address, 'privencryptionkey') + try: + privSigningKeyBase58 = BMConfigParser().get( + address, 'privsigningkey') + privEncryptionKeyBase58 = BMConfigParser().get( + 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( - privSigningKeyBase58)) - privEncryptionKeyHex = hexlify(shared.decodeWalletImportFormat( - privEncryptionKeyBase58)) + privSigningKeyHex = hexlify( + highlevelcrypto.decodeWalletImportFormat(privSigningKeyBase58)) + privEncryptionKeyHex = hexlify( + highlevelcrypto.decodeWalletImportFormat(privEncryptionKeyBase58)) # The \x04 on the beginning of the public keys are not sent. # This way there is only one acceptable way to encode @@ -252,9 +257,7 @@ class singleWorker(StoppableThread): message once it is done with the POW""" # Look up my stream number based on my address hash myAddress = shared.myAddressesByHash[adressHash] - # status - _, addressVersionNumber, streamNumber, adressHash = ( - decodeAddress(myAddress)) + addressVersionNumber, streamNumber = decodeAddress(myAddress)[1:3] # 28 days from now plus or minus five minutes TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300)) @@ -267,18 +270,15 @@ class singleWorker(StoppableThread): payload += protocol.getBitfield(myAddress) try: - # privSigningKeyHex, privEncryptionKeyHex - _, _, pubSigningKey, pubEncryptionKey = \ - self._getKeysForAddress(myAddress) - except (configparser.NoSectionError, configparser.NoOptionError) as err: - self.logger.warning("Section or Option did not found: %s", err) - except Exception as err: - self.logger.error( + pubSigningKey, pubEncryptionKey = self._getKeysForAddress( + myAddress)[2:] + except ValueError: + return + except Exception: + return self.logger.error( 'Error within doPOWForMyV2Pubkey. Could not read' ' the keys from the keys.dat file for a requested' - ' address. %s\n', err - ) - return + ' address. %s\n', exc_info=True) payload += pubSigningKey + pubEncryptionKey @@ -316,9 +316,8 @@ class singleWorker(StoppableThread): try: myAddress = shared.myAddressesByHash[adressHash] except KeyError: - # The address has been deleted. - self.logger.warning("Can't find %s in myAddressByHash", hexlify(adressHash)) - return + return self.logger.warning( # The address has been deleted. + "Can't find %s in myAddressByHash", hexlify(adressHash)) if BMConfigParser().safeGetBoolean(myAddress, 'chan'): self.logger.info('This is a chan address. Not sending pubkey.') return @@ -349,15 +348,13 @@ class singleWorker(StoppableThread): # , privEncryptionKeyHex privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \ self._getKeysForAddress(myAddress) - except (configparser.NoSectionError, configparser.NoOptionError) as err: - self.logger.warning("Section or Option did not found: %s", err) - except Exception as err: - self.logger.error( + except ValueError: + return + except Exception: + return self.logger.error( 'Error within sendOutOrStoreMyV3Pubkey. Could not read' ' the keys from the keys.dat file for a requested' - ' address. %s\n', err - ) - return + ' address. %s\n', exc_info=True) payload += pubSigningKey + pubEncryptionKey @@ -424,15 +421,13 @@ class singleWorker(StoppableThread): # , privEncryptionKeyHex privSigningKeyHex, _, pubSigningKey, pubEncryptionKey = \ self._getKeysForAddress(myAddress) - except (configparser.NoSectionError, configparser.NoOptionError) as err: - self.logger.warning("Section or Option did not found: %s", err) - except Exception as err: - self.logger.error( + except ValueError: + return + except Exception: + return self.logger.error( 'Error within sendOutOrStoreMyV4Pubkey. Could not read' ' the keys from the keys.dat file for a requested' - ' address. %s\n', err - ) - return + ' address. %s\n', exc_info=True) dataToEncrypt += pubSigningKey + pubEncryptionKey @@ -1114,8 +1109,9 @@ class singleWorker(StoppableThread): ' from the keys.dat file for our own address. %s\n', err) continue - privEncryptionKeyHex = hexlify(shared.decodeWalletImportFormat( - privEncryptionKeyBase58)) + privEncryptionKeyHex = hexlify( + highlevelcrypto.decodeWalletImportFormat( + privEncryptionKeyBase58)) pubEncryptionKeyBase256 = unhexlify(highlevelcrypto.privToPub( privEncryptionKeyHex))[1:] requiredAverageProofOfWorkNonceTrialsPerByte = \ diff --git a/src/highlevelcrypto.py b/src/highlevelcrypto.py index 3c084b12..6cfd3953 100644 --- a/src/highlevelcrypto.py +++ b/src/highlevelcrypto.py @@ -30,6 +30,35 @@ def calculateInventoryHash(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) + + def makeCryptor(privkey): """Return a private `.pyelliptic.ECC` instance""" private_key = a.changebase(privkey, 16, 256, minlen=32) diff --git a/src/shared.py b/src/shared.py index 8adb73b2..3ac49199 100644 --- a/src/shared.py +++ b/src/shared.py @@ -23,8 +23,6 @@ from bmconfigparser import BMConfigParser from debug import logger from helper_sql import sqlQuery -from pyelliptic import arithmetic - myECCryptorObjects = {} MyECSubscriptionCryptorObjects = {} @@ -76,35 +74,6 @@ def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address): 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(): """Reload keys for user's addresses from the config file""" logger.debug('reloading keys from keys.dat file') @@ -118,30 +87,39 @@ def reloadMyAddressHashes(): hasEnabledKeys = False for addressInKeysFile in BMConfigParser().addresses(): isEnabled = BMConfigParser().getboolean(addressInKeysFile, 'enabled') - if isEnabled: - hasEnabledKeys = True - # status - addressVersionNumber, streamNumber, hashobj = decodeAddress( - addressInKeysFile)[1:] - if addressVersionNumber in (2, 3, 4): - # Returns a simple 32 bytes of information encoded - # in 64 Hex characters, or null if there was an error. - privEncryptionKey = hexlify(decodeWalletImportFormat( - BMConfigParser().get(addressInKeysFile, 'privencryptionkey'))) - # It is 32 bytes encoded as 64 hex characters - if len(privEncryptionKey) == 64: - myECCryptorObjects[hashobj] = \ - highlevelcrypto.makeCryptor(privEncryptionKey) - myAddressesByHash[hashobj] = addressInKeysFile - tag = highlevelcrypto.double_sha512( - encodeVarint(addressVersionNumber) - + encodeVarint(streamNumber) + hashobj)[32:] - myAddressesByTag[tag] = addressInKeysFile - else: - logger.error( - 'Error in reloadMyAddressHashes: Can\'t handle' - ' address versions other than 2, 3, or 4.' - ) + if not isEnabled: + continue + + hasEnabledKeys = True + + addressVersionNumber, streamNumber, hashobj = decodeAddress( + addressInKeysFile)[1:] + if addressVersionNumber not in (2, 3, 4): + logger.error( + 'Error in reloadMyAddressHashes: Can\'t handle' + ' 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 + if len(privEncryptionKey) == 64: + myECCryptorObjects[hashobj] = \ + highlevelcrypto.makeCryptor(privEncryptionKey) + myAddressesByHash[hashobj] = addressInKeysFile + tag = highlevelcrypto.double_sha512( + encodeVarint(addressVersionNumber) + + encodeVarint(streamNumber) + hashobj)[32:] + myAddressesByTag[tag] = addressInKeysFile if not keyfileSecure: fixSensitiveFilePermissions(os.path.join( -- 2.45.1 From 659b292357fc638905792f7c1d77a73e9630e49c Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 29 Jul 2021 21:37:01 +0300 Subject: [PATCH 08/14] test_wif(): import from highlevelcrypto and add encoding checks --- src/tests/test_addresses.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/tests/test_addresses.py b/src/tests/test_addresses.py index 43b4884f..4c4a33d6 100644 --- a/src/tests/test_addresses.py +++ b/src/tests/test_addresses.py @@ -2,7 +2,7 @@ import unittest from binascii import unhexlify -from pybitmessage import addresses, shared +from pybitmessage import addresses, highlevelcrypto from .samples import ( sample_address, sample_daddr3_512, sample_daddr4_512, @@ -65,9 +65,21 @@ class TestAddresses(unittest.TestCase): """Decode WIFs of [chan] bitmessage and check the keys""" self.assertEqual( sample_wif_privsigningkey, - shared.decodeWalletImportFormat( + highlevelcrypto.decodeWalletImportFormat( b'5K42shDERM5g7Kbi3JT5vsAWpXMqRhWZpX835M2pdSoqQQpJMYm')) self.assertEqual( sample_wif_privencryptionkey, - shared.decodeWalletImportFormat( + 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') -- 2.45.1 From b80fb5a6755d06211828639a2301f956b0a6950f Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Wed, 1 Sep 2021 18:42:30 +0300 Subject: [PATCH 09/14] Use proofofwork.trial_value() in tests.test_openclpow --- src/tests/test_openclpow.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/tests/test_openclpow.py b/src/tests/test_openclpow.py index 341beec9..4770072e 100644 --- a/src/tests/test_openclpow.py +++ b/src/tests/test_openclpow.py @@ -1,10 +1,10 @@ """ Tests for openclpow module """ -import hashlib + import unittest -from struct import pack, unpack -from pybitmessage import openclpow + +from pybitmessage import openclpow, proofofwork class TestOpenClPow(unittest.TestCase): @@ -25,7 +25,5 @@ class TestOpenClPow(unittest.TestCase): "b93f3ffeba0ef2fd08a8dc2f87b68ae5a0dc819ab57f22ad2c4c9c8618a43b3" ).decode("hex") nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target_) - trialValue, = unpack( - '>Q', hashlib.sha512(hashlib.sha512( - pack('>Q', nonce) + initialHash).digest()).digest()[0:8]) - self.assertLess((nonce - trialValue), target_) + self.assertLess( + nonce - proofofwork.trial_value(nonce, initialHash), target_) -- 2.45.1 From f3ccc361fcac59c10eeea078122c31b9f5e0c669 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 9 Dec 2021 18:44:24 +0200 Subject: [PATCH 10/14] Move randomBytes to highlevelcrypto --- src/helper_ackPayload.py | 12 ++++++------ src/helper_random.py | 13 ------------- src/highlevelcrypto.py | 11 +++++++++++ src/network/tcp.py | 2 +- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/helper_ackPayload.py b/src/helper_ackPayload.py index d30f4c0d..1c5ddf98 100644 --- a/src/helper_ackPayload.py +++ b/src/helper_ackPayload.py @@ -22,26 +22,26 @@ def genAckPayload(streamNumber=1, stealthLevel=0): - level 1: a getpubkey request for a (random) dummy key hash - level 2: a standard message, encrypted to a random pubkey """ - if stealthLevel == 2: # Generate privacy-enhanced payload + if stealthLevel == 2: # Generate privacy-enhanced payload # Generate a dummy privkey and derive the pubkey dummyPubKeyHex = highlevelcrypto.privToPub( - hexlify(helper_random.randomBytes(32))) + hexlify(highlevelcrypto.randomBytes(32))) # Generate a dummy message of random length # (the smallest possible standard-formatted message is 234 bytes) - dummyMessage = helper_random.randomBytes( + dummyMessage = highlevelcrypto.randomBytes( helper_random.randomrandrange(234, 801)) # Encrypt the message using standard BM encryption (ECIES) ackdata = highlevelcrypto.encrypt(dummyMessage, dummyPubKeyHex) acktype = 2 # message version = 1 - elif stealthLevel == 1: # Basic privacy payload (random getpubkey) - ackdata = helper_random.randomBytes(32) + elif stealthLevel == 1: # Basic privacy payload (random getpubkey) + ackdata = highlevelcrypto.randomBytes(32) acktype = 0 # getpubkey version = 4 else: # Minimum viable payload (non stealth) - ackdata = helper_random.randomBytes(32) + ackdata = highlevelcrypto.randomBytes(32) acktype = 2 # message version = 1 diff --git a/src/helper_random.py b/src/helper_random.py index 2e6a151b..e6da707e 100644 --- a/src/helper_random.py +++ b/src/helper_random.py @@ -1,12 +1,7 @@ """Convenience functions for random operations. Not suitable for security / cryptography operations.""" -import os import random -try: - from pyelliptic.openssl import OpenSSL -except ImportError: - from .pyelliptic.openssl import OpenSSL NoneType = type(None) @@ -16,14 +11,6 @@ def seed(): random.seed() -def randomBytes(n): - """Method randomBytes.""" - try: - return os.urandom(n) - except NotImplementedError: - return OpenSSL.rand(n) - - def randomshuffle(population): """Method randomShuffle. diff --git a/src/highlevelcrypto.py b/src/highlevelcrypto.py index 6cfd3953..610596b5 100644 --- a/src/highlevelcrypto.py +++ b/src/highlevelcrypto.py @@ -8,6 +8,7 @@ High level cryptographic functions based on `.pyelliptic` OpenSSL bindings. """ import hashlib +import os from binascii import hexlify import pyelliptic @@ -59,6 +60,16 @@ def encodeWalletImportFormat(privKey): 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) + + def makeCryptor(privkey): """Return a private `.pyelliptic.ECC` instance""" private_key = a.changebase(privkey, 16, 256, minlen=32) diff --git a/src/network/tcp.py b/src/network/tcp.py index ff778378..77e8ba65 100644 --- a/src/network/tcp.py +++ b/src/network/tcp.py @@ -17,7 +17,7 @@ import knownnodes import protocol import state from bmconfigparser import BMConfigParser -from helper_random import randomBytes +from highlevelcrypto import randomBytes from inventory import Inventory from network.advanceddispatcher import AdvancedDispatcher from network.assemble import assemble_addr -- 2.45.1 From bbede1d449b65afcd53b99f8a5684743316c1d8c Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 9 Dec 2021 18:44:57 +0200 Subject: [PATCH 11/14] A dummy test for randomBytes --- src/tests/test_crypto.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tests/test_crypto.py b/src/tests/test_crypto.py index 3563098d..024fa23f 100644 --- a/src/tests/test_crypto.py +++ b/src/tests/test_crypto.py @@ -74,6 +74,14 @@ class TestHighlevelcrypto(unittest.TestCase): 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_verify(self): """Verify sample signatures and newly generated ones""" pubkey_hex = hexlify(sample_pubsigningkey) -- 2.45.1 From 7e21dd7ca16290b4512300d43b44df4b5f5232b9 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 9 Dec 2021 19:37:15 +0200 Subject: [PATCH 12/14] Define functions for generating keys in the highlevelcrypto --- src/class_addressGenerator.py | 36 ++++++++++++++--------------------- src/highlevelcrypto.py | 16 ++++++++++++++++ 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py index 4a2c20ec..06ca16d4 100644 --- a/src/class_addressGenerator.py +++ b/src/class_addressGenerator.py @@ -15,7 +15,6 @@ from addresses import decodeAddress, encodeAddress, encodeVarint from bmconfigparser import BMConfigParser from fallback import RIPEMD160Hash from network import StoppableThread -from pyelliptic.openssl import OpenSSL from six.moves import configparser, queue @@ -128,17 +127,13 @@ class addressGenerator(StoppableThread): # the \x00 or \x00\x00 bytes thus making the address shorter. startTime = time.time() numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0 - potentialPrivSigningKey = OpenSSL.rand(32) - potentialPubSigningKey = highlevelcrypto.pointMult( - potentialPrivSigningKey) + privSigningKey, pubSigningKey = highlevelcrypto.random_keys() while True: numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 - potentialPrivEncryptionKey = OpenSSL.rand(32) - potentialPubEncryptionKey = highlevelcrypto.pointMult( - potentialPrivEncryptionKey) + potentialPrivEncryptionKey, potentialPubEncryptionKey = \ + highlevelcrypto.random_keys() sha = hashlib.new('sha512') - sha.update( - potentialPubSigningKey + potentialPubEncryptionKey) + sha.update(pubSigningKey + potentialPubEncryptionKey) ripe = RIPEMD160Hash(sha.digest()).digest() if ( ripe[:numberOfNullBytesDemandedOnFrontOfRipeHash] @@ -163,7 +158,7 @@ class addressGenerator(StoppableThread): addressVersionNumber, streamNumber, ripe) privSigningKeyWIF = highlevelcrypto.encodeWalletImportFormat( - potentialPrivSigningKey) + privSigningKey) privEncryptionKeyWIF = highlevelcrypto.encodeWalletImportFormat( potentialPrivEncryptionKey) @@ -235,18 +230,15 @@ class addressGenerator(StoppableThread): numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0 while True: numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 - potentialPrivSigningKey = hashlib.sha512( - deterministicPassphrase - + encodeVarint(signingKeyNonce) - ).digest()[:32] - potentialPrivEncryptionKey = hashlib.sha512( - deterministicPassphrase - + encodeVarint(encryptionKeyNonce) - ).digest()[:32] - potentialPubSigningKey = highlevelcrypto.pointMult( - potentialPrivSigningKey) - potentialPubEncryptionKey = highlevelcrypto.pointMult( - potentialPrivEncryptionKey) + potentialPrivSigningKey, potentialPubSigningKey = \ + highlevelcrypto.deterministic_keys( + deterministicPassphrase, + encodeVarint(signingKeyNonce)) + potentialPrivEncryptionKey, potentialPubEncryptionKey = \ + highlevelcrypto.deterministic_keys( + deterministicPassphrase, + encodeVarint(encryptionKeyNonce)) + signingKeyNonce += 2 encryptionKeyNonce += 2 sha = hashlib.new('sha512') diff --git a/src/highlevelcrypto.py b/src/highlevelcrypto.py index 610596b5..cd105763 100644 --- a/src/highlevelcrypto.py +++ b/src/highlevelcrypto.py @@ -70,6 +70,22 @@ def randomBytes(n): 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 + + def makeCryptor(privkey): """Return a private `.pyelliptic.ECC` instance""" private_key = a.changebase(privkey, 16, 256, minlen=32) -- 2.45.1 From 46c15e815d803be25aead01ca77d4ef813c972b0 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 9 Dec 2021 19:46:02 +0200 Subject: [PATCH 13/14] Tests for keys generation this implementation for deterministic keys requires a passphrase of type bytes --- src/tests/samples.py | 5 +++-- src/tests/test_crypto.py | 20 ++++++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/tests/samples.py b/src/tests/samples.py index 8efae597..52607732 100644 --- a/src/tests/samples.py +++ b/src/tests/samples.py @@ -33,9 +33,10 @@ sample_point = ( 94730058721143827257669456336351159718085716196507891067256111928318063085006 ) -sample_seed = 'TIGER, tiger, burning bright. In the forests of the night' -# Deterministic addresses with stream 1 and versions 3, 4 +sample_seed = b'TIGER, tiger, burning bright. In the forests of the night' +# RIPE hash on step 22 with signing key nonce 42 sample_deterministic_ripe = b'00cfb69416ae76f68a81c459de4e13460c7d17eb' +# Deterministic addresses with stream 1 and versions 3, 4 sample_deterministic_addr3 = 'BM-2DBPTgeSawWYZceFD69AbDT5q4iUWtj1ZN' sample_deterministic_addr4 = 'BM-2cWzSnwjJ7yRP3nLEWUV5LisTZyREWSzUK' sample_daddr3_512 = 18875720106589866286514488037355423395410802084648916523381 diff --git a/src/tests/test_crypto.py b/src/tests/test_crypto.py index 024fa23f..44b53e6e 100644 --- a/src/tests/test_crypto.py +++ b/src/tests/test_crypto.py @@ -16,10 +16,10 @@ except ImportError: RIPEMD = None from .samples import ( - sample_double_sha512, + sample_deterministic_ripe, sample_double_sha512, sample_msg, sample_pubsigningkey, sample_pubencryptionkey, sample_privsigningkey, sample_privencryptionkey, sample_ripe, - sample_sig, sample_sig_sha1 + sample_seed, sample_sig, sample_sig_sha1 ) @@ -82,6 +82,22 @@ class TestHighlevelcrypto(unittest.TestCase): 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) + 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) -- 2.45.1 From 803937290ff3434ee0ce24052adb02461ffee216 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 10 Dec 2021 18:44:26 +0200 Subject: [PATCH 14/14] Started a dummy test for random keys in pyelliptic.ECC() --- src/pyelliptic/tests/test_openssl.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/pyelliptic/tests/test_openssl.py b/src/pyelliptic/tests/test_openssl.py index cb789277..ef411ef6 100644 --- a/src/pyelliptic/tests/test_openssl.py +++ b/src/pyelliptic/tests/test_openssl.py @@ -4,9 +4,10 @@ Test if OpenSSL is working correctly import unittest try: + from pyelliptic.ecc import ECC from pyelliptic.openssl import OpenSSL except ImportError: - from pybitmessage.pyelliptic import OpenSSL + from pybitmessage.pyelliptic import ECC, OpenSSL try: OpenSSL.BN_bn2binpad @@ -55,3 +56,10 @@ class TestOpenSSL(unittest.TestCase): if b.raw != c.raw.rjust(OpenSSL.BN_num_bytes(n), b'\x00'): bad += 1 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') -- 2.45.1