Welcome to ECPy’s documentation!

Indices and tables

Status

ECPy is in beta stage but already used in some internal tooling.
Any constructive comment is welcome.
Version

1.0beta.

Authors

Cedric Mesnil, <csalshm@gmail.com>

License

Apache 2.0


Install

ECPy is originally coded for Python 3, but should run under python 2.7 by using future.

If you are running Python 2:

  • please install the future into the present:

    pip install future

  • as python 2 is in ‘End Of Life’ status, consider to move your code to python 3, future release will may not support python 2.

Then install ECPy:

  • From pypi (using pip or pip3)
    • pip install ECPy

  • Rebuild from git clone:
    • python3 setup.py sdist

    • cd dist

    • tar xzvf ECPy-M.m.tar.gz

    • python3 setup install

  • Install from dist package:
    • Download last dist tarball

    • tar xzvf ECPy-M.m.tar.gz

    • python3 setup.py install


Overview

ECPy (pronounced ekpy), is a pure python Elliptic Curve library. It provides ECDSA, EDDSA, ECSchnorr signature as well as Point operation.

ECDSA sample

from ecpy.curves     import Curve,Point
from ecpy.keys       import ECPublicKey, ECPrivateKey
from ecpy.ecdsa      import ECDSA

cv     = Curve.get_curve('secp256k1')
pu_key = ECPublicKey(Point(0x65d5b8bf9ab1801c9f168d4815994ad35f1dcb6ae6c7a1a303966b677b813b00,

                           0xe6b865e529b8ecbf71cf966e900477d49ced5846d7662dd2dd11ccd55c0aff7f,
                           cv))
pv_key = ECPrivateKey(0xfb26a4e75eec75544c0f44e937dcf5ee6355c7176600b9688c667e5c283b43c5,
                      cv)


signer = ECDSA()
sig    = signer.sign(b'01234567890123456789012345678912',pv_key)
assert(signer.verify(b'01234567890123456789012345678912',sig,pu_key))

Point sample

from ecpy.curves     import Curve,Point

cv = Curve.get_curve('secp256k1')
P  = Point(0x65d5b8bf9ab1801c9f168d4815994ad35f1dcb6ae6c7a1a303966b677b813b00,
           0xe6b865e529b8ecbf71cf966e900477d49ced5846d7662dd2dd11ccd55c0aff7f,
           cv)
k  = 0xfb26a4e75eec75544c0f44e937dcf5ee6355c7176600b9688c667e5c283b43c5
Q  = k*P
R  = P+Q

Supported Curves & Signature

ECPy support the following curves
  • Short Weierstrass form: y²=x³+a*x+b

  • Twisted Edward form: a*x²+y2=1+d*x²*y²

  • Montgomery form: b.y²=x³+a*x²+x.

See pyec.Curve. get_curve_names

ECPy supports the following signature schemes:

  • ecdsa

  • eddsa

  • ecschnorr

Types

ECPY use binary bytes and int as primary types.

int are used when scalar is required, as for point coordinate, scalar multiplication, ….
bytes are used when data is required, as hash value, message, …

Other main types are Point, Curve, Key, ECDSA, EDDSA, ECSchnorr. Borromean.

See API details…

API

curves module

Elliptic Curve and Point manipulation

exception ecpy.curves.ECPyException(value)[source]

Bases: Exception

class ecpy.curves.Curve(parameters)[source]

Bases: object

Elliptic Curve abstraction

You should not directly create such Object. Use get_curve to get the predefined curve or create a well-know type of curve with your parameters

Supported well know elliptic curve are:
  • Short Weierstrass form: y²=x³+a*x+b

  • Twisted Edward a*x²+y2=1+d*x²*y²

  • Montgomery: b.y²=x³+a*x²+x.

name

curve name, the one given to get_curve or return by get_curve_names

Type

str

size

bit size of curve

Type

int

a

first curve parameter

Type

int

b d

second curve parameter

Type

int

field

curve field

Type

int

generator

curve point generator

Type

Point

order

order of generator

Type

int

add_point(P, Q)[source]

Returns the sum of P and Q

Parameters
  • P (Point) – first point to add

  • Q (Point) – second point to add

Returns

A new Point R = P+Q

Return type

Point

Raises

ECPyException – with “Point not on curve”, if Point R is not on curve, thus meaning either P or Q was not on.

decode_point(eP)[source]

decode/decompress a point according to its curve

encode_point(P)[source]

encode/compress a point according to its curve

static get_curve(name)[source]

Return a Curve object according to its name

Parameters

name (str) – curve name to retrieve

Returns

Curve object, or None if curve is unknown

Return type

Curve

static get_curve_names()[source]

Returns all known curve names

Returns

list of names as str

Return type

tuple

is_on_curve(P)[source]

Check if P is on this curve

This function ignores the default curve attach to P

Parameters

P (Point) – Point to check

Returns

True if P is on curve, False else

Return type

bool

mul_point(k, P)[source]

Returns the scalar multiplication P with k.

This function ignores the default curve attach to P and Q, and assumes P and Q are on this curve.

Parameters
  • P (Point) – point to mul_point

  • k (int) – scalar to multiply

Returns

A new Point R = k*Q

Return type

Point

Raises
  • ECPyException – with “Point not on curve”, if Point R is not

  • on curve, thus meaning P was not on.

neg_point(P)[source]

Returns R, R = -P.

Parameters

P (Point) – point to mul_point

Returns

A new Point R = -Q

Return type

Point

Raises
  • ECPyException – with “Point not on curve”, if Point R is not

  • on curve, thus meaning P was not on.

sub_point(P, Q)[source]

Returns the difference of P and Q

Parameters
  • P (Point) – first point to subtract with

  • Q (Point) – second point to subtract to

Returns

A new Point R = P-Q

Return type

Point

Raises

ECPyException – with “Point not on curve”, if Point R is not on curve, thus meaning either P or Q was not on.

x_recover(y, sign=0)[source]

Recover the x coordinate according to y

This method is currently only supported for TwiestedEdward curve

Parameters
  • the coordinate (y) –

  • the sign of x (sign) –

Returns

x coordinate

y_recover(x, sign=0)[source]

Recover the y coordinate according to x

This method is currently only supported for Weierstrass and Montgomery curve

Parameters
  • the coordinate (x) –

  • the sign of y (sign) –

Returns

y coordinate

class ecpy.curves.MontgomeryCurve(domain)[source]

Bases: ecpy.curves.Curve

An elliptic curve defined by the equation: b.y²=x³+a*x²+x.

The given domain must be a dictionary providing the following keys/values:
  • name (str) : curve unique name

  • size (int) : bit size

  • a (int) : a equation coefficient

  • b (int) : b equation coefficient

  • field (inf) : field value

  • generator (int[2]) : x,y coordinate of generator

  • order (int) : order of generator

Note: you should not use the constructor and only use Curve.get_curve() builder to ensure using supported curve.

Parameters

domain (dict) – a dictionary providing curve domain parameters

decode_point(eP)[source]

Decodes a point P according to RFC7748.

Parameters
  • eP (bytes) – encoded point

  • curve (Curve) – curve on witch point is

Returns

decoded point

Return type

Point

encode_point(P)[source]

Encodes a point P according to RFC7748.

Parameters

P – point to encode

Returns

encoded point

Return type

bytes

is_on_curve(P)[source]

See Curve.is_on_curve()

y_recover(x, sign=0)[source]
class ecpy.curves.Point(x, y, curve, check=True)[source]

Bases: object

Immutable Elliptic Curve Point.

A Point support the following operator:

  • - : Point Subtraction.

  • + : Point Addition, with automatic doubling support.

  • * : Scalar multiplication, can write as k*P or P*k, with P :class:Point and k :class:int.

  • ==: Point comparison.

  • - : Point negation (unary operator).

x

Affine x coordinate

Type

int

y

Affine y coordinate

Type

int

curve

Curve on which the point is define

Type

Curve

check

Check or not if the built point is on curve

Type

bool

Parameters
  • x (int) – x coordinate

  • y (int) – y coordinate

  • check (bool) – if True enforce x,y is on curve

Raises

ECPyException – if check=True and x,y is not on curve

add(Q)[source]

Return the addition of self and provided point.

Parameters

Q (Point) – Point to add

Returns

self+Q

Return type

Point

eq(Q)[source]

Tells is the provided Point and this point have the same coordinate.

Parameters

Q (Point) – Point to check the equality

Returns

True if P==Q, False else.

Return type

bool

static infinity()[source]

Return the unique (singleton) point at infinity

Returns

infinity Point

Return type

Point

mul(k)[source]

Return the scalar multiplication of self by k

Parameters

k (int) – the scalar to multiply

Returns

k*self

Return type

Point

neg()[source]

Return the opposite self point bycallinf Curve.neg function

Returns

-self

Return type

Point

recover(sign=0)[source]

Recvoer the missing corrdinate according to the know one and the provided sign of the missing one

Parameters

sign (int) – zero or one

sub(Q)[source]

Return the subtraction of felf and provided point.

Parameters

Q (Point) – Point to subtract

Returns

self-Q

Return type

Point

property curve

Returned the curve on which this point is defined

Returns

this point curve

Return type

Curve

property has_x

Tell if this point has y coordinate

Returns

Trueu if x coordinate is set, False else

property has_y

Tell if this point has y coordinate

Returns

Trueu if y coordinate is set, False else

property is_infinity

Tell is this pointn is the inifinity one

Returns

true if self is infinity

Return type

bool

property is_on_curve

” Tells if this point is on the curve

Returns

True if point on curve, False else

Return type

bool

property x

X affine coordinate of this point

Returns

x coordinate

Raises
property y

Y affine coordinate of this point

Returns

x coordinate

Raises
class ecpy.curves.TwistedEdwardCurve(domain)[source]

Bases: ecpy.curves.Curve

An elliptic curve defined by the equation: a*x²+y²=1+d*x²*y²

The given domain must be a dictionary providing the following keys/values:
  • name (str) : curve unique name

  • size (int) : bit size

  • a (int) : a equation coefficient

  • d (int) : b equation coefficient

  • field (inf) : field value

  • generator (int[2]) : x,y coordinate of generator

  • order (int) : order of generator

Note: you should not use the constructor and only use Curve.get_curve() builder to ensure using supported curve.

Parameters

domain (dict) – a dictionary providing curve domain parameters

decode_point(eP)[source]

Decodes a point P according to draft_irtf-cfrg-eddsa-04.

Parameters
  • eP (bytes) – encoded point

  • curve (Curve) – curve on witch point is

Returns

decoded point

Return type

Point

static decode_scalar_25519(k)[source]

decode scalar according to RF7748 and draft-irtf-cfrg-eddsa

Parameters

k (bytes) – scalar to decode

Returns

decoded scalar

Return type

int

encode_point(P)[source]

Encodes a point P according to draft_irtf-cfrg-eddsa-04.

Parameters

P – point to encode

Returns

encoded point

Return type

bytes

static encode_scalar_25519(k)[source]

encode scalar according to RF7748 and draft-irtf-cfrg-eddsa

Parameters

k (int) – scalar to encode

Returns

encoded scalar

Return type

bytes

is_on_curve(P)[source]

See Curve.is_on_curve()

x_recover(y, sign=0)[source]

Retrieves the x coordinate according to the y one, such that point (x,y) is on curve.

Parameters
  • y (int) – y coordinate

  • sign (int) – sign of x

Returns

the computed x coordinate

Return type

int

class ecpy.curves.WeierstrassCurve(domain)[source]

Bases: ecpy.curves.Curve

An elliptic curve defined by the equation: y²=x³+a*x+b.

The given domain must be a dictionary providing the following keys/values:
  • name (str) : curve unique name

  • size (int) : bit size

  • a (int) : a equation coefficient

  • b (int) : b equation coefficient

  • field (inf) : field value

  • generator (int[2]) : x,y coordinate of generator

  • order (int) : order of generator

  • cofactor (int) : cofactor

Note: you should not use the constructor and only use Curve.get_curve() builder to ensure using supported curve.

Parameters

domain (dict) – a dictionary providing curve parameters

decode_point(eP)[source]

Decodes a point P according to P1363-2008.

Parameters
  • eP (bytes) – encoded point

  • curve (Curve) – curve on witch point is

Returns

decoded point

Return type

Point

encode_point(P, compressed=False)[source]

Encodes a point P according to P1363-2000.

Parameters

P – point to encode

Returns

encoded point [04 | x | y] or [02 | x | sign]

Return type

bytes

is_on_curve(P)[source]

See Curve.is_on_curve()

y_recover(x, sign=0)[source]

keys module

class ecpy.keys.ECPrivateKey(d, curve)[source]

Bases: object

Public EC key.

Can be used for both ECDSA and EDDSA signature

Attributes

d (int) : private key scalar curve (Curve) : curve

Parameters
  • d (int) – private key value

  • curve (Curve) – curve

get_public_key()[source]

Returns the public key corresponding to this private key

This method considers the private key the generator multiplier and return pv*Generator in all cases.

For specific derivation such as in EdDSA, see ecpy.eddsa.get_public_key

Returns

public key

Return type

ECPublicKey

class ecpy.keys.ECPublicKey(W)[source]

Bases: object

Public EC key.

Can be used for both ECDSA and EDDSA signature

W

public key point

Type

Point

Parameters

W (Point) – public key value

ECDSA module

class ecpy.ecdsa.ECDSA(fmt='DER')[source]

Bases: object

ECDSA signer.

Parameters

fmt (str) – in/out signature format. See ecpy.formatters

sign(msg, pv_key, canonical=False)[source]

Signs a message hash.

Parameters
sign_k(msg, pv_key, k, canonical=False)[source]

Signs a message hash with provided random

Parameters
sign_rfc6979(msg, pv_key, hasher, canonical=False)[source]

Signs a message hash according to RFC6979

Parameters
  • msg (bytes) – the message hash to sign

  • pv_key (ecpy.keys.ECPrivateKey) – key to use for signing

  • hasher (hashlib) – hasher conform to hashlib interface

verify(msg, sig, pu_key)[source]

Verifies a message signature.

Parameters

EDDSA module

class ecpy.eddsa.EDDSA(hasher, hash_len=None, fmt='EDDSA')[source]

Bases: object

EDDSA signer implemenation according to:

Parameters
  • hasher (hashlib) – callable constructor returning an object with update(), digest() interface. Example: hashlib.sha256, hashlib.sha512…

  • fmt (str) – in/out signature format. See ecpy.formatters.

static get_public_key(pv_key, hasher=<built-in function openssl_sha512>, hash_len=None)[source]

Returns the public key corresponding to this private key

This method compute the public key according to draft-irtf-cfrg-eddsa-05.

The hash parameter shall be the same as the one used for signing and verifying.

Parameters
  • hasher (hashlib) – callable constructor returning an object with update(), digest() interface. Example: hashlib.sha256, hashlib.sha512…

  • pv_key (ecpy.keys.ECPrivateKey) – key to use for signing

Returns

public key

Return type

ECPublicKey

sign(msg, pv_key)[source]

Signs a message.

Parameters
verify(msg, sig, pu_key)[source]

Verifies a message signature.

Parameters

ECSchnorr module

class ecpy.ecschnorr.ECSchnorr(hasher, option='ISO', fmt='DER')[source]

Bases: object

ECSchnorr signer implementation according to:

In order to select the specification to be conform to, choose the corresponding string option: “BSI”, “ISO”, “ISOx”, “LIBSECP”, “Z”

Signature:

  • “BSI”: compute r,s according to to BSI :
    1. k = RNG(1:n-1)

    2. Q = [k]G

    3. r = H(M ||Qx) If r = 0 mod n, goto 1.

    4. s = k - r.d mod n If s = 0 goto 1.

    5. Output (r, s)

  • “ISO”: compute r,s according to ISO :
    1. k = RNG(1:n-1)

    2. Q = [k]G If r = 0 mod n, goto 1.

    3. r = H(Qx||Qy||M).

    4. s = (k + r.d) mod n If s = 0 goto 1.

    5. Output (r, s)

  • “ISOx”: compute r,s according to optimized ISO variant:
    1. k = RNG(1:n-1)

    2. Q = [k]G If r = 0 mod n, goto 1.

    3. r = H(Qx||Qy||M).

    4. s = (k + r.d) mod n If s = 0 goto 1.

    5. Output (r, s)

  • “LIBSECP”: compute r,s according to bitcoin lib:
    1. k = RNG(1:n-1)

    2. Q = [k]G if Qy is odd, negate k and goto 2

    3. r = Qx % n

    4. h = H(r || m). if h == 0 or h >= order goto 1

    5. s = k - h.d.

    6. Output (r, s)

  • “Z”: compute r,s according to zilliqa lib:
    1. Generate a random k from [1, …, order-1]

    2. Compute the commitment Q = kG, where G is the base point

    3. Compute the challenge r = H(Q, kpub, m) [CME: mod n according to pdf/code, Q and kpub compressed “02|03 x” according to code)

    4. If r = 0 mod(order), goto 1

    5. Compute s = k - r*kpriv mod(order)

    6. If s = 0 goto 1.

    7. Output (r, s)

Verification

  • “BSI”: verify r,s according to to BSI :
    1. Verify that r in {0, … , 2**t - 1} and s in {1, 2, … , n - 1}. If the check fails, output False and terminate.

    2. Q = [s]G + [r]W If Q = 0, output Error and terminate.

    3. v = H(M||Qx)

    4. Output True if v = r, and False otherwise.

  • “ISO”: verify r,s according to ISO :
    1. check…

    2. Q = [s]G - [r]W If Q = 0, output Error and terminate.

    3. v = H(Qx||Qy||M).

    4. Output True if v = r, and False otherwise.

  • “ISOx”: verify r,s according to optimized ISO variant:
    1. check…

    2. Q = [s]G - [r]W If Q = 0, output Error and terminate.

    3. v = H(Qx||M).

    4. Output True if v = r, and False otherwise.

  • “LIBSECP”:
    1. Signature is invalid if s >= order. Signature is invalid if r >= p.

    2. h = H(r || m). Signature is invalid if h == 0 or h >= order.

    3. R = [h]Q + [s]G. Signature is invalid if R is infinity or R’s y coordinate is odd.

    4. Signature is valid if the serialization of R’s x coordinate equals r.

  • “Z”:
    1. Check if r,s is in [1, …, order-1]

    2. Compute Q = sG + r*kpub

    3. If Q = O (the neutral point), return 0;

    4. r’ = H(Q, kpub, m) [CME: mod n according to pdf/code, according to code), Q and kpub compressed “02|03 x”]

    5. return r’ == r

Default is “ISO”

Parameters
  • hasher (hashlib) – callable constructor returning an object with update(), digest() interface. Example: hashlib.sha256, hashlib.sha512…

  • option (str) – one of “BSI”,”ISO”,”ISOx”,”LIBSECP”

  • fmt (str) – in/out signature format. See ecpy.formatters

sign(msg, pv_key)[source]

Signs a message hash.

Parameters
sign_k(msg, pv_key, k)[source]

Signs a message hash with provided random

Parameters
verify(msg, sig, pu_key)[source]

Verifies a message signature.

Parameters

Borromean module

class ecpy.borromean.Borromean(fmt='BTUPLE')[source]

Bases: object

Borromean Ring signer implementation according to:

https://github.com/Blockstream/borromean_paper/blob/master/borromean_draft_0.01_9ade1e49.pdf

https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/rangeproof/borromean_impl.h

ElementsProject implementation has some tweaks compared to PDF. This implementation is ElementsProject compliant.

For now, only secp256k1+sha256 is supported. This constraint will be release soon.

Parameters

fmt (str) – in/out signature format. See ecpy.formatters. IGNORED.

sign(msg, rings, pv_keys, pv_keys_index)[source]

Signs a message hash.

The public rings argument is a tuple of public key array. In other words each element of the ring tuple is an array containing the public keys list of that ring

A Private key must be given for each provided ring. For each private key, the corresponding public key is specified by its index in the ring.

Exemple:

let r1 be the first ring with 2 keys: pu11, pu12 let 21 be the second ring with 3 keys: pu21,pu22,pu23 let say we want to produce a signature with sec12 and sec21 sign should be called as:

borromean.sign(m,
              ([pu11,pu12],[pu21,pu22,pu23]),
              [sec12, sec21], [1,0])

The return value is a tuple (e0, [s0,s1….]). Each value is encoded as binary (bytes).

Parameters
Returns

signature

Return type

(e0, [s0,s1….])

verify(msg, sig, rings)[source]

Verifies a message signature.

Parameters
  • msg (bytes) – the message hash to verify the signature

  • sig (bytes) – signature to verify

  • rings (key.ECPublicKey) – key to use for verifying

Returns

True if signature is verified, False else

Return type

boolean

ecrand module

ecpy.ecrand.rnd(q)[source]

Returns a random number less than q, with the same bits length than q

Parameters

q (int) – field/modulo

Returns

random

Return type

int

ecpy.ecrand.rnd_rfc6979(hashmsg, secret, q, hasher, V=None)[source]

Generates a deterministic value according to RF6979.

See https://tools.ietf.org/html/rfc6979#section-3.2

if V == None, this is the first try, so compute the initial value for V. Else it means the previous value has been rejected by the caller, so generate the next one!

Warning: the hashmsg parameter is the message hash, not the message itself. In other words, hashmsg is equal to h1 in the rfc6979, section-3.2, step a.

Parameters
  • hasher (hashlib) – hasher

  • hashmsg (bytes) – message hash

  • secret (int) – secret

  • q (int) – field/modulo

  • V – previous value for continuation

The function returns a couple (k,V) with k the expected value and V is the continuation value to pass to next cal if k is rejected.

Returns

(k,V)

Return type

tuple

formatters module

ecpy.formatters.decode_sig(sig, fmt='DER')[source]

encore signature according format

Parameters
  • rs (bytes,ints,tuple) – r,s value

  • fmt (str) – ‘DER’|’BTUPLE’|’ITUPLES’|’RAW’|’EDDSA’

Returns

(r,s)

Return type

ints

ecpy.formatters.encode_sig(r, s, fmt='DER', size=0)[source]

encore signature according format

Parameters
  • r (int) – r value

  • s (int) – s value

  • fmt (str) – ‘DER’|’BTUPLE’|’ITUPLE’|’RAW’|’EDDSA

Returns

TLV for DER encoding

Return type

bytes

Returns

(r,s) for BTUPLE encoding

Return type

bytes

Returns

(r,s) for ITUPLE encoding

Return type

ints

Returns

r|s for RAW encoding

Return type

bytes