def __init__(self, config): """ Constructor for WPERequester. Initialize the HTTP jrpc client. Parameters : @param config - dict of config read """ self._uri_client = HttpJrpcClient( config["KMEListener"]["kme_listener_url"]) self._conn_retries = config["KMEListener"]["connection_retry"] worker_id = config.get("WorkerConfig")["worker_id"] # Calculate sha256 of worker id to get 32 bytes. The TC spec proxy # model contracts expect byte32. Then take a hexdigest for hex str. worker_id = hashlib.sha256(worker_id.encode("UTF-8")).hexdigest() if config.get("KvStorage") is None: logger.error("Kv Storage path is missing") sys.exit(-1) try: kv_helper = connector.open(config['KvStorage']['remote_url']) except Exception as err: logger.error("Failed to open KV storage interface; " + "exiting Intel SGX Enclave manager: {}".format(err)) sys.exit(-1) worker_kv_delegate = WorkerKVDelegate(kv_helper) self._worker = worker_kv_delegate.get_worker_by_id(worker_id) jrpc_methods = {} jrpc_methods["kme-uid"] = "GetUniqueVerificationKey" jrpc_methods["kme-reg"] = "RegisterWorkOrderProcessor" jrpc_methods["kme-preprocess"] = "PreProcessWorkOrder" # Map too hold workload-id to JRPC method mapping self._jrpc_methods = jrpc_methods
def __init__(self, config): self.__uri_client = HttpJrpcClient(config["tcf"]["json_rpc_uri"])
class JRPCWorkerRegistryImpl(WorkerRegistry): """ This class is to read the worker registry to get the more details of worker. """ def __init__(self, config): self.__uri_client = HttpJrpcClient(config["tcf"]["json_rpc_uri"]) def worker_retrieve(self, worker_id, id=None): """ Retrieve the worker identified by worker ID. Parameters: worker_id Worker ID value derived from the worker's DID id Optional Optional JSON RPC request ID Returns: JRPC response containing: organization ID, application ID, worker status, and worker details. """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkerRetrieve", "id": id, "params": { "workerId": worker_id } } response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def worker_lookup(self, worker_type=None, organization_id=None, application_type_id=None, id=None): """ Worker lookup based on worker type, organization ID, and application ID. All fields are optional and, if present, condition should match for all fields. If none are passed it should return all workers. Parameters: worker_type Optional characteristic of Workers for which you may wish to search. Currently defined types are: * "TEE-SGX": an Intel SGX Trusted Execution Environment * "MPC": Multi-Party Compute * "ZK": Zero-Knowledge organization_id Optional parameter representing the organization that hosts the Worker, e.g. a bank in the consortium or anonymous entity application_type_id Optional application type that has to be supported by the worker id Optional Optional JSON RPC request ID Returns: JRPC response containing number of workers, lookup tag, and list of worker IDs. """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkerLookUp", "id": id, "params": {} } if worker_type is not None: json_rpc_request["params"]["workerType"] = worker_type.value if organization_id is not None: json_rpc_request["params"]["organizationId"] = organization_id if application_type_id is not None: json_rpc_request["params"]["applicationTypeId"] = \ application_type_id response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def worker_lookup_next(self, lookup_tag, worker_type=None, organization_id=None, application_type_id=None, id=None): """ Retrieve subsequent Worker lookup results based on worker type, organization ID, and application ID. Similar to workerLookUp with additional parameter lookup_tag. Parameters: lookup_tag Used to lookup subsequent results after calling worker_lookup worker_type Optional characteristic of Workers for which you may wish to search. Currently defined types are: * "TEE-SGX": an Intel SGX Trusted Execution Environment * "MPC": Multi-Party Compute * "ZK": Zero-Knowledge organization_id Optional parameter representing the organization that hosts the Worker, e.g. a bank in the consortium or anonymous entity application_type_id Optional application type that has to be supported by the worker id Optional Optional JSON RPC request ID Returns: JRPC response containing number of workers, lookup tag, and list of worker IDs. """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkerLookUpNext", "id": id, "params": { "lookUpTag": lookup_tag } } if worker_type is not None: json_rpc_request["params"]["workerType"] = worker_type.value if organization_id is not None: json_rpc_request["params"]["organizationId"] = organization_id if application_type_id is not None: json_rpc_request["params"]["applicationTypeId"] = \ application_type_id response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def worker_register(self, worker_id, worker_type, org_id, application_type_ids, details, id=None): """ Adds worker details to registry Parameters: worker_id Worker ID value derived from the worker's DID worker_type Type of Worker. Currently defined types are: * "TEE-SGX": an Intel SGX Trusted Execution Environment * "MPC": Multi-Party Compute * "ZK": Zero-Knowledge org_id Organization that hosts the Worker, e.g. a bank in the consortium or anonymous entity application_type_ids Application types supported by the worker id Optional JSON RPC request ID Returns: JRPC response with worker registry status. """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkerRegister", "id": id, "params": { "workerId": worker_id, "workerType": worker_type.value, "organizationId": org_id, "applicationTypeId": application_type_ids, "details": details } } response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def worker_update(self, worker_id, details, id=None): """ Update worker with new information. Parameters: worker_id Worker ID value derived from the worker's DID details Detailed information about the worker in JSON RPC format as defined in id Optional JSON RPC request ID Returns: JRPC response with update status. """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkerUpdate", "id": id, "params": { "workerId": worker_id, "details": details } } response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def worker_set_status(self, worker_id, status, id=None): """ Set the worker status to active, offline, decommissioned, or compromised state. Parameters: worker_id Worker ID value derived from the worker's DID status Worker status value to set id Optional JSON RPC request ID Returns: JRPC response with status. """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkerSetStatus", "id": id, "params": { "workerId": worker_id, "status": status.value } } response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response
class WPERequester(): """ JRPC requester acting on behalf of WorkOrderProcessorEnclaveManager """ def __init__(self, config): """ Constructor for WPERequester. Initialize the HTTP jrpc client. Parameters : @param config - dict of config read """ self._uri_client = HttpJrpcClient( config["KMEListener"]["kme_listener_url"]) self._conn_retries = config["KMEListener"]["connection_retry"] worker_id = config.get("WorkerConfig")["worker_id"] # Calculate sha256 of worker id to get 32 bytes. The TC spec proxy # model contracts expect byte32. Then take a hexdigest for hex str. worker_id = hashlib.sha256(worker_id.encode("UTF-8")).hexdigest() if config.get("KvStorage") is None: logger.error("Kv Storage path is missing") sys.exit(-1) try: kv_helper = connector.open(config['KvStorage']['remote_url']) except Exception as err: logger.error("Failed to open KV storage interface; " + "exiting Intel SGX Enclave manager: {}".format(err)) sys.exit(-1) worker_kv_delegate = WorkerKVDelegate(kv_helper) self._worker = worker_kv_delegate.get_worker_by_id(worker_id) jrpc_methods = {} jrpc_methods["kme-uid"] = "GetUniqueVerificationKey" jrpc_methods["kme-reg"] = "RegisterWorkOrderProcessor" jrpc_methods["kme-preprocess"] = "PreProcessWorkOrder" # Map too hold workload-id to JRPC method mapping self._jrpc_methods = jrpc_methods def get_unique_verification_key(self, verification_key_nonce): """ Request wrapper to get a unique id from the KME Parameters : @param verification_key_nonce - Random nonce generated by this WPE Returns : @returns result - Result received from the KME which includes the public verification key which is supposed to be included in REPORTDATA by the WPE. None, in case of failure. """ workload_id = "kme-uid" in_data = [verification_key_nonce] # Create session key and iv to sign work order request session_key = crypto_utils.generate_key() session_iv = crypto_utils.generate_iv() wo_req = self._construct_wo_req(in_data, workload_id, self._worker.encryption_key, session_key, session_iv) response = self._post_and_get_result(wo_req) if "result" in response: wo_response_json = response["result"] if self._verify_res_signature(wo_response_json, self._worker.verification_key, wo_req["params"]["requesterNonce"]): decrypted_res = crypto_utils.decrypted_response( wo_response_json, session_key, session_iv) # Response contains an array of results. In this case, the # array has single element and the data field is of interest. # The data contains result,verification_key and # verification_key_signature delimited by ' '. # @TODO : Update to use multiple out_data fields. return decrypted_res[0]['data'] return None else: logger.error( "Could not get a unique id from the KME : {}".format(response)) return None def register_wo_processor(self, unique_verification_id, encryption_key, proof_data, mr_enclave): """ Request to register this WPE with the KME Parameters : @param unique_verification_id - Unique verifying key received from KME. @param encryption_key - encryption key of WPE @param proof_data - The IAS attestation report/DCAP quote @param mr_enclave - MRENCLAVE for this WPE Returns : @returns status - The status of the registration. True, for success. None, in case of errors. """ workload_id = "kme-reg" registration_params = { "unique_id": unique_verification_id, "proof_data": proof_data, "wpe_encryption_key": encryption_key, "mr_enclave": mr_enclave } in_data = [json.dumps(registration_params)] # Create session key and iv to sign work order request session_key = crypto_utils.generate_key() session_iv = crypto_utils.generate_iv() wo_req = self._construct_wo_req(in_data, workload_id, self._worker.encryption_key, session_key, session_iv) response = self._post_and_get_result(wo_req) if "result" in response: wo_response_json = response["result"] if "error" not in wo_response_json and self._verify_res_signature( wo_response_json, self._worker.verification_key, wo_req["params"]["requesterNonce"]): decrypted_res = crypto_utils.decrypted_response( wo_response_json, session_key, session_iv) # Response contains an array of results. In this case, the # array has single element and the data field is of interest. # It is integer with status of registration. return decrypted_res[0]['data'] return None else: logger.error( "Could not register this WPE with the KME : {}".format( response)) return None def preprocess_work_order(self, wo_request, encryption_key): """ Request to preprocess a work order Parameters : @param wo_request - The original work order request as str @param encryption_key - WPE's public encryption key Returns : @returns result - Result from KME that includes the workorder key info. error response, in case of failure. """ workload_id = "kme-preprocess" in_data = [wo_request, encryption_key] # Create session key and iv to sign work order request session_key = crypto_utils.generate_key() session_iv = crypto_utils.generate_iv() wo_req = self._construct_wo_req(in_data, workload_id, self._worker.encryption_key, session_key, session_iv) response = self._post_and_get_result(wo_req) if "result" in response: wo_response_json = response["result"] if self._verify_res_signature(wo_response_json, self._worker.verification_key, wo_req["params"]["requesterNonce"]): decrypted_res = crypto_utils.decrypted_response( wo_response_json, session_key, session_iv) # Response contains an array of results. In this case, the # array has single element and the data field is of interest. return decrypted_res[0]['data'] return None else: logger.error( "Could not preprocess work order at KME : {}".format(response)) return response def _construct_wo_req(self, in_data, workload_id, encryption_key, session_key, session_iv): """ Construct the parameters for a standard work order request Parameters : @param in_data - List of data to be passed to workload processor @param workload_id - Id of the target workload @encryption_key - Worker encryption key @session_key - Session key to be embedded in request @session_iv - Session key iv for encryption algorithm Returns : @returns A json request prepared using parameters passed """ # Create work order # Convert workloadId to hex workload_id_hex = workload_id.encode("UTF-8").hex() work_order_id = secrets.token_hex(32) requester_id = secrets.token_hex(32) requester_nonce = secrets.token_hex(16) # worker id is not known here. Hence passing a random string worker_id = secrets.token_hex(32) # Create work order params wo_params = WorkOrderParams(work_order_id, worker_id, workload_id_hex, requester_id, session_key, session_iv, requester_nonce, result_uri=" ", notify_uri=" ", worker_encryption_key=encryption_key, data_encryption_algorithm="AES-GCM-256") for value in in_data: wo_params.add_in_data(value) # Encrypt work order request hash wo_params.add_encrypted_request_hash() return { "jsonrpc": "2.0", "id": random.randint(0, 100000), "params": json.loads(wo_params.to_string()), "method": self._jrpc_methods[workload_id] } def _verify_res_signature(self, work_order_res, worker_verification_key, requester_nonce): """ Verify work order result signature Parameters : @param work_order_res - Result from work order response @param worker_verification_key - Worker verification key @param requester_nonce - requester generated nonce in work order request Returns : @returns True - If verification succeeds False - If verification fails """ sig_obj = signature.ClientSignature() status = sig_obj.verify_signature(work_order_res, worker_verification_key, requester_nonce) if status == SignatureStatus.PASSED: logger.info("Signature verification successful") else: logger.error("Signature verification failed") return False return True def _post_and_get_result(self, json_rpc_request): """ Helper method to serialize and send JRPC request and get response from the KME. Parameters : @param json_rpc_request - JSON containing RPC request Returns : @returns response - Response received from the KME """ json_request_str = json.dumps(json_rpc_request) logger.info("Request to KME listener %s", json_request_str) try: # Attempt to post request could fail if KME yet to be ready to # receive requests. Hence pass in the number of retries(>1) # to post a request. response = self._uri_client._postmsg(json_request_str, self._conn_retries) except Exception as err: logger.error("Exception occured in communication with KME") raise err logger.info("Response from KME %s", response) return response
def local_main(config): if not input_json_str and not input_json_dir: LOGGER.error("JSON input file is not provided") exit(1) if not output_json_file_name: LOGGER.error("JSON output file is not provided") exit(1) if not server_uri: LOGGER.error("Server URI is not provided") exit(1) LOGGER.info("Execute work order") uri_client = HttpJrpcClient(server_uri) response = None wo_id = None if input_json_dir: directory = os.fsencode(input_json_dir) files = os.listdir(directory) for file in sorted(files): LOGGER.info("---------------- Input file name: %s -------------\n", file.decode("utf-8")) input_json_str1 = futils.read_json_file( (directory.decode("utf-8") + file.decode("utf-8"))) # ----------------------------------------------------------------- # If Client request is WorkOrderSubmit, a requester payload's # signature with the requester private signing key is generated. if "WorkOrderSubmit" in input_json_str1: # Update workOrderId , workerId and workloadId input_json_obj = json.loads(input_json_str1) wo_id = hex(random.randint(1, 2**64 - 1)) input_json_obj["params"]["workOrderId"] = wo_id input_json_obj["params"]["workerId"] = worker_obj.worker_id # Convert workloadId to a hex string and update the request workload_id = input_json_obj["params"]["workloadId"] workload_id_hex = workload_id.encode("UTF-8").hex() input_json_obj["params"]["workloadId"] = workload_id_hex input_json_str1 = json.dumps(input_json_obj) # Generate session iv an encrypted session key session_iv = enclave_helper.generate_iv() session_key = enclave_helper.generate_key() encrypted_session_key = enclave_helper.generate_encrypted_key( session_key, worker_obj.encryption_key) input_json_str1, status = sig_obj.generate_client_signature( input_json_str1, worker_obj, private_key, session_key, session_iv, encrypted_session_key) if status != SignatureStatus.PASSED: LOGGER.info("Generate signature failed\n") exit(1) if input_json_str1 is None: continue # ----------------------------------------------------------------- # Update the worker ID if response: if "workerId" in input_json_str1: # Retrieve the worker id from the "WorkerRetrieve" # response and update the worker id information for # further json requests. if "result" in response and \ "ids" in response["result"].keys(): input_json_final = json.loads(input_json_str1) worker_id = response["result"]["ids"][0] input_json_final["params"]["workerId"] = worker_id input_json_str1 = json.dumps(input_json_final) LOGGER.info( "********** Worker details Updated with " "Worker ID*********\n%s\n", input_json_str1) # ----------------------------------------------------------------- if "WorkOrderGetResult" in input_json_str1 or \ "WorkOrderReceiptRetrieve": input_json_obj = json.loads(input_json_str1) input_json_obj["params"]["workOrderId"] = wo_id input_json_str1 = json.dumps(input_json_obj) LOGGER.info("*********Request Json********* \n%s\n", input_json_str1) response = uri_client._postmsg(input_json_str1) LOGGER.info("**********Received Response*********\n%s\n", response) # ----------------------------------------------------------------- # Worker details are loaded into Worker_Obj if "WorkerRetrieve" in input_json_str1 and "result" in response: worker_obj.load_worker(response["result"]["details"]) # ----------------------------------------------------------------- # Poll for "WorkOrderGetResult" and break when you get the result while ("WorkOrderGetResult" in input_json_str1 and "result" not in response): if response["error"]["code"] != WorkOrderStatus.PENDING: break response = uri_client._postmsg(input_json_str1) LOGGER.info("Received Response: %s, \n \n ", response) time.sleep(3) # ----------------------------------------------------------------- # Verify the signature if "WorkOrderGetResult" in input_json_str1: if "error" in response: # Response has error, hence skip Signature verification LOGGER.info("Work order response has error; " "skipping signature verification") continue sig_bool = sig_obj.verify_signature( response['result'], worker_obj.verification_key) try: if sig_bool > 0: LOGGER.info("Signature Verified") enclave_helper.decrypted_response( response['result'], session_key, session_iv) else: LOGGER.info("Signature verification Failed") exit(1) except Exception as e: LOGGER.error("ERROR: Failed to analyze " + "Signature Verification") exit(1) # ----------------------------------------------------------------- else: LOGGER.info("Input Request %s", input_json_str) response = uri_client._postmsg(input_json_str) LOGGER.info("Received Response: %s , \n \n ", response) exit(0)
def local_main(config): global input_json_str if not input_json_str and not input_json_dir: LOGGER.error("JSON input file is not provided") exit(1) if not output_json_file_name: LOGGER.error("JSON output file is not provided") exit(1) if not server_uri: LOGGER.error("Server URI is not provided") exit(1) LOGGER.info("Execute work order") uri_client = HttpJrpcClient(server_uri) response = None wo_id = None if input_json_dir: directory = os.fsencode(input_json_dir) files = os.listdir(directory) for file in sorted(files): LOGGER.info("---------------- Input file name: %s -------------\n", file.decode("utf-8")) input_json_str = futils.read_json_file( (directory.decode("utf-8") + file.decode("utf-8"))) # ----------------------------------------------------------------- # If Client request is WorkOrderSubmit, a requester payload's # signature with the requester private signing key is generated. if "WorkOrderSubmit" in input_json_str: # Update workOrderId , workerId and workloadId input_json_obj = json.loads(input_json_str) wo_id = hex(random.randint(1, 2**64 - 1)) input_json_obj["params"]["workOrderId"] = wo_id input_json_obj["params"]["workerId"] = worker_id # Convert workloadId to a hex string and update the request workload_id = input_json_obj["params"]["workloadId"] workload_id_hex = workload_id.encode("UTF-8").hex() input_json_obj["params"]["workloadId"] = workload_id_hex encrypt = worker_encryption.WorkerEncrypt() # Generate session key, session iv and encrypted session key session_key = encrypt.generate_session_key() session_iv = encrypt.generate_iv() encrypted_session_key = encrypt.encrypt_session_key( session_key, worker_obj.encryption_key) input_json_obj["params"]["encryptedSessionKey"] = \ crypto_utility.byte_array_to_hex(encrypted_session_key) input_json_obj["params"]["sessionKeyIv"] = \ crypto_utility.byte_array_to_hex(session_iv) if "requesterNonce" in input_json_obj["params"]: if len(input_json_obj["params"]["requesterNonce"]) == 0: # [NO_OF_BYTES] 16 BYTES for nonce. # This is the recommendation by NIST to # avoid collisions by the "Birthday Paradox". requester_nonce = secrets.token_hex(NO_OF_BYTES) input_json_obj["params"]["requesterNonce"] = \ requester_nonce requester_nonce = input_json_obj["params"]["requesterNonce"] # Encode data in inData for data_obj in input_json_obj["params"]["inData"]: encoded_data = data_obj["data"].encode('UTF-8') data_obj["data"] = encoded_data # Encrypt inData encrypt.encrypt_work_order_data_json( input_json_obj["params"]["inData"], session_key, session_iv) req_hash = worker_hash.WorkerHash().calculate_request_hash( input_json_obj["params"]) encrypted_req_hash = encrypt.encrypt_data( req_hash, session_key, session_iv) input_json_obj["params"]["encryptedRequestHash"] = \ encrypted_req_hash.hex() signer = worker_signing.WorkerSign() signer.generate_signing_key() wo_req_sig = signer.sign_message(req_hash) input_json_obj["params"]["requesterSignature"] = \ crypto_utility.byte_array_to_base64(wo_req_sig) input_json_obj["params"]["verifyingKey"] = \ signer.get_public_sign_key().decode('utf-8') input_json_str = json.dumps(input_json_obj) if input_json_str is None: continue # ----------------------------------------------------------------- # Update the worker ID if response: if "workerId" in input_json_str: # Retrieve the worker id from the "WorkerRetrieve" # response and update the worker id information for # further json requests. if "result" in response and \ "ids" in response["result"].keys() and \ len(response["result"]["ids"]) > 0: input_json_final = json.loads(input_json_str) worker_id = response["result"]["ids"][0] input_json_final["params"]["workerId"] = worker_id input_json_str = json.dumps(input_json_final) LOGGER.info( "********** Worker details Updated with " "Worker ID*********\n%s\n", input_json_str) # ----------------------------------------------------------------- if "WorkOrderGetResult" in input_json_str or \ "WorkOrderReceiptRetrieve" in input_json_str: input_json_obj = json.loads(input_json_str) input_json_obj["params"]["workOrderId"] = wo_id input_json_str = json.dumps(input_json_obj) LOGGER.info("*********Request Json********* \n%s\n", input_json_str) response = uri_client._postmsg(input_json_str) LOGGER.info("**********Received Response*********\n%s\n", response) # ----------------------------------------------------------------- # Worker details are loaded into Worker_Obj if "WorkerRetrieve" in input_json_str and "result" in response: worker_obj.load_worker(response["result"]["details"]) # ----------------------------------------------------------------- # Poll for "WorkOrderGetResult" and break when you get the result while ("WorkOrderGetResult" in input_json_str and "result" not in response): if response["error"]["code"] != WorkOrderStatus.PENDING: break response = uri_client._postmsg(input_json_str) LOGGER.info("Received Response: %s, \n \n ", response) time.sleep(3) # ----------------------------------------------------------------- # Verify the signature if "WorkOrderGetResult" in input_json_str: if "error" in response: # Response has error, hence skip Signature verification LOGGER.info("Work order response has error; " "skipping signature verification") continue sig_bool = signer.verify_signature(response['result'], worker_obj.verification_key, requester_nonce) try: if sig_bool > 0: LOGGER.info("Signature Verified") encrypt.decrypt_work_order_data_json( response['result']['outData'], session_key, session_iv) else: LOGGER.info("Signature verification Failed") exit(1) except Exception as e: LOGGER.error("ERROR: Failed to verify signature of " + "work order response") exit(1) # ----------------------------------------------------------------- else: LOGGER.info("Input Request %s", input_json_str) response = uri_client._postmsg(input_json_str) LOGGER.info("Received Response: %s , \n \n ", response) exit(0)
class JRPCWorkOrderImpl(WorkOrder): """ This class is for to manage to the work orders from client side. """ def __init__(self, config): self.__uri_client = HttpJrpcClient(config["tcf"]["json_rpc_uri"]) def work_order_submit(self, work_order_id, worker_id, requester_id, work_order_request, id=None): """ Submit a work order request to an Avalon listener. Parameters: work_order_id Work order ID worker_id Worker ID value derived from the worker's DID requester_id Requester ID work_order_request Work order request in JSON RPC string format id Optional JSON RPC request ID """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkOrderSubmit", "id": id } json_rpc_request["params"] = json.loads(work_order_request) logging.debug("Work order request %s", json.dumps(json_rpc_request)) response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def work_order_get_result_nonblocking(self, work_order_id, id=None): """ Get the work order result in non-blocking way. Parameters: work_order_id Work order ID id Optional JSON RPC request ID Returns: JSON RPC response of dictionary type """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkOrderGetResult", "id": id, "params": { "workOrderId": work_order_id } } response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def work_order_get_result(self, work_order_id, id=None): """ Get the work order result in a blocking way until it gets a result or error. Parameters: work_order_id Work order ID id Optional JSON RPC request ID Returns: JSON RPC response of dictionary type """ response = self.work_order_get_result_nonblocking(work_order_id, id) if "error" in response: if response["error"]["code"] != WorkOrderStatus.PENDING: return response else: while "error" in response and \ response["error"]["code"] == WorkOrderStatus.PENDING: response = self.work_order_get_result_nonblocking( work_order_id, id) # TODO: currently pooling after every 2 sec interval # forever. # We should implement feature to timeout after # responseTimeoutMsecs in the request. time.sleep(2) return response else: return response def encryption_key_get(self, worker_id, requester_id, last_used_key_nonce=None, tag=None, signature_nonce=None, signature=None, id=None): """ API to receive a worker's key. Parameters: worker_id Worker ID of the worker whose encryption key is requested last_used_key_nonce Optional nonce associated with the last retrieved key. If it is provided, the key retrieved should be newer than this one. Otherwise any key can be retrieved tag Tag that should be associated with the returned key, e.g. the requester ID. This is an optional parameter. If it is not provided, requester_id is used as a key requester_id ID of the requester that plans to use the returned key to submit one or more work orders using this key signature_nonce Optional nonce associated with the signature and is used only if signature below is also provided signature Optional signature of worker_id, last_used_key_nonce, tag, and signature_nonce. id Optional JSON RPC request ID """ json_rpc_request = { "jsonrpc": "2.0", "method": "EncryptionKeyGet", "id": id, "params": { "workerId": worker_id, "lastUsedKeyNonce": last_used_key_nonce, "tag": tag, "requesterId": requester_id, "signatureNonce": signature_nonce, "signature": signature } } response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def encryption_key_set(self, worker_id, encryption_key, encryption_nonce, tag, signature_nonce, signature, id=None): """ API called by a Worker or Worker Service to receive a Worker's key. Parameters: worker_id ID of the worker to set an encryption key encryption_key Encryption key to set encryption_nonce Nonce associated with the key tag Tag that should be associated with the returned key, e.g. requester ID. signature_nonce Nonce associated with the signature signature Signature generated by the worker on the worker_id, tag and encryption_nonce id Optional JSON RPC request ID Returns: JRPC response with the result of the operation. """ # Not supported for direct model. return { "jsonrpc": "2.0", "method": "EncryptionKeySet", "id": id, "result": { "code": JRPCErrorCodes.INVALID_PARAMETER_FORMAT_OR_VALUE, "message": "Unsupported method" } }
class JRPCWorkOrderReceiptImpl(WorkOrderReceipt): """ This class is an implementation of WorkOrderReceiptInterface to manage work order receipts from the client side. """ def __init__(self, config): self.__uri_client = HttpJrpcClient(config["tcf"]["json_rpc_uri"]) def work_order_receipt_create(self, work_order_id, worker_service_id, worker_id, requester_id, receipt_create_status, work_order_request_hash, requester_nonce, requester_signature, signature_rules, receipt_verification_key, id=None): """ Create a Work Order Receipt JSON RPC request and submit to an Avalon listener. Parameters: work_order_id Work order ID worker_service_id Worker service ID worker_id Worker ID value derived from the worker's DID requester_id Requester ID receipt_create_status Receipt creation status work_order_request_hash Work order request hash value requester_nonce Requester generated nonce requester_signature Signature generated by the requester signature_rules Defines hashing and signing algorithms; separated by forward slash '/' receipt_verification_key Receipt verification key id Optional JSON RPC request ID """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkOrderReceiptCreate", "id": id, "params": { "workOrderId": work_order_id, "workerServiceId": worker_service_id, "workerId": worker_id, "requesterId": requester_id, "receiptCreateStatus": receipt_create_status, "workOrderRequestHash": work_order_request_hash, "requesterGeneratedNonce": requester_nonce, "requesterSignature": requester_signature, "signatureRules": signature_rules, "receiptVerificationKey": receipt_verification_key } } response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def work_order_receipt_update(self, work_order_id, updater_id, update_type, update_data, update_signature, signature_rules, id=None): """ Update a Work Order Receipt JSON RPC request and submit an Avalon listener. Parameters: work_order_id Work Order ID updater_id Updater ID update_type Updater type update_data Receipt update data update_signature Signature of the update signature_rules Defines hashing and signing algorithms; separated by forward slash '/' id Optional JSON RPC request ID """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkOrderReceiptUpdate", "id": id, "params": { "workOrderId": work_order_id, "updaterId": updater_id, "updateType": update_type, "updateData": update_data, "updateSignature": update_signature, "signatureRules": signature_rules } } response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def work_order_receipt_retrieve(self, work_order_id, id=None): """ Retrieve a work order receipt JSON RPC request and submit to an Avalon listener. Parameters: work_order_id Work order ID id Optional Optional JSON RPC request ID """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkOrderReceiptRetrieve", "id": id, "params": { "workOrderId": work_order_id } } response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def work_order_receipt_update_retrieve(self, work_order_id, updater_id, update_index, id=None): """ Retrieve a work order receipt update JSON RPC request and submit to an Avalon listener. Parameters: work_order_id Work order ID id Optional Optional JSON RPC request ID """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkOrderReceiptUpdateRetrieve", "id": id, "params": { "workOrderId": work_order_id, "updaterId": updater_id, "updateIndex": update_index } } response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def work_order_receipt_lookup(self, worker_service_id=None, worker_id=None, requester_id=None, receipt_status=None, id=None): """ Work Order Receipt Lookup All fields are optional and, if present, condition should match for all fields. If none are passed it should return all work order receipts. Parameters: worker_service_id Optional worker service ID to lookup worker_id Optional worker ID value derived from the worker's DID requester_id Optional requester ID to lookup receipt_status Optional receipt status id Optional JSON RPC request ID """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkOrderReceiptLookUp", "id": id, "params": {} } if worker_service_id is not None: json_rpc_request["params"]["workerServiceId"] = worker_service_id if worker_id is not None: json_rpc_request["params"]["workerId"] = worker_id if requester_id is not None: json_rpc_request["params"]["requesterId"] = requester_id if receipt_status is not None: json_rpc_request["params"]["requestCreateStatus"] = receipt_status response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response def work_order_receipt_lookup_next(self, last_lookup_tag, worker_service_id=None, worker_id=None, requester_id=None, receipt_status=None, id=None): """ Work Order Receipt Lookup Next. Call to retrieve subsequent results after calling work_order_receipt_lookup or Parameters: last_lookup_tag Last lookup tag returned by work_order_receipt_lookup worker_service_id Optional worker service ID to lookup worker_id Optional worker ID value derived from the worker's DID requester_id Optional requester ID to lookup receipt_status Optional receipt status id Optional JSON RPC request ID """ json_rpc_request = { "jsonrpc": "2.0", "method": "WorkOrderReceiptLookUpNext", "id": id, "params": { "lastLookUpTag": last_lookup_tag } } if worker_service_id is not None: json_rpc_request["params"]["workerServiceId"] = worker_service_id if worker_id is not None: json_rpc_request["params"]["workerId"] = worker_id if requester_id is not None: json_rpc_request["params"]["requesterId"] = requester_id if receipt_status is not None: json_rpc_request["params"]["requestCreateStatus"] = receipt_status response = self.__uri_client._postmsg(json.dumps(json_rpc_request)) return response