AgentMask / src /ledger /signing.py
b2230765034
stage4: ledger persistence, signing, parallel agents, UI improvements
631d977
"""
ECDSA Signing Module
=====================
Provides ECDSA signature functionality for ledger blocks.
"""
import hashlib
from typing import Optional, Tuple
from dataclasses import dataclass
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend
from cryptography.exceptions import InvalidSignature
@dataclass
class KeyPair:
"""Holds ECDSA key pair."""
private_key: ec.EllipticCurvePrivateKey
public_key: ec.EllipticCurvePublicKey
def get_public_key_hex(self) -> str:
"""Get public key as hex string."""
public_bytes = self.public_key.public_bytes(
encoding=serialization.Encoding.X962,
format=serialization.PublicFormat.CompressedPoint
)
return public_bytes.hex()
def get_private_key_pem(self) -> bytes:
"""Get private key as PEM bytes (for secure storage)."""
return self.private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
class ECDSASigner:
"""
ECDSA signer for creating and verifying signatures.
Uses secp256k1 curve (same as Bitcoin/Ethereum).
"""
CURVE = ec.SECP256K1()
def __init__(self, private_key: Optional[ec.EllipticCurvePrivateKey] = None):
"""
Initialize the signer.
Args:
private_key: Existing private key. If None, generates a new one.
"""
if private_key:
self.private_key = private_key
else:
self.private_key = ec.generate_private_key(self.CURVE, default_backend())
self.public_key = self.private_key.public_key()
@classmethod
def generate_keypair(cls) -> KeyPair:
"""Generate a new ECDSA key pair."""
private_key = ec.generate_private_key(cls.CURVE, default_backend())
public_key = private_key.public_key()
return KeyPair(private_key=private_key, public_key=public_key)
@classmethod
def from_private_key_pem(cls, pem_data: bytes) -> 'ECDSASigner':
"""
Create a signer from PEM-encoded private key.
Args:
pem_data: PEM-encoded private key bytes
Returns:
ECDSASigner instance
"""
private_key = serialization.load_pem_private_key(
pem_data,
password=None,
backend=default_backend()
)
return cls(private_key=private_key)
def sign(self, data: str) -> str:
"""
Sign data and return signature as hex string.
Args:
data: Data string to sign (typically a block hash)
Returns:
Signature as hex string
"""
data_bytes = data.encode('utf-8')
signature = self.private_key.sign(
data_bytes,
ec.ECDSA(hashes.SHA256())
)
return signature.hex()
def verify(self, data: str, signature_hex: str) -> bool:
"""
Verify a signature.
Args:
data: Original data string
signature_hex: Signature as hex string
Returns:
True if signature is valid
"""
try:
data_bytes = data.encode('utf-8')
signature = bytes.fromhex(signature_hex)
self.public_key.verify(
signature,
data_bytes,
ec.ECDSA(hashes.SHA256())
)
return True
except (InvalidSignature, ValueError):
return False
def get_public_key_hex(self) -> str:
"""Get public key as hex string."""
public_bytes = self.public_key.public_bytes(
encoding=serialization.Encoding.X962,
format=serialization.PublicFormat.CompressedPoint
)
return public_bytes.hex()
class SignatureVerifier:
"""
Verifier for ECDSA signatures using only public key.
Use this when you only need to verify signatures, not create them.
"""
def __init__(self, public_key_hex: str):
"""
Initialize verifier with public key.
Args:
public_key_hex: Public key as hex string (compressed X962 format)
"""
public_bytes = bytes.fromhex(public_key_hex)
self.public_key = ec.EllipticCurvePublicKey.from_encoded_point(
ec.SECP256K1(),
public_bytes
)
def verify(self, data: str, signature_hex: str) -> bool:
"""
Verify a signature.
Args:
data: Original data string
signature_hex: Signature as hex string
Returns:
True if signature is valid
"""
try:
data_bytes = data.encode('utf-8')
signature = bytes.fromhex(signature_hex)
self.public_key.verify(
signature,
data_bytes,
ec.ECDSA(hashes.SHA256())
)
return True
except (InvalidSignature, ValueError):
return False
def sign_block(block_hash: str, signer: ECDSASigner) -> str:
"""
Convenience function to sign a block hash.
Args:
block_hash: The block hash to sign
signer: ECDSASigner instance
Returns:
Signature as hex string
"""
return signer.sign(block_hash)
def verify_block_signature(
block_hash: str,
signature_hex: str,
public_key_hex: str
) -> bool:
"""
Convenience function to verify a block signature.
Args:
block_hash: The block hash that was signed
signature_hex: The signature to verify
public_key_hex: Public key of the signer
Returns:
True if signature is valid
"""
verifier = SignatureVerifier(public_key_hex)
return verifier.verify(block_hash, signature_hex)