diff --git a/docs/INSTALL.md b/docs/INSTALL.md index cb4c16c3c..c9d834265 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -24,7 +24,7 @@ is actually newer in version number, than what was there already. To install everything (client and server), install these packages: - sudo apt-get install python-dev python-pip git build-essential automake pkg-config libtool libffi-dev libssl-dev + sudo apt-get install python-dev python-pip git build-essential automake pkg-config libtool libffi-dev libssl-dev libgmp-dev (+ `libsodium-dev` if you can find it, else build after) diff --git a/install.sh b/install.sh index 81e73d7ed..f905a7b81 100755 --- a/install.sh +++ b/install.sh @@ -14,7 +14,7 @@ sha256_verify () deps_install () { if [[ ${install_os} == 'debian' ]]; then - if deb_deps_install "python-virtualenv curl python-dev python-pip build-essential automake pkg-config libtool"; then + if deb_deps_install "python-virtualenv curl python-dev python-pip build-essential automake pkg-config libtool libgmp-dev"; then return 0 else return 1 @@ -204,7 +204,7 @@ libffi_install () popd } -libsecp256k1-py_build () +coincurve_build () { if [[ -d "${jm_deps}/secp256k1-${secp256k1_version}" ]]; then unlink ./libsecp256k1 @@ -216,19 +216,19 @@ libsecp256k1-py_build () return "$?" } -secp256k1-py_install () +coincurve_install () { - secp256k1_py_version='0.13.2.4' - secp256k1_py_lib_tar="${secp256k1_py_version}.tar.gz" - secp256k1_py_lib_sha='f7920b1b887fe6745c49aebea40cefe867adddf11eb2c164624f1f2729f74657' - secp256k1_py_url='https://github.com/ludbb/secp256k1-py/archive' + coincurve_version='9.0.0' + coincurve_lib_tar="${coincurve_version}.tar.gz" + coincurve_lib_sha='81561e954b4a978231e6611ae6153740bfbaebb214caff7a7b4e71fe9affbe09' + coincurve_url='https://github.com/ofek/coincurve/archive' - rm -rf "./secp256k1-py-${secp256k1_py_version}" - if ! dep_get "${secp256k1_py_lib_tar}" "${secp256k1_py_lib_sha}" "${secp256k1_py_url}"; then + rm -rf "./coincurve-${coincurve_version}" + if ! dep_get "${coincurve_lib_tar}" "${coincurve_lib_sha}" "${coincurve_url}"; then return 1 fi - pushd "secp256k1-py-${secp256k1_py_version}" - if ! libsecp256k1-py_build; then + pushd "coincurve-${coincurve_version}" + if ! coincurve_build; then return 1 fi popd @@ -250,7 +250,7 @@ libsecp256k1_install () if ! dep_get "${secp256k1_lib_tar}" "${secp256k1_lib_sha}" "${secp256k1_url}"; then return 1 fi - if ! secp256k1-py_install; then + if ! coincurve_install; then return 1 fi } diff --git a/jmbitcoin/jmbitcoin/__init__.py b/jmbitcoin/jmbitcoin/__init__.py index db4553b4a..209bc1d89 100644 --- a/jmbitcoin/jmbitcoin/__init__.py +++ b/jmbitcoin/jmbitcoin/__init__.py @@ -1,4 +1,4 @@ -import secp256k1 +import coincurve as secp256k1 from jmbitcoin.secp256k1_main import * from jmbitcoin.secp256k1_transaction import * from jmbitcoin.secp256k1_deterministic import * diff --git a/jmbitcoin/jmbitcoin/secp256k1_main.py b/jmbitcoin/jmbitcoin/secp256k1_main.py index 53d59b91c..fa73fc011 100644 --- a/jmbitcoin/jmbitcoin/secp256k1_main.py +++ b/jmbitcoin/jmbitcoin/secp256k1_main.py @@ -5,14 +5,10 @@ import re import sys import base64 -import secp256k1 +import coincurve as secp256k1 #Required only for PoDLE calculation: N = 115792089237316195423570985008687907852837564279074904382605163141518161494337 -#Global context for secp256k1 operations (helps with performance) -ctx = secp256k1.lib.secp256k1_context_create(secp256k1.ALL_FLAGS) -#required for point addition -dummy_pub = secp256k1.PublicKey(ctx=ctx) #Standard prefix for Bitcoin message signing. BITCOIN_MESSAGE_MAGIC = '\x18' + 'Bitcoin Signed Message:\n' @@ -103,7 +99,7 @@ def getG(compressed=True): representation of secp256k1 G """ priv = "\x00"*31 + "\x01" - G = secp256k1.PrivateKey(priv, ctx=ctx).pubkey.serialize(compressed) + G = secp256k1.PrivateKey(priv).public_key.format(compressed) return G podle_PublicKey_class = secp256k1.PublicKey @@ -112,12 +108,12 @@ def getG(compressed=True): def podle_PublicKey(P): """Returns a PublicKey object from a binary string """ - return secp256k1.PublicKey(P, raw=True, ctx=ctx) + return secp256k1.PublicKey(P) def podle_PrivateKey(priv): """Returns a PrivateKey object from a binary string """ - return secp256k1.PrivateKey(priv, ctx=ctx) + return secp256k1.PrivateKey(priv) def privkey_to_address(priv, from_hex=True, magicbyte=0): @@ -290,8 +286,8 @@ def privkey_to_pubkey_inner(priv, usehex): and return compressed/uncompressed public key as appropriate.''' compressed, priv = read_privkey(priv) #secp256k1 checks for validity of key value. - newpriv = secp256k1.PrivateKey(privkey=priv, ctx=ctx) - return newpriv.pubkey.serialize(compressed=compressed) + newpriv = secp256k1.PrivateKey(secret=priv) + return newpriv.public_key.format(compressed) def privkey_to_pubkey(priv, usehex=True): '''To avoid changing the interface from the legacy system, @@ -313,25 +309,20 @@ def multiply(s, pub, usehex, rawpub=True, return_serialized=True): of the scalar s. ('raw' options passed in) ''' - newpub = secp256k1.PublicKey(pub, raw=rawpub, ctx=ctx) + newpub = secp256k1.PublicKey(pub) #see note to "tweak_mul" function in podle.py - res = secp256k1._tweak_public(newpub, - secp256k1.lib.secp256k1_ec_pubkey_tweak_mul, - s) + res = newpub.multiply(s) if not return_serialized: return res - return res.serialize() + return res.format() @hexbin def add_pubkeys(pubkeys, usehex): '''Input a list of binary compressed pubkeys and return their sum as a binary compressed pubkey.''' - r = secp256k1.PublicKey(ctx=ctx) #dummy holding object - pubkey_list = [secp256k1.PublicKey(x, - raw=True, - ctx=ctx).public_key for x in pubkeys] - r.combine(pubkey_list) - return r.serialize() + pubkey_list = [secp256k1.PublicKey(x) for x in pubkeys] + r = secp256k1.PublicKey.combine_keys(pubkey_list) + return r.format() @hexbin def add_privkeys(priv1, priv2, usehex): @@ -345,8 +336,8 @@ def add_privkeys(priv1, priv2, usehex): else: compressed = y[0] newpriv1, newpriv2 = (y[1], z[1]) - p1 = secp256k1.PrivateKey(newpriv1, raw=True, ctx=ctx) - res = p1.tweak_add(newpriv2) + p1 = secp256k1.PrivateKey(newpriv1) + res = p1.add(newpriv2).secret if compressed: res += '\x01' return res @@ -376,13 +367,12 @@ def ecdsa_raw_sign(msg, raise Exception("Invalid hash input to ECDSA raw sign.") if rawpriv: compressed, p = read_privkey(priv) - newpriv = secp256k1.PrivateKey(p, raw=True, ctx=ctx) + newpriv = secp256k1.PrivateKey(p) else: - newpriv = secp256k1.PrivateKey(priv, raw=False, ctx=ctx) + newpriv = secp256k1.PrivateKey.from_hex(priv) if formsg: - sig = newpriv.ecdsa_sign_recoverable(msg, raw=rawmsg) - s, rid = newpriv.ecdsa_recoverable_serialize(sig) - return chr(31+rid) + s + sig = newpriv.sign_recoverable(msg) + return sig #Donations, thus custom nonce, currently disabled, hence not covered. elif usenonce: #pragma: no cover raise NotImplementedError @@ -396,8 +386,11 @@ def ecdsa_raw_sign(msg, else: #partial fix for secp256k1-transient not including customnonce; #partial because donations will crash on windows in the "if". - sig = newpriv.ecdsa_sign(msg, raw=rawmsg) - return newpriv.ecdsa_serialize(sig) + if rawmsg: + sig = newpriv.sign(msg, hasher=None) + else: + sig = newpriv.sign(msg) + return sig @hexbin def ecdsa_raw_verify(msg, pub, sig, usehex, rawmsg=False): @@ -415,9 +408,11 @@ def ecdsa_raw_verify(msg, pub, sig, usehex, rawmsg=False): try: if rawmsg: assert len(msg) == 32 - newpub = secp256k1.PublicKey(pubkey=pub, raw=True, ctx=ctx) - sigobj = newpub.ecdsa_deserialize(sig) - retval = newpub.ecdsa_verify(msg, sigobj, raw=rawmsg) + newpub = secp256k1.PublicKey(pub) + if rawmsg: + retval = newpub.verify(sig, msg, hasher=None) + else: + retval = newpub.verify(sig, msg) except: return False return retval diff --git a/jmbitcoin/setup.py b/jmbitcoin/setup.py index 6660711cb..1d7d3372d 100644 --- a/jmbitcoin/setup.py +++ b/jmbitcoin/setup.py @@ -9,5 +9,5 @@ author_email='', license='GPL', packages=['jmbitcoin'], - install_requires=['future', 'secp256k1',], + install_requires=['future', 'coincurve',], zip_safe=False) diff --git a/jmclient/jmclient/podle.py b/jmclient/jmclient/podle.py index d9ce86afe..bf2abfe10 100644 --- a/jmclient/jmclient/podle.py +++ b/jmclient/jmclient/podle.py @@ -58,7 +58,7 @@ def __init__(self, if len(priv) == 66 and priv[-2:] == '01': priv = priv[:-2] self.priv = podle_PrivateKey(binascii.unhexlify(priv)) - self.P = self.priv.pubkey + self.P = self.priv.public_key if P2: self.P2 = podle_PublicKey(binascii.unhexlify(P2)) else: @@ -83,7 +83,7 @@ def get_commitment(self): raise PoDLEError("Cannot construct commitment, no P2 available") if not isinstance(self.P2, podle_PublicKey_class): raise PoDLEError("Cannot construct commitment, P2 is not a pubkey") - self.commitment = hashlib.sha256(self.P2.serialize()).digest() + self.commitment = hashlib.sha256(self.P2.format()).digest() return binascii.hexlify(self.commitment) def generate_podle(self, index=0, k=None): @@ -119,14 +119,14 @@ def generate_podle(self, index=0, k=None): if not k: k = os.urandom(32) J = getNUMS(index) - KG = podle_PrivateKey(k).pubkey - KJ = multiply(k, J.serialize(), False, return_serialized=False) + KG = podle_PrivateKey(k).public_key + KJ = multiply(k, J.format(), False, return_serialized=False) self.P2 = getP2(self.priv, J) self.get_commitment() - self.e = hashlib.sha256(''.join([x.serialize( + self.e = hashlib.sha256(''.join([x.format( ) for x in [KG, KJ, self.P, self.P2]])).digest() k_int = decode(k, 256) - priv_int = decode(self.priv.private_key, 256) + priv_int = decode(self.priv.secret, 256) e_int = decode(self.e, 256) sig_int = (k_int + priv_int * e_int) % N self.s = encode(sig_int, 256, minlen=32) @@ -142,7 +142,7 @@ def reveal(self): self.get_commitment() Phex, P2hex, shex, ehex, commit = [ binascii.hexlify(x) - for x in [self.P.serialize(), self.P2.serialize(), self.s, self.e, + for x in [self.P.format(), self.P2.format(), self.s, self.e, self.commitment] ] return {'used': str(self.used), @@ -180,17 +180,17 @@ def verify(self, commitment, index_range): return False for J in [getNUMS(i) for i in index_range]: sig_priv = podle_PrivateKey(self.s) - sG = sig_priv.pubkey - sJ = multiply(self.s, J.serialize(), False) + sG = sig_priv.public_key + sJ = multiply(self.s, J.format(), False) e_int = decode(self.e, 256) minus_e = encode(-e_int % N, 256, minlen=32) - minus_e_P = multiply(minus_e, self.P.serialize(), False) - minus_e_P2 = multiply(minus_e, self.P2.serialize(), False) - KGser = add_pubkeys([sG.serialize(), minus_e_P], False) + minus_e_P = multiply(minus_e, self.P.format(), False) + minus_e_P2 = multiply(minus_e, self.P2.format(), False) + KGser = add_pubkeys([sG.format(), minus_e_P], False) KJser = add_pubkeys([sJ, minus_e_P2], False) #check 2: e =?= H(K_G || K_J || P || P2) - e_check = hashlib.sha256(KGser + KJser + self.P.serialize() + - self.P2.serialize()).digest() + e_check = hashlib.sha256(KGser + KJser + self.P.format() + + self.P2.format()).digest() if e_check == self.e: return True #commitment fails for any NUMS in the provided range @@ -242,7 +242,7 @@ def verify_all_NUMS(write=False): """ nums_points = {} for i in range(256): - nums_points[i] = binascii.hexlify(getNUMS(i).serialize()) + nums_points[i] = binascii.hexlify(getNUMS(i).format()) if write: with open("nums_basepoints.txt", "wb") as f: from pprint import pformat @@ -258,9 +258,9 @@ def getP2(priv, nums_pt): just the most easy way to manipulate it in the library), calculate priv*nums_pt """ - priv_raw = priv.private_key + priv_raw = priv.secret return multiply(priv_raw, - nums_pt.serialize(), + nums_pt.format(), False, return_serialized=False) diff --git a/test/Dockerfiles/bionic.Dockerfile b/test/Dockerfiles/bionic.Dockerfile index 6b2e5b5cf..62729c0d3 100755 --- a/test/Dockerfiles/bionic.Dockerfile +++ b/test/Dockerfiles/bionic.Dockerfile @@ -5,7 +5,7 @@ SHELL ["/bin/bash", "-c"] RUN apt-get update RUN apt-get install -y build-essential RUN apt-get install -y \ - automake pkg-config libtool + automake pkg-config libtool libgmp-dev RUN apt-get install -y \ python-dev python-pip python-virtualenv python-qt4 python-sip diff --git a/test/Dockerfiles/centos7.Dockerfile b/test/Dockerfiles/centos7.Dockerfile index 265647cb5..b046f4ec6 100755 --- a/test/Dockerfiles/centos7.Dockerfile +++ b/test/Dockerfiles/centos7.Dockerfile @@ -6,7 +6,7 @@ RUN yum -y groups install 'Development tools' RUN yum -y install epel-release && \ yum -y update RUN yum -y install \ - python-devel python2-pip python-virtualenv + python-devel python2-pip python-virtualenv gmp-devel RUN useradd --home-dir /home/chaum --create-home --shell /bin/bash --skel /etc/skel/ chaum ARG core_version diff --git a/test/Dockerfiles/fedora27.Dockerfile b/test/Dockerfiles/fedora27.Dockerfile index efc7a1a02..15f05da77 100755 --- a/test/Dockerfiles/fedora27.Dockerfile +++ b/test/Dockerfiles/fedora27.Dockerfile @@ -5,7 +5,7 @@ SHELL ["/bin/bash", "-c"] RUN dnf -y groups install 'Development tools' RUN dnf -y install \ autoconf libtool pkgconfig \ - python-devel python-pip python2-virtualenv + python-devel python-pip python2-virtualenv gmp-devel # needed for build time # https://stackoverflow.com/questions/34624428/g-error-usr-lib-rpm-redhat-redhat-hardened-cc1-no-such-file-or-directory diff --git a/test/Dockerfiles/stretch.Dockerfile b/test/Dockerfiles/stretch.Dockerfile index 822882cef..8d5d31a37 100755 --- a/test/Dockerfiles/stretch.Dockerfile +++ b/test/Dockerfiles/stretch.Dockerfile @@ -5,7 +5,7 @@ SHELL ["/bin/bash", "-c"] RUN apt-get update RUN apt-get install -y build-essential RUN apt-get install -y \ - automake pkg-config libtool + automake pkg-config libtool libgmp-dev RUN apt-get install -y \ python-dev python-pip python-virtualenv python-qt4 python-sip diff --git a/test/Dockerfiles/xenial.Dockerfile b/test/Dockerfiles/xenial.Dockerfile index a6e054426..e24861223 100755 --- a/test/Dockerfiles/xenial.Dockerfile +++ b/test/Dockerfiles/xenial.Dockerfile @@ -5,7 +5,7 @@ SHELL ["/bin/bash", "-c"] RUN apt-get update RUN apt-get install -y build-essential RUN apt-get install -y \ - automake pkg-config libtool + automake pkg-config libtool libgmp-dev RUN apt-get install -y \ python-dev python-pip python-virtualenv python-qt4 python-sip