From d92d3a884df60544bd900bed1981d1c7de4fea2e Mon Sep 17 00:00:00 2001 From: Cong Lin Date: Mon, 9 Sep 2024 11:43:00 -0700 Subject: [PATCH 1/2] Add "bootCertificateChainSignature" to Drm plugin getPropertyByteArray() This allows Widevine RKP HAL to query BCC signature via DRM interface during BCC extraction for remote provisioning phase 3. The query returns the "additional_signature" field from OEMCrypto_GetBootCertificateChain(). Test: Manual BCC extraction on Pixel 9 Bug: 355160637 Change-Id: I1a310a80c0cfef82ee3697f06c1293d5c1c3896a --- .../cdm/core/include/wv_cdm_constants.h | 2 ++ libwvdrmengine/cdm/core/src/cdm_engine.cpp | 20 +++++++++++++++++++ .../cdm/test/request_license_test.cpp | 6 ++++++ libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp | 12 +++++++++++ libwvdrmengine/src/WVDrmFactory.cpp | 2 ++ 5 files changed, 42 insertions(+) diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index c35236aa..8c73a4aa 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -124,6 +124,8 @@ static const std::string QUERY_KEY_PRODUCTION_READY = "ProductionReady"; // Internal query key. Should not be exposed to Android apps. static const std::string QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN = "DebugBootCertificateChain"; +static const std::string QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN_SIGNATURE = + "DebugBootCertificateChainSignature"; static const std::string QUERY_KEY_DEVICE_INFORMATION = "DeviceInformation"; static const std::string QUERY_VALUE_TRUE = "True"; diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index d0e26d61..424e5755 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -903,6 +903,26 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level, LOGE("Failed to extract BCC: status = %d", status.ToInt()); return status; } + if (query_token == QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN_SIGNATURE) { + std::string bcc_unused; + std::string signature; + const CdmResponseType status = crypto_session->GetBootCertificateChain( + security_level, &bcc_unused, &signature); + if (status == NO_ERROR) { + LOGV("BCC signature length: %zu", signature.size()); + *query_response = std::move(signature); + return CdmResponseType(NO_ERROR); + } + if (status == NOT_IMPLEMENTED_ERROR || + status == PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR) { + LOGD("BCC signature not available: %s", status.ToString().c_str()); + *query_response = QUERY_VALUE_NONE; + return CdmResponseType(NO_ERROR); + } + LOGE("Failed to extract BCC signature: status = %s", + status.ToString().c_str()); + return status; + } if (query_token == QUERY_KEY_DEVICE_INFORMATION) { std::string device_info; const CdmResponseType status = diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 6de975d1..b8048085 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -5388,6 +5388,12 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatus) { // actual value. EXPECT_FALSE(value.empty()) << "BCC is empty"; EXPECT_NE(value, wvcdm::QUERY_VALUE_NONE) << "BCC is none"; + // BCC signature is optional. Do not validate the actual value. + EXPECT_EQ( + wvcdm::NO_ERROR, + decryptor_->QueryStatus( + kLevelDefault, + wvcdm::QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN_SIGNATURE, &value)); } else { EXPECT_EQ(value, wvcdm::QUERY_VALUE_NONE); } diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index 32d705e7..5ad1bc7b 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -1296,6 +1296,18 @@ static WvStatus getDeviceSignedCsrPayload( } else { value = StrToVector(boot_certificate_chain); } + } else if (name == "bootCertificateChainSignature" && isCsrAccessAllowed()) { + std::string boot_certificate_chain_signature; + const CdmResponseType res = mCDM->QueryStatus(wvcdm::kLevelDefault, + wvcdm::QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN_SIGNATURE, + &boot_certificate_chain_signature); + if (res != wvcdm::NO_ERROR) { + ALOGE("Error querying CDM boot certificate chain signature: %d", + static_cast(res)); + status = mapCdmResponseType(res); + } else { + value = StrToVector(boot_certificate_chain_signature); + } } else if (name == "verifiedDeviceInfo" && isCsrAccessAllowed()) { std::string verified_device_info; CdmResponseType res = mCDM->QueryStatus(wvcdm::kLevelDefault, diff --git a/libwvdrmengine/src/WVDrmFactory.cpp b/libwvdrmengine/src/WVDrmFactory.cpp index b33c8442..17caffc2 100644 --- a/libwvdrmengine/src/WVDrmFactory.cpp +++ b/libwvdrmengine/src/WVDrmFactory.cpp @@ -346,6 +346,8 @@ void WVDrmFactory::printCdmProperties(int fd) { // Debug properties. Not exposed to app. {"boot_certificate_chain", wvcdm::QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN}, + {"boot_certificate_chain_signature", + wvcdm::QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN_SIGNATURE}, {"device_information", wvcdm::QUERY_KEY_DEVICE_INFORMATION}, }; From 380148e3e68904555abc7b95b75885756a4112d8 Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Tue, 1 Oct 2024 21:14:19 -0700 Subject: [PATCH 2/2] Fixed HLS parsing of bad content IDs. [ Merge of http://go/wvgerrit/207457 ] When parsing Widevine's HLS key data, the key details are contained in a data URI in the HLS X-KEY URI field. The data of the URI is a base64 encoded JSON object, containing the information required to generate the license request. The "content_id" field of the JSON object is expected to be a base64 encoded; however, the HLS parser did not verify that the decoding was successful. In the event that was not successful, the decoder would return an empty string, which the parser would attempt to access the first element by reference which may be a null reference. In C++, creating a reference from a null point (without actually accessing the value) is undefined; however most C++ implemenations will not cause a segment fault; but it is not guarenteed by the standard. This change checks if the decoding was successful before attempting to store the decoded "content_id" value. A unit test is added to ensure that a parser fails gracefully. Bug: 356210640 Test: HlsParseTest.BadHlsData_InvalidContentId Change-Id: Ie2ad42d69953258659178dd1464d830b2723c6c7 --- .../cdm/core/src/initialization_data.cpp | 14 +++++------ .../test/initialization_data_unittest.cpp | 23 +++++++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/libwvdrmengine/cdm/core/src/initialization_data.cpp b/libwvdrmengine/cdm/core/src/initialization_data.cpp index cf87d24a..2c0312ca 100644 --- a/libwvdrmengine/cdm/core/src/initialization_data.cpp +++ b/libwvdrmengine/cdm/core/src/initialization_data.cpp @@ -454,8 +454,7 @@ bool InitializationData::ConstructWidevineInitData( LOGV("Base64 decode of json data failed"); return false; } - std::string json_string((const char*)(&json_init_data[0]), - json_init_data.size()); + const std::string json_string(json_init_data.begin(), json_init_data.end()); // Parse the Json string using jsmn jsmn_parser parser; @@ -513,12 +512,13 @@ bool InitializationData::ConstructWidevineInitData( break; case kContentIdState: if (tokens[i].type == JSMN_STRING) { - std::string base64_content_id(json_string, tokens[i].start, - tokens[i].end - tokens[i].start); - std::vector content_id_data = + const std::string base64_content_id = json_string.substr( + tokens[i].start, tokens[i].end - tokens[i].start); + const std::vector content_id_data = wvutil::Base64Decode(base64_content_id); - content_id.assign(reinterpret_cast(&content_id_data[0]), - content_id_data.size()); + if (!content_id_data.empty()) { + content_id.assign(content_id_data.begin(), content_id_data.end()); + } } state = kParseState; break; diff --git a/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp b/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp index 57d2265f..7d072853 100644 --- a/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp @@ -869,4 +869,27 @@ INSTANTIATE_TEST_SUITE_P( HlsAttributeVariant(kHlsAttributeListInvalidIv, HLS_IV_ATTRIBUTE, kHlsTestHexValueWithOddBytes, false))); +TEST_F(HlsParseTest, BadHlsData_InvalidContentId) { + std::ostringstream hls_uri_json_stream; + hls_uri_json_stream << "{"; + hls_uri_json_stream << "\"provider\": \"HlsParseTest.BadHlsData\", "; + // Intentionally bad Base64 content ID. + hls_uri_json_stream << "\"content_id\": \"$$$$\", "; + hls_uri_json_stream << "\"key_ids\": [\"00000000000000000000000000000000\"]"; + hls_uri_json_stream << "}"; + const std::string hls_uri_json = hls_uri_json_stream.str(); + + std::ostringstream hls_stream; + hls_stream << "#EXT-X-KEY:"; + hls_stream << "METHOD=AES-128,"; + hls_stream << "URI=\"data:text/plain;base64," + << wvutil::Base64Encode(hls_uri_json) << "\","; + hls_stream << "IV=0x00000000000000000000000000000000,"; + hls_stream << "KEYFORMAT=\"com.widevine\","; + hls_stream << "KEYFORMATVERSIONS=\"1\""; + const std::string hls_data = hls_stream.str(); + // std::cout << "HLS Data:" << std::endl << hls_data << std::endl; + InitializationData init_data(HLS_INIT_DATA_FORMAT, hls_data); + EXPECT_TRUE(init_data.is_hls()); +} } // namespace wvcdm