def __encrypt_workorder_indata(self, input_json_params, session_key, session_iv, worker_encryption_key, data_key=None, data_iv=None): """ Function to encrypt inData of workorder Parameters: - input_json_params is inData and outData elements within the work order request as per TCF API 6.1.7 Work Order Data Formats. - session_key is a one-time encryption 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. """ indata_objects = input_json_params['inData'] indata_objects.sort(key=lambda x: x['index']) input_json_params['inData'] = indata_objects logger.info("Encrypting Workorder Data") i = 0 for item in indata_objects: data = item['data'].encode('UTF-8') e_key = item['encryptedDataEncryptionKey'].encode('UTF-8') if (not e_key) or (e_key == "null".encode('UTF-8')): enc_data = crypto_utility.encrypt_data(data, session_key, session_iv) input_json_params['inData'][i]['data'] = \ crypto.byte_array_to_base64(enc_data) logger.debug("encrypted indata - %s", crypto.byte_array_to_base64(enc_data)) elif e_key == "-".encode('UTF-8'): # Skip encryption and just encode workorder data to # base64 format. input_json_params['inData'][i]['data'] = \ crypto.byte_array_to_base64(data) else: enc_data = crypto_utility.encrypt_data(data, data_key, data_iv) input_json_params['inData'][i]['data'] = \ crypto.byte_array_to_base64(enc_data) logger.debug("encrypted indata - %s", crypto.byte_array_to_base64(enc_data)) i = i + 1 logger.debug("Workorder InData after encryption: %s", indata_objects)
def _add_data_item(self, data_copy, data_item): try: index = data_item['index'] data = data_item['data'].encode('UTF-8') if 'encryptedDataEncryptionKey' in data_item: e_key = data_item['encryptedDataEncryptionKey'].encode('UTF-8') else: e_key = "null".encode('UTF-8') if (not e_key) or (e_key == "null".encode('UTF-8')): enc_data = self.encrypt_data(data, self.session_key, self.session_iv) base64_enc_data = (crypto.byte_array_to_base64(enc_data)) if 'dataHash' in data_item: dataHash_enc_data = (self.byte_array_to_hex_str( crypto.compute_message_hash(data))) logger.debug("encrypted indata - %s", crypto.byte_array_to_base64(enc_data)) elif e_key == "-".encode('UTF-8'): # Skip encryption and just encode workorder data # to base64 format base64_enc_data = (crypto.byte_array_to_base64(data)) if 'dataHash' in data_item: dataHash_enc_data = (self.byte_array_to_hex_str( crypto.compute_message_hash(data))) else: data_key = None data_iv = None enc_data = self.encrypt_data(data, data_key, data_iv) base64_enc_data = (crypto.byte_array_to_base64(enc_data)) if 'dataHash' in data_item: dataHash_enc_data = (self.byte_array_to_hex_str( crypto.compute_message_hash(data))) logger.debug("encrypted indata - %s", crypto.byte_array_to_base64(enc_data)) enc_indata_item = { 'index': index, 'dataHash': dataHash_enc_data, 'data': base64_enc_data, 'encryptedDataEncryptionKey': data_item['encryptedDataEncryptionKey'], 'iv': data_item['iv'] } data_copy.append(enc_indata_item) return data_copy except Exception: return None
def _compute_requester_signature(self): self.public_key = self.private_key.GetPublicKey().Serialize() signature_result = self.private_key.SignMessage(self.final_hash) self.requester_signature = crypto.byte_array_to_base64( signature_result) self.params_obj["requesterSignature"] = self.requester_signature self.params_obj["verifyingKey"] = self.public_key
def add_encrypted_request_hash(self): """ calculates request has 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.byte_array_to_base64( crypto.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.compute_message_hash(concat_hash) encrypted_request_hash = crypto_utility.encrypt_data( self.final_hash, self.session_key, self.session_iv) self.params_obj["encryptedRequestHash"] = crypto.byte_array_to_hex( encrypted_request_hash)
def calculate_request_hash(self, input_json): """ Function to create the work order reuest 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.byte_array_to_base64( crypto.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.compute_message_hash(concat_hash) final_hash_str = crypto.byte_array_to_hex(final_hash) return final_hash_str
def calculate_datahash(self, data_objects): """ Function to calculate a hash value of the array concatenating dataHash, data, encryptedDataEncryptionKey, iv for each item in the inData/outData array Parameters: - data_objects is each item in inData or outData part of workorder request as per TCF API 6.1.7 Work Order Data Formats """ hash_str = "" for item in data_objects: datahash = "".encode('UTF-8') e_key = "".encode('UTF-8') iv = "".encode('UTF-8') if 'dataHash' in item: datahash = item['dataHash'].encode('UTF-8') data = item['data'].encode('UTF-8') if 'encryptedDataEncryptionKey' in item: e_key = item['encryptedDataEncryptionKey'].encode('UTF-8') if 'iv' in item: iv = item['iv'].encode('UTF-8') concat_string = datahash + data + e_key + iv concat_hash = bytes(concat_string) hash = crypto.compute_message_hash(concat_hash) hash_str = hash_str + crypto.byte_array_to_base64(hash) return hash_str
def __calculate_hash_on_concatenated_string(self, input_json_params, nonce_hash): """ Function to calculate a hash value of the string concatenating the following values: requesterNonce, workOrderId, workerId, workloadId, and requesterId. Parameters: - input_json_params is a collection of parameters, as per TCF APi 6.1.1 Work Order Request Payload - nonce_hash is SHA256 hashed value of a random string generated by the participant. """ workorder_id = (input_json_params['workOrderId']).encode('UTF-8') worker_id = (input_json_params['workerId']).encode('UTF-8') workload_id = "".encode('UTF-8') if 'workloadId' in input_json_params: workload_id = (input_json_params['workloadId']).encode('UTF-8') requester_id = (input_json_params['requesterId']).encode('UTF-8') concat_string = nonce_hash + workorder_id + worker_id + workload_id + \ requester_id concat_hash = bytes(concat_string) # SHA-256 hashing is used hash_1 = crypto.compute_message_hash(concat_hash) result_hash = crypto.byte_array_to_base64(hash_1) return result_hash
def __encrypt_data(self, data, encrypted_data_encryption_key=None, data_iv=None): data = data.encode("UTF-8") if encrypted_data_encryption_key is None or \ encrypted_data_encryption_key == "" or \ encrypted_data_encryption_key == "null": enc_data = crypto_utility.encrypt_data( data, self.session_key, self.session_iv ) return crypto.byte_array_to_base64(enc_data) elif encrypted_data_encryption_key == "-".encode('UTF-8'): # Skip encryption and just encode workorder data to # base64 format. enc_data = crypto.byte_array_to_base64(data) return enc_data else: enc_data = crypto_utility.encrypt_data( data, encrypted_data_encryption_key, data_iv) return crypto.byte_array_to_base64(enc_data)
def main(): logging.info("Executing Unit test cases for encryption at client") msg = "This is client request" iv = crypto_utility.generate_sessioniv() enc_sess_key = test_encrypt_session_key(iv) if enc_sess_key: enc_data = test_encrypt_data(iv, enc_sess_key[:16], msg) if enc_data: b64_enc_data = crypto.byte_array_to_base64(enc_data) iv_hex = crypto.byte_array_to_hex(iv) test_decrypt_data(iv_hex, enc_sess_key[:16], msg, b64_enc_data) logging.info( "Unit test case execution for encryption/decryption complete.")
def _compute_encrypted_request_hash(self): first_string = self.nonce_hash worker_order_id = self.get_work_order_id() if worker_order_id is not None: first_string = first_string + worker_order_id.encode('UTF-8') logger.info("first_string - %s", first_string) else: first_string = first_string + "".encode('UTF-8') worker_id = self.get_worker_id() if worker_id is not None: first_string = first_string + worker_id.encode('UTF-8') else: first_string = first_string + "".encode('UTF-8') workload_id = self.get_workload_id() if workload_id is not None: first_string = first_string + workload_id.encode('UTF-8') else: first_string = first_string + "".encode('UTF-8') requester_id = self.get_requester_id() if requester_id is not None: first_string = first_string + requester_id.encode('UTF-8') else: first_string = first_string + "".encode('UTF-8') concat_hash = bytes(first_string) self.hash_1 = crypto.byte_array_to_base64( crypto.compute_message_hash(concat_hash)) in_data = self.get_in_data() out_data = self.get_out_data() self.hash_2 = "" if in_data is not None: self.hash_2 = self._compute_hash_string(in_data) self.hash_3 = "" if out_data is not None: self.hash_3 = self._compute_hash_string(out_data) final_string = self.hash_1 + self.hash_2 + self.hash_3 self.final_hash = crypto.compute_message_hash( bytes(final_string, 'UTF-8')) self.encrypted_request_hash = self.byte_array_to_hex_str( self.encrypt_data(self.final_hash, self.session_key, self.session_iv)) self.params_obj["encryptedRequestHash"] = self.encrypted_request_hash
def EncryptionKeyGet(self, **params): """ Function to process get encryption key request. Parameters: - param is the 'param' object in the a worker request as per TCF API 6.1.10 Get Encryption Key Request Payload """ worker_id = str(params['workerId']) value = self.kv_helper.get("workers", worker_id) if value is None: raise JSONRPCDispatchException( WorkerError.INVALID_PARAMETER_FORMAT_OR_VALUE, "Worker id not found in the database. Hence invalid parameter") worker_type_data = json.loads(value).get("details").get( "workerTypeData") encryptionKey = worker_type_data["encryptionKey"] try: encryptionKeyNonce = worker_type_data["encryptionKeyNonce"] except Exception: encryptionKeyNonce = crypto.random_bit_string(NO_OF_BYTES) tag = "" # calculate signature concat_string = worker_id.encode('UTF-8') + encryptionKey.encode( 'UTF-8') + encryptionKeyNonce.encode('UTF-8') + tag.encode('UTF-8') concat_hash = bytes(concat_string) hash_1 = crypto.compute_message_hash(concat_hash) s1 = crypto.byte_array_to_base64(hash_1) # Requires worker private key to sign. # signature = self.PrivateKey.SignMessage(hash) result = { "workerId": worker_id, "encryptionKey": encryptionKey, "encryptionKeyNonce": encryptionKeyNonce, "tag": "", "signature": s1, } return result
def generate_signature(self, hash, private_key): """ Function to generate signature object Parameters: - hash is the combined array of all hashes calculated on the message - private_key is Client private key Returns tuple(status, signature) """ try: self.private_key = private_key self.public_key = self.private_key.GetPublicKey().Serialize() signature_result = self.private_key.SignMessage(hash) signature_base64 = crypto.byte_array_to_base64(signature_result) except Exception as err: logger.error("Exception occurred during signature generation: %s", str(err)) return False, None return True, signature_base64
def _compute_hash_string(self, data): final_hash_str = "" hash_string = "" for data_item in data: data = "".encode('UTF-8') datahash = "".encode('UTF-8') e_key = "".encode('UTF-8') iv = "".encode('UTF-8') if 'dataHash' in data_item: datahash = data_item['dataHash'].encode('UTF-8') if 'data' in data_item: data = data_item['data'].encode('UTF-8') if 'encryptedDataEncryptionKey' in data_item: e_key = \ data_item['encryptedDataEncryptionKey'].encode('UTF-8') if 'iv' in data_item: iv = data_item['iv'].encode('UTF-8') hash_string = datahash + data + e_key + iv complete_bytes = bytes(hash_string) hash = crypto.compute_message_hash(complete_bytes) final_hash_str = final_hash_str + crypto.byte_array_to_base64(hash) return final_hash_str
def execute(self): serialized_byte_array = crypto.string_to_byte_array(self.work_order) encrypted_request = crypto.byte_array_to_base64(serialized_byte_array) try: encoded_encrypted_response = \ self.enclave_service.send_to_sgx_worker(encrypted_request) assert encoded_encrypted_response except Exception as err: logger.exception('workorder request invocation failed: %s', str(err)) raise try: decrypted_response = crypto.base64_to_byte_array( encoded_encrypted_response) response_string = crypto.byte_array_to_string(decrypted_response) response_parsed = json.loads(response_string[0:-1]) except Exception as err: logger.exception('workorder response is invalid: %s', str(err)) raise return response_parsed
def add_json_values(self, input_json_temp, worker_obj, private_key, tamper): self.private_key = private_key self.worker_obj = worker_obj input_params_list = input_json_temp["params"].keys() if "responseTimeoutMSecs" in input_params_list: if input_json_temp["params"]["responseTimeoutMSecs"] != "": self.set_response_timeout_msecs( input_json_temp["params"]["responseTimeoutMSecs"]) else: self.set_response_timeout_msecs(6000) if "payloadFormat" in input_params_list: if input_json_temp["params"]["payloadFormat"] != "": self.set_payload_format( input_json_temp["params"]["payloadFormat"]) else: self.set_payload_format("JSON-RPC") if "resultUri" in input_params_list: if input_json_temp["params"]["resultUri"] != "": self.set_result_uri(input_json_temp["params"]["resultUri"]) else: self.set_result_uri("") if "notifyUri" in input_params_list: if input_json_temp["params"]["notifyUri"] != "": self.set_notify_uri(input_json_temp["params"]["notifyUri"]) else: self.set_notify_uri("") if "workOrderId" in input_params_list: if input_json_temp["params"]["workOrderId"] != "": self.set_work_order_id( input_json_temp["params"]["workOrderId"]) else: work_order_id = hex(random.randint(1, 2**64 - 1)) self.set_work_order_id(work_order_id) if "workerId" in input_params_list: if input_json_temp["params"]["workerId"] != "": self.set_worker_id(input_json_temp["params"]["workerId"]) else: self.set_worker_id(worker_obj.worker_id) if "workloadId" in input_params_list: if input_json_temp["params"]["workloadId"] != "": self.set_workload_id(input_json_temp["params"] ["workloadId"].encode('UTF-8').hex()) else: workload_id = "echo-client" self.set_workload_id(workload_id.encode('UTF-8').hex()) if "requesterId" in input_params_list: if input_json_temp["params"]["requesterId"] != "": self.set_requester_id(input_json_temp["params"]["requesterId"]) else: self.set_requester_id("0x3456") if "workerEncryptionKey" in input_params_list: if input_json_temp["params"]["workerEncryptionKey"] != "": self.set_worker_encryption_key( input_json_temp["params"]["workerEncryptionKey"].encode( "UTF-8").hex()) else: self.set_worker_encryption_key( crypto_utils.strip_begin_end_public_key( worker_obj.encryption_key).encode("UTF-8").hex()) if "dataEncryptionAlgorithm" in input_params_list: if input_json_temp["params"]["dataEncryptionAlgorithm"] != "": self.set_data_encryption_algorithm( input_json_temp["params"]["dataEncryptionAlgorithm"]) else: self.set_data_encryption_algorithm("AES-GCM-256") if "sessionKeyIv" in input_params_list: if input_json_temp["params"]["sessionKeyIv"] != "": self.set_session_key_iv( input_json_temp["params"]["sessionKeyIv"]) else: self.set_session_key_iv( self.byte_array_to_hex_str(self.session_iv)) if "encryptedSessionKey" in input_params_list: if input_json_temp["params"]["encryptedSessionKey"] != "": self.set_encrypted_session_key( input_json_temp["params"]["encryptedSessionKey"]) self.encrypted_session_key = ( input_json_temp["params"]["encryptedSessionKey"]) else: self.encrypted_session_key = (self.generate_encrypted_key( self.session_key, worker_obj.encryption_key)) self.set_encrypted_session_key( self.byte_array_to_hex_str(self.encrypted_session_key)) if "requesterNonce" in input_params_list: if input_json_temp["params"]["requesterNonce"] != "": self.nonce = crypto.string_to_byte_array( input_json_temp["params"]["requesterNonce"]) self.nonce_hash = (crypto.byte_array_to_base64( crypto.compute_message_hash(self.nonce))).encode('UTF-8') self.set_requester_nonce( crypto.byte_array_to_base64( crypto.compute_message_hash(self.nonce))) else: self.nonce = crypto.random_bit_string(NO_OF_BYTES) self.nonce_hash = (crypto.byte_array_to_base64( crypto.compute_message_hash(self.nonce))).encode('UTF-8') self.set_requester_nonce( crypto.byte_array_to_base64( crypto.compute_message_hash(self.nonce))) if "inData" in input_params_list: if input_json_temp["params"]["inData"] != "": input_json_inData = input_json_temp["params"]["inData"] self.add_in_data(input_json_inData) else: self.params_obj["inData"] = "" if "outData" in input_params_list: if input_json_temp["params"]["outData"] != "": input_json_outData = input_json_temp["params"]["outData"] self.add_out_data(input_json_outData) else: self.params_obj["outData"] = "" if "encryptedRequestHash" in input_params_list: if input_json_temp["params"]["encryptedRequestHash"] != "": self.params_obj["encryptedRequestHash"] = \ input_json_temp["params"]["encryptedRequestHash"] else: self._compute_encrypted_request_hash() if "requesterSignature" in input_params_list: if input_json_temp["params"]["requesterSignature"] != "": self.params_obj["requesterSignature"] = \ input_json_temp["params"]["requesterSignature"] else: self.params_obj["requesterSignature"] = "" if "verifyingKey" in input_params_list: if input_json_temp["params"]["verifyingKey"] != "": self.params_obj["verifyingKey"] = \ input_json_temp["params"]["verifyingKey"] if "default" in tamper.keys(): if "params" in tamper["default"].keys(): for key, value in tamper["default"]["params"]: param = key value = value self.set_unknown_parameter(param, value)
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 TCF 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 None 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 None 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 None 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 input_json_params["requesterNonce"] and \ is_valid_hex_str(input_json_params["requesterNonce"]): nonce = crypto.string_to_byte_array( input_json_params["requesterNonce"]) else: # [NO_OF_BYTES] 16 BYTES for nonce. # This is the recommendation by NIST to # avoid collisions by the "Birthday Paradox". nonce = crypto.random_bit_string(NO_OF_BYTES) request_nonce_hash = crypto.compute_message_hash(nonce) nonce_hash = ( crypto.byte_array_to_base64(request_nonce_hash)).encode('UTF-8') hash_string_1 = self.__calculate_hash_on_concatenated_string( input_json_params, nonce_hash) 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'] data_objects.sort(key=lambda x: x['index']) 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 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['requesterNonce'] = crypto.byte_array_to_base64( request_nonce_hash) 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