def test_verify_encryption_key_signature(): enc_key_sig_byte = crypto_utility.string_to_byte_array(worker_enc_key) enc_key_hash = crypto_utility.compute_message_hash(enc_key_sig_byte) try: # sign encryption key status, enc_key_signature = \ sig_obj.generate_signature(enc_key_hash, worker_signing_key) enc_key_signature = \ crypto_utility.base64_to_byte_array(enc_key_signature) enc_key_signature_hex = \ crypto_utility.byte_array_to_hex(enc_key_signature) status = sig_obj.verify_encryption_key_signature( enc_key_signature_hex, worker_enc_key, worker_verifying_key) if status == SignatureStatus.PASSED: logging.info("PASSED: verify_encryption_key_signature") return 0 else: logging.info("FAILED: verify_encryption_key_signature") return 1 except Exception as err: logging.info("FAILED: verify_encryption_key_signature") return 1
def test_generate_client_signature(): logging.info("Testing generate_client_signature...") worker_obj = worker.SGXWorkerDetails() worker_obj.encryption_key = worker_enc_key encrypted_session_key = crypto_utility.generate_encrypted_key( session_key, worker_obj.encryption_key) encrypted_session_key_hex = \ crypto_utility.byte_array_to_hex(encrypted_session_key) read_json = read_json_file("wo_request.json", ["./"]) wo_submit_request = json.loads(read_json) wo_request = wo_submit_request["params"] wo_request["workOrderId"] = work_order_id wo_request["workerId"] = worker_id wo_request["requesterId"] = requester_id wo_request["sessionKeyIv"] = session_key_iv wo_request["encryptedSessionKey"] = encrypted_session_key_hex wo_request["requesterNonce"] = requester_nonce try: input_json_str = json.dumps(wo_submit_request) input_json_str, status = sig_obj.generate_client_signature( input_json_str, worker_obj, client_private_key, session_key, session_iv, encrypted_session_key) if status == SignatureStatus.PASSED: logging.info("PASSED: generate_client_signature") return 0 else: logging.info("FAILED: generate_client_signature") return 1 except Exception as err: return 1
def add_encrypted_request_hash(self): """ Calculates request hash based on EEA trusted-computing spec 6.1.8.1 and set encryptedRequestHash parameter in the request. """ sig_obj = signature.ClientSignature() concat_string = self.get_requester_nonce() + \ self.get_work_order_id() + \ self.get_worker_id() + \ self.get_workload_id() + \ self.get_requester_id() concat_bytes = bytes(concat_string, "UTF-8") # SHA-256 hashing is used hash_1 = crypto_utility.byte_array_to_base64( crypto_utility.compute_message_hash(concat_bytes)) hash_2 = sig_obj.calculate_datahash(self.get_in_data()) hash_3 = "" out_data = self.get_out_data() if out_data and len(out_data) > 0: hash_3 = sig_obj.calculate_datahash(out_data) concat_hash = hash_1 + hash_2 + hash_3 concat_hash = bytes(concat_hash, "UTF-8") self.final_hash = crypto_utility.compute_message_hash(concat_hash) encrypted_request_hash = crypto_utility.encrypt_data( self.final_hash, self.session_key, self.session_iv) encrypted_request_hash_hex = crypto_utility.byte_array_to_hex( encrypted_request_hash) self.params_obj["encryptedRequestHash"] = encrypted_request_hash_hex
def calculate_request_hash(self, input_json): """ Function to create the work order request hash as defined in EEA spec 6.1.8.1 Parameters: - input_json is dictionary contains work order request payload as define EEA spec 6.1.1 Returns hash of work order request as string """ wo_request_params = input_json["params"] concat_string = wo_request_params["requesterNonce"] + \ wo_request_params["workOrderId"] + \ wo_request_params["workerId"] + \ wo_request_params["workloadId"] + \ wo_request_params["requesterId"] concat_bytes = bytes(concat_string, "UTF-8") # SHA-256 hashing is used hash_1 = crypto_utility.byte_array_to_base64( crypto_utility.compute_message_hash(concat_bytes)) hash_2 = self.calculate_datahash(wo_request_params["inData"]) hash_3 = "" if "outData" in wo_request_params and \ len(wo_request_params["outData"]) > 0: hash_3 = self.calculate_datahash(wo_request_params["outData"]) concat_hash = hash_1 + hash_2 + hash_3 concat_hash = bytes(concat_hash, "UTF-8") final_hash = crypto_utility.compute_message_hash(concat_hash) final_hash_str = crypto_utility.byte_array_to_hex(final_hash) return final_hash_str
def __init__(self, work_order_id, worker_id, workload_id, requester_id, session_key, session_iv, requester_nonce, verifying_key=None, payload_format="JSON-RPC", response_timeout_msecs=6000, result_uri=None, notify_uri=None, worker_encryption_key=None, data_encryption_algorithm=None): self.params_obj = {} self.set_work_order_id(work_order_id) self.set_response_timeout_msecs(response_timeout_msecs) self.set_payload_format(payload_format) if result_uri: self.set_result_uri(result_uri) if notify_uri: self.set_notify_uri(notify_uri) self.set_worker_id(worker_id) self.set_workload_id(workload_id) self.set_requester_id(requester_id) if worker_encryption_key: self.set_worker_encryption_key( worker_encryption_key.encode("UTF-8").hex()) if data_encryption_algorithm: self.set_data_encryption_algorithm(data_encryption_algorithm) encrypted_session_key = crypto_utility.generate_encrypted_key( session_key, worker_encryption_key) self.set_encrypted_session_key( crypto_utility.byte_array_to_hex(encrypted_session_key)) self.session_iv = session_iv self.set_session_key_iv(crypto_utility.byte_array_to_hex(session_iv)) self.set_requester_nonce(requester_nonce) self.params_obj["encryptedRequestHash"] = "" self.params_obj["requesterSignature"] = "" self.params_obj["inData"] = [] self.session_key = session_key
def __init__( self, work_order_id, worker_id, workload_id, requester_id, session_key, session_iv, requester_nonce, verifying_key=None, payload_format="JSON-RPC", response_timeout_msecs=6000, result_uri=None, notify_uri=None, worker_encryption_key=None, data_encryption_algorithm=None): self.params_obj = {} self.set_work_order_id(work_order_id) self.set_response_timeout_msecs(response_timeout_msecs) self.set_payload_format(payload_format) if result_uri: self.set_result_uri(result_uri) if notify_uri: self.set_notify_uri(notify_uri) self.set_worker_id(worker_id) self.set_workload_id(workload_id) self.set_requester_id(requester_id) if worker_encryption_key: self.set_worker_encryption_key( worker_encryption_key) if data_encryption_algorithm: self.set_data_encryption_algorithm(data_encryption_algorithm) if worker_encryption_key and session_key: try: encrypted_session_key = crypto_utility.generate_encrypted_key( session_key, worker_encryption_key) self.set_encrypted_session_key( crypto_utility.byte_array_to_hex(encrypted_session_key)) except Exception as err: raise ValueError("Encrypting Session key failed: \ Invalid session key or worker encryption key") self.session_iv = session_iv self.set_session_key_iv( crypto_utility.byte_array_to_hex(session_iv) ) self.set_requester_nonce(requester_nonce) self.params_obj["encryptedRequestHash"] = "" self.params_obj["requesterSignature"] = "" self.params_obj["inData"] = [] self.session_key = session_key
def update_receipt(self, work_order_id, update_type, update_data, signing_key): """ Update the existing work order receipt with update_type and update_data. Parameters: work_order_id Work order ID whose receipt needs to be updated update_type Update type. These values correspond to receipt status as defined in EEA Spec 7.1.1 update_data Update-specific data that depends on the workOrderStatus Returns: JSON RPC work order update receipt request of type dictionary """ data = update_data if update_type in [ ReceiptCreateStatus.PROCESSED.value, ReceiptCreateStatus.COMPLETED.value ]: # Work Order Receipt status is set to be completed or processed, # then update_data should be work order response. wo_resp_str = json.dumps(update_data) wo_resp_bytes = bytes(wo_resp_str, "UTF-8") # Hash of work order response is update_data wo_resp_hash = crypto_utility.compute_message_hash(wo_resp_bytes) wo_resp_hash_str = crypto_utility.byte_array_to_hex(wo_resp_hash) data = wo_resp_hash_str public_key = signing_key.verifying_key.to_pem().decode("ascii") updater_id = crypto_utility.strip_begin_end_public_key(public_key) wo_receipt_update = { "workOrderId": work_order_id, "updaterId": updater_id, "updateType": update_type, "updateData": data, "signatureRules": self.HASHING_ALGORITHM + "/" + self.SIGNING_ALGORITHM, "receiptVerificationKey": public_key } wo_receipt_str = wo_receipt_update["workOrderId"] + \ str(wo_receipt_update["updateType"]) + \ wo_receipt_update["updateData"] wo_receipt_bytes = bytes(wo_receipt_str, "UTF-8") wo_receipt_hash = crypto_utility.compute_message_hash(wo_receipt_bytes) status, wo_receipt_sign = self.sig_obj.generate_signature( wo_receipt_hash, signing_key) if status is False: # if generating signature failed. wo_receipt_update["updateSignature"] = "" raise Exception("Generate signature is failed") else: wo_receipt_update["updateSignature"] = wo_receipt_sign return wo_receipt_update
def __validate_work_order_receipt_update_req(self, wo_receipt_req): """ Function to validate the work order receipt create request parameters Parameters: - wo_receipt_req is work order receipt request as dictionary Returns - tuple containing validation status(Boolean) and error message(string) """ valid_params = [ "workOrderId", "updaterId", "updateType", "updateData", "updateSignature", "signatureRules", "receiptVerificationKey" ] for key in wo_receipt_req["params"]: if key not in valid_params: return False, "Missing parameter " + key + " in the request" else: if key in ["workOrderId", "updaterId"]: if not is_valid_hex_str(wo_receipt_req[key]): return False, "invalid data parameter for " + key elif key in ["updateData", "updateSignature"]: try: base64.b64decode(wo_receipt_req[key]) except Exception: return False, "Invalid data format for " + key update_type = wo_receipt_req["params"]["updateType"] try: update_enum_value = ReceiptCreateStatus(update_type) except Exception as err: return False, "Invalid receipt update type {}: {}".format( update_enum_value, str(err)) # If update type is completed or processed, # it is a hash value of the Work Order Response if wo_receipt_req["params"]["updateType"] in [ ReceiptCreateStatus.PROCESSED.value, ReceiptCreateStatus.COMPLETED.value ]: wo_id = wo_receipt_req["params"]["workOrderId"] # Load the work order response and calculate it's hash wo_resp = self.kv_helper.get("wo-responses", wo_id) wo_resp_bytes = bytes(wo_resp, "UTF-8") wo_resp_hash = worker_hash.WorkerHash().compute_message_hash( wo_resp_bytes) wo_resp_hash_str = crypto_utility.byte_array_to_hex(wo_resp_hash) if wo_resp_hash_str != wo_receipt_req["params"]["updateData"]: return False, "Invalid Update data in the request" # If all validation is pass return True, ""
def add_encrypted_request_hash(self): """ Calculates request hash based on EEA trusted-computing spec 6.1.8.1 and set encryptedRequestHash parameter in the request. """ try: sig_obj = signature.ClientSignature() self.request_hash = sig_obj.calculate_request_hash(self.params_obj) encrypted_request_hash = crypto_utility.encrypt_data( self.request_hash, self.session_key, self.session_iv) enc_request_hash_hex = crypto_utility.byte_array_to_hex( encrypted_request_hash) self.params_obj["encryptedRequestHash"] = enc_request_hash_hex return None except Exception as err: return util.create_error_response( WorkOrderStatus.INVALID_PARAMETER_FORMAT_OR_VALUE, 0, err)
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)
def create_request(self, work_order_id, worker_id, workload_id, requester_id, session_key, session_iv, requester_nonce, verifying_key=None, payload_format="JSON-RPC", response_timeout_msecs=6000, result_uri=None, notify_uri=None, worker_encryption_key=None, data_encryption_algorithm=None, encrypted_session_key=None): """validate and creates workorder request with received values""" if work_order_id: self.set_work_order_id(work_order_id) self.set_response_timeout_msecs(response_timeout_msecs) self.set_payload_format(payload_format) self.set_requester_nonce(requester_nonce) self.session_key = session_key self.set_workload_id(workload_id) self.set_worker_id(worker_id) if requester_id is not None: self.set_requester_id(requester_id) if session_iv: self.set_session_key_iv( crypto_utility.byte_array_to_hex(session_iv)) if result_uri: self.set_result_uri(result_uri) if notify_uri: self.set_notify_uri(notify_uri) if worker_encryption_key: self.set_worker_encryption_key(worker_encryption_key) if data_encryption_algorithm: self.set_data_encryption_algorithm(data_encryption_algorithm) self.set_encrypted_session_key(encrypted_session_key) code, err_msg = WOcheck.schema_validation("sdk_WorkOrderSubmit", self.params_obj) # When the WorkorderSubmit request fails basic Json Validation # the init object created is deleted to avoid futhur processing # on that object by the user. if not code: return util.create_error_response( WorkOrderStatus.INVALID_PARAMETER_FORMAT_OR_VALUE, 0, err_msg) self.set_worker_encryption_key( worker_encryption_key.encode("UTF-8").hex()) self.session_iv = session_iv self.params_obj["encryptedRequestHash"] = "" self.params_obj["requesterSignature"] = "" self.params_obj["inData"] = list() if encrypted_session_key is None: try: encrypted_session_key = crypto_utility.generate_encrypted_key( session_key, worker_encryption_key) self.set_encrypted_session_key( crypto_utility.byte_array_to_hex(encrypted_session_key)) except Exception as err: return util.create_error_response( WorkOrderStatus.INVALID_PARAMETER_FORMAT_OR_VALUE, 0, err) return None
"gTVuC8crfJBUt+zOpiXUv8DaD+kVdAdDdZlgHBW2K9gcivXDegAz84WPbhDrQ3CM\n"\ "M9SJ22B99CR1eG/ez/wzY0GiAZOCIB31IWk34Ehc8tTKjm8fVnXWvYJnXKxACnYd\n"\ "3isoueUA1x01+U0HDnY5ZR0CAwEAAQ==\n"\ "-----END PUBLIC KEY-----\n" sig_obj = signature.ClientSignature() # client values worker_id = secrets.token_hex(32) work_order_id = secrets.token_hex(32) requester_id = secrets.token_hex(32) requester_nonce = secrets.token_hex(16) session_key = crypto_utility.generate_key() session_iv = crypto_utility.generate_iv() session_key_iv = crypto_utility.byte_array_to_hex(session_iv) client_private_key = crypto_utility.generate_signing_keys() client_public_key = crypto_utility.get_verifying_key(client_private_key) # worker values worker_nonce = secrets.token_hex(16) worker_enc_key = rsa_public_key worker_signing_key = crypto_utility.generate_signing_keys() worker_verifying_key = crypto_utility.get_verifying_key(worker_signing_key) # ----------------------------------------------------------------------------- def test_calculate_datahash(): read_json = read_json_file("wo_request.json", ["./"])