def test_byte_array_to_hex_str(self): """Tests to verify byte_array_to_hex_str(in_byte_array) function """ bytearr = b'teststring' # positive case hexstr = byte_array_to_hex_str(bytearr) self.assertEqual(hexstr, "74657374737472696e67") bytearr = b'' # empty array case hexstr = byte_array_to_hex_str(bytearr) self.assertEqual(hexstr, "")
def registry_update(self, org_id, uri, sc_addr, app_type_ids): """ Update a registry. Parameters: org_id bytes[] identifies organization that hosts the registry, e.g. a bank in the consortium or an anonymous entity uri string that defines a URI for this registry that supports the Off-Chain Worker Registry JSON RPC API sc_addr bytes[] defines a Fabric chain code name that runs the Worker Registry Smart Contract API smart contract for this registry app_type_ids []bytes[] is an optional parameter that defines application types supported by the worker managed by the registry Returns: Transaction receipt on success or None on error. """ if (self.__fabric_wrapper is not None): if (is_valid_hex_str(binascii.hexlify(org_id).decode("utf8")) is False): logging.error("Invalid Org id {}".format(org_id)) return None if (sc_addr is not None and is_valid_hex_str( binascii.hexlify(sc_addr).decode("utf8")) is False): logging.error( "Invalid smart contract address {}".format(sc_addr)) return None if (not uri): logging.error("Empty uri {}".format(uri)) return None app_ids = [] for aid in app_type_ids: if (is_valid_hex_str(binascii.hexlify(aid).decode("utf8")) is False): logging.error("Invalid application id {}".format(aid)) return None else: app_ids.append(byte_array_to_hex_str(aid)) params = [] params.append(byte_array_to_hex_str(org_id)) params.append(uri) params.append(byte_array_to_hex_str(sc_addr)) params.append(','.join(app_ids)) txn_status = self.__fabric_wrapper.invoke_chaincode( self.CHAIN_CODE, 'registryUpdate', params) return txn_status else: logging.error("Fabric wrapper instance is not initialized") return None
def registry_set_status(self, org_id, status): """ Set registry status. Parameters: org_id bytes[] identifies organization that hosts the registry, e.g. a bank in the consortium or an anonymous entity status Defines the registry status to set. The currently defined values are: 1 - the registry is active 2 - the registry is temporarily "off-line" 3 - the registry is decommissioned Returns: Transaction receipt on success or None on error. """ if (self.__fabric_wrapper is not None): if (is_valid_hex_str(binascii.hexlify(org_id).decode("utf8")) is False): logging.info("Invalid Org id {}".format(org_id)) return None if not isinstance(status, RegistryStatus): logging.info("Invalid registry status {}".format(status)) return None params = [] params.append(byte_array_to_hex_str(org_id)) params.append(str(status)) txn_status = self.__fabric_wrapper.invoke_chaincode( self.CHAIN_CODE, 'registrySetStatus', params) return txn_status else: logging.error("Fabric wrapper instance is not initialized") return None
def registry_retrieve(self, org_id): """ Retrieving Registry Information identified by organization id It returns tuple containing following on success. 1. uri is string defines a URI for this registry that supports the Off-Chain Worker Registry JSON RPC API. It is going to be None for proxy model. 2. sc_addr Fabric address for worker registry smart contract address. 3. application_type_ids list of application ids(array of byte[]) 4. status of the registry Returns None on error. """ if (self.__fabric_wrapper is not None): if (is_valid_hex_str(binascii.hexlify(org_id).decode("utf8")) is False): logging.info("Invalid Org id {}".format(org_id)) return None else: params = [] params.append(byte_array_to_hex_str(org_id)) registryDetails = \ self.__fabric_wrapper.invoke_chaincode( self.CHAIN_CODE, 'registryRetrieve', params ) return registryDetails else: logging.error("Fabric wrapper instance is not initialized") return None
def registry_lookup(self, app_type_id=None): """ Registry Lookup identified by application type id It returns following 1. totalCount is the total number of entries matching a specified lookup criteria. If this number is larger than the size of the ids array, the caller should use the lookupTag to call workerLookUpNext to retrieve the rest of the ids. 2. lookupTag is an optional parameter. If it is returned, it means that there are more matching registry ids that can be retrieved by calling the function registry_lookup_next with this tag as an input parameter. 3. ids is an array of the registry organization ids that match the input parameters Returns tuple containing count, lookup tag and list of organization ids on success and returns None on error. """ if (self.__fabric_wrapper is not None): if app_type_id is not None: if is_valid_hex_str( binascii.hexlify(app_type_id).decode("utf8")): params = [] params.append(byte_array_to_hex_str(app_type_id)) lookupResult = \ self.__fabric_wrapper.invoke_chaincode( self.CHAIN_CODE, 'registryLookUp', params) else: logging.info( "Invalid application type id {}".format(app_type_id)) return None else: logging.error("Fabric wrapper instance is not initialized") return None
def verify_data_hash(msg, data_hash): ''' Function to verify data hash msg - Input text data_hash - hash of the data in hex format ''' verify_success = True msg_hash = compute_data_hash(msg) # Convert both hash hex string values to upper case msg_hash_hex = hex_utils.byte_array_to_hex_str(msg_hash).upper() data_hash = data_hash.upper() if msg_hash_hex == data_hash: logger.info("Computed hash of message matched with data hash") else: logger.error("Computed hash of message does not match with data hash") verify_success = False return verify_success
def test_worker_lookup(self): logging.info( "Calling worker_lookup..\n worker_type: %d\n orgId: %s\n " + "applicationId: %s", self.__worker_type.value, hex_to_utf8(self.__org_id), hex_to_utf8(self.__application_ids[0])) result = self.__eth_conn.worker_lookup( self.__worker_type, '419c007ce1f6ecb2e52a830b03a0c8be36438b94c950d9cf2aeb48b0f99a8276', '7b1d714fc499ddc59dde26683a0bf928848801087dc5f0372c340c120848ed7b') logging.info("worker_lookup result {} {}".format(result, type(result))) logging.info("worker_lookup status {} {} {}".format( result[0], result[1], result[2])) match = byte_array_to_hex_str(self.__worker_id) in result[2] self.assertEqual(result[0], 1, "Worker lookup response count doesn't match") self.assertTrue(match, "Worker lookup response worker id doesn't match")
def _generate_nonce(self, params): """ Generate nonce of length specified in params. Parameters : params: Parameters received in request Returns : JSON RPC response containing requested nonce. """ nonce_size = params["nonce_size"] # Generate random bytes of size equivalent to nonce_size/2, # so that when encoded to hex string results in string of # size equal to nonce_size nonce_bytes = crypto_utility.generate_random_bytes(int(nonce_size / 2)) # Convert nonce to hex string and persist in the EnclaveData self._nonce = byte_array_to_hex_str(nonce_bytes) return json.dumps({"nonce": self._nonce})
def registry_lookup_next(self, app_type_id, lookup_tag): """ Get additional registry lookup results. This function is called to retrieve additional results of the Registry lookup initiated by the registry_lookup call. Parameters: app_type_id Application type ID that has to be supported by the workers retrieved lookup_tag Returned by a previous call to either this function or to registry_lookup Returns: Outputs a tuple on success containing the following: total_count Total number of entries matching the lookup criteria. If this number is larger than the number of IDs returned so far, the caller should use lookup_tag to call registry_lookup_next to retrieve the rest of the IDs new_lookup_tag is an optional parameter. If it is returned, it means that there are more matching registry IDs that can be retrieved by calling this function again with this tag as an input parameter ids Array of the registry IDs that match the input parameters Returns None on error. """ if (self.__fabric_wrapper is not None): if is_valid_hex_str(binascii.hexlify(app_type_id).decode("utf8")): params = [] params.append(byte_array_to_hex_str(app_type_id)) params.append(lookup_tag) lookupResult = self.__fabric_wrapper.invoke_chaincode( self.CHAIN_CODE, 'registryLookUpNext', params) else: logging.info( "Invalid application type id {}".format(app_type_id)) return None else: logging.error("Fabric wrapper instance is not initialized") return None
def generate_client_signature( self, input_json_str, worker, private_key, session_key, session_iv, encrypted_session_key, data_key=None, data_iv=None): """ Function to generate client signature Parameters: - input_json_str is requester Work Order Request payload in a JSON-RPC based format defined 6.1.1 Work Order Request Payload - worker is a worker object to store all the common details of worker as per Trusted Compute EEA API 8.1 Common Data for All Worker Types - private_key is Client private key - session_key is one time session key generated by the participant submitting the work order. - session_iv is an initialization vector if required by the data encryption algorithm (encryptedSessionKey). The default is all zeros. - data_key is a one time key generated by participant used to encrypt work order indata - data_iv is an initialization vector used along with data_key. Default is all zeros. - encrypted_session_key is a encrypted version of session_key. Returns a tuple containing signature and status """ if (self.__payload_json_check(input_json_str) is False): logger.error("ERROR: Signing the request failed") return input_json_str, SignatureStatus.FAILED if (self.tcs_worker['HashingAlgorithm'] != worker.hashing_algorithm): logger.error( "ERROR: Signing the request failed. Hashing " + "algorithm is not supported for %s", worker.hashing_algorithm) return input_json_str, SignatureStatus.FAILED if (self.tcs_worker['SigningAlgorithm'] != worker.signing_algorithm): logger.error( "ERROR: Signing the request failed. Signing " + "algorithm is not supported for %s", worker.signing_algorithm) return input_json_str, SignatureStatus.FAILED input_json = json.loads(input_json_str) input_json_params = input_json['params'] input_json_params["sessionKeyIv"] = byte_array_to_hex_str(session_iv) encrypted_session_key_str = byte_array_to_hex_str( encrypted_session_key) self.__encrypt_workorder_indata( input_json_params, session_key, session_iv, worker.encryption_key, data_key, data_iv) if "requesterNonce" in input_json_params: if len(input_json_params["requesterNonce"]) == 0: # [NO_OF_BYTES] 16 BYTES for nonce. # This is the recommendation by NIST to # avoid collisions by the "Birthday Paradox". input_json_params["requesterNonce"] = secrets.token_hex( NO_OF_BYTES) elif not is_valid_hex_str(input_json_params["requesterNonce"]): logger.error("Invalid data format for requesterNonce") return input_json_params, SignatureStatus.FAILED else: logger.error("Missing parameter requesterNonce") return input_json_params, SignatureStatus.FAILED hash_string_1 = self.__calculate_hash_on_concatenated_string( input_json_params, input_json_params["requesterNonce"].encode( 'UTF-8' )) data_objects = input_json_params['inData'] hash_string_2 = self.calculate_datahash(data_objects) hash_string_3 = "" if 'outData' in input_json_params: data_objects = input_json_params['outData'] hash_string_3 = self.calculate_datahash(data_objects) concat_string = hash_string_1 + hash_string_2 + hash_string_3 concat_hash = bytes(concat_string, 'UTF-8') final_hash = crypto.compute_message_hash(concat_hash) encrypted_request_hash = crypto_utility.encrypt_data( final_hash, session_key, session_iv) encrypted_request_hash_str = \ byte_array_to_hex_str(encrypted_request_hash) logger.debug("encrypted request hash: \n%s", encrypted_request_hash_str) # Update the input json params input_json_params["encryptedRequestHash"] = encrypted_request_hash_str status, signature = self.generate_signature(final_hash, private_key) if status is False: return input_json_str, SignatureStatus.FAILED input_json_params['requesterSignature'] = signature input_json_params["encryptedSessionKey"] = encrypted_session_key_str # Temporary mechanism to share client's public key. Not a part of Spec input_json_params['verifyingKey'] = self.public_key input_json['params'] = input_json_params input_json_str = json.dumps(input_json) logger.info("Request Json successfully Signed") return input_json_str, SignatureStatus.PASSED
def Main(args=None): ParseCommandLine(args) config["Logging"] = {"LogFile": "__screen__", "LogLevel": "INFO"} plogger.setup_loggers(config.get("Logging", {})) sys.stdout = plogger.stream_to_logger(logging.getLogger("STDOUT"), logging.DEBUG) sys.stderr = plogger.stream_to_logger(logging.getLogger("STDERR"), logging.WARN) logger.info("***************** AVALON *****************") # Connect to registry list and retrieve registry if not off_chain: registry_list_instance = direct_jrpc.get_worker_registry_list_instance( ) # Lookup returns tuple, first element is number of registries and # second is element is lookup tag and # third is list of organization ids. registry_count, lookup_tag, registry_list = \ registry_list_instance.registry_lookup() logger.info("\n Registry lookup response: registry count: {} " + "lookup tag: {} registry list: {}\n".format( registry_count, lookup_tag, registry_list)) if (registry_count == 0): logger.warn("No registries found") sys.exit(1) # Retrieve the first registry details. registry_retrieve_result = registry_list_instance.registry_retrieve( registry_list[0]) logger.info("\n Registry retrieve response: {}\n".format( registry_retrieve_result)) config["tcf"]["json_rpc_uri"] = registry_retrieve_result[0] # Prepare worker req_id = 31 global worker_id worker_registry_instance = direct_jrpc.get_worker_registry_instance() if not worker_id: worker_lookup_result = worker_registry_instance.worker_lookup( worker_type=WorkerType.TEE_SGX, id=req_id) logger.info("\n Worker lookup response: {}\n".format( json.dumps(worker_lookup_result, indent=4))) if "result" in worker_lookup_result and \ "ids" in worker_lookup_result["result"].keys(): if worker_lookup_result["result"]["totalCount"] != 0: worker_id = worker_lookup_result["result"]["ids"][0] else: logger.error("ERROR: No workers found") sys.exit(1) else: logger.error("ERROR: Failed to lookup worker") sys.exit(1) req_id += 1 worker_retrieve_result = worker_registry_instance.worker_retrieve( worker_id, req_id) logger.info("\n Worker retrieve response: {}\n".format( json.dumps(worker_retrieve_result, indent=4))) worker_obj.load_worker(worker_retrieve_result) logger.info( "**********Worker details Updated with Worker ID" + "*********\n%s\n", worker_id) # Convert workloadId to hex workload_id = "echo-result".encode("UTF-8").hex() work_order_id = secrets.token_hex(32) requester_id = secrets.token_hex(32) session_iv = utility.generate_iv() session_key = utility.generate_key() requester_nonce = secrets.token_hex(16) # Create work order wo_params = WorkOrderParams( work_order_id, worker_id, workload_id, requester_id, session_key, session_iv, requester_nonce, result_uri=" ", notify_uri=" ", worker_encryption_key=worker_obj.encryption_key, data_encryption_algorithm="AES-GCM-256") # Add worker input data if input_data_hash: # Compute data hash for data params inData data_hash = utility.compute_data_hash(message) # Convert data_hash to hex data_hash = hex_utils.byte_array_to_hex_str(data_hash) wo_params.add_in_data(message, data_hash) else: wo_params.add_in_data(message) # Encrypt work order request hash wo_params.add_encrypted_request_hash() private_key = utility.generate_signing_keys() if requester_signature: # Add requester signature and requester verifying_key if wo_params.add_requester_signature(private_key) is False: logger.info("Work order request signing failed") exit(1) # Submit work order logger.info("Work order submit request : %s, \n \n ", wo_params.to_jrpc_string(req_id)) work_order_instance = direct_jrpc.get_work_order_instance() req_id += 1 response = work_order_instance.work_order_submit( wo_params.get_work_order_id(), wo_params.get_worker_id(), wo_params.get_requester_id(), wo_params.to_string(), id=req_id) logger.info("Work order submit response : {}\n ".format( json.dumps(response, indent=4))) if "error" in response and response["error"]["code"] != \ WorkOrderStatus.PENDING: sys.exit(1) # Create receipt wo_receipt_instance = direct_jrpc.get_work_order_receipt_instance() req_id += 1 # Create work order receipt object using WorkOrderReceiptRequest class wo_request = json.loads(wo_params.to_jrpc_string(req_id)) wo_receipt_obj = WorkOrderReceiptRequest() wo_create_receipt = wo_receipt_obj.create_receipt( wo_request, ReceiptCreateStatus.PENDING.value, private_key) logger.info("Work order create receipt request : {} \n \n ".format( json.dumps(wo_create_receipt, indent=4))) # Submit work order create receipt jrpc request wo_receipt_resp = wo_receipt_instance.work_order_receipt_create( wo_create_receipt["workOrderId"], wo_create_receipt["workerServiceId"], wo_create_receipt["workerId"], wo_create_receipt["requesterId"], wo_create_receipt["receiptCreateStatus"], wo_create_receipt["workOrderRequestHash"], wo_create_receipt["requesterGeneratedNonce"], wo_create_receipt["requesterSignature"], wo_create_receipt["signatureRules"], wo_create_receipt["receiptVerificationKey"], req_id) logger.info("Work order create receipt response : {} \n \n ".format( wo_receipt_resp)) # Retrieve result req_id += 1 res = work_order_instance.work_order_get_result(work_order_id, req_id) logger.info("Work order get result : {}\n ".format( json.dumps(res, indent=4))) sig_obj = signature.ClientSignature() if "result" in res: status = sig_obj.verify_signature(res, worker_obj.verification_key) try: if status == SignatureStatus.PASSED: logger.info( "Work order response signature verification Successful") decrypted_res = utility.decrypted_response( res, session_key, session_iv) logger.info("\nDecrypted response:\n {}".format(decrypted_res)) if input_data_hash: decrypted_data = decrypted_res[0]["data"] data_hash_in_resp = (decrypted_res[0]["dataHash"]).upper() # Verify data hash in response if utility.verify_data_hash(decrypted_data, data_hash_in_resp) is False: sys.exit(1) else: logger.info("Signature verification Failed") sys.exit(1) except Exception as err: logger.error("ERROR: Failed to decrypt response: %s", str(err)) sys.exit(1) else: logger.info("\n Work order get result failed {}\n".format(res)) sys.exit(1) # Retrieve receipt receipt_res = wo_receipt_instance.work_order_receipt_retrieve( work_order_id, id=req_id) logger.info("\n Retrieve receipt response:\n {}".format( json.dumps(receipt_res, indent=4))) # Retrieve last update to receipt by passing 0xFFFFFFFF req_id += 1 receipt_update_retrieve = \ wo_receipt_instance.work_order_receipt_update_retrieve( work_order_id, None, 1 << 32, id=req_id) logger.info("\n Last update to receipt receipt is:\n {}".format( json.dumps(receipt_update_retrieve, indent=4))) status = sig_obj.verify_update_receipt_signature(receipt_update_retrieve) if status == SignatureStatus.PASSED: logger.info( "Work order receipt retrieve signature verification Successful") else: logger.info( "Work order receipt retrieve signature verification failed!!") sys.exit(1) # Receipt lookup based on requesterId req_id += 1 receipt_lookup_res = wo_receipt_instance.work_order_receipt_lookup( requester_id=requester_id, id=req_id) logger.info("\n Work order receipt lookup response :\n {}".format( json.dumps(receipt_lookup_res, indent=4)))
def byte_array_to_hex(byte_array): hex_value = hex_utils.byte_array_to_hex_str(byte_array) return hex_value.upper()