Browse Source

Merge #223: Replace secp256k1-py with coincurve

abad597 Replace secp256k1-py with coincurve (James Hilliard)
master
AdamISZ 7 years ago
parent
commit
974b59873f
No known key found for this signature in database
GPG Key ID: 141001A1AF77F20B
  1. 2
      docs/INSTALL.md
  2. 24
      install.sh
  3. 2
      jmbitcoin/jmbitcoin/__init__.py
  4. 61
      jmbitcoin/jmbitcoin/secp256k1_main.py
  5. 2
      jmbitcoin/setup.py
  6. 34
      jmclient/jmclient/podle.py
  7. 2
      test/Dockerfiles/bionic.Dockerfile
  8. 2
      test/Dockerfiles/centos7.Dockerfile
  9. 2
      test/Dockerfiles/fedora27.Dockerfile
  10. 2
      test/Dockerfiles/stretch.Dockerfile
  11. 2
      test/Dockerfiles/xenial.Dockerfile

2
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: 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) (+ `libsodium-dev` if you can find it, else build after)

24
install.sh

@ -14,7 +14,7 @@ sha256_verify ()
deps_install () deps_install ()
{ {
if [[ ${install_os} == 'debian' ]]; then 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 return 0
else else
return 1 return 1
@ -204,7 +204,7 @@ libffi_install ()
popd popd
} }
libsecp256k1-py_build () coincurve_build ()
{ {
if [[ -d "${jm_deps}/secp256k1-${secp256k1_version}" ]]; then if [[ -d "${jm_deps}/secp256k1-${secp256k1_version}" ]]; then
unlink ./libsecp256k1 unlink ./libsecp256k1
@ -216,19 +216,19 @@ libsecp256k1-py_build ()
return "$?" return "$?"
} }
secp256k1-py_install () coincurve_install ()
{ {
secp256k1_py_version='0.13.2.4' coincurve_version='9.0.0'
secp256k1_py_lib_tar="${secp256k1_py_version}.tar.gz" coincurve_lib_tar="${coincurve_version}.tar.gz"
secp256k1_py_lib_sha='f7920b1b887fe6745c49aebea40cefe867adddf11eb2c164624f1f2729f74657' coincurve_lib_sha='81561e954b4a978231e6611ae6153740bfbaebb214caff7a7b4e71fe9affbe09'
secp256k1_py_url='https://github.com/ludbb/secp256k1-py/archive' coincurve_url='https://github.com/ofek/coincurve/archive'
rm -rf "./secp256k1-py-${secp256k1_py_version}" rm -rf "./coincurve-${coincurve_version}"
if ! dep_get "${secp256k1_py_lib_tar}" "${secp256k1_py_lib_sha}" "${secp256k1_py_url}"; then if ! dep_get "${coincurve_lib_tar}" "${coincurve_lib_sha}" "${coincurve_url}"; then
return 1 return 1
fi fi
pushd "secp256k1-py-${secp256k1_py_version}" pushd "coincurve-${coincurve_version}"
if ! libsecp256k1-py_build; then if ! coincurve_build; then
return 1 return 1
fi fi
popd popd
@ -250,7 +250,7 @@ libsecp256k1_install ()
if ! dep_get "${secp256k1_lib_tar}" "${secp256k1_lib_sha}" "${secp256k1_url}"; then if ! dep_get "${secp256k1_lib_tar}" "${secp256k1_lib_sha}" "${secp256k1_url}"; then
return 1 return 1
fi fi
if ! secp256k1-py_install; then if ! coincurve_install; then
return 1 return 1
fi fi
} }

2
jmbitcoin/jmbitcoin/__init__.py

@ -1,4 +1,4 @@
import secp256k1 import coincurve as secp256k1
from jmbitcoin.secp256k1_main import * from jmbitcoin.secp256k1_main import *
from jmbitcoin.secp256k1_transaction import * from jmbitcoin.secp256k1_transaction import *
from jmbitcoin.secp256k1_deterministic import * from jmbitcoin.secp256k1_deterministic import *

61
jmbitcoin/jmbitcoin/secp256k1_main.py

@ -5,14 +5,10 @@ import hashlib
import re import re
import sys import sys
import base64 import base64
import secp256k1 import coincurve as secp256k1
#Required only for PoDLE calculation: #Required only for PoDLE calculation:
N = 115792089237316195423570985008687907852837564279074904382605163141518161494337 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. #Standard prefix for Bitcoin message signing.
BITCOIN_MESSAGE_MAGIC = '\x18' + 'Bitcoin Signed Message:\n' BITCOIN_MESSAGE_MAGIC = '\x18' + 'Bitcoin Signed Message:\n'
@ -103,7 +99,7 @@ def getG(compressed=True):
representation of secp256k1 G representation of secp256k1 G
""" """
priv = "\x00"*31 + "\x01" priv = "\x00"*31 + "\x01"
G = secp256k1.PrivateKey(priv, ctx=ctx).pubkey.serialize(compressed) G = secp256k1.PrivateKey(priv).public_key.format(compressed)
return G return G
podle_PublicKey_class = secp256k1.PublicKey podle_PublicKey_class = secp256k1.PublicKey
@ -112,12 +108,12 @@ podle_PrivateKey_class = secp256k1.PrivateKey
def podle_PublicKey(P): def podle_PublicKey(P):
"""Returns a PublicKey object from a binary string """Returns a PublicKey object from a binary string
""" """
return secp256k1.PublicKey(P, raw=True, ctx=ctx) return secp256k1.PublicKey(P)
def podle_PrivateKey(priv): def podle_PrivateKey(priv):
"""Returns a PrivateKey object from a binary string """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): 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.''' and return compressed/uncompressed public key as appropriate.'''
compressed, priv = read_privkey(priv) compressed, priv = read_privkey(priv)
#secp256k1 checks for validity of key value. #secp256k1 checks for validity of key value.
newpriv = secp256k1.PrivateKey(privkey=priv, ctx=ctx) newpriv = secp256k1.PrivateKey(secret=priv)
return newpriv.pubkey.serialize(compressed=compressed) return newpriv.public_key.format(compressed)
def privkey_to_pubkey(priv, usehex=True): def privkey_to_pubkey(priv, usehex=True):
'''To avoid changing the interface from the legacy system, '''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. of the scalar s.
('raw' options passed in) ('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 #see note to "tweak_mul" function in podle.py
res = secp256k1._tweak_public(newpub, res = newpub.multiply(s)
secp256k1.lib.secp256k1_ec_pubkey_tweak_mul,
s)
if not return_serialized: if not return_serialized:
return res return res
return res.serialize() return res.format()
@hexbin @hexbin
def add_pubkeys(pubkeys, usehex): def add_pubkeys(pubkeys, usehex):
'''Input a list of binary compressed pubkeys '''Input a list of binary compressed pubkeys
and return their sum as a binary compressed pubkey.''' and return their sum as a binary compressed pubkey.'''
r = secp256k1.PublicKey(ctx=ctx) #dummy holding object pubkey_list = [secp256k1.PublicKey(x) for x in pubkeys]
pubkey_list = [secp256k1.PublicKey(x, r = secp256k1.PublicKey.combine_keys(pubkey_list)
raw=True, return r.format()
ctx=ctx).public_key for x in pubkeys]
r.combine(pubkey_list)
return r.serialize()
@hexbin @hexbin
def add_privkeys(priv1, priv2, usehex): def add_privkeys(priv1, priv2, usehex):
@ -345,8 +336,8 @@ def add_privkeys(priv1, priv2, usehex):
else: else:
compressed = y[0] compressed = y[0]
newpriv1, newpriv2 = (y[1], z[1]) newpriv1, newpriv2 = (y[1], z[1])
p1 = secp256k1.PrivateKey(newpriv1, raw=True, ctx=ctx) p1 = secp256k1.PrivateKey(newpriv1)
res = p1.tweak_add(newpriv2) res = p1.add(newpriv2).secret
if compressed: if compressed:
res += '\x01' res += '\x01'
return res return res
@ -376,13 +367,12 @@ def ecdsa_raw_sign(msg,
raise Exception("Invalid hash input to ECDSA raw sign.") raise Exception("Invalid hash input to ECDSA raw sign.")
if rawpriv: if rawpriv:
compressed, p = read_privkey(priv) compressed, p = read_privkey(priv)
newpriv = secp256k1.PrivateKey(p, raw=True, ctx=ctx) newpriv = secp256k1.PrivateKey(p)
else: else:
newpriv = secp256k1.PrivateKey(priv, raw=False, ctx=ctx) newpriv = secp256k1.PrivateKey.from_hex(priv)
if formsg: if formsg:
sig = newpriv.ecdsa_sign_recoverable(msg, raw=rawmsg) sig = newpriv.sign_recoverable(msg)
s, rid = newpriv.ecdsa_recoverable_serialize(sig) return sig
return chr(31+rid) + s
#Donations, thus custom nonce, currently disabled, hence not covered. #Donations, thus custom nonce, currently disabled, hence not covered.
elif usenonce: #pragma: no cover elif usenonce: #pragma: no cover
raise NotImplementedError raise NotImplementedError
@ -396,8 +386,11 @@ def ecdsa_raw_sign(msg,
else: else:
#partial fix for secp256k1-transient not including customnonce; #partial fix for secp256k1-transient not including customnonce;
#partial because donations will crash on windows in the "if". #partial because donations will crash on windows in the "if".
sig = newpriv.ecdsa_sign(msg, raw=rawmsg) if rawmsg:
return newpriv.ecdsa_serialize(sig) sig = newpriv.sign(msg, hasher=None)
else:
sig = newpriv.sign(msg)
return sig
@hexbin @hexbin
def ecdsa_raw_verify(msg, pub, sig, usehex, rawmsg=False): 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: try:
if rawmsg: if rawmsg:
assert len(msg) == 32 assert len(msg) == 32
newpub = secp256k1.PublicKey(pubkey=pub, raw=True, ctx=ctx) newpub = secp256k1.PublicKey(pub)
sigobj = newpub.ecdsa_deserialize(sig) if rawmsg:
retval = newpub.ecdsa_verify(msg, sigobj, raw=rawmsg) retval = newpub.verify(sig, msg, hasher=None)
else:
retval = newpub.verify(sig, msg)
except: except:
return False return False
return retval return retval

2
jmbitcoin/setup.py

@ -9,5 +9,5 @@ setup(name='joinmarketbitcoin',
author_email='', author_email='',
license='GPL', license='GPL',
packages=['jmbitcoin'], packages=['jmbitcoin'],
install_requires=['future', 'secp256k1',], install_requires=['future', 'coincurve',],
zip_safe=False) zip_safe=False)

34
jmclient/jmclient/podle.py

@ -58,7 +58,7 @@ class PoDLE(object):
if len(priv) == 66 and priv[-2:] == '01': if len(priv) == 66 and priv[-2:] == '01':
priv = priv[:-2] priv = priv[:-2]
self.priv = podle_PrivateKey(binascii.unhexlify(priv)) self.priv = podle_PrivateKey(binascii.unhexlify(priv))
self.P = self.priv.pubkey self.P = self.priv.public_key
if P2: if P2:
self.P2 = podle_PublicKey(binascii.unhexlify(P2)) self.P2 = podle_PublicKey(binascii.unhexlify(P2))
else: else:
@ -83,7 +83,7 @@ class PoDLE(object):
raise PoDLEError("Cannot construct commitment, no P2 available") raise PoDLEError("Cannot construct commitment, no P2 available")
if not isinstance(self.P2, podle_PublicKey_class): if not isinstance(self.P2, podle_PublicKey_class):
raise PoDLEError("Cannot construct commitment, P2 is not a pubkey") 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) return binascii.hexlify(self.commitment)
def generate_podle(self, index=0, k=None): def generate_podle(self, index=0, k=None):
@ -119,14 +119,14 @@ class PoDLE(object):
if not k: if not k:
k = os.urandom(32) k = os.urandom(32)
J = getNUMS(index) J = getNUMS(index)
KG = podle_PrivateKey(k).pubkey KG = podle_PrivateKey(k).public_key
KJ = multiply(k, J.serialize(), False, return_serialized=False) KJ = multiply(k, J.format(), False, return_serialized=False)
self.P2 = getP2(self.priv, J) self.P2 = getP2(self.priv, J)
self.get_commitment() 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() ) for x in [KG, KJ, self.P, self.P2]])).digest()
k_int = decode(k, 256) 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) e_int = decode(self.e, 256)
sig_int = (k_int + priv_int * e_int) % N sig_int = (k_int + priv_int * e_int) % N
self.s = encode(sig_int, 256, minlen=32) self.s = encode(sig_int, 256, minlen=32)
@ -142,7 +142,7 @@ class PoDLE(object):
self.get_commitment() self.get_commitment()
Phex, P2hex, shex, ehex, commit = [ Phex, P2hex, shex, ehex, commit = [
binascii.hexlify(x) 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] self.commitment]
] ]
return {'used': str(self.used), return {'used': str(self.used),
@ -180,17 +180,17 @@ class PoDLE(object):
return False return False
for J in [getNUMS(i) for i in index_range]: for J in [getNUMS(i) for i in index_range]:
sig_priv = podle_PrivateKey(self.s) sig_priv = podle_PrivateKey(self.s)
sG = sig_priv.pubkey sG = sig_priv.public_key
sJ = multiply(self.s, J.serialize(), False) sJ = multiply(self.s, J.format(), False)
e_int = decode(self.e, 256) e_int = decode(self.e, 256)
minus_e = encode(-e_int % N, 256, minlen=32) minus_e = encode(-e_int % N, 256, minlen=32)
minus_e_P = multiply(minus_e, self.P.serialize(), False) minus_e_P = multiply(minus_e, self.P.format(), False)
minus_e_P2 = multiply(minus_e, self.P2.serialize(), False) minus_e_P2 = multiply(minus_e, self.P2.format(), False)
KGser = add_pubkeys([sG.serialize(), minus_e_P], False) KGser = add_pubkeys([sG.format(), minus_e_P], False)
KJser = add_pubkeys([sJ, minus_e_P2], False) KJser = add_pubkeys([sJ, minus_e_P2], False)
#check 2: e =?= H(K_G || K_J || P || P2) #check 2: e =?= H(K_G || K_J || P || P2)
e_check = hashlib.sha256(KGser + KJser + self.P.serialize() + e_check = hashlib.sha256(KGser + KJser + self.P.format() +
self.P2.serialize()).digest() self.P2.format()).digest()
if e_check == self.e: if e_check == self.e:
return True return True
#commitment fails for any NUMS in the provided range #commitment fails for any NUMS in the provided range
@ -242,7 +242,7 @@ def verify_all_NUMS(write=False):
""" """
nums_points = {} nums_points = {}
for i in range(256): for i in range(256):
nums_points[i] = binascii.hexlify(getNUMS(i).serialize()) nums_points[i] = binascii.hexlify(getNUMS(i).format())
if write: if write:
with open("nums_basepoints.txt", "wb") as f: with open("nums_basepoints.txt", "wb") as f:
from pprint import pformat from pprint import pformat
@ -258,9 +258,9 @@ def getP2(priv, nums_pt):
just the most easy way to manipulate it in the just the most easy way to manipulate it in the
library), calculate priv*nums_pt library), calculate priv*nums_pt
""" """
priv_raw = priv.private_key priv_raw = priv.secret
return multiply(priv_raw, return multiply(priv_raw,
nums_pt.serialize(), nums_pt.format(),
False, False,
return_serialized=False) return_serialized=False)

2
test/Dockerfiles/bionic.Dockerfile

@ -5,7 +5,7 @@ SHELL ["/bin/bash", "-c"]
RUN apt-get update RUN apt-get update
RUN apt-get install -y build-essential RUN apt-get install -y build-essential
RUN apt-get install -y \ RUN apt-get install -y \
automake pkg-config libtool automake pkg-config libtool libgmp-dev
RUN apt-get install -y \ RUN apt-get install -y \
python-dev python-pip python-virtualenv python-qt4 python-sip python-dev python-pip python-virtualenv python-qt4 python-sip

2
test/Dockerfiles/centos7.Dockerfile

@ -6,7 +6,7 @@ RUN yum -y groups install 'Development tools'
RUN yum -y install epel-release && \ RUN yum -y install epel-release && \
yum -y update yum -y update
RUN yum -y install \ 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 RUN useradd --home-dir /home/chaum --create-home --shell /bin/bash --skel /etc/skel/ chaum
ARG core_version ARG core_version

2
test/Dockerfiles/fedora27.Dockerfile

@ -5,7 +5,7 @@ SHELL ["/bin/bash", "-c"]
RUN dnf -y groups install 'Development tools' RUN dnf -y groups install 'Development tools'
RUN dnf -y install \ RUN dnf -y install \
autoconf libtool pkgconfig \ autoconf libtool pkgconfig \
python-devel python-pip python2-virtualenv python-devel python-pip python2-virtualenv gmp-devel
# needed for build time # needed for build time
# https://stackoverflow.com/questions/34624428/g-error-usr-lib-rpm-redhat-redhat-hardened-cc1-no-such-file-or-directory # https://stackoverflow.com/questions/34624428/g-error-usr-lib-rpm-redhat-redhat-hardened-cc1-no-such-file-or-directory

2
test/Dockerfiles/stretch.Dockerfile

@ -5,7 +5,7 @@ SHELL ["/bin/bash", "-c"]
RUN apt-get update RUN apt-get update
RUN apt-get install -y build-essential RUN apt-get install -y build-essential
RUN apt-get install -y \ RUN apt-get install -y \
automake pkg-config libtool automake pkg-config libtool libgmp-dev
RUN apt-get install -y \ RUN apt-get install -y \
python-dev python-pip python-virtualenv python-qt4 python-sip python-dev python-pip python-virtualenv python-qt4 python-sip

2
test/Dockerfiles/xenial.Dockerfile

@ -5,7 +5,7 @@ SHELL ["/bin/bash", "-c"]
RUN apt-get update RUN apt-get update
RUN apt-get install -y build-essential RUN apt-get install -y build-essential
RUN apt-get install -y \ RUN apt-get install -y \
automake pkg-config libtool automake pkg-config libtool libgmp-dev
RUN apt-get install -y \ RUN apt-get install -y \
python-dev python-pip python-virtualenv python-qt4 python-sip python-dev python-pip python-virtualenv python-qt4 python-sip

Loading…
Cancel
Save