ae_error_t Helper::LoadCertificateChain( /*out*/ std::list<upse::Buffer>& certChain) { ae_error_t status = AESM_PSE_PR_CERT_LOAD_ERROR; char* szParseString = NULL; do { // Read the delimited file of certificate names upse::Buffer certChainListBuffer; if (AE_SUCCESS != upsePersistentStorage::Read(PSE_PR_CERTIFICATE_CHAIN_FID, certChainListBuffer)) break; szParseString = (char*)calloc(1, certChainListBuffer.getSize() + 1); if (NULL == szParseString) break; memcpy_s(szParseString, certChainListBuffer.getSize(), certChainListBuffer.getData(), certChainListBuffer.getSize()); szParseString[certChainListBuffer.getSize()] = '\0'; char* nextToken = NULL; char* szCertificateNamePostfix = get_next_token(szParseString, TOKEN_SEPARATOR, &nextToken); // For each certificate name, read the certificate and save in list aesm_data_id_t fileid = PSE_PR_CERTIFICATE_FID; while (NULL != szCertificateNamePostfix) { upse::Buffer cert; if (AE_SUCCESS != upsePersistentStorage::Read(fileid++, cert)) { break; } if (PSE_PR_CERTIFICATE_FID_MAX == fileid) { break; } certChain.push_back(cert); szCertificateNamePostfix = get_next_token(NULL, TOKEN_SEPARATOR, &nextToken); } if (NULL != szCertificateNamePostfix) { break; } status = AE_SUCCESS; } while (0); if (NULL != szParseString) { free(szParseString); } SGX_DBGPRINT_PRINT_FUNCTION_AND_RETURNVAL(__FUNCTION__, status); return status; }
//********************************************************************* // Main engine routine for Certificate Chain Provisioning //********************************************************************* ae_error_t certificate_chain_provisioning(const endpoint_selection_infos_t& es_info, platform_info_blob_wrapper_t* pib_wrapper) { ae_error_t status = AE_FAILURE; AESM_DBG_TRACE("enter fun"); try { do { status = do_certificate_chain_provisioning(es_info, pib_wrapper); if (status == PSE_PR_ENCLAVE_LOST_ERROR) { // // went to sleep while in enclave // in this case (beginning of flow), we should just retry, after first destroying and then reloading // note that this code gets significantly more complicated if the PSE-pr ever becomes multi-threaded // for (unsigned rcount = 0; rcount < PSEPR_LOST_ENCLAVE_RETRY_COUNT; rcount++) { CPSEPRClass::instance().unload_enclave(); if (0 != CPSEPRClass::instance().load_enclave()) { status = AE_FAILURE; break; } SaveEnclaveID(CPSEPRClass::instance().GetEID()); status = do_certificate_chain_provisioning(es_info, pib_wrapper); if (status != PSE_PR_ENCLAVE_LOST_ERROR) break; } } BREAK_IF_FAILED(status); status = AE_SUCCESS; } while (0); } catch (...) { status = AESM_PSE_PR_EXCEPTION; } SGX_DBGPRINT_PRINT_FUNCTION_AND_RETURNVAL(__FUNCTION__, status); SGX_DBGPRINT_PRINT_ANSI_STRING("End Certificate Chain Provisioning"); return status; }
//********************************************************************* // Call quoting enclave to convert report to name-based quote //********************************************************************* static ae_error_t do_get_quote ( /*in */ upse::Buffer& reportBuffer, /*in */ upse::Buffer& sigRLBuffer, /*out*/ upse::Buffer& quoteBuffer ) { // Call QE to convert REPORT to a name-based QUOTE ae_error_t status = AE_FAILURE; ae_error_t tmp_status = AE_SUCCESS; do { #ifndef FAKE_QUOTE uint32_t nQuote; // in - Quote buffer size sgx_report_t enclaveReport; // in sgx_quote_sign_type_t quote_type; // in sgx_spid_t spid = {{0}}; // in uint8_t* pSigRL = NULL; // in uint32_t nSigRL = 0; // in - Sig RL buffer size memset(&enclaveReport, 0, sizeof(enclaveReport)); nSigRL = sigRLBuffer.getSize(); if (0 != nSigRL) pSigRL = const_cast<uint8_t*>(sigRLBuffer.getData()); if (SGX_SUCCESS != sgx_calc_quote_size(pSigRL, nSigRL, &nQuote)) break; tmp_status = quoteBuffer.Alloc(nQuote); if (AE_FAILED(tmp_status)) break; upse::BufferWriter bwQuote(quoteBuffer); uint8_t* pQuote; tmp_status = bwQuote.reserve(nQuote, &pQuote); // out if (AE_FAILED(tmp_status)) break; quote_type = SGX_UNLINKABLE_SIGNATURE; // or SGX_LINKABLE_SIGNATURE // LSB16(SHA256("SGX PSE PROVISIONING SERVER")) // const char* SPID_VALUE = "SGX PSE PROVISIONING SERVER"; // sgx_sha256_hash_t spid_hash; // sgx_sha256_msg((const uint8_t*)SPID_VALUE, strlen(SPID_VALUE), &spid_hash); // memcpy_s(spid.id, sizeof(spid.id), &spid_hash[0], 16); static uint8_t spid_hash[] = { 0x32, 0x81, 0xE5, 0x9E, 0xB1, 0x23, 0xFA, 0xCD, 0x56, 0xDB, 0x62, 0x1E, 0x3B, 0x37, 0xFB, 0xE2 }; memcpy_s(spid.id, sizeof(spid.id), spid_hash, sizeof(spid_hash)); if (reportBuffer.getSize() != sizeof(enclaveReport)) break; memcpy_s(&enclaveReport, reportBuffer.getSize(), reportBuffer.getData(), reportBuffer.getSize()); aesm_error_t result; result = AESMLogic::get_quote( (uint8_t*)&enclaveReport, sizeof(enclaveReport), quote_type, (uint8_t*)&spid, sizeof(spid), NULL, 0, pSigRL, nSigRL, NULL, 0, (uint8_t*)pQuote, nQuote); if (result == AESM_BUSY) { //EPID_PROVISION triggered, make sure previous EPID provision has finished ae_error_t temp_ret = wait_pve_thread(); BREAK_IF_TRUE(AE_SUCCESS != temp_ret , status, PSE_PR_PCH_EPID_UNKNOWN_ERROR); //redo get_quote result = AESMLogic::get_quote( (uint8_t*)&enclaveReport, sizeof(enclaveReport), quote_type, (uint8_t*)&spid, sizeof(spid), NULL, 0, pSigRL, nSigRL, NULL, 0, (uint8_t*)pQuote, nQuote); } BREAK_IF_TRUE(AESM_OUT_OF_EPC == result, status, AESM_AE_OUT_OF_EPC); BREAK_IF_TRUE(AESM_SUCCESS != result, status, AESM_PSE_PR_GET_QUOTE_ERROR); #else const uint16_t SIGNATURE_LENGTH = 32; tmp_status = quoteBuffer.Alloc(sizeof(sgx_quote_t) + SIGNATURE_LENGTH); if (AE_FAILED(tmp_status)) break; sgx_quote_t* pQuote; tmp_status = upse::BufferWriter(quoteBuffer).reserve(quoteBuffer.getSize(), (uint8_t**)&pQuote); if (AE_FAILED(tmp_status)) break; uint16_t CPUSVN = 1; pQuote->version = 1; memcpy_s(pQuote->epid_group_id, sizeof(pQuote->epid_group_id), &GID_TO_USE, sizeof(GID_TO_USE)); pQuote->report_body.isv_prod_id = 0x0002; //0x8086; pQuote->report_body.isv_svn = 1; memcpy_s(pQuote->report_body.cpu_svn, sizeof(pQuote->report_body.cpu_svn), &CPUSVN, sizeof(CPUSVN)); const sgx_report_t* pReport = (sgx_report_t*)reportBuffer.getData(); memcpy_s(pQuote->report_body.report_data, sizeof(pQuote->report_body.report_data), pReport->body.report_data, sizeof(pQuote->report_body.report_data)); pQuote->signature_len = SIGNATURE_LENGTH; //NOTE: The signature is not valid when doing a FAKE_QUOTE #endif status = AE_SUCCESS; } while (0); if ((AE_FAILURE == status) && AE_FAILED(tmp_status)) status = tmp_status; SGX_DBGPRINT_PRINT_FUNCTION_AND_RETURNVAL(__FUNCTION__, status); return status; }
//********************************************************************* // Call quoting enclave to get target info //********************************************************************* static ae_error_t do_quote_initialization ( /*out */ upse::Buffer& targetInfo, /*out */ GroupId* pGID ) { ae_error_t status = AE_FAILURE; do { BREAK_IF_TRUE( (NULL == pGID), status, PSE_PR_BAD_POINTER_ERROR); #ifndef FAKE_QUOTE if (AE_FAILED(targetInfo.Alloc(sizeof(sgx_target_info_t)))) break; upse::BufferWriter bwTargetInfo(targetInfo); uint8_t* p; status = bwTargetInfo.reserve(sizeof(sgx_target_info_t), &p); if (AE_FAILED(status)) break; sgx_target_info_t* pTargetInfo = (sgx_target_info_t*)p; aesm_error_t result; SGX_DBGPRINT_PRINT_ANSI_STRING("aesmLogic.init_quote?"); result = AESMLogic::init_quote( (uint8_t*)pTargetInfo, sizeof(sgx_target_info_t), (uint8_t*)pGID, sizeof(*pGID)); if (result == AESM_BUSY) { //EPID_PROVISION triggered, make sure previous EPID provision has finished ae_error_t temp_ret = wait_pve_thread(); BREAK_IF_TRUE(AE_SUCCESS != temp_ret , status, PSE_PR_PCH_EPID_UNKNOWN_ERROR); //redo init_quote result = AESMLogic::init_quote( (uint8_t*)pTargetInfo, sizeof(sgx_target_info_t), (uint8_t*)pGID, sizeof(*pGID)); } BREAK_IF_TRUE(AESM_UPDATE_AVAILABLE == result, status, PSW_UPDATE_REQUIRED); BREAK_IF_TRUE(AESM_OUT_OF_EPC == result, status, AESM_AE_OUT_OF_EPC); BREAK_IF_TRUE(AESM_SUCCESS != result, status, AESM_PSE_PR_INIT_QUOTE_ERROR); #else //NRG: m_tmpGID = 0; upse::Buffer m_tmpGID; if (AE_FAILED(m_tmpGID.Alloc(GID_TO_USE, sizeof(GID_TO_USE)))) break; // m_tmpGID = 1244; // upse::BufferWriter(m_tmpGID).writeRaw(GID_TO_USE, sizeof(GID_TO_USE)); SigmaData::SetGID(m_tmpGID); memcpy_s(pGID, sizeof(GroupId), m_tmpGID.getData(), sizeof(GroupId)); if (AE_FAILED(targetInfo.Alloc(sizeof(sgx_target_info_t)))) break; #endif SGX_DBGPRINT_PRINT_ANSI_STRING("aesmLogic.init_quote success"); status = AE_SUCCESS; } while (0); SGX_DBGPRINT_PRINT_FUNCTION_AND_RETURNVAL(__FUNCTION__, status); return status; }
ae_error_t Helper::PrepareCertificateChainVLR( /*in*/ std::list<upse::Buffer>& certChain, /*out*/ upse::Buffer& certChainVLR) { ae_error_t status = AESM_PSE_PR_LOAD_VERIFIER_CERT_ERROR; try { do { int nPaddedBytes = 0; int nCertChain = 0; #if !defined(LEAFTOROOT) #error LEAFTOROOT not #defined #endif // // spec'd behavior is to receive certs in leaft to root order // then, it only makes sense to store them leaf to root // but sigma wants them root to leaf // we'll leave the #if here since, cumulatively, it shows how to traverse // in both directions // #if !LEAFTOROOT SGX_DBGPRINT_PRINT_STRING_LTP("leaf cert to root cert direction, padding"); std::list<upse::Buffer>::reverse_iterator it; for (it = certChain.rbegin(); it != certChain.rend(); ++it) { int nSize = (*it).getSize(); nPaddedBytes += REQUIRED_PADDING_DWORD_ALIGNMENT(nSize); nCertChain += nSize; } #else SGX_DBGPRINT_PRINT_STRING_LTP("root cert to leaf cert direction, padding"); std::list<upse::Buffer>::iterator it; for (it = certChain.begin(); it != certChain.end(); ++it) { int nSize = (*it).getSize(); nPaddedBytes += REQUIRED_PADDING_DWORD_ALIGNMENT(nSize); nCertChain += nSize; } #endif SGX_DBGPRINT_PRINT_STRING_LTP("less cert padding"); //NRG: This doesn't work, but should. It should replace the previous nPaddedBytes = REQUIRED_PADDING_DWORD_ALIGNMENT(nCertChain); if(UINT16_MAX - ((int)sizeof(SIGMA_VLR_HEADER) + nPaddedBytes) < nCertChain){ break; } int nLength = static_cast<int>(sizeof(SIGMA_VLR_HEADER)) + nPaddedBytes + nCertChain; certChainVLR.Alloc(nLength); upse::BufferWriter bw(certChainVLR); VERIFIER_CERT_CHAIN_VLR* pVLR; uint8_t* p; if (AE_FAILED(bw.reserve(nLength, &p))) break; pVLR = (VERIFIER_CERT_CHAIN_VLR*)p; pVLR->VlrHeader.ID = VERIFIER_CERTIFICATE_CHAIN_VLR_ID; pVLR->VlrHeader.PaddedBytes = (UINT8)nPaddedBytes; pVLR->VlrHeader.Length = (UINT16)nLength; memset(pVLR->VerifierCertificateChain, 0, nPaddedBytes + nCertChain); int ndx = 0; // // see above // #if (!LEAFTOROOT) SGX_DBGPRINT_PRINT_STRING_LTP("leaf cert to root cert direction"); for (it = certChain.rbegin(); it != certChain.rend(); ++it) { memcpy_s(pVLR->VerifierCertificateChain + ndx, (*it).getSize(), (*it).getData(), (*it).getSize()); ndx += (*it).getSize(); } #else SGX_DBGPRINT_PRINT_STRING_LTP("root cert to leaf cert direction"); for (it = certChain.begin(); it != certChain.end(); ++it) { memcpy_s(pVLR->VerifierCertificateChain + ndx, (*it).getSize(), (*it).getData(), (*it).getSize()); ndx += (*it).getSize(); } #endif status = AE_SUCCESS; } while (0); } catch(...) { } SGX_DBGPRINT_PRINT_FUNCTION_AND_RETURNVAL(__FUNCTION__, status); return status; }
ae_error_t Helper::SaveCertificateChain( /*in */ std::list<upse::Buffer>& certChain) { ae_error_t status = AESM_PSE_PR_CERT_SAVE_ERROR; char* szParseString = NULL; int nLen, pos=0; do { RemoveCertificateChain(); // Allocate enough space for the list of names and separator character if(((size_t)(INT32_MAX-1))/sizeof(CERT_FILENAME_POSTFIX_FORMAT) < certChain.size() ) break; int nBytes = static_cast<int>(certChain.size() * sizeof(CERT_FILENAME_POSTFIX_FORMAT)) + 1; szParseString = (char*)calloc(1, nBytes); if (NULL == szParseString) { break; } char* szNextCertNamePostfix = szParseString; int fileNo = 0; char szFilenamePostfix[80]; std::list<upse::Buffer>::iterator it = certChain.begin(); aesm_data_id_t fileid = PSE_PR_CERTIFICATE_FID; while (it != certChain.end()) { nLen = sprintf_s(szFilenamePostfix, sizeof(szFilenamePostfix), CERT_FILENAME_POSTFIX_FORMAT, ++fileNo); if (AE_SUCCESS != upsePersistentStorage::Write(fileid++, *it)) { break; } if (PSE_PR_CERTIFICATE_FID_MAX == fileid) { break; } strcpy_s(szNextCertNamePostfix, nBytes-pos, szFilenamePostfix); pos += nLen; szNextCertNamePostfix += nLen; ++it; if (it != certChain.end()) { if (pos >= nBytes) { break; } *szNextCertNamePostfix = ';'; pos++; ++szNextCertNamePostfix; } } if ((szNextCertNamePostfix - szParseString) > nBytes) { break; } nBytes = static_cast<int>(szNextCertNamePostfix - szParseString); upse::Buffer nameListBuffer; if (AE_FAILED(nameListBuffer.Alloc(nBytes))) break; upse::BufferWriter bw(nameListBuffer); bw.writeRaw((uint8_t*)szParseString, nBytes); if (AE_SUCCESS != upsePersistentStorage::Write(PSE_PR_CERTIFICATE_CHAIN_FID, nameListBuffer)) { break; } status = AE_SUCCESS; } while (0); if (NULL != szParseString) { free(szParseString); } SGX_DBGPRINT_PRINT_FUNCTION_AND_RETURNVAL(__FUNCTION__, status); return status; }
ae_error_t Helper::RemoveCertificateChain() { ae_error_t status = AESM_PSE_PR_CERT_DELETE_ERROR; char* szParseString = NULL; int nError = 0; do { // Read the delimited file of certificate names upse::Buffer certChainListBuffer; if (AE_SUCCESS != upsePersistentStorage::Read(PSE_PR_CERTIFICATE_CHAIN_FID, certChainListBuffer)) { break; } szParseString = (char*) calloc(1, certChainListBuffer.getSize() + 1); if (NULL == szParseString) { break; } memcpy_s(szParseString, certChainListBuffer.getSize(), certChainListBuffer.getData(), certChainListBuffer.getSize()); szParseString[certChainListBuffer.getSize()] = '\0'; char* nextToken = NULL; char* szCertificateNamePostfix = get_next_token(szParseString, TOKEN_SEPARATOR, &nextToken); aesm_data_id_t fileid = PSE_PR_CERTIFICATE_FID; // For each certificate name, delete the file while (NULL != szCertificateNamePostfix) { if (AE_SUCCESS != upsePersistentStorage::Delete(fileid++)) { ++nError; } if (PSE_PR_CERTIFICATE_FID_MAX == fileid) { break; } szCertificateNamePostfix = get_next_token(NULL, TOKEN_SEPARATOR, &nextToken); } if (PSE_PR_CERTIFICATE_FID_MAX == fileid) { break; } if (AE_SUCCESS != upsePersistentStorage::Delete(PSE_PR_CERTIFICATE_CHAIN_FID)) { ++nError; } if (0 != nError) { break; } status = AE_SUCCESS; } while (0); if (NULL != szParseString) { free(szParseString); } SGX_DBGPRINT_PRINT_FUNCTION_AND_RETURNVAL(__FUNCTION__, status); return status; }
ae_error_t SigmaHelper::GetOcspResponseFromServer ( /*in */ const std::list<upse::Buffer>& certChain, /*in */ const OCSP_REQ& ocspReq, /*out*/ upse::Buffer& ocspResp ) { ae_error_t status = AE_FAILURE; int nPaddedBytes = 0; int nTotalOcspBytes = 0; do { if (ocspReq.ReqType == NO_OCSP) { status = AE_SUCCESS; break; } const char *url = EndpointSelectionInfo::instance().get_server_url( PSE_OCSP); if (url == NULL){ return OAL_CONFIG_FILE_ERROR; } // Load the root certificate into a local buffer upse::Buffer rootCert; SigmaHelper::GetRootCA(rootCert); std::list<upse::Buffer> ocspResponseList; // loop through chain and get an OCSP Response for each certificate/issuer pair bool fDone = false; // // certs were added leaf to root direction (assuming server functions according to spec) // std::list<upse::Buffer>::const_iterator itCertificate = certChain.begin(); do { if (itCertificate == certChain.end()) { status = AE_FAILURE; break; } upse::Buffer ocspResponse; const upse::Buffer& verifierCertificate = *itCertificate; ++itCertificate; int busy_loop = 0; do { if (itCertificate != certChain.end()) { const upse::Buffer& issuerCertificate = *itCertificate; status = Get_OCSPResponse(url, &ocspReq.OcspNonce, verifierCertificate, issuerCertificate, ocspResponse); } else { fDone = true; const upse::Buffer& issuerCertificate = rootCert; status = Get_OCSPResponse(url, &ocspReq.OcspNonce, verifierCertificate, issuerCertificate, ocspResponse); } if (AESM_PSE_PR_OCSP_RESPONSE_STATUS_TRYLATER != status) break; se_sleep(OCSP_BUSY_RETRY_SLEEP_MILLISECONDS); } while (busy_loop++ < MAX_OCSP_BUSY_RETRIES); if (AE_FAILED(status)) break; nPaddedBytes += REQUIRED_PADDING_DWORD_ALIGNMENT(ocspResponse.getSize()); nTotalOcspBytes += ocspResponse.getSize(); ocspResponseList.push_back(ocspResponse); } while (!fDone); if (AE_FAILED(status)) break; if (0 == ocspResponseList.size()) { status = AE_FAILURE; break; } nPaddedBytes = REQUIRED_PADDING_DWORD_ALIGNMENT(nTotalOcspBytes); if(UINT16_MAX-((int)sizeof(SIGMA_VLR_HEADER) + nPaddedBytes) < nTotalOcspBytes){ status = AE_FAILURE; break; } int nLength = static_cast<int>(sizeof(SIGMA_VLR_HEADER)) + nPaddedBytes + nTotalOcspBytes; ocspResp.Alloc(nLength); upse::BufferWriter bw(ocspResp); uint8_t* p; status = bw.reserve(nLength, &p); if (AE_FAILED(status)) break; OCSP_RESPONSE_VLR* pVLR = (OCSP_RESPONSE_VLR*)p; pVLR->VlrHeader.ID = OCSP_RESPONSE_VLR_ID; pVLR->VlrHeader.PaddedBytes = (UINT8)nPaddedBytes; pVLR->VlrHeader.Length = (UINT16)nLength; memset(pVLR->OcspResponse, 0, nPaddedBytes + nTotalOcspBytes); int nNext = 0; // // order above doesn't really matter since it's between verifier/host and ocsp responder // and each request/response is independent // spec basically says what's correct here // but we'll leave condition to show how to traverse in either order // #if !defined(LEAFTOROOT) #error LEAFTOROOT not defined #endif #if !LEAFTOROOT // // this clause adds responses from root to leaf // SGX_DBGPRINT_PRINT_STRING_LTP("root ocsp to leaf ocsp direction"); std::list<upse::Buffer>::reverse_iterator itRespList = ocspResponseList.rbegin(); for ( ; itRespList != ocspResponseList.rend(); ++itRespList) { const upse::Buffer& item = *itRespList; memcpy_s(pVLR->OcspResponse + nNext, item.getSize(), item.getData(), item.getSize()); nNext += item.getSize(); } #else SGX_DBGPRINT_PRINT_STRING_LTP("leaf ocsp to root ocsp direction"); // // this clause adds responses from leaf to root // std::list<upse::Buffer>::iterator itRespList = ocspResponseList.begin(); for ( ; itRespList != ocspResponseList.end(); ++itRespList) { const upse::Buffer& item = *itRespList; memcpy_s(pVLR->OcspResponse + nNext, item.getSize(), item.getData(), item.getSize()); nNext += item.getSize(); } #endif Helper::write_ocsp_response_vlr(ocspResp); status = AE_SUCCESS; } while (0); if (status == OAL_NETWORK_UNAVAILABLE_ERROR) { if (ocspReq.ReqType == CACHED && AE_SUCCEEDED(Helper::read_ocsp_response_vlr(ocspResp))) { status = AE_SUCCESS; } } SGX_DBGPRINT_PRINT_FUNCTION_AND_RETURNVAL(__FUNCTION__, status); return status; }