Title: ECDSA in python
Post by: etotheipi on July 12, 2011, 03:12:57 PM
As I work on developing my python backend for a BTC client, and finding it difficult to find a decent ECDSA implementation in python that would be sufficient for my purposes. I have looked at the following:
M2Crypto -- looks like it's the best implementation in general, but I can't get the binary strings of the keys and signatures out of it. I can only save them to PEM files. python-nss -- doesn't look to be cross-platform pycrypto -- doesn't have ECDSA python-ecdsa(*) -- temporary winner! -- https://github.com/warner/python-ecdsa
There's a lot of good info on cryptography packages, here: http://mikeivanov.com/pc/python-crypto.pdf (http://mikeivanov.com/pc/python-crypto.pdf)
M2Crypto would be my first choice, but as far as I can tell, there's no way to get the binary keys directly from the library. You can generate a key, generate signatures, and save things to PEM files, but no access to the keys themselves. I am not fond of writing the keys to HDD and then reverse-engineering the PEM file format to get the key out. Maybe if you could write it to a RAM file... but still seems like a hack. (maybe someone has experience with this?)
python-ecdsa seems to be the best choice, as it doesn't appear to have any limitations, or any non-standard dependencies beyond it's own files, and should work on any platform. It looks slow, but I'm not convinced that's a problem unless you're going to verify the entire block-chain.
Anyone know of any other python library, or technique for getting python to do the DSA signing/verification needed for Bitcoin?
Title: Re: ECDSA in python
Post by: etotheipi on July 12, 2011, 04:31:32 PM
Wow, that is exactly what I was looking for! Everything you need in a single, dependency-less python file! I will surely donate to Lis!
P.S. - I did a search for "python ecdsa" in the forum search bar. I only got 3 hits (probably 4 now, with this post). I guess I needed to dig a little deeper.
-Eto
Title: Re: ECDSA in python
Post by: etotheipi on July 13, 2011, 12:38:02 AM
Okay, so the ECDSA library by Lis is fantastic, except I'm missing one critical piece of the puzzle -- I cannot for my life figure out how signatures are encoded. A signature consists of two integers, (r,s), DER-encoded. I'm digging through the very-dry DER encoding document, and I can't figure it out. Most people use an SSL library for DER encoding/decoding, but I am trying not to have such dependencies in my client.
Is anyone familiar enough with the DER encoding to know how to break apart a binary signature into the two integers r and s?
Title: Re: ECDSA in python
Post by: etotheipi on July 13, 2011, 01:44:44 PM
Update: After enough hex-diving and specification documents, I figured out how they are encoded. I'm sure it would be irresponsible to blindly decode a DER chunk without checking byte codes/flags, but I imagine this will never change in Bitcoin, so I am foregoing a proper DER library.
Byte Listing: -------------- 1: \x30 2: 1-byte length of DER-encoded (r,s) pair (assume 68)
3: \x02 4: 1-byte length of r-value (assume 32) 5-36: integer value of r
37: \x02 38: 1-byte length of s-value (assume 32) 39-70: integer value of s
71: \x01 --------------
The values of r and s aren't always 32 bits so you still need to read all the length bytes. The \x30 and \x02 bytes are bit strings of flags related to DER encoding scheme.
Title: Re: ECDSA in python
Post by: titeuf_87 on July 13, 2011, 04:11:47 PM
This thread (http://forum.bitcoin.org/index.php?topic=18051.0) contains some sample code for M2Crypto and in a later post a code sample about how DER encoding works.
Hope this helps!
Title: Re: ECDSA in python
Post by: Joric on July 13, 2011, 10:04:11 PM
I've spend some time to get i2d_ECPrivateKey/i2o_ECPublicKey exports working (see https://github.com/joric/pywallet) Relevant code (it uses only ctypes): # Had to import i2d_ECPrivateKey/i2o_ECPublicKey from dynamic libs. # Looks like PyCrypto/M2Crypto/pyOpenSSL modules do not support them.
dlls = list() if 'win' in sys.platform: for d in ('libeay32.dll', 'libssl32.dll', 'ssleay32.dll'): try: dlls.append( cdll.LoadLibrary(d) ) except: pass else: dlls.append( cdll.LoadLibrary('libssl.so') ) class BIGNUM_Struct (Structure): _fields_ = [("d",c_void_p),("top",c_int),("dmax",c_int),("neg",c_int),("flags",c_int)] class BN_CTX_Struct (Structure): _fields_ = [ ("_", c_byte) ]
BIGNUM = POINTER( BIGNUM_Struct ) BN_CTX = POINTER( BN_CTX_Struct )
def load_func( name, args, returns = c_int): d = sys.modules[ __name__ ].__dict__ f = None for dll in dlls: try: f = getattr(dll, name) f.argtypes = args f.restype = returns d[ name ] = f return except: pass raise ImportError('Unable to load required functions from SSL dlls') load_func( 'BN_new', [], BIGNUM ) load_func( 'BN_CTX_new', [], BN_CTX ) load_func( 'BN_CTX_free', [BN_CTX], None ) load_func( 'BN_num_bits', [BIGNUM], c_int ) load_func( 'BN_bn2bin', [BIGNUM, c_char_p] ) load_func( 'BN_bin2bn', [c_char_p, c_int, BIGNUM], BIGNUM ) load_func( 'EC_KEY_new_by_curve_name', [c_int], c_void_p ) load_func( 'EC_KEY_get0_group', [c_void_p], c_void_p) load_func( 'EC_KEY_get0_private_key', [c_void_p], BIGNUM) load_func( 'EC_POINT_new', [c_void_p], c_void_p) load_func( 'EC_POINT_free', [c_void_p]) load_func( 'EC_POINT_mul', [c_void_p, c_void_p, BIGNUM, c_void_p, BIGNUM, BN_CTX], c_int) load_func( 'EC_KEY_set_private_key', [c_void_p, BIGNUM], c_void_p) load_func( 'EC_KEY_set_public_key', [c_void_p, c_void_p], c_void_p) load_func( 'i2d_ECPrivateKey', [ c_void_p, POINTER(POINTER(c_char))], c_int ) load_func( 'i2o_ECPublicKey', [ c_void_p, POINTER(POINTER(c_char))], c_int )
def BN_num_bytes(a): return ((BN_num_bits(a)+7)/8)
NID_secp256k1 = 714
pkey = 0
def EC_KEY_regenerate_key(eckey, priv_key): group = EC_KEY_get0_group(eckey) ctx = BN_CTX_new() pub_key = EC_POINT_new(group) EC_POINT_mul(group, pub_key, priv_key, None, None, ctx) EC_KEY_set_private_key(eckey, priv_key) EC_KEY_set_public_key(eckey, pub_key) EC_POINT_free(pub_key) BN_CTX_free(ctx)
def GetSecret(pkey): bn = EC_KEY_get0_private_key(pkey) nSize = BN_num_bytes(bn) b = create_string_buffer(nSize) BN_bn2bin(bn, b) return b.raw def GetPrivKey(pkey): nSize = i2d_ECPrivateKey(pkey, None) p = create_string_buffer(nSize) i2d_ECPrivateKey(pkey, byref(cast(p, POINTER(c_char)))) return p.raw
def GetPubKey(pkey): nSize = i2o_ECPublicKey(pkey, None) p = create_string_buffer(nSize) i2o_ECPublicKey(pkey, byref(cast(p, POINTER(c_char)))) return p.raw
def Hash(data): s1 = hashlib.sha256() s1.update(data) h1 = s1.digest() s2 = hashlib.sha256() s2.update(h1) h2 = s2.digest() return h2
def EncodeBase58Check(vchIn): hash = Hash(vchIn) return b58encode(vchIn + hash[0:4])
def DecodeBase58Check(psz): vchRet = b58decode(psz, None) key = vchRet[0:-4] csum = vchRet[-4:] hash = Hash(key) cs32 = hash[0:4] if cs32 != csum: return None else: return key
def SecretToASecret(privkey): vchSecret = privkey[9:9+32] # add 1-byte version number vchIn = "\x80" + vchSecret return EncodeBase58Check(vchIn)
def ASecretToSecret(key): vch = DecodeBase58Check(key) if vch: return vch[1:] else: return False
def importprivkey(db, key): vchSecret = ASecretToSecret(key)
if not vchSecret: return False
pkey = EC_KEY_new_by_curve_name(NID_secp256k1) bn = BN_bin2bn(vchSecret, 32, BN_new()) EC_KEY_regenerate_key(pkey, bn)
secret = GetSecret(pkey) private_key = GetPrivKey(pkey) public_key = GetPubKey(pkey) addr = public_key_to_bc_address(public_key)
print "Address: %s" % addr print "Privkey: %s" % SecretToASecret(private_key)
update_wallet(db, 'key', { 'public_key' : public_key, 'private_key' : private_key }) update_wallet(db, 'name', { 'hash' : addr, 'name' : '' })
return True
Title: Re: ECDSA in python
Post by: TierNolan on July 13, 2011, 10:25:59 PM
Update: After enough hex-diving and specification documents, I figured out how they are encoded. I'm sure it would be irresponsible to blindly decode a DER chunk without checking byte codes/flags, but I imagine this will never change in Bitcoin, so I am foregoing a proper DER library.
Byte Listing: -------------- 1: \x30 2: 1-byte length of DER-encoded (r,s) pair (assume 68)
3: \x02 4: 1-byte length of r-value (assume 32) 5-36: integer value of r
37: \x02 38: 1-byte length of s-value (assume 32) 39-70: integer value of s
71: \x01 --------------
The values of r and s aren't always 32 bits so you still need to read all the length bytes. The \x30 and \x02 bytes are bit strings of flags related to DER encoding scheme.
R and S are 33 bytes if it would cause a negative number. 02_20_8000..... is a 32 byte integer with the MSB = 1. This is negative, so they change it to 02_21_008000..... This is a 33 byte integer, but with leading zeros, so it is considered positive.
Title: Re: ECDSA in python
Post by: Joric on July 24, 2011, 03:11:16 AM
Made some investigations about 279-byte DER key. There is an embedded DER encoder/decoder in the python-ecdsa library (https://github.com/warner/python-ecdsa). Example i2d_ECPrivateKey output (from http://bitcointools.appspot.com): Address: 1AJ3vE2NNYW2Jzv3fLwyjKF1LYbZ65Ez64 Private key: 5JMhGPWc3pkdgPd9jqVZkRtEp3QB3Ze8ihv62TmmvzABmkNzBHw i2d_ECPrivateKey: 30820113020101042047510706d76bc74a5d57bdcffc68c9bbbc2d496bef87c9 1de7f616129ac62b5fa081a53081a2020101302c06072a8648ce3d0101022100 fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f3006040100040107044104 79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817984 83ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b802 2100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101a 144034200046211d9b7836892c8eef49c4d0cad7797815eff95108e1d30745c0 3577596c9c00d2cb1ab27c7f95c28771278f89b7ff40da49fe9b4ee834a3f6a88 324db837d8 Javascript ASN1 decoder (http://lapo.it/asn1js/) decodes it to: SEQUENCE(4 elem) INTEGER1 OCTET STRING(32 byte) A0DC65FFCA799873CBEA0AC274015B9526505DAA.. [0] (1) SEQUENCE(6 elem) INTEGER 1 SEQUENCE(2 elem) OBJECT IDENTIFIER 1.2.840.10045.1.1 INTEGER (256 bit) SEQUENCE(2 elem) OCTET STRING(1 byte) 00 OCTET STRING(1 byte) 07 OCTET STRING(65 byte) 0479BE667EF9DCBBAC55A06295CE870B07029BFC.. INTEGER(256 bit) INTEGER1 [1] (1) BIT STRING(520 bit)
I wrote python-ecdsa SigningKey method that generates the same output as i2d_ECPrivateKey (279 bytes): def i2d(self):
_p = self.curve.curve.p() _r = self.curve.generator.order() # self.curve.order _Gx = self.curve.generator.x() _Gy = self.curve.generator.y()
oid2 = (1, 2, 840, 10045, 1, 1) # ecdsa-with-SHA1 + id-prime-Field encoded_oid2 = der.encode_oid(*oid2)
encoded_gxgy = "\x04" + ("%64x" % _Gx).decode('hex') + ("%64x" % _Gy).decode('hex')
param_sequence = der.encode_sequence ( der.encode_integer(1), der.encode_sequence ( encoded_oid2, der.encode_integer(_p), ), der.encode_sequence ( der.encode_octet_string("\x00"), der.encode_octet_string("\x07"), ), der.encode_octet_string(encoded_gxgy), der.encode_integer(_r), der.encode_integer(1), );
encoded_vk = "\x00\x04" + self.get_verifying_key().to_string()
return der.encode_sequence ( der.encode_integer(1), der.encode_octet_string(self.to_string()), der.encode_constructed(0, param_sequence), der.encode_constructed(1, der.encode_bitstring(encoded_vk)), )
Also see https://github.com/joric/brutus (ecdsa_pure.py and ecdsa_ssl.py, I've updated them recently).
Title: Re: ECDSA in python
Post by: Telariust on August 10, 2019, 09:03:26 PM
sry for necro-up, but this topic is consistent( search tag: python, btc, bitcoin , ecc , ecdsa , secp256k1, lib , library, compare )Comparing bitcoin libraries, generation speed of rand privkey to pubkey (maybe someone will need) Python37 C:\Python37>python.exe _benchmark_bitcoin_lib.py [timeit] 10 loops, 1.14888 sec ecdsa [timeit] 10 loops, 0.613698 sec pycoin [timeit] 10 loops, 0.0630259 sec starkbank-ecdsa [timeit] 10 loops, 0.0613364 sec pybitcointools [timeit] 10 loops, 0.0381225 sec fastecdsa [timeit] 10 loops, 0.0427408 sec python-bitcoinlib [timeit] 10 loops, 0.0214886 sec openssl [timeit] 10 loops, 0.000839192 sec coincurve
Python27 C:\Python27>python.exe _benchmark_bitcoin_lib.py [timeit] 10 loops, 1.33738 sec ecdsa [timeit] 10 loops, 0.660795 sec pycoin [timeit] 10 loops, 0.00585777 sec starkbank-ecdsa [timeit] 10 loops, 0.0984026 sec pybitcointools [timeit] 10 loops, 0.0389676 sec python-bitcoinlib [timeit] 10 loops, 0.0010109 sec coincurve
in short: - coincurve lib the undisputed leader (https://github.com/ofek/coincurve) - starkbank anomal x10 faster under python2 - pycoin have problem mode openssl (i dont only for me local or global) for python3 #!/usr/bin/env python3 ##################### import timeit
rounds = 10
import random randrange = random.SystemRandom().randrange
##################### # ecdsa # (use python) # python -m pip install ecdsa==0.13
def test_ecdsa(test_name = 'ecdsa', rounds = 10): loops = rounds try: import ecdsa except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; #secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big")
from ecdsa.ecdsa import generator_secp256k1, Public_key, Private_key #from ecdsa.util import string_to_number, number_to_string g = generator_secp256k1 pubkey = Public_key( g, g * secret ).point #print('[pubkey] {0}'.format(pubkey));exit(0);
setup = 'import random; from ecdsa.ecdsa import generator_secp256k1, Public_key, Private_key; g=generator_secp256k1; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'Public_key(g,g*random.randint(1,n)).point'
print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # starkbank-ecdsa # (use python with quick jacobian multiply) # python -m pip install starkbank-ecdsa==0.1.4
def test_starkbank_ecdsa(test_name = 'starkbank-ecdsa', rounds = 10): loops = rounds try: import ellipticcurve except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big")
from ellipticcurve.privateKey import PrivateKey pubkey = PrivateKey.fromString(secret).publicKey().toString() #print('[pubkey] {0}'.format(pubkey));exit(0);
setup = 'import random; from ellipticcurve.privateKey import PrivateKey; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'secret = random.randint(1,n); secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big"); PrivateKey.fromString(secret).publicKey().toString();'
print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # bitcoin (pybitcointools) # (use python with quick jacobian multiply) # (Author: Vitalik Buterin) # python -m pip install bitcoin==1.1.42 # python -m pip install pybitcointools==1.1.15
def test_pybitcointools(test_name = 'pybitcointools', rounds = 10): loops = rounds try: import pybitcointools except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; #secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big")
from pybitcointools import fast_multiply, G pubkey = fast_multiply(G, secret) #print('[pubkey] {0}'.format(pubkey));exit(0);
setup = 'import random; from pybitcointools import fast_multiply, G; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'fast_multiply(G, random.randint(1,n))'
print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # pycoin # (use python, use openssl(fail)) # python -m pip install pycoin==0.80
def test_pycoin(test_name = 'pycoin', rounds = 10): loops = rounds #if os.getenv("PYCOIN_NATIVE") != "openssl": try: import pycoin except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; #secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big")
#from pycoin.key import Key #pubkey = Key(secret_exponent=secret).public_pair() #print('[pubkey] {0}'.format(pubkey));exit(0);
from pycoin.ecdsa import public_pair_for_secret_exponent, generator_secp256k1 pubkey = public_pair_for_secret_exponent(generator_secp256k1, secret) #print('[pubkey] {0}'.format(pubkey));exit(0);
setup = 'import random; from pycoin.ecdsa import public_pair_for_secret_exponent, generator_secp256k1; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'public_pair_for_secret_exponent(generator_secp256k1, random.randint(1,n))'
print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # bit # (use coincurve) # python -m pip install bit==0.4.3
##################### # pybitcoin # (use pybitcointools + use ecdsa) # python -m pip install pybitcoin==0.9.9
##################### # python-bitcoin # (use ecdsa) # python -m pip install python-bitcoin==0.0.10
##################### # bitcoinlib # (use ecdsa) # python -m pip install bitcoinlib==0.4.4
##################### # python-bitcoinlib # (use openssl) # python -m pip install python-bitcoinlib==0.10.1 # (inst path to PythonXX/Lib/site-packages/bitcoin , its conflict with pybitcointools)
def test_python_bitcoinlib(test_name = 'python-bitcoinlib', rounds = 10): loops = rounds try: import bitcoin except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big")
from bitcoin.wallet import CKey pubkey = CKey(secret,compressed=False).pub #print('[pubkey] {0}'.format(pubkey));exit(0);
setup = 'import random; from bitcoin.wallet import CKey; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'secret = random.randint(1,n);secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big");CKey(secret,compressed=False).pub'
print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
from bitcoin.core.key import CECKey, CPubKey pubkey = CECKey() pubkey.set_secretbytes(secret) pubkey.set_compressed(compressed=False) pubkey = CPubKey(pubkey.get_pubkey(), pubkey) #print('[pubkey] {0}'.format(pubkey));exit(0);
setup = 'import random; from bitcoin.core.key import CECKey, CPubKey; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'secret = random.randint(1,n);secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big");pubkey = CECKey();pubkey.set_secretbytes(secret);pubkey = CPubKey(pubkey.get_pubkey(), pubkey);'
#print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # py_ecc # (use python with quick jacobian multiply) # python -m pip install py_ecc==1.4.7 # (bitcoin_easy)
##################### # ecc-0.0.1
##################### # bitcoin-utils # (use ecdsa) # python -m pip install bitcoin-utils==0.2.2
##################### # bitcoin_tools # (use python) # python -m pip install bitcoin_tools==0.0.13
##################### # python_bitcoin_tools # (use ecdsa) # python -m pip install python_bitcoin_tools==0.2.3
##################### # # (use ) # python -m pip install
#exit(0);
##################### # openssl
def test_openssl(test_name = 'openssl', rounds = 10): loops = rounds try: import ctypes ssl_library = ctypes.cdll.LoadLibrary("libeay32.dll") #ssl_library = ctypes.cdll.LoadLibrary(r"C:\Windows\System32\libeay32.dll") #ssl_library = ctypes.cdll.LoadLibrary(r"C:\Windows\SysWOW64\libeay32.dll") #ssl_library = ctypes.cdll.LoadLibrary("libssl.so") except Exception: print('[error] import {} failed; need libeay32.dll/.so'.format(test_name,test_name)) return(1)
NID_secp256k1 = 714
def get_public_key1(private_key, curve_name=NID_secp256k1): k = ssl_library.EC_KEY_new_by_curve_name(curve_name)
if ssl_library.EC_KEY_generate_key(k) != 1: raise Exception("internal error")
size = ssl_library.i2o_ECPublicKey(k, 0) storage = ctypes.create_string_buffer(size) ssl_library.i2o_ECPublicKey(k, ctypes.byref(ctypes.pointer(storage))) public_key = storage.raw
ssl_library.EC_KEY_free(k) return public_key
def get_public_key2(private_key, curve_name=NID_secp256k1): k = ssl_library.EC_KEY_new_by_curve_name(curve_name) storage = ctypes.create_string_buffer(private_key) bignum_private_key = ssl_library.BN_new() ssl_library.BN_bin2bn(storage, 32, bignum_private_key)
group = ssl_library.EC_KEY_get0_group(k) point = ssl_library.EC_POINT_new(group)
ssl_library.EC_POINT_mul(group, point, bignum_private_key, None, None, None) ssl_library.EC_KEY_set_private_key(k, bignum_private_key) ssl_library.EC_KEY_set_public_key(k, point)
size = ssl_library.i2o_ECPublicKey(k, 0) storage = ctypes.create_string_buffer(size) ssl_library.i2o_ECPublicKey(k, ctypes.byref(ctypes.pointer(storage))) public_key = storage.raw
ssl_library.EC_POINT_free(point) ssl_library.BN_free(bignum_private_key) ssl_library.EC_KEY_free(k) return public_key
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, 'big')
#pubkey = get_public_key1(secret) pubkey = get_public_key2(secret) #print('[pubkey] {0}'.format(pubkey.hex()));exit(0);
setup = 'import random; import ctypes; ssl_library = ctypes.cdll.LoadLibrary("libeay32.dll"); n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = '''
if 1: NID_secp256k1 = 714
def get_public_key1(private_key, curve_name=NID_secp256k1): k = ssl_library.EC_KEY_new_by_curve_name(curve_name)
if ssl_library.EC_KEY_generate_key(k) != 1: raise Exception("internal error")
size = ssl_library.i2o_ECPublicKey(k, 0) storage = ctypes.create_string_buffer(size) ssl_library.i2o_ECPublicKey(k, ctypes.byref(ctypes.pointer(storage))) public_key = storage.raw
ssl_library.EC_KEY_free(k) return public_key
def get_public_key2(private_key, curve_name=NID_secp256k1): k = ssl_library.EC_KEY_new_by_curve_name(curve_name) storage = ctypes.create_string_buffer(private_key) bignum_private_key = ssl_library.BN_new() ssl_library.BN_bin2bn(storage, 32, bignum_private_key)
group = ssl_library.EC_KEY_get0_group(k) point = ssl_library.EC_POINT_new(group)
ssl_library.EC_POINT_mul(group, point, bignum_private_key, None, None, None) ssl_library.EC_KEY_set_private_key(k, bignum_private_key) ssl_library.EC_KEY_set_public_key(k, point)
size = ssl_library.i2o_ECPublicKey(k, 0) storage = ctypes.create_string_buffer(size) ssl_library.i2o_ECPublicKey(k, ctypes.byref(ctypes.pointer(storage))) public_key = storage.raw
ssl_library.EC_POINT_free(point) ssl_library.BN_free(bignum_private_key) ssl_library.EC_KEY_free(k) return public_key
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; secret=random.randint(1,n); secret=secret.to_bytes((secret.bit_length()+7)//8 or 1, "big"); pubkey = get_public_key2(secret); ''' print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # coincurve # python -m pip install coincurve==9.0.0 # (use libsecp256k1.dll/.so)
def test_coincurve(test_name = 'coincurve', rounds = 10): loops = rounds try: import coincurve except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big")
from coincurve.keys import PrivateKey, PublicKey pubkey = PublicKey.from_valid_secret(secret).point() #print('[pubkey] {0}'.format(pubkey));exit(0); setup = 'import random; from coincurve.keys import PrivateKey, PublicKey; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'secret=random.randint(1,n); secret=secret.to_bytes((secret.bit_length()+7)//8 or 1, "big"); PublicKey.from_valid_secret(secret).point();' print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#stmt = "secret=random.randint(1,n); secret=secret.to_bytes((secret.bit_length()+7)//8 or 1, 'big'); PublicKey.from_valid_secret(secret).format(False);" #print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # fastecdsa # python -m pip install fastecdsa==1.6.5 # (use Cython)
def test_fastecdsa(test_name = 'fastecdsa', rounds = 10): loops = rounds try: import fastecdsa except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; #secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big")
from fastecdsa import keys, curve pubkey = keys.get_public_key(secret, curve.P256) #print('[pubkey] {0}'.format(pubkey));exit(0); setup = 'import random; from fastecdsa import keys, curve; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'secret=random.randint(1,n); keys.get_public_key(secret, curve.P256);' print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### ##################### #####################
test_ecdsa() test_pycoin() test_starkbank_ecdsa() test_pybitcointools() test_fastecdsa() test_python_bitcoinlib() test_openssl() test_coincurve()
need some fix for python2 #!/usr/bin/env python2 ##################### import timeit
rounds = 10
import random randrange = random.SystemRandom().randrange
##################### # ecdsa # (use python) # python -m pip install ecdsa==0.13
def test_ecdsa(test_name = 'ecdsa', rounds = 10): loops = rounds try: import ecdsa except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; #secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big")
from ecdsa.ecdsa import generator_secp256k1, Public_key, Private_key #from ecdsa.util import string_to_number, number_to_string g = generator_secp256k1 pubkey = Public_key( g, g * secret ).point #print('[pubkey] {0}'.format(pubkey));exit(0);
setup = 'import random; from ecdsa.ecdsa import generator_secp256k1, Public_key, Private_key; g=generator_secp256k1; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'Public_key(g,g*random.randint(1,n)).point'
print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # starkbank-ecdsa # (use python with quick jacobian multiply) # python -m pip install starkbank-ecdsa==0.1.4
def test_starkbank_ecdsa(test_name = 'starkbank-ecdsa', rounds = 10): loops = rounds try: import ellipticcurve except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; #secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big") secret = bytes((secret.bit_length()+7)//8 or 1)
from ellipticcurve.privateKey import PrivateKey pubkey = PrivateKey.fromString(secret).publicKey().toString() #print('[pubkey] {0}'.format(pubkey));exit(0);
setup = 'import random; from ellipticcurve.privateKey import PrivateKey; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'secret = random.randint(1,n); secret = bytes((secret.bit_length()+7)//8 or 1); PrivateKey.fromString(secret).publicKey().toString();'
print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # bitcoin (pybitcointools) # (use python with quick jacobian multiply) # (Author: Vitalik Buterin) # python -m pip install bitcoin==1.1.42 # python -m pip install pybitcointools==1.1.15
def test_pybitcointools(test_name = 'pybitcointools', rounds = 10): loops = rounds try: import pybitcointools except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; #secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big")
from pybitcointools import fast_multiply, G pubkey = fast_multiply(G, secret) #print('[pubkey] {0}'.format(pubkey));exit(0);
setup = 'import random; from pybitcointools import fast_multiply, G; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'fast_multiply(G, random.randint(1,n))'
print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # pycoin # (use python, use openssl(fail)) # python -m pip install pycoin==0.80
def test_pycoin(test_name = 'pycoin', rounds = 10): loops = rounds #if os.getenv("PYCOIN_NATIVE") != "openssl": try: import pycoin except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; #secret = secret.to_bytes((secret.bit_length()+7)//8 or 1, "big")
#from pycoin.key import Key #pubkey = Key(secret_exponent=secret).public_pair() #print('[pubkey] {0}'.format(pubkey));exit(0);
from pycoin.ecdsa import public_pair_for_secret_exponent, generator_secp256k1 pubkey = public_pair_for_secret_exponent(generator_secp256k1, secret) #print('[pubkey] {0}'.format(pubkey));exit(0);
setup = 'import random; from pycoin.ecdsa import public_pair_for_secret_exponent, generator_secp256k1; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'public_pair_for_secret_exponent(generator_secp256k1, random.randint(1,n))'
print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # bit # (use coincurve) # python -m pip install bit==0.4.3
##################### # pybitcoin # (use pybitcointools + use ecdsa) # python -m pip install pybitcoin==0.9.9
##################### # python-bitcoin # (use ecdsa) # python -m pip install python-bitcoin==0.0.10
##################### # bitcoinlib # (use ecdsa) # python -m pip install bitcoinlib==0.4.4
##################### # python-bitcoinlib # (use openssl) # python -m pip install python-bitcoinlib==0.10.1 # (inst path to PythonXX/Lib/site-packages/bitcoin , its conflict with pybitcointools)
def test_python_bitcoinlib(test_name = 'python-bitcoinlib', rounds = 10): loops = rounds try: import bitcoin except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; secret = bytes((secret.bit_length()+7)//8 or 1)
from bitcoin.wallet import CKey pubkey = CKey(secret,compressed=False).pub #print('[pubkey] {0}'.format(pubkey));exit(0);
setup = 'import random; from bitcoin.wallet import CKey; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'secret = random.randint(1,n);secret = bytes((secret.bit_length()+7)//8 or 1);CKey(secret,compressed=False).pub'
print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
from bitcoin.core.key import CECKey, CPubKey pubkey = CECKey() pubkey.set_secretbytes(secret) pubkey.set_compressed(compressed=False) pubkey = CPubKey(pubkey.get_pubkey(), pubkey) #print('[pubkey] {0}'.format(pubkey));exit(0);
setup = 'import random; from bitcoin.core.key import CECKey, CPubKey; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'secret = random.randint(1,n);secret = bytes((secret.bit_length()+7)//8 or 1, "big");pubkey = CECKey();pubkey.set_secretbytes(secret);pubkey = CPubKey(pubkey.get_pubkey(), pubkey);'
#print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # py_ecc # (use python with quick jacobian multiply) # python -m pip install py_ecc==1.4.7 # (bitcoin_easy)
##################### # ecc-0.0.1
##################### # bitcoin-utils # (use ecdsa) # python -m pip install bitcoin-utils==0.2.2
##################### # bitcoin_tools # (use python) # python -m pip install bitcoin_tools==0.0.13
##################### # python_bitcoin_tools # (use ecdsa) # python -m pip install python_bitcoin_tools==0.2.3
##################### # # (use ) # python -m pip install
#exit(0);
##################### # openssl
def test_openssl(test_name = 'openssl', rounds = 10): loops = rounds try: import ctypes ssl_library = ctypes.cdll.LoadLibrary("libeay32.dll") #ssl_library = ctypes.cdll.LoadLibrary(r"C:\Windows\System32\libeay32.dll") #ssl_library = ctypes.cdll.LoadLibrary(r"C:\Windows\SysWOW64\libeay32.dll") #ssl_library = ctypes.cdll.LoadLibrary("libssl.so") except Exception: print('[error] import {} failed; need libeay32.dll/.so'.format(test_name,test_name)) return(1)
NID_secp256k1 = 714
def get_public_key1(private_key, curve_name=NID_secp256k1): k = ssl_library.EC_KEY_new_by_curve_name(curve_name)
if ssl_library.EC_KEY_generate_key(k) != 1: raise Exception("internal error")
size = ssl_library.i2o_ECPublicKey(k, 0) storage = ctypes.create_string_buffer(size) ssl_library.i2o_ECPublicKey(k, ctypes.byref(ctypes.pointer(storage))) public_key = storage.raw
ssl_library.EC_KEY_free(k) return public_key
def get_public_key2(private_key, curve_name=NID_secp256k1): k = ssl_library.EC_KEY_new_by_curve_name(curve_name) storage = ctypes.create_string_buffer(private_key) bignum_private_key = ssl_library.BN_new() ssl_library.BN_bin2bn(storage, 32, bignum_private_key)
group = ssl_library.EC_KEY_get0_group(k) point = ssl_library.EC_POINT_new(group)
ssl_library.EC_POINT_mul(group, point, bignum_private_key, None, None, None) ssl_library.EC_KEY_set_private_key(k, bignum_private_key) ssl_library.EC_KEY_set_public_key(k, point)
size = ssl_library.i2o_ECPublicKey(k, 0) storage = ctypes.create_string_buffer(size) ssl_library.i2o_ECPublicKey(k, ctypes.byref(ctypes.pointer(storage))) public_key = storage.raw
ssl_library.EC_POINT_free(point) ssl_library.BN_free(bignum_private_key) ssl_library.EC_KEY_free(k) return public_key
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; secret = bytes((secret.bit_length()+7)//8 or 1)
#pubkey = get_public_key1(secret) pubkey = get_public_key2(secret) #print('[pubkey] {0}'.format(pubkey.hex()));exit(0);
setup = 'import random; import ctypes; ssl_library = ctypes.cdll.LoadLibrary("libeay32.dll"); n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = '''
if 1: NID_secp256k1 = 714
def get_public_key1(private_key, curve_name=NID_secp256k1): k = ssl_library.EC_KEY_new_by_curve_name(curve_name)
if ssl_library.EC_KEY_generate_key(k) != 1: raise Exception("internal error")
size = ssl_library.i2o_ECPublicKey(k, 0) storage = ctypes.create_string_buffer(size) ssl_library.i2o_ECPublicKey(k, ctypes.byref(ctypes.pointer(storage))) public_key = storage.raw
ssl_library.EC_KEY_free(k) return public_key
def get_public_key2(private_key, curve_name=NID_secp256k1): k = ssl_library.EC_KEY_new_by_curve_name(curve_name) storage = ctypes.create_string_buffer(private_key) bignum_private_key = ssl_library.BN_new() ssl_library.BN_bin2bn(storage, 32, bignum_private_key)
group = ssl_library.EC_KEY_get0_group(k) point = ssl_library.EC_POINT_new(group)
ssl_library.EC_POINT_mul(group, point, bignum_private_key, None, None, None) ssl_library.EC_KEY_set_private_key(k, bignum_private_key) ssl_library.EC_KEY_set_public_key(k, point)
size = ssl_library.i2o_ECPublicKey(k, 0) storage = ctypes.create_string_buffer(size) ssl_library.i2o_ECPublicKey(k, ctypes.byref(ctypes.pointer(storage))) public_key = storage.raw
ssl_library.EC_POINT_free(point) ssl_library.BN_free(bignum_private_key) ssl_library.EC_KEY_free(k) return public_key
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; secret=random.randint(1,n); secret=bytes((secret.bit_length()+7)//8 or 1); pubkey = get_public_key2(secret); ''' print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### # coincurve # python -m pip install coincurve==9.0.0 # (use libsecp256k1.dll/.so)
def test_coincurve(test_name = 'coincurve', rounds = 10): loops = rounds try: import coincurve except Exception: print('[error] import {} failed; try: python -m pip install {}'.format(test_name,test_name)) return(1)
n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; #secret = random.randint(1,n) secret = 0xebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffff; secret = bytes((secret.bit_length()+7)//8 or 1)
from coincurve.keys import PrivateKey, PublicKey pubkey = PublicKey.from_valid_secret(secret).point() #print('[pubkey] {0}'.format(pubkey));exit(0); setup = 'import random; from coincurve.keys import PrivateKey, PublicKey; n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;' stmt = 'secret=random.randint(1,n); secret=bytes((secret.bit_length()+7)//8 or 1); PublicKey.from_valid_secret(secret).point();' print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#stmt = "secret=random.randint(1,n); secret=secret.to_bytes((secret.bit_length()+7)//8 or 1, 'big'); PublicKey.from_valid_secret(secret).format(False);" #print('[timeit] {0} loops, {1:.6} sec '.format( loops, timeit.timeit(stmt, setup, number=loops) ).ljust(50,' ') + test_name )
#exit(0);
##################### ##################### #####################
test_ecdsa() test_pycoin() test_starkbank_ecdsa() test_pybitcointools() test_python_bitcoinlib() #test_openssl() test_coincurve()
Title: Re: ECDSA in python
Post by: pooya87 on August 11, 2019, 03:05:36 AM
- coincurve lib the undisputed leader (https://github.com/ofek/coincurve)
it is worth noting that libraries such as python-ecdsa are written purely in python with no optimization and they also work for all the elliptic curves while a library like coincurve is a wrapper around libsecp256k1 which is a heavily optimized library written in C and only works for 1 curve (the one bitcoin uses). it was a good comparison though. thanks for posting.
Title: Re: ECDSA in python
Post by: darosior on August 11, 2019, 02:45:17 PM
For what it worth, I've found this one (https://github.com/ethereum/py_ecc/blob/master/py_ecc/secp256k1/secp256k1.py) quite neat when tinkering with transactions : a simple file without any dependency. It's from the Ethereum guys but they use the same curve as Bitcoin. If you are looking for broader ECDSA than secp256k1, then python-ecdsa (https://pypi.org/project/ecdsa/) is cool.
|