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