diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py index c32c3994..929ac364 100644 --- a/src/class_addressGenerator.py +++ b/src/class_addressGenerator.py @@ -16,7 +16,6 @@ from addresses import decodeAddress, encodeAddress, encodeVarint from bmconfigparser import config from fallback import RIPEMD160Hash from network import StoppableThread -from pyelliptic.openssl import OpenSSL from tr import _translate @@ -129,17 +128,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] @@ -164,7 +159,7 @@ class addressGenerator(StoppableThread): addressVersionNumber, streamNumber, ripe) privSigningKeyWIF = highlevelcrypto.encodeWalletImportFormat( - potentialPrivSigningKey) + privSigningKey) privEncryptionKeyWIF = highlevelcrypto.encodeWalletImportFormat( potentialPrivEncryptionKey) @@ -238,18 +233,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 e497c31c..d7af85de 100644 --- a/src/highlevelcrypto.py +++ b/src/highlevelcrypto.py @@ -17,10 +17,10 @@ from pyelliptic import arithmetic as a __all__ = [ - 'decodeWalletImportFormat', 'encodeWalletImportFormat', - 'double_sha512', 'calculateInventoryHash', + 'decodeWalletImportFormat', 'deterministic_keys', + 'double_sha512', 'calculateInventoryHash', 'encodeWalletImportFormat', 'encrypt', 'makeCryptor', 'pointMult', 'privToPub', 'randomBytes', - 'sign', 'verify'] + 'random_keys', 'sign', 'verify'] # WIF (uses arithmetic ): @@ -74,6 +74,77 @@ def calculateInventoryHash(data): return double_sha512(data)[:32] +# 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 hexToPubkey(pubkey): + """Convert a pubkey from hex to binary""" + pubkey_raw = a.changebase(pubkey[2:], 16, 256, minlen=64) + pubkey_bin = b'\x02\xca\x00 ' + pubkey_raw[:32] + b'\x00 ' + pubkey_raw[32:] + return pubkey_bin + + +def privToPub(privkey): + """Converts hex private key into hex public key""" + private_key = a.changebase(privkey, 16, 256, minlen=32) + public_key = pointMult(private_key) + return hexlify(public_key) + + +def pointMult(secret): + """ + Does an EC point multiplication; turns a private key into a public key. + + Evidently, this type of error can occur very rarely: + + >>> File "highlevelcrypto.py", line 54, in pointMult + >>> group = OpenSSL.EC_KEY_get0_group(k) + >>> WindowsError: exception: access violation reading 0x0000000000000008 + """ + while True: + try: + k = OpenSSL.EC_KEY_new_by_curve_name( + OpenSSL.get_curve('secp256k1')) + priv_key = OpenSSL.BN_bin2bn(secret, 32, None) + group = OpenSSL.EC_KEY_get0_group(k) + pub_key = OpenSSL.EC_POINT_new(group) + + OpenSSL.EC_POINT_mul(group, pub_key, priv_key, None, None, None) + OpenSSL.EC_KEY_set_private_key(k, priv_key) + OpenSSL.EC_KEY_set_public_key(k, pub_key) + + size = OpenSSL.i2o_ECPublicKey(k, None) + mb = OpenSSL.create_string_buffer(size) + OpenSSL.i2o_ECPublicKey(k, OpenSSL.byref(OpenSSL.pointer(mb))) + + return mb.raw + + except Exception: + import traceback + import time + traceback.print_exc() + time.sleep(0.2) + finally: + OpenSSL.EC_POINT_free(pub_key) + OpenSSL.BN_free(priv_key) + OpenSSL.EC_KEY_free(k) + + +# Encryption + def makeCryptor(privkey, curve='secp256k1'): """Return a private `.pyelliptic.ECC` instance""" private_key = a.changebase(privkey, 16, 256, minlen=32) @@ -84,26 +155,12 @@ def makeCryptor(privkey, curve='secp256k1'): return cryptor -def hexToPubkey(pubkey): - """Convert a pubkey from hex to binary""" - pubkey_raw = a.changebase(pubkey[2:], 16, 256, minlen=64) - pubkey_bin = b'\x02\xca\x00 ' + pubkey_raw[:32] + b'\x00 ' + pubkey_raw[32:] - return pubkey_bin - - def makePubCryptor(pubkey): """Return a public `.pyelliptic.ECC` instance""" pubkey_bin = hexToPubkey(pubkey) return pyelliptic.ECC(curve='secp256k1', pubkey=pubkey_bin) -def privToPub(privkey): - """Converts hex private key into hex public key""" - private_key = a.changebase(privkey, 16, 256, minlen=32) - public_key = pointMult(private_key) - return hexlify(public_key) - - def encrypt(msg, hexPubkey): """Encrypts message with hex public key""" return pyelliptic.ECC(curve='secp256k1').encrypt( @@ -120,6 +177,8 @@ def decryptFast(msg, cryptor): return cryptor.decrypt(msg) +# Signatures + def _choose_digest_alg(name): """ Choose openssl digest constant by name raises ValueError if not appropriate @@ -160,42 +219,3 @@ def verify(msg, sig, hexPubkey, digestAlg=None): sig, msg, digest_alg=_choose_digest_alg(digestAlg)) except: return False - - -def pointMult(secret): - """ - Does an EC point multiplication; turns a private key into a public key. - - Evidently, this type of error can occur very rarely: - - >>> File "highlevelcrypto.py", line 54, in pointMult - >>> group = OpenSSL.EC_KEY_get0_group(k) - >>> WindowsError: exception: access violation reading 0x0000000000000008 - """ - while True: - try: - k = OpenSSL.EC_KEY_new_by_curve_name( - OpenSSL.get_curve('secp256k1')) - priv_key = OpenSSL.BN_bin2bn(secret, 32, None) - group = OpenSSL.EC_KEY_get0_group(k) - pub_key = OpenSSL.EC_POINT_new(group) - - OpenSSL.EC_POINT_mul(group, pub_key, priv_key, None, None, None) - OpenSSL.EC_KEY_set_private_key(k, priv_key) - OpenSSL.EC_KEY_set_public_key(k, pub_key) - - size = OpenSSL.i2o_ECPublicKey(k, None) - mb = OpenSSL.create_string_buffer(size) - OpenSSL.i2o_ECPublicKey(k, OpenSSL.byref(OpenSSL.pointer(mb))) - - return mb.raw - - except Exception: - import traceback - import time - traceback.print_exc() - time.sleep(0.2) - finally: - OpenSSL.EC_POINT_free(pub_key) - OpenSSL.BN_free(priv_key) - OpenSSL.EC_KEY_free(k) diff --git a/src/tests/samples.py b/src/tests/samples.py index d96187ca..9f6c9d5b 100644 --- a/src/tests/samples.py +++ b/src/tests/samples.py @@ -43,8 +43,9 @@ sample_point = ( ) 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' +# 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 0a6f9f96..e518d7fd 100644 --- a/src/tests/test_crypto.py +++ b/src/tests/test_crypto.py @@ -8,7 +8,7 @@ import unittest from abc import ABCMeta, abstractmethod from binascii import hexlify -from pybitmessage import highlevelcrypto +from pybitmessage import highlevelcrypto, fallback try: @@ -17,10 +17,10 @@ except ImportError: RIPEMD160 = None from .samples import ( - sample_double_sha512, sample_hash_data, + sample_deterministic_ripe, sample_double_sha512, sample_hash_data, sample_msg, sample_pubsigningkey, sample_pubencryptionkey, sample_privsigningkey, sample_privencryptionkey, sample_ripe, - sample_sig, sample_sig_sha1 + sample_seed, sample_sig, sample_sig_sha1 ) @@ -81,6 +81,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(fallback.RIPEMD160Hash( + hashlib.sha512(sigkey + enkey).digest()).digest())) + def test_signatures(self): """Verify sample signatures and newly generated ones""" pubkey_hex = hexlify(sample_pubsigningkey)