Browse Source

support 3.3

tb
ofek 7 years ago
parent
commit
d0cf49d620
  1. 8
      .travis.yml
  2. 1
      .travis/deploy.sh
  3. 1
      .travis/install.sh
  4. 233
      README.md
  5. 50
      README.rst
  6. 32
      coincurve/keys.py
  7. 2
      tox.ini

8
.travis.yml

@ -2,6 +2,7 @@ sudo: true
language: python
python:
- '2.7'
- '3.3'
- '3.4'
- '3.5'
- '3.6'
@ -27,6 +28,13 @@ matrix:
env:
- TRAVIS_PYTHON_VERSION=2.7
- BUNDLED=1
- os: osx
language: generic
osx_image: xcode7.1
python: 3.3
env:
- TRAVIS_PYTHON_VERSION=3.3
- BUNDLED=1
- os: osx
language: generic
osx_image: xcode7.1

1
.travis/deploy.sh

@ -18,7 +18,6 @@ if [[ "$TRAVIS_OS_NAME" == "linux" && ${BUILD_LINUX_WHEELS} -eq 1 ]]; then
fi
.travis/build_windows_wheels.sh
else
# Only build wheels for the non experimental bundled version
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
python -m pip install wheel
python setup.py bdist_wheel

1
.travis/install.sh

@ -10,6 +10,7 @@ if [[ $TRAVIS_OS_NAME == "osx" ]]; then
# We use the official python.org installers to make sure our wheels are
# going to be as widely compatible as possible
PYTHON_PKG_27="https://www.python.org/ftp/python/2.7.12/python-2.7.12-macosx10.6.pkg"
PYTHON_PKG_33="http://www.python.org/ftp/python/3.3.5/python-3.3.5-macosx10.6.dmg"
PYTHON_PKG_34="https://www.python.org/ftp/python/3.4.4/python-3.4.4-macosx10.6.pkg"
PYTHON_PKG_35="https://www.python.org/ftp/python/3.5.2/python-3.5.2-macosx10.6.pkg"
PYTHON_PKG_36="https://www.python.org/ftp/python/3.6.1/python-3.6.1-macosx10.6.pkg"

233
README.md

@ -1,233 +0,0 @@
# Coincurve
Python CFFI bindings for [libsecp256k1](https://github.com/bitcoin-core/secp256k1)
(an experimental and optimized C library for EC operations on curve secp256k1).
This is a fork of [https://github.com/ludbb/secp256k1-py](https://github.com/ludbb/secp256k1-py).
New features include:
- Support for Windows (Python 3 only though, unlike Linux/macOS)
- Linux, macOS, and Windows all have binary packages for both 64 and 32-bit architectures
- A fix to remove CFFI warnings
- A global context is used by default, drastically increasing performance
- Each release uses newest version of [libsecp256k1](https://github.com/bitcoin-core/secp256k1)
- Implements a fix for [https://bugs.python.org/issue28150](https://bugs.python.org/issue28150)
to support Python 3.6+ on macOS
- Linux & macOS wheels now use GMP
- Endomorphism optimization is now enabled
**To retain backward compatibility with secp256k1-py, use coincurve version 2.1.1 specifically!**
Anything after that will have a modified (cleaner :) API and only support Python 3.
## Installation
```
pip install coincurve==2.1.1
```
### Precompiled binary packages (wheels)
Precompiled binary wheels is available for Python 2.7, 3.3, 3.4, and 3.5 on Linux. To take advantage of those you need to use pip >= 8.1.0.
## API
#### class `coincurve.PrivateKey(privkey, raw, flags)`
The `PrivateKey` class loads or creates a private key by obtaining 32 bytes from urandom and operates over it.
##### Instantiation parameters
- `privkey=None` - generate a new private key if None, otherwise load a private key.
- `raw=True` - if `True`, it is assumed that `privkey` is just a sequence of bytes, otherwise it is assumed that it is in the DER format. This is not used when `privkey` is not specified.
- `flags=coincurve.ALL_FLAGS` - see Constants.
##### Methods and instance attributes
- `pubkey`: an instance of `coincurve.PublicKey`.
- `private_key`: raw bytes for the private key.
- `set_raw_privkey(privkey)`<br/>
update the `private_key` for this instance with the bytes specified by `privkey`. If `privkey` is invalid, an Exception is raised. The `pubkey` is also updated based on the new private key.
- `serialize()` -> bytes<br/>
convert the raw bytes present in `private key` to a hexadecimal string.
- `deserialize(privkey_ser)` -> bytes<br/>
convert from a hexadecimal string to raw bytes and update the `pubkey` and `private_key` for this instance.
- `tweak_add(scalar)` -> bytes<br/>
tweak the current private key by adding a 32 byte scalar to it and return a new raw private key composed of 32 bytes.
- `tweak_mul(scalar)` -> bytes<br/>
tweak the current private key by multiplying it by a 32 byte scalar and return a new raw private key composed of 32 bytes.
- `ecdsa_sign(msg, raw=False, digest=hashlib.sha256)` -> internal object<br/>
by default, create an ECDSA-SHA256 signature from the bytes in `msg`. If `raw` is True, then the `digest` function is not applied over `msg`, otherwise the `digest` must produce 256 bits or an `Exception` will be raised.<br/><br/>
The returned object is a structure from the C lib. If you want to store it (on a disk or similar), use `ecdsa_serialize` and later on use `ecdsa_deserialize` when loading.
- `ecdsa_sign_recoverable(msg, raw=False, digest=hashlib.sha256)` -> internal object<br/>
create a recoverable ECDSA signature. See `ecdsa_sign` for parameters description.
> NOTE: `ecdsa_sign_recoverable` can only be used if the `secp256k1` C library is compiled with support for it. If there is no support, an Exception will be raised when calling it.
- `schnorr_sign(msg, raw=False, digest=hashlib.sha256)` -> bytes<br/>
create a signature using a custom EC-Schnorr-SHA256 construction. It
produces non-malleable 64-byte signatures which support public key recovery
batch validation, and multiparty signing. `msg`, `raw`, and `digest` are used as described in `ecdsa_sign`.
- `schnorr_generate_nonce_pair(msg, raw=False, digest=hashlib.sha256)` -> (internal object, internal object)<br/>
generate a nonce pair deterministically for use with `schnorr_partial_sign`. `msg`, `raw`, and `digest` are used as described in `ecdsa_sign`.
- `schnorr_partial_sign(msg, privnonce, pubnonce_others, raw=False, digest=hashlib.sha256)` -> bytes<br/>
produce a partial Schnorr signature, which can be combined using `schnorr_partial_combine` to end up with a full signature that is verifiable using `PublicKey.schnorr_verify`. `privnonce` is the second item in the tuple returned by `schnorr_generate_nonce_pair`, `pubnonce_others` represent the combined public nonces excluding the one associated to this `privnonce`. `msg`, `raw`, and `digest` are used as described in `ecdsa_sign`.<br/><br/>
To combine pubnonces, use `PublicKey.combine`.<br/><br/>
Do not pass the pubnonce produced for the respective privnonce; combine the pubnonces from other signers and pass that instead.
#### class `coincurve.PublicKey(pubkey, raw, flags)`
The `PublicKey` class loads an existing public key and operates over it.
##### Instantiation parameters
- `pubkey=None` - do not load a public key if None, otherwise do.
- `raw=False` - if `False`, it is assumed that `pubkey` has gone through `PublicKey.deserialize` already, otherwise it must be specified as bytes.
- `flags=coincurve.FLAG_VERIFY` - see Constants.
##### Methods and instance attributes
- `public_key`: an internal object representing the public key.
- `serialize(compressed=True)` -> bytes<br/>
convert the `public_key` to bytes. If `compressed` is True, 33 bytes will be produced, otherwise 65 will be.
- `deserialize(pubkey_ser)` -> internal object<br/>
convert the bytes resulting from a previous `serialize` call back to an internal object and update the `public_key` for this instance. The length of `pubkey_ser` determines if it was serialized with `compressed=True` or not. This will raise an Exception if the size is invalid or if the key is invalid.
- `combine(pubkeys)` -> internal object<br/>
combine multiple public keys (those returned from `PublicKey.deserialize`) and return a public key (which can be serialized as any other regular public key). The `public_key` for this instance is updated to use the resulting combined key. If it is not possible the combine the keys, an Exception is raised.
- `tweak_add(scalar)` -> internal object<br/>
tweak the current public key by adding a 32 byte scalar times the generator to it and return a new PublicKey instance.
- `tweak_mul(scalar)` -> internal object<br/>
tweak the current public key by multiplying it by a 32 byte scalar and return a new PublicKey instance.
- `ecdsa_verify(msg, raw_sig, raw=False, digest=hashlib.sha256)` -> bool<br/>
verify an ECDSA signature and return True if the signature is correct, False otherwise. `raw_sig` is expected to be an object returned from `ecdsa_sign` (or if it was serialized using `ecdsa_serialize`, then first run it through `ecdsa_deserialize`). `msg`, `raw`, and `digest` are used as described in `ecdsa_sign`.
- `schnorr_verify(msg, schnorr_sig, raw=False, digest=hashlib.sha256)` -> bool<br/>
verify a Schnorr signature and return True if the signature is correct, False otherwise. `schnorr_sig` is expected to be the result from either `schnorr_partial_combine` or `schnorr_sign`. `msg`, `raw`, and `digest` are used as described in `ecdsa_sign`.
- `ecdh(scalar)` -> bytes<br/>
compute an EC Diffie-Hellman secret in constant time. The instance `public_key` is used as the public point, and the `scalar` specified must be composed of 32 bytes. It outputs 32 bytes representing the ECDH secret computed. If the `scalar` is invalid, an Exception is raised.
> NOTE: `ecdh` can only be used if the `secp256k1` C library is compiled with support for it. If there is no support, an Exception will be raised when calling it.
#### class `coincurve.ECDSA`
The `ECDSA` class is intended to be used as a mix in. Its methods can be accessed from any `coincurve.PrivateKey` or `coincurve.PublicKey` instances.
##### Methods
- `ecdsa_serialize(raw_sig)` -> bytes<br/>
convert the result from `ecdsa_sign` to DER.
- `ecdsa_deserialie(ser_sig)` -> internal object<br/>
convert DER bytes to an internal object.
- `ecdsa_serialize_compact(raw_sig)` -> bytes<br/>
convert the result from `ecdsa_sign` to a compact serialization of 64 bytes.
- `ecdsa_deserialize_compact(ser_sig)` -> internal object<br/>
convert a compact serialization of 64 bytes to an internal object.
- `ecdsa_signature_normalize(raw_sig, check_only=False)` -> (bool, internal object | None)<br/>
check and optionally convert a signature to a normalized lower-S form. If `check_only` is True then the normalized signature is not returned.<br/><br/>
This function always return a tuple containing a boolean (True if not previously normalized or False if signature was already normalized), and the normalized signature. When `check_only` is True, the normalized signature returned is always None.
- `ecdsa_recover(msg, recover_sig, raw=False, digest=hashlib.sha256)` -> internal object<br/>
recover an ECDSA public key from a signature generated by `ecdsa_sign_recoverable`. `recover_sig` is expected to be an object returned from `ecdsa_sign_recoverable` (or if it was serialized using `ecdsa_recoverable_serialize`, then first run it through `ecdsa_recoverable_deserialize`). `msg`, `raw`, and `digest` are used as described in `ecdsa_sign`.<br/><br/>
In order to call `ecdsa_recover` from a `PublicKey` instance, it's necessary to create the instance by settings `flags` to `ALL_FLAGS`: `coincurve.PublicKey(..., flags=coincurve.ALL_FLAGS)`.
- `ecdsa_recoverable_serialize(recover_sig)` -> (bytes, int)<br/>
convert the result from `ecdsa_sign_recoverable` to a tuple composed of 65 bytesand an integer denominated as recovery id.
- `ecdsa_recoverable_deserialize(ser_sig, rec_id)`-> internal object<br/>
convert the result from `ecdsa_recoverable_serialize` back to an internal object that can be used by `ecdsa_recover`.
- `ecdsa_recoverable_convert(recover_sig)` -> internal object<br/>
convert a recoverable signature to a normal signature, i.e. one that can be used by `ecdsa_serialize` and related methods.
> NOTE: `ecdsa_recover*` can only be used if the `secp256k1` C library is compiled with support for it. If there is no support, an Exception will be raised when calling any of them.
#### Constants
##### `coincurve.FLAG_SIGN`
##### `coincurve.FLAG_VERIFY`
##### `coincurve.ALL_FLAGS`
`ALL_FLAGS` combines `FLAG_SIGN` and `FLAG_VERIFY` using bitwise OR.
These flags are used during context creation (undocumented here) and affect which parts of the context are initialized in the C library. In these bindings, some calls are disabled depending on the active flags but this should not be noticeable unless you are manually specifying flags.
## Example
```python
from coincurve import PrivateKey, PublicKey
privkey = PrivateKey()
privkey_der = privkey.serialize()
assert privkey.deserialize(privkey_der) == privkey.private_key
sig = privkey.ecdsa_sign(b'hello')
verified = privkey.pubkey.ecdsa_verify(b'hello', sig)
assert verified
sig_der = privkey.ecdsa_serialize(sig)
sig2 = privkey.ecdsa_deserialize(sig_der)
vrf2 = privkey.pubkey.ecdsa_verify(b'hello', sig2)
assert vrf2
pubkey = privkey.pubkey
pub = pubkey.serialize()
pubkey2 = PublicKey(pub, raw=True)
assert pubkey2.serialize() == pub
assert pubkey2.ecdsa_verify(b'hello', sig)
```
```python
from coincurve import PrivateKey
key = '31a84594060e103f5a63eb742bd46cf5f5900d8406e2726dedfc61c7cf43ebad'
msg = '9e5755ec2f328cc8635a55415d0e9a09c2b6f2c9b0343c945fbbfe08247a4cbe'
sig = '30440220132382ca59240c2e14ee7ff61d90fc63276325f4cbe8169fc53ade4a407c2fc802204d86fbe3bde6975dd5a91fdc95ad6544dcdf0dab206f02224ce7e2b151bd82ab'
privkey = PrivateKey(bytes(bytearray.fromhex(key)), raw=True)
sig_check = privkey.ecdsa_sign(bytes(bytearray.fromhex(msg)), raw=True)
sig_ser = privkey.ecdsa_serialize(sig_check)
assert sig_ser == bytes(bytearray.fromhex(sig))
```
```python
from coincurve import PrivateKey
key = '7ccca75d019dbae79ac4266501578684ee64eeb3c9212105f7a3bdc0ddb0f27e'
pub_compressed = '03e9a06e539d6bf5cf1ca5c41b59121fa3df07a338322405a312c67b6349a707e9'
pub_uncompressed = '04e9a06e539d6bf5cf1ca5c41b59121fa3df07a338322405a312c67b6349a707e94c181c5fe89306493dd5677143a329065606740ee58b873e01642228a09ecf9d'
privkey = PrivateKey(bytes(bytearray.fromhex(key)))
pubkey_ser = privkey.pubkey.serialize()
pubkey_ser_uncompressed = privkey.pubkey.serialize(compressed=False)
assert pubkey_ser == bytes(bytearray.fromhex(pub_compressed))
assert pubkey_ser_uncompressed == bytes(bytearray.fromhex(pub_uncompressed))
```

50
README.rst

@ -0,0 +1,50 @@
Coincurve
=========
.. image:: https://img.shields.io/pypi/v/coincurve.svg?style=flat-square
:target: https://pypi.org/project/coincurve
.. image:: https://img.shields.io/travis/ofek/coincurve.svg?branch=master&style=flat-square
:target: https://travis-ci.org/ofek/coincurve
.. image:: https://img.shields.io/pypi/pyversions/coincurve.svg?style=flat-square
:target: https://pypi.org/project/coincurve
.. image:: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square
:target: https://en.wikipedia.org/wiki/MIT_License
-----
This library provides well-tested Python CFFI bindings for
`libsecp256k1 <https://github.com/bitcoin-core/secp256k1>`_, the heavily
optimized C library used by `Bitcoin Core <https://github.com/bitcoin/bitcoin>`_
for operations on elliptic curve secp256k1.
Coincurve replaces `secp256k1-py <https://github.com/ludbb/secp256k1-py>`_.
New features include:
- Support for Windows
- Linux, macOS, and Windows all have binary packages for both 64 and 32-bit architectures
- Linux & macOS use GMP for faster computation
- Endomorphism optimization is enabled
- A global context is used by default, drastically increasing performance
- A fix to remove CFFI warnings
- Each release uses newest version of `libsecp256k1 <https://github.com/bitcoin-core/secp256k1>`_
- Implements a fix for `<https://bugs.python.org/issue28150>`_ to support Python 3.6+ on macOS
**To retain backward compatibility with secp256k1-py, use coincurve version 2.1.1 specifically!**
Anything after that has a modified (cleaner :) API.
Installation
------------
Coincurve is distributed on PyPI and is available on Linux/macOS and Windows and
supports Python 3.3+ and PyPy3.3-5.5+.
.. code-block:: bash
$ pip install coincurve
API
---

32
coincurve/keys.py

@ -236,20 +236,6 @@ class PublicKey:
public_key = self.format(compressed=False)
return bytes_to_int(public_key[1:33]), bytes_to_int(public_key[33:])
def combine(self, public_keys):
"""Add a number of public keys together."""
new_key = ffi.new('secp256k1_pubkey *')
combined = lib.secp256k1_ec_pubkey_combine(
self.context.ctx, new_key, [pk.public_key for pk in public_keys],
len(public_keys)
)
if not combined:
raise ValueError('The sum of the public keys is not valid.')
self.public_key = new_key
def verify(self, signature, message, hasher=sha256):
msg_hash = hasher(message)
if len(msg_hash) != 32:
@ -318,5 +304,23 @@ class PublicKey:
return PublicKey(new_key, self.context)
def combine(self, public_keys, update=False):
"""Add a number of public keys together."""
new_key = ffi.new('secp256k1_pubkey *')
combined = lib.secp256k1_ec_pubkey_combine(
self.context.ctx, new_key, [pk.public_key for pk in public_keys],
len(public_keys)
)
if not combined:
raise ValueError('The sum of the public keys is invalid.')
if update:
self.public_key = new_key
return self
return PublicKey(new_key, self.context)
def __eq__(self, other):
return self.format(compressed=False) == other.format(compressed=False)

2
tox.ini

@ -1,6 +1,6 @@
[tox]
mintoxversion = 2.0
envlist = py{27,34,35,36,py}-{default,experimental}
envlist = py{27,33,34,35,36,py}-{default,experimental}
[testenv]
passenv = *

Loading…
Cancel
Save