55from cryptography .hazmat .primitives .asymmetric import padding
66from cryptography .hazmat .primitives .asymmetric import rsa
77from eth_keys import keys
8- from .types import CtString , CtUint , ItString , ItUint
8+ from .types import CtUint256 , ItUint256 , CtString , CtUint , ItString , ItUint
99
1010block_size = AES .block_size
1111address_size = 20
@@ -94,6 +94,46 @@ def sign_input_text(sender_address: str, contract_address: str, function_selecto
9494 return sign (message , key )
9595
9696
97+ def sign_input_text_256 (sender_address : str , contract_address : str , function_selector : str , ct , key ):
98+ """
99+ Sign input text for 256-bit encrypted values.
100+
101+ Similar to sign_input_text but accepts 64-byte ciphertext (CtUint256 blob).
102+
103+ Args:
104+ sender_address: Address of the sender (20 bytes, without 0x prefix)
105+ contract_address: Address of the contract (20 bytes, without 0x prefix)
106+ function_selector: Function selector (hex string with 0x prefix, e.g., '0x12345678')
107+ ct: Ciphertext bytes (must be 64 bytes for uint256)
108+ key: Signing key (32 bytes)
109+
110+ Returns:
111+ bytes: The signature
112+
113+ Raises:
114+ ValueError: If any input has invalid length
115+ """
116+ function_selector_bytes = bytes .fromhex (function_selector [2 :])
117+
118+ if len (sender_address ) != address_size :
119+ raise ValueError (f"Invalid sender address length: { len (sender_address )} bytes, must be { address_size } bytes" )
120+ if len (contract_address ) != address_size :
121+ raise ValueError (f"Invalid contract address length: { len (contract_address )} bytes, must be { address_size } bytes" )
122+ if len (function_selector_bytes ) != function_selector_size :
123+ raise ValueError (f"Invalid signature size: { len (function_selector_bytes )} bytes, must be { function_selector_size } bytes" )
124+
125+ # 256-bit IT has 64 bytes CT
126+ if len (ct ) != 64 :
127+ raise ValueError (f"Invalid ct length: { len (ct )} bytes, must be 64 bytes for uint256" )
128+
129+ if len (key ) != key_size :
130+ raise ValueError (f"Invalid key length: { len (key )} bytes, must be { key_size } bytes" )
131+
132+ message = sender_address + contract_address + function_selector_bytes + ct
133+
134+ return sign (message , key )
135+
136+
97137def sign (message , key ):
98138 # Sign the message
99139 pk = keys .PrivateKey (key )
@@ -122,7 +162,7 @@ def build_input_text(plaintext: int, user_aes_key: str, sender_address: str, con
122162 }
123163
124164
125- def build_string_input_text (plaintext : int , user_aes_key : str , sender_address : str , contract_address : str , function_selector : str , signing_key : str ) -> ItString :
165+ def build_string_input_text (plaintext : str , user_aes_key : str , sender_address : str , contract_address : str , function_selector : str , signing_key : str ) -> ItString :
126166 input_text = {
127167 'ciphertext' : {
128168 'value' : []
@@ -169,6 +209,130 @@ def decrypt_uint(ciphertext: CtUint, user_aes_key: str) -> int:
169209 return decrypted_uint
170210
171211
212+ def create_ciphertext_256 (plaintext_int : int , user_aes_key_bytes : bytes ) -> bytes :
213+ """
214+ Create a 256-bit ciphertext by encrypting high and low 128-bit parts separately.
215+
216+ Args:
217+ plaintext_int: Integer value to encrypt (must fit in 256 bits)
218+ user_aes_key_bytes: AES encryption key (16 bytes)
219+
220+ Returns:
221+ bytes: 64-byte ciphertext blob formatted as:
222+ [high_ciphertext(16) | high_r(16) | low_ciphertext(16) | low_r(16)]
223+
224+ Raises:
225+ ValueError: If plaintext exceeds 256 bits
226+ """
227+ # Convert 256-bit int to 32 bytes (Big Endian)
228+ try :
229+ plaintext_bytes = plaintext_int .to_bytes (32 , 'big' )
230+ except OverflowError :
231+ raise ValueError ("Plaintext size must be 256 bits or smaller." )
232+
233+ # Split into High and Low 128-bit parts
234+ high_bytes = plaintext_bytes [:16 ]
235+ low_bytes = plaintext_bytes [16 :]
236+
237+ # Encrypt High
238+ high_ct , high_r = encrypt (user_aes_key_bytes , high_bytes )
239+
240+ # Encrypt Low
241+ low_ct , low_r = encrypt (user_aes_key_bytes , low_bytes )
242+
243+ # Construct format: high.ciphertext + high.r + low.ciphertext + low.r
244+ return high_ct + high_r + low_ct + low_r
245+
246+
247+ def prepare_it_256 (plaintext : int , user_aes_key : str , sender_address : str , contract_address : str , function_selector : str , signing_key : str ) -> ItUint256 :
248+ """
249+ Prepare Input Text for a 256-bit encrypted integer.
250+
251+ Encrypts a 256-bit integer and creates a signed Input Text structure
252+ suitable for smart contract interaction.
253+
254+ Args:
255+ plaintext: Integer value to encrypt (must fit in 256 bits)
256+ user_aes_key: AES encryption key (hex string without 0x prefix, 32 hex chars)
257+ sender_address: Address of the sender (hex string with 0x prefix, 40 hex chars)
258+ contract_address: Address of the contract (hex string with 0x prefix, 40 hex chars)
259+ function_selector: Function selector (hex string with 0x prefix, e.g., '0x12345678')
260+ signing_key: Private key for signing (32 bytes)
261+
262+ Returns:
263+ ItUint256: Dictionary containing ciphertext (ciphertextHigh, ciphertextLow) and signature
264+
265+ Raises:
266+ ValueError: If plaintext exceeds 256 bits
267+ """
268+ if plaintext .bit_length () > 256 :
269+ raise ValueError ("Plaintext size must be 256 bits or smaller." )
270+
271+ user_aes_key_bytes = bytes .fromhex (user_aes_key )
272+
273+ ct_blob = create_ciphertext_256 (plaintext , user_aes_key_bytes )
274+
275+ # Split for types
276+ # ct_blob is 64 bytes: [high_ct(16) | high_r(16) | low_ct(16) | low_r(16)]
277+ high_blob = ct_blob [:32 ]
278+ low_blob = ct_blob [32 :]
279+
280+ # Sign the full 64-byte blob
281+ signature = sign_input_text_256 (
282+ bytes .fromhex (sender_address [2 :]),
283+ bytes .fromhex (contract_address [2 :]),
284+ function_selector ,
285+ ct_blob ,
286+ signing_key
287+ )
288+
289+ return {
290+ 'ciphertext' : {
291+ 'ciphertextHigh' : int .from_bytes (high_blob , 'big' ),
292+ 'ciphertextLow' : int .from_bytes (low_blob , 'big' )
293+ },
294+ 'signature' : signature
295+ }
296+
297+
298+ def decrypt_uint256 (ciphertext : CtUint256 , user_aes_key : str ) -> int :
299+ """
300+ Decrypt a 256-bit encrypted integer.
301+
302+ Decrypts both high and low 128-bit parts and combines them back into
303+ a single 256-bit integer.
304+
305+ Args:
306+ ciphertext: CtUint256 dictionary containing ciphertextHigh and ciphertextLow
307+ user_aes_key: AES decryption key (hex string without 0x prefix, 32 hex chars)
308+
309+ Returns:
310+ int: The decrypted 256-bit integer value
311+ """
312+ user_aes_key_bytes = bytes .fromhex (user_aes_key )
313+
314+ # Process High
315+ ct_high_int = ciphertext ['ciphertextHigh' ]
316+ ct_high_bytes = ct_high_int .to_bytes (32 , 'big' )
317+ cipher_high = ct_high_bytes [:block_size ]
318+ r_high = ct_high_bytes [block_size :]
319+
320+ plaintext_high = decrypt (user_aes_key_bytes , r_high , cipher_high )
321+
322+ # Process Low
323+ ct_low_int = ciphertext ['ciphertextLow' ]
324+ ct_low_bytes = ct_low_int .to_bytes (32 , 'big' )
325+ cipher_low = ct_low_bytes [:block_size ]
326+ r_low = ct_low_bytes [block_size :]
327+
328+ plaintext_low = decrypt (user_aes_key_bytes , r_low , cipher_low )
329+
330+ # Combine back to 256-bit int
331+ # High part is MSB
332+ full_bytes = plaintext_high + plaintext_low
333+ return int .from_bytes (full_bytes , 'big' )
334+
335+
172336def decrypt_string (ciphertext : CtString , user_aes_key : str ) -> str :
173337 if 'value' in ciphertext or hasattr (ciphertext , 'value' ): # format when reading ciphertext from an event
174338 __ciphertext = ciphertext ['value' ]
@@ -231,6 +395,7 @@ def decrypt_rsa(private_key_bytes: bytes, ciphertext: bytes):
231395 )
232396 return plaintext
233397
398+
234399#This function recovers a user's key by decrypting two encrypted key shares with the given private key,
235400#and then XORing the two key shares together.
236401def recover_user_key (private_key_bytes : bytes , encrypted_key_share0 : bytes , encrypted_key_share1 : bytes ):
@@ -239,3 +404,4 @@ def recover_user_key(private_key_bytes: bytes, encrypted_key_share0: bytes, encr
239404
240405 # XOR both key shares to get the user key
241406 return bytes ([a ^ b for a , b in zip (key_share0 , key_share1 )])
407+
0 commit comments