Spaces:
Running
Running
| """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 | |
| 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}") | |
| 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 | |