From 2c0463c079396e54872e6628760fe041a06c9bba Mon Sep 17 00:00:00 2001 From: Spencer Miller Date: Sun, 18 Aug 2024 10:35:56 +0300 Subject: [PATCH 1/5] 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/5] 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/5] 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 e8c9fe518e7930804aae9834a0fdddf4411bfcff Mon Sep 17 00:00:00 2001 From: Guy Mesika Date: Sun, 6 Oct 2024 23:53:24 +0300 Subject: [PATCH 4/5] update upgrade --- README.md | 683 +++++++++++++++--------------------------------------- 1 file changed, 183 insertions(+), 500 deletions(-) diff --git a/README.md b/README.md index 45dcc3b..7115792 100644 --- a/README.md +++ b/README.md @@ -86,603 +86,286 @@ devnet 0x71C7656EC7ab88b098defB751B7401B5f6d8976F ## Libraries -There are two libraries located in the [libs](/libs/) folder that will allow you to interact with the COTI network. +There are two libraries located in the [coti](/coti/) folder that will allow you to interact with the COTI network. -# Crypto Utilities (crypto_utils.py) Functions +# Crypto Utilities (crypto_utils.py) +## Functions -### 1. `encrypt(key, plaintext)` +### `encrypt(key, plaintext)` -**Purpose:** Encrypts a plaintext message using AES encryption with a provided key. +Encrypts a 128-bit `plaintext` using the provided 128-bit AES `key` and ECB mode. -**Usage:** +- **Parameters:** + - `key (bytes)`: 128-bit AES key (16 bytes). + - `plaintext (bytes)`: 128-bit or smaller plaintext to encrypt. -```python -ciphertext, r = encrypt(key, plaintext) -``` - -**Parameters:** - -- `key`: A 128-bit (16-byte) AES key. -- `plaintext`: The plaintext message to be encrypted. Must be 128 bits (16 bytes) or smaller. - -**Returns:** - -- `ciphertext`: The encrypted message. -- `r`: The random value used during encryption. - -### 2. `decrypt(key, r, ciphertext)` - -**Purpose:** Decrypts a ciphertext message using AES decryption with a provided key and random value. - -**Usage:** - -```python -plaintext = decrypt(key, r, ciphertext) -``` - -**Parameters:** - -- `key`: A 128-bit (16-byte) AES key. -- `r`: The random value used during encryption. -- `ciphertext`: The encrypted message to be decrypted. - -**Returns:** - -- `plaintext`: The decrypted message. - -### 3. `generate_aes_key()` - -**Purpose:** Generates a random 128-bit AES key. - -**Usage:** - -```python -key = generate_aes_key() -``` - -**Returns:** - -- `key`: The generated 128-bit AES key. - -### 4. `generate_ECDSA_private_key()` - -**Purpose:** Generates a new ECDSA private key. - -**Usage:** - -```python -private_key = generate_ECDSA_private_key() -``` - -**Returns:** - -- `private_key`: The raw bytes of the ECDSA private key. - -### 5. `signIT(sender, addr, func_sig, ct, key)` - -**Purpose:** Signs a message composed of various inputs using a private key. - -**Usage:** - -```python -signature = signIT(sender, addr, func_sig, ct, key) -``` - -**Parameters:** - -- `sender`: The sender's address. -- `addr`: The contract address. -- `func_sig`: The function signature. -- `ct`: The ciphertext. -- `key`: The private key used for signing. - -**Returns:** - -- `signature`: The generated signature. - -### 6. `sign(message, key)` - -**Purpose:** Signs a message using a private key. - -**Usage:** - -```python -signature = sign(message, key) -``` - -**Parameters:** - -- `message`: The message to be signed. -- `key`: The private key used for signing. - -**Returns:** - -- `signature`: The generated signature. - -### 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_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:** - -- `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_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_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": { "value": int[] }, "signature": bytes[] } - -### 9. `generate_rsa_keypair()` - -**Purpose:** Generates an RSA key pair. - -**Usage:** - -```python -private_key_bytes, public_key_bytes = generate_rsa_keypair() -``` - -**Returns:** - -- `private_key_bytes`: The serialized private key. -- `public_key_bytes`: The serialized public key. - -### 10. `encrypt_rsa(public_key_bytes, plaintext)` - -**Purpose:** Encrypts plaintext using RSA encryption with a provided public key. - -**Usage:** - -```python -ciphertext = encrypt_rsa(public_key_bytes, plaintext) -``` - -**Parameters:** - -- `public_key_bytes`: The serialized public key. -- `plaintext`: The plaintext message to be encrypted. - -**Returns:** - -- `ciphertext`: The encrypted message. - -### 11. `decrypt_rsa(private_key_bytes, ciphertext)` - -**Purpose:** Decrypts ciphertext using RSA decryption with a provided private key. - -**Usage:** - -```python -plaintext = decrypt_rsa(private_key_bytes, ciphertext) -``` - -**Parameters:** - -- `private_key_bytes`: The serialized private key. -- `ciphertext`: The encrypted message to be decrypted. - -**Returns:** - -- `plaintext`: The decrypted message. - -### 12. `decrypt_uint(ciphertext, user_key)` +- **Returns:** + - `ciphertext (bytes)`: The encrypted text. + - `r (bytes)`: A random value used during encryption. -**Purpose:** Decrypts a value stored in a contract using a user key +### `decrypt(key, r, ciphertext)` -**Usage:** +Decrypts the `ciphertext` using the provided 128-bit AES `key` and the random value `r`. -```python -plaintext = decrypt_uint(ciphertext, user_key) -``` - -**Parameters:** - -- `ciphertext`: The value to be decrypted. -- `userKey`: The user's AES key. - -**Returns:** - -- `result`: The decrypted value. - -### 13. `decrypt_string(ciphertext, user_key)` - -**Purpose:** Decrypts a value stored in a contract using a user key - -**Usage:** - -```python -plaintext = decrypt_string(ciphertext, user_key) -``` - -**Parameters:** - -- `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:** - -- `result`: The decrypted value. - -# Utilities (utils.py) Functions - -### 1. `web3_connected(web3)` - -**Purpose:** Checks if the Web3 instance is connected. - -**Usage:** - -```python -connected = web3_connected(web3) -``` - -**Parameters:** - -- `web3`: An instance of Web3. - -**Returns:** - -- `connected`: Boolean indicating if Web3 is connected. - -### 2. `print_network_details(web3)` - -**Purpose:** Prints the network details of the Web3 instance. - -**Usage:** - -```python -print_network_details(web3) -``` - -**Parameters:** - -- `web3`: An instance of Web3. - -### 3. `print_account_details(web3)` - -**Purpose:** Prints the account details of the default account in the Web3 instance. - -**Usage:** - -```python -print_account_details(web3) -``` - -**Parameters:** - -- `web3`: An instance of Web3. - -### 4. `init_web3(node_https_address, eoa, error_not_connected=True)` - -**Purpose:** Initializes the Web3 instance with the given node address and externally owned account (EOA). - -**Usage:** - -```python -web3 = init_web3(node_https_address, eoa) -``` - -**Parameters:** - -- `node_https_address`: The HTTPS address of the COTI node. -- `eoa`: The externally owned account. -- `error_not_connected`: Boolean indicating whether to raise an error if not connected. +- **Parameters:** + - `key (bytes)`: 128-bit AES key. + - `r (bytes)`: Random value used during encryption. + - `ciphertext (bytes)`: Encrypted text to decrypt. -**Returns:** +- **Returns:** + - `plaintext (bytes)`: The decrypted original message. -- `web3`: The initialized Web3 instance. +### `generate_aes_key()` -### 5. `get_eoa(account_private_key)` +Generates a random 128-bit AES key. -**Purpose:** Generates an externally owned account (EOA) from a private key. +- **Returns:** + - `key (bytes)`: Randomly generated 128-bit key (16 bytes). -**Usage:** +### `sign_input_text(sender, addr, function_selector, ct, key)` -```python -eoa = get_eoa(account_private_key) -``` - -**Parameters:** - -- `account_private_key`: The private key of the account. +Signs an input message composed of multiple parts, including sender address, contract address, function selector, and ciphertext. -**Returns:** +- **Parameters:** + - `sender (bytes)`: Sender address. + - `addr (bytes)`: Contract address. + - `function_selector (str)`: Ethereum function selector (in hex). + - `ct (bytes)`: Ciphertext (concatenated). + - `key (bytes)`: Private key for signing. -- `eoa`: The generated EOA. +- **Returns:** + - `signature (bytes)`: Digital signature of the message. -### 6. `validate_address(address)` +### `sign(message, key)` -**Purpose:** Validates and returns the checksum address for a given address. +Signs a `message` using the provided `key` (Ethereum-style private key). -**Usage:** +- **Parameters:** + - `message (bytes)`: Message to sign. + - `key (bytes)`: Private key to use for signing. -```python -result = validate_address(address) -``` +- **Returns:** + - `signature (bytes)`: The generated signature. -**Parameters:** +### `build_input_text(plaintext, user_aes_key, sender, contract, function_selector, signing_key)` -- `address`: The address to be validated. +Encrypts a plaintext integer and signs the resulting ciphertext along with other parameters. -**Returns:** +- **Parameters:** + - `plaintext (int)`: Integer to encrypt. + - `user_aes_key (bytes)`: AES key for encryption. + - `sender (object)`: Ethereum-like sender object (with `address` attribute). + - `contract (object)`: Ethereum-like contract object (with `address` attribute). + - `function_selector (str)`: Function selector (in hex). + - `signing_key (bytes)`: Signing key for signature. -- `result`: A dictionary with `valid` (boolean) and `safe` (checksum address). +- **Returns:** + - `dict`: Contains the `ciphertext` and `signature`. -### 7. `get_latest_block(web3)` +### `build_string_input_text(plaintext, user_aes_key, sender, contract, function_selector, signing_key)` -**Purpose:** Retrieves the latest block from the COTI network. +Encrypts and signs string-based input data, breaking it into 8-byte chunks. -**Usage:** +- **Parameters:** + - `plaintext (str)`: String to encrypt. + - `user_aes_key (bytes)`: AES key for encryption. + - `sender (object)`: Ethereum-like sender object. + - `contract (object)`: Ethereum-like contract object. + - `function_selector (str)`: Function selector (in hex). + - `signing_key (bytes)`: Signing key for signature. -```python -latest_block = get_latest_block(web3) -``` +- **Returns:** + - `dict`: Contains the `ciphertext` and `signature`. -**Parameters:** +### `decrypt_uint(ciphertext, user_key)` -- `web3`: An instance of Web3. +Decrypts a ciphertext into an unsigned integer using AES. -**Returns:** +- **Parameters:** + - `ciphertext (int)`: Ciphertext to decrypt (in integer format). + - `user_key (bytes)`: AES key to use for decryption. -- `latest_block`: The latest block object. +- **Returns:** + - `int`: The decrypted integer. -### 8. `get_nonce(web3)` +### `decrypt_string(ciphertext, user_key)` -**Purpose:** Retrieves the nonce for the default account. +Decrypts a ciphertext back into a string, handling multiple formats of ciphertext. -**Usage:** +- **Parameters:** + - `ciphertext (dict/tuple)`: Ciphertext to decrypt, can be in event or state variable format. + - `user_key (bytes)`: AES key to use for decryption. -```python -nonce = get_nonce(web3) -``` +- **Returns:** + - `str`: The decrypted string. -**Parameters:** +### `generate_rsa_keypair()` -- `web3`: An instance of Web3. +Generates an RSA key pair for encryption and decryption. -**Returns:** +- **Returns:** + - `private_key_bytes (bytes)`: Serialized private key. + - `public_key_bytes (bytes)`: Serialized public key. -- `nonce`: The nonce for the default account. +### `decrypt_rsa(private_key_bytes, ciphertext)` -### 9. `get_address_valid_and_checksum(address)` +Decrypts a ciphertext using RSA and a provided private key. -**Purpose:** Validates an address and returns the checksum address. +- **Parameters:** + - `private_key_bytes (bytes)`: Private key used for decryption. + - `ciphertext (bytes)`: Ciphertext to decrypt. -**Usage:** +- **Returns:** + - `plaintext (bytes)`: Decrypted plaintext. + -```python -result = get_address_valid_and_checksum(address) -``` +# Web3 Utilities (utils.py) +## Functions -**Parameters:** +### `web3_connected(web3)` -- `address`: The address to be validated. +Checks if the Web3 instance is connected to the blockchain node. -**Returns:** +- **Parameters:** + - `web3 (Web3)`: Web3 instance. -- `result`: A dictionary with `valid` (boolean) and `safe` (checksum address). +- **Returns:** + - `bool`: `True` if connected, otherwise `False`. -### 10. `address_valid(address)` +### `print_network_details(web3)` -**Purpose:** Checks if an address is valid. +Prints details about the connected network, such as the provider, chain ID, and the latest block hash. -**Usage:** +- **Parameters:** + - `web3 (Web3)`: Web3 instance. -```python -valid = address_valid(address) -``` +### `print_account_details(web3)` -**Parameters:** +Prints details about the default account, including its address, balance in Wei and Ether, and the account nonce. -- `address`: The address to be validated. +- **Parameters:** + - `web3 (Web3)`: Web3 instance. -**Returns:** +### `init_web3(node_https_address, eoa, error_not_connected=True)` -- `valid`: Boolean indicating if the address is valid. +Initializes the Web3 instance and sets up the default account. It also injects the Geth POA middleware for POA chains. -### 11. `get_native_balance(web3, address=None)` +- **Parameters:** + - `node_https_address (str)`: The Ethereum node's HTTPS address. + - `eoa (eth_account.Account)`: The account to set as the default. + - `error_not_connected (bool)`: If `True`, raises an error if the connection fails. -**Purpose:** Retrieves the native balance of an address. +- **Returns:** + - `web3 (Web3)`: Initialized Web3 instance. -**Usage:** +### `get_eoa(account_private_key)` -```python -balance = get_native_balance(web3, address) -``` +Creates an externally owned account (EOA) from a private key. -**Parameters:** +- **Parameters:** + - `account_private_key (str)`: The private key for the account. -- `web3`: An instance of Web3. -- `address`: The address to check the balance of (default is the default account). +- **Returns:** + - `eoa (eth_account.Account)`: The Ethereum account object. -**Returns:** +### `validate_address(address)` -- `balance`: The native balance in wei. +Validates if the given address is a valid Ethereum address and returns the checksum version of the address. -### 12. `load_contract(file_path)` +- **Parameters:** + - `address (str)`: The Ethereum address. -**Purpose:** Loads a Solidity contract source code from a file. +- **Returns:** + - `dict`: Contains `valid` (boolean) and `safe` (checksum address). -**Usage:** +### `get_latest_block(web3)` -```python -contract_code = load_contract(file_path) -``` +Retrieves the latest block on the blockchain. -**Parameters:** +- **Parameters:** + - `web3 (Web3)`: Web3 instance. -- `file_path`: Path to the Solidity source code file. +- **Returns:** + - `block (dict)`: The latest block. -**Returns:** +### `get_nonce(web3)` -- `contract_code`: The content of the file. +Retrieves the nonce (transaction count) for the default account. -### 13. `transfer_native(web3, recipient_address, private_key, amount_to_transfer_ether, native_gas_units)` +- **Parameters:** + - `web3 (Web3)`: Web3 instance. -**Purpose:** Transfers native cryptocurrency from the default account to a recipient address. +- **Returns:** + - `int`: The transaction count (nonce). -**Usage:** +### `get_native_balance(web3, address=None)` -```python -tx_receipt = transfer_native(web3, recipient_address, private_key, amount_to_transfer_ether, native_gas_units) -``` +Gets the native token balance (e.g., Ether) for a given address or the default account. -**Parameters:** +- **Parameters:** + - `web3 (Web3)`: Web3 instance. + - `address (str, optional)`: The Ethereum address. Defaults to the default account. -- `web3`: An instance of Web3. -- `recipient_address`: The address of the recipient. -- `private_key`: The private key of the sender. -- `amount_to_transfer_ether`: The amount of Ether to transfer. -- `native_gas_units`: The gas limit for the transaction. +- **Returns:** + - `int`: The balance in Wei. -**Returns:** +### `load_contract(file_path)` -- `tx_receipt`: The transaction receipt. +Loads a Solidity contract source code from a file. -### 14. `validate_gas_estimation(web3, tx)` +- **Parameters:** + - `file_path (str)`: Path to the Solidity contract file. -**Purpose:** Validates the gas estimation for a transaction. +- **Returns:** + - `str`: The contract source code. -**Usage:** +### `transfer_native(web3, recipient_address, private_key, amount_to_transfer_ether, native_gas_units)` -```python -validate_gas_estimation(web3, tx) -``` +Transfers the native token (e.g., Ether) from the default account to a recipient address. -**Parameters:** +- **Parameters:** + - `web3 (Web3)`: Web3 instance. + - `recipient_address (str)`: Address to send the Ether. + - `private_key (str)`: Private key of the sender. + - `amount_to_transfer_ether (float)`: Amount of Ether to transfer. + - `native_gas_units (int)`: Gas units to use for the transaction. -- `web3`: An instance of Web3. -- `tx`: The transaction object. +- **Returns:** + - `dict`: Transaction receipt. -### 15. `is_gas_units_estimation_valid(web3, tx)` +### `deploy_contract(contract, kwargs, tx_params)` -**Purpose:** Checks if the provided gas units are sufficient for the transaction. +Deploys a smart contract with the given constructor arguments and transaction parameters. -**Usage:** - -```python -valid, gas_estimate = is_gas_units_estimation_valid(web3, tx) -``` - -**Parameters:** - -- `web3`: An instance of Web3. -- `tx`: The transaction object. - -**Returns:** - -- `valid`: Boolean indicating if the gas units are sufficient. -- `gas_estimate`: The estimated gas units. - -### 16. `deploy_contract(contract, kwargs, tx_params)` - -**Purpose:** Deploys a contract with the given parameters. - -**Usage:** - -```python -tx_receipt = deploy_contract(contract, kwargs, tx_params) -``` +- **Parameters:** + - `contract (Contract)`: Web3 contract instance. + - `kwargs (dict)`: Constructor arguments for the contract. + - `tx_params (dict)`: Transaction parameters including gas limit, gas price, and private key. -**Parameters:** +- **Returns:** + - `dict`: Transaction receipt. -- `contract`: The contract object. -- `kwargs`: Keyword arguments for the contract constructor. -- `tx_params`: Transaction parameters. +### `exec_func_via_transaction(func, tx_params)` -**Returns:** +Executes a contract function via a transaction. -- `tx_receipt`: The transaction receipt. +- **Parameters:** + - `func (ContractFunction)`: Contract function to execute. + - `tx_params (dict)`: Transaction parameters including gas limit, gas price, and private key. -### 17. `exec_func_via_transaction(func, tx_params)` - -**Purpose:** Executes a contract function via a transaction. - -**Usage:** - -```python -tx_receipt = exec_func_via_transaction(func, tx_params) -``` - -**Parameters:** - -- `func`: The contract function to be executed. -- `tx_params`: Transaction parameters. - -**Returns:** - -- `tx_receipt`: The transaction receipt. - -### 18. `sign_and_send_tx(web3, private_key, transaction)` - -**Purpose:** Signs and sends a transaction. - -**Usage:** - -```python -tx_receipt = sign_and_send_tx(web3, private_key, transaction) -``` - -**Parameters:** - -- `web3`: An instance of Web3. -- `private_key`: The private key of the sender. -- `transaction`: The transaction object. - -**Returns:** - -- `tx_receipt`: The transaction receipt. - -### 19. `decrypt_value(contract_value, user_key)` - -**Purpose:** Decrypts a value stored in a contract using a user key. - -**Usage:** - -```python -decrypted_balance = decrypt_value(contract_value, user_key) -``` +- **Returns:** + - `dict`: Transaction receipt. -**Parameters:** +### `sign_and_send_tx(web3, private_key, transaction)` -- `contract_value`: The value to be decrypted. -- `user_key`: The user's AES key. +Signs a transaction with the provided private key and sends it to the blockchain. -**Returns:** +- **Parameters:** + - `web3 (Web3)`: Web3 instance. + - `private_key (str)`: Private key to sign the transaction. + - `transaction (dict)`: Transaction details. -- `decrypted_balance`: The decrypted value. +- **Returns:** + - `dict`: Transaction receipt. #### To report issues, please create a [github issue](https://github.com/coti-io/coti-sdk-python/issues) From f5a55658849892da3b184c6975e1033df2c3815e Mon Sep 17 00:00:00 2001 From: Daniel Gruesso Date: Tue, 24 Sep 2024 15:34:58 -0400 Subject: [PATCH 5/5] Add CoC, license, and contributing guidelines --- CODE_OF_CONDUCT.md | 44 ++++++++++ CONTRIBUTING.md | 101 +++++++++++++++++++++++ LICENSE | 201 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 346 insertions(+) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..aaec964 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,44 @@ +# COTI Community Code of Conduct + +## Our Pledge +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project maintainers. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The COTI team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution +This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct/](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..894aa6a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,101 @@ + +# Contributing to COTI Python SDK + +Thank you for your interest in contributing to the COTI Python SDK project! We welcome contributions from the community and appreciate your efforts to improve our project. + +## Table of Contents + +1. [Code of Conduct](#code-of-conduct) +2. [How Can I Contribute?](#how-can-i-contribute) + - [Reporting Bugs](#reporting-bugs) + - [Suggesting Enhancements](#suggesting-enhancements) + - [Contributing Code](#contributing-code) +3. [Development Guidelines](#development-guidelines) + - [Development Workflow](#development-workflow) + - [Coding Standards](#coding-standards) + - [Commit Messages](#commit-messages) +4. [License](#license) + +## Code of Conduct + +Please note that this project is governed by our [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. + +## How Can I Contribute? + +### Reporting Bugs + +If you encounter any bugs, please report them by [opening an issue](https://github.com/coti-io/coti-sdk-python/issues/new) on GitHub. Include as much detail as possible, including steps to reproduce the bug, the environment you encountered it in, and any other relevant information. + +### Suggesting Enhancements + +If you have ideas for new features or improvements, feel free to suggest them by [opening an issue](https://github.com/coti-io/coti-sdk-python/issues/new) on GitHub. Please provide a clear description of the proposed enhancement and why it would be beneficial to the project. + +### Contributing Code + +We welcome code contributions! To get started, follow these steps: + +1. **Fork the repository**: Click the **"Fork"** button at the top right of the repository page to create your own copy of the project. + +2. **Clone your fork**: + ```sh + git clone https://github.com/your-username/coti-sdk-python.git + cd coti-sdk-python + ``` + +3. **Create a branch**: + ```sh + git checkout -b feature/my-new-feature + ``` + +4. **Make your changes**: Develop your feature or bug fix. + +5. **Commit your changes**: + ```sh + git commit -m 'Add some feature' + ``` + +6. **Push to your fork**: + ```sh + git push origin feature/my-new-feature + ``` + +7. **Create a pull request**: Go to the original repository and open a pull request. + +## Development Guidelines + +### Development Workflow + +1. Ensure that you have the latest version of the main branch: + ```sh + git checkout main + git pull origin main + ``` + +2. Create a new branch for your work: + ```sh + git checkout -b feature/your-feature-name + ``` + +3. Make your changes and test thoroughly. + +4. Commit your changes with a clear and descriptive message. + +5. Push your branch to your fork. + +6. Create a pull request against the main branch of the original repository. + +### Coding Standards + +- Follow the existing coding style. +- Write clear, concise, and meaningful commit messages. +- Ensure your code passes all tests and linter checks. + +### Commit Messages + +- Use the present tense ("Add feature" not "Added feature"). +- Capitalize the first letter. +- Keep the message concise but descriptive. + +## License + +By contributing, you agree that your contributions will be licensed under the [Apache 2.0 License](/LICENSE). \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7e559f1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 COTI + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file