NOTE: THIS LIBRARY HAS ONLY BEEN TESTED ON MACOSX. AND IT HAS BEEN BARELY TESTED REALLY AT ALL.
This library is designed to directly mimic the functionality of bitcoin-cores's libsecp256k1 library. It is designed to be a lower level wrapper around libsecp256k1 so it can be be easily integrated into other projects.
The CFFI library is used to create the Python bindings.
From libsecp256k1, make sure libsecp256k1.dylib
is installed in /usr/local/lib/
via:
$ ./autogen.sh
$ ./configure --enable-module-ecdh --enable-module-recovery --enable-experimental
$ make
$ sudo make install
Install from PyPi:
$ pip install secpy256k1
Import the secpy256k1
package at the top of your python script:
import secpy256k1
$ git clone git@github.com:rrybarczyk/secpy256k1.git
$ cd ./secpy256k1
Build submodule bitcoin-core libsecp256k1 repo library repo:
$ cd ./secp256k1
$ git submodule init
$ git submodule update
$ ./autogen.sh
$ ./configure --enable-module-ecdh --enable-module-recovery --enable-experimental
$ make
$ sudo make install
Install dependencies and build _secpy256k1
bindings:
$ pipenv install
The tests are currently lacking. Intend to use test vectors from the libsecp256k1 library.
$ pipenv run pytest
$ pipenv run python ./secpy256k1/examples/ex_script.py
Barring context_create
, the first argument to each function is a secp256k1_context
object. The context object is initialized as SECP256K1_CONTEXT_NONE
, SECP256K1_CONTEXT_VERIFY
, or SECP256K1_CONTEXT_SIGN
.
For functions that are context agnostic, it is customary to use SECP256K1_NONE
. These functions are:
context_destroy
Destroy a secp256k1 context object.context_clone
Copies a secp256k1 context object.context_set_illegal_callback
(TODO) Set a callback function to be called when an illegal argument is passed to an API call. It will only trigger for violations that are mentioned explicitly in the header.context_set_error_callback
(TODO) Set a callback function to be called when an internal consistency check fails. The default is crashing.scratch_space_create
(TODO) Create a secp256k1 scratch space object.ec_pubkey_parse
Parse a variable-length public key into the pubkey object.ec_pubkey_serialize
Serialize a pubkey object into a serialized byte sequence.ecdsa_signature_parse_compact
Parse an ECDSA signature in compact (64 bytes) format.ecdsa_signature_parse_der
Parse a DER ECDSA signature.ecdsa_signature_serialize_der
Serialize an ECDSA signature in DER format.ecdsa_signature_serialize_compact
Serialize an ECDSA signature in compact (64 byte) format.ecdsa_signature_normalize
Convert a signature to a normalized lower-S form.ec_seckey_verify
Verify an ECDSA secret key.ec_privkey_negate
Negates a private key in place.ec_pubkey_negate
Negates a public key in place.ec_privkey_tweak_add
Tweak a private key by adding tweak to it.ec_privkey_tweak_mul
Tweak a private key by multiplying it by a tweak.ec_pubkey_combine
Add a number of public keys together.
The functions that require the context to be initialized to SECP256K1_CONTEXT_VERIFY
are:
ecdsa_verify
Verify an ECDSA signature.ec_pubkey_tweak_add
Tweak a public key by adding tweak times the generator to it.ec_pubkey_tweak_mul
Tweak a public key by multiplying it by a tweak value.
The functions that require the context to be initialized as SECP256K1_CONTEXT_SIGN
are:
ecdsa_sign
Create an ECDSA signature.ec_pubkey_create
Compute the public key for a secret key.context_randomize
Updates the context randomization to protect against side-channel leakage.
# Set verify flag
flags = secpy256k1.lib.SECP256K1_CONTEXT_VERIFY
# Set sign flag
flags = secpy256k1.lib.SECP256K1_CONTEXT_SIGN
# Set none flag
flags = secpy256k1.lib.SECP256K1_CONTEXT_NONE
secp256k1_ctx = secpy256k1.context_create(flags)
secp256k1_ctx_clone = secpy256k1.context_clone(secp256k1_ctx)
secp256k1_ctx_destroy = secpy256k1.context_destroy(secp256k1_ctx)
Call this function after secpy256k1.context_create
or secpy256k1.context_clone
and may call this repeatedly afterwards.
import os
seed32 = os.urandom(32)
func_res = secpy256k1.context_randomize(ctx=secp256k1_ctx, seed32)
Public key flags:
SECP256K1_COMPRESSED
- flags a 33-byte compressed pubkeySECP256K1_UNCOMPRESSED
- flags a 65-byte uncompressed pubkey
Set pubkey compression flag:
# Set compressed pubkey flag
pubkey = bytes.fromhex('0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352')
compression_flag = secpy256k1.lib.SECP256K1_COMPRESSED
# Set uncompressed pubkey flag
pubkey = bytes.fromhex('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8')
compression_flag = secpy256k1.lib.SECP256K1_UNCOMPRESSED
Create a pointer to a secp256k1_pubkey
object from a compressed or uncompressed serialized pubkey byte string:
pk_valid, secp256k1_pk = secpy256k1.ec_pubkey_parse(secp256k1_ctx, pubkey)
Serialize secp256k1_pubkey
object into a serialized pubkey byte string:
# Set compression flag
compression_flag = secpy256k1.lib.SECP256k1_EC_COMPRESSED
pubkey_valid, pubkey, pubkey_len = secpy256k1.ec_pubkey_serialize(secp256k1_ctx, secp256k1_pk, compression_flag
Create a pointer to a secp26k1_pubkey
object containing the corresponding public key to a given private key:
import os
priv_key = os.urandom(32)
func_ret, secp256k1_pubkey = secpy256k1.ec_pubkey_create(ctx=secp256k1_ctx, seckey=priv_key)
Negate public key:
TODO -> ec_pubkey_negate(ctx=secp256k1_ctx, pubkey=secp256k1_pubkey)
Negate private key:
TODO -> ec_privkey_negate(ctx=secp256k1_ctx, seckey=priv_key)
Add a number of public keys together:
pubkeys = [pubkey1, pubkey2, pubkey3]
func_ret, secp256k1_pubkey = secpy256k1.ec_pubkey_combine(ctx=secp256k1_ctx, pubkeys=pubkeys)
- ecdsa_signature_parse_compact
- ecdsa_signature_parse_der
- ecdsa_signature_serialize_der
- ecdsa_signature_serialize_compact
- ecdsa_verify
- ecdsa_signature_normalize
- ecdsa_sign
- ec_seckey_verify
Define a tweak:
import os
tweak = os.urandom(32)
Tweak a secp256k1_pubkey object by adding tweak
times the generator to it:
func_ret, secp256k1_pubkey_tweaked = secpy256k1.ec_pubkey_tweak_add(ctx=secp256k1_ctx, pubkey=secp256k1_pubkey, tweak=tweak)
Tweak a private key by adding tweak
times the generator to it:
func_ret, priv_key_tweaked = secpy256k1.ec_privkey_tweak_add(ctx=secp256k1_ctx, seckey=priv_key, tweak=tweak)
Tweak a secp256k1_pubkey object by multiplying tweak
by a tweak value:
func_ret, secp256k1_pubkey_tweaked = secpy256k1.ec_pubkey_tweak_mul(ctx=secp256k1_ctx, pubkey=secp256k1_pubkey, tweak=tweak)
Tweak a private key by multiplying tweak
it by a tweak value:
func_ret, priv_key_tweaked = secpy256k1.ec_privkey_tweak_mul(ctx=secp256k1_ctx, seckey=priv_key, tweak=tweak)
Compute an ECDH secret in constant time:
func_ret, ecdh_secret = secpy256k1.ecdh(ctx=secp256k1_ctx, pubkey=secp256k1_pubkey, privkey=priv_key)