/* * External function used to verify EPID Blob and check whether QE has * been updated. * * @param p_blob[in, out] Pointer to EPID Blob. * @param blob_size[in] The size of EPID Blob, in bytes. * @param p_is_resealed[out] Whether the EPID Blob is resealed within this function call. * @param p_cpusvn[out] Return the raw CPUSVN. * @return uint32_t AE_SUCCESS or other error cases. */ uint32_t verify_blob( uint8_t *p_blob, uint32_t blob_size, uint8_t *p_is_resealed, sgx_cpu_svn_t *p_cpusvn) { se_plaintext_epid_data_sdk_t plain_text; /* Actually, some cases here will be checked with code generated by edger8r. Here we just want to defend in depth. */ if(NULL == p_blob || NULL == p_is_resealed || NULL == p_cpusvn) return QE_PARAMETER_ERROR; if(SGX_TRUSTED_EPID_BLOB_SIZE_SDK != blob_size) return QE_PARAMETER_ERROR; // // if we mispredict here and blob_size is too // small, we might overflow // sgx_lfence(); if(!sgx_is_within_enclave(p_blob, blob_size)) return QE_PARAMETER_ERROR; return random_stack_advance(verify_blob_internal, p_blob, blob_size, p_is_resealed, FALSE, plain_text, (MemberCtx**) NULL, p_cpusvn); }
// TKE interface for isv enclaves sgx_status_t sgx_ra_get_keys( sgx_ra_context_t context, sgx_ra_key_type_t type, sgx_ra_key_128_t *p_key) { if(vector_size(&g_ra_db) <= context || !p_key) return SGX_ERROR_INVALID_PARAMETER; ra_db_item_t* item = NULL; if(0 != vector_get(&g_ra_db, context, reinterpret_cast<void**>(&item)) || item == NULL ) return SGX_ERROR_INVALID_PARAMETER; if(!sgx_is_within_enclave(p_key, sizeof(sgx_ra_key_128_t))) return SGX_ERROR_INVALID_PARAMETER; sgx_status_t ret = SGX_SUCCESS; sgx_spin_lock(&item->item_lock); //sgx_ra_proc_msg2_trusted fill the keys, so keys are available after it's called. if (item->state != ra_proc_msg2ed) ret = SGX_ERROR_INVALID_STATE; else if(SGX_RA_KEY_MK == type) memcpy(p_key, item->mk_key, sizeof(sgx_ra_key_128_t)); else if(SGX_RA_KEY_SK == type) memcpy(p_key, item->sk_key, sizeof(sgx_ra_key_128_t)); else ret = SGX_ERROR_INVALID_PARAMETER; sgx_spin_unlock(&item->item_lock); return ret; }
sgx_status_t sgx_read_rand(unsigned char *rand, size_t length_in_bytes) { // check parameters // // rand can be within or outside the enclave if(!rand || !length_in_bytes) { return SGX_ERROR_INVALID_PARAMETER; } if(!sgx_is_within_enclave(rand, length_in_bytes) && !sgx_is_outside_enclave(rand, length_in_bytes)) { return SGX_ERROR_INVALID_PARAMETER; } // loop to rdrand uint32_t rand_num = 0; while(length_in_bytes > 0) { sgx_status_t status = __do_get_rand32(&rand_num); if(status != SGX_SUCCESS) { return status; } size_t size = (length_in_bytes < sizeof(rand_num)) ? length_in_bytes : sizeof(rand_num); memcpy(rand, &rand_num, size); rand += size; length_in_bytes -= size; } memset_s(&rand_num, sizeof(rand_num), 0, sizeof(rand_num)); return SGX_SUCCESS; }
/* ecall_pointer_out: * the buffer of val is copied to the untrusted side. */ void ecall_pointer_out(int *val) { if (sgx_is_within_enclave(val, sizeof(int)) != 1) abort(); assert(*val == 0); *val = 1234; }
SGX_WEAK void* SGXAPI operator new[] (size_t dwBytes, void* ptr) throw() { if( !sgx_is_within_enclave(ptr, dwBytes) ){ //compiler will check the pointer before call object construct, so it is OK to return NULL here return NULL; } return ptr; }
// sgx_register_exception_handler() // register a custom exception handler // Parameter // is_first_handler - the order in which the handler should be called. // if the parameter is nonzero, the handler is the first handler to be called. // if the parameter is zero, the handler is the last handler to be called. // exception_handler - a pointer to the handler to be called. // Return Value // handler - success // NULL - fail void *sgx_register_exception_handler(int is_first_handler, sgx_exception_handler_t exception_handler) { // initialize g_veh_cookie for the first time sgx_register_exception_handler is called. if(unlikely(g_veh_cookie == 0)) { uintptr_t rand = 0; do { if(SGX_SUCCESS != sgx_read_rand((unsigned char *)&rand, sizeof(rand))) { return NULL; } } while(rand == 0); sgx_spin_lock(&g_handler_lock); if(g_veh_cookie == 0) { g_veh_cookie = rand; } sgx_spin_unlock(&g_handler_lock); } if(!sgx_is_within_enclave((const void*)exception_handler, 0)) { return NULL; } handler_node_t *node = (handler_node_t *)malloc(sizeof(handler_node_t)); if(!node) { return NULL; } node->callback = ENC_VEH_POINTER(exception_handler); // write lock sgx_spin_lock(&g_handler_lock); if((g_first_node == NULL) || is_first_handler) { node->next = g_first_node; g_first_node = node; } else { handler_node_t *tmp = g_first_node; while(tmp->next != NULL) { tmp = tmp->next; } node->next = NULL; tmp->next = node; } // write unlock sgx_spin_unlock(&g_handler_lock); return node; }
sgx_status_t sgx_verify_report(const sgx_report_t *report) { sgx_mac_t mac; sgx_key_request_t key_request; sgx_key_128bit_t key; sgx_status_t err = SGX_ERROR_UNEXPECTED; //check parameter if(!report||!sgx_is_within_enclave(report, sizeof(*report))) { return SGX_ERROR_INVALID_PARAMETER; } memset(&mac, 0, sizeof(sgx_mac_t)); memset(&key_request, 0, sizeof(sgx_key_request_t)); memset(&key, 0, sizeof(sgx_key_128bit_t)); //prepare the key_request key_request.key_name = SGX_KEYSELECT_REPORT; memcpy_s(&key_request.key_id, sizeof(key_request.key_id), &report->key_id, sizeof(report->key_id)); //get the report key // Since the key_request is not an input parameter by caller, // we suppose sgx_get_key would never return the following error code: // SGX_ERROR_INVALID_PARAMETER // SGX_ERROR_INVALID_ATTRIBUTE // SGX_ERROR_INVALID_CPUSVN // SGX_ERROR_INVALID_ISVSVN // SGX_ERROR_INVALID_KEYNAME err = sgx_get_key(&key_request, &key); if(err != SGX_SUCCESS) { return err; // err must be SGX_ERROR_OUT_OF_MEMORY or SGX_ERROR_UNEXPECTED } //get the report mac err = sgx_rijndael128_cmac_msg((sgx_cmac_128bit_key_t*)&key, (const uint8_t *)(&report->body), sizeof(sgx_report_body_t), &mac); memset_s (&key, sizeof(sgx_key_128bit_t), 0, sizeof(sgx_key_128bit_t)); if (SGX_SUCCESS != err) { if(err != SGX_ERROR_OUT_OF_MEMORY) err = SGX_ERROR_UNEXPECTED; return err; } if(consttime_memequal(mac, report->mac, sizeof(sgx_mac_t)) == 0) { return SGX_ERROR_MAC_MISMATCH; } else { return SGX_SUCCESS; } }
/* * External function used to init white list. It will check whether the input * buffer is correctly copied into EPC, and check the size of the buffer. * * @param wl_cert_chain[in] Pointer to the white list cert chain. * @param wl_cert_chain_size[in] The size of white list cert chain, in bytes. * @return uint32_t AE_SUCCESS for success, otherwise for errors. */ uint32_t le_init_white_list_wrapper( const uint8_t *wl_cert_chain, uint32_t wl_cert_chain_size) { const wl_cert_chain_t *p_wl_cert_chain = NULL; uint32_t entry_number = 0; uint32_t temp_size = 0; if(wl_cert_chain == NULL) { return LE_INVALID_PARAMETER; } if(!sgx_is_within_enclave(wl_cert_chain, wl_cert_chain_size)) return LE_INVALID_PARAMETER; p_wl_cert_chain = (const wl_cert_chain_t *)wl_cert_chain; // First compare wl_cert_chain_size with the minimal size of cert chain. // It should have at least one entry of mrsigner. if(wl_cert_chain_size < sizeof(wl_cert_chain_t) + sizeof(sgx_measurement_t) + sizeof(sgx_ec256_signature_t)) { return LE_INVALID_PARAMETER; } entry_number = p_wl_cert_chain->wl_cert.entry_number; entry_number = _ntohl(entry_number); // limits max MRSIGNER entry number in // WL Cert to be <= 512 if(entry_number > LE_MAX_MRSIGNER_NUMBER) { return LE_INVALID_PARAMETER; } temp_size = static_cast<uint32_t>(sizeof(wl_cert_chain_t) + sizeof(sgx_ec256_signature_t) + (sizeof(sgx_measurement_t) * entry_number)); if(wl_cert_chain_size != temp_size) { return LE_INVALID_PARAMETER; } return le_init_white_list(p_wl_cert_chain, entry_number, wl_cert_chain_size); }
// TKE interface for isv enclaves sgx_status_t sgx_ra_init_ex( const sgx_ec256_public_t *p_pub_key, int b_pse, sgx_ra_derive_secret_keys_t derive_key_cb, sgx_ra_context_t *p_context) { int valid = 0; sgx_status_t ret = SGX_SUCCESS; sgx_ecc_state_handle_t ecc_state = NULL; // initialize g_kdf_cookie for the first time sgx_ra_init_ex is called. if (unlikely(g_kdf_cookie == 0)) { uintptr_t rand = 0; do { if (SGX_SUCCESS != sgx_read_rand((unsigned char *)&rand, sizeof(rand))) { return SGX_ERROR_UNEXPECTED; } } while (rand == 0); sgx_spin_lock(&g_ra_db_lock); if (g_kdf_cookie == 0) { g_kdf_cookie = rand; memset_s(&rand, sizeof(rand), 0, sizeof(rand)); } sgx_spin_unlock(&g_ra_db_lock); } if(!p_pub_key || !p_context) return SGX_ERROR_INVALID_PARAMETER; if(!sgx_is_within_enclave(p_pub_key, sizeof(sgx_ec256_public_t))) return SGX_ERROR_INVALID_PARAMETER; //derive_key_cb can be NULL if (NULL != derive_key_cb && !sgx_is_within_enclave((const void*)derive_key_cb, 0)) { return SGX_ERROR_INVALID_PARAMETER; } ret = sgx_ecc256_open_context(&ecc_state); if(SGX_SUCCESS != ret) { if(SGX_ERROR_OUT_OF_MEMORY != ret) ret = SGX_ERROR_UNEXPECTED; return ret; } ret = sgx_ecc256_check_point((const sgx_ec256_public_t *)p_pub_key, ecc_state, &valid); if(SGX_SUCCESS != ret) { if(SGX_ERROR_OUT_OF_MEMORY != ret) ret = SGX_ERROR_UNEXPECTED; sgx_ecc256_close_context(ecc_state); return ret; } if(!valid) { sgx_ecc256_close_context(ecc_state); return SGX_ERROR_INVALID_PARAMETER; } sgx_ecc256_close_context(ecc_state); //add new item to g_ra_db ra_db_item_t* new_item = (ra_db_item_t*)malloc(sizeof(ra_db_item_t)); if (!new_item) { return SGX_ERROR_OUT_OF_MEMORY; } memset(new_item,0, sizeof(ra_db_item_t)); memcpy(&new_item->sp_pubkey, p_pub_key, sizeof(new_item->sp_pubkey)); if(b_pse) { //sgx_create_pse_session() must have been called ret = sgx_get_ps_sec_prop(&new_item->ps_sec_prop); if (ret!=SGX_SUCCESS) { SAFE_FREE(new_item); return ret; } } new_item->derive_key_cb = ENC_KDF_POINTER(derive_key_cb); new_item->state = ra_inited; //find first empty slot in g_ra_db int first_empty = -1; ra_db_item_t* item = NULL; sgx_spin_lock(&g_ra_db_lock); uint32_t size = vector_size(&g_ra_db); for (uint32_t i = 0; i < size; i++) { if(0 != vector_get(&g_ra_db, i, reinterpret_cast<void**>(&item))) { sgx_spin_unlock(&g_ra_db_lock); SAFE_FREE(new_item); return SGX_ERROR_UNEXPECTED; } if(item == NULL) { first_empty = i; break; } } //if there is a empty slot, use it if (first_empty >= 0) { errno_t vret = vector_set(&g_ra_db, first_empty, new_item); UNUSED(vret); assert(vret == 0); *p_context = first_empty; } //if there are no empty slots, add a new item to g_ra_db else { if(size >= INT32_MAX) { //overflow sgx_spin_unlock(&g_ra_db_lock); SAFE_FREE(new_item); return SGX_ERROR_OUT_OF_MEMORY; } if(0 != vector_push_back(&g_ra_db, new_item)) { sgx_spin_unlock(&g_ra_db_lock); SAFE_FREE(new_item); return SGX_ERROR_OUT_OF_MEMORY; } *p_context = size; } sgx_spin_unlock(&g_ra_db_lock); return SGX_SUCCESS; }
/* * External function used to get quote. Prefix "emp_" means it is a pointer * points memory outside enclave. * * @param p_blob[in, out] Pointer to the EPID Blob. * @param blob_size[in] The size of EPID Blob, in bytes. * @param p_enclave_report[in] The application enclave's report. * @param quote_type[in] The type of quote, random based or name based. * @param p_spid[in] Pointer to SPID. * @param p_nonce[in] Pointer to nonce. * @param emp_sig_rl[in] Pointer to SIG-RL. * @param sig_rl_size[in] The size of SIG-RL, in bytes. * @param p_qe_report[out] Pointer to QE report, which reportdata is * sha256(nonce || quote) * @param emp_quote[out] Pointer to the output buffer for quote. * @param quote_size[in] The size of emp_quote, in bytes. * @param pce_isvsvn[in] The ISVSVN of PCE. * @return ae_error_t AE_SUCCESS for success, otherwise for errors. */ uint32_t get_quote( uint8_t *p_blob, uint32_t blob_size, const sgx_report_t *p_enclave_report, sgx_quote_sign_type_t quote_type, const sgx_spid_t *p_spid, const sgx_quote_nonce_t *p_nonce, const uint8_t *emp_sig_rl, uint32_t sig_rl_size, sgx_report_t *p_qe_report, uint8_t *emp_quote, uint32_t quote_size, sgx_isv_svn_t pce_isvsvn) { ae_error_t ret = AE_SUCCESS; EpidStatus epid_ret = kEpidNoErr; MemberCtx *p_epid_context = NULL; sgx_quote_t quote_body; uint8_t is_resealed = 0; sgx_basename_t basename = {{0}}; uint64_t sign_size = 0; sgx_status_t se_ret = SGX_SUCCESS; sgx_report_t qe_report; uint64_t required_buffer_size = 0; se_sig_rl_t sig_rl_header; se_plaintext_epid_data_sdk_t plaintext; sgx_ec256_signature_t ec_signature; sgx_cpu_svn_t cpusvn; memset("e_body, 0, sizeof(quote_body)); memset(&sig_rl_header, 0, sizeof(sig_rl_header)); memset(&plaintext, 0, sizeof(plaintext)); memset(&ec_signature, 0, sizeof(ec_signature)); memset(&cpusvn, 0, sizeof(cpusvn)); /* Actually, some cases here will be checked with code generated by edger8r. Here we just want to defend in depth. */ if((NULL == p_blob) || (NULL == p_enclave_report) || (NULL == p_spid) || (NULL == emp_quote) || (!quote_size) || ((NULL != emp_sig_rl) && (sig_rl_size < sizeof(se_sig_rl_t) + 2 * SE_ECDSA_SIGN_SIZE)) // // this size check could mispredict and cause us to // overflow, but we have an lfence below // that's safe to use for this case // || ((NULL == emp_sig_rl) && (sig_rl_size != 0))) return QE_PARAMETER_ERROR; if(SGX_TRUSTED_EPID_BLOB_SIZE_SDK != blob_size) return QE_PARAMETER_ERROR; // // this could mispredict and cause us to // overflow, but we have an lfence below // that's safe to use for this case // if(SGX_LINKABLE_SIGNATURE != quote_type && SGX_UNLINKABLE_SIGNATURE != quote_type) return QE_PARAMETER_ERROR; if(!p_nonce && p_qe_report) return QE_PARAMETER_ERROR; if(p_nonce && !p_qe_report) return QE_PARAMETER_ERROR; /* To reduce the memory footprint of QE, we should leave sig_rl and quote buffer outside enclave. */ if(!sgx_is_outside_enclave(emp_sig_rl, sig_rl_size)) return QE_PARAMETER_ERROR; // // for user_check SigRL input // based on quote_size input parameter // sgx_lfence(); if(!sgx_is_outside_enclave(emp_quote, quote_size)) return QE_PARAMETER_ERROR; /* Check whether p_blob is copied into EPC. If we want to reduce the memory usage, maybe we can leave the p_blob outside EPC. */ if(!sgx_is_within_enclave(p_blob, blob_size)) return QE_PARAMETER_ERROR; if(!sgx_is_within_enclave(p_enclave_report, sizeof(*p_enclave_report))) return QE_PARAMETER_ERROR; if(!sgx_is_within_enclave(p_spid, sizeof(*p_spid))) return QE_PARAMETER_ERROR; /* If the code reach here, if p_nonce is NULL, then p_qe_report will be NULL also. So we only check p_nonce here.*/ if(p_nonce) { /* Actually Edger8r will alloc the buffer within EPC, this is just kind of defense in depth. */ if(!sgx_is_within_enclave(p_nonce, sizeof(*p_nonce))) return QE_PARAMETER_ERROR; if(!sgx_is_within_enclave(p_qe_report, sizeof(*p_qe_report))) return QE_PARAMETER_ERROR; } /* Verify the input report. */ if(SGX_SUCCESS != sgx_verify_report(p_enclave_report)) return QE_PARAMETER_ERROR; /* Verify EPID p_blob and create the context */ ret = random_stack_advance(verify_blob_internal, p_blob, blob_size, &is_resealed, TRUE, plaintext, &p_epid_context, &cpusvn); if(AE_SUCCESS != ret) goto CLEANUP; /* If SIG-RL is provided, we should check its size. */ if(emp_sig_rl) { uint64_t temp_size = 0; uint64_t n2 = 0; memcpy(&sig_rl_header, emp_sig_rl, sizeof(sig_rl_header)); if(sig_rl_header.protocol_version != SE_EPID_SIG_RL_VERSION) { ret = QE_PARAMETER_ERROR; goto CLEANUP; } if(sig_rl_header.epid_identifier != SE_EPID_SIG_RL_ID) { ret = QE_PARAMETER_ERROR; goto CLEANUP; } if(memcmp(&sig_rl_header.sig_rl.gid, &plaintext.epid_group_cert.gid, sizeof(sig_rl_header.sig_rl.gid))) { ret = QE_PARAMETER_ERROR; goto CLEANUP; } temp_size = se_get_sig_rl_size(&sig_rl_header); if(temp_size != sig_rl_size) { ret = QE_PARAMETER_ERROR; goto CLEANUP; } se_static_assert(sizeof(ec_signature.x) == SE_ECDSA_SIGN_SIZE); se_static_assert(sizeof(ec_signature.y) == SE_ECDSA_SIGN_SIZE); memcpy(ec_signature.x, emp_sig_rl + sig_rl_size - (SE_ECDSA_SIGN_SIZE * 2), sizeof(ec_signature.x)); SWAP_ENDIAN_32B(ec_signature.x); memcpy(ec_signature.y, emp_sig_rl + sig_rl_size - (SE_ECDSA_SIGN_SIZE * 1), sizeof(ec_signature.y)); SWAP_ENDIAN_32B(ec_signature.y); n2 = SWAP_4BYTES(sig_rl_header.sig_rl.n2); temp_size = sizeof(EpidSignature) - sizeof(NrProof) + n2 * sizeof(NrProof); if(temp_size > UINT32_MAX) { ret = QE_PARAMETER_ERROR; goto CLEANUP; } sign_size = temp_size; } else { sign_size = sizeof(BasicSignature) + sizeof(uint32_t) // rl_ver + sizeof(uint32_t); // rl_num } /* Verify sizeof basename is large enough and it should always be true*/ se_static_assert(sizeof(basename) > sizeof(*p_spid)); /* Because basename has already been zeroed, so we don't need to concatenating with 0s.*/ memcpy(&basename, p_spid, sizeof(*p_spid)); if(SGX_UNLINKABLE_SIGNATURE == quote_type) { uint8_t *p = (uint8_t *)&basename + sizeof(*p_spid); se_ret = sgx_read_rand(p, sizeof(basename) - sizeof(*p_spid)); if(SGX_SUCCESS != se_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } } epid_ret = EpidRegisterBasename(p_epid_context, (uint8_t *)&basename, sizeof(basename)); if(kEpidNoErr != epid_ret) { ret = QE_UNEXPECTED_ERROR; goto CLEANUP; } required_buffer_size = SE_QUOTE_LENGTH_WITHOUT_SIG + sign_size; /* We should make sure the buffer size is big enough. */ if(quote_size < required_buffer_size) { ret = QE_PARAMETER_ERROR; goto CLEANUP; } // // for user_check SigRL input // based on n2 field in SigRL // sgx_lfence(); /* Copy the data in the report into quote body. */ memset(emp_quote, 0, quote_size); quote_body.version = QE_QUOTE_VERSION; quote_body.sign_type = (uint16_t)quote_type; quote_body.pce_svn = pce_isvsvn; // Both are little endian quote_body.xeid = plaintext.xeid; // Both are little endian se_static_assert(sizeof(plaintext.epid_group_cert.gid) == sizeof(OctStr32)); se_static_assert(sizeof(quote_body.epid_group_id) == sizeof(uint32_t)); ((uint8_t *)("e_body.epid_group_id))[0] = plaintext.epid_group_cert.gid.data[3]; ((uint8_t *)("e_body.epid_group_id))[1] = plaintext.epid_group_cert.gid.data[2]; ((uint8_t *)("e_body.epid_group_id))[2] = plaintext.epid_group_cert.gid.data[1]; ((uint8_t *)("e_body.epid_group_id))[3] = plaintext.epid_group_cert.gid.data[0]; memcpy("e_body.basename, &basename, sizeof(quote_body.basename)); // Get the QE's report. se_ret = sgx_create_report(NULL, NULL, &qe_report); if(SGX_SUCCESS != se_ret) { ret = QE_PARAMETER_ERROR; goto CLEANUP; } // Copy QE's security version in to Quote body. quote_body.qe_svn = qe_report.body.isv_svn; // Copy the incoming report into Quote body. memcpy("e_body.report_body, &(p_enclave_report->body), sizeof(quote_body.report_body)); /* Because required_buffer_size is larger than signature_len, so if we get here, then no integer overflow will ocur. */ quote_body.signature_len = (uint32_t)(sizeof(se_wrap_key_t) + QUOTE_IV_SIZE + sizeof(uint32_t) + sign_size + sizeof(sgx_mac_t)); /* Make the signature. */ ret = qe_epid_sign(p_epid_context, plaintext, &basename, emp_sig_rl ? ((const se_sig_rl_t *)emp_sig_rl)->sig_rl.bk : NULL, &sig_rl_header, &ec_signature, p_enclave_report, p_nonce, p_qe_report, emp_quote, "e_body, (uint32_t)sign_size); if(AE_SUCCESS != ret) { // Only need to clean the buffer after the fixed length part. memset_s(emp_quote + sizeof(sgx_quote_t), quote_size - sizeof(sgx_quote_t), 0, quote_size - sizeof(sgx_quote_t)); goto CLEANUP; } memcpy(emp_quote, "e_body, sizeof(sgx_quote_t)); CLEANUP: if(p_epid_context) epid_member_delete(&p_epid_context); return ret; }
extern "C" sgx_status_t sgx_mac_aadata_ex(const uint16_t key_policy, const sgx_attributes_t attribute_mask, const sgx_misc_select_t misc_mask, const uint32_t additional_MACtext_length, const uint8_t *p_additional_MACtext, const uint32_t sealed_data_size, sgx_sealed_data_t *p_sealed_data) { sgx_status_t err = SGX_ERROR_UNEXPECTED; sgx_report_t report; sgx_key_id_t keyID; sgx_key_request_t tmp_key_request; uint8_t payload_iv[SGX_SEAL_IV_SIZE]; memset(&payload_iv, 0, sizeof(payload_iv)); uint32_t sealedDataSize = sgx_calc_sealed_data_size(additional_MACtext_length, 0); // Check for overflow if (sealedDataSize == UINT32_MAX) { return SGX_ERROR_INVALID_PARAMETER; } // // Check parameters // // check key_request->key_policy reserved bits are not set and one of policy bits are set if ((key_policy & ~(SGX_KEYPOLICY_MRENCLAVE | SGX_KEYPOLICY_MRSIGNER)) || ((key_policy & (SGX_KEYPOLICY_MRENCLAVE | SGX_KEYPOLICY_MRSIGNER)) == 0)) { return SGX_ERROR_INVALID_PARAMETER; } if ((attribute_mask.flags & 0x3) != 0x3) { return SGX_ERROR_INVALID_PARAMETER; } // The AAD must be provided if ((additional_MACtext_length == 0) || (p_additional_MACtext == NULL)) { return SGX_ERROR_INVALID_PARAMETER; } // Ensure AAD does not cross enclave boundary if (!(sgx_is_within_enclave(p_additional_MACtext, additional_MACtext_length) || sgx_is_outside_enclave(p_additional_MACtext, additional_MACtext_length))) { return SGX_ERROR_INVALID_PARAMETER; } // Ensure sealed data blob is within an enclave during the sealing process if ((p_sealed_data == NULL) || (!sgx_is_within_enclave(p_sealed_data, sealed_data_size))) { return SGX_ERROR_INVALID_PARAMETER; } if (sealedDataSize != sealed_data_size) { return SGX_ERROR_INVALID_PARAMETER; } memset(&report, 0, sizeof(sgx_report_t)); memset(p_sealed_data, 0, sealedDataSize); memset(&keyID, 0, sizeof(sgx_key_id_t)); memset(&tmp_key_request, 0, sizeof(sgx_key_request_t)); // Get the report to obtain isv_svn and cpu_svn err = sgx_create_report(NULL, NULL, &report); if (err != SGX_SUCCESS) { goto clear_return; } // Get a random number to populate the key_id of the key_request err = sgx_read_rand(reinterpret_cast<uint8_t *>(&keyID), sizeof(sgx_key_id_t)); if (err != SGX_SUCCESS) { goto clear_return; } memcpy(&(tmp_key_request.cpu_svn), &(report.body.cpu_svn), sizeof(sgx_cpu_svn_t)); memcpy(&(tmp_key_request.isv_svn), &(report.body.isv_svn), sizeof(sgx_isv_svn_t)); tmp_key_request.key_name = SGX_KEYSELECT_SEAL; tmp_key_request.key_policy = key_policy; tmp_key_request.attribute_mask.flags = attribute_mask.flags; tmp_key_request.attribute_mask.xfrm = attribute_mask.xfrm; memcpy(&(tmp_key_request.key_id), &keyID, sizeof(sgx_key_id_t)); tmp_key_request.misc_mask = misc_mask; err = sgx_seal_data_iv(additional_MACtext_length, p_additional_MACtext, 0, NULL, payload_iv, &tmp_key_request, p_sealed_data); if (err == SGX_SUCCESS) { // Copy data from the temporary key request buffer to the sealed data blob memcpy(&(p_sealed_data->key_request), &tmp_key_request, sizeof(sgx_key_request_t)); } clear_return: // Clear temp state memset_s(&report, sizeof(sgx_report_t), 0, sizeof(sgx_report_t)); memset_s(&keyID, sizeof(sgx_key_id_t), 0, sizeof(sgx_key_id_t)); return err; }
extern "C" sgx_status_t sgx_unmac_aadata(const sgx_sealed_data_t *p_sealed_data, uint8_t *p_additional_MACtext, uint32_t *p_additional_MACtext_length) { sgx_status_t err = SGX_ERROR_UNEXPECTED; // Ensure the the sgx_sealed_data_t members are all inside enclave before using them. if ((p_sealed_data == NULL) || (!sgx_is_within_enclave(p_sealed_data, sizeof(sgx_sealed_data_t)))) { return SGX_ERROR_INVALID_PARAMETER; } // If using this API, the sealed blob must have no encrypted data. // So the encryt_text_length must be 0. uint32_t encrypt_text_length = sgx_get_encrypt_txt_len(p_sealed_data); if (encrypt_text_length != 0) { return SGX_ERROR_MAC_MISMATCH; // Return error indicating the blob is corrupted } // The sealed blob must have AAD. So the add_text_length must not be 0. uint32_t add_text_length = sgx_get_add_mac_txt_len(p_sealed_data); if (add_text_length == UINT32_MAX || add_text_length == 0) { return SGX_ERROR_MAC_MISMATCH; // Return error indicating the blob is corrupted } uint32_t sealedDataSize = sgx_calc_sealed_data_size(add_text_length, encrypt_text_length); if (sealedDataSize == UINT32_MAX) { return SGX_ERROR_MAC_MISMATCH; // Return error indicating the blob is corrupted } // // Check parameters // // Ensure sealed data blob is within an enclave during the sealing process if (!sgx_is_within_enclave(p_sealed_data, sealedDataSize)) { return SGX_ERROR_INVALID_PARAMETER; } if (p_additional_MACtext == NULL || p_additional_MACtext_length == NULL) { return SGX_ERROR_INVALID_PARAMETER; } // Ensure AAD does not cross enclave boundary if (!(sgx_is_within_enclave(p_additional_MACtext, add_text_length) || sgx_is_outside_enclave(p_additional_MACtext, add_text_length))) { return SGX_ERROR_INVALID_PARAMETER; } uint32_t additional_MACtext_length = *p_additional_MACtext_length; if (additional_MACtext_length < add_text_length) { return SGX_ERROR_INVALID_PARAMETER; } err = sgx_unseal_data_helper(p_sealed_data, p_additional_MACtext, add_text_length, NULL, encrypt_text_length); if (err == SGX_SUCCESS) { *p_additional_MACtext_length = add_text_length; } return err; }
ae_error_t prepare_for_certificate_provisioning ( /*in */ UINT64 nonce64, /*in */ const sgx_target_info_t* pTargetInfo, /*in */ UINT16 nMax_CSR_pse, /*out*/ UINT8* pCSR_pse, /*out*/ UINT16* pnLen_CSR_pse, /*out*/ sgx_report_t* pREPORT, /*i/o*/ pairing_blob_t* pPairingBlob ) { // Flow: 1) Check pointers for buffer data sizes // 2) If buffers are too small, return and tell caller size required // 3) Validate pointers and ensure buffers are within the enclave // 4) Generate a new private/public ECDSA key pair // 5) Request signed CSR template // 6) Calculate HASH_pse of (CSR_pse || nonce64) // 7) Generate REPORT with HASH_pse as the REPORTDATA, targeting QE // 8) Copy private key and public key into unsealed_pairing buffer // 9) Seal pairing blob // 10) Return Sealed pairing blob, generated CSR, REPORT, and status ae_error_t status = AE_FAILURE; pairing_data_t pairingData; EcDsaPrivKey privateKey; EcDsaPubKey publicKey; uint8_t temp_instance_id[16]; SignCSR CSR; size_t nMaxSizeCSR = CSR.GetMaxSize(); sgx_ecc_state_handle_t csr_ecc_handle = NULL; memset(&pairingData, 0, sizeof(pairingData)); ///////////////////////////////////////////////////////////////// do { //********************************************************************* // Validate pointers and sizes //********************************************************************* BREAK_IF_TRUE((NULL == pPairingBlob), status, PSE_PR_BAD_POINTER_ERROR); // save SW_INSTANCE_ID memcpy(temp_instance_id, pPairingBlob->plaintext.pse_instance_id, sizeof(temp_instance_id)); { BREAK_IF_TRUE((NULL == pTargetInfo), status, PSE_PR_BAD_POINTER_ERROR); BREAK_IF_TRUE((NULL == pREPORT), status, PSE_PR_BAD_POINTER_ERROR); BREAK_IF_TRUE((NULL == pCSR_pse || NULL == pnLen_CSR_pse), status, PSE_PR_BAD_POINTER_ERROR); BREAK_IF_TRUE((nMax_CSR_pse < nMaxSizeCSR), status, PSE_PR_PARAMETER_ERROR); BREAK_IF_FALSE(sgx_is_within_enclave(pCSR_pse, nMaxSizeCSR), status, PSE_PR_BAD_POINTER_ERROR); //********************************************************************* // Generate a new ECDSA Key Pair //********************************************************************* sgx_status_t sgx_status = sgx_ecc256_open_context(&csr_ecc_handle); BREAK_IF_TRUE((SGX_ERROR_OUT_OF_MEMORY == sgx_status), status, PSE_PR_INSUFFICIENT_MEMORY_ERROR); BREAK_IF_TRUE((SGX_SUCCESS != sgx_status), status, PSE_PR_KEY_PAIR_GENERATION_ERROR); sgx_status = sgx_ecc256_create_key_pair((sgx_ec256_private_t *)privateKey, (sgx_ec256_public_t*)publicKey, csr_ecc_handle); BREAK_IF_TRUE((SGX_SUCCESS != sgx_status), status, PSE_PR_KEY_PAIR_GENERATION_ERROR); *pnLen_CSR_pse = (uint16_t)nMaxSizeCSR; //********************************************************************* // Get a signed Certificate Signing Request from the template //********************************************************************* status = CSR.GetSignedTemplate(&privateKey, &publicKey, csr_ecc_handle, pCSR_pse, pnLen_CSR_pse); BREAK_IF_FAILED(status); //********************************************************************* // Calculate HASH_pse of (CSR_pse || nonce64) //********************************************************************* PrepareHashSHA256 hash; SHA256_HASH computedHash; status = hash.Update(pCSR_pse, *pnLen_CSR_pse); BREAK_IF_FAILED(status); status = hash.Update(&nonce64, sizeof(nonce64)); BREAK_IF_FAILED(status); status = hash.Finalize(&computedHash); BREAK_IF_FAILED(status); //********************************************************************* // Generate a REPORT with HASH_pse //********************************************************************* sgx_report_data_t report_data = {{0}}; memcpy(&report_data, &computedHash, sizeof(computedHash)); if (SGX_SUCCESS != sgx_create_report(const_cast<sgx_target_info_t*>(pTargetInfo), &report_data, (sgx_report_t*)pREPORT)) { status = PSE_PR_CREATE_REPORT_ERROR; break; } //********************************************************************* // Try to unseal the pairing data //********************************************************************* status = UnsealPairingBlob(pPairingBlob, &pairingData); if (AE_FAILED(status)) memset_s(&pairingData, sizeof(pairingData), 0, sizeof(pairingData)); //********************************************************************* // Seal ECDSA Verifier Private Key into blob //********************************************************************* memcpy(pairingData.secret_data.VerifierPrivateKey, &privateKey, sizeof(EcDsaPrivKey)); } // "Public" PSE Cert // Set pairingData.plaintext.pse_instance_id using saved temp_instance_id memcpy(pairingData.plaintext.pse_instance_id, temp_instance_id, sizeof(pairingData.plaintext.pse_instance_id)); status = SealPairingBlob(&pairingData, pPairingBlob); BREAK_IF_FAILED(status); //********************************************************************* // WE PASSED ALL BARRIERS TO SUCCESS //********************************************************************* status = AE_SUCCESS; // OutputOctets("::tPrepareForCertificateProvisioning:: New CSR generated", NULL, 0); } while (false); // Defense-in-depth: clear the data on stack that contains enclave secret. memset_s(&pairingData, sizeof(pairingData), 0, sizeof(pairingData)); memset_s(&privateKey, sizeof(privateKey), 0, sizeof(privateKey)); if (csr_ecc_handle != NULL) sgx_ecc256_close_context(csr_ecc_handle); return map_error_for_return(status); }
void ecall_pointer_in(int *val) { if (sgx_is_within_enclave(val, sizeof(int)) != 1) abort(); *val = 1234; }
sgx_status_t sgx_get_key(const sgx_key_request_t *key_request, sgx_key_128bit_t *key) { sgx_status_t err = SGX_ERROR_UNEXPECTED; void *buffer = NULL; size_t size = 0, buf_ptr = 0; sgx_key_request_t *tmp_key_request = NULL; sgx_key_128bit_t *tmp_key = NULL; egetkey_status_t egetkey_status = EGETKEY_SUCCESS; int i = 0; const sgx_report_t *report = NULL; // check parameters // // key_request must be within the enclave if(!key_request || !sgx_is_within_enclave(key_request, sizeof(*key_request))) { err = SGX_ERROR_INVALID_PARAMETER; goto CLEANUP; } if (key_request->reserved1 != 0) { err = SGX_ERROR_INVALID_PARAMETER; goto CLEANUP; } for (i=0; i<SGX_KEY_REQUEST_RESERVED2_BYTES; ++i) { if (key_request->reserved2[i] != 0) { err = SGX_ERROR_INVALID_PARAMETER; goto CLEANUP; } } // key must be within the enclave if(!key || !sgx_is_within_enclave(key, sizeof(*key))) { err = SGX_ERROR_INVALID_PARAMETER; goto CLEANUP; } // check key_request->key_policy reserved bits if(key_request->key_policy & ~(SGX_KEYPOLICY_MRENCLAVE | SGX_KEYPOLICY_MRSIGNER | (KEY_POLICY_KSS))) { err = SGX_ERROR_INVALID_PARAMETER; goto CLEANUP; } // check if KSS flag is disabled but KSS related policy or config_svn is set report = sgx_self_report(); if (!(report->body.attributes.flags & SGX_FLAGS_KSS) && ((key_request->key_policy & KEY_POLICY_KSS) || key_request->config_svn > 0)) { err = SGX_ERROR_INVALID_PARAMETER; goto CLEANUP; } // allocate memory // // To minimize the effort of memory management, the two elements allocation // are combined in a single malloc. The calculation for the required size has // an assumption, that // the elements should be allocated in descending order of the alignment size. // // If the alignment requirements are changed, the allocation order needs to // change accordingly. // // Current allocation order is: // key_request -> key // // key_request: 512-byte aligned, 512-byte length // key: 16-byte aligned, 16-byte length size = ROUND_TO(sizeof(*key_request), KEY_REQUEST_ALIGN_SIZE) + ROUND_TO(sizeof(*key), KEY_ALIGN_SIZE); size += MAX(KEY_REQUEST_ALIGN_SIZE, KEY_ALIGN_SIZE) - 1; buffer = malloc(size); if(buffer == NULL) { err = SGX_ERROR_OUT_OF_MEMORY; goto CLEANUP; } memset(buffer, 0, size); buf_ptr = reinterpret_cast<size_t>(buffer); buf_ptr = ROUND_TO(buf_ptr, KEY_REQUEST_ALIGN_SIZE); tmp_key_request = reinterpret_cast<sgx_key_request_t *>(buf_ptr); buf_ptr += sizeof(*tmp_key_request); buf_ptr = ROUND_TO(buf_ptr, KEY_ALIGN_SIZE); tmp_key = reinterpret_cast<sgx_key_128bit_t *>(buf_ptr); // Copy data from user buffer to the aligned memory memcpy_s(tmp_key_request, sizeof(*tmp_key_request), key_request, sizeof(*key_request)); // Do EGETKEY egetkey_status = (egetkey_status_t) do_egetkey(tmp_key_request, tmp_key); switch(egetkey_status) { case EGETKEY_SUCCESS: err = SGX_SUCCESS; break; case EGETKEY_INVALID_ATTRIBUTE: err = SGX_ERROR_INVALID_ATTRIBUTE; break; case EGETKEY_INVALID_CPUSVN: err = SGX_ERROR_INVALID_CPUSVN; break; case EGETKEY_INVALID_ISVSVN: err = SGX_ERROR_INVALID_ISVSVN; break; case EGETKEY_INVALID_KEYNAME: err = SGX_ERROR_INVALID_KEYNAME; break; default: err = SGX_ERROR_UNEXPECTED; break; } CLEANUP: if((SGX_SUCCESS != err) && (NULL != key)) { // The key buffer should be filled with random number. // If sgx_read_rand returns failure, let the key buffer untouched sgx_read_rand(reinterpret_cast<uint8_t *>(key), sizeof(*key)); } else if(NULL != key) { // Copy data to the user buffer memcpy_s(key, sizeof(*key), tmp_key, sizeof(*tmp_key)); } // cleanup if(buffer) { memset_s(buffer, size, 0, size); free(buffer); } return err; }