ECDSA cryptographic signature library (pure python)
—
Primary ECDSA/EdDSA functionality for creating and verifying digital signatures. This module provides the main interface for elliptic curve cryptographic operations including key generation, deterministic signing, and signature verification with support for multiple curves and hash functions.
The SigningKey class represents a private key for creating digital signatures using ECDSA or EdDSA algorithms.
class SigningKey:
@classmethod
def generate(cls, curve=NIST192p, entropy=None, hashfunc=sha1):
"""
Generate a new random signing key.
Parameters:
- curve: Curve object (default NIST192p)
- entropy: callable returning random bytes, or None for os.urandom
- hashfunc: hash function for signatures (default sha1)
Returns:
SigningKey object
"""
@classmethod
def from_secret_exponent(cls, secexp, curve=NIST192p, hashfunc=sha1):
"""
Create SigningKey from integer secret exponent.
Parameters:
- secexp: int, secret exponent (private key value)
- curve: Curve object (default NIST192p)
- hashfunc: hash function for signatures (default sha1)
Returns:
SigningKey object
"""
@classmethod
def from_string(cls, string, curve=NIST192p, hashfunc=sha1):
"""
Create SigningKey from raw byte string.
Parameters:
- string: bytes, raw private key bytes
- curve: Curve object (default NIST192p)
- hashfunc: hash function for signatures (default sha1)
Returns:
SigningKey object
"""
@classmethod
def from_pem(cls, string, hashfunc=sha1, valid_curve_encodings=None):
"""
Create SigningKey from PEM-encoded private key.
Parameters:
- string: str or bytes, PEM-encoded private key
- hashfunc: hash function for signatures (default sha1)
- valid_curve_encodings: list of acceptable curve encodings or None
Returns:
SigningKey object
"""
@classmethod
def from_der(cls, string, hashfunc=sha1, valid_curve_encodings=None):
"""
Create SigningKey from DER-encoded private key.
Parameters:
- string: bytes, DER-encoded private key
- hashfunc: hash function for signatures (default sha1)
- valid_curve_encodings: list of acceptable curve encodings or None
Returns:
SigningKey object
"""def sign(self, data, entropy=None, hashfunc=None, sigencode=sigencode_string, k=None, allow_truncate=True):
"""
Sign data using ECDSA/EdDSA.
Parameters:
- data: bytes, data to sign
- entropy: callable returning random bytes, or None for default
- hashfunc: hash function, or None for default
- sigencode: signature encoding function (default sigencode_string)
- k: int, specific k value for signature (for testing only)
- allow_truncate: bool, allow hash truncation for curve compatibility
Returns:
bytes, encoded signature
"""
def sign_deterministic(self, data, hashfunc=None, sigencode=sigencode_string, extra_entropy=b""):
"""
Sign data using RFC 6979 deterministic ECDSA.
Parameters:
- data: bytes, data to sign
- hashfunc: hash function, or None for default
- sigencode: signature encoding function (default sigencode_string)
- extra_entropy: bytes, additional entropy for deterministic generation
Returns:
bytes, encoded signature
"""
def sign_digest(self, digest, entropy=None, sigencode=sigencode_string, k=None, allow_truncate=False):
"""
Sign a pre-computed hash digest.
Parameters:
- digest: bytes, hash digest to sign
- entropy: callable returning random bytes, or None for default
- sigencode: signature encoding function (default sigencode_string)
- k: int, specific k value for signature (for testing only)
- allow_truncate: bool, allow hash truncation for curve compatibility
Returns:
bytes, encoded signature
"""
def sign_digest_deterministic(self, digest, hashfunc=None, sigencode=sigencode_string, extra_entropy=b"", allow_truncate=False):
"""
Sign a pre-computed hash digest using RFC 6979 deterministic ECDSA.
Parameters:
- digest: bytes, hash digest to sign
- hashfunc: hash function used to create digest (required for RFC 6979)
- sigencode: signature encoding function (default sigencode_string)
- extra_entropy: bytes, additional entropy for deterministic generation
- allow_truncate: bool, allow hash truncation for curve compatibility
Returns:
bytes, encoded signature
"""
def sign_number(self, number, entropy=None, k=None):
"""
Sign a number directly (low-level interface).
Parameters:
- number: int, number to sign
- entropy: callable returning random bytes, or None for default
- k: int, specific k value for signature (for testing only)
Returns:
tuple[int, int], (r, s) signature pair
"""def get_verifying_key(self):
"""
Get the corresponding public key.
Returns:
VerifyingKey object
"""
def to_string(self):
"""
Export private key as raw bytes.
Returns:
bytes, raw private key
"""
def to_pem(self, point_encoding="uncompressed", format="ssleay", curve_parameters_encoding=None):
"""
Export private key in PEM format.
Parameters:
- point_encoding: str, point encoding format ("uncompressed", "compressed", "hybrid")
- format: str, PEM format variant ("ssleay", "pkcs8")
- curve_parameters_encoding: str or None, curve parameter encoding
Returns:
bytes, PEM-encoded private key
"""
def to_der(self, point_encoding="uncompressed", format="ssleay", curve_parameters_encoding=None):
"""
Export private key in DER format.
Parameters:
- point_encoding: str, point encoding format ("uncompressed", "compressed", "hybrid")
- format: str, DER format variant ("ssleay", "pkcs8")
- curve_parameters_encoding: str or None, curve parameter encoding
Returns:
bytes, DER-encoded private key
"""
def to_ssh(self):
"""
Export private key in SSH format.
Returns:
bytes, SSH-encoded private key
"""curve: Curve # The elliptic curve object
default_hashfunc: callable # Default hash function
baselen: int # Length of raw key encoding in bytes
verifying_key: VerifyingKey # Associated public keyThe VerifyingKey class represents a public key for verifying digital signatures created with ECDSA or EdDSA algorithms.
class VerifyingKey:
@classmethod
def from_public_point(cls, point, curve=NIST192p, hashfunc=sha1, validate_point=True):
"""
Create VerifyingKey from elliptic curve point.
Parameters:
- point: Point object, public key point on curve
- curve: Curve object (default NIST192p)
- hashfunc: hash function for signatures (default sha1)
- validate_point: bool, validate point is on curve
Returns:
VerifyingKey object
"""
@classmethod
def from_string(cls, string, curve=NIST192p, hashfunc=sha1, validate_point=True, valid_encodings=None):
"""
Create VerifyingKey from raw byte string.
Parameters:
- string: bytes, raw public key bytes
- curve: Curve object (default NIST192p)
- hashfunc: hash function for signatures (default sha1)
- validate_point: bool, validate point is on curve
- valid_encodings: list of acceptable point encodings or None
Returns:
VerifyingKey object
"""
@classmethod
def from_pem(cls, string, hashfunc=sha1, valid_encodings=None, valid_curve_encodings=None):
"""
Create VerifyingKey from PEM-encoded public key.
Parameters:
- string: str or bytes, PEM-encoded public key
- hashfunc: hash function for signatures (default sha1)
- valid_encodings: list of acceptable point encodings or None
- valid_curve_encodings: list of acceptable curve encodings or None
Returns:
VerifyingKey object
"""
@classmethod
def from_der(cls, string, hashfunc=sha1, valid_encodings=None, valid_curve_encodings=None):
"""
Create VerifyingKey from DER-encoded public key.
Parameters:
- string: bytes, DER-encoded public key
- hashfunc: hash function for signatures (default sha1)
- valid_encodings: list of acceptable point encodings or None
- valid_curve_encodings: list of acceptable curve encodings or None
Returns:
VerifyingKey object
"""
@classmethod
def from_public_key_recovery(cls, signature, data, curve, hashfunc=sha1, sigdecode=sigdecode_string, allow_truncate=True):
"""
Recover public key(s) from signature and original data.
Parameters:
- signature: bytes, encoded signature
- data: bytes, original signed data
- curve: Curve object
- hashfunc: hash function used for signing (default sha1)
- sigdecode: signature decoding function (default sigdecode_string)
- allow_truncate: bool, allow hash truncation for curve compatibility
Returns:
list[VerifyingKey], possible public keys (usually 2 or 4 options)
"""
@classmethod
def from_public_key_recovery_with_digest(cls, signature, digest, curve, hashfunc=sha1, sigdecode=sigdecode_string, allow_truncate=False):
"""
Recover public key(s) from signature and pre-computed digest.
Parameters:
- signature: bytes, encoded signature
- digest: bytes, hash digest of original data
- curve: Curve object
- hashfunc: hash function used to create digest (default sha1)
- sigdecode: signature decoding function (default sigdecode_string)
- allow_truncate: bool, allow hash truncation for curve compatibility
Returns:
list[VerifyingKey], possible public keys (usually 2 or 4 options)
"""def verify(self, signature, data, hashfunc=None, sigdecode=sigdecode_string, allow_truncate=True):
"""
Verify signature against data.
Parameters:
- signature: bytes, encoded signature to verify
- data: bytes, original data that was signed
- hashfunc: hash function, or None for default
- sigdecode: signature decoding function (default sigdecode_string)
- allow_truncate: bool, allow hash truncation for curve compatibility
Returns:
bool, True if signature is valid
Raises:
BadSignatureError: if signature is invalid
"""
def verify_digest(self, signature, digest, sigdecode=sigdecode_string, allow_truncate=False):
"""
Verify signature against pre-computed hash digest.
Parameters:
- signature: bytes, encoded signature to verify
- digest: bytes, hash digest of original data
- sigdecode: signature decoding function (default sigdecode_string)
- allow_truncate: bool, allow hash truncation for curve compatibility
Returns:
bool, True if signature is valid
Raises:
BadSignatureError: if signature is invalid
"""def precompute(self, lazy=False):
"""
Precompute values for faster signature verification.
Parameters:
- lazy: bool, compute values lazily on first use
"""
def to_string(self, encoding="raw"):
"""
Export public key as raw bytes.
Parameters:
- encoding: str, point encoding format ("raw", "uncompressed", "compressed", "hybrid")
Returns:
bytes, encoded public key
"""
def to_pem(self, point_encoding="uncompressed", curve_parameters_encoding=None):
"""
Export public key in PEM format.
Parameters:
- point_encoding: str, point encoding format ("uncompressed", "compressed", "hybrid")
- curve_parameters_encoding: str or None, curve parameter encoding
Returns:
bytes, PEM-encoded public key
"""
def to_der(self, point_encoding="uncompressed", curve_parameters_encoding=None):
"""
Export public key in DER format.
Parameters:
- point_encoding: str, point encoding format ("uncompressed", "compressed", "hybrid")
- curve_parameters_encoding: str or None, curve parameter encoding
Returns:
bytes, DER-encoded public key
"""
def to_ssh(self):
"""
Export public key in SSH format.
Returns:
bytes, SSH-encoded public key
"""curve: Curve # The elliptic curve object
default_hashfunc: callable # Default hash function
pubkey: object # Internal public key objectclass BadSignatureError(Exception):
"""Raised when signature verification fails."""
class BadDigestError(Exception):
"""Raised when hash digest is too large for the curve."""
class MalformedPointError(Exception):
"""Raised when point encoding is invalid or point is not on curve."""from ecdsa import SigningKey, NIST256p
import hashlib
# Generate a new signing key
sk = SigningKey.generate(curve=NIST256p)
vk = sk.verifying_key
# Sign some data
message = b"Hello, cryptographic world!"
signature = sk.sign(message, hashfunc=hashlib.sha256)
# Verify the signature
try:
vk.verify(signature, message, hashfunc=hashlib.sha256)
print("Signature verified successfully!")
except BadSignatureError:
print("Signature verification failed!")from ecdsa import SigningKey, NIST256p
import hashlib
sk = SigningKey.generate(curve=NIST256p)
message = b"Deterministic signing example"
# Create deterministic signature (same signature every time)
sig1 = sk.sign_deterministic(message, hashfunc=hashlib.sha256)
sig2 = sk.sign_deterministic(message, hashfunc=hashlib.sha256)
assert sig1 == sig2 # Signatures are identical
# Verify deterministic signature
vk = sk.verifying_key
vk.verify(sig1, message, hashfunc=hashlib.sha256)from ecdsa import SigningKey, VerifyingKey, NIST256p
# Generate key pair
sk = SigningKey.generate(curve=NIST256p)
vk = sk.verifying_key
# Export keys in various formats
private_pem = sk.to_pem()
public_pem = vk.to_pem()
private_der = sk.to_der()
public_der = vk.to_der()
# Load keys from serialized formats
loaded_sk = SigningKey.from_pem(private_pem)
loaded_vk = VerifyingKey.from_pem(public_pem)
loaded_sk_der = SigningKey.from_der(private_der)
loaded_vk_der = VerifyingKey.from_der(public_der)
# Verify they work the same
message = b"Test message"
original_sig = sk.sign(message)
loaded_sig = loaded_sk.sign(message)
# Both signatures should verify with both public keys
vk.verify(original_sig, message)
loaded_vk.verify(loaded_sig, message)from ecdsa import SigningKey, NIST256p
from ecdsa.util import sigencode_der, sigdecode_der, sigencode_string, sigdecode_string
sk = SigningKey.generate(curve=NIST256p)
vk = sk.verifying_key
message = b"Encoding example"
# Sign with different encodings
sig_raw = sk.sign(message, sigencode=sigencode_string)
sig_der = sk.sign(message, sigencode=sigencode_der)
# Verify with corresponding decodings
vk.verify(sig_raw, message, sigdecode=sigdecode_string)
vk.verify(sig_der, message, sigdecode=sigdecode_der)Install with Tessl CLI
npx tessl i tessl/pypi-ecdsa