From 2c0463c079396e54872e6628760fe041a06c9bba Mon Sep 17 00:00:00 2001 From: Spencer Miller Date: Sun, 18 Aug 2024 10:35:56 +0300 Subject: [PATCH 1/4] update string encryption/decryption --- README.md | 39 +++++++++++++++++++++++------- coti/crypto_utils.py | 56 ++++++++++++++++++++++++++++++-------------- 2 files changed, 69 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index ad5816c..73c8f67 100644 --- a/README.md +++ b/README.md @@ -223,7 +223,30 @@ int_cipher_text, signature = build_input_text(plaintext, user_aes_key, sender, c - `int_cipher_text`: The integer representation of the ciphertext. - `signature`: The generated signature. -### 8. `generate_rsa_keypair()` +### 8. `build_string_input_text(plaintext, user_aes_key, sender, contract, func_sig, signing_key)` + +**Purpose:** Builds input text by encrypting the plaintext and signing it. + +**Usage:** + +```python +int_cipher_text, signature = build_string_input_text(plaintext, user_aes_key, sender, contract, func_sig, signing_key) +``` + +**Parameters:** + +- `plaintext`: The plaintext message. +- `user_aes_key`: The user's AES key. +- `sender`: The sender's address. +- `contract`: The contract address. +- `func_sig`: The function signature. +- `signing_key`: The private key used for signing. + +**Returns:** + +- `input_text`: A dictionary of the form { "ciphertext": { "value": int[] }, "signature": bytes[] } + +### 9. `generate_rsa_keypair()` **Purpose:** Generates an RSA key pair. @@ -238,7 +261,7 @@ private_key_bytes, public_key_bytes = generate_rsa_keypair() - `private_key_bytes`: The serialized private key. - `public_key_bytes`: The serialized public key. -### 9. `encrypt_rsa(public_key_bytes, plaintext)` +### 10. `encrypt_rsa(public_key_bytes, plaintext)` **Purpose:** Encrypts plaintext using RSA encryption with a provided public key. @@ -257,7 +280,7 @@ ciphertext = encrypt_rsa(public_key_bytes, plaintext) - `ciphertext`: The encrypted message. -### 10. `decrypt_rsa(private_key_bytes, ciphertext)` +### 11. `decrypt_rsa(private_key_bytes, ciphertext)` **Purpose:** Decrypts ciphertext using RSA decryption with a provided private key. @@ -276,7 +299,7 @@ plaintext = decrypt_rsa(private_key_bytes, ciphertext) - `plaintext`: The decrypted message. -### 11. `keccak256(data)` +### 12. `keccak256(data)` **Purpose:** Computes the Keccak-256 hash of the provided data. @@ -294,7 +317,7 @@ hash_value = keccak256(data) - `hash_value`: The computed hash. -### 12. `get_func_sig(function_signature)` +### 13. `get_func_sig(function_signature)` **Purpose:** Computes the function signature hash using Keccak-256. @@ -312,7 +335,7 @@ func_sig_hash = get_func_sig(function_signature) - `func_sig_hash`: The first 4 bytes of the computed hash. -### 13. `decrypt_uint(ciphertext, user_key)` +### 14. `decrypt_uint(ciphertext, user_key)` **Purpose:** Decrypts a value stored in a contract using a user key @@ -331,7 +354,7 @@ plaintext = decrypt_uint(ciphertext, user_key) - `result`: The decrypted value. -### 14. `decrypt_string(ciphertext, user_key)` +### 15. `decrypt_string(ciphertext, user_key)` **Purpose:** Decrypts a value stored in a contract using a user key @@ -343,7 +366,7 @@ plaintext = decrypt_string(ciphertext, user_key) **Parameters:** -- `ciphertext`: The value to be decrypted. +- `ciphertext`: A dictionary of the form { "value": int[] } where each cell holds up to 8 characters (padded at the end with zeroes) encrypted - `userKey`: The user's AES key. **Returns:** diff --git a/coti/crypto_utils.py b/coti/crypto_utils.py index 804d2ce..8714746 100644 --- a/coti/crypto_utils.py +++ b/coti/crypto_utils.py @@ -1,6 +1,3 @@ -import binascii -from array import array - from Crypto.Cipher import AES from Crypto.Hash import keccak from Crypto.Random import get_random_bytes @@ -9,6 +6,7 @@ from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.asymmetric import rsa from eth_keys import keys +from math import ceil block_size = AES.block_size address_size = 20 @@ -24,6 +22,7 @@ def encrypt(key, plaintext): # Ensure key size is 128 bits (16 bytes) if len(key) != block_size: + print(len(key), block_size) raise ValueError("Key size must be 128 bits.") # Create a new AES cipher block using the provided key @@ -126,14 +125,34 @@ def build_input_text(plaintext, user_aes_key, sender, contract, func_sig, signin def build_string_input_text(plaintext, user_aes_key, sender, contract, func_sig, signing_key): - encoded_plaintext = array('B', plaintext.encode('utf-8')) - encrypted_str = [{'ciphertext': 0, 'signature': b''} for _ in range(len(encoded_plaintext))] - for i in range(len(encoded_plaintext)): - ct_int, signature = build_input_text(int(encoded_plaintext[i]), user_aes_key, sender, contract, - func_sig, signing_key) - encrypted_str[i] = {'ciphertext': ct_int, 'signature': signature} + input_text = { + 'ciphertext': { + 'value': [] + }, + 'signature': [] + } + + encoded_plaintext = bytearray(list(plaintext.encode('utf-8'))) + + for i in range(ceil(len(encoded_plaintext) / 8)): + start_idx = i * 8 + end_idx = min(start_idx + 8, len(encoded_plaintext)) + + byte_arr = encoded_plaintext[start_idx:end_idx] + bytearray(8 - (end_idx - start_idx)) + + ct_int, sig = build_input_text( + int.from_bytes(byte_arr, 'big'), + user_aes_key, + sender, + contract, + func_sig, + signing_key + ) - return encrypted_str + input_text['ciphertext']['value'].append(ct_int) + input_text['signature'].append(sig) + + return input_text def decrypt_uint(ciphertext, user_key): @@ -154,18 +173,19 @@ def decrypt_uint(ciphertext, user_key): def decrypt_string(ciphertext, user_key): - string_from_input_tx = "" - for input_text_from_tx in ciphertext: - decrypted_input_from_tx = decrypt_uint(input_text_from_tx, user_key) - byte_length = (decrypted_input_from_tx.bit_length() + 7) // 8 # calculate the byte length + decrypted_string = "" + + for value in ciphertext['value']: + decrypted = decrypt_uint(value, user_key) + byte_length = (decrypted.bit_length() + 7) // 8 # calculate the byte length # Convert the integer to bytes - decrypted_bytes = decrypted_input_from_tx.to_bytes(byte_length, byteorder='big') + decrypted_bytes = decrypted.to_bytes(byte_length, byteorder='big') # Decode the bytes to a string - string_from_input_tx += decrypted_bytes.decode('utf-8') - - return string_from_input_tx + decrypted_string += decrypted_bytes.decode('utf-8') + + return decrypted_string.strip('\0') def generate_rsa_keypair(): From 9126ab942f0c82d422b8b322ca2625e63c6d5df5 Mon Sep 17 00:00:00 2001 From: Spencer Miller Date: Sun, 18 Aug 2024 17:45:25 +0300 Subject: [PATCH 2/4] remove function selector computation logic --- README.md | 78 +++++++------------------------------------- coti/crypto_utils.py | 56 ++++++++++++------------------- coti/utils.py | 8 ----- 3 files changed, 32 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index 73c8f67..45dcc3b 100644 --- a/README.md +++ b/README.md @@ -199,14 +199,14 @@ signature = sign(message, key) - `signature`: The generated signature. -### 7. `build_input_text(plaintext, user_aes_key, sender, contract, func_sig, signing_key)` +### 7. `build_input_text(plaintext, user_aes_key, sender, contract, func_selector, signing_key)` **Purpose:** Builds input text by encrypting the plaintext and signing it. **Usage:** ```python -int_cipher_text, signature = build_input_text(plaintext, user_aes_key, sender, contract, func_sig, signing_key) +int_cipher_text, signature = build_input_text(plaintext, user_aes_key, sender, contract, func_selector, signing_key) ``` **Parameters:** @@ -215,7 +215,7 @@ int_cipher_text, signature = build_input_text(plaintext, user_aes_key, sender, c - `user_aes_key`: The user's AES key. - `sender`: The sender's address. - `contract`: The contract address. -- `func_sig`: The function signature. +- `func_selector`: The function selector. - `signing_key`: The private key used for signing. **Returns:** @@ -223,14 +223,14 @@ int_cipher_text, signature = build_input_text(plaintext, user_aes_key, sender, c - `int_cipher_text`: The integer representation of the ciphertext. - `signature`: The generated signature. -### 8. `build_string_input_text(plaintext, user_aes_key, sender, contract, func_sig, signing_key)` +### 8. `build_string_input_text(plaintext, user_aes_key, sender, contract, func_selector, signing_key)` **Purpose:** Builds input text by encrypting the plaintext and signing it. **Usage:** ```python -int_cipher_text, signature = build_string_input_text(plaintext, user_aes_key, sender, contract, func_sig, signing_key) +int_cipher_text, signature = build_string_input_text(plaintext, user_aes_key, sender, contract, func_selector, signing_key) ``` **Parameters:** @@ -239,7 +239,7 @@ int_cipher_text, signature = build_string_input_text(plaintext, user_aes_key, se - `user_aes_key`: The user's AES key. - `sender`: The sender's address. - `contract`: The contract address. -- `func_sig`: The function signature. +- `func_selector`: The function selector. - `signing_key`: The private key used for signing. **Returns:** @@ -299,43 +299,7 @@ plaintext = decrypt_rsa(private_key_bytes, ciphertext) - `plaintext`: The decrypted message. -### 12. `keccak256(data)` - -**Purpose:** Computes the Keccak-256 hash of the provided data. - -**Usage:** - -```python -hash_value = keccak256(data) -``` - -**Parameters:** - -- `data`: The data to be hashed. - -**Returns:** - -- `hash_value`: The computed hash. - -### 13. `get_func_sig(function_signature)` - -**Purpose:** Computes the function signature hash using Keccak-256. - -**Usage:** - -```python -func_sig_hash = get_func_sig(function_signature) -``` - -**Parameters:** - -- `function_signature`: The function signature string. - -**Returns:** - -- `func_sig_hash`: The first 4 bytes of the computed hash. - -### 14. `decrypt_uint(ciphertext, user_key)` +### 12. `decrypt_uint(ciphertext, user_key)` **Purpose:** Decrypts a value stored in a contract using a user key @@ -354,7 +318,7 @@ plaintext = decrypt_uint(ciphertext, user_key) - `result`: The decrypted value. -### 15. `decrypt_string(ciphertext, user_key)` +### 13. `decrypt_string(ciphertext, user_key)` **Purpose:** Decrypts a value stored in a contract using a user key @@ -643,25 +607,7 @@ valid, gas_estimate = is_gas_units_estimation_valid(web3, tx) - `valid`: Boolean indicating if the gas units are sufficient. - `gas_estimate`: The estimated gas units. -### 16. `get_function_signature(function_abi)` - -**Purpose:** Generates the function signature from the ABI. - -**Usage:** - -```python -func_sig = get_function_signature(function_abi) -``` - -**Parameters:** - -- `function_abi`: The ABI of the function. - -**Returns:** - -- `func_sig`: The function signature. - -### 17. `deploy_contract(contract, kwargs, tx_params)` +### 16. `deploy_contract(contract, kwargs, tx_params)` **Purpose:** Deploys a contract with the given parameters. @@ -681,7 +627,7 @@ tx_receipt = deploy_contract(contract, kwargs, tx_params) - `tx_receipt`: The transaction receipt. -### 18. `exec_func_via_transaction(func, tx_params)` +### 17. `exec_func_via_transaction(func, tx_params)` **Purpose:** Executes a contract function via a transaction. @@ -700,7 +646,7 @@ tx_receipt = exec_func_via_transaction(func, tx_params) - `tx_receipt`: The transaction receipt. -### 19. `sign_and_send_tx(web3, private_key, transaction)` +### 18. `sign_and_send_tx(web3, private_key, transaction)` **Purpose:** Signs and sends a transaction. @@ -720,7 +666,7 @@ tx_receipt = sign_and_send_tx(web3, private_key, transaction) - `tx_receipt`: The transaction receipt. -### 20. `decrypt_value(contract_value, user_key)` +### 19. `decrypt_value(contract_value, user_key)` **Purpose:** Decrypts a value stored in a contract using a user key. diff --git a/coti/crypto_utils.py b/coti/crypto_utils.py index 8714746..483d4f3 100644 --- a/coti/crypto_utils.py +++ b/coti/crypto_utils.py @@ -1,5 +1,4 @@ from Crypto.Cipher import AES -from Crypto.Hash import keccak from Crypto.Random import get_random_bytes from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import serialization @@ -10,7 +9,7 @@ block_size = AES.block_size address_size = 20 -func_sig_size = 4 +function_selector_size = 4 ct_size = 32 key_size = 32 @@ -74,14 +73,16 @@ def generate_aes_key(): return key -def sign_input_text(sender, addr, func_sig, ct, key): +def sign_input_text(sender, addr, function_selector, ct, key): + function_selector_bytes = bytes.fromhex(function_selector[2:]) + # Ensure all input sizes are the correct length if len(sender) != address_size: raise ValueError(f"Invalid sender address length: {len(sender)} bytes, must be {address_size} bytes") if len(addr) != address_size: raise ValueError(f"Invalid contract address length: {len(addr)} bytes, must be {address_size} bytes") - if len(func_sig) != func_sig_size: - raise ValueError(f"Invalid signature size: {len(func_sig)} bytes, must be {func_sig_size} bytes") + if len(function_selector_bytes) != function_selector_size: + raise ValueError(f"Invalid signature size: {len(function_selector_bytes)} bytes, must be {function_selector_size} bytes") if len(ct) != ct_size: raise ValueError(f"Invalid ct length: {len(ct)} bytes, must be {ct_size} bytes") # Ensure the key is the correct length @@ -89,7 +90,7 @@ def sign_input_text(sender, addr, func_sig, ct, key): raise ValueError(f"Invalid key length: {len(key)} bytes, must be {key_size} bytes") # Create the message to be signed by appending all inputs - message = sender + addr + func_sig + ct + message = sender + addr + function_selector_bytes + ct return sign(message, key) @@ -102,7 +103,7 @@ def sign(message, key): return signature -def build_input_text(plaintext, user_aes_key, sender, contract, func_sig, signing_key): +def build_input_text(plaintext, user_aes_key, sender, contract, function_selector, signing_key): sender_address_bytes = bytes.fromhex(sender.address[2:]) contract_address_bytes = bytes.fromhex(contract.address[2:]) @@ -113,10 +114,8 @@ def build_input_text(plaintext, user_aes_key, sender, contract, func_sig, signin ciphertext, r = encrypt(user_aes_key, plaintext_bytes) ct = ciphertext + r - # Create the function signature - func_hash = get_func_sig(func_sig) # Sign the message - signature = sign_input_text(sender_address_bytes, contract_address_bytes, func_hash, ct, signing_key) + signature = sign_input_text(sender_address_bytes, contract_address_bytes, function_selector, ct, signing_key) # Convert the ct to an integer int_cipher_text = int.from_bytes(ct, byteorder='big') @@ -124,7 +123,7 @@ def build_input_text(plaintext, user_aes_key, sender, contract, func_sig, signin return int_cipher_text, signature -def build_string_input_text(plaintext, user_aes_key, sender, contract, func_sig, signing_key): +def build_string_input_text(plaintext, user_aes_key, sender, contract, function_selector, signing_key): input_text = { 'ciphertext': { 'value': [] @@ -145,7 +144,7 @@ def build_string_input_text(plaintext, user_aes_key, sender, contract, func_sig, user_aes_key, sender, contract, - func_sig, + function_selector, signing_key ) @@ -173,10 +172,18 @@ def decrypt_uint(ciphertext, user_key): def decrypt_string(ciphertext, user_key): + if 'value' in ciphertext or hasattr(ciphertext, 'value'): # format when reading ciphertext from an event + __ciphertext = ciphertext['value'] + elif isinstance(ciphertext, tuple): # format when reading ciphertext from state variable + __ciphertext = ciphertext[0] + else: + raise RuntimeError('Unrecognized ciphertext format') + decrypted_string = "" - for value in ciphertext['value']: + for value in __ciphertext: decrypted = decrypt_uint(value, user_key) + byte_length = (decrypted.bit_length() + 7) // 8 # calculate the byte length # Convert the integer to bytes @@ -225,26 +232,3 @@ def decrypt_rsa(private_key_bytes, ciphertext): ) ) return plaintext - - -# Function to compute Keccak-256 hash -def keccak256(data): - # Create Keccak-256 hash object - hash_obj = keccak.new(digest_bits=256) - - # Update hash object with data - hash_obj.update(data) - - # Compute hash and return - return hash_obj.digest() - - -def get_func_sig(function_signature): - # Convert function signature to bytes - function_signature_bytes = function_signature.encode('utf-8') - - # Compute Keccak-256 hash on the function signature - function_signature_bytes_hash = keccak256(function_signature_bytes) - - # Take first 4 bytes of the hash - return function_signature_bytes_hash[:4] diff --git a/coti/utils.py b/coti/utils.py index 932fc3d..6e7149e 100644 --- a/coti/utils.py +++ b/coti/utils.py @@ -111,14 +111,6 @@ def is_gas_units_estimation_valid(web3, tx): return False, estimate_gas -def get_function_signature(function_abi): - # Extract the input types from the ABI - input_types = ','.join([param['type'] for param in function_abi.get('inputs', [])]) - - # Generate the function signature - return f"{function_abi['name']}({input_types})" - - def deploy_contract(contract, kwargs, tx_params): func = contract.constructor(**kwargs) return exec_func_via_transaction(func, tx_params) From 5a24b5df85dea21ff8bcecfd988c120dd5c446ad Mon Sep 17 00:00:00 2001 From: Spencer Miller Date: Sun, 29 Sep 2024 09:14:09 +0300 Subject: [PATCH 3/4] update uint encryption --- coti/crypto_utils.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/coti/crypto_utils.py b/coti/crypto_utils.py index 483d4f3..435ad85 100644 --- a/coti/crypto_utils.py +++ b/coti/crypto_utils.py @@ -5,7 +5,6 @@ from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.asymmetric import rsa from eth_keys import keys -from math import ceil block_size = AES.block_size address_size = 20 @@ -21,7 +20,6 @@ def encrypt(key, plaintext): # Ensure key size is 128 bits (16 bytes) if len(key) != block_size: - print(len(key), block_size) raise ValueError("Key size must be 128 bits.") # Create a new AES cipher block using the provided key @@ -120,7 +118,10 @@ def build_input_text(plaintext, user_aes_key, sender, contract, function_selecto # Convert the ct to an integer int_cipher_text = int.from_bytes(ct, byteorder='big') - return int_cipher_text, signature + return { + 'ciphertext': int_cipher_text, + 'signature': signature + } def build_string_input_text(plaintext, user_aes_key, sender, contract, function_selector, signing_key): @@ -133,13 +134,12 @@ def build_string_input_text(plaintext, user_aes_key, sender, contract, function_ encoded_plaintext = bytearray(list(plaintext.encode('utf-8'))) - for i in range(ceil(len(encoded_plaintext) / 8)): - start_idx = i * 8 + for start_idx in range(0, len(encoded_plaintext), 8): end_idx = min(start_idx + 8, len(encoded_plaintext)) byte_arr = encoded_plaintext[start_idx:end_idx] + bytearray(8 - (end_idx - start_idx)) - ct_int, sig = build_input_text( + it_int = build_input_text( int.from_bytes(byte_arr, 'big'), user_aes_key, sender, @@ -148,8 +148,8 @@ def build_string_input_text(plaintext, user_aes_key, sender, contract, function_ signing_key ) - input_text['ciphertext']['value'].append(ct_int) - input_text['signature'].append(sig) + input_text['ciphertext']['value'].append(it_int['ciphertext']) + input_text['signature'].append(it_int['signature']) return input_text From ba8c6444bf1e6511639f26a1bd839f96182bc0f5 Mon Sep 17 00:00:00 2001 From: Spencer Miller Date: Sun, 29 Sep 2024 09:22:15 +0300 Subject: [PATCH 4/4] add support for ctAddress --- README.md | 52 ++++++++++++++++++++++++++++++---- coti/crypto_utils.py | 67 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 45dcc3b..7863f60 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,30 @@ int_cipher_text, signature = build_string_input_text(plaintext, user_aes_key, se - `input_text`: A dictionary of the form { "ciphertext": { "value": int[] }, "signature": bytes[] } -### 9. `generate_rsa_keypair()` +### 9. `build_address_input_text(plaintext, user_aes_key, sender, contract, func_selector, signing_key)` + +**Purpose:** Builds input text by encrypting the plaintext and signing it. + +**Usage:** + +```python +int_cipher_text, signature = build_address_input_text(plaintext, user_aes_key, sender, contract, func_selector, signing_key) +``` + +**Parameters:** + +- `plaintext`: The plaintext message. +- `user_aes_key`: The user's AES key. +- `sender`: The sender's address. +- `contract`: The contract address. +- `func_selector`: The function selector. +- `signing_key`: The private key used for signing. + +**Returns:** + +- `input_text`: A dictionary of the form { "ciphertext": { "ct1": int, "ct2": int, "ct3": int }, "signature1": bytes, "signature2": bytes, "signature3": bytes } + +### 10. `generate_rsa_keypair()` **Purpose:** Generates an RSA key pair. @@ -261,7 +284,7 @@ private_key_bytes, public_key_bytes = generate_rsa_keypair() - `private_key_bytes`: The serialized private key. - `public_key_bytes`: The serialized public key. -### 10. `encrypt_rsa(public_key_bytes, plaintext)` +### 11. `encrypt_rsa(public_key_bytes, plaintext)` **Purpose:** Encrypts plaintext using RSA encryption with a provided public key. @@ -280,7 +303,7 @@ ciphertext = encrypt_rsa(public_key_bytes, plaintext) - `ciphertext`: The encrypted message. -### 11. `decrypt_rsa(private_key_bytes, ciphertext)` +### 12. `decrypt_rsa(private_key_bytes, ciphertext)` **Purpose:** Decrypts ciphertext using RSA decryption with a provided private key. @@ -299,7 +322,7 @@ plaintext = decrypt_rsa(private_key_bytes, ciphertext) - `plaintext`: The decrypted message. -### 12. `decrypt_uint(ciphertext, user_key)` +### 13. `decrypt_uint(ciphertext, user_key)` **Purpose:** Decrypts a value stored in a contract using a user key @@ -318,7 +341,7 @@ plaintext = decrypt_uint(ciphertext, user_key) - `result`: The decrypted value. -### 13. `decrypt_string(ciphertext, user_key)` +### 14. `decrypt_string(ciphertext, user_key)` **Purpose:** Decrypts a value stored in a contract using a user key @@ -337,6 +360,25 @@ plaintext = decrypt_string(ciphertext, user_key) - `result`: The decrypted value. +### 15. `decrypt_address(ciphertext, user_key)` + +**Purpose:** Decrypts a value stored in a contract and encrypted using a user key + +**Usage:** + +```python +plaintext = decrypt_string(ciphertext, user_key) +``` + +**Parameters:** + +- `ciphertext`: A dictionary of the form { "ct1": int, "ct2": int, "ct3": int } where each cell holds a portion of the address encrypted +- `userKey`: The user's AES key. + +**Returns:** + +- `result`: The decrypted address. + # Utilities (utils.py) Functions ### 1. `web3_connected(web3)` diff --git a/coti/crypto_utils.py b/coti/crypto_utils.py index 435ad85..b3f3ba6 100644 --- a/coti/crypto_utils.py +++ b/coti/crypto_utils.py @@ -5,6 +5,7 @@ from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.asymmetric import rsa from eth_keys import keys +from web3 import Web3 block_size = AES.block_size address_size = 20 @@ -153,6 +154,50 @@ def build_string_input_text(plaintext, user_aes_key, sender, contract, function_ return input_text +def build_address_input_text(plaintext, user_aes_key, sender, contract, function_selector, signing_key): + if not Web3.is_address(plaintext): + raise ValueError("Plaintext must be a valid address.") + + it_int_1 = build_input_text( + int(plaintext[2:18], 16), # bytes 1 - 8 + user_aes_key, + sender, + contract, + function_selector, + signing_key + ) + + it_int_2 = build_input_text( + int(plaintext[18:34], 16), # bytes 9 - 16 + user_aes_key, + sender, + contract, + function_selector, + signing_key + ) + + it_int_3 = build_input_text( + int(plaintext[34:42], 16), # bytes 17 - 20 + user_aes_key, + sender, + contract, + function_selector, + signing_key + ) + + input_text = { + 'ciphertext': { + 'ct1': it_int_1['ciphertext'], + 'ct2': it_int_2['ciphertext'], + 'ct3': it_int_3['ciphertext'] + }, + 'signature1': it_int_1['signature'], + 'signature2': it_int_2['signature'], + 'signature3': it_int_3['signature'] + } + + return input_text + def decrypt_uint(ciphertext, user_key): # Convert ct to bytes (big-endian) @@ -194,6 +239,28 @@ def decrypt_string(ciphertext, user_key): return decrypted_string.strip('\0') +def decrypt_address(ciphertext, user_key): + if isinstance(ciphertext, list): # format when reading ciphertext from a state variable + __ciphertext = ciphertext + else: # format when reading ciphertext from an event + __ciphertext = list(ciphertext.values()) + + addr = '0x' + + decrypted = decrypt_uint(__ciphertext[0], user_key) + + addr += hex(decrypted)[2:].rjust(16, '0') # 8 bytes is 16 characters + + decrypted = decrypt_uint(__ciphertext[1], user_key) + + addr += hex(decrypted)[2:].rjust(16, '0') # 8 bytes is 16 characters + + decrypted = decrypt_uint(__ciphertext[2], user_key) + + addr += hex(decrypted)[2:].rjust(8, '0') # 4 bytes is 8 characters + + return Web3.to_checksum_address(addr) + def generate_rsa_keypair(): # Generate RSA key pair