diff --git a/libwvdrmengine/Android.bp b/libwvdrmengine/Android.bp index ee4714e8..8d3931f0 100644 --- a/libwvdrmengine/Android.bp +++ b/libwvdrmengine/Android.bp @@ -216,10 +216,6 @@ cc_library_static { "libutils_headers", ], - generated_headers: [ - "wv_android_build_id", - ], - shared_libs: [ "libbinder_ndk", "libcrypto", @@ -356,23 +352,3 @@ cc_fuzz { ], }, } - -python_binary_host { - name: "wv-android-build-id-gen", - srcs: [ - "wv-android-build-id-gen.py", - ], - version: { - py3: { - embedded_launcher: true, - }, - }, -} - -genrule { - name: "wv_android_build_id", - srcs: ["apex/apex_manifest.json"], - tools: ["wv-android-build-id-gen"], - cmd: "$(location wv-android-build-id-gen) < $(in) > $(out)", - out: ["wv_android_build_id.h"], -} diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 4e0f3222..992040e0 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -332,6 +332,12 @@ class CdmEngine { return CryptoSession::SetDebugIgnoreKeyboxCount(count); } + // This tells the OEMCrypto adapter to allow the device to continue with a + // test keybox. Otherwise, the keybox is reported as invalid. + static CdmResponseType SetAllowTestKeybox(bool allow) { + return CryptoSession::SetAllowTestKeybox(allow); + } + static CdmResponseType ParseDecryptHashString(const std::string& hash_string, CdmSessionId* id, uint32_t* frame_number, diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index 58e7d2ff..1878187d 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -338,6 +338,10 @@ class CryptoSession { // report that it needs provisioning instead. static CdmResponseType SetDebugIgnoreKeyboxCount(uint32_t count); + // This tells the OEMCrypto adapter to allow the device to continue with a + // test keybox. Otherwise, the keybox is reported as invalid. + static CdmResponseType SetAllowTestKeybox(bool allow); + // Returns a system-wide singleton instance of SystemFallbackPolicy // to be used for communicating OTA keybox provisioning state between // apps. Returns a null pointer if OTA provisioning is not supported, diff --git a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h index 79af2264..afcf32bd 100644 --- a/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h +++ b/libwvdrmengine/cdm/core/include/oemcrypto_adapter.h @@ -20,6 +20,10 @@ OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox( // report that it needs provisioning instead. OEMCryptoResult OEMCrypto_SetDebugIgnoreKeyboxCount(uint32_t count); +// This tells the OEMCrypto adapter to allow the device to continue with a +// test keybox. Otherwise, the keybox is reported as invalid. +OEMCryptoResult OEMCrypto_SetAllowTestKeybox(bool allow); + // This attempts to open a session at the desired security level. // If one level is not available, the other will be used instead. OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session, diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 15998d46..2eb11cac 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -2404,9 +2404,9 @@ CdmResponseType CdmEngine::SignRsa(const std::string& wrapped_key, { std::unique_lock lock(session_map_lock_); if (!session_map_.FindSession(session_id, &session)) { - LOGE("Session not found: session_id = %s", IdToString(session_id)); - CloseSession(session_id); - return CdmResponseType(SESSION_NOT_FOUND_24); + LOGE("Session not found: session_id = %s", IdToString(session_id)); + CloseSession(session_id); + return CdmResponseType(SESSION_NOT_FOUND_24); } } diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 6d7e1815..af6828ae 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -263,14 +263,8 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id, usage_entry_ = std::move(license_data.usage_entry); usage_entry_index_ = license_data.usage_entry_index; - // If ATSC mode is enabled, use ATSC DRM cert/private key, rather than any - // cert/private key embedded in the license. - CdmResponseType result = - atsc_mode_enabled_ - ? LoadPrivateKey() - : LoadPrivateOrLegacyKey(license_data.drm_certificate, - license_data.wrapped_private_key); - + CdmResponseType result = LoadPrivateOrLegacyKey( + license_data.drm_certificate, license_data.wrapped_private_key); if (result != NO_ERROR) return result; // Attempts to restore a released offline license are treated as a release diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 24660014..9221ca01 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -3375,6 +3375,11 @@ CdmResponseType CryptoSession::SetDebugIgnoreKeyboxCount(uint32_t count) { return MapOEMCryptoResult(status, UNKNOWN_ERROR, "SetDebugIgnoreKeyboxCount"); } +CdmResponseType CryptoSession::SetAllowTestKeybox(bool allow) { + OEMCryptoResult status = OEMCrypto_SetAllowTestKeybox(allow); + return MapOEMCryptoResult(status, UNKNOWN_ERROR, "SetAllowTestKeybox"); +} + okp::SystemFallbackPolicy* CryptoSession::GetOkpFallbackPolicy() { const auto getter = [&]() -> okp::SystemFallbackPolicy* { // If not set, then OTA keybox provisioning is not supported or diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index 08f52835..5a955bba 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -626,6 +626,17 @@ std::string GetIgnoreCountFile() { return path; } +std::string GetAllowTestKeyboxFile() { + std::string path; + if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL1, + &path)) { + LOGW("GetAllowTestKeyboxFile: Unable to get base path"); + path = "/data/"; + } + path += "debug_allow_test_keybox.txt"; + return path; +} + uint32_t GetDebugIgnoreKeyboxCount() { const std::string filename = GetIgnoreCountFile(); wvutil::FileSystem file_system; @@ -678,6 +689,49 @@ OEMCryptoResult SetDebugIgnoreKeyboxCount(uint32_t count) { return OEMCrypto_SUCCESS; } +bool GetAllowTestKeybox() { + const std::string filename = GetAllowTestKeyboxFile(); + wvutil::FileSystem file_system; + if (!file_system.Exists(filename)) { + return 0; + } + auto file = file_system.Open(filename, file_system.kReadOnly); + if (!file) { + LOGE("Error opening %s", filename.c_str()); + return 0; + } + ssize_t size = file_system.FileSize(filename); + std::string contents(size, ' '); + ssize_t size_read = file->Read(const_cast(contents.data()), size); + if (size != size_read) { + LOGE("Short allow_test_keybox = %zu", size_read); + return 0; + } + // skip whitespace or any extra garbage. + return (std::string::npos != contents.find("true")); +} + +OEMCryptoResult SetAllowTestKeybox(bool allow) { + const std::string filename = GetAllowTestKeyboxFile(); + wvutil::FileSystem file_system; + auto file = + file_system.Open(filename, file_system.kCreate | file_system.kTruncate); + if (!file) { + LOGE("Could not create file %s", filename.c_str()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const std::string contents = allow ? "true\n" : "false\n"; + const size_t size = contents.size(); + ssize_t size_written = file->Write(contents.data(), size); + if (static_cast(size) != size_written) { + LOGE("Wrote %zd bytes of %s, not %zd, to file %s", size_written, + contents.c_str(), size, filename.c_str()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + LOGD("Wrote %s to %s", contents.c_str(), filename.c_str()); + return OEMCrypto_SUCCESS; +} + typedef enum OEMCryptoSessionType { SESSION_TYPE_OEMCRYPTO = 0, SESSION_TYPE_ENTITLED_KEY = 1, @@ -1260,6 +1314,18 @@ class Adapter { return result; } + // Check the system ID of the keybox. This should only be called if the device + // uses provisioning 2.0. + bool UsingTestKeybox() { + uint8_t key_data[256]; + size_t key_data_len = sizeof(key_data); + OEMCryptoResult sts = OEMCrypto_GetKeyData(key_data, &key_data_len); + if (sts != OEMCrypto_SUCCESS) return true; + uint32_t* data = reinterpret_cast(key_data); + uint32_t system_id = htonl(data[1]); + return system_id == 7912; + } + // Check the L1 keybox or cert. If it is valid, return success. If not, try to // install one. If one is not available, but OTA provisioning is supported, // return OEMCrypto_ERROR_NEEDS_KEYBOX_PROVISIONING. If none of these work, @@ -1295,6 +1361,19 @@ class Adapter { // Check if the keybox or oem certificate is valid, if so, we are finished // with initialization. Record some metrics and return success. const OEMCryptoResult rot_valid = level1_.IsKeyboxOrOEMCertValid(); + // For production systems, we do wish to use a test keybox. We do not force + // a fallback to L3 at this point, because this can be overridden by test + // code that requires a test keybox. + if ((rot_valid == OEMCrypto_SUCCESS) && + (provisioning_method == OEMCrypto_Keybox) && UsingTestKeybox()) { + if (GetAllowTestKeybox()) { + LOGW("Allowing device with test keybox installed."); + } else { + LOGW("Device has test keybox installed."); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + } + if (rot_valid == OEMCrypto_SUCCESS) { // The keybox or certificate is valid -- that means initialization is done // and we only have save some metrics and return. @@ -1745,6 +1824,9 @@ OEMCryptoResult OEMCrypto_GetOEMPublicCertificate( OEMCryptoResult OEMCrypto_SetDebugIgnoreKeyboxCount(uint32_t count) { return SetDebugIgnoreKeyboxCount(count); } +OEMCryptoResult OEMCrypto_SetAllowTestKeybox(bool allow) { + return SetAllowTestKeybox(allow); +} OEMCrypto_WatermarkingSupport OEMCrypto_GetWatermarkingSupport( wvcdm::RequestedSecurityLevel level) { diff --git a/libwvdrmengine/cdm/core/test/http_socket.cpp b/libwvdrmengine/cdm/core/test/http_socket.cpp index 9760a65e..37604ff4 100644 --- a/libwvdrmengine/cdm/core/test/http_socket.cpp +++ b/libwvdrmengine/cdm/core/test/http_socket.cpp @@ -165,12 +165,25 @@ bool HttpSocket::ParseUrl(const std::string& url, std::string* scheme, return false; } + // Strip off the domain name and port. In the url it will be terminated by + // either a splash or a question mark: + // like this example.com?key=value + // or this example.com/path/to/resource if (!Tokenize(url, "/", offset, domain_name, &offset)) { - // The rest of the URL belongs to the domain name. - domain_name->assign(url, offset, std::string::npos); - // No explicit path after the domain name. - path->assign("/"); + if (Tokenize(url, "?", offset, domain_name, &offset)) { + // url had no '/', but it did have '?'. Use the default path but + // keep the extra parameters. i.e. turn '?extra' into '/?extra'. + path->assign("/"); + path->append(url, offset - 1, std::string::npos); + } else { + // url had no '/' or '?'. + // The rest of the URL belongs to the domain name. + domain_name->assign(url, offset, std::string::npos); + // Use the default path. + path->assign("/"); + } } else { + // url had a '/'. // The rest of the URL, including the preceding slash, belongs to the path. path->assign(url, offset - 1, std::string::npos); } @@ -192,7 +205,7 @@ bool HttpSocket::ParseUrl(const std::string& url, std::string* scheme, } HttpSocket::HttpSocket(const std::string& url) - : socket_fd_(-1), ssl_(nullptr), ssl_ctx_(nullptr) { + : url_(url), socket_fd_(-1), ssl_(nullptr), ssl_ctx_(nullptr) { valid_url_ = ParseUrl(url, &scheme_, &secure_connect_, &domain_name_, &port_, &resource_path_); create_time_ = @@ -280,10 +293,12 @@ bool HttpSocket::Connect(int timeout_in_ms) { if (ret == EAI_SYSTEM) { // EAI_SYSTEM implies an underlying system issue. Error is // specified by |errno|. - LOGE("getaddrinfo failed due to system error: errno = %d", GetError()); + LOGE("getaddrinfo %s (port %s) failed due to system error: errno = %d", + domain_name_.c_str(), port_.c_str(), GetError()); } else { // Error is specified by return value. - LOGE("getaddrinfo failed: ret = %d", ret); + LOGE("getaddrinfo %s (port %s) failed: ret = %d", domain_name_.c_str(), + port_.c_str(), ret); } return false; } @@ -292,7 +307,8 @@ bool HttpSocket::Connect(int timeout_in_ms) { socket_fd_ = socket(addr_info->ai_family, addr_info->ai_socktype, addr_info->ai_protocol); if (socket_fd_ < 0) { - LOGE("Cannot open socket: errno = %d", GetError()); + LOGE("Cannot open socket %s (port %s): errno = %d", domain_name_.c_str(), + port_.c_str(), GetError()); return false; } @@ -300,19 +316,22 @@ bool HttpSocket::Connect(int timeout_in_ms) { #ifdef _WIN32 u_long mode = 1; // Non-blocking mode. if (ioctlsocket(socket_fd_, FIONBIO, &mode) != 0) { - LOGE("ioctlsocket error, wsa error = %d", WSAGetLastError()); + LOGE("ioctlsocket error %s (port %s), wsa error = %d", domain_name_.c_str(), + port_.c_str(), WSAGetLastError()); CloseSocket(); return false; } #else const int original_flags = fcntl(socket_fd_, F_GETFL, 0); if (original_flags == -1) { - LOGE("fcntl error, errno = %d", errno); + LOGE("fcntl error %s (port %s), errno = %d", domain_name_.c_str(), + port_.c_str(), errno); CloseSocket(); return false; } if (fcntl(socket_fd_, F_SETFL, original_flags | O_NONBLOCK) == -1) { - LOGE("fcntl error, errno = %d", errno); + LOGE("fcntl error %s (port %s), errno = %d", domain_name_.c_str(), + port_.c_str(), errno); CloseSocket(); return false; } diff --git a/libwvdrmengine/cdm/core/test/http_socket.h b/libwvdrmengine/cdm/core/test/http_socket.h index ae5761d6..72c5f0f9 100644 --- a/libwvdrmengine/cdm/core/test/http_socket.h +++ b/libwvdrmengine/cdm/core/test/http_socket.h @@ -32,9 +32,7 @@ class HttpSocket { const std::string& domain_name() const { return domain_name_; } int port() const { return atoi(port_.c_str()); } const std::string& resource_path() const { return resource_path_; } - std::string url() const { - return scheme_ + "://" + domain_name_ + ":" + port_ + resource_path_; - } + const std::string& url() const { return url_; } int ReadAndLogErrors(char* data, int len, int timeout_in_ms); int WriteAndLogErrors(const char* data, int len, int timeout_in_ms); @@ -57,6 +55,7 @@ class HttpSocket { bool Wait(bool for_read, int timeout_in_ms); FRIEND_TEST(HttpSocketTest, ParseUrlTest); + std::string url_; std::string scheme_; bool secure_connect_; std::string domain_name_; diff --git a/libwvdrmengine/cdm/core/test/message_dumper.cpp b/libwvdrmengine/cdm/core/test/message_dumper.cpp new file mode 100644 index 00000000..65289fba --- /dev/null +++ b/libwvdrmengine/cdm/core/test/message_dumper.cpp @@ -0,0 +1,218 @@ +// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. + +#include "message_dumper.h" + +#include "license_request.h" +#include "odk.h" +#include "odk_message.h" +#include "odk_serialize.h" +#include "odk_structs.h" +#include "odk_structs_priv.h" +#include "oec_device_features.h" +#include "test_base.h" + +using video_widevine::License; +using video_widevine::LicenseRequest; +using video_widevine::SignedMessage; +using video_widevine::SignedProvisioningMessage; + +namespace wvcdm { +namespace { +void DumpHeader(std::ofstream* out, const std::string& type) { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + const std::string suite = test_info->test_case_name(); + const std::string name = test_info->name(); + std::string new_test_name = suite + "_" + name; + // Replace the slashes with underscores so we can use it as a name again. + std::replace(new_test_name.begin(), new_test_name.end(), '/', '_'); + *out << "\nTEST_F(ODKGolden" << type << "V" << ODK_MAJOR_VERSION << ", " + << new_test_name << ") {\n"; +} + +void DumpHex(std::ofstream* out, const std::string& name, + const std::string& value) { + *out << "const uint8_t " << name << "_raw[] = {\n"; + *out << " "; + for (unsigned int i = 0; i < value.length(); i++) { + if ((i > 0) && (i % 10 == 0)) *out << "\n "; + uint8_t c = value[i]; + *out << "0x" << std::hex << std::setw(2) << std::setfill('0') << int(c) + << ", "; + } + *out << "\n};\n"; + *out << name << "_ = std::string (\n" + << " reinterpret_cast(" << name << "_raw), \n" + << " sizeof(" << name << "_raw));\n"; + *out << std::dec; // Turn off hex when we're done. +} +} // namespace + +std::ofstream MessageDumper::license_file; +std::ofstream MessageDumper::renewal_file; +std::ofstream MessageDumper::provision_file; + +void MessageDumper::SetUp() { + LOGD("Creating golden data files for ODK golden data tests."); + license_file.open("license_data.cpp"); + if (!license_file) LOGE("Could not open dump file license_data.cpp"); + renewal_file.open("renewal_data.cpp"); + if (!renewal_file) LOGE("Could not open dump file renewal_data.cpp"); + provision_file.open("provision_data.cpp"); + if (!provision_file) LOGE("Could not open dump file provision_data.cpp"); +} + +void MessageDumper::TearDown() { + LOGD("Closing golden data files."); + license_file.close(); + renewal_file.close(); + provision_file.close(); +} + +void MessageDumper::DumpLicenseRequest(const CdmKeyRequest& request) { + SignedMessage signed_message; + DumpHeader(&license_file, "License"); + EXPECT_TRUE(signed_message.ParseFromString(request.message)); + EXPECT_TRUE(signed_message.has_oemcrypto_core_message()); + DumpHex(&license_file, "core_request", + signed_message.oemcrypto_core_message()); + // Since this is run within a test, we can also verify that the + // request is valid. + video_widevine::LicenseRequest license_request; + EXPECT_TRUE(license_request.ParseFromString(signed_message.msg())); +} + +void MessageDumper::DumpLicense(const std::string& response) { + SignedMessage signed_response; + EXPECT_TRUE(signed_response.ParseFromString(response)); + EXPECT_TRUE(signed_response.has_oemcrypto_core_message()); + DumpHex(&license_file, "core_response", + signed_response.oemcrypto_core_message()); + + video_widevine::License license; + EXPECT_TRUE(license.ParseFromString(signed_response.msg())); + DumpHex(&license_file, "serialized_license", signed_response.msg()); + + std::string message = + signed_response.oemcrypto_core_message() + signed_response.msg(); + ODK_Message odk_msg = ODK_Message_Create( + reinterpret_cast(const_cast(message.c_str())), + message.length()); + ODK_Message_SetSize(&odk_msg, + signed_response.oemcrypto_core_message().length()); + ODK_ParsedLicense odk_parsed_license = {}; + ODK_LicenseResponse odk_license_response = {}; + odk_license_response.parsed_license = &odk_parsed_license; + Unpack_ODK_LicenseResponse(&odk_msg, &odk_license_response); + EXPECT_EQ(ODK_Message_GetStatus(&odk_msg), MESSAGE_STATUS_OK); + // Valid hash is only needed for v16 messages. + std::string hash(ODK_SHA256_HASH_SIZE, ' '); + DumpHex(&license_file, "core_request_sha256", hash); + license_file << " nonce_required_ = " + << (odk_parsed_license.nonce_required ? "true" : "false") + << ";\n"; + license_file << " RunTest();\n"; + license_file << "}\n\n"; +} + +void MessageDumper::DumpRenewalRequest(const CdmKeyRequest& request) { + DumpHeader(&renewal_file, "Renewal"); + SignedMessage signed_message; + EXPECT_TRUE(signed_message.ParseFromString(request.message)); + EXPECT_TRUE(signed_message.has_oemcrypto_core_message()); + DumpHex(&renewal_file, "core_request", + signed_message.oemcrypto_core_message()); + + video_widevine::LicenseRequest renewal_request; + EXPECT_TRUE(renewal_request.ParseFromString(signed_message.msg())); +} + +void MessageDumper::DumpRenewal(const std::string& response) { + SignedMessage signed_response; + EXPECT_TRUE(signed_response.ParseFromString(response)) + << "Response = " << wvutil::b2a_hex(response); + EXPECT_TRUE(signed_response.has_oemcrypto_core_message()); + DumpHex(&renewal_file, "core_response", + signed_response.oemcrypto_core_message()); + + video_widevine::License renewal; + EXPECT_TRUE(renewal.ParseFromString(signed_response.msg())); + DumpHex(&renewal_file, "renewal", signed_response.msg()); + + std::string message = + signed_response.oemcrypto_core_message() + signed_response.msg(); + ODK_Message odk_msg = ODK_Message_Create( + reinterpret_cast(const_cast(message.c_str())), + message.length()); + ODK_Message_SetSize(&odk_msg, + signed_response.oemcrypto_core_message().length()); + ODK_RenewalResponse odk_renewal_response = {}; + Unpack_ODK_RenewalResponse(&odk_msg, &odk_renewal_response); + EXPECT_EQ(ODK_Message_GetStatus(&odk_msg), MESSAGE_STATUS_OK); + renewal_file << " renewal_duration_seconds_ = " + << odk_renewal_response.renewal_duration_seconds << ";\n"; + renewal_file << " RunTest();\n"; + renewal_file << "}\n\n"; +} + +void MessageDumper::DumpProvisioningRequest( + const CdmProvisioningRequest& request) { + if (wvoec::global_features.derive_key_method == + wvoec::DeviceFeatures::TEST_PROVISION_40) { + LOGD("Provisioning 4.0 does not have a v17 or v18 core message."); + } else { + DumpHeader(&provision_file, "Provision"); + SignedProvisioningMessage signed_message; + EXPECT_TRUE(signed_message.ParseFromString(request)) + << "Request = " << wvutil::b2a_hex(request); + EXPECT_TRUE(signed_message.has_oemcrypto_core_message()); + DumpHex(&provision_file, "core_request", + signed_message.oemcrypto_core_message()); + } +} + +void MessageDumper::DumpProvisioning(const CdmProvisioningResponse& response) { + if (wvoec::global_features.derive_key_method == + wvoec::DeviceFeatures::TEST_PROVISION_40) { + LOGD("Provisioning 4.0 does not have a v17 core message."); + } else { + SignedProvisioningMessage signed_response; + if (!signed_response.ParseFromString(response)) { + // A binary provisioning response is buried within a json structure. + std::string extracted_message; + EXPECT_TRUE(CertificateProvisioning::ExtractAndDecodeSignedMessage( + response, &extracted_message)); + EXPECT_TRUE(signed_response.ParseFromString(extracted_message)); + } + EXPECT_TRUE(signed_response.has_oemcrypto_core_message()); + DumpHex(&provision_file, "core_response", + signed_response.oemcrypto_core_message()); + DumpHex(&provision_file, "provisioning_response", + signed_response.message()); + // The choice of ECC or RSA key is decided at the server, based on + // information in the DCSL. We can only reproduce this by looking + // at the current response. + std::string message = + signed_response.oemcrypto_core_message() + signed_response.message(); + ODK_Message odk_msg = ODK_Message_Create( + reinterpret_cast(const_cast(message.c_str())), + message.length()); + ODK_Message_SetSize(&odk_msg, + signed_response.oemcrypto_core_message().length()); + ODK_ParsedProvisioning odk_parsed_response; + ODK_ProvisioningResponse provisioning_response; + provisioning_response.parsed_provisioning = &odk_parsed_response; + Unpack_ODK_ProvisioningResponse(&odk_msg, &provisioning_response); + EXPECT_EQ(ODK_Message_GetStatus(&odk_msg), MESSAGE_STATUS_OK); + provision_file << " device_key_type_ = " + << ((odk_parsed_response.key_type == + OEMCrypto_RSA_Private_Key) + ? "OEMCrypto_RSA_Private_Key;\n" + : "OEMCrypto_ECC_Private_Key;\n"); + provision_file << " RunTest();\n"; + provision_file << "}\n\n"; + } +} +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/message_dumper.h b/libwvdrmengine/cdm/core/test/message_dumper.h new file mode 100644 index 00000000..fe5d0084 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/message_dumper.h @@ -0,0 +1,39 @@ +// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. + +#ifndef WVCDM_CORE_TEST_MESSAGE_DUMPER_H_ +#define WVCDM_CORE_TEST_MESSAGE_DUMPER_H_ + +#include + +#include +#include +#include + +#include "log.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +class MessageDumper : public ::testing::Environment { + // This dumps messages to a file so that the data can easily be turned + // into golden data tests for the ODK library. + public: + ~MessageDumper() override {} + void SetUp() override; + void TearDown() override; + + static void DumpLicenseRequest(const CdmKeyRequest& request); + static void DumpLicense(const std::string& response); + static void DumpRenewalRequest(const CdmKeyRequest& request); + static void DumpRenewal(const std::string& response); + static void DumpProvisioningRequest(const CdmProvisioningRequest& request); + static void DumpProvisioning(const CdmProvisioningResponse& response); + static std::ofstream license_file; + static std::ofstream renewal_file; + static std::ofstream provision_file; +}; +} // namespace wvcdm + +#endif // WVCDM_CORE_TEST_MESSAGE_DUMPER_H_ diff --git a/libwvdrmengine/cdm/core/test/test_base.cpp b/libwvdrmengine/cdm/core/test/test_base.cpp index 1c478342..d6fde02a 100644 --- a/libwvdrmengine/cdm/core/test/test_base.cpp +++ b/libwvdrmengine/cdm/core/test/test_base.cpp @@ -205,6 +205,7 @@ TestCryptoSession::TestCryptoSession(metrics::CryptoMetrics* crypto_metrics, void TestCryptoSession::MaybeInstallTestKeybox() { if (IsTestKeyboxNeeded()) { + CryptoSession::SetAllowTestKeybox(true); ReinitializeForTest(); WvCdmTestBase::InstallTestRootOfTrust(); } diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index 50cb0582..4431895e 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -237,6 +237,10 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { // device to request a keybox OTA reprovisioning. virtual CdmResponseType SetDebugIgnoreKeyboxCount(uint32_t count); + // Allow the device to continue with a test keybox. Otherwise, it will fall + // back to L3. + virtual CdmResponseType SetAllowTestKeybox(bool allow); + virtual CdmResponseType SetDecryptHash(const std::string& hash_data, CdmSessionId* session_id); virtual CdmResponseType GetDecryptHashError(const CdmSessionId& session_id, diff --git a/libwvdrmengine/cdm/src/properties_android.cpp b/libwvdrmengine/cdm/src/properties_android.cpp index 1c572438..39f61680 100644 --- a/libwvdrmengine/cdm/src/properties_android.cpp +++ b/libwvdrmengine/cdm/src/properties_android.cpp @@ -2,19 +2,17 @@ // source code may only be used and distributed under the Widevine License // Agreement. -#include "properties.h" -#include "properties_configuration.h" -#include "wv_android_build_id.h" - +#include #include + #include #include -#include - #include "log.h" -#include "wv_android_constants.h" +#include "properties.h" +#include "properties_configuration.h" #include "widevine_apex_info.h" +#include "wv_android_constants.h" namespace { @@ -137,17 +135,15 @@ bool Properties::GetWVCdmVersion(std::string* version) { return false; } - std::string build_number; + std::string apex_version = ""; #ifdef __ANDROID_APEX__ { auto info = widevine::apex::GetApexInfo(); - build_number = std::to_string(info->version); + apex_version = "@" + std::to_string(info->version); } -#else - build_number = WV_ANDROID_BUILD_ID; #endif - *version = kWVAndroidCdmVersion + "@" + build_number; + *version = kWVAndroidCdmVersion + apex_version; return true; } diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index b21a075c..11d549c2 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -675,6 +675,10 @@ CdmResponseType WvContentDecryptionModule::SetDebugIgnoreKeyboxCount( return CdmEngine::SetDebugIgnoreKeyboxCount(count); } +CdmResponseType WvContentDecryptionModule::SetAllowTestKeybox(bool allow) { + return CdmEngine::SetAllowTestKeybox(allow); +} + CdmResponseType WvContentDecryptionModule::SetDecryptHash( const std::string& hash_data, CdmSessionId* id) { if (id == nullptr) { diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index a7233f72..6672dbd9 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -154,12 +154,10 @@ HdcpLevel mapHdcpLevel(const std::string& level) { } } -bool isCsrAccessAllowed() { - const uid_t AID_ROOT = 0; - const uid_t AID_SYSTEM = 1000; - const uid_t AID_SHELL = 2000; +bool isRootOrShell() { + const uid_t AID_ROOT = 0, AID_SHELL = 2000; const uid_t uid = AIBinder_getCallingUid(); - return (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL); + return (uid == AID_ROOT || uid == AID_SHELL); } bool IsAtscKeySetId(const CdmKeySetId& keySetId) { @@ -1279,7 +1277,7 @@ static WvStatus getDeviceSignedCsrPayload( value = StrToVector(serialized_metrics); } } - } else if (name == "bootCertificateChain" && isCsrAccessAllowed()) { + } else if (name == "bootCertificateChain" && isRootOrShell()) { std::string boot_certificate_chain; CdmResponseType res = mCDM->QueryStatus( wvcdm::kLevelDefault, wvcdm::QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN, @@ -1291,7 +1289,7 @@ static WvStatus getDeviceSignedCsrPayload( } else { value = StrToVector(boot_certificate_chain); } - } else if (name == "verifiedDeviceInfo" && isCsrAccessAllowed()) { + } else if (name == "verifiedDeviceInfo" && isRootOrShell()) { std::string verified_device_info; CdmResponseType res = mCDM->QueryStatus(wvcdm::kLevelDefault, wvcdm::QUERY_KEY_DEVICE_INFORMATION, @@ -1303,7 +1301,7 @@ static WvStatus getDeviceSignedCsrPayload( } else { value = StrToVector(verified_device_info); } - } else if (name == "deviceSignedCsrPayload" && isCsrAccessAllowed()) { + } else if (name == "deviceSignedCsrPayload" && isRootOrShell()) { std::string signed_csr_payload; status = getDeviceSignedCsrPayload(mCDM, mCertificateSigningRequestChallenge, @@ -1505,10 +1503,10 @@ static WvStatus getDeviceSignedCsrPayload( } else { return toNdkScopedAStatus(Status::BAD_VALUE); } - } else if (name == "certificateSigningRequestChallenge" && isCsrAccessAllowed()) { + } else if (name == "certificateSigningRequestChallenge" && isRootOrShell()) { mCertificateSigningRequestChallenge = std::string(_value.begin(), _value.end()); - } else if (name == "deviceInfo" && isCsrAccessAllowed()) { + } else if (name == "deviceInfo" && isRootOrShell()) { mDeviceInfo = std::string(_value.begin(), _value.end()); } else { ALOGE("App set unknown byte array property %s", name.c_str()); diff --git a/libwvdrmengine/oemcrypto/CHANGELOG.md b/libwvdrmengine/oemcrypto/CHANGELOG.md index 72cdac19..28a7098c 100644 --- a/libwvdrmengine/oemcrypto/CHANGELOG.md +++ b/libwvdrmengine/oemcrypto/CHANGELOG.md @@ -2,6 +2,21 @@ [TOC] +## [Version 18.4][v18.4] + +Version 18.4 includes the reference implementation in OPK to support MediaCAS, +and an end-to-end demo of OEMCrypto CAS functionality. These updates were in +fact introduced in Version 17.2. See CAS-related updates in the change log of +Version 17.2 for details. Since Version 17.2 changes were merged to OEMCrypto +v18 after Version 18.3 got published, we bumped the version to 18.4 to reflect +the updates. If your device doesn't support MediaCAS, this update can be skipped. + +### Other changes + +- A fix in ODK that matches minor version with major version during session +initialization. +- Added a unit test for zero subsample size. + ## [Version 18.3][v18.3] Version 18.3 includes a major feature (Cast with Provisioning 4.0) and various @@ -9,7 +24,7 @@ minor changes. Version 18.2 was an internal version bump for the ODK that included changes used by the provisioning server. Since we keep the ODK and OPK version numbers in sync, the OPK version effectively skipped 18.2. -## Cast with Provisioning 4.0 +### Cast with Provisioning 4.0 The OPK now supports devices that wish to act as cast receivers while using Provisioning 4.0. Previously, only devices using Provisioning 2.0 or devices @@ -20,7 +35,7 @@ The OPK changes are included in this release. The CDM changes are part of Android U. The provisioning server changes are live on Widevine staging servers, and will be pushed to production by August 2023. -## OP-TEE port changes +### OP-TEE port changes - Added CSR and DeviceInformation implementations for Provisioning 4.0. - Bugfix: REE->TEE message shared memory was sized based on the incoming request @@ -29,7 +44,7 @@ did not fit in the nearest page boundary. Fixed by setting the shared memory size to the maximum allowed and passing in the request size as a separate TEE_Param. -## Other changes +### Other changes - Updated BoringSSL dependency to https://boringssl.googlesource.com/boringssl/+/e1b8685770d0e82e5a4a3c5d24ad1602e05f2e83 @@ -50,7 +65,7 @@ license response. The client-side value of `MAX_NUM_KEYS` must be changed in `odk/include/odk_target.h` to match the server's value. This is only intended for closed network systems. -## Known issues +### Known issues - CdmOtaKeyboxTest.BasicTest may fail due to server issues - The ODK renewal clock is not correctly checked for all circumstances. This @@ -243,6 +258,46 @@ OS. 4.0. - The OPK does not yet support MediaCAS functionality. +## [Version 17.2][v17.2] + +This release contains the first version of OPK to support MediaCAS, an +end-to-end demo of OEMCrypto CAS functionality, several bug fixes in OPK and a +few updates to the OEMCrypto unit tests and fuzz tests. + +MediaCAS support has been added to OPK. `OPK_Pack_LoadCasECMKeys_Request()`, +`OPK_Unpack_LoadCasECMKeys_Request()`, `OPK_Pack_LoadCasECMKeys_Response()`, +`OPK_Unpack_LoadCasECMKeys_Response()` are moved out of the auto-generated +serialization code and are added to the special cases, to allow implementor to +pack customized data. CAS-specific WTPI functions along with a reference +implementation have been added. + +A new `cas` directory is added to the `ports/linux` project. This contains +an end-to-end demo of OEMCrypto CAS functionality. The OEMCrypto CAS test client +communicates with the Linux `tee_simulator_cas` via `liboemcrypto.so` and +`libtuner.so`. `tee_simulator_cas` loads CAS keys and performs descrambling. + +All CAS specific code in OPK is guarded by the compiler flag `SUPPORT_CAS`. + +Several other updates and fixes to OPK in this release include: +- `strnlen()` is removed from OPK to avoid issue caused by the terminating '\0'. +- Explicit call to `builtin_add_overflow()` is removed and `oemcrypto_overflow` + wrappers are used instead. +- Added non-NULL checks in `WTPI_UnwrapValidateAndInstallKeybox()`, + `OEMCrypto_OPK_SerializationVersion()`, and `OPKI_GetFromObjectTable()`. +- Validated the wrapped key size to be non-zero. +- Set OP-TEE serialized request size to the maximum size expected. +- HMACs are compared in constant time. +- Fixed pointer arithmetic with size_t to avoid unexpected truncation of the + calculated address. +- No-op for zero-sized subsample instead of aborting OPK. + +This release also contains a few updates to the OEMCrypto unit tests and fuzz +tests: +- Reduced clock skew in flaky duration tests. +- Removed device ID check since it is not required for v17. +- Added a test for zero subsample size. +- Cleaned up fuzz helper classes and added more fuzz test coverage. + ## [OPK Version 17.1.1][v17.1+opk-v17.1.1] This release fixes a flaw in the OPK code that could allow content that requires @@ -420,4 +475,7 @@ Public release for OEMCrypto API and ODK library version 16.4. [v17+test-updates+opk+mk]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17+test-updates+opk+mk [v17.1]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17.1 [v17.1+opk-v17.1.1]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17.1+opk-v17.1.1 +[v17.2]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v17.2 [v18.1]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.1 +[v18.3]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.3 +[v18.4]: https://widevine-partner.googlesource.com/oemcrypto/+/refs/tags/v18.4 diff --git a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h index 99e0f618..18d5474e 100644 --- a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h +++ b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h @@ -3,7 +3,7 @@ // License Agreement. /** - * @mainpage OEMCrypto API v18.3 + * @mainpage OEMCrypto API v18.4 * * OEMCrypto is the low level library implemented by the OEM to provide key and * content protection, usually in a separate secure memory or process space. The @@ -136,6 +136,7 @@ * license to be reloaded. * * @defgroup entitled Entitlement License API + * Functions that are needed for entitled and entitlement licenses. * * [Entitlement licensing](../../index#entitlement) is a way to provide access * to content keys that may be stored elsewhere, such as in the content itself. @@ -1199,6 +1200,8 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE * @retval OEMCrypto_ERROR_SESSION_LOST_STATE * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_INVALID_KEY if the session's private key is not a + * DRM key. * * @buffer_size * OEMCrypto shall support message sizes as described in the section @@ -3355,6 +3358,8 @@ uint32_t OEMCrypto_MinorAPIVersion(void); * different TA builds. * - "build_timestamp" [string]: ISO 8601 formatted timestamp of the time the * TA was compiled, eg "YYYY-MM-DDTHH:MM:SS" + * - "is_factory_mode" [bool]: Whether this was built with FACTORY_MODE_ONLY + * defined * * While not required, another optional top level struct can be added to the * build information string to provide information about liboemcrypto.so: @@ -4973,7 +4978,9 @@ OEMCryptoResult OEMCrypto_GenerateCertificateKeyPair( OEMCrypto_PrivateKeyType* key_type); /** - * Get the serialized device information in CBOR map format. + * Get the serialized device information in CBOR map format. This is for devices + * that use Provisioning 4.0, with the device key uploading option in the + * factory. * * The device * information may contain, for example, device make and model, "fused" status, @@ -4982,7 +4989,9 @@ OEMCryptoResult OEMCrypto_GenerateCertificateKeyPair( * provisioning request is coming from the expected device in the fields, based * on the values previously uploaded and registered. * - * This method is used by provisioning 4 only. + * Devices that do not support Provisioning 4.0, or do not support + * Provisioning 4.0 Uploading Option should return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * * @param[out] device_info: pointer to the buffer that receives the serialized * device information in CBOR map format. @@ -5008,24 +5017,87 @@ OEMCryptoResult OEMCrypto_GetDeviceInformation(uint8_t* device_info, size_t* device_info_length); /** - * Get the serialized signed Certificate Signing Request (Csr) payload in - * COSE_Sign1 format. + * Get the serialized signed Certificate Signing Request (CSR) payload in + * COSE_Sign1 format. This is for devices that use Provisioning 4.0, with the + * device key uploading option in the factory. * - * The signed CSR payload contains challenge and device information. It is - * signed by the leaf cert of the boot certificate chain (BCC). It is only used - * in the factory, uploaded and validated during device registration. + * With the uploading option, the RKP factory extraction tool provided by Google + * makes a call to this function to collect the signed CSR payload for + * generating the CSR to be uploaded to the device database. The CSR payload is + * signed by the leaf cert of the Boot Certificate Chain. * - * This method is used by provisioning 4 only. + * The format of a CSR payload before COSE_Sign1 is a CBOR array described in + * Android IRemotelyProvisionedComponent.aidl (under "CsrPayload"): + * + * ~~~ + * CsrPayload = [ ; CBOR Array defining the payload for CSR. + * version: 3, ; The CsrPayload CDDL Schema version. + * CertificateType: "widevine" ; The type of certificate being requested. + * DeviceInfo, ; Defined in Android DeviceInfo.aidl + * KeysToSign: [] ; Empty list + * ] + * ~~~ + * + * The type of CertificateType is tstr and the value should always be + * "widevine". The type of KeysToSign is CBOR array and the value is not used, + * which should be left as an empty list. Note that the DeviceInfo above is a + * CBOR map structure defined in DeviceInfo.aidl, which can be constructed from + * the input |encoded_device_info|. DeviceInfo must be canonicalized according + * to the specification in RFC 7049. The required fields from DeviceInfo.aidl + * are: brand, manufacturer, product, model, device, vb_state, bootloader_state, + * vbmeta_digest, security_level. + * + * Once CsrPayload is prepared, together with |challenge| it is signed by the + * leaf cert of BCC, in the format of: + * + * ~~~ + * |signed_csr_payload| = SignedData<[ + * challenge: bstr .size (0..64), + * bstr .cbor CsrPayload, + * ]> + * ~~~ + * + * This function should output |signed_csr_payload| in the format of + * SignedData, which is a COSE_Sign1 CBOR and is defined in Android + * IRemotelyProvisionedComponent.aidl (under "SignedData"): + * + * ~~~ + * SignedData = [ + * protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 / + * AlgorithmES384 }, + * unprotected: {}, + * payload: bstr .cbor Data / nil, + * signature: bstr ; PureEd25519(priv_key, Sig_structure) / + * ; ECDSA(priv_key, Sig_structure) + * ] + * ~~~ + * + * Also see OEMCrypto_GenerateCertificateKeyPair() for more details of + * SignedData and Sig_structure. + * + * Data in the payload field of SignedData is a CBOR array: + * + * ~~~ + * Data = [ + * challenge: bstr .size (0..64), + * bstr .cbor CsrPayload, + * ] + * ~~~ + * + * Devices that do not support Provisioning 4.0, or do not support + * Provisioning 4.0 Uploading Option should return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * * @param[in] challenge: pointer to the buffer containing a byte string to be - * signed. + * signed. It is generated by the RKP factory extraction tool. * @param[in] challenge_length: size of the challenge buffer. * @param[in] encoded_device_info: pointer to the buffer containing the - * serialized device information in CBOR map format. + * serialized device information in CBOR map format. It should be returned as + * `device_info` in a call to the function `OEMCrypto_GetDeviceInformation()`. * @param[in] encoded_device_info_length: size of the encoded_device_info * buffer. * @param[out] signed_csr_payload: pointer to the buffer that receives the - * serialized CSR payload in COSE_Sign1 format. + * serialized signed CSR payload in COSE_Sign1 format. * @param[in,out] signed_csr_payload_length: on input, size of the caller's * signed_csr_payload buffer. On output, the number of bytes written into the * buffer. diff --git a/libwvdrmengine/oemcrypto/include/level3_file_system.h b/libwvdrmengine/oemcrypto/include/level3_file_system.h index 232fbf8e..5d16c0ce 100644 --- a/libwvdrmengine/oemcrypto/include/level3_file_system.h +++ b/libwvdrmengine/oemcrypto/include/level3_file_system.h @@ -13,6 +13,8 @@ #include +#include "platform.h" + namespace wvoec3 { class OEMCrypto_Level3FileSystem { diff --git a/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h b/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h index a4e54380..7da8b45a 100644 --- a/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h +++ b/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h @@ -122,6 +122,7 @@ typedef enum OEMCrypto_Usage_Entry_Status { kInactiveUnused = 4, } OEMCrypto_Usage_Entry_Status; +/* Not used publicly. Not documented with Doxygen. */ typedef enum OEMCrypto_ProvisioningRenewalType { OEMCrypto_NoRenewal = 0, OEMCrypto_RenewalACert = 1, @@ -137,7 +138,9 @@ typedef enum OEMCrypto_LicenseType { OEMCrypto_LicenseType_MaxValue = OEMCrypto_EntitlementLicense, } OEMCrypto_LicenseType; -/* Private key type used in the provisioning response. */ +/** + * Private key type used in the provisioning response. + */ typedef enum OEMCrypto_PrivateKeyType { OEMCrypto_RSA_Private_Key = 0, OEMCrypto_ECC_Private_Key = 1, diff --git a/libwvdrmengine/oemcrypto/odk/include/core_message_features.h b/libwvdrmengine/oemcrypto/odk/include/core_message_features.h index 1365dd84..99566c96 100644 --- a/libwvdrmengine/oemcrypto/odk/include/core_message_features.h +++ b/libwvdrmengine/oemcrypto/odk/include/core_message_features.h @@ -26,9 +26,9 @@ struct CoreMessageFeatures { // This is the published version of the ODK Core Message library. The default // behavior is for the server to restrict messages to at most this version - // number. The default is 18.3. + // number. The default is 18.4. uint32_t maximum_major_version = 18; - uint32_t maximum_minor_version = 3; + uint32_t maximum_minor_version = 4; bool operator==(const CoreMessageFeatures &other) const; bool operator!=(const CoreMessageFeatures &other) const { diff --git a/libwvdrmengine/oemcrypto/odk/include/odk_structs.h b/libwvdrmengine/oemcrypto/odk/include/odk_structs.h index 1debc12b..d97c8ff3 100644 --- a/libwvdrmengine/oemcrypto/odk/include/odk_structs.h +++ b/libwvdrmengine/oemcrypto/odk/include/odk_structs.h @@ -16,10 +16,10 @@ extern "C" { /* The version of this library. */ #define ODK_MAJOR_VERSION 18 -#define ODK_MINOR_VERSION 3 +#define ODK_MINOR_VERSION 4 /* ODK Version string. Date changed automatically on each release. */ -#define ODK_RELEASE_DATE "ODK v18.3 2023-07-07" +#define ODK_RELEASE_DATE "ODK v18.4 2023-08-03" /* The lowest version number for an ODK message. */ #define ODK_FIRST_VERSION 16 diff --git a/libwvdrmengine/oemcrypto/odk/src/core_message_features.cpp b/libwvdrmengine/oemcrypto/odk/src/core_message_features.cpp index b8cae56f..c8a9c3b0 100644 --- a/libwvdrmengine/oemcrypto/odk/src/core_message_features.cpp +++ b/libwvdrmengine/oemcrypto/odk/src/core_message_features.cpp @@ -19,7 +19,9 @@ CoreMessageFeatures CoreMessageFeatures::DefaultFeatures( uint32_t maximum_major_version) { CoreMessageFeatures features; features.maximum_major_version = maximum_major_version; - // The default minor version is the highest for each major version. + // The default minor version is the highest for each major version. This also + // needs to be updated with new version releases in + // ODK_InitializeSessionValues() when the minor version is being set. switch (maximum_major_version) { case 16: features.maximum_minor_version = 5; // 16.5 @@ -28,7 +30,7 @@ CoreMessageFeatures CoreMessageFeatures::DefaultFeatures( features.maximum_minor_version = 2; // 17.2 break; case 18: - features.maximum_minor_version = 3; // 18.3 + features.maximum_minor_version = 4; // 18.4 break; default: features.maximum_minor_version = 0; diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_assert.h b/libwvdrmengine/oemcrypto/odk/src/odk_assert.h index 0517818b..e0de19df 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_assert.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_assert.h @@ -9,7 +9,7 @@ extern "C" { #endif -#if (__STDC_VERSION__ >= 201112L) +#if defined(_MSC_VER) || (__STDC_VERSION__ >= 201112L) #include #define odk_static_assert static_assert #else diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_timer.c b/libwvdrmengine/oemcrypto/odk/src/odk_timer.c index 69945321..c837d783 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_timer.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk_timer.c @@ -264,7 +264,22 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits, ODK_InitializeClockValues(clock_values, 0); nonce_values->api_major_version = api_major_version; - nonce_values->api_minor_version = ODK_MINOR_VERSION; + // This needs to be updated with new version releases in the default features + // of core message features. + switch (nonce_values->api_major_version) { + case 16: + nonce_values->api_minor_version = 5; + break; + case 17: + nonce_values->api_minor_version = 2; + break; + case 18: + nonce_values->api_minor_version = 4; + break; + default: + nonce_values->api_minor_version = 0; + break; + } nonce_values->nonce = 0; nonce_values->session_id = session_id; diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp index 71073fc1..0c3f92a3 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp @@ -1216,7 +1216,7 @@ std::vector TestCases() { // number. {16, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 16, 5}, {17, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 17, 2}, - {18, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 18, 3}, + {18, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 18, 4}, // Here are some known good versions. Make extra sure they work. {ODK_MAJOR_VERSION, 16, 3, 16, 3}, {ODK_MAJOR_VERSION, 16, 4, 16, 4}, @@ -1226,12 +1226,13 @@ std::vector TestCases() { {ODK_MAJOR_VERSION, 18, 1, 18, 1}, {ODK_MAJOR_VERSION, 18, 2, 18, 2}, {ODK_MAJOR_VERSION, 18, 3, 18, 3}, + {ODK_MAJOR_VERSION, 18, 4, 18, 4}, {0, 16, 3, 16, 3}, {0, 16, 4, 16, 4}, {0, 16, 5, 16, 5}, {0, 17, 1, 17, 1}, {0, 17, 2, 17, 2}, - {0, 18, 3, 18, 3}, // Change to 19 when the default version is updated. + {0, 18, 4, 18, 4}, // Change to 19 when the default version is updated. }; return test_cases; } diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_entitled_key_session_fuzz.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_entitled_key_session_fuzz.cc new file mode 100644 index 00000000..78323dd9 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_entitled_key_session_fuzz.cc @@ -0,0 +1,136 @@ +// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine +// License Agreement. + +#include + +#include "FuzzedDataProvider.h" +#include "OEMCryptoCENC.h" +#include "oemcrypto_fuzz_helper.h" + +namespace { + +enum class ApiMethod { + kOpenSession, + kCloseSession, + kCreateEntitledKeySession, + kReassociateEntitledKeySession, + kRemoveEntitledKeySession, + kMaxValue = kRemoveEntitledKeySession, +}; + +struct Session { + OEMCrypto_SESSION value; + std::vector::const_iterator iterator; +}; + +Session PickSession(FuzzedDataProvider& fuzzed_data, + const std::vector& sessions) { + Session session; + + session.iterator = + sessions.cbegin() + + fuzzed_data.ConsumeIntegralInRange(0, sessions.size()); + + if (session.iterator != sessions.cend()) { + session.value = *session.iterator; + } else { + session.value = fuzzed_data.ConsumeIntegral(); + } + + return session; +} + +} // namespace + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + wvoec::RedirectStdoutToFile(); + + wvoec::SessionUtil session_util; + wvoec::InitializeFuzz(session_util); + + // Contains all open and some closed OEMCrypto sessions. + std::vector oec_sessions; + + // Contains all current and some removed key sessions. + std::vector key_sessions; + + FuzzedDataProvider fuzzed_data(data, size); + + while (fuzzed_data.remaining_bytes() > 0) { + switch (fuzzed_data.ConsumeEnum()) { + case ApiMethod::kOpenSession: { + OEMCrypto_SESSION session = 0; + const OEMCryptoResult result = OEMCrypto_OpenSession(&session); + + if (result == OEMCrypto_SUCCESS) { + oec_sessions.push_back(session); + } + + break; + } + + case ApiMethod::kCloseSession: { + const Session session = PickSession(fuzzed_data, oec_sessions); + + const OEMCryptoResult result = OEMCrypto_CloseSession(session.value); + + if (result == OEMCrypto_SUCCESS && + session.iterator != oec_sessions.cend() && + fuzzed_data.ConsumeBool()) { + oec_sessions.erase(session.iterator); + } + + break; + } + + case ApiMethod::kCreateEntitledKeySession: { + const OEMCrypto_SESSION oec_session = + PickSession(fuzzed_data, oec_sessions).value; + + OEMCrypto_SESSION key_session_data = 0; + OEMCrypto_SESSION* const key_session = + fuzzed_data.ConsumeBool() ? &key_session_data : nullptr; + + const OEMCryptoResult result = + OEMCrypto_CreateEntitledKeySession(oec_session, key_session); + + if (result == OEMCrypto_SUCCESS) { + key_sessions.push_back(*key_session); + } + + break; + } + + case ApiMethod::kReassociateEntitledKeySession: { + const OEMCrypto_SESSION key_session = + PickSession(fuzzed_data, key_sessions).value; + + const OEMCrypto_SESSION oec_session = + PickSession(fuzzed_data, oec_sessions).value; + + OEMCrypto_ReassociateEntitledKeySession(key_session, oec_session); + + break; + } + + case ApiMethod::kRemoveEntitledKeySession: { + const Session key_session = PickSession(fuzzed_data, key_sessions); + + const OEMCryptoResult result = + OEMCrypto_RemoveEntitledKeySession(key_session.value); + + if (result == OEMCrypto_SUCCESS && + key_session.iterator != key_sessions.cend() && + fuzzed_data.ConsumeBool()) { + key_sessions.erase(key_session.iterator); + } + + break; + } + } + } + + OEMCrypto_Terminate(); + return 0; +} diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc new file mode 100644 index 00000000..97877b0b --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc @@ -0,0 +1,37 @@ +#include "oemcrypto_session_tests_helper.h" +#include "properties.h" + +using namespace wvoec; + +static bool is_init = false; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + SessionUtil session_helper; + if (!is_init) { + wvoec::global_features.Initialize(); + wvoec::global_features.RestrictFilter("*"); + wvutil::Properties::Init(); + is_init = true; + } + + OEMCrypto_Initialize(); + OEMCrypto_EnterTestMode(); + session_helper.EnsureTestROT(); + + Session s; + s.open(); + s.GenerateDerivedKeysFromKeybox(session_helper.keybox_); + + static const uint32_t SignatureBufferMaxLength = size; + vector signature(SignatureBufferMaxLength); + size_t signature_length = signature.size(); + + OEMCryptoResult sts; + sts = OEMCrypto_GenerateSignature(s.session_id(), data, size, &signature[0], + &signature_length); + + s.close(); + OEMCrypto_Terminate(); + + return 0; +} diff --git a/libwvdrmengine/oemcrypto/test/oec_device_features.cpp b/libwvdrmengine/oemcrypto/test/oec_device_features.cpp index 2ad9c4f6..cfb742b7 100644 --- a/libwvdrmengine/oemcrypto/test/oec_device_features.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_device_features.cpp @@ -148,13 +148,6 @@ void DeviceFeatures::Initialize() { std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) { std::string filter = initial_filter; // clang-format off - // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for - // provisioning 4. Disabled here temporarily. - if (!loads_certificate || - provisioning_method == OEMCrypto_BootCertificateChain) - FilterOut(&filter, "OEMCryptoLoadsCert*"); - if (!generic_crypto) FilterOut(&filter, "*GenericCrypto*"); - if (derive_key_method == NO_METHOD) FilterOut(&filter, "*SessionTest*"); if (api_version < 17) FilterOut(&filter, "*API17*"); if (api_version < 18) FilterOut(&filter, "*API18*"); // clang-format on diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index bbf7e25c..e6e4e329 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -82,30 +82,6 @@ class FuzzedData { size_t source_size_; }; -// Encrypt a block of data using CTR mode. -void EncryptCTR(const vector& in_buffer, const uint8_t* key, - const uint8_t* starting_iv, vector* out_buffer) { - ASSERT_NE(nullptr, key); - ASSERT_NE(nullptr, starting_iv); - ASSERT_NE(nullptr, out_buffer); - AES_KEY aes_key; - AES_set_encrypt_key(key, AES_BLOCK_SIZE * 8, &aes_key); - out_buffer->resize(in_buffer.size()); - - uint8_t iv[AES_BLOCK_SIZE]; // Current iv. - - memcpy(iv, &starting_iv[0], AES_BLOCK_SIZE); - size_t l = 0; // byte index into encrypted subsample. - while (l < in_buffer.size()) { - uint8_t aes_output[AES_BLOCK_SIZE]; - AES_encrypt(iv, aes_output, &aes_key); - for (size_t n = 0; n < AES_BLOCK_SIZE && l < in_buffer.size(); n++, l++) { - (*out_buffer)[l] = aes_output[n] ^ in_buffer[l]; - } - ctr128_inc64(1, iv); - } -} - // Uses OEMCrypto to decrypt some random data in 'cenc' mode. This function // assumes that the correct key is already selected in the session. It requires // the plaintext of that key so that it can encrypt the test data. It resizes @@ -138,6 +114,30 @@ OEMCryptoResult DecryptCTR(const vector& key_handle, } // namespace +// Encrypt a block of data using CTR mode. +void EncryptCTR(const vector& in_buffer, const uint8_t* key, + const uint8_t* starting_iv, vector* out_buffer) { + ASSERT_NE(nullptr, key); + ASSERT_NE(nullptr, starting_iv); + ASSERT_NE(nullptr, out_buffer); + AES_KEY aes_key; + AES_set_encrypt_key(key, AES_BLOCK_SIZE * 8, &aes_key); + out_buffer->resize(in_buffer.size()); + + uint8_t iv[AES_BLOCK_SIZE]; // Current iv. + + memcpy(iv, &starting_iv[0], AES_BLOCK_SIZE); + size_t l = 0; // byte index into encrypted subsample. + while (l < in_buffer.size()) { + uint8_t aes_output[AES_BLOCK_SIZE]; + AES_encrypt(iv, aes_output, &aes_key); + for (size_t n = 0; n < AES_BLOCK_SIZE && l < in_buffer.size(); n++, l++) { + (*out_buffer)[l] = aes_output[n] ^ in_buffer[l]; + } + ctr128_inc64(1, iv); + } +} + int GetRandBytes(unsigned char* buf, size_t num) { // returns 1 on success, -1 if not supported, or 0 if other failure. return RAND_bytes(buf, static_cast(num)); @@ -1231,6 +1231,12 @@ void EntitledMessage::MakeOneKey(size_t entitlement_key_index) { sizeof(key_data->content_key_data_iv))); offsets->content_key_data_iv = FindSubstring( key_data->content_key_data_iv, sizeof(key_data->content_key_data_iv)); + + EXPECT_EQ(1, + GetRandBytes(key_data->content_iv, sizeof(key_data->content_iv))); + key_data->content_iv_length = sizeof(key_data->content_iv); + offsets->content_iv = + FindSubstring(key_data->content_iv, key_data->content_iv_length); } OEMCrypto_EntitledContentKeyObject* EntitledMessage::entitled_key_array() { @@ -1364,8 +1370,8 @@ void EntitledMessage::LoadCasKeys(bool load_even, bool load_odd, // Convert the OEMCrypto_EntitledContentKeyObject to // OEMCrypto_EntitledCasKeyObject. Only the first two key object is used. - OEMCrypto_EntitledContentKeyObject even_key; - OEMCrypto_EntitledContentKeyObject odd_key; + OEMCrypto_EntitledContentKeyObject even_key = {}; + OEMCrypto_EntitledContentKeyObject odd_key = {}; bool has_even = load_even && num_keys_ >= 1; bool has_odd = load_odd && num_keys_ >= 2; if (has_even) { @@ -1373,14 +1379,14 @@ void EntitledMessage::LoadCasKeys(bool load_even, bool load_odd, even_key.content_key_id = entitled_key_array_[0].content_key_id; even_key.content_key_data_iv = entitled_key_array_[0].content_key_data_iv; even_key.content_key_data = entitled_key_array_[0].content_key_data; - even_key.content_iv.length = 0; + even_key.content_iv = entitled_key_array_[0].content_iv; } if (has_odd) { odd_key.entitlement_key_id = entitled_key_array_[1].entitlement_key_id; odd_key.content_key_id = entitled_key_array_[1].content_key_id; odd_key.content_key_data_iv = entitled_key_array_[1].content_key_data_iv; odd_key.content_key_data = entitled_key_array_[1].content_key_data; - odd_key.content_iv.length = 0; + even_key.content_iv = entitled_key_array_[1].content_iv; } OEMCryptoResult sts = OEMCrypto_LoadCasECMKeys( @@ -1461,6 +1467,7 @@ void EntitledMessage::VerifyDecrypt() { void RenewalRoundTrip::VerifyRequestSignature( const vector& data, const vector& generated_signature, size_t core_message_length) { + (void)core_message_length; ASSERT_EQ(HMAC_SHA256_SIGNATURE_SIZE, generated_signature.size()); std::vector expected_signature; session()->key_deriver().ClientSignBuffer(data, &expected_signature); diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.h b/libwvdrmengine/oemcrypto/test/oec_session_util.h index e13bb519..ec11dcef 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.h +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.h @@ -110,6 +110,8 @@ struct EntitledContentKeyData { uint8_t content_key_data_iv[KEY_IV_SIZE]; uint8_t content_key_data[KEY_SIZE]; uint8_t encrypted_content_key_data[KEY_SIZE]; + uint8_t content_iv[KEY_IV_SIZE]; + size_t content_iv_length; size_t key_index; // Index into the license's key array. Only for testing. }; @@ -121,6 +123,10 @@ void GenerateSimpleSampleDescription(const std::vector& in, OEMCrypto_SampleDescription* sample, OEMCrypto_SubSampleDescription* subsample); +// Encrypt a block of data using CTR mode. +void EncryptCTR(const vector& in_buffer, const uint8_t* key, + const uint8_t* starting_iv, vector* out_buffer); + // Increment counter for AES-CTR. The CENC spec specifies we increment only // the low 64 bits of the IV counter, and leave the high 64 bits alone. This // is different from the OpenSSL implementation, so we implement the CTR loop @@ -334,6 +340,7 @@ class Provisioning40RoundTrip void CreateDefaultResponse() override{}; void EncryptAndSignResponse() override{}; OEMCryptoResult LoadResponse(Session* session) override { + (void)session; return OEMCrypto_ERROR_NOT_IMPLEMENTED; } @@ -597,7 +604,7 @@ class RenewalRoundTrip class EntitledMessage { public: EntitledMessage(LicenseRoundTrip* license_messages) - : license_messages_(license_messages), num_keys_() {} + : license_messages_(license_messages) {} void FillKeyArray(); void MakeOneKey(size_t entitlement_key_index); void SetEntitledKeySession(uint32_t key_session) { @@ -631,13 +638,13 @@ class EntitledMessage { void VerifyDecrypt(); LicenseRoundTrip* license_messages_; - uint32_t num_keys_; + uint32_t num_keys_ = 0; // Clear Entitlement key data. This is the backing data for // |entitled_key_array_|. - EntitledContentKeyData entitled_key_data_[kMaxNumKeys]; + EntitledContentKeyData entitled_key_data_[kMaxNumKeys] = {}; // Entitled key object. Pointers are backed by |entitled_key_data_|. - OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys]; - uint32_t entitled_key_session_; + OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys] = {}; + uint32_t entitled_key_session_ = 0; }; class Session { diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_basic_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_basic_test.cpp index 7b070539..5ec1c26b 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_basic_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_basic_test.cpp @@ -156,7 +156,7 @@ TEST_F(OEMCryptoClientTest, FreeUnallocatedSecureBufferNoFailure) { */ TEST_F(OEMCryptoClientTest, VersionNumber) { const std::string log_message = - "OEMCrypto unit tests for API 18.3. Tests last updated 2023-07-07"; + "OEMCrypto unit tests for API 18.4. Tests last updated 2023-09-07"; cout << " " << log_message << "\n"; cout << " " << "These tests are part of Android U." @@ -165,7 +165,7 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { // If any of the following fail, then it is time to update the log message // above. EXPECT_EQ(ODK_MAJOR_VERSION, 18); - EXPECT_EQ(ODK_MINOR_VERSION, 3); + EXPECT_EQ(ODK_MINOR_VERSION, 4); EXPECT_EQ(kCurrentAPI, static_cast(ODK_MAJOR_VERSION)); OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel(); EXPECT_GT(level, OEMCrypto_Level_Unknown); @@ -200,6 +200,13 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { if (build_info.size() != buf_length) { build_info.resize(buf_length); } + const std::string comma = ","; + const std::string pretty_comma = ",\n "; + std::string::size_type pos = 0; + while ((pos = build_info.find(comma, pos)) != std::string::npos) { + build_info.replace(pos, comma.size(), pretty_comma); + pos += pretty_comma.size(); + } cout << " BuildInformation: " << build_info << endl; OEMCrypto_WatermarkingSupport support = OEMCrypto_GetWatermarkingSupport(); cout << " WatermarkingSupport: " << support << endl; diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_cast_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_cast_test.cpp index 6dc25527..91730253 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_cast_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_cast_test.cpp @@ -11,21 +11,36 @@ using ::testing::Range; namespace wvoec { -// The alternate padding is only required for cast receivers, but all devices -// should forbid the alternate padding for regular certificates. -TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPaddingAPI09) { - LoadWithAllowedSchemes(kSign_RSASSA_PSS, - true); // Use default padding scheme - DisallowForbiddenPadding(kSign_PKCS1_Block1, 50); -} - -// The alternate padding is only required for cast receivers, but if a device -// does load an alternate certificate, it should NOT use it for generating -// a license request signature. +/** If a device can load a private key with the alternate padding schemes, it + * should support signing with the alternate scheme. */ TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) { // Try to load an RSA key with alternative padding schemes. This signing // scheme is used by cast receivers. - LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); + LoadCastCertificateKey(false); + // If the device is a cast receiver, then this scheme is required. + if (global_features.cast_receiver) { + ASSERT_TRUE(key_loaded_); + } + // If the key loaded with no error, then we will verify that it is not used + // for forbidden padding schemes. + if (key_loaded_) { + if (global_features.cast_receiver) { + // A signature with a valid size should succeed. + TestSignature(kSign_PKCS1_Block1, 83); + TestSignature(kSign_PKCS1_Block1, 50); + } + // A signature with padding that is too big should fail. + DisallowForbiddenPaddingDRMKey(kSign_PKCS1_Block1, 84); // too big. + } +} + +/** The alternate padding is only required for cast receivers, but if a device + * does load an alternate certificate, it should NOT be used as a DRM cert + * key. */ +TEST_F(OEMCryptoLoadsCertificateAlternates, ForbidUseAsDRMCert) { + // Try to load an RSA key with alternative padding schemes. This signing + // scheme is used by cast receivers. + LoadCastCertificateKey(false); // If the device is a cast receiver, then this scheme is required. if (global_features.cast_receiver) { ASSERT_TRUE(key_loaded_); @@ -34,15 +49,44 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) { // for forbidden padding schemes. if (key_loaded_) { // The other padding scheme should fail. - DisallowForbiddenPadding(kSign_RSASSA_PSS, 83); + DisallowForbiddenPaddingDRMKey(kSign_RSASSA_PSS, 83); DisallowDeriveKeys(); - if (global_features.cast_receiver) { - // A signature with a valid size should succeed. - TestSignature(kSign_PKCS1_Block1, 83); - TestSignature(kSign_PKCS1_Block1, 50); - } - // A signature with padding that is too big should fail. - DisallowForbiddenPadding(kSign_PKCS1_Block1, 84); // too big. + } +} + +/** A Cast receiver certificate private key cannot be used with the function + * PrepAndSignLicenseRequest. + */ +TEST_F(OEMCryptoLoadsCertificateAlternates, ForbidPrepAndSign) { + // Try to load an RSA key with alternative padding schemes. This signing + // scheme is used by cast receivers. + LoadCastCertificateKey(false); + // If the device is a cast receiver, then this scheme is required. + if (global_features.cast_receiver) { + ASSERT_TRUE(key_loaded_); + } + // If the key loaded with no error, then we will verify that it is not used + // for forbidden padding schemes. + if (key_loaded_) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); + s.GenerateNonce(); + + size_t core_message_length = 100; + std::vector message(128, 0); + std::vector signature(256, 0); + size_t signature_length = signature.size(); + + OEMCryptoResult result = OEMCrypto_PrepAndSignLicenseRequest( + s.session_id(), message.data(), message.size(), &core_message_length, + signature.data(), &signature_length); + // TODO: remove OEMCrypto_ERROR_INVALID_RSA_KEY once OEMCrypto v16 is not + // supported anymore. This error code has been deprecated since v17. + ASSERT_TRUE(result == OEMCrypto_ERROR_INVALID_KEY || + result == OEMCrypto_ERROR_INVALID_RSA_KEY); + const vector zero(signature.size(), 0); + ASSERT_EQ(signature, zero); // Signature should not have been computed. } } @@ -275,7 +319,7 @@ TEST_F(OEMCryptoCastReceiverTest, SupportsCertificatesAPI13) { // # PKCS#1 v1.5 Signature Example 15.1 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_1) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "f45d55f35551e975d6a8dc7ea9f48859" "3940cc75694a278f27e578a163d839b3" @@ -314,7 +358,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_1) { // # PKCS#1 v1.5 Signature Example 15.2 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_2) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "c14b4c6075b2f9aad661def4ecfd3cb9" "33c623f4e63bf53410d2f016d1ab98e2" @@ -349,7 +393,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_2) { // # PKCS#1 v1.5 Signature Example 15.3 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_3) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "d02371ad7ee48bbfdb2763de7a843b94" "08ce5eb5abf847ca3d735986df84e906" @@ -390,7 +434,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_3) { // # PKCS#1 v1.5 Signature Example 15.4 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_4) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "29035584ab7e0226a9ec4b02e8dcf127" "2dc9a41d73e2820007b0f6e21feccd5b" @@ -419,7 +463,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_4) { // # PKCS#1 v1.5 Signature Example 15.5 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_5) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex("bda3a1c79059eae598308d3df609"); vector signature = wvutil::a2b_hex( "a156176cb96777c7fb96105dbd913bc4" @@ -444,7 +488,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_5) { // # PKCS#1 v1.5 Signature Example 15.6 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_6) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "c187915e4e87da81c08ed4356a0cceac" "1c4fb5c046b45281b387ec28f1abfd56" @@ -476,7 +520,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_6) { // # PKCS#1 v1.5 Signature Example 15.7 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_7) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "abfa2ecb7d29bd5bcb9931ce2bad2f74" "383e95683cee11022f08e8e7d0b8fa05" @@ -509,7 +553,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_7) { // # PKCS#1 v1.5 Signature Example 15.8 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_8) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "df4044a89a83e9fcbf1262540ae3038b" "bc90f2b2628bf2a4467ac67722d8546b" @@ -548,7 +592,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_8) { // # PKCS#1 v1.5 Signature Example 15.9 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_9) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "ea941ff06f86c226927fcf0e3b11b087" "2676170c1bfc33bda8e265c77771f9d0" @@ -585,7 +629,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_9) { // # PKCS#1 v1.5 Signature Example 15.10 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_10) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "d8b81645c13cd7ecf5d00ed2c91b9acd" "46c15568e5303c4a9775ede76b48403d" @@ -615,7 +659,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_10) { // # PKCS#1 v1.5 Signature Example 15.11 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_11) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "e5739b6c14c92d510d95b826933337ff" "0d24ef721ac4ef64c2bad264be8b44ef" @@ -649,7 +693,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_11) { // # PKCS#1 v1.5 Signature Example 15.12 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_12) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "7af42835917a88d6b3c6716ba2f5b0d5" "b20bd4e2e6e574e06af1eef7c81131be" @@ -690,7 +734,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_12) { // # PKCS#1 v1.5 Signature Example 15.13 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_13) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "ebaef3f9f23bdfe5fa6b8af4c208c189" "f2251bf32f5f137b9de4406378686b3f" @@ -719,7 +763,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_13) { // # PKCS#1 v1.5 Signature Example 15.14 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_14) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "c5a2711278761dfcdd4f0c99e6f5619d" "6c48b5d4c1a80982faa6b4cf1cf7a60f" @@ -755,7 +799,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_14) { // # PKCS#1 v1.5 Signature Example 15.15 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_15) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "9bf8aa253b872ea77a7e23476be26b23" "29578cf6ac9ea2805b357f6fc3ad130d" @@ -794,7 +838,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_15) { // # PKCS#1 v1.5 Signature Example 15.16 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_16) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "32474830e2203754c8bf0681dc4f842a" "fe360930378616c108e833656e5640c8" @@ -835,7 +879,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_16) { // # PKCS#1 v1.5 Signature Example 15.17 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_17) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "008e59505eafb550aae5e845584cebb0" "0b6de1733e9f95d42c882a5bbeb5ce1c" @@ -864,7 +908,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_17) { // # PKCS#1 v1.5 Signature Example 15.18 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_18) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "6abc54cf8d1dff1f53b17d8160368878" "a8788cc6d22fa5c2258c88e660b09a89" @@ -894,7 +938,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_18) { // # PKCS#1 v1.5 Signature Example 15.19 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_19) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "af2d78152cf10efe01d274f217b177f6" "b01b5e749f1567715da324859cd3dd88" @@ -931,7 +975,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_19) { // # PKCS#1 v1.5 Signature Example 15.20 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_20) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "40ee992458d6f61486d25676a96dd2cb" "93a37f04b178482f2b186cf88215270d" @@ -974,4 +1018,4 @@ TEST_P(OEMCryptoSessionTestLoadCasKeysWithHDCP, CasOnlyLoadCasKeysAPI17) { } INSTANTIATE_TEST_SUITE_P(TestHDCP, OEMCryptoSessionTestLoadCasKeysWithHDCP, Range(1, 6)); -} // namespace wvoec \ No newline at end of file +} // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_cast_test.h b/libwvdrmengine/oemcrypto/test/oemcrypto_cast_test.h index b6e2116c..c812ebea 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_cast_test.h +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_cast_test.h @@ -25,36 +25,6 @@ std::string MaybeHex(const std::vector& data); // This test attempts to use alternate algorithms for loaded device certs. class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { protected: - void DisallowForbiddenPadding(RSA_Padding_Scheme scheme, size_t size) { - OEMCryptoResult sts; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); - - // Sign a Message - vector licenseRequest(size); - GetRandBytes(licenseRequest.data(), licenseRequest.size()); - size_t signature_length = 256; - vector signature(signature_length); - sts = OEMCrypto_GenerateRSASignature( - s.session_id(), licenseRequest.data(), licenseRequest.size(), - signature.data(), &signature_length, scheme); - // Allow OEMCrypto to request a full buffer. - if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { - ASSERT_NE(static_cast(0), signature_length); - signature.assign(signature_length, 0); - sts = OEMCrypto_GenerateRSASignature( - s.session_id(), licenseRequest.data(), licenseRequest.size(), - signature.data(), &signature_length, scheme); - } - - EXPECT_NE(OEMCrypto_SUCCESS, sts) - << "Signed with forbidden padding scheme=" << (int)scheme - << ", size=" << (int)size; - const vector zero(signature.size(), 0); - ASSERT_EQ(zero, signature); // signature should not be computed. - } - void TestSignature(RSA_Padding_Scheme scheme, size_t size) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -105,7 +75,9 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { } // If force is true, we assert that the key loads successfully. - void LoadWithAllowedSchemes(uint32_t schemes, bool force) { + void LoadCastCertificateKey(bool force) { + // Padding scheme used to sign cast data. + constexpr uint32_t schemes = kSign_PKCS1_Block1; // prov 2 or prov 3 if (global_features.provisioning_method == OEMCrypto_Keybox || global_features.provisioning_method == OEMCrypto_OEMCertificate) { diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp index 31b9bb52..6b603dcf 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp @@ -1,12 +1,14 @@ /* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */ /* source code may only be used and distributed under the Widevine */ /* License Agreement. */ + #include "oemcrypto_corpus_generator_helper.h" #include #include namespace wvoec { + bool g_generate_corpus; void AppendToFile(const std::string& file_name, const char* message, @@ -32,7 +34,7 @@ void AppendSeparator(const std::string& file_name) { std::string GetFileName(const char* directory) { std::string file_name(PATH_TO_CORPUS); file_name += directory; - file_name += "/"; + file_name += '/'; file_name += std::to_string(rand()); return file_name; } @@ -40,5 +42,7 @@ std::string GetFileName(const char* directory) { void SetGenerateCorpus(bool should_generate_corpus) { g_generate_corpus = should_generate_corpus; } + bool ShouldGenerateCorpus() { return g_generate_corpus; } + } // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.h b/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.h index e3a73b43..8bfc4925 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.h +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.h @@ -1,16 +1,19 @@ /* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */ /* source code may only be used and distributed under the Widevine */ /* License Agreement. */ + #ifndef CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_ #define CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_ #define PATH_TO_CORPUS "./oemcrypto/test/fuzz_tests/corpus/" -#include +#include #include + #include namespace wvoec { + const uint8_t kFuzzDataSeparator[] = {'-', '_', '^', '_'}; void AppendToFile(const std::string& file_name, const char* message, @@ -22,9 +25,11 @@ void AppendSeparator(const std::string& file_name); std::string GetFileName(const char* directory); void SetGenerateCorpus(bool should_generate_corpus); + // Output of this function decides if binary data needs to be written // to corpus files or not. Controlled by --generate_corpus flag. bool ShouldGenerateCorpus(); + } // namespace wvoec #endif // CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_ diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_decrypt_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_decrypt_test.cpp index 22c810c0..10c32541 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_decrypt_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_decrypt_test.cpp @@ -540,6 +540,17 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) { ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } +TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptZeroSizeSubSample) { + ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ + {10, 10}, + {0, 0}, + })); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); +} + // There are probably no frames this small, but we should handle them anyway. TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) { ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_decrypt_test.h b/libwvdrmengine/oemcrypto/test/oemcrypto_decrypt_test.h index 9f9b5005..36a249c8 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_decrypt_test.h +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_decrypt_test.h @@ -98,6 +98,10 @@ class OEMCryptoSessionTestsDecryptTests protected: void SetUp() override { OEMCryptoLicenseTestAPI16::SetUp(); + if (wvoec::global_features.derive_key_method == + wvoec::DeviceFeatures::NO_METHOD) { + GTEST_SKIP() << "Test for devices that can derive session keys only."; + } pattern_ = ::testing::get<0>(GetParam()); cipher_mode_ = ::testing::get<1>(GetParam()); decrypt_inplace_ = ::testing::get<2>(GetParam()).decrypt_inplace; diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.cpp index 1a3d2ee6..1f47fd36 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.cpp @@ -84,13 +84,6 @@ void TestMaxKeys(SessionUtil* util, size_t num_keys_per_session) { } } -TEST_F(OEMCryptoSessionTestKeyboxTest, TestKeyboxIsValid) { - if (global_features.provisioning_method != OEMCrypto_Keybox) { - GTEST_SKIP() << "Test for Prov 2.0 devices only."; - } - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()); -} - TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryPrepareLicenseRequestForHugeRequestMessageLength) { TestPrepareLicenseRequestForHugeBufferLengths( diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.h b/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.h index a2860503..8d7339b2 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.h +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.h @@ -39,6 +39,10 @@ class OEMCryptoSessionTests : public OEMCryptoClientTest { void SetUp() override { OEMCryptoClientTest::SetUp(); + if (wvoec::global_features.derive_key_method == + wvoec::DeviceFeatures::NO_METHOD) { + GTEST_SKIP() << "Test for devices that can derive session keys only."; + } EnsureTestROT(); if (global_features.usage_table) { CreateUsageTableHeader(); @@ -92,8 +96,6 @@ class OEMCryptoSessionTests : public OEMCryptoClientTest { } }; -class OEMCryptoSessionTestKeyboxTest : public OEMCryptoSessionTests {}; - // This class is for testing a single license with the default API version // of 16. class OEMCryptoLicenseTestAPI16 : public OEMCryptoSessionTests { @@ -407,4 +409,4 @@ class OEMCryptoRefreshTestAPI16 : public OEMCryptoRefreshTest {}; } // namespace wvoec -#endif // CDM_OEMCRYPTO_LICENSE_TEST_ \ No newline at end of file +#endif // CDM_OEMCRYPTO_LICENSE_TEST_ diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.cpp index 965b2b38..47d7228d 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.cpp @@ -119,36 +119,24 @@ TEST_F(OEMCryptoProv30Test, OEMCertValid) { ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify. } -// This verifies that the OEM Certificate cannot be used for other RSA padding -// schemes. Those schemes should only be used by cast receiver certificates. -TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { +/** This verifies that the OEM Certificate cannot be used with + * GenerateRSASignature. + */ +TEST_F(OEMCryptoProv30Test, OEMCertForbidGenerateRSASignature1) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); - OEMCryptoResult sts; - // Sign a Message - vector data(500); - GetRandBytes(data.data(), data.size()); - size_t signature_length = 0; - // We need a size one vector to pass as a pointer. - vector signature(1, 0); - vector zero(1, 0); + DisallowForbiddenPadding(s.session_id(), kSign_PKCS1_Block1, 80); +} - sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), data.size(), - signature.data(), &signature_length, - kSign_PKCS1_Block1); - if (OEMCrypto_ERROR_SHORT_BUFFER == sts) { - // The OEMCrypto could complain about buffer length first, so let's - // resize and check if it's writing to the signature again. - signature.resize(signature_length, 0); - zero.resize(signature_length, 0); - sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), - data.size(), signature.data(), - &signature_length, kSign_PKCS1_Block1); - } - EXPECT_NE(OEMCrypto_SUCCESS, sts) - << "OEM Cert Signed with forbidden kSign_PKCS1_Block1."; - ASSERT_EQ(zero, signature); // signature should not be computed. +/** This verifies that the OEM Certificate cannot be used with + * GenerateRSASignature. + */ +TEST_F(OEMCryptoProv30Test, OEMCertForbidGenerateRSASignature2) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); + DisallowForbiddenPadding(s.session_id(), kSign_RSASSA_PSS, 80); } // Calling OEMCrypto_GetOEMPublicCertificate should not change the session's @@ -186,6 +174,46 @@ TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) { ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } +/** This verifies that the OEM Certificate cannot be used with + * GenerateRSASignature. + */ +TEST_F(OEMCryptoProv40Test, OEMCertForbidGenerateRSASignature1) { + // Create an OEM Cert and save it for later. + Session s1; + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(CreateProv4OEMKey(&s1)); + ASSERT_EQ(s1.IsPublicKeySet(), true); + s1.close(); + Session s2; + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_InstallOemPrivateKey( + s2.session_id(), oem_key_type_, + reinterpret_cast(wrapped_oem_key_.data()), + wrapped_oem_key_.size())); + DisallowForbiddenPadding(s2.session_id(), kSign_PKCS1_Block1, 80); +} + +/** This verifies that the OEM Certificate cannot be used with + * GenerateRSASignature. + */ +TEST_F(OEMCryptoProv40Test, OEMCertForbidGenerateRSASignature2) { + // Create an OEM Cert and save it for later. + Session s1; + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(CreateProv4OEMKey(&s1)); + ASSERT_EQ(s1.IsPublicKeySet(), true); + s1.close(); + Session s2; + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_InstallOemPrivateKey( + s2.session_id(), oem_key_type_, + reinterpret_cast(wrapped_oem_key_.data()), + wrapped_oem_key_.size())); + DisallowForbiddenPadding(s2.session_id(), kSign_RSASSA_PSS, 80); +} + // This verifies that the device really does claim to have BCC. // It should be filtered out for devices that have a keybox or factory OEM // cert. @@ -539,7 +567,7 @@ TEST_F(OEMCryptoProv40Test, InstallOemPrivateKeyCanBeUsed) { * cert. */ TEST_F(OEMCryptoProv40Test, OEMPrivateKeyCannotBeDRMKey) { - // Create an OEM Cert and save it for alter. + // Create an OEM Cert and save it for later. Session s1; ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(CreateProv4OEMKey(&s1)); @@ -654,7 +682,22 @@ TEST_P(OEMCryptoProv40CastTest, ProvisionCastWorks) { INSTANTIATE_TEST_SUITE_P(Prov4CastProvisioningBasic, OEMCryptoProv40CastTest, testing::Values(true, false)); +// Verify that you cannot use GenerateRSASignature with a normal DRM Cert. +// that function needs a cast cert. +TEST_F(OEMCryptoLoadsCertificate, ForbidRSASignatureForDRMKey1) { + DisallowForbiddenPadding(session_.session_id(), kSign_RSASSA_PSS, 80); +} + +TEST_F(OEMCryptoLoadsCertificate, ForbidRSASignatureForDRMKey2) { + DisallowForbiddenPadding(session_.session_id(), kSign_PKCS1_Block1, 80); +} + TEST_F(OEMCryptoLoadsCertificate, PrepAndSignLicenseRequestCounterAPI18) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -675,6 +718,11 @@ TEST_F(OEMCryptoLoadsCertificate, PrepAndSignLicenseRequestCounterAPI18) { // This test verifies that we can create a wrapped RSA key, and then reload it. TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -682,6 +730,11 @@ TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) { } TEST_F(OEMCryptoLoadsCertificate, SignProvisioningRequest) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ASSERT_NO_FATAL_FAILURE(s.open()); if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { @@ -696,6 +749,11 @@ TEST_F(OEMCryptoLoadsCertificate, SignProvisioningRequest) { // This tests a large message size. The size is larger than we required in v15. TEST_F(OEMCryptoLoadsCertificate, SignLargeProvisioningRequestAPI16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ASSERT_NO_FATAL_FAILURE(s.open()); if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { @@ -714,6 +772,11 @@ TEST_F(OEMCryptoLoadsCertificate, SignLargeProvisioningRequestAPI16) { // unencrypted key is not found in the wrapped key. The wrapped key should be // encrypted. TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -730,6 +793,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -747,6 +815,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1_API16) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -764,6 +837,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2_API16) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -783,6 +861,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3_API16) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -802,6 +885,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4_API16) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { GTEST_SKIP() << "Test for Prov 3.0 devices only."; } @@ -825,6 +913,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30_API16) { // TODO(b/144186970): This test should also run on Prov 3.0 devices. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadSignatureKeyboxTestAPI16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } if (global_features.provisioning_method != OEMCrypto_Keybox) { GTEST_SKIP() << "Test for Prov 2.0 devices only."; } @@ -842,6 +935,11 @@ TEST_F(OEMCryptoLoadsCertificate, // Test that RewrapDeviceRSAKey verifies the nonce is current. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonce_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -856,6 +954,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonce_API16) { // Test that RewrapDeviceRSAKey verifies the RSA key is valid. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKey) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -874,6 +977,11 @@ TEST_F(OEMCryptoLoadsCertificate, if (global_features.provisioning_method != OEMCrypto_Keybox) { GTEST_SKIP() << "Test for Prov 2.0 devices only."; } + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -893,6 +1001,11 @@ TEST_F(OEMCryptoLoadsCertificate, // Test that RewrapDeviceRSAKey accepts the maximum message size. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBuffer) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); const size_t max_size = GetResourceValue(kLargeMessageSize); @@ -910,6 +1023,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBuffer) { // Test that a wrapped RSA key can be loaded. TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -918,6 +1036,15 @@ TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { class OEMCryptoLoadsCertVariousKeys : public OEMCryptoLoadsCertificate { public: + void SetUp() override { + OEMCryptoLoadsCertificate::SetUp(); + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } + } + void TestKey(const uint8_t* key, size_t key_length) { encoded_rsa_key_.assign(key, key + key_length); ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); @@ -993,6 +1120,11 @@ TEST_F(OEMCryptoLoadsCertVariousKeys, TestEulerZeroNormalDer) { // This tests that two sessions can use different RSA keys simultaneously. TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); Session s1; // Session s1 loads the default rsa key, but doesn't use it // until after s2 uses its key. @@ -1029,6 +1161,11 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { // This tests the maximum number of DRM private keys that OEMCrypto can load TEST_F(OEMCryptoLoadsCertificate, TestMaxDRMKeys) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } const size_t max_total_keys = GetResourceValue(kMaxTotalDRMPrivateKeys); std::vector> sessions; std::vector> licenses; @@ -1096,6 +1233,11 @@ TEST_F(OEMCryptoLoadsCertificate, TestMaxDRMKeys) { // Devices that load certificates, should at least support RSA 2048 keys. TEST_F(OEMCryptoLoadsCertificate, SupportsCertificatesAPI13) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NE(0u, OEMCrypto_Supports_RSA_2048bit & OEMCrypto_SupportedCertificates()) << "Supported certificates is only " << OEMCrypto_SupportedCertificates(); @@ -1104,6 +1246,11 @@ TEST_F(OEMCryptoLoadsCertificate, SupportsCertificatesAPI13) { // This test is not run by default, because it takes a long time and // is used to measure RSA performance, not test functionality. TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } const std::chrono::milliseconds kTestDuration(5000); OEMCryptoResult sts; std::chrono::steady_clock clock; @@ -1205,7 +1352,9 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { delta_time / std::chrono::milliseconds(1) / count; OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel(); - printf("PERF:head, security, provision (ms), lic req(ms), derive keys(ms)\n"); + printf( + "PERF:head, security, provision (ms), lic req(ms), derive " + "keys(ms)\n"); printf("PERF:stat, %u, %8.3f, %8.3f, %8.3f\n", static_cast(level), provision_time, license_request_time, derive_keys_time); diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.h b/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.h index dccfb572..8877b175 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.h +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.h @@ -17,65 +17,84 @@ namespace wvoec { -// Tests using this class are only used for devices with a keybox. They are not -// run for devices with an OEM Certificate. -class OEMCryptoKeyboxTest : public OEMCryptoClientTest { - void SetUp() override { - OEMCryptoClientTest::SetUp(); - OEMCryptoResult sts = OEMCrypto_IsKeyboxValid(); - if (global_features.provisioning_method != OEMCrypto_Keybox) { - GTEST_SKIP() << "Test for Prov 2.0 devices only."; - } - // If the production keybox is valid, use it for these tests. Most of the - // other tests will use a test keybox anyway, but it's nice to check the - // device ID for the real keybox if we can. - if (sts == OEMCrypto_SUCCESS) return; - printf("Production keybox is NOT valid. All tests use test keybox.\n"); - ASSERT_EQ( - OEMCrypto_SUCCESS, - OEMCrypto_LoadTestKeybox(reinterpret_cast(&kTestKeybox), - sizeof(kTestKeybox))); - } -}; - -// This class is for tests that have an OEM Certificate instead of a keybox. -class OEMCryptoProv30Test : public OEMCryptoClientTest { - void SetUp() override { - OEMCryptoClientTest::SetUp(); - if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { - GTEST_SKIP() << "Test for Prov 3.0 devices only."; - } - } -}; - -// This class is for tests that have boot certificate chain instead of a keybox. -class OEMCryptoProv40Test : public OEMCryptoClientTest { - void SetUp() override { - OEMCryptoClientTest::SetUp(); - if (global_features.provisioning_method != OEMCrypto_BootCertificateChain) { - GTEST_SKIP() << "Test for Prov 4.0 devices only."; - } - } -}; - -class OEMCryptoProv40CastTest : public OEMCryptoClientTest, - public testing::WithParamInterface { - void SetUp() override { - OEMCryptoClientTest::SetUp(); - if (!global_features.cast_receiver) { - GTEST_SKIP() << "Test for cast devices only."; - } - if (global_features.provisioning_method != OEMCrypto_BootCertificateChain) { - GTEST_SKIP() << "Test for Prov 4.0 devices only."; - } - } -}; - // // Certificate Root of Trust Tests // -class OEMCryptoLoadsCertificate : public OEMCryptoSessionTestKeyboxTest { +// These tests are run by all L1 devices that load and use certificates. It is +// also run by a few L3 devices that use a baked in certificate, but cannot load +// a certificate. +class OEMCryptoUsesCertificate : public OEMCryptoSessionTests { protected: + void SetUp() override { + OEMCryptoSessionTests::SetUp(); + ASSERT_NO_FATAL_FAILURE(session_.open()); + if (global_features.derive_key_method == + DeviceFeatures::LOAD_TEST_RSA_KEY) { + ASSERT_NO_FATAL_FAILURE(session_.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + } else { + InstallTestDrmKey(&session_); + } + } + + void TearDown() override { + ASSERT_NO_FATAL_FAILURE(session_.close()); + OEMCryptoSessionTests::TearDown(); + } + + Session session_; +}; + +/** These tests cover all systems that can load a DRM Certificate. That includes + * Provisioning 2, 3 and 4. */ +class OEMCryptoLoadsCertificate : public OEMCryptoUsesCertificate { + protected: + void SetUp() override { + OEMCryptoUsesCertificate::SetUp(); + if (!global_features.loads_certificate) { + GTEST_SKIP() << "Test for devices that load a DRM certificate only."; + } + } + + /** Verify that the specified padding scheme does not work with the DRM + * key and the function OEMCrypto_GenerateRSASignature. */ + void DisallowForbiddenPaddingDRMKey(RSA_Padding_Scheme scheme, size_t size) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); + DisallowForbiddenPadding(s.session_id(), scheme, size); + } + + /** Verify that the specified padding scheme does not work with whichever key + * is currently loaded into the specified session and the function + * OEMCrypto_GenerateRSASignature. */ + void DisallowForbiddenPadding(OEMCrypto_SESSION session, + RSA_Padding_Scheme scheme, size_t size) { + OEMCryptoResult sts; + // Sign a Message + vector message(size); + GetRandBytes(message.data(), message.size()); + size_t signature_length = 256; + vector signature(signature_length); + sts = OEMCrypto_GenerateRSASignature(session, message.data(), + message.size(), signature.data(), + &signature_length, scheme); + // Allow OEMCrypto to request a full buffer. + if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { + ASSERT_NE(static_cast(0), signature_length); + signature.assign(signature_length, 0); + sts = OEMCrypto_GenerateRSASignature(session, message.data(), + message.size(), signature.data(), + &signature_length, scheme); + } + + EXPECT_NE(OEMCrypto_SUCCESS, sts) + << "Signed with forbidden padding scheme=" << (int)scheme + << ", size=" << (int)size; + const vector zero(signature.size(), 0); + ASSERT_EQ(zero, signature); // signature should not be computed. + } + void TestPrepareProvisioningRequestForHugeBufferLengths( const std::function f, bool check_status) { @@ -142,29 +161,61 @@ class OEMCryptoLoadsCertificate : public OEMCryptoSessionTestKeyboxTest { } }; -// These tests are run by all L1 devices that load and use certificates. It is -// also run by a few L3 devices that use a baked in certificate, but cannot load -// a certificate. -class OEMCryptoUsesCertificate : public OEMCryptoLoadsCertificate { +// Tests using this class are only used for devices with a keybox. They are not +// run for devices with an OEM Certificate. +class OEMCryptoKeyboxTest : public OEMCryptoLoadsCertificate { protected: void SetUp() override { OEMCryptoLoadsCertificate::SetUp(); - ASSERT_NO_FATAL_FAILURE(session_.open()); - if (global_features.derive_key_method == - DeviceFeatures::LOAD_TEST_RSA_KEY) { - ASSERT_NO_FATAL_FAILURE(session_.SetRsaPublicKeyFromPrivateKeyInfo( - encoded_rsa_key_.data(), encoded_rsa_key_.size())); - } else { - InstallTestDrmKey(&session_); + if (global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } + OEMCryptoResult sts = OEMCrypto_IsKeyboxValid(); + // If the production keybox is valid, use it for these tests. Most of the + // other tests will use a test keybox anyway, but it's nice to check the + // device ID for the real keybox if we can. + if (sts == OEMCrypto_SUCCESS) return; + printf("Production keybox is NOT valid. All tests use test keybox.\n"); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_LoadTestKeybox(reinterpret_cast(&kTestKeybox), + sizeof(kTestKeybox))); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()) + << "After loading Test keybox, the keybox was still not valid."; + } +}; + +// This class is for tests that have an OEM Certificate instead of a keybox. +class OEMCryptoProv30Test : public OEMCryptoLoadsCertificate { + protected: + void SetUp() override { + OEMCryptoLoadsCertificate::SetUp(); + if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { + GTEST_SKIP() << "Test for Prov 3.0 devices only."; } } +}; - void TearDown() override { - ASSERT_NO_FATAL_FAILURE(session_.close()); - OEMCryptoLoadsCertificate::TearDown(); +// This class is for tests that have boot certificate chain instead of a keybox. +class OEMCryptoProv40Test : public OEMCryptoLoadsCertificate { + protected: + void SetUp() override { + OEMCryptoLoadsCertificate::SetUp(); + if (global_features.provisioning_method != OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for Prov 4.0 devices only."; + } } +}; - Session session_; +class OEMCryptoProv40CastTest : public OEMCryptoProv40Test, + public testing::WithParamInterface { + protected: + void SetUp() override { + OEMCryptoProv40Test::SetUp(); + if (!global_features.cast_receiver) { + GTEST_SKIP() << "Test for cast devices only."; + } + } }; } // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_security_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_security_test.cpp index af9b0462..48cd73b1 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_security_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_security_test.cpp @@ -606,6 +606,12 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeSignatureLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } auto oemcrypto_function = [&](size_t signature_size) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); @@ -638,6 +644,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeWrappedRsaKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } auto oemcrypto_function = [&](size_t buffer_length) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); @@ -663,6 +675,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadDrmPrivateKeyForHugeWrappedRsaKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); auto oemcrypto_function = [&](size_t wrapped_rsa_key_length) { Session s; @@ -685,6 +703,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadDrmPrivateKeyForHugeWrappedRsaKeyLengthStartingFromLength1) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); auto oemcrypto_function = [&](size_t wrapped_rsa_key_length) { Session s; @@ -765,8 +789,14 @@ TEST_F(OEMCryptoUsesCertificate, TEST_F(OEMCryptoLoadsCertificateAlternates, OEMCryptoMemoryGenerateRSASignatureForHugeBuffer) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } OEMCryptoResult sts; - LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); + LoadCastCertificateKey(false); // If the device is a cast receiver, then this scheme is required. if (global_features.cast_receiver) { ASSERT_TRUE(key_loaded_); @@ -782,6 +812,7 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, sts = OEMCrypto_GenerateRSASignature(s.session_id(), message_buffer.data(), message_buffer.size(), nullptr, &signature_length, kSign_PKCS1_Block1); + if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) return; ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_NE(static_cast(0), signature_length); vector signature(signature_length); @@ -799,7 +830,13 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, TEST_F(OEMCryptoLoadsCertificateAlternates, OEMCryptoMemoryGenerateRSASignatureForHugeSignatureLength) { - LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } + LoadCastCertificateKey(false); // If the device is a cast receiver, then this scheme is required. if (global_features.cast_receiver) { ASSERT_TRUE(key_loaded_); diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 337cf9e2..7998ebec 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -835,9 +835,7 @@ TEST_P(OEMCryptoLicenseTest, EntitledKeySessionMultipleKeySessionsAPI17) { session_.session_id(), &key_session_id_2); // For DRM, but not for CAS, we allow there to be only a single entitled // session. - if (!global_features.supports_cas && - (key_session_id_2 == key_session_id_1 || - status == OEMCrypto_ERROR_TOO_MANY_SESSIONS)) { + if (status == OEMCrypto_ERROR_TOO_MANY_SESSIONS) { GTEST_SKIP() << "Skipping test because multiple entitled sessions not supported."; } @@ -1429,6 +1427,12 @@ INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoLicenseOverflowTest, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeResponseLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_message_size(message_size); @@ -1438,6 +1442,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_core_message_size(message_size); @@ -1447,6 +1457,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t length, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().enc_private_key.length = length; @@ -1456,6 +1472,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t offset, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().enc_private_key.offset = offset; @@ -1466,6 +1488,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths( [](size_t response_message_length, ProvisioningRoundTrip* provisioning_messages) { @@ -1479,6 +1507,12 @@ TEST_F( TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths( [](size_t response_message_length, ProvisioningRoundTrip* provisioning_messages) { @@ -1491,6 +1525,12 @@ TEST_F( TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyIvLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t length, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().enc_private_key_iv.length = @@ -1501,6 +1541,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyIvOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t offset, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().enc_private_key_iv.offset = @@ -1512,6 +1558,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyIvLengthAPI16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths( [](size_t response_message_length, ProvisioningRoundTrip* provisioning_messages) { @@ -1525,6 +1577,12 @@ TEST_F( TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyIvOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths( [](size_t response_message_length, ProvisioningRoundTrip* provisioning_messages) { @@ -1537,6 +1595,12 @@ TEST_F( TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncMessageKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t length, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().encrypted_message_key.length = @@ -1547,6 +1611,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncMessageKeyOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t offset, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().encrypted_message_key.offset = @@ -1558,6 +1628,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncMessageKeyLengthProv30) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { GTEST_SKIP() << "Test for Prov 3.0 devices only."; } @@ -1574,6 +1650,12 @@ TEST_F( TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncMessageKeyOffsetProv30) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { GTEST_SKIP() << "Test for Prov 3.0 devices only."; } @@ -1594,6 +1676,12 @@ TEST_F( TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryPrepareProvisioningRequestForHugeRequestMessageLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestPrepareProvisioningRequestForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_message_size(message_size); @@ -1603,6 +1691,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryPrepareProvisioningRequestForHugeSignatureLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestPrepareProvisioningRequestForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_request_signature_size(message_size); @@ -1612,6 +1706,12 @@ TEST_F(OEMCryptoLoadsCertificate, TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryPrepareProvisioningRequestForHugeCoreMessageLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestPrepareProvisioningRequestForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_core_message_size(message_size); @@ -1625,4 +1725,90 @@ TEST_F(OEMCryptoLoadsCertificate, /// @{ /// @} + +#ifdef CAS_TEST + +#include "tuner_hal.h" + +class OEMCryptoCasDemoTest : public OEMCryptoEntitlementLicenseTest {}; + +TEST_P(OEMCryptoCasDemoTest, BasicFlow) { + // License contains entitlement keys, function reused from + // OEMCryptoEntitlementLicenseTest + LoadEntitlementLicense(); + uint32_t key_session_id = 0; + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( + session_.session_id(), &key_session_id)); + + EntitledMessage entitled_message(&license_messages_); + + // Randomly generate entitled content keys + entitled_message.FillKeyArray(); + if (session_.session_id() == key_session_id) { + GTEST_SKIP() + << "Skipping test because entitled and entitlement sessions are both " + << key_session_id << "."; + } + entitled_message.SetEntitledKeySession(key_session_id); + + // Encrypt and load 0th key (even key) into OEMCrypto + ASSERT_NO_FATAL_FAILURE(entitled_message.LoadCasKeys( + /*load_even=*/true, /*load_odd=*/false, OEMCrypto_SUCCESS)); + + // + // Perform DecryptCTR() but for CAS + // + vector unencrypted_data(256, 0); + vector encrypted_data(256, 0); + vector output_buffer(256, 0); + unencrypted_data.resize(encrypted_data.size()); + output_buffer.resize(encrypted_data.size()); + + OEMCrypto_SampleDescription sample_description; + OEMCrypto_SubSampleDescription subsample_description; + GenerateSimpleSampleDescription(encrypted_data, output_buffer, + &sample_description, &subsample_description); + + // Use 0th entitled content key and IV to encrypt test data + EncryptCTR(unencrypted_data, + entitled_message.entitled_key_data()->content_key_data, + entitled_message.entitled_key_data()->content_iv, &encrypted_data); + + // Assume 0,0 pattern for CTR example + OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; + + // Demo only -- copy IV into sample description so we can use + // WTPI_DecryptSample() in the Tuner decrypt impl. A real implementation would + // use the IV from the entitled content key, but the demo relies on the + // existing decrypt which uses SampleDescription IV. + memcpy(sample_description.iv, + entitled_message.entitled_key_data()->content_iv, 16); + + // Get key token to send to Tuner for decrypt + std::vector key_token; + size_t key_token_length = key_token.size(); + OEMCryptoResult res = OEMCrypto_GetOEMKeyToken( + key_session_id, key_token.data(), &key_token_length); + if (res == OEMCrypto_ERROR_SHORT_BUFFER) { + key_token.resize(key_token_length); + res = OEMCrypto_GetOEMKeyToken(key_session_id, key_token.data(), + &key_token_length); + } + ASSERT_EQ(OEMCrypto_SUCCESS, res); + + // Decrypt the data + ASSERT_EQ(TUNER_HAL_SUCCESS, + TunerHal_Decrypt(key_token.data(), key_token_length, + TunerHal_KeyParityType_EvenKey, + &sample_description, // an array of samples. + 1, // the number of samples. + &pattern)); + + ASSERT_EQ(unencrypted_data, output_buffer); +} + +INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoCasDemoTest, + Range(kCoreMessagesAPI, kCurrentAPI + 1)); + +#endif } // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test_android.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test_android.cpp index 1b58771a..4337f756 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test_android.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test_android.cpp @@ -122,9 +122,8 @@ TEST_F(OEMCryptoAndroidMNCTest, MinVersionNumber10) { // Android devices using Provisioning 2.0 must be able to load a test keybox. // If they are not using Provisioning 2.0, then they must use Provisioning 3.0. TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) { - if (global_features.provisioning_method != OEMCrypto_Keybox && - global_features.provisioning_method != OEMCrypto_OEMCertificate) { - GTEST_SKIP() << "Test for Prov 2.0 and 3.0 devices only."; + if (global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; } if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { ASSERT_EQ( diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_usage_table_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_usage_table_test.cpp index 77c62400..ff15b240 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_usage_table_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_usage_table_test.cpp @@ -440,6 +440,9 @@ TEST_P(OEMCryptoUsageTableTest, LoadEntryInMultipleSessions) { // Test generic encrypt when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) { + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); @@ -479,6 +482,9 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) { // Test generic decrypt when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoDecrypt) { + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); @@ -516,6 +522,9 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoDecrypt) { // Test generic sign when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoSign) { + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); @@ -565,6 +574,9 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoSign) { // Test generic verify when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoVerify) { + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_usage_table_test.h b/libwvdrmengine/oemcrypto/test/oemcrypto_usage_table_test.h index ce5d7ab9..b36404a5 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_usage_table_test.h +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_usage_table_test.h @@ -28,6 +28,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest { void SetUp() override { OEMCryptoRefreshTest::SetUp(); + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE( license_messages_.CreateResponseWithGenericCryptoKeys());