ECDSA cryptographic signature library (pure python)
—
Edwards-curve Digital Signature Algorithm (EdDSA) implementation providing pure EdDSA signatures using Ed25519 and Ed448 curves. EdDSA offers deterministic signatures, resistance to side-channel attacks, and high performance compared to traditional ECDSA.
Public key class for EdDSA signature verification on Edwards curves.
class PublicKey:
"""Public key for Edwards Digital Signature Algorithm."""
def __init__(self, generator, public_key, public_point=None):
"""
Create EdDSA public key.
Parameters:
- generator: PointEdwards, curve generator point
- public_key: bytes, encoded public key
- public_point: PointEdwards, optional pre-computed point
Raises:
ValueError: if public key length is incorrect
"""
def public_point(self):
"""
Get public key as Edwards curve point.
Returns:
PointEdwards, public key point on Edwards curve
"""
def public_key(self):
"""
Get encoded public key bytes.
Returns:
bytes, encoded public key
"""
def verify(self, data, signature):
"""
Verify EdDSA signature over data.
Parameters:
- data: bytes or str, data that was signed
- signature: bytes, EdDSA signature (R || S)
Returns:
bool, True if signature is valid
Raises:
ValueError: if signature length is incorrect
"""Private key class for EdDSA signature creation on Edwards curves.
class PrivateKey:
"""Private key for Edwards Digital Signature Algorithm."""
def __init__(self, generator, private_key):
"""
Create EdDSA private key.
Parameters:
- generator: PointEdwards, curve generator point
- private_key: bytes, raw private key bytes
Raises:
ValueError: if private key length is incorrect
"""
def private_key(self):
"""
Get raw private key bytes.
Returns:
bytes, raw private key material
"""
def public_key(self):
"""
Generate corresponding public key.
Returns:
PublicKey, public key derived from this private key
"""
def sign(self, data):
"""
Create EdDSA signature over data.
Parameters:
- data: bytes or str, data to sign
Returns:
bytes, EdDSA signature (R || S format)
"""Pre-defined Edwards curve parameters for Ed25519 and Ed448 algorithms.
# Ed25519 curve and generator (RFC 8032)
curve_ed25519: CurveEdTw # Edwards curve y² = -x² + 1 - (121665/121666)x²y²
generator_ed25519: PointEdwards # Base point for Ed25519
# Ed448 curve and generator (RFC 8032)
curve_ed448: CurveEdTw # Edwards curve x² + y² = 1 - 39081x²y²
generator_ed448: PointEdwards # Base point for Ed448from ecdsa.eddsa import PrivateKey, PublicKey, generator_ed25519
import os
# Generate Ed25519 private key (32 bytes for Ed25519)
private_key_bytes = os.urandom(32)
private_key = PrivateKey(generator_ed25519, private_key_bytes)
# Get corresponding public key
public_key = private_key.public_key()
print(f"Private key length: {len(private_key.private_key())} bytes")
print(f"Public key length: {len(public_key.public_key())} bytes")
# Sign some data
message = b"Hello, EdDSA world!"
signature = private_key.sign(message)
print(f"Signature length: {len(signature)} bytes")
print(f"Signature (hex): {signature.hex()}")
# Verify signature
is_valid = public_key.verify(message, signature)
print(f"Signature valid: {is_valid}")
# Test with modified message (should fail)
try:
modified_message = b"Hello, EdDSA world!!"
is_valid_modified = public_key.verify(modified_message, signature)
print(f"Modified message signature valid: {is_valid_modified}")
except:
print("Signature verification failed for modified message")from ecdsa.eddsa import PrivateKey, PublicKey, generator_ed448
import os
# Generate Ed448 private key (57 bytes for Ed448)
private_key_bytes = os.urandom(57)
private_key = PrivateKey(generator_ed448, private_key_bytes)
# Get corresponding public key
public_key = private_key.public_key()
print(f"Ed448 private key length: {len(private_key.private_key())} bytes")
print(f"Ed448 public key length: {len(public_key.public_key())} bytes")
# Sign data with Ed448
message = b"Ed448 provides higher security level"
signature = private_key.sign(message)
print(f"Ed448 signature length: {len(signature)} bytes")
print(f"Ed448 signature (hex): {signature.hex()}")
# Verify Ed448 signature
is_valid = public_key.verify(message, signature)
print(f"Ed448 signature valid: {is_valid}")from ecdsa.eddsa import PrivateKey, generator_ed25519
import os
# EdDSA signatures are deterministic - same message produces same signature
private_key_bytes = os.urandom(32)
private_key = PrivateKey(generator_ed25519, private_key_bytes)
message = b"Deterministic signature test"
# Sign the same message multiple times
sig1 = private_key.sign(message)
sig2 = private_key.sign(message)
sig3 = private_key.sign(message)
print(f"Signature 1: {sig1.hex()[:32]}...")
print(f"Signature 2: {sig2.hex()[:32]}...")
print(f"Signature 3: {sig3.hex()[:32]}...")
print(f"All signatures identical: {sig1 == sig2 == sig3}")
# Different messages produce different signatures
message2 = b"Different message"
sig_different = private_key.sign(message2)
print(f"Different message signature: {sig_different.hex()[:32]}...")
print(f"Signatures different: {sig1 != sig_different}")from ecdsa.eddsa import PrivateKey, generator_ed25519
from ecdsa.ellipticcurve import PointEdwards
import os
# Create private key and examine the underlying mathematics
private_key_bytes = os.urandom(32)
private_key = PrivateKey(generator_ed25519, private_key_bytes)
public_key = private_key.public_key()
# Get the public key point
public_point = public_key.public_point()
print(f"Public key point type: {type(public_point)}")
print(f"Point x coordinate: {public_point.x()}")
print(f"Point y coordinate: {public_point.y()}")
# Verify point is on the Edwards curve
curve = generator_ed25519.curve()
x, y = public_point.x(), public_point.y()
on_curve = curve.contains_point(x, y)
print(f"Public key point is on curve: {on_curve}")
# Demonstrate point arithmetic on Edwards curves
generator = generator_ed25519
scalar = 12345
result_point = scalar * generator
print(f"Scalar multiplication result: ({result_point.x()}, {result_point.y()})")
print(f"Result point on curve: {curve.contains_point(result_point.x(), result_point.y())}")from ecdsa import SigningKey, NIST256p
from ecdsa.eddsa import PrivateKey, generator_ed25519
import time
import os
# Create ECDSA key (for comparison)
ecdsa_key = SigningKey.generate(curve=NIST256p)
# Create EdDSA key
eddsa_private_bytes = os.urandom(32)
eddsa_key = PrivateKey(generator_ed25519, eddsa_private_bytes)
message = b"Performance test message" * 10 # 250 bytes
# Time ECDSA signing
start_time = time.time()
for _ in range(100):
ecdsa_sig = ecdsa_key.sign(message)
ecdsa_sign_time = time.time() - start_time
# Time EdDSA signing
start_time = time.time()
for _ in range(100):
eddsa_sig = eddsa_key.sign(message)
eddsa_sign_time = time.time() - start_time
print(f"ECDSA signing (100 iterations): {ecdsa_sign_time:.4f} seconds")
print(f"EdDSA signing (100 iterations): {eddsa_sign_time:.4f} seconds")
print(f"EdDSA speedup: {ecdsa_sign_time / eddsa_sign_time:.2f}x")
# Time verification
ecdsa_vk = ecdsa_key.verifying_key
eddsa_pk = eddsa_key.public_key()
start_time = time.time()
for _ in range(100):
ecdsa_vk.verify(ecdsa_sig, message)
ecdsa_verify_time = time.time() - start_time
start_time = time.time()
for _ in range(100):
eddsa_pk.verify(message, eddsa_sig)
eddsa_verify_time = time.time() - start_time
print(f"ECDSA verification (100 iterations): {ecdsa_verify_time:.4f} seconds")
print(f"EdDSA verification (100 iterations): {eddsa_verify_time:.4f} seconds")
print(f"EdDSA verification speedup: {ecdsa_verify_time / eddsa_verify_time:.2f}x")from ecdsa.eddsa import PrivateKey, PublicKey, generator_ed25519
import os
import json
import base64
# Generate key pair
private_key_bytes = os.urandom(32)
private_key = PrivateKey(generator_ed25519, private_key_bytes)
public_key = private_key.public_key()
# Serialize keys for storage
private_key_b64 = base64.b64encode(private_key.private_key()).decode('ascii')
public_key_b64 = base64.b64encode(public_key.public_key()).decode('ascii')
# Create key storage format
key_data = {
"algorithm": "Ed25519",
"private_key": private_key_b64,
"public_key": public_key_b64
}
print("Serialized key data:")
print(json.dumps(key_data, indent=2))
# Deserialize keys
loaded_private_bytes = base64.b64decode(key_data["private_key"])
loaded_public_bytes = base64.b64decode(key_data["public_key"])
# Reconstruct keys
loaded_private_key = PrivateKey(generator_ed25519, loaded_private_bytes)
loaded_public_key = PublicKey(generator_ed25519, loaded_public_bytes)
# Verify keys work correctly
test_message = b"Key serialization test"
signature = loaded_private_key.sign(test_message)
is_valid = loaded_public_key.verify(test_message, signature)
print(f"Loaded keys work correctly: {is_valid}")
print(f"Original and loaded private keys match: {private_key == loaded_private_key}")
print(f"Original and loaded public keys match: {public_key == loaded_public_key}")from ecdsa.eddsa import PrivateKey, generator_ed25519, generator_ed448
import os
# Demonstrate EdDSA security properties
print("EdDSA Security Properties:")
print("=" * 40)
# 1. Deterministic signatures (no randomness during signing)
private_key = PrivateKey(generator_ed25519, os.urandom(32))
message = b"Security test message"
sig1 = private_key.sign(message)
sig2 = private_key.sign(message)
print(f"1. Deterministic signatures: {sig1 == sig2}")
# 2. Context separation (different private keys, same message)
private_key2 = PrivateKey(generator_ed25519, os.urandom(32))
sig_different_key = private_key2.sign(message)
print(f"2. Different keys produce different signatures: {sig1 != sig_different_key}")
# 3. Ed25519 vs Ed448 security levels
ed25519_key = PrivateKey(generator_ed25519, os.urandom(32))
ed448_key = PrivateKey(generator_ed448, os.urandom(57))
ed25519_sig = ed25519_key.sign(message)
ed448_sig = ed448_key.sign(message)
print(f"3. Ed25519 signature length: {len(ed25519_sig)} bytes")
print(f" Ed448 signature length: {len(ed448_sig)} bytes")
print(f" Ed25519 security level: ~128 bits")
print(f" Ed448 security level: ~224 bits")
# 4. Signature malleability resistance
print("4. EdDSA signatures are not malleable (unlike some ECDSA implementations)")
# 5. Side-channel resistance
print("5. EdDSA is designed to be resistant to timing attacks")
print(" - No secret branches in the signing algorithm")
print(" - No secret array indices")
print(" - No secret memory access patterns")EdDSA provides several security and performance advantages over ECDSA:
Install with Tessl CLI
npx tessl i tessl/pypi-ecdsa