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

0.8.2

Authors

Cedric Mesnil, <cedric.mesnil@ubinity.com>

License

Apache 2.0


Install

ECPy is originally coded for Python 3, but run under python 2.7 (and maybe 2.6) by using future. If you run Python 2, please install the future into the present:

pip install future

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 a*x²+y2=1+d*x²*y²

See pyec.Curve. get_curve_names

ECPy supports the following

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)

Bases: Exception

class ecpy.curves.Curve(parameters)

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²

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)

Returns the sum of P and Q

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

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)

decode/decompress a point according to its curve

encode_point(P)

encode/compress a point according to its curve

static get_curve(name)

Return a Curve object according to its name

Parameters

name (str) – curve name to retrieve

Returns

Curve object

Return type

Curve

static get_curve_names()

Returns all known curve names

Returns

list of names as str

Return type

tuple

is_on_curve(P)

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)

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.

sub_point(P, Q)

Returns the difference of P and Q

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

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.

class ecpy.curves.MontgomeryCurve(domain)

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)

Decodes a point P according to RFC7748.

Parameters
  • eP (bytes) – encoded point

  • curve (Curve) – curve on witch point is

Returns

Point : decoded point

encode_point(P)

Encodes a point P according to RFC7748.

Parameters

P – point to encode

Returns

bytes : encoded point

is_on_curve(P)

See Curve.is_on_curve()

mul_point(k, P)

See Curve.add_point()

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

Bases: object

Immutable Elliptic Curve Point.

A Point support the following operator:

  • + : 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

x

Affine x coordinate

Type

int

y

Affine y coordinate

Type

int

curve

Curve on which the point is define

Type

Curve

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

class ecpy.curves.TwistedEdwardCurve(domain)

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

add_point(P, Q)

See Curve.add_point()

decode_point(eP)

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

Parameters
  • eP (bytes) – encoded point

  • curve (Curve) – curve on witch point is

Returns

Point : decoded point

encode_point(P)

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

Parameters

P – point to encode

Returns

bytes : encoded point

is_on_curve(P)

See Curve.is_on_curve()

mul_point(k, P)

See Curve.add_point()

x_recover(y, sign=0)

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)

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

add_point(P, Q)

See Curve.add_point()

decode_point(eP)

Decodes a point P according to P1363-2000.

Parameters
  • eP (bytes) – encoded point

  • curve (Curve) – curve on witch point is

Returns

Point : decoded point

encode_point(P, compressed=False)

Encodes a point P according to P1363-2000.

Parameters

P – point to encode

Returns

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

is_on_curve(P)

See Curve.is_on_curve()

mul_point(k, P)

See Curve.mul_point()

y_recover(x, sign=0)
ecpy.curves.decode_scalar_25519(k)

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

Parameters

k (bytes) – scalar to decode

Returns

decoded scalar

Return type

int

ecpy.curves.encode_scalar_25519(k)

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

Parameters

k (int) – scalar to encode

Returns

encoded scalar

Return type

bytes

keys module

class ecpy.keys.ECPrivateKey(d, curve)

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()

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)

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')

Bases: object

ECDSA signer.

Parameters

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

sign(msg, pv_key, canonical=False)

Signs a message hash.

Parameters
sign_k(msg, pv_key, k, canonical=False)

Signs a message hash with provided random

Parameters
sign_rfc6979(msg, pv_key, hasher, canonical=False)

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)

Verifies a message signature.

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

  • sig (bytes) – signature to verify

  • pu_key (ecpy.keys.ECPublicKey) – key to use for verifying

EDDSA module

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

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)

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)

Signs a message.

Parameters
verify(msg, sig, pu_key)

Verifies a message signature.

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

  • sig (bytes) – signature to verify

  • pu_key (ecpy.keys.ECPublicKey) – key to use for verifying

ECSchnorr module

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

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)

Signs a message hash.

Parameters
sign_k(msg, pv_key, k)

Signs a message hash with provided random

Parameters
verify(msg, sig, pu_key)

Verifies a message signature.

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

  • sig (bytes) – signature to verify

  • pu_key (ecpy.keys.ECPublicKey) – key to use for verifying

Borromean module

class ecpy.borromean.Borromean(fmt='BTUPLE')

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)

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)

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)

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)

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')

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)

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