Elliptic curve crypto in python including secp256k1, alt_bn128, and bls12_381
—
BLS (Boneh-Lynn-Shacham) signature schemes according to IETF standards, providing digital signatures with aggregation capabilities. These schemes are particularly important for blockchain applications where signature aggregation can significantly reduce data size and verification costs.
The basic BLS signature scheme using G2 elements for signatures. This is the most commonly used BLS signature variant.
class G2Basic:
DST: bytes # Domain separation tag
@classmethod
def SkToPk(cls, sk: int) -> bytes:
"""
Convert a secret key to a public key.
Args:
sk (int): Secret key as integer
Returns:
bytes: 48-byte compressed public key
"""
@staticmethod
def KeyValidate(pk: bytes) -> bool:
"""
Validate a public key.
Args:
pk (bytes): 48-byte public key to validate
Returns:
bool: True if key is valid, False otherwise
"""
@classmethod
def Sign(cls, sk: int, message: bytes) -> bytes:
"""
Sign a message with a secret key.
Args:
sk (int): Secret key as integer
message (bytes): Message to sign
Returns:
bytes: 96-byte signature
"""
@classmethod
def Verify(cls, pk: bytes, message: bytes, signature: bytes) -> bool:
"""
Verify a signature.
Args:
pk (bytes): 48-byte public key
message (bytes): Original message
signature (bytes): 96-byte signature to verify
Returns:
bool: True if signature is valid, False otherwise
"""
@classmethod
def Aggregate(cls, signatures: Sequence[bytes]) -> bytes:
"""
Aggregate multiple signatures into one.
Args:
signatures (Sequence[bytes]): List of 96-byte signatures
Returns:
bytes: 96-byte aggregated signature
"""
@classmethod
def AggregateVerify(cls, pks: Sequence[bytes], messages: Sequence[bytes], signature: bytes) -> bool:
"""
Verify an aggregated signature against multiple public keys and messages.
Args:
pks (Sequence[bytes]): List of 48-byte public keys
messages (Sequence[bytes]): List of messages (one per public key)
signature (bytes): 96-byte aggregated signature
Returns:
bool: True if aggregated signature is valid, False otherwise
"""
@classmethod
def FastAggregateVerify(cls, pks: Sequence[bytes], message: bytes, signature: bytes) -> bool:
"""
Fast verification of aggregated signature when all messages are identical.
Args:
pks (Sequence[bytes]): List of 48-byte public keys
message (bytes): Single message signed by all keys
signature (bytes): 96-byte aggregated signature
Returns:
bool: True if aggregated signature is valid, False otherwise
"""BLS signature scheme with message augmentation to prevent rogue key attacks without requiring proof of possession.
class G2MessageAugmentation:
DST: bytes # Domain separation tag
@classmethod
def SkToPk(cls, sk: int) -> bytes:
"""Convert secret key to public key."""
@staticmethod
def KeyValidate(pk: bytes) -> bool:
"""Validate a public key."""
@classmethod
def Sign(cls, sk: int, message: bytes) -> bytes:
"""Sign a message with message augmentation."""
@classmethod
def Verify(cls, pk: bytes, message: bytes, signature: bytes) -> bool:
"""Verify a signature with message augmentation."""
@classmethod
def Aggregate(cls, signatures: Sequence[bytes]) -> bytes:
"""Aggregate multiple signatures."""
@classmethod
def AggregateVerify(cls, pks: Sequence[bytes], messages: Sequence[bytes], signature: bytes) -> bool:
"""Verify aggregated signature with message augmentation."""BLS signature scheme with proof of possession, providing the most secure aggregation capabilities.
class G2ProofOfPossession:
DST: bytes # Domain separation tag
POP_DST: bytes # Proof of possession domain separation tag
@staticmethod
def SkToPk(sk: int) -> bytes:
"""Convert secret key to public key."""
@staticmethod
def KeyValidate(pk: bytes) -> bool:
"""Validate a public key."""
@classmethod
def Sign(cls, sk: int, message: bytes) -> bytes:
"""Sign a message."""
@classmethod
def Verify(cls, pk: bytes, message: bytes, signature: bytes) -> bool:
"""Verify a signature."""
@classmethod
def Aggregate(cls, signatures: Sequence[bytes]) -> bytes:
"""Aggregate multiple signatures."""
@classmethod
def AggregateVerify(cls, pks: Sequence[bytes], messages: Sequence[bytes], signature: bytes) -> bool:
"""Verify aggregated signature."""
@staticmethod
def FastAggregateVerify(pks: Sequence[bytes], message: bytes, signature: bytes) -> bool:
"""Fast verification when all messages are identical."""
@staticmethod
def PopProve(sk: int) -> bytes:
"""
Generate proof of possession for a secret key.
Args:
sk (int): Secret key
Returns:
bytes: 96-byte proof of possession
"""
@staticmethod
def PopVerify(pk: bytes, proof: bytes) -> bool:
"""
Verify proof of possession for a public key.
Args:
pk (bytes): 48-byte public key
proof (bytes): 96-byte proof of possession
Returns:
bool: True if proof is valid, False otherwise
"""from py_ecc.bls import G2Basic
# Generate key pair
private_key = 12345
public_key = G2Basic.SkToPk(private_key)
# Sign message
message = b"Hello, BLS signatures!"
signature = G2Basic.Sign(private_key, message)
# Verify signature
is_valid = G2Basic.Verify(public_key, message, signature)
assert is_validfrom py_ecc.bls import G2Basic
# Multiple signers
private_keys = [123, 456, 789]
public_keys = [G2Basic.SkToPk(sk) for sk in private_keys]
messages = [b"Message 1", b"Message 2", b"Message 3"]
# Each signer signs their message
signatures = [G2Basic.Sign(sk, msg) for sk, msg in zip(private_keys, messages)]
# Aggregate signatures
aggregated_sig = G2Basic.Aggregate(signatures)
# Verify aggregated signature
is_valid = G2Basic.AggregateVerify(public_keys, messages, aggregated_sig)
assert is_validfrom py_ecc.bls import G2Basic
# Multiple signers signing the same message
private_keys = [111, 222, 333]
public_keys = [G2Basic.SkToPk(sk) for sk in private_keys]
message = b"Same message for all"
# Each signer signs the same message
signatures = [G2Basic.Sign(sk, message) for sk in private_keys]
# Aggregate signatures
aggregated_sig = G2Basic.Aggregate(signatures)
# Fast verification (more efficient when all messages are identical)
is_valid = G2Basic.FastAggregateVerify(public_keys, message, aggregated_sig)
assert is_validfrom py_ecc.bls import G2ProofOfPossession
# Generate key pair
private_key = 54321
public_key = G2ProofOfPossession.SkToPk(private_key)
# Generate proof of possession
pop_proof = G2ProofOfPossession.PopProve(private_key)
# Verify proof of possession
is_valid_pop = G2ProofOfPossession.PopVerify(public_key, pop_proof)
assert is_valid_pop
# Now the public key can be safely used in aggregation
message = b"Secure aggregation message"
signature = G2ProofOfPossession.Sign(private_key, message)
is_valid = G2ProofOfPossession.Verify(public_key, message, signature)
assert is_validAll BLS signature functions may raise ValidationError from eth_utils for invalid inputs:
Always validate inputs when working with untrusted data:
from py_ecc.bls import G2Basic
from eth_utils import ValidationError
try:
is_valid_key = G2Basic.KeyValidate(untrusted_public_key)
if is_valid_key:
result = G2Basic.Verify(untrusted_public_key, message, signature)
else:
print("Invalid public key")
except ValidationError as e:
print(f"Validation error: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-py-ecc