Skip to content

Migration Guides

This page shows how to migrate to ECUtils from other Python ECC libraries, or how to use them together. Every example is a complete, runnable workflow.

Migrating from ecdsa

The ecdsa library is one of the most popular pure-Python ECC packages. Below are side-by-side comparisons showing how each task is done in ecdsa vs. ECUtils.

Key Generation

from ecdsa import SECP256k1, SigningKey

sk = SigningKey.generate(curve=SECP256k1)
vk = sk.get_verifying_key()

print(vk.to_string("compressed").hex())
import secrets
from ecutils import get_curve, get_generator

curve = get_curve("secp256k1")
G = get_generator("secp256k1")

private_key = secrets.randbelow(curve.n - 1) + 1
public_key = private_key * G

print(public_key.compress_sec1().hex())

Signing a Message

import hashlib
from ecdsa import SECP256k1, SigningKey

sk = SigningKey.generate(curve=SECP256k1)
message = b"Hello, world!"

# ecdsa hashes internally by default (sha1), or you pass hashfunc
signature = sk.sign(message, hashfunc=hashlib.sha256)
print(signature.hex())
import secrets
from ecutils import DigitalSignature, get_curve

curve = get_curve("secp256k1")
private_key = secrets.randbelow(curve.n - 1) + 1

ds = DigitalSignature(private_key, curve_name="secp256k1")
r, s = ds.sign_message(b"Hello, world!")  # SHA-256 applied internally

print(f"r = {r}")
print(f"s = {s}")

Verifying a Signature

import hashlib
from ecdsa import SECP256k1, SigningKey, BadSignatureError

sk = SigningKey.generate(curve=SECP256k1)
vk = sk.get_verifying_key()
message = b"Hello, world!"

signature = sk.sign(message, hashfunc=hashlib.sha256)

try:
    vk.verify(signature, message, hashfunc=hashlib.sha256)
    print("Valid")
except BadSignatureError:
    print("Invalid")
import secrets
from ecutils import DigitalSignature, get_curve

curve = get_curve("secp256k1")
private_key = secrets.randbelow(curve.n - 1) + 1

ds = DigitalSignature(private_key, curve_name="secp256k1")
message = b"Hello, world!"

r, s = ds.sign_message(message)
is_valid = ds.verify_message(ds.public_key, message, r, s)

print("Valid" if is_valid else "Invalid")  # Valid

Importing an ecdsa Public Key into ECUtils

If you have an existing system using ecdsa and want to verify or operate on its keys in ECUtils:

from ecdsa import SECP256k1, SigningKey
from ecutils import Point, get_curve

# --- Generate key in ecdsa ---
sk = SigningKey.generate(curve=SECP256k1)
vk = sk.get_verifying_key()

# Export as SEC 1 compressed bytes
ecdsa_pub_bytes = vk.to_string("compressed")

# --- Import into ecutils ---
curve = get_curve("secp256k1")
pub = Point.from_sec1(ecdsa_pub_bytes, curve)

print(pub)                  # Point(x=..., y=...)
print(pub.is_on_curve())    # True

# Now you can do EC arithmetic
doubled = pub + pub
scalar_mul = 42 * pub
print(doubled)              # Point(x=..., y=...)
print(scalar_mul)           # Point(x=..., y=...)

Exporting an ECUtils Public Key to ecdsa

If you generated a key in ECUtils and need to use it with ecdsa:

import secrets
from ecutils import get_curve, get_generator
from ecdsa import SECP256k1, VerifyingKey

# --- Generate key in ecutils ---
curve = get_curve("secp256k1")
G = get_generator("secp256k1")
private_key = secrets.randbelow(curve.n - 1) + 1
pub = private_key * G

# Export as SEC 1 compressed bytes
sec1_bytes = pub.compress_sec1()

# --- Import into ecdsa ---
vk = VerifyingKey.from_string(sec1_bytes, curve=SECP256k1)

print(vk.to_string("compressed").hex())

Cross-Verification: Sign with ecutils, Verify with ecdsa

The ultimate interoperability test — sign a message with ECUtils and verify the signature using ecdsa, passing through DER encoding:

import hashlib
from ecdsa import SECP256k1, SigningKey, BadSignatureError
from ecdsa.util import sigencode_der, sigdecode_der
from ecutils import DigitalSignature, get_curve

# --- Generate key with ecdsa ---
sk = SigningKey.generate(curve=SECP256k1)
vk = sk.get_verifying_key()
private_key = int(sk.to_string().hex(), 16)

# --- Sign with ecutils ---
curve = get_curve("secp256k1")
ds = DigitalSignature(private_key, curve_name="secp256k1")
message = b"Hello, world!"

r, s = ds.sign_message(message)

# Convert (r, s) to DER using ecdsa's utility
der_signature = sigencode_der(r, s, order=curve.n)
print(f"DER: {der_signature.hex()}")

# --- Verify with ecdsa ---
try:
    vk.verify(der_signature, message, hashfunc=hashlib.sha256, sigdecode=sigdecode_der)
    print("ecdsa verified the ecutils signature: VALID")
except BadSignatureError:
    print("INVALID")

Cross-Verification: Sign with ecdsa, Verify with ecutils

The reverse — sign with ecdsa and verify with ECUtils:

import hashlib
from ecdsa import SECP256k1, SigningKey
from ecdsa.util import sigencode_string
from ecutils import DigitalSignature, Point, get_curve

# --- Generate key with ecdsa ---
sk = SigningKey.generate(curve=SECP256k1)
vk = sk.get_verifying_key()
private_key = int(sk.to_string().hex(), 16)
message = b"Hello, world!"

# --- Sign with ecdsa (raw r||s format) ---
signature = sk.sign(message, hashfunc=hashlib.sha256, sigencode=sigencode_string)

# Extract r and s from raw bytes (each is 32 bytes for secp256k1)
byte_len = 32
r = int.from_bytes(signature[:byte_len], "big")
s = int.from_bytes(signature[byte_len:], "big")

# --- Verify with ecutils ---
curve = get_curve("secp256k1")
vk_bytes = vk.to_string("compressed")
pub = Point.from_sec1(vk_bytes, curve)

ds = DigitalSignature(private_key, curve_name="secp256k1")
is_valid = ds.verify_message(pub, message, r, s)

print(f"ecutils verified the ecdsa signature: {'VALID' if is_valid else 'INVALID'}")

Curve Name Mapping

ecdsa ecutils
SECP256k1 "secp256k1"
NIST192p "secp192r1"
NIST224p "secp224r1"
NIST256p "secp256r1"
NIST384p "secp384r1"
NIST521p "secp521r1"

Migrating from cryptography

The cryptography library is the most widely used Python crypto package, backed by OpenSSL. It uses a very different API style (builder pattern, serialization formats).

Key Generation

from cryptography.hazmat.primitives.asymmetric import ec

private_key = ec.generate_private_key(ec.SECP256K1())
public_key = private_key.public_key()

print(public_key.key_size)  # 256
import secrets
from ecutils import get_curve, get_generator

curve = get_curve("secp256k1")
G = get_generator("secp256k1")

private_key = secrets.randbelow(curve.n - 1) + 1
public_key = private_key * G

print(public_key)                # Point(x=..., y=...)
print(public_key.is_on_curve())  # True

Signing a Message

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec, utils

private_key = ec.generate_private_key(ec.SECP256K1())
message = b"Hello, world!"

signature = private_key.sign(message, ec.ECDSA(hashes.SHA256()))
# signature is DER-encoded (r, s)
r, s = utils.decode_dss_signature(signature)

print(f"r = {r}")
print(f"s = {s}")
import secrets
from ecutils import DigitalSignature, get_curve

curve = get_curve("secp256k1")
private_key = secrets.randbelow(curve.n - 1) + 1

ds = DigitalSignature(private_key, curve_name="secp256k1")
r, s = ds.sign_message(b"Hello, world!")
# r, s are plain integers — no DER encoding

print(f"r = {r}")
print(f"s = {s}")

Verifying a Signature

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec, utils
from cryptography.exceptions import InvalidSignature

private_key = ec.generate_private_key(ec.SECP256K1())
public_key = private_key.public_key()
message = b"Hello, world!"

signature = private_key.sign(message, ec.ECDSA(hashes.SHA256()))
r, s = utils.decode_dss_signature(signature)

der_sig = utils.encode_dss_signature(r, s)
try:
    public_key.verify(der_sig, message, ec.ECDSA(hashes.SHA256()))
    print("Valid")
except InvalidSignature:
    print("Invalid")
import secrets
from ecutils import DigitalSignature, get_curve

curve = get_curve("secp256k1")
private_key = secrets.randbelow(curve.n - 1) + 1

ds = DigitalSignature(private_key, curve_name="secp256k1")
message = b"Hello, world!"

r, s = ds.sign_message(message)
is_valid = ds.verify_message(ds.public_key, message, r, s)

print("Valid" if is_valid else "Invalid")  # Valid

Importing a cryptography Public Key into ECUtils

from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import (
    Encoding, PublicFormat,
)
from ecutils import Point, get_curve

# --- Generate key in cryptography ---
private_key = ec.generate_private_key(ec.SECP256K1())
pub_bytes = private_key.public_key().public_bytes(
    Encoding.X962, PublicFormat.CompressedPoint
)

# --- Import into ecutils ---
curve = get_curve("secp256k1")
pub = Point.from_sec1(pub_bytes, curve)

print(pub)                  # Point(x=..., y=...)
print(pub.is_on_curve())    # True

# EC arithmetic works immediately
doubled = pub + pub
scalar_mul = 42 * pub
print(doubled)              # Point(x=..., y=...)
print(scalar_mul)           # Point(x=..., y=...)

Exporting an ECUtils Public Key to cryptography

import secrets
from ecutils import get_curve, get_generator
from cryptography.hazmat.primitives.asymmetric import ec

# --- Generate key in ecutils ---
curve = get_curve("secp256k1")
G = get_generator("secp256k1")
private_key = secrets.randbelow(curve.n - 1) + 1
pub = private_key * G

# Export as SEC 1 compressed bytes
sec1_bytes = pub.compress_sec1()

# --- Import into cryptography ---
crypto_pub = ec.EllipticCurvePublicKey.from_encoded_point(
    ec.SECP256K1(), sec1_bytes
)

print(crypto_pub.key_size)  # 256

Diffie-Hellman: cryptography ↔ ecutils

A complete ECDH key exchange where one party uses cryptography and the other uses ECUtils:

import secrets
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import (
    Encoding, PublicFormat,
)
from ecutils import DiffieHellman, Point, get_curve

curve = get_curve("secp256k1")

# --- Alice (cryptography) ---
alice_sk = ec.generate_private_key(ec.SECP256K1())
alice_pub_bytes = alice_sk.public_key().public_bytes(
    Encoding.X962, PublicFormat.CompressedPoint
)

# --- Bob (ecutils) ---
bob = DiffieHellman(
    private_key=secrets.randbelow(curve.n - 1) + 1,
    curve_name="secp256k1",
)
bob_pub_bytes = bob.public_key.compress_sec1()

# --- Bob computes shared secret (ecutils) ---
alice_pub = Point.from_sec1(alice_pub_bytes, curve)
bob_shared = bob.compute_shared_secret(alice_pub)

# --- Alice computes shared secret (cryptography) ---
bob_crypto_pub = ec.EllipticCurvePublicKey.from_encoded_point(
    ec.SECP256K1(), bob_pub_bytes
)
alice_shared = alice_sk.exchange(ec.ECDH(), bob_crypto_pub)

# Both arrive at the same x-coordinate
bob_shared_bytes = bob_shared.x.to_bytes(32, "big")
assert alice_shared == bob_shared_bytes

print("Shared secrets match!")

Cross-Verification: Sign with ecutils, Verify with cryptography

Sign a message with ECUtils and verify using cryptography via DER encoding:

import secrets
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec, utils
from cryptography.hazmat.primitives.serialization import (
    Encoding, PublicFormat,
)
from cryptography.exceptions import InvalidSignature
from ecutils import DigitalSignature, Point, get_curve, get_generator

# --- Generate key in ecutils ---
curve = get_curve("secp256k1")
G = get_generator("secp256k1")
private_key = secrets.randbelow(curve.n - 1) + 1
pub = private_key * G

# --- Sign with ecutils ---
ds = DigitalSignature(private_key, curve_name="secp256k1")
message = b"Hello, world!"
r, s = ds.sign_message(message)

# Convert (r, s) to DER
der_signature = utils.encode_dss_signature(r, s)
print(f"DER: {der_signature.hex()}")

# --- Verify with cryptography ---
sec1_bytes = pub.compress_sec1()
crypto_pub = ec.EllipticCurvePublicKey.from_encoded_point(
    ec.SECP256K1(), sec1_bytes
)

try:
    crypto_pub.verify(der_signature, message, ec.ECDSA(hashes.SHA256()))
    print("cryptography verified the ecutils signature: VALID")
except InvalidSignature:
    print("INVALID")

Cross-Verification: Sign with cryptography, Verify with ecutils

The reverse — sign with cryptography and verify with ECUtils:

import secrets
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec, utils
from cryptography.hazmat.primitives.serialization import (
    Encoding, PublicFormat,
)
from ecutils import DigitalSignature, Point, get_curve

# --- Generate key in cryptography ---
crypto_sk = ec.generate_private_key(ec.SECP256K1())
crypto_pub = crypto_sk.public_key()
message = b"Hello, world!"

# --- Sign with cryptography ---
der_signature = crypto_sk.sign(message, ec.ECDSA(hashes.SHA256()))

# Extract r, s from DER
r, s = utils.decode_dss_signature(der_signature)

# --- Verify with ecutils ---
pub_bytes = crypto_pub.public_bytes(
    Encoding.X962, PublicFormat.CompressedPoint
)
curve = get_curve("secp256k1")
pub = Point.from_sec1(pub_bytes, curve)

# Get private key number for DigitalSignature
private_numbers = crypto_sk.private_numbers()
ds = DigitalSignature(private_numbers.private_value, curve_name="secp256k1")

is_valid = ds.verify_message(pub, message, r, s)
print(f"ecutils verified the cryptography signature: {'VALID' if is_valid else 'INVALID'}")

Curve Name Mapping

cryptography ecutils
ec.SECP256K1() "secp256k1"
ec.SECP192R1() "secp192r1"
ec.SECP224R1() "secp224r1"
ec.SECP256R1() "secp256r1"
ec.SECP384R1() "secp384r1"
ec.SECP521R1() "secp521r1"

Why Migrate?

Aspect ecdsa cryptography ecutils
Dependencies Pure Python OpenSSL (C) Pure Python, zero deps
API style Object-oriented Builder pattern Operator overloading (+, *)
EC arithmetic Not exposed Not exposed Full access (P + Q, k * P)
Educational use Limited Not designed for Built for it
Point compression Yes (SEC 1) Yes (SEC 1) Yes (SEC 1 + tuple)
Koblitz encoding No No Yes
Massey-Omura No No Yes
Coordinate systems Affine only Hidden (OpenSSL) Affine + Jacobian (selectable)
Production ready Yes Yes Educational / prototyping

ECUtils is not a replacement for cryptography in production — it is designed for learning, prototyping, and research where transparent access to the math is more important than hardened security.