CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-ecdsa

ECDSA cryptographic signature library (pure python)

Pending
Overview
Eval results
Files

ecdh.mddocs/

ECDH Key Exchange

Elliptic Curve Diffie-Hellman (ECDH) key agreement protocol implementation for secure shared secret generation between parties. ECDH allows two parties to establish a shared secret over an insecure channel by exchanging public keys and combining them with their private keys.

Capabilities

ECDH Class

The ECDH class provides a complete implementation of the elliptic curve Diffie-Hellman key agreement protocol.

Initialization and Curve Management

class ECDH:
    def __init__(self, curve=None, private_key=None, public_key=None):
        """
        Initialize ECDH instance.

        Parameters:
        - curve: Curve object, elliptic curve to use
        - private_key: SigningKey object, existing private key
        - public_key: VerifyingKey object, existing public key
        """

    def set_curve(self, key_curve):
        """
        Set the elliptic curve for ECDH operations.

        Parameters:
        - key_curve: Curve object, curve to use for operations
        """

Private Key Management

def generate_private_key(self):
        """
        Generate a new random private key and return corresponding public key.

        Returns:
        VerifyingKey object, the generated public key

        Raises:
        NoCurveError: if curve is not set
        """

    def load_private_key(self, private_key):
        """
        Load private key from SigningKey object.

        Parameters:
        - private_key: SigningKey object

        Returns:
        VerifyingKey object, corresponding public key

        Raises:
        InvalidCurveError: if key curve doesn't match set curve
        """

    def load_private_key_bytes(self, private_key):
        """
        Load private key from raw byte string.

        Parameters:
        - private_key: bytes, raw private key

        Returns:
        VerifyingKey object, corresponding public key

        Raises:
        NoCurveError: if curve is not set
        """

    def load_private_key_der(self, private_key_der):
        """
        Load private key from DER-encoded bytes.

        Parameters:
        - private_key_der: bytes, DER-encoded private key

        Returns:
        VerifyingKey object, corresponding public key
        """

    def load_private_key_pem(self, private_key_pem):
        """
        Load private key from PEM-encoded string or bytes.

        Parameters:
        - private_key_pem: str or bytes, PEM-encoded private key

        Returns:
        VerifyingKey object, corresponding public key
        """

    def get_public_key(self):
        """
        Get the current public key.

        Returns:
        VerifyingKey object, current public key

        Raises:
        NoKeyError: if no private key is loaded
        """

Remote Public Key Management

def load_received_public_key(self, public_key):
        """
        Load the other party's public key from VerifyingKey object.

        Parameters:
        - public_key: VerifyingKey object

        Raises:
        InvalidCurveError: if key curve doesn't match set curve
        """

    def load_received_public_key_bytes(self, public_key_str, valid_encodings=None):
        """
        Load the other party's public key from raw bytes.

        Parameters:
        - public_key_str: bytes, raw public key
        - valid_encodings: list of str, acceptable point encodings or None

        Raises:
        NoCurveError: if curve is not set
        """

    def load_received_public_key_der(self, public_key_der):
        """
        Load the other party's public key from DER-encoded bytes.

        Parameters:
        - public_key_der: bytes, DER-encoded public key
        """

    def load_received_public_key_pem(self, public_key_pem):
        """
        Load the other party's public key from PEM-encoded string or bytes.

        Parameters:
        - public_key_pem: str or bytes, PEM-encoded public key
        """

Shared Secret Generation

def generate_sharedsecret(self):
        """
        Generate shared secret as integer.

        Returns:
        int, shared secret value

        Raises:
        NoKeyError: if private key or received public key not loaded
        InvalidSharedSecretError: if computed secret is invalid (point at infinity)
        """

    def generate_sharedsecret_bytes(self):
        """
        Generate shared secret as byte string.

        Returns:
        bytes, shared secret as raw bytes

        Raises:
        NoKeyError: if private key or received public key not loaded
        InvalidSharedSecretError: if computed secret is invalid (point at infinity)
        """

Exception Classes

class NoKeyError(Exception):
    """Raised when a required key is not set but needed for operation."""

class NoCurveError(Exception):
    """Raised when curve is not set but needed for operation."""

class InvalidCurveError(Exception):
    """Raised when public and private keys use different curves."""

class InvalidSharedSecretError(Exception):
    """Raised when shared secret computation results in point at infinity."""

Usage Examples

Basic ECDH Key Exchange

from ecdsa import ECDH, NIST256p

# Party A setup
alice = ECDH(curve=NIST256p)
alice_public_key = alice.generate_private_key()

# Party B setup
bob = ECDH(curve=NIST256p)
bob_public_key = bob.generate_private_key()

# Exchange public keys (over insecure channel)
alice.load_received_public_key(bob_public_key)
bob.load_received_public_key(alice_public_key)

# Both parties compute the same shared secret
alice_secret = alice.generate_sharedsecret_bytes()
bob_secret = bob.generate_sharedsecret_bytes()

assert alice_secret == bob_secret
print(f"Shared secret established: {alice_secret.hex()}")

ECDH with Existing Keys

from ecdsa import ECDH, SigningKey, SECP256k1

# Use existing private keys
alice_private = SigningKey.generate(curve=SECP256k1)
bob_private = SigningKey.generate(curve=SECP256k1)

# Setup ECDH instances with existing keys
alice_ecdh = ECDH(curve=SECP256k1)
alice_public = alice_ecdh.load_private_key(alice_private)

bob_ecdh = ECDH(curve=SECP256k1)
bob_public = bob_ecdh.load_private_key(bob_private)

# Exchange public keys
alice_ecdh.load_received_public_key(bob_public)
bob_ecdh.load_received_public_key(alice_public)

# Generate shared secrets
alice_secret = alice_ecdh.generate_sharedsecret()
bob_secret = bob_ecdh.generate_sharedsecret()

assert alice_secret == bob_secret

ECDH with Key Serialization

from ecdsa import ECDH, NIST384p

# Alice generates key pair and exports public key
alice = ECDH(curve=NIST384p)
alice_public_key = alice.generate_private_key()
alice_public_pem = alice_public_key.to_pem()

# Bob generates key pair and exports public key
bob = ECDH(curve=NIST384p)
bob_public_key = bob.generate_private_key()
bob_public_pem = bob_public_key.to_pem()

# Exchange serialized public keys (e.g., over network)
# Alice loads Bob's public key from PEM
alice.load_received_public_key_pem(bob_public_pem)

# Bob loads Alice's public key from PEM
bob.load_received_public_key_pem(alice_public_pem)

# Both parties generate the same shared secret
alice_secret = alice.generate_sharedsecret_bytes()
bob_secret = bob.generate_sharedsecret_bytes()

assert alice_secret == bob_secret
print(f"ECDH key exchange completed successfully")

Error Handling

from ecdsa import ECDH, NIST256p, NoKeyError, NoCurveError, InvalidCurveError

try:
    # Attempt to generate key without setting curve
    ecdh = ECDH()
    public_key = ecdh.generate_private_key()  # Raises NoCurveError
except NoCurveError:
    print("Must set curve before generating keys")

try:
    # Attempt to generate shared secret without loading received key
    ecdh = ECDH(curve=NIST256p)
    ecdh.generate_private_key()
    secret = ecdh.generate_sharedsecret()  # Raises NoKeyError
except NoKeyError:
    print("Must load received public key before generating shared secret")

try:
    # Attempt to use keys from different curves
    from ecdsa import SECP256k1
    alice = ECDH(curve=NIST256p)
    alice_public = alice.generate_private_key()
    
    bob = ECDH(curve=SECP256k1)
    bob_public = bob.generate_private_key()
    
    alice.load_received_public_key(bob_public)  # Raises InvalidCurveError
except InvalidCurveError:
    print("Both parties must use the same curve")

Different Curves Support

from ecdsa import ECDH, SECP256k1, Ed25519, BRAINPOOLP384r1

# Bitcoin's secp256k1 curve
bitcoin_ecdh = ECDH(curve=SECP256k1)
bitcoin_public = bitcoin_ecdh.generate_private_key()

# Edwards curve (note: Ed25519 typically used for EdDSA, not ECDH)
# Standard ECDH is usually done with Weierstrass curves
edwards_ecdh = ECDH(curve=Ed25519)
edwards_public = edwards_ecdh.generate_private_key()

# Brainpool curve
brainpool_ecdh = ECDH(curve=BRAINPOOLP384r1)
brainpool_public = brainpool_ecdh.generate_private_key()

print(f"Generated keys for different curves:")
print(f"- Bitcoin secp256k1: {len(bitcoin_public.to_string())} bytes")
print(f"- Edwards Ed25519: {len(edwards_public.to_string())} bytes")
print(f"- Brainpool P384r1: {len(brainpool_public.to_string())} bytes")

Install with Tessl CLI

npx tessl i tessl/pypi-ecdsa

docs

curves.md

ecdh.md

eddsa.md

encoding.md

index.md

keys-signatures.md

mathematical-functions.md

tile.json