/* * Copyright (C) 2011-2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /** * File: pse_op.cpp * Description: Definition for interfaces provided by platform service enclave.. * * Definition for interfaces provided by platform service enclave. */ #include "utility.h" #include "session_mgr.h" #include "monotonic_counter.h" #include "trusted_time.h" #include "pse_op_t.c" #include "dh.h" #include "pairing_blob.h" #include "monotonic_counter_database_sqlite_bin_hash_tree_utility.h" #include "sgx_lfence.h" #define BREAK_ON_MALLOC_FAIL(ptr,ret_val) if (!(ptr)) {ret_val=PSE_OP_INTERNAL_ERROR;break;} typedef pse_op_error_t (*srv_pfn_t)(const isv_attributes_t &, const uint8_t*, uint8_t *); static const struct service_handler_t { uint16_t service_id; uint16_t service_cmd; uint16_t req_size; uint16_t resp_size; srv_pfn_t srv_pfn; } service_handler[] = { {PSE_MC_SERVICE, PSE_MC_CREATE, sizeof(pse_mc_create_req_t), sizeof(pse_mc_create_resp_t), pse_mc_create}, {PSE_MC_SERVICE, PSE_MC_READ, sizeof(pse_mc_read_req_t), sizeof(pse_mc_read_resp_t), pse_mc_read }, {PSE_MC_SERVICE, PSE_MC_INC, sizeof(pse_mc_inc_req_t), sizeof(pse_mc_inc_resp_t), pse_mc_inc }, {PSE_MC_SERVICE, PSE_MC_DEL, sizeof(pse_mc_del_req_t), sizeof(pse_mc_del_resp_t), pse_mc_del }, {PSE_TRUSTED_TIME_SERVICE, PSE_TIMER_READ, sizeof(pse_timer_read_req_t), sizeof(pse_timer_read_resp_t), pse_read_timer}, }; /******************************************************************* ** Function name: create_session_wrapper ** Descrption: ** This function will initialize the AppEnclave<->Pse-Op session establishment process. ** Parameters: ** tick - the number of milliseconds that have elapsed since the system was started, ** used to detect which session is idle for the longest time. ** sid - session id ** Returns: ae_error_t *******************************************************************/ ae_error_t create_session_wrapper( /* IN */ uint64_t tick, /* OUT */ uint32_t* sid, /* OUT */ pse_dh_msg1_t* dh_msg1) { pse_op_error_t op_ret = OP_SUCCESS; if (!sid || !dh_msg1) { return PSE_OP_PARAMETER_ERROR; } // ephemeral session must have been established if(!is_eph_session_active()) { // the ephemeral session is not active return PSE_OP_EPHEMERAL_SESSION_INVALID; } op_ret = pse_create_session(tick, *sid, *dh_msg1); return error_reinterpret(op_ret); } /******************************************************************* ** Function name: exchange_report_wrapper ** Descrption: ** This function is used to exchange report between AppEnclave and Pse-Op, if success, ** a session will be established. ** Parameters: ** tick - the number of milliseconds that have elapsed since the system was started, ** used to detect which session is idle for the longest time. ** sid - session id ** dh_msg2 - DH message2 ** dh_msg3 - DH message3 ** Returns: ae_error_t *******************************************************************/ ae_error_t exchange_report_wrapper( /* IN */ uint64_t tick, /* IN */ uint32_t sid, /* IN */ sgx_dh_msg2_t* dh_msg2, /* OUT */ pse_dh_msg3_t* dh_msg3) { pse_op_error_t op_ret = OP_SUCCESS; if(!dh_msg2 || !dh_msg3) { return PSE_OP_PARAMETER_ERROR; } // ephemeral session must have been established if(!is_eph_session_active()) // the ephemeral session is not active { return PSE_OP_EPHEMERAL_SESSION_INVALID; } op_ret = pse_exchange_report(tick, sid, *dh_msg2, *dh_msg3); return error_reinterpret(op_ret); } /******************************************************************* ** Function name: close_session_wrapper ** Descrption: ** This function is used to close a session ** Parameters: ** sid - session id ** Returns: ae_error_t *******************************************************************/ ae_error_t close_session_wrapper( /* IN */ uint32_t sid ) { return error_reinterpret(pse_close_session(sid)); } /******************************************************************* ** Function name: invoke_service_wrapper ** Descrption: ** This function is used to invoke a service call ** Parameters: ** tick - the number of milliseconds that have elapsed since the system was started, ** used to detect which session is idle for the longest time. ** req_msg - service request message ** req_msg_size - size of request message ** resp_msg - service response message ** resp_msg_size - size of response message ** Returns: ae_error_t *******************************************************************/ ae_error_t invoke_service_wrapper ( /* IN */ uint64_t tick, /* IN */ uint8_t* req_msg, /* IN */ uint32_t req_msg_size, /* OUT */ uint8_t* resp_msg, /* IN */ uint32_t resp_msg_size) { // check parameter ae_error_t ae_ret = AE_SUCCESS; pse_message_t* pse_req_msg = (pse_message_t*)req_msg; pse_message_t* pse_resp_msg = (pse_message_t*)resp_msg; pse_op_error_t op_ret; if (!req_msg || !resp_msg) { return PSE_OP_PARAMETER_ERROR; } // // make sure the header is inside enclave // if (req_msg_size < sizeof(pse_message_t)) { return PSE_OP_PARAMETER_ERROR; } // // if this mispredicts, we might overflow below // sgx_lfence(); if (pse_req_msg->payload_size > UINT32_MAX - sizeof(pse_message_t) // check potential overflow || req_msg_size != sizeof(pse_message_t) + pse_req_msg->payload_size) { return PSE_OP_PARAMETER_ERROR; } if (resp_msg_size < sizeof(pse_message_t) // make sure the header is inside enclave || pse_req_msg->exp_resp_size > UINT32_MAX - sizeof(pse_message_t) // check potential overflow || resp_msg_size < sizeof(pse_message_t) + pse_req_msg->exp_resp_size) { return PSE_OP_PARAMETER_ERROR; } // // put LFENCE here mostly for pse_req_msg->payload_size // check above. I don't think we use // pse_req_msg->exp_resp_size to calculate // any pointers. // sgx_lfence(); pse_session_t* session = sid2session(pse_req_msg->session_id); // ephemeral session must have been established if(!is_eph_session_active()) { // the ephemeral session is not active return PSE_OP_EPHEMERAL_SESSION_INVALID; } //if session is invalid (session not exists or established, or sequence num overflow) if (!is_isv_session_valid(session)) { return PSE_OP_SESSION_INVALID; } // update session tick update_session_tick_count(session, tick); //clear response message memset(resp_msg, 0, resp_msg_size); uint8_t* req = (uint8_t*)malloc(pse_req_msg->payload_size); uint8_t* resp= NULL; uint32_t session_seq_num = get_session_seq_num(session); do { BREAK_ON_MALLOC_FAIL(req, ae_ret) // decrypt service request message using session key if(false == decrypt_msg(pse_req_msg, req, (sgx_key_128bit_t*)session->active.AEK)) { ae_ret = PSE_OP_SERVICE_MSG_ERROR; break; } pse_req_hdr_t* req_hdr = (pse_req_hdr_t*)req; // check session sequence number if(req_hdr->seq_num != session_seq_num) { ae_ret = PSE_OP_SESSION_INVALID; //close session free_session(session); break; } // Dispatch the service request to the proper handler int i; int service_count = static_cast(sizeof(service_handler) / sizeof(service_handler_t)); for (i = 0; i < service_count; i++) { // // might mispredict the end of the loop // sgx_lfence(); if (req_hdr->service_id == service_handler[i].service_id && req_hdr->service_cmd == service_handler[i].service_cmd) { if (pse_req_msg->payload_size != service_handler[i].req_size || pse_req_msg->exp_resp_size < service_handler[i].resp_size) // response message buffer must be large enough to hold response data { ae_ret = PSE_OP_SERVICE_MSG_ERROR; goto clean_up; } resp = (uint8_t*)malloc(service_handler[i].resp_size); if (resp == NULL) { ae_ret = PSE_OP_INTERNAL_ERROR; goto clean_up; } // // in case payload_size, req_size comparisons // mispredict // sgx_lfence(); // serve the request op_ret = service_handler[i].srv_pfn(session->isv_attributes, req, resp); if(op_ret != OP_SUCCESS) { ae_ret = error_reinterpret(op_ret); goto clean_up; } // set payload size for valid requests pse_resp_msg->payload_size = service_handler[i].resp_size; break; } } if (i == service_count) { // service_id or service_cmd mismatch resp = (uint8_t*)malloc(sizeof(pse_resp_hdr_t)); BREAK_ON_MALLOC_FAIL(resp, ae_ret) // for unknown requests, payload data only includes response header pse_resp_msg->payload_size = sizeof(pse_resp_hdr_t); // set error status ((pse_resp_hdr_t*)resp)->status = PSE_ERROR_UNKNOWN_REQ; } // prepare the response message pse_resp_hdr_t* resp_hdr = (pse_resp_hdr_t*)resp; pse_resp_msg->exp_resp_size = 0; pse_resp_msg->session_id = pse_req_msg->session_id; //set response header, status code is already set in service functions resp_hdr->seq_num = session_seq_num + 1; // addition overflow already checked in is_isv_session_valid() resp_hdr->service_id = req_hdr->service_id; resp_hdr->service_cmd = req_hdr->service_cmd; // update sequence number for current session set_session_seq_num(session, resp_hdr->seq_num + 1); // encrypt the response message if(false == encrypt_msg((pse_message_t*)pse_resp_msg, (uint8_t*)resp, (sgx_key_128bit_t*)session->active.AEK)) { ae_ret = PSE_OP_INTERNAL_ERROR; break; } } while (0); clean_up: SAFE_FREE(req); SAFE_FREE(resp); return ae_ret; } /******************************************************************* ** Function name: initialize_sqlite_database_file_wrapper ** Descrption: ** Initialize the vmc database ** Parameters: ** is_for_empty_db_creation - if true, always create a new database ** Returns: ae_error_t *******************************************************************/ ae_error_t initialize_sqlite_database_file_wrapper(bool is_for_empty_db_creation) { // ephemeral session must have been established. if(!is_eph_session_active()) { return PSE_OP_EPHEMERAL_SESSION_INVALID; } return error_reinterpret(initialize_sqlite_database_file(is_for_empty_db_creation)); } /******************************************************************* ** Function name: ephemeral_session_m2m3_wrapper ** Descrption: ** Exchange M2 and M3 between CSE and PSE-Op ** Parameters: ** sealed_blob - Long term pairing blob ** pse_cse_msg2 - Message2 from CSE ** pse_cse_msg3 - Message3 generated by PSE-Op ** Returns: ae_error_t *******************************************************************/ ae_error_t ephemeral_session_m2m3_wrapper( /* IN */ pairing_blob_t* sealed_blob, /* IN */ pse_cse_msg2_t* pse_cse_msg2, /* OUT */ pse_cse_msg3_t* pse_cse_msg3) { pse_op_error_t op_ret = OP_SUCCESS; // check parameters if (!sealed_blob || !pse_cse_msg2 || !pse_cse_msg3) { return PSE_OP_PARAMETER_ERROR; } op_ret = ephemeral_session_m2m3(sealed_blob, *pse_cse_msg2, *pse_cse_msg3); return error_reinterpret(op_ret); } /******************************************************************* ** Function name: ephemeral_session_m4_wrapper ** Descrption: ** Handle Msg4 from CSE, if successful, an ephemeral session will be established ** Parameters: ** pse_cse_msg4 - Message4 from CSE ** Returns: ae_error_t *******************************************************************/ ae_error_t ephemeral_session_m4_wrapper( /* IN */ pse_cse_msg4_t* pse_cse_msg4) { pse_op_error_t op_ret = OP_SUCCESS; if (!pse_cse_msg4) { return PSE_OP_PARAMETER_ERROR; } op_ret = ephemeral_session_m4(*pse_cse_msg4); return error_reinterpret(op_ret); }