From 723a6f38d17307b7cac9061e03b29381ab40cd7f Mon Sep 17 00:00:00 2001 From: msft-gumunjal <81185076+msft-gumunjal@users.noreply.github.com> Date: Tue, 14 Jun 2022 09:47:01 -0700 Subject: [PATCH] Updating collateral verification API to fetch custom data. (#157) * Updating collateral verification API to fetch custom data. * Updating TCB info API request. * converting custom param to base64 before appending to the URL. * Updating base64 conversion algo. * updating function signature for sgx_ql_get_quote_verification_collateral_with_params. * Handling rebasing conflicts. * Passing JSON without flattening to extract_from_json() method. * Updating curl command threshold to run cache tests. * Adding test case for icelake cluster to fetch custom params. * Catch exceptions as reference to handle polymorphic type error. * reverting the API version to call into THIM service. --- src/UnitTest/test_quote_prov.cpp | 246 ++++++++++++++++++++- src/Windows/curl_easy.cpp | 82 ++++--- src/Windows/dll/dcap_provider.def | 4 +- src/Windows/local_cache.cpp | 1 - src/dcap_provider.cpp | 350 +++++++++++++++++++++++++++--- src/dcap_provider.h | 3 + 6 files changed, 621 insertions(+), 65 deletions(-) diff --git a/src/UnitTest/test_quote_prov.cpp b/src/UnitTest/test_quote_prov.cpp index d8e6da87..355e6064 100644 --- a/src/UnitTest/test_quote_prov.cpp +++ b/src/UnitTest/test_quote_prov.cpp @@ -68,6 +68,14 @@ typedef quote3_error_t (*sgx_ql_get_quote_verification_collateral_t)( const char* pck_ca, sgx_ql_qve_collateral_t** pp_quote_collateral); +typedef quote3_error_t (*sgx_ql_get_quote_verification_collateral_with_params_t)( + const uint8_t* fmspc, + const uint16_t fmspc_size, + const char* pck_ca, + const void* custom_param, + const uint16_t custom_param_length, + sgx_ql_qve_collateral_t** pp_quote_collateral); + typedef quote3_error_t (*sgx_ql_get_qve_identity_t)( char** pp_qve_identity, uint32_t* p_qve_identity_size, @@ -93,6 +101,8 @@ static sgx_ql_free_qve_identity_t sgx_ql_free_qve_identity; static sgx_ql_free_root_ca_crl_t sgx_ql_free_root_ca_crl; static sgx_ql_get_quote_verification_collateral_t sgx_ql_get_quote_verification_collateral; +static sgx_ql_get_quote_verification_collateral_with_params_t + sgx_ql_get_quote_verification_collateral_with_params; static sgx_ql_get_qve_identity_t sgx_ql_get_qve_identity; static sgx_ql_get_root_ca_crl_t sgx_ql_get_root_ca_crl; @@ -100,6 +110,10 @@ static sgx_ql_get_root_ca_crl_t sgx_ql_get_root_ca_crl; static constexpr uint8_t TEST_FMSPC[] = {0x00, 0x90, 0x6E, 0xA1, 0x00, 0x00}; static constexpr uint8_t ICX_TEST_FMSPC[] = {0x00, 0x60, 0x6a, 0x00, 0x00, 0x00}; +const uint16_t custom_param_length = 45; +const char *custom_param = "tcbEvaluationDataNumber=11;region=us central"; +std::string tcbEvaluationDataNumber = "11"; + // Test input (choose an arbitrary Azure server) static uint8_t qe_id[16] = { 0x00, @@ -205,6 +219,8 @@ static void Log(sgx_ql_log_level_t level, const char* message) printf("[%s]: %s\n", levelText, message); } + + #if defined __LINUX__ static void* LoadFunctions() { @@ -253,6 +269,11 @@ static void* LoadFunctions() dlsym(library, "sgx_ql_free_root_ca_crl")); EXPECT_NE(sgx_ql_free_root_ca_crl, nullptr); + sgx_ql_get_quote_verification_collateral_with_params = reinterpret_cast< + sgx_ql_get_quote_verification_collateral_with_params_t>( + dlsym(library, "sgx_ql_get_quote_verification_collateral_with_params")); + EXPECT_NE(sgx_ql_get_quote_verification_collateral_with_params, nullptr); + sgx_ql_get_quote_verification_collateral = reinterpret_cast( dlsym(library, "sgx_ql_get_quote_verification_collateral")); @@ -321,6 +342,11 @@ static HINSTANCE LoadFunctions() hLibCapdll, "sgx_ql_get_quote_verification_collateral")); EXPECT_NE(sgx_ql_get_quote_verification_collateral, nullptr); + sgx_ql_get_quote_verification_collateral_with_params = reinterpret_cast< + sgx_ql_get_quote_verification_collateral_with_params_t>(GetProcAddress( + hLibCapdll, "sgx_ql_get_quote_verification_collateral_with_params")); + EXPECT_NE(sgx_ql_get_quote_verification_collateral_with_params, nullptr); + sgx_ql_get_qve_identity = reinterpret_cast( GetProcAddress(hLibCapdll, "sgx_ql_get_qve_identity")); EXPECT_NE(sgx_ql_get_qve_identity, nullptr); @@ -333,6 +359,33 @@ static HINSTANCE LoadFunctions() } #endif +// +// extract raw value from response body, if exists +// +sgx_plat_error_t extract_from_json( + const nlohmann::json& json, + const std::string& item, + std::string* out_header) +{ + try + { + nlohmann::json raw_value = json[item]; + if (!raw_value.is_string()) + { + raw_value = raw_value.dump(); + } + if (out_header != nullptr) + { + *out_header = raw_value; + } + } + catch (const exception& ex) + { + return SGX_PLAT_ERROR_UNEXPECTED_SERVER_RESPONSE; + } + return SGX_PLAT_ERROR_OK; +} + // // Fetches and validates certification data for a platform // @@ -511,6 +564,40 @@ static void GetVerificationCollateralTest() VerifyCollateral(collateral); } +// +// Fetches and validates verification APIs of QPL with custom params provided +// +static void GetVerificationCollateralTestWithParams() +{ + // Test input (choose an arbitrary Azure server) + + sgx_ql_qve_collateral_t* collateral = nullptr; + std::string tcbInfoTcbEvaluationDataNumber; + std::string enclaveIdentityTcbEvaluationDataNumber; + nlohmann::json json_body; + quote3_error_t result = sgx_ql_get_quote_verification_collateral_with_params( + TEST_FMSPC, + sizeof(TEST_FMSPC), + "processor", + custom_param, + custom_param_length, + &collateral); + ASSERT_TRUE(SGX_QL_SUCCESS == result); + json_body = nlohmann::json::parse(collateral->tcb_info); + extract_from_json( + json_body.flatten(), + "/tcbInfo/tcbEvaluationDataNumber", + &tcbInfoTcbEvaluationDataNumber); + ASSERT_TRUE(tcbInfoTcbEvaluationDataNumber.compare(tcbEvaluationDataNumber) == 0); + json_body = nlohmann::json::parse(collateral->qe_identity); + extract_from_json( + json_body.flatten(), + "/enclaveIdentity/tcbEvaluationDataNumber", + &enclaveIdentityTcbEvaluationDataNumber); + ASSERT_TRUE(enclaveIdentityTcbEvaluationDataNumber.compare(tcbEvaluationDataNumber) == 0); + VerifyCollateral(collateral); +} + // // Fetches and validates verification APIs of QPL // @@ -526,6 +613,38 @@ static void GetVerificationCollateralTestICXV3() VerifyCollateral(collateral); } +// +// Fetches and validates verification APIs of QPL with custom params provided +// +static void GetVerificationCollateralTestICXV3WithParams() +{ + sgx_ql_qve_collateral_t* collateral = nullptr; + std::string tcbEvaluationDataNumber; + std::string enclaveIdentityTcbEvaluationDataNumber; + quote3_error_t result = sgx_ql_get_quote_verification_collateral_with_params( + ICX_TEST_FMSPC, + sizeof(ICX_TEST_FMSPC), + "platform", + custom_param, + custom_param_length, + &collateral); + ASSERT_TRUE(SGX_QL_SUCCESS == result); + nlohmann::json json_body = nlohmann::json::parse(collateral->tcb_info); + extract_from_json( + json_body.flatten(), + "/tcbInfo/tcbEvaluationDataNumber", + &tcbEvaluationDataNumber); + ASSERT_TRUE(tcbEvaluationDataNumber.compare(tcbEvaluationDataNumber) == 0); + json_body = nlohmann::json::parse(collateral->qe_identity); + extract_from_json( + json_body.flatten(), + "/enclaveIdentity/tcbEvaluationDataNumber", + &enclaveIdentityTcbEvaluationDataNumber); + ASSERT_TRUE( + enclaveIdentityTcbEvaluationDataNumber.compare(tcbEvaluationDataNumber) == 0); + VerifyCollateral(collateral); +} + static boolean GetQveIdentityTest() { boolean TEST_SUCCESS = false; @@ -569,7 +688,7 @@ static void GetRootCACrlTest() #ifdef __LINUX__ constexpr auto CURL_TOLERANCE = 0.002; #else -constexpr auto CURL_TOLERANCE = 0.004; +constexpr auto CURL_TOLERANCE = 0.008; #endif static inline float MeasureFunction(measured_function_t func) @@ -603,7 +722,6 @@ static inline void VerifyDurationChecks( EXPECT_TRUE( fabs(duration_curl_verification - duration_local_verification) > CURL_TOLERANCE); - constexpr int NUMBER_VERIFICATION_CURL_CALLS = 4; EXPECT_TRUE( duration_local_verification < @@ -636,6 +754,33 @@ boolean RunQuoteProviderTests(bool caching_enabled = false) return true; } +boolean RunQuoteProviderTestsWithCustomParams(bool caching_enabled = false) +{ + local_cache_clear(); + auto duration_curl_cert = MeasureFunction(GetCertsTest); + GetCrlTest(); + auto duration_curl_verification_with_params = + MeasureFunction(GetVerificationCollateralTestWithParams); + GetRootCACrlTest(); + + // + // Second pass: Ensure that we ONLY get data from the cache + // + auto duration_local_cert = MeasureFunction(GetCertsTest); + GetCrlTest(); + GetRootCACrlTest(); + auto duration_local_verification_with_params = + MeasureFunction(GetVerificationCollateralTestWithParams); + VerifyDurationChecks( + duration_local_cert, + duration_local_verification_with_params, + duration_curl_cert, + duration_curl_verification_with_params, + caching_enabled); + + return true; +} + boolean RunQuoteProviderTestsICXV3(bool caching_enabled = false) { local_cache_clear(); @@ -660,6 +805,32 @@ boolean RunQuoteProviderTestsICXV3(bool caching_enabled = false) return true; } +boolean RunQuoteProviderTestsICXV3WithParam(bool caching_enabled = false) +{ + local_cache_clear(); + auto duration_curl_cert = MeasureFunction(GetCertsTestICXV3); + GetCrlTestICXV3(); + auto duration_curl_verification_with_params = + MeasureFunction(GetVerificationCollateralTestICXV3WithParams); + GetRootCACrlTest(); + + // + // Second pass: Ensure that we ONLY get data from the cache + // + auto duration_local_cert = MeasureFunction(GetCertsTestICXV3); + GetCrlTestICXV3(); + GetRootCACrlTest(); + auto duration_local_verification_with_params = + MeasureFunction(GetVerificationCollateralTestICXV3WithParams); + VerifyDurationChecks( + duration_local_cert, + duration_local_verification_with_params, + duration_curl_cert, + duration_curl_verification_with_params, + caching_enabled); + return true; +} + void ReloadLibrary(libary_type_t* library, bool set_logging_callback = true) { #if defined __LINUX__ @@ -829,7 +1000,8 @@ boolean RunCachePermissionTests(libary_type_t* library) {STANDARD_RIGHTS_ALL, SET_ACCESS}, {GENERIC_READ | GENERIC_WRITE, DENY_ACCESS}, {GENERIC_READ, DENY_ACCESS}, - {GENERIC_WRITE, DENY_ACCESS}}; + {GENERIC_WRITE, DENY_ACCESS} + }; EXPECT_TRUE(SetEnvironmentVariableA("AZDCAP_CACHE", permission_folder)); #endif @@ -859,6 +1031,49 @@ boolean RunCachePermissionTests(libary_type_t* library) return true; } +boolean RunCachePermissionTestsWithCustomParamToFetchCollateral(libary_type_t* library) +{ +#if defined __LINUX__ + auto permission_folder = "./test_permission"; + int permissions[] = {0700, 0400, 0200, 0000}; + setenv("AZDCAP_CACHE", permission_folder, 1); +#else + auto permission_folder = ".\\test_permission"; + permission_type_t permissions[] = { + {STANDARD_RIGHTS_ALL, SET_ACCESS}, + {GENERIC_READ | GENERIC_WRITE, DENY_ACCESS}, + {GENERIC_READ, DENY_ACCESS}, + {GENERIC_WRITE, DENY_ACCESS} + }; + EXPECT_TRUE(SetEnvironmentVariableA("AZDCAP_CACHE", permission_folder)); +#endif + + // Create the parent folder before the library runs + for (auto permission : permissions) + { + ReloadLibrary(library); + make_folder(permission_folder, permission); + RunQuoteProviderTestsWithCustomParams(is_caching_allowed(permission)); + allow_access(permission_folder); + remove_folder(permission_folder); + } + + // Change the permissions on the parent folder after the + // library has used it + for (auto permission : permissions) + { + ReloadLibrary(library); + make_folder(permission_folder, permissions[0]); + RunQuoteProviderTestsWithCustomParams(true); + change_permission(permission_folder, permission); + RunQuoteProviderTestsWithCustomParams(false); + allow_access(permission_folder); + remove_folder(permission_folder); + } + + return true; +} + void SetupEnvironment(std::string version) { #if defined __LINUX__ @@ -978,6 +1193,7 @@ TEST(testQuoteProv, quoteProviderTestsV3DataFromService) SetupEnvironmentToReachSecondary(); ASSERT_TRUE(RunQuoteProviderTests()); ASSERT_TRUE(RunQuoteProviderTestsICXV3()); + ASSERT_TRUE(RunQuoteProviderTestsICXV3WithParam()); ASSERT_TRUE(GetQveIdentityTest()); #if defined __LINUX__ @@ -998,6 +1214,7 @@ TEST(testQuoteProv, quoteProviderTestsV3Data) SetupEnvironment("v3"); ASSERT_TRUE(RunQuoteProviderTests()); ASSERT_TRUE(RunQuoteProviderTestsICXV3()); + ASSERT_TRUE(RunQuoteProviderTestsICXV3WithParam()); ASSERT_TRUE(GetQveIdentityTest()); #if defined __LINUX__ @@ -1017,7 +1234,7 @@ TEST(testQuoteProv, testWithoutLogging) // SetupEnvironment("v2"); ReloadLibrary(&library, false); - ASSERT_TRUE(RunQuoteProviderTests()); + ASSERT_TRUE(RunQuoteProviderTestsWithCustomParams()); ASSERT_TRUE(GetQveIdentityTest()); #if defined __LINUX__ @@ -1036,9 +1253,30 @@ TEST(testQuoteProv, testRestrictAccessToFilesystem) // Get the data from the service // SetupEnvironment("v2"); + SetupEnvironmentToReachSecondary(); ReloadLibrary(&library, false); ASSERT_TRUE(RunCachePermissionTests(&library)); +#if defined __LINUX__ + dlclose(library); +#else + FreeLibrary(library); +#endif +} + +TEST(testQuoteProv, testRestrictAccessToFilesystemForCustomParamCollateral) +{ + libary_type_t library = LoadFunctions(); + ASSERT_TRUE(SGX_PLAT_ERROR_OK == sgx_ql_set_logging_function(Log)); + + // + // Get the data from the service + // + SetupEnvironment("v2"); + SetupEnvironmentToReachSecondary(); + ReloadLibrary(&library, false); + ASSERT_TRUE(RunCachePermissionTestsWithCustomParamToFetchCollateral(&library)); + #if defined __LINUX__ dlclose(library); #else diff --git a/src/Windows/curl_easy.cpp b/src/Windows/curl_easy.cpp index 876cfb9b..eb69e718 100644 --- a/src/Windows/curl_easy.cpp +++ b/src/Windows/curl_easy.cpp @@ -17,6 +17,8 @@ /////////////////////////////////////////////////////////////////////////////// // Constants /////////////////////////////////////////////////////////////////////////////// +static constexpr int maximum_retries = 5; +static constexpr int initial_retry_delay_ms = 20; static constexpr WCHAR content_type_header[] = L"Content-Type: application/json"; @@ -262,37 +264,61 @@ DWORD curl_easy::get_response_code() const void curl_easy::perform() const { - // Start the protocol exchange with the server. - if (!WinHttpSendRequest( - request.get(), - request_header_text.c_str(), - (DWORD)request_header_text.size(), - (PVOID)request_body_data.c_str(), - (DWORD)request_body_data.size(), - (DWORD)request_body_data.size(), - 0)) + int retry_delay = initial_retry_delay_ms; + int attempts = 0; + do { - throw_on_error( - GetLastError(), "curl_easy::perform/WinHttpSendRequest"); - } + // Start the protocol exchange with the server. + if (!WinHttpSendRequest( + request.get(), + request_header_text.c_str(), + (DWORD)request_header_text.size(), + (PVOID)request_body_data.c_str(), + (DWORD)request_body_data.size(), + (DWORD)request_body_data.size(), + 0)) + { + throw_on_error( + GetLastError(), "curl_easy::perform/WinHttpSendRequest"); + } - // Wait for the response from the server. - if (!WinHttpReceiveResponse(request.get(), nullptr)) - { - DWORD lastError = GetLastError(); - throw_on_error( - lastError, "curl_easy::perform/WinHttpReceiveRequest"); - } + // Wait for the response from the server. + if (!WinHttpReceiveResponse(request.get(), nullptr)) + { + DWORD lastError = GetLastError(); + if (lastError == ERROR_WINHTTP_TIMEOUT || + lastError == ERROR_WINHTTP_RESEND_REQUEST) + { + if (attempts <= maximum_retries) + { + attempts++; + log(SGX_QL_LOG_INFO, + "CURL timeout detected (WINHTTP Error %zd). Retrying " + "after %d milliseconds (attempt %d / %d) ", + lastError, + retry_delay, + attempts, + maximum_retries); + Sleep(retry_delay); + retry_delay *= 2; + continue; + } + } + throw_on_error( + lastError, "curl_easy::perform/WinHttpReceiveRequest"); + } - DWORD response_code = get_response_code(); - if (response_code >= HTTP_STATUS_BAD_REQUEST && response_code <= HTTP_STATUS_SERVER_ERROR) - { - log(SGX_QL_LOG_INFO, - "HTTP Error (%d) on curl->perform() request", - response_code); - throw_on_error(WINHTTP_ERROR_BASE, "curl_easy::perform"); - } - return; + DWORD response_code = get_response_code(); + if (response_code >= HTTP_STATUS_BAD_REQUEST && + response_code <= HTTP_STATUS_SERVER_ERROR) + { + log(SGX_QL_LOG_INFO, + "HTTP Error (%d) on curl->perform() request", + response_code); + throw_on_error(WINHTTP_ERROR_BASE, "curl_easy::perform"); + } + return; + } while (true); } const std::vector& curl_easy::get_body() const diff --git a/src/Windows/dll/dcap_provider.def b/src/Windows/dll/dcap_provider.def index 3464573a..c9abeb39 100644 --- a/src/Windows/dll/dcap_provider.def +++ b/src/Windows/dll/dcap_provider.def @@ -9,7 +9,9 @@ EXPORTS sgx_ql_free_qve_identity; sgx_ql_free_root_ca_crl; sgx_ql_get_quote_verification_collateral; + sgx_ql_get_quote_verification_collateral_with_params; sgx_ql_get_qve_identity; sgx_ql_get_root_ca_crl; sgx_get_qe_identity_info - sgx_free_qe_identity_info \ No newline at end of file + sgx_free_qe_identity_info + diff --git a/src/Windows/local_cache.cpp b/src/Windows/local_cache.cpp index 098ee88f..7fd42af8 100644 --- a/src/Windows/local_cache.cpp +++ b/src/Windows/local_cache.cpp @@ -229,7 +229,6 @@ wil::unique_hfile OpenHandle(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dw do { file.reset(CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile)); - retry = false; if (!file && (GetLastError() == ERROR_SHARING_VIOLATION) && (i < MAX_RETRY)) { retry = true; diff --git a/src/dcap_provider.cpp b/src/dcap_provider.cpp index 85876ad5..26505cbc 100644 --- a/src/dcap_provider.cpp +++ b/src/dcap_provider.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -434,9 +433,13 @@ sgx_plat_error_t extract_from_json( { try { - std::string raw_value = json[item]; - log(SGX_QL_LOG_INFO, "Required information from JSON"); - log(SGX_QL_LOG_INFO, "% s: [% s] ", item.c_str(), raw_value.c_str()); + nlohmann::json raw_value = json[item]; + if (!raw_value.is_string()) + { + raw_value = raw_value.dump(); + } + log(SGX_QL_LOG_INFO, + "Fetched %s value from JSON. \n", item.c_str()); if (out_header != nullptr) { *out_header = raw_value; @@ -445,7 +448,7 @@ sgx_plat_error_t extract_from_json( catch (const exception& ex) { log(SGX_QL_LOG_ERROR, - "Require information '%s' is missing.", item.c_str()); + "Required information '%s' is missing. \n", item.c_str()); return SGX_PLAT_ERROR_UNEXPECTED_SERVER_RESPONSE; } return SGX_PLAT_ERROR_OK; @@ -646,7 +649,7 @@ static void pck_cert_url( std::string cpu_svn, std::string pce_svn, std::string pce_id, - std::string eppid_json, + std::string eppid_json, bool append_eppid = true) { url << '/' << version; @@ -671,7 +674,7 @@ static void pck_cert_url( url << API_VERSION_07_2021; } -static void build_pck_cert_url(const sgx_ql_pck_cert_id_t& pck_cert_id, certificate_fetch_url& certificate_url, std::stringstream& cached_file_name ) +static void build_pck_cert_url(const sgx_ql_pck_cert_id_t& pck_cert_id, certificate_fetch_url& certificate_url, std::stringstream& cached_file_name) { const std::string qe_id = format_as_hex_string(pck_cert_id.p_qe3_id, pck_cert_id.qe3_id_size); @@ -712,9 +715,14 @@ static sgx_plat_error_t build_cert_chain(const curl_easy& curl, const nlohmann:: result = extract_from_json(json, "pckCert", &leaf_cert); if (result != SGX_PLAT_ERROR_OK) return result; + log(SGX_QL_LOG_INFO, "pckCert : %s from JSON", leaf_cert.c_str()); result = extract_from_json(json, headers::PCK_CERT_ISSUER_CHAIN, &temp_chain); if (result != SGX_PLAT_ERROR_OK) return result; + log(SGX_QL_LOG_INFO, + "%s : %s", + headers::PCK_CERT_ISSUER_CHAIN, + temp_chain.c_str()); chain = curl.unescape(temp_chain); // The cache service does not return a newline in the response @@ -724,7 +732,7 @@ static sgx_plat_error_t build_cert_chain(const curl_easy& curl, const nlohmann:: leaf_cert += "\n"; } - log(SGX_QL_LOG_INFO, "libquote_provider.so: [%s]", chain.c_str()); + log(SGX_QL_LOG_INFO, "Cert chain formed: [%s]", chain.c_str()); if (out_header != nullptr) { *out_header = leaf_cert + chain; @@ -782,10 +790,9 @@ static sgx_plat_error_t parse_svn_values( sgx_plat_error_t result = SGX_PLAT_ERROR_OK; std::string tcb; result = extract_from_json(json, headers::TCB_INFO, &tcb); - if (result != SGX_PLAT_ERROR_OK) return result; - + log(SGX_QL_LOG_INFO, "%s : %s", headers::TCB_INFO, tcb.c_str()); // string size == byte size * 2 (for hex-encoding) static constexpr size_t CPUSVN_SIZE = 2 * sizeof(quote_config->cert_cpu_svn); @@ -898,7 +905,140 @@ static sgx_plat_error_t build_pck_crl_url( return SGX_PLAT_ERROR_OK; } -static std::string build_tcb_info_url(const std::string& fmspc) +// Base64 alphabet defined in RFC 4648 +/* Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y +*/ +char get_base64_char(uint8_t val) +{ + if (val < 26) + { + return 'A' + val; + } + + if (val < 52) + { + return 'a' + val - 26; + } + + if (val < 62) + { + return '0' + val - 52; + } + + if (val == 62) + { + return '+'; + } + + if (val == 63) + { + return '/'; + } + + // error case + throw SGX_QL_ERROR_INVALID_PARAMETER; +} + + std::string base64_encode( + const void* source, + const uint16_t custom_param_length) +{ + // Character set of base64 encoding scheme + const uint8_t* input = static_cast(source); + + // Group input string into 3 characters. Since we are converting 8 bit(3 bytes) to + // 6 bit(4 bytes) and lcm of these is 24, the base case is having 24 bits to work with. + size_t groups = custom_param_length / 3; + + // Last group will either have 1 or 2 characters. This case will be handled + // separately at the end. + size_t last_group_size = custom_param_length % 3; + std::string encoded_string; + try + { + for(size_t i = 0; i < groups; ++i) + { + // To get base 64 encoded first byte, take the first 6 bits from the first byte of input string + uint8_t byte1 = (input[i * 3] >> 2); + // To get base 64 encoded second byte, take the first 4 bits from the second byte of the input + // string and the remaining 2 bits of the first byte of the input string + uint8_t byte2 = (input[i * 3 + 1] >> 4) | ((input[i * 3] & 3) << 4); + // To get base 64 encoded third byte, take the first 2 bits from the third byte of the input string + // and the remaining 4 bits of the second byte of the input string + uint8_t byte3 = (input[i * 3 + 2] >> 6) | ((input[i * 3 + 1] & 15) << 2); + // To get base 64 encoded last byte, take the remaining 6 bits from the third byte of the input string + uint8_t byte4 = input[i * 3 + 2] & 63; + encoded_string.push_back(get_base64_char(byte1)); + encoded_string.push_back(get_base64_char(byte2)); + encoded_string.push_back(get_base64_char(byte3)); + encoded_string.push_back(get_base64_char(byte4)); + } + + // Last group has 1 character to encode + if (last_group_size == 1) + { + // To get base 64 encoded first byte, take the first 6 bits from the + // first byte of input string + uint8_t byte1 = input[groups * 3] >> 2; + // To get base 64 encoded second byte, take the remaining 2 bits of the first byte of the input string + uint8_t byte2 = (input[groups * 3] & 3) << 4; + encoded_string.push_back(get_base64_char(byte1)); + encoded_string.push_back(get_base64_char(byte2)); + + // Add padding for the remianing bits + encoded_string.push_back('='); + encoded_string.push_back('='); + } + // Last group has 2 characters to encode + else if (last_group_size == 2) + { + // To get base 64 encoded first byte, take the first 6 bits from the + // first byte of input string + uint8_t byte1 = input[groups * 3] >> 2; + // To get base 64 encoded second byte, take the first 4 bits from the + // second byte of the input string and the remaining 2 bits of the first + // byte of the input string + uint8_t byte2 = (input[groups * 3 + 1] >> 4) | ((input[groups * 3] & 3) << 4); + // To get base 64 encoded third byte, take the remaining 4 bits of the second + // byte of the input string + uint8_t byte3 = (input[groups * 3 + 1] & 15) << 2; + encoded_string.push_back(get_base64_char(byte1)); + encoded_string.push_back(get_base64_char(byte2)); + encoded_string.push_back(get_base64_char(byte3)); + + // Add padding for the remianing bits + encoded_string.push_back('='); + } + } + catch(exception& SGX_QL_ERROR_INVALID_PARAMETER) + { + log(SGX_QL_LOG_ERROR, "Incorrect parameter passed for encoding."); + throw SGX_QL_ERROR_INVALID_PARAMETER; + } + return encoded_string; +} + +static std::string build_tcb_info_url( + const std::string& fmspc, + const void* custom_param = nullptr, + const uint16_t custom_param_length = 0) { std::string version = get_collateral_version(); std::string client_id = get_client_id(); @@ -909,8 +1049,25 @@ static std::string build_tcb_info_url(const std::string& fmspc) { tcb_info_url << "/" << version; } - tcb_info_url << "/tcb/"; - tcb_info_url << format_as_hex_string(fmspc.c_str(), fmspc.size()) << "?"; + + tcb_info_url << "/tcb?"; + tcb_info_url << "fmspc=" << format_as_hex_string(fmspc.c_str(), fmspc.size()) << "&"; + + if (custom_param != nullptr) + { + std::string encoded_str; + try + { + encoded_str = base64_encode(custom_param, custom_param_length); + } + catch (exception& e) + { + log(SGX_QL_LOG_ERROR, "TCB_Info_URL: Invalid parameters provided."); + throw e; + } + tcb_info_url << customParam << "=" << encoded_str << "&"; + } + if (!client_id.empty()) { @@ -924,10 +1081,23 @@ static std::string build_tcb_info_url(const std::string& fmspc) // The expected URL for a given TCB. // static std::string build_tcb_info_url( - const sgx_ql_get_revocation_info_params_t& params) + const sgx_ql_get_revocation_info_params_t& params, + const void* custom_param = nullptr, + const uint16_t custom_param_length = 0) { std::string fmspc((char*)params.fmspc, params.fmspc_size); - return build_tcb_info_url(fmspc); + std ::string tcb_info_url; + try + { + tcb_info_url = + build_tcb_info_url(fmspc, custom_param, custom_param_length); + return tcb_info_url; + } + catch (exception& e) + { + throw e; + } + } // @@ -935,7 +1105,9 @@ static std::string build_tcb_info_url( // static std::string build_enclave_id_url( bool qve, - std::string& expected_issuer_chain_header) + std::string& expected_issuer_chain_header, + const void* custom_param = nullptr, + const uint16_t custom_param_length = 0) { std::string version = get_collateral_version(); std::string client_id = get_client_id(); @@ -960,7 +1132,22 @@ static std::string build_enclave_id_url( return ""; } - qe_id_url << "/" << (qve ? "qveid" : "qeid") << "?"; + qe_id_url << "/" << (qve ? "qve/identity" : "qe/identity") << "?"; + + if (custom_param != nullptr) + { + std::string encoded_str; + try + { + encoded_str = base64_encode(custom_param, custom_param_length); + } + catch (exception& e) + { + log(SGX_QL_LOG_ERROR, "Enclave_Id_URL: Invalid parameters provided."); + throw e; + } + qe_id_url << customParam << "=" << encoded_str << "&"; + } if (!client_id.empty()) { @@ -1012,6 +1199,10 @@ static quote3_error_t get_collateral( url.c_str()); response_body = *cache_hit_collateral; issuer_chain = std::string(cache_hit_issuer_chain->begin(), cache_hit_issuer_chain->end()); + log(SGX_QL_LOG_INFO, + "Successfully fetched %s from cache: '%s'.", + friendly_name.c_str(), + url.c_str()); return SGX_QL_SUCCESS; } } @@ -1028,6 +1219,10 @@ static quote3_error_t get_collateral( retval = convert_to_intel_error(get_issuer_chain_operation); if (retval == SGX_QL_SUCCESS) { + log(SGX_QL_LOG_INFO, + "Successfully fetched %s from URL: '%s'.", + friendly_name.c_str(), + url.c_str()); std::string cache_control; auto get_cache_header_operation = get_unescape_header(*curl_operation, headers::CACHE_CONTROL, &cache_control); retval = convert_to_intel_error(get_cache_header_operation); @@ -1173,14 +1368,24 @@ extern "C" quote3_error_t sgx_ql_get_quote_config( retval, 0); } - if (!recieved_certificate) + if (recieved_certificate) + { + log(SGX_QL_LOG_INFO, + "Successfully fetched certificate from primary URL: '%s'.", + primary_base_url.c_str()); + } + else { log(SGX_QL_LOG_INFO, "Trying to fetch response from local cache."); recieved_certificate = check_cache(cached_file_name.str(), pp_quote_config); if (recieved_certificate) + { + log(SGX_QL_LOG_INFO, + "Successfully fetched certificate from cache."); return SGX_QL_SUCCESS; + } else { log(SGX_QL_LOG_INFO, @@ -1193,6 +1398,9 @@ extern "C" quote3_error_t sgx_ql_get_quote_config( retval); if (!recieved_certificate) return retval; + log(SGX_QL_LOG_INFO, + "Successfully fetched certificate from secondary URL: '%s'.", + secondary_base_url.c_str()); } } @@ -1260,10 +1468,13 @@ extern "C" quote3_error_t sgx_ql_get_quote_config( std::string cache_control; auto get_cache_header_operation = extract_from_json( json_body, headers::CERT_CACHE_CONTROL, &cache_control); - retval = convert_to_intel_error(get_cache_header_operation); if (retval == SGX_QL_SUCCESS) { + log(SGX_QL_LOG_INFO, + "%s : %s", + headers::CERT_CACHE_CONTROL, + cache_control.c_str()); time_t expiry = 0; if (get_cert_cache_expiration_time(cache_control, cached_file_name.str(), expiry)) { @@ -1313,7 +1524,6 @@ extern "C" quote3_error_t sgx_ql_free_quote_config( sgx_ql_config_t* p_quote_config) { delete[] p_quote_config; - return SGX_QL_SUCCESS; } @@ -1371,9 +1581,12 @@ extern "C" sgx_plat_error_t sgx_ql_get_revocation_info( log(SGX_QL_LOG_INFO, "Fetching revocation info from remote server: '%s'", crl_url.c_str()); - crl_operation->set_headers(headers::default_values); crl_operation->perform(); + log(SGX_QL_LOG_INFO, + "Successfully fetched %s from URL: '%s'", + get_collateral_friendly_name(CollateralTypes::PckCrl).c_str(), + crl_url.c_str()); crls.push_back(crl_operation->get_body()); total_crl_size = safe_add(total_crl_size, crls.back().size()); total_crl_size = @@ -1407,6 +1620,10 @@ extern "C" sgx_plat_error_t sgx_ql_get_revocation_info( "Fetching TCB Info from remote server: '%s'.", tcb_info_url.c_str()); tcb_info_operation->perform(); + log(SGX_QL_LOG_INFO, + "Successfully fetched '%s' from URL: '%s'", + get_collateral_friendly_name(CollateralTypes::TcbInfo).c_str(), + tcb_info_url.c_str()); tcb_info = tcb_info_operation->get_body(); @@ -1556,14 +1773,26 @@ extern "C" sgx_plat_error_t sgx_get_qe_identity_info( std::string issuer_chain; std::string request_id; size_t total_buffer_size = 0; - std::string qe_id_url = - build_enclave_id_url(false, issuer_chain_header); + std::string qe_id_url; + try + { + qe_id_url = build_enclave_id_url(false, issuer_chain_header); + } + catch (exception& e) + { + log(SGX_QL_LOG_ERROR, "QE_ID_URL can't be formed. Validate the parameters passed."); + return SGX_PLAT_ERROR_INVALID_PARAMETER; + } const auto curl = curl_easy::create(qe_id_url, nullptr); log(SGX_QL_LOG_INFO, "Fetching QE Identity from remote server: '%s'.", qe_id_url.c_str()); curl->perform(); + log(SGX_QL_LOG_INFO, + "Successfully fetched '%s' from URL: '%s'", + get_collateral_friendly_name(CollateralTypes::QeIdentity).c_str(), + qe_id_url.c_str()); // issuer chain result = get_unescape_header(*curl, issuer_chain_header, &issuer_chain); @@ -1699,11 +1928,13 @@ extern "C" quote3_error_t sgx_ql_free_root_ca_crl(char* p_root_ca_crl) return SGX_QL_SUCCESS; } -extern "C" quote3_error_t sgx_ql_get_quote_verification_collateral( +quote3_error_t sgx_ql_fetch_quote_verification_collateral( const uint8_t* fmspc, const uint16_t fmspc_size, const char* pck_ca, - sgx_ql_qve_collateral_t** pp_quote_collateral) + sgx_ql_qve_collateral_t** pp_quote_collateral, + const void* custom_param = nullptr, + const uint16_t custom_param_length = 0) { log(SGX_QL_LOG_INFO, "Getting quote verification collateral"); sgx_ql_qve_collateral_t* p_quote_collateral = nullptr; @@ -1808,8 +2039,18 @@ extern "C" quote3_error_t sgx_ql_get_quote_verification_collateral( } // Get Tcb Info & Issuer Chain - std::string tcb_info_url = build_tcb_info_url(str_fmspc); - const auto tcb_info_operation = + std::string tcb_info_url; + try + { + tcb_info_url = build_tcb_info_url( + str_fmspc, custom_param, custom_param_length); + } + catch (exception& e) + { + log(SGX_QL_LOG_ERROR, "TCB_INFO_URL can't be formed. Validate the parameters passed."); + return SGX_QL_ERROR_INVALID_PARAMETER; + } + const auto tcb_info_operation = curl_easy::create(tcb_info_url, nullptr); operation_result = get_collateral( @@ -1828,7 +2069,18 @@ extern "C" quote3_error_t sgx_ql_get_quote_verification_collateral( // Get QE Identity & Issuer Chain std::string issuer_chain_header; - std::string qe_identity_url = build_enclave_id_url(false, issuer_chain_header); + std::string qe_identity_url; + try + { + qe_identity_url = build_enclave_id_url( + false, issuer_chain_header, custom_param, custom_param_length); + } + catch (exception& e) + { + log(SGX_QL_LOG_ERROR, + "QE_IDENTITY_URL can't be formed. Validate the parameters passed."); + return SGX_QL_ERROR_INVALID_PARAMETER; + } const auto qe_identity_operation = curl_easy::create(qe_identity_url, nullptr); @@ -1925,6 +2177,33 @@ extern "C" quote3_error_t sgx_ql_get_quote_verification_collateral( } } +extern "C" quote3_error_t sgx_ql_get_quote_verification_collateral( + const uint8_t* fmspc, + const uint16_t fmspc_size, + const char* pck_ca, + sgx_ql_qve_collateral_t** pp_quote_collateral) +{ + return sgx_ql_fetch_quote_verification_collateral( + fmspc, fmspc_size, pck_ca, pp_quote_collateral); +} + +extern "C" quote3_error_t sgx_ql_get_quote_verification_collateral_with_params( + const uint8_t* fmspc, + const uint16_t fmspc_size, + const char* pck_ca, + const void* custom_param, + const uint16_t custom_param_length, + sgx_ql_qve_collateral_t** pp_quote_collateral) +{ + return sgx_ql_fetch_quote_verification_collateral( + fmspc, + fmspc_size, + pck_ca, + pp_quote_collateral, + custom_param, + custom_param_length); +} + extern "C" quote3_error_t sgx_ql_get_qve_identity( char** pp_qve_identity, uint32_t* p_qve_identity_size, @@ -1967,10 +2246,19 @@ extern "C" quote3_error_t sgx_ql_get_qve_identity( std::vector qve_identity; std::string expected_issuer; std::string issuer_chain; - std::string qve_url = build_enclave_id_url(true, expected_issuer); - if (qve_url.empty()) + std::string qve_url; + try + { + qve_url = build_enclave_id_url(true, expected_issuer); + if (qve_url.empty()) + { + log(SGX_QL_LOG_ERROR, "V1 QVE is not supported"); + return SGX_QL_ERROR_INVALID_PARAMETER; + } + } + catch (exception& e) { - log(SGX_QL_LOG_ERROR, "V1 QVE is not supported"); + log(SGX_QL_LOG_ERROR, "QVE_URL can't be formed. Validate the parameters passed."); return SGX_QL_ERROR_INVALID_PARAMETER; } diff --git a/src/dcap_provider.h b/src/dcap_provider.h index fd1bbdcf..6848dbf7 100644 --- a/src/dcap_provider.h +++ b/src/dcap_provider.h @@ -7,6 +7,7 @@ #include #include +#include using namespace std; @@ -115,4 +116,6 @@ typedef void ( typedef sgx_plat_error_t (*sgx_ql_set_logging_function_t)( sgx_ql_logging_function_t logger); +const std::string customParam = "customParameter"; + #endif // #ifndef PLATFORM_QUOTE_PROVIDER_H