svkrishna
🤖 Implement Story 2: Inter-Agent Contract Module
4d56236
Raw
History Blame Contribute Delete
7.39 kB
"""Cryptographic utilities for contract signing and verification."""
import base64
from typing import Tuple
import nacl.encoding
import nacl.signing
from nacl.exceptions import BadSignatureError
from fastmcp.utilities.logging import get_logger
logger = get_logger(__name__)
class CryptoError(Exception):
"""Cryptographic operation error."""
pass
class Ed25519Signer:
"""Ed25519 signature operations for contract signing."""
def __init__(self, private_key: bytes = None):
"""Initialize signer with optional private key.
Args:
private_key: Optional private key bytes. If None, generates new key pair.
"""
if private_key is None:
self._signing_key = nacl.signing.SigningKey.generate()
else:
self._signing_key = nacl.signing.SigningKey(private_key)
self._verify_key = self._signing_key.verify_key
@classmethod
def from_private_key_b64(cls, private_key_b64: str) -> "Ed25519Signer":
"""Create signer from base64-encoded private key.
Args:
private_key_b64: Base64-encoded private key
Returns:
Ed25519Signer instance
"""
try:
private_key = base64.b64decode(private_key_b64)
return cls(private_key)
except Exception as e:
raise CryptoError(f"Invalid private key format: {e}")
@classmethod
def from_public_key_b64(cls, public_key_b64: str) -> "Ed25519Signer":
"""Create signer from base64-encoded public key (verification only).
Args:
public_key_b64: Base64-encoded public key
Returns:
Ed25519Signer instance (verification only)
"""
try:
public_key = base64.b64decode(public_key_b64)
verify_key = nacl.signing.VerifyKey(public_key)
signer = cls.__new__(cls)
signer._signing_key = None # No private key for verification only
signer._verify_key = verify_key
return signer
except Exception as e:
raise CryptoError(f"Invalid public key format: {e}")
def sign(self, message: str) -> str:
"""Sign a message.
Args:
message: Message to sign
Returns:
Base64-encoded signature
Raises:
CryptoError: If signing fails
"""
if self._signing_key is None:
raise CryptoError("Cannot sign: no private key available")
try:
message_bytes = message.encode('utf-8')
signed = self._signing_key.sign(message_bytes)
signature = signed.signature
return base64.b64encode(signature).decode('ascii')
except Exception as e:
raise CryptoError(f"Signing failed: {e}")
def verify(self, message: str, signature: str) -> bool:
"""Verify a signature.
Args:
message: Original message
signature: Base64-encoded signature to verify
Returns:
True if signature is valid, False otherwise
"""
try:
message_bytes = message.encode('utf-8')
signature_bytes = base64.b64decode(signature)
self._verify_key.verify(message_bytes, signature_bytes)
return True
except (BadSignatureError, Exception) as e:
logger.debug(f"Signature verification failed: {e}")
return False
def get_public_key_b64(self) -> str:
"""Get base64-encoded public key.
Returns:
Base64-encoded public key
"""
return base64.b64encode(self._verify_key.encode()).decode('ascii')
def get_private_key_b64(self) -> str:
"""Get base64-encoded private key.
Returns:
Base64-encoded private key
Raises:
CryptoError: If no private key available
"""
if self._signing_key is None:
raise CryptoError("No private key available")
return base64.b64encode(self._signing_key.encode()).decode('ascii')
def get_key_pair_b64(self) -> Tuple[str, str]:
"""Get both public and private keys as base64 strings.
Returns:
Tuple of (public_key_b64, private_key_b64)
Raises:
CryptoError: If no private key available
"""
return self.get_public_key_b64(), self.get_private_key_b64()
class ContractSigner:
"""High-level contract signing operations."""
def __init__(self, signer: Ed25519Signer):
"""Initialize with Ed25519 signer.
Args:
signer: Ed25519Signer instance
"""
self._signer = signer
def sign_contract(self, contract_id: str, content_hash: str, signer_id: str, signer_type: str) -> str:
"""Sign a contract.
Args:
contract_id: Contract ID
content_hash: SHA-256 hash of contract content
signer_id: ID of the signing party
signer_type: Type of signer (e.g., 'provider', 'payor', 'patient')
Returns:
Base64-encoded signature
Raises:
CryptoError: If signing fails
"""
# Create signing message
signing_message = f"{contract_id}:{content_hash}:{signer_id}:{signer_type}"
return self._signer.sign(signing_message)
def verify_contract_signature(self, contract_id: str, content_hash: str, signer_id: str,
signer_type: str, signature: str) -> bool:
"""Verify a contract signature.
Args:
contract_id: Contract ID
content_hash: SHA-256 hash of contract content
signer_id: ID of the signing party
signer_type: Type of signer
signature: Base64-encoded signature to verify
Returns:
True if signature is valid, False otherwise
"""
# Create signing message
signing_message = f"{contract_id}:{content_hash}:{signer_id}:{signer_type}"
return self._signer.verify(signing_message, signature)
def get_public_key_b64(self) -> str:
"""Get base64-encoded public key."""
return self._signer.get_public_key_b64()
def get_private_key_b64(self) -> str:
"""Get base64-encoded private key."""
return self._signer.get_private_key_b64()
def generate_key_pair() -> Tuple[str, str]:
"""Generate a new Ed25519 key pair.
Returns:
Tuple of (public_key_b64, private_key_b64)
"""
signer = Ed25519Signer()
return signer.get_key_pair_b64()
def verify_signature(public_key_b64: str, message: str, signature: str) -> bool:
"""Verify a signature with a public key.
Args:
public_key_b64: Base64-encoded public key
message: Original message
signature: Base64-encoded signature
Returns:
True if signature is valid, False otherwise
"""
try:
signer = Ed25519Signer.from_public_key_b64(public_key_b64)
return signer.verify(message, signature)
except CryptoError:
return False