Digital Signatures
✍️ Ed25519-ZIP215 ZIP-215 Compliant EdDSA
📋 Quick Navigation
📖 Overview
Ed25519-ZIP215 is a variant of Ed25519 that follows the ZIP-215 specification for signature validation. It provides compatibility with Zcash and other systems that require specific validation rules for edge cases, ensuring deterministic validation across different implementations for consensus systems.
✨ Key Features
ZIP-215 Compliant
Follows Zcash Improvement Proposal 215 specification
Consensus Safe
Deterministic validation across all implementations
Ed25519 Compatible
Same key and signature format as standard Ed25519
Batch Verification
Efficient multi-signature validation support
Blockchain Ready
Designed for blockchain consensus systems
Edge Case Handling
Precise rules for non-canonical encodings
🔄 Key Differences from Standard Ed25519
🎯 ZIP-215 Specific Rules
- Non-canonical Points: Accepts certain non-canonical encodings
- Cofactor Handling: Specific rules for small-order points
- Validation Rules: Stricter deterministic validation
- Edge Case Coverage: Handles all possible input encodings
🔗 Consensus Applications
- Zcash Protocol: Primary use in Zcash blockchain
- Cross-chain Bridges: Deterministic validation needs
- Multi-party Protocols: Consensus-critical signature validation
- Blockchain Validators: Consistent validation across nodes
🔧 Algorithm Parameters
📊 Ed25519-ZIP215 Parameters
Security Level
128 bits
Same as standard Ed25519
Public Key Size
32 bytes
Edwards curve point encoding
Private Key Size
32 bytes
Secret scalar value
Signature Size
64 bytes
R (32 bytes) + S (32 bytes)
Validation Rules
ZIP-215
Zcash Improvement Proposal 215
Curve
edwards25519
Twisted Edwards curve
💻 Usage Examples
Basic Usage (Same API as Ed25519)
from metamui_crypto import Ed25519ZIP215
# Generate keypair
keypair = Ed25519ZIP215.generate_keypair()
# Sign message
message = b"Zcash transaction"
signature = Ed25519ZIP215.sign(message, keypair.private_key)
# Verify signature (ZIP-215 rules)
is_valid = Ed25519ZIP215.verify(signature, message, keypair.public_key)
print(f"Signature valid: {is_valid}")
Handling Edge Cases
from metamui_crypto import Ed25519ZIP215, Ed25519
# Some signatures may validate differently
message = b"Edge case test"
keypair = Ed25519ZIP215.generate_keypair()
signature = Ed25519ZIP215.sign(message, keypair.private_key)
# Standard Ed25519 might reject, ZIP-215 accepts
try:
# This might fail with standard Ed25519
standard_valid = Ed25519.verify(signature, message, keypair.public_key)
except:
standard_valid = False
# ZIP-215 has specific acceptance rules
zip215_valid = Ed25519ZIP215.verify(signature, message, keypair.public_key)
# ZIP-215 may accept signatures that standard Ed25519 rejects
print(f"Standard Ed25519: {standard_valid}")
print(f"ZIP-215: {zip215_valid}")
Consensus-Critical Validation
from metamui_crypto import Ed25519ZIP215
class ConsensusValidator:
"""Blockchain consensus validation"""
@staticmethod
def validate_block_signature(block_data, signature, validator_key):
"""Validate block signature for consensus"""
# Must use ZIP-215 for consensus
return Ed25519ZIP215.verify(
signature,
block_data,
validator_key
)
@staticmethod
def validate_transaction_batch(transactions):
"""Batch validate transactions"""
messages = []
signatures = []
public_keys = []
for tx in transactions:
messages.append(tx['data'])
signatures.append(tx['signature'])
public_keys.append(tx['public_key'])
# Batch verification with ZIP-215 rules
return Ed25519ZIP215.batch_verify(
signatures,
messages,
public_keys
)
Migrating from Standard Ed25519
from metamui_crypto import Ed25519, Ed25519ZIP215
class SignatureMigration:
"""Migrate between Ed25519 variants"""
def __init__(self):
self.accept_non_canonical = True
def verify_signature(self, signature, message, public_key):
"""Verify with appropriate rules"""
if self.accept_non_canonical:
# Use ZIP-215 for broader acceptance
return Ed25519ZIP215.verify(signature, message, public_key)
else:
# Use standard Ed25519
return Ed25519.verify(signature, message, public_key)
def sign_with_compatibility(self, message, private_key):
"""Sign in a way that's valid for both"""
# Signatures created by either variant are generally
# valid for both, but verification rules differ
signature = Ed25519ZIP215.sign(message, private_key)
# Verify it's canonical (optional check)
if self.is_canonical_signature(signature):
return signature
else:
# Re-sign if needed (rare)
return Ed25519.sign(message, private_key)
@staticmethod
def is_canonical_signature(signature):
"""Check if signature is in canonical form"""
# Check S value is canonical
s = int.from_bytes(signature[32:], 'little')
return s < Ed25519ZIP215.L
Implementation Details
ZIP-215 Validation Rules
- Point Validation:
- Accept non-canonical encodings of valid points
- Specific handling of points with small order
- Deterministic for all 2^256 possible encodings
- Signature Components:
- R: 32-byte point encoding (may be non-canonical)
- S: 32-byte scalar (must be < L)
- Verification Algorithm:
def verify_zip215(signature, message, public_key): R = signature[:32] S = signature[32:] # Decode points (ZIP-215 accepts more encodings) R_point = decode_point_zip215(R) A_point = decode_point_zip215(public_key) # Check S is valid scalar s_int = int.from_bytes(S, 'little') if s_int >= L: return False # Compute challenge h = SHA512(R || public_key || message) h_int = int.from_bytes(h, 'little') % L # Verify equation: [s]B = R + [h]A return scalar_mult(s_int, B) == point_add(R_point, scalar_mult(h_int, A_point))
Edge Cases Handled
# Example edge cases that ZIP-215 handles differently
# 1. Non-canonical point encodings
non_canonical_y = (1 << 255) | valid_y # High bit set
# Standard Ed25519: Reject
# ZIP-215: Accept if point is valid
# 2. Small order points
small_order_point = encode_point(EIGHT_TORSION[1])
# Standard Ed25519: May reject
# ZIP-215: Specific acceptance rules
# 3. Mixed order signatures
R = small_order_point
S = valid_scalar
# ZIP-215 has deterministic rules for these cases
🔒 Security Considerations
Consensus Security
- Deterministic: All implementations must agree
- No Malleability: Signatures cannot be modified
- Complete Coverage: Handles all possible inputs
Cryptographic Security
- Same Security Level: 128-bit security as Ed25519
- No Additional Weaknesses: ZIP-215 doesn’t weaken security
- Batch Verification: Same security properties
Implementation Security
- Careful Decoding: Must implement ZIP-215 decoding exactly
- Test Vectors: Extensive test coverage for edge cases
- Consensus Critical: Bugs can cause chain splits
Common Use Cases
Zcash Integration
from metamui_crypto import Ed25519ZIP215
class ZcashTransaction:
"""Zcash transaction signing"""
def __init__(self, private_key):
self.private_key = private_key
self.public_key = Ed25519ZIP215.get_public_key(private_key)
def sign_transaction(self, tx_data):
"""Sign Zcash transaction"""
# Compute sighash
sighash = self.compute_sighash(tx_data)
# Sign with ZIP-215
signature = Ed25519ZIP215.sign(sighash, self.private_key)
return {
'txid': tx_data['txid'],
'signature': signature,
'public_key': self.public_key
}
def compute_sighash(self, tx_data):
"""Compute Zcash sighash"""
# Implement Zcash-specific sighash algorithm
# This is simplified
return Blake2b.hash(
tx_data['inputs'] +
tx_data['outputs'] +
tx_data['locktime']
)
Blockchain Validator
class BlockchainValidator:
"""Validator for ZIP-215 based blockchain"""
def __init__(self, validator_key):
self.signing_key = validator_key
self.address = self.derive_address(
Ed25519ZIP215.get_public_key(validator_key)
)
def validate_and_sign_block(self, block):
"""Validate block and add signature"""
# Validate all transactions
for tx in block['transactions']:
if not self.validate_transaction(tx):
return None
# Validate block structure
if not self.validate_block_structure(block):
return None
# Sign block
block_hash = self.compute_block_hash(block)
signature = Ed25519ZIP215.sign(block_hash, self.signing_key)
block['validator_signature'] = signature
return block
def validate_transaction(self, tx):
"""Validate individual transaction"""
return Ed25519ZIP215.verify(
tx['signature'],
tx['data'],
tx['sender_public_key']
)
Cross-Chain Bridge
class CrossChainBridge:
"""Bridge between Ed25519 and ZIP-215 chains"""
def __init__(self):
self.standard_chain = "ethereum" # Uses standard Ed25519
self.zip215_chain = "zcash" # Uses ZIP-215
def verify_cross_chain_signature(self, signature, message, public_key, source_chain):
"""Verify signature from either chain"""
if source_chain == self.zip215_chain:
# Source uses ZIP-215
return Ed25519ZIP215.verify(signature, message, public_key)
else:
# Try standard first
try:
return Ed25519.verify(signature, message, public_key)
except:
# Fall back to ZIP-215 for compatibility
return Ed25519ZIP215.verify(signature, message, public_key)
def create_portable_signature(self, message, private_key):
"""Create signature valid on both chains"""
# Sign with standard Ed25519
signature = Ed25519.sign(message, private_key)
# Verify it's also valid under ZIP-215
public_key = Ed25519.get_public_key(private_key)
assert Ed25519ZIP215.verify(signature, message, public_key)
return signature
Testing and Validation
Test Vector Verification
# ZIP-215 specific test vectors
zip215_test_vectors = [
{
"message": "5a63...",
"public_key": "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
"signature": "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
"valid_zip215": True,
"valid_standard": False # Non-canonical encoding
},
# More test vectors...
]
def validate_implementation():
"""Validate ZIP-215 implementation"""
for vector in zip215_test_vectors:
message = bytes.fromhex(vector["message"])
public_key = bytes.fromhex(vector["public_key"])
signature = bytes.fromhex(vector["signature"])
# Check ZIP-215 validation
result = Ed25519ZIP215.verify(signature, message, public_key)
assert result == vector["valid_zip215"], f"ZIP-215 validation failed"
# Check standard Ed25519 difference
if vector["valid_standard"] != vector["valid_zip215"]:
try:
std_result = Ed25519.verify(signature, message, public_key)
assert std_result == vector["valid_standard"]
except:
assert not vector["valid_standard"]
Fuzzing Edge Cases
import random
def fuzz_zip215_validation():
"""Fuzz test ZIP-215 validation"""
for _ in range(10000):
# Generate random 32-byte values
random_point = bytes(random.randint(0, 255) for _ in range(32))
random_scalar = bytes(random.randint(0, 255) for _ in range(32))
random_signature = random_point + random_scalar
random_message = bytes(random.randint(0, 255) for _ in range(64))
random_pubkey = bytes(random.randint(0, 255) for _ in range(32))
# ZIP-215 must not crash on any input
try:
result = Ed25519ZIP215.verify(
random_signature,
random_message,
random_pubkey
)
# Result should be deterministic
result2 = Ed25519ZIP215.verify(
random_signature,
random_message,
random_pubkey
)
assert result == result2
except Exception as e:
print(f"ZIP-215 crashed on input: {e}")
raise
Performance Considerations
Batch Verification
# ZIP-215 batch verification is similar to Ed25519
# but must handle edge cases correctly
def benchmark_batch_verification():
"""Compare batch vs individual verification"""
# Generate test signatures
n = 1000
keypairs = [Ed25519ZIP215.generate_keypair() for _ in range(n)]
messages = [f"Message {i}".encode() for i in range(n)]
signatures = [
Ed25519ZIP215.sign(msg, kp.private_key)
for msg, kp in zip(messages, keypairs)
]
public_keys = [kp.public_key for kp in keypairs]
# Time individual verification
start = time.time()
for sig, msg, pk in zip(signatures, messages, public_keys):
Ed25519ZIP215.verify(sig, msg, pk)
individual_time = time.time() - start
# Time batch verification
start = time.time()
Ed25519ZIP215.batch_verify(signatures, messages, public_keys)
batch_time = time.time() - start
print(f"Individual: {individual_time:.3f}s")
print(f"Batch: {batch_time:.3f}s")
print(f"Speedup: {individual_time/batch_time:.1f}x")
Migration Guide
From Standard Ed25519
# Before: Standard Ed25519
verifier = Ed25519.verify
# After: ZIP-215 for consensus
verifier = Ed25519ZIP215.verify
# Compatibility wrapper
def verify_compatible(signature, message, public_key):
"""Try both validation rules"""
# Try ZIP-215 first (more permissive)
if Ed25519ZIP215.verify(signature, message, public_key):
return True
# Try standard (stricter)
try:
return Ed25519.verify(signature, message, public_key)
except:
return False
Consensus System Migration
class ConsensusMigration:
"""Migrate consensus to ZIP-215"""
def __init__(self, activation_height):
self.activation_height = activation_height
def verify_signature(self, signature, message, public_key, block_height):
"""Height-based activation"""
if block_height >= self.activation_height:
# New rules (ZIP-215)
return Ed25519ZIP215.verify(signature, message, public_key)
else:
# Old rules
return Ed25519.verify(signature, message, public_key)
Resources
- ZIP-215 Specification - Official specification
- Ed25519 Validation - Validation complexities
- Test Vectors - Official test vectors
- Security Analysis - Security properties
- Zcash Protocol - Protocol specification