Spaces:
Running
Running
File size: 7,388 Bytes
4d56236 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | """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
|