Python Library for Elliptic Curve Cryptography: key exchanges (Diffie-Hellman, Massey-Omura), ECDSA signatures, and Koblitz encoding. Suitable for crypto education and secure systems.
pip install ecutilsfrom ecutils import Point, get_curve, get_generator
curve = get_curve("secp256k1")
G = get_generator("secp256k1")
# Private key → public key
private_key = 0xDEADBEEFCAFE
public_key = private_key * G
print(public_key) # Point(x=..., y=...)
print(public_key.is_on_curve()) # TrueArithmetic operations on elliptic curves y² = x³ + ax + b (mod p) with two internal coordinate systems.
from ecutils import Point, CurveParams, CoordinateSystem
# Jacobian (default — faster)
curve = CurveParams(p=23, a=1, b=1, n=28, h=1)
# Affine (explicit)
curve = CurveParams(p=23, a=1, b=1, n=28, h=1, coord=CoordinateSystem.AFFINE)
P = Point(0, 1, curve)
Q = Point(6, 19, curve)
P + Q # addition
P - Q # subtraction
-P # negation (additive inverse)
5 * P # scalar multiplication
P * 5 # scalar multiplication (commutative)
P == Q # equalityCurves are validated automatically — singular curves (4a³ + 27b² ≡ 0 mod p) are rejected:
CurveParams(p=23, a=0, b=0, n=1) # ❌ ValueError: singular curvePoints are validated on construction:
Point(0, 1, curve) # ✅ valid
Point(0, 5, curve) # ❌ ValueError: not on the curve
Point(curve=curve) # ✅ point at infinity (identity)Point compression and decompression:
x, parity = P.compress() # (x, y_parity)
recovered = Point.decompress(x, parity, curve) # full point
assert recovered == Pfrom ecutils import get_curve, get_generator
curve = get_curve("secp256k1")
G = get_generator("secp256k1")Available curves: secp192k1, secp192r1, secp224k1, secp224r1, secp256k1, secp256r1, secp384r1, secp521r1.
Key exchange between two parties.
from ecutils import DiffieHellman
alice = DiffieHellman(private_key=0xA1, curve_name="secp256k1")
bob = DiffieHellman(private_key=0xB2, curve_name="secp256k1")
# Each party shares their public key
shared_alice = alice.compute_shared_secret(bob.public_key)
shared_bob = bob.compute_shared_secret(alice.public_key)
assert shared_alice == shared_bob # same shared secretThree-pass protocol — no prior public key exchange required.
from ecutils import MasseyOmura, Koblitz
# Encode message as a curve point
kob = Koblitz(curve_name="secp521r1")
M, j = kob.encode("secret message")
alice = MasseyOmura(private_key=0xA1, curve_name="secp521r1")
bob = MasseyOmura(private_key=0xB2, curve_name="secp521r1")
# Three passes
c1 = alice.encrypt(M) # Alice → Bob
c2 = bob.encrypt(c1) # Bob → Alice
c3 = alice.decrypt(c2) # Alice → Bob
plaintext = bob.decrypt(c3) # Bob recovers M
assert kob.decode(plaintext, j) == "secret message"from ecutils import DigitalSignature
signer = DigitalSignature(private_key=123456789, curve_name="secp256k1")
# Sign (SHA-256 hashing is done automatically)
r, s = signer.sign_message(b"hello")
# Verify
assert signer.verify_message(signer.public_key, b"hello", r, s)For manual hashing, use sign(message_hash) and verify(pub, message_hash, r, s) directly.
Encode text as curve points and decode back.
from ecutils import Koblitz
kob = Koblitz(curve_name="secp521r1")
point, j = kob.encode("Hello, world!")
text = kob.decode(point, j)
assert text == "Hello, world!"ecutils/
├── __init__.py # Public API
├── py.typed # PEP 561 — type checker support
├── core/
│ ├── curve.py # CurveParams, CoordinateSystem
│ ├── point.py # Point
│ └── arithmetic/
│ ├── affine.py # affine coordinate arithmetic
│ └── jacobian.py # Jacobian coordinate arithmetic
├── curves/
│ └── registry.py # pre-defined curves, get_curve(), get_generator()
├── protocols/
│ ├── diffie_hellman.py # DiffieHellman
│ └── massey_omura.py # MasseyOmura
├── algorithms/
│ ├── digital_signature.py # DigitalSignature (ECDSA)
│ └── koblitz.py # Koblitz
└── utils/
├── settings.py # global settings
└── math.py # quadratic residue, modular square root
Contributions are welcome! Please read our contributing guidelines to get started.
This project is licensed under the MIT License.