From 1f770855719fff0921970a8bf000a3a9c6de2508 Mon Sep 17 00:00:00 2001 From: conglin Date: Mon, 9 Jun 2025 23:25:13 +0000 Subject: [PATCH] Re-purpose internal factory upload tool for AOSP This tool was supposed to be used for internal debugging purpose on Android devices. It already supports RKP uploading CSR format. Extend this tool to support Widevine uploading format: JSON csr and make this format as default output for AOSP (non-GMS) partners. A later change will move it to its own aosp/ directory. Test: run "wv_factory_extraction_tool json_csr" on Pixel 9 Bug: 414642286 Change-Id: I9cf4e9696d32201cc1ad70b6bee7932f7126a4ba --- .../tools/factory_upload_tool/Android.bp | 3 + .../tools/factory_upload_tool/cli.cpp | 76 +++++++++++++-- .../include/WidevineProvisioner.h | 5 + .../src/WidevineOemcryptoInterface.cpp | 34 ------- .../src/WidevineProvisioner.cpp | 93 ++++++++++++++++++- 5 files changed, 168 insertions(+), 43 deletions(-) diff --git a/libwvdrmengine/tools/factory_upload_tool/Android.bp b/libwvdrmengine/tools/factory_upload_tool/Android.bp index f5db00dd..8790850b 100644 --- a/libwvdrmengine/tools/factory_upload_tool/Android.bp +++ b/libwvdrmengine/tools/factory_upload_tool/Android.bp @@ -26,6 +26,9 @@ cc_binary { "liblog", "libutils", ], + static_libs: [ + "libwv_cdm_utils", + ], srcs: [ "cli.cpp", "src/log.cpp", diff --git a/libwvdrmengine/tools/factory_upload_tool/cli.cpp b/libwvdrmengine/tools/factory_upload_tool/cli.cpp index 3648d59c..166c276b 100644 --- a/libwvdrmengine/tools/factory_upload_tool/cli.cpp +++ b/libwvdrmengine/tools/factory_upload_tool/cli.cpp @@ -160,29 +160,82 @@ std::unique_ptr getCsrV3( return composeCertificateRequestV3(csr); } +void printHelp(const char* tool_name) { + fprintf(stdout, "Widevine Factory Extraction Tool for AOSP\n\n"); + fprintf(stdout, "Usage: %s [command]\n\n", tool_name); + fprintf(stdout, + "This tool extracts BCC and device information and generates CSR " + "required for Widevine Provisioning 4.0 factory uploading.\n\n"); + fprintf(stdout, "Commands:\n"); + fprintf(stdout, " json_csr (default)\n"); + fprintf(stdout, + " Generates and prints a JSON-formatted " + "Certificate Signing\n"); + fprintf(stdout, + " Request (CSR) to be uploaded to the " + "Widevine provisioning server.\n\n"); + fprintf(stdout, " bcc\n"); + fprintf(stdout, + " Outputs the raw binary Bootloader " + "Certificate Chain (BCC).\n\n"); + fprintf(stdout, " bcc_str\n"); + fprintf(stdout, + " Outputs a human-readable, parsed string " + "representation of the BCC.\n\n"); + fprintf(stdout, " device_info\n"); + fprintf( + stdout, + " Outputs the raw binary device information blob.\n\n"); + fprintf(stdout, " csr\n"); + fprintf(stdout, + " Generates and outputs a legacy format Certificate " + "Signing Request (CSR) to be uploaded to RKP backend.\n\n"); + fprintf(stdout, " csr_v3\n"); + fprintf(stdout, + " Generates and outputs a V3 format Certificate " + "Signing Request (CSR) to be uploaded to RKP backend.\n\n"); + fprintf(stdout, " help\n"); + fprintf(stdout, " Displays this help message.\n"); +} + int main(int argc, char** argv) { - if (argc < 2) { - fprintf(stderr, "%s \n", argv[0]); - return 0; - } widevine::WidevineProvisioner provisioner; - if (!std::strcmp(argv[1], "bcc")) { + + // Default to Widevine uploading request format "json_csr" if no arguments are + // provided. + const char* command = (argc > 1) ? argv[1] : "json_csr"; + if (!std::strcmp(command, "json_csr")) { + std::string request; + if (provisioner.GenerateWidevineUploadRequest(request)) { + std::copy(request.begin(), request.end(), + std::ostream_iterator(std::cout)); + } else { + fprintf(stderr, + "Failed to generate Widevine uploading request json CSR.\n"); + return 1; + } + } else if (!std::strcmp(command, "help")) { + printHelp(argv[0]); + } else if (!std::strcmp(command, "bcc")) { auto bcc = provisioner.GetBcc(); fwrite(bcc.data(), 1, bcc.size(), stdout); fflush(stdout); - } else if (!std::strcmp(argv[1], "bcc_str")) { + } else if (!std::strcmp(command, "bcc_str")) { auto bcc = provisioner.GetBcc(); widevine::BccParser bcc_parser; std::string parsed_bcc = bcc_parser.Parse(bcc); std::copy(parsed_bcc.begin(), parsed_bcc.end(), std::ostream_iterator(std::cout)); - } else if (!std::strcmp(argv[1], "device_info")) { + } else if (!std::strcmp(command, "device_info")) { std::vector deviceInfo; if (provisioner.GetDeviceInfo(deviceInfo)) { fwrite(deviceInfo.data(), 1, deviceInfo.size(), stdout); fflush(stdout); + } else { + fprintf(stderr, "Failed to get device info.\n"); + return 1; } - } else if (!std::strcmp(argv[1], "csr")) { + } else if (!std::strcmp(command, "csr")) { auto csr = getCsr(provisioner); auto bytes = csr.encode(); std::copy(bytes.begin(), bytes.end(), @@ -193,7 +246,14 @@ int main(int argc, char** argv) { auto bytes = csr->encode(); std::copy(bytes.begin(), bytes.end(), std::ostream_iterator(std::cout)); + } else { + fprintf(stderr, "Failed to generate CSR V3.\n"); + return 1; } + } else { + fprintf(stderr, "Error: Unknown command '%s'\n\n", command); + printHelp(argv[0]); + return 1; } return 0; } diff --git a/libwvdrmengine/tools/factory_upload_tool/include/WidevineProvisioner.h b/libwvdrmengine/tools/factory_upload_tool/include/WidevineProvisioner.h index d3e81d9d..7f1db2ad 100644 --- a/libwvdrmengine/tools/factory_upload_tool/include/WidevineProvisioner.h +++ b/libwvdrmengine/tools/factory_upload_tool/include/WidevineProvisioner.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,7 @@ class WidevineProvisioner { bool GenerateCertificateRequestV2(const std::vector& challenge, std::vector* csr); bool GetDeviceInfo(std::vector& device_info); + bool GenerateWidevineUploadRequest(std::string& request); private: bool GenerateProtectedData( @@ -48,6 +50,9 @@ class WidevineProvisioner { bool GetDeviceInfoCommon(cppbor::Map& device_info_map); bool TryAddVerifiedDeviceInfo(cppbor::Map& device_info_map); bool GetDeviceInfoV2(cppbor::Map& device_info_map); + void PopulateDeviceInfoFromCborMap( + const cppbor::Map& device_info_map, + std::map& request_map); std::unique_ptr crypto_interface_; }; diff --git a/libwvdrmengine/tools/factory_upload_tool/src/WidevineOemcryptoInterface.cpp b/libwvdrmengine/tools/factory_upload_tool/src/WidevineOemcryptoInterface.cpp index ccb105a3..8fc2b87c 100644 --- a/libwvdrmengine/tools/factory_upload_tool/src/WidevineOemcryptoInterface.cpp +++ b/libwvdrmengine/tools/factory_upload_tool/src/WidevineOemcryptoInterface.cpp @@ -7,8 +7,6 @@ #include #include "OEMCryptoCENC.h" -#include "clock.h" -#include "file_store.h" #include "log.h" // These macros lookup the obfuscated name used for OEMCrypto. @@ -24,38 +22,6 @@ #define LOAD_SYM_IF_EXIST(name) \ name = reinterpret_cast(LOOKUP(handle_, OEMCrypto_##name)); -// These are implementations required by OEMCrypto Reference Implementation -// and/or the Testbed, but not needed in this package. -namespace wvutil { -int64_t Clock::GetCurrentTime() { return 0; } - -class FileImpl final : public File { - public: - FileImpl() {} - ssize_t Read(char*, size_t) override { return 0; } - ssize_t Write(const char*, size_t) override { return 0; } -}; - -class FileSystem::Impl { - public: - Impl() {} -}; - -FileSystem::FileSystem() {} -FileSystem::FileSystem(const std::string&, void*) {} -FileSystem::~FileSystem() {} -std::unique_ptr FileSystem::Open(const std::string&, int) { - return std::unique_ptr(new FileImpl()); -} -bool FileSystem::Exists(const std::string&) { return false; } -bool FileSystem::Remove(const std::string&) { return false; } -ssize_t FileSystem::FileSize(const std::string&) { return false; } -bool FileSystem::List(const std::string&, std::vector*) { - return false; -} - -} // namespace wvutil - namespace widevine { OEMCryptoInterface::~OEMCryptoInterface() { diff --git a/libwvdrmengine/tools/factory_upload_tool/src/WidevineProvisioner.cpp b/libwvdrmengine/tools/factory_upload_tool/src/WidevineProvisioner.cpp index 6f9d1f00..b0181990 100644 --- a/libwvdrmengine/tools/factory_upload_tool/src/WidevineProvisioner.cpp +++ b/libwvdrmengine/tools/factory_upload_tool/src/WidevineProvisioner.cpp @@ -22,6 +22,7 @@ #include "WidevineOemcryptoInterface.h" #include "log.h" #include "properties.h" +#include "string_conversions.h" namespace widevine { @@ -31,6 +32,51 @@ const std::vector> kAuthorizedEekRoots = { 0x62, 0xDC, 0x3E, 0x61, 0xAB, 0x57, 0x48, 0x7D, 0x75, 0x37, 0x29, 0xAD, 0x76, 0x80, 0x32, 0xD2, 0xB3, 0xCB, 0x63, 0x58, 0xD9}, }; + +std::string EscapeJson(const std::string& input) { + std::string result; + for (std::string::const_iterator c = input.begin(); c != input.end(); ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + default: + result += *c; + break; + } + } + return result; +} + +std::string StringMapToJson( + const std::map& string_map) { + std::string json = "{"; + for (const auto& value_pair : string_map) { + json.append("\"" + value_pair.first + "\": " + "\"" + value_pair.second + + "\","); + } + json.resize(json.size() - 1); // Remove the last comma. + json.append("}"); + return json; +} } // namespace WidevineProvisioner::WidevineProvisioner() { @@ -107,7 +153,8 @@ bool WidevineProvisioner::TryAddVerifiedDeviceInfo( } bool WidevineProvisioner::GetDeviceInfoCommon(cppbor::Map& device_info_map) { - if (!TryAddVerifiedDeviceInfo(device_info_map)) return false; + // Best effort to populate device info from TEE first + TryAddVerifiedDeviceInfo(device_info_map); // Add device information from OS properties if the verified device info is // not present if (device_info_map.get("brand") == nullptr || @@ -390,6 +437,50 @@ bool WidevineProvisioner::GenerateCertificateRequestV2( return true; } +// Caller ensures the validity of `device_info_map` as a `cppbor::Map&`. +void WidevineProvisioner::PopulateDeviceInfoFromCborMap( + const cppbor::Map& device_info_map, + std::map& request_map) { + if (device_info_map.get("manufacturer")) { + request_map["company"] = + device_info_map.get("manufacturer")->asTstr()->value(); + } + if (device_info_map.get("device")) { + request_map["name"] = device_info_map.get("device")->asTstr()->value(); + } + if (device_info_map.get("architecture")) { + request_map["architecture"] = + device_info_map.get("architecture")->asTstr()->value(); + } + if (device_info_map.get("model")) { + request_map["model"] = device_info_map.get("model")->asTstr()->value(); + } + if (device_info_map.get("product")) { + request_map["product"] = device_info_map.get("product")->asTstr()->value(); + } + if (device_info_map.get("fingerprint")) { + request_map["build_info"] = + device_info_map.get("fingerprint")->asTstr()->value(); + } + if (device_info_map.get("oemcrypto_build_info")) { + request_map["oemcrypto_build_info"] = EscapeJson( + device_info_map.get("oemcrypto_build_info")->asTstr()->value()); + } +} + +bool WidevineProvisioner::GenerateWidevineUploadRequest(std::string& request) { + std::map request_map; + auto bcc = GetBcc(); + request_map["bcc"] = wvutil::Base64Encode(bcc); + + auto device_info_map = cppbor::Map(); + if (!GetDeviceInfoCommon(device_info_map)) return false; + PopulateDeviceInfoFromCborMap(device_info_map, request_map); + + request = StringMapToJson(request_map); + return true; +} + void WidevineProvisioner::InitializeCryptoInterface() { std::string oemcrypto_path; if (!wvcdm::Properties::GetOEMCryptoPath(&oemcrypto_path)) {