diff --git a/libwvdrmengine/Android.bp b/libwvdrmengine/Android.bp index d9bdc2d6..8d3931f0 100644 --- a/libwvdrmengine/Android.bp +++ b/libwvdrmengine/Android.bp @@ -230,6 +230,14 @@ cc_library_static { min_sdk_version: "34", } +filegroup { + name:"libwvdrmengine_src", + srcs: [ + "src/WVCDMSingleton.cpp", + "src/Utils.cpp", + ], +} + // ---------------------------------------------------------------------------- // Builds libwvaidl.so // diff --git a/libwvdrmengine/build_all_unit_tests.sh b/libwvdrmengine/build_all_unit_tests.sh index f1f3be5f..7db340b4 100755 --- a/libwvdrmengine/build_all_unit_tests.sh +++ b/libwvdrmengine/build_all_unit_tests.sh @@ -39,6 +39,7 @@ WV_UNITTESTS="base64_test \ cdm_usage_table_unittest \ certificate_provisioning_unittest \ counter_metric_unittest \ + core_integration_test \ crypto_session_unittest \ device_files_unittest \ distribution_unittest \ @@ -71,7 +72,8 @@ WV_UNITTESTS="base64_test \ system_id_extractor_unittest \ timer_unittest \ value_metric_unittest \ - wv_cdm_metrics_test" + wv_cdm_metrics_test \ + wv_plugin_test" cd $ANDROID_BUILD_TOP pwd diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index 9526d8de..304c41ff 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -150,7 +150,12 @@ CdmResponseType CertificateProvisioning::SetSpoidParameter( return status; } request->set_stable_id(device_unique_id + origin); - } // No else clause, by design. It is valid to do nothing. + } else { + // It is valid to do nothing for legacy devices. For most recently + // launched devices this is an error. For now, we will log + // but not return an error. + LOGE("No spoid/provider id/stable id set"); + } return CdmResponseType(NO_ERROR); } @@ -378,9 +383,8 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal( // Since |stored_oem_cert| is empty, the client identification token will be // retrieved from OEMCrypto, which is the BCC in this case. - status = FillEncryptedClientIdWithAdditionalParameter( - stored_oem_cert, additional_parameter, provisioning_request, - wv_service_cert); + status = FillEncryptedClientId(stored_oem_cert, provisioning_request, + wv_service_cert); if (status != NO_ERROR) return status; } else { // This is the second stage provisioning. diff --git a/libwvdrmengine/cdm/core/src/client_identification.cpp b/libwvdrmengine/cdm/core/src/client_identification.cpp index 0bc07bf4..b3f74b56 100644 --- a/libwvdrmengine/cdm/core/src/client_identification.cpp +++ b/libwvdrmengine/cdm/core/src/client_identification.cpp @@ -150,7 +150,8 @@ CdmResponseType ClientIdentification::Prepare( } ClientIdentification_NameValue* client_info; - if (is_license_request_) { + // Include app parameters for license and provisioning requests + if (!is_okp_request_) { CdmAppParameterMap::const_iterator iter; for (iter = app_parameters.begin(); iter != app_parameters.end(); ++iter) { if (IsPropertyKeyReserved(iter->first)) { diff --git a/libwvdrmengine/cdm/core/test/core_integration_test.cpp b/libwvdrmengine/cdm/core/test/core_integration_test.cpp new file mode 100644 index 00000000..1b847918 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/core_integration_test.cpp @@ -0,0 +1,237 @@ +// 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 "certificate_provisioning.h" +#include "log.h" +#include "provisioning_holder.h" +#include "test_base.h" +#include "wv_cdm_types.h" + +static const std::string kTestAppPackageName1 = "test_app_package_1"; +static const std::string kTestAppPackageName2 = "test_app_package_2"; +static const std::string kTestOrigin1 = "test_origin_1"; +static const std::string kTestOrigin2 = "test_origin_2"; + +namespace wvcdm { + +class CoreIntegrationTest : public WvCdmTestBaseWithEngine { + public: + void SetUp() override { + WvCdmTestBase::SetUp(); + EnsureProvisioned(); + } + + using WvCdmTestBase::Provision; + virtual bool Provision(const std::string app_package_name, std::string* spoid, + std::string* drm_certificate_serial_number) { + if (spoid == nullptr) { + ADD_FAILURE() << "|spoid| not provided"; + return false; + } + + if (drm_certificate_serial_number == nullptr) { + ADD_FAILURE() << "|drm_certificate_serial_number| not provided"; + return false; + } + + std::string device_id; + CdmResponseType sts = + cdm_engine_.QueryStatus(kLevelDefault, QUERY_KEY_DEVICE_ID, &device_id); + + if (!sts.IsOk()) { + ADD_FAILURE() << "Query Device ID failed: " << sts.code(); + return false; + } + + std::string value; + sts = cdm_engine_.QueryStatus(kLevelDefault, QUERY_KEY_SYSTEM_ID, &value); + + if (!sts.IsOk()) { + ADD_FAILURE() << "Query System ID failed: " << sts.code(); + return false; + } + + long val = std::strtol(value.c_str(), nullptr, 10); + if (val <= 0 || val > std::numeric_limits::max()) { + ADD_FAILURE() << "System ID invalid: " << val; + return false; + } + + uint32_t system_id = static_cast(val); + + *spoid = device_id + app_package_name; + cdm_engine_.SetSpoid(*spoid); + + ProvisioningHolder provisioner(&cdm_engine_, config_.provisioning_server(), + config_.provisioning_service_certificate()); + if (system_id == wvcdm::NULL_SYSTEM_ID) { + // Either first stage prov 4.0 or OKP + provisioner.Provision(binary_provisioning_); + } + provisioner.Provision(binary_provisioning_); + + if (!GetDrmCertificateSerialNumber(provisioner.response(), + drm_certificate_serial_number)) { + ADD_FAILURE() << "Failed to get DRM Certificate Serial Number: " + << sts.code(); + return false; + } + + return true; + } + + virtual bool Provision(const std::string app_package_name, + const std::string origin, std::string* spoid, + std::string* drm_certificate_serial_number) { + std::string name = app_package_name + origin; + return Provision(name, spoid, drm_certificate_serial_number); + } + + private: + bool GetDrmCertificateSerialNumber( + const wvcdm::CdmProvisioningResponse& response, + std::string* serial_number) { + if (serial_number == nullptr) { + ADD_FAILURE() << "|serial_number| not provided"; + return false; + } + + wvcdm::CdmProvisioningResponse provisioning_response; + if (!wvcdm::CertificateProvisioning::ExtractAndDecodeSignedMessage( + response, &provisioning_response)) { + ADD_FAILURE() << "Failed to decode signed provisioning message"; + return false; + } + + video_widevine::SignedProvisioningMessage signed_response; + if (!signed_response.ParseFromString(provisioning_response) || + !signed_response.has_message()) { + ADD_FAILURE() << "Failed to parse signed provisioning response"; + return false; + } + + video_widevine::ProvisioningResponse prov_response; + if (!prov_response.ParseFromString(signed_response.message()) || + !prov_response.has_device_certificate()) { + ADD_FAILURE() << "Failed to parse provisioning response"; + return false; + } + + if (!wvcdm::CertificateProvisioning::ExtractDeviceInfo( + prov_response.device_certificate(), serial_number, nullptr, nullptr, + nullptr)) { + ADD_FAILURE() << "Failed to extract device information"; + return false; + } + + return true; + } +}; + +// Verify that SPOIDs and DRM certificate serial number are stable between +// factory resets/provisioning attempts for the same app and different between +// different apps. Test using two different apps and origins. +TEST_F(CoreIntegrationTest, ProvisioningStableSpoidTest) { + std::string level; + ASSERT_EQ( + NO_ERROR, + cdm_engine_.QueryStatus(kLevelDefault, QUERY_KEY_SECURITY_LEVEL, &level) + .code()); + + ASSERT_TRUE(level == QUERY_VALUE_SECURITY_LEVEL_L1 || + level == QUERY_VALUE_SECURITY_LEVEL_L3) + << "Unknown security level: " << level; + + CdmSecurityLevel security_level = level == QUERY_VALUE_SECURITY_LEVEL_L1 + ? kSecurityLevelL1 + : kSecurityLevelL3; + + // App 1, first provisioning attempt + std::string spoid_app_1[2]; + std::string drm_cert_serial_number_app_1[2]; + ASSERT_TRUE(Provision(kTestAppPackageName1, &spoid_app_1[0], + &drm_cert_serial_number_app_1[0])); + + cdm_engine_.Unprovision(security_level); + + // second provisioning attempt + ASSERT_TRUE(Provision(kTestAppPackageName1, &spoid_app_1[1], + &drm_cert_serial_number_app_1[1])); + + // App 2, first provisioning attempt + std::string spoid_app_2[2]; + std::string drm_cert_serial_number_app_package_name_2[2]; + ASSERT_TRUE(Provision(kTestAppPackageName2, &spoid_app_2[0], + &drm_cert_serial_number_app_package_name_2[0])); + + cdm_engine_.Unprovision(security_level); + + // second provisioning attempt + ASSERT_TRUE(Provision(kTestAppPackageName2, &spoid_app_2[1], + &drm_cert_serial_number_app_package_name_2[1])); + + // App 1 and Origin 1, first provisioning attempt + std::string spoid_app_1_origin_1[2]; + std::string drm_cert_serial_number_app_1_origin_1[2]; + ASSERT_TRUE(Provision(kTestAppPackageName1, kTestOrigin1, + &spoid_app_1_origin_1[0], + &drm_cert_serial_number_app_1_origin_1[0])); + + cdm_engine_.Unprovision(security_level); + + // second provisioning attempt + ASSERT_TRUE(Provision(kTestAppPackageName1, kTestOrigin1, + &spoid_app_1_origin_1[1], + &drm_cert_serial_number_app_1_origin_1[1])); + + // App 1 and Origin 2, first provisioning attempt + std::string spoid_app_1_origin_2[2]; + std::string drm_cert_serial_number_app_1_origin_2[2]; + ASSERT_TRUE(Provision(kTestAppPackageName1, kTestOrigin2, + &spoid_app_1_origin_2[0], + &drm_cert_serial_number_app_1_origin_2[0])); + + cdm_engine_.Unprovision(security_level); + + // second provisioning attempt + ASSERT_TRUE(Provision(kTestAppPackageName1, kTestOrigin2, + &spoid_app_1_origin_2[1], + &drm_cert_serial_number_app_1_origin_2[1])); + + // Verify that SPOID and DRM cert are stable + ASSERT_EQ(spoid_app_1[0], spoid_app_1[1]); + ASSERT_EQ(spoid_app_2[0], spoid_app_2[1]); + ASSERT_EQ(spoid_app_1_origin_1[0], spoid_app_1_origin_1[1]); + ASSERT_EQ(spoid_app_1_origin_2[0], spoid_app_1_origin_2[1]); + + ASSERT_EQ(drm_cert_serial_number_app_1[0], drm_cert_serial_number_app_1[1]); + ASSERT_EQ(drm_cert_serial_number_app_package_name_2[0], + drm_cert_serial_number_app_package_name_2[1]); + ASSERT_EQ(drm_cert_serial_number_app_1_origin_1[0], + drm_cert_serial_number_app_1_origin_1[1]); + ASSERT_EQ(drm_cert_serial_number_app_1_origin_2[0], + drm_cert_serial_number_app_1_origin_2[1]); + + // Verify that SPOID and DRM cert do not match different apps/origins + ASSERT_NE(spoid_app_1[0], spoid_app_2[0]); + ASSERT_NE(spoid_app_1[0], spoid_app_1_origin_1[0]); + ASSERT_NE(spoid_app_1[0], spoid_app_1_origin_2[0]); + ASSERT_NE(spoid_app_2[0], spoid_app_1_origin_1[0]); + ASSERT_NE(spoid_app_2[0], spoid_app_1_origin_2[0]); + ASSERT_NE(spoid_app_1_origin_1[0], spoid_app_1_origin_2[0]); + + ASSERT_NE(drm_cert_serial_number_app_1[0], + drm_cert_serial_number_app_package_name_2[0]); + ASSERT_NE(drm_cert_serial_number_app_1[0], + drm_cert_serial_number_app_1_origin_1[0]); + ASSERT_NE(drm_cert_serial_number_app_1[0], + drm_cert_serial_number_app_1_origin_2[0]); + ASSERT_NE(drm_cert_serial_number_app_package_name_2[0], + drm_cert_serial_number_app_1_origin_1[0]); + ASSERT_NE(drm_cert_serial_number_app_package_name_2[0], + drm_cert_serial_number_app_1_origin_2[0]); + ASSERT_NE(drm_cert_serial_number_app_1_origin_1[0], + drm_cert_serial_number_app_1_origin_2[0]); +} +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/provisioning_holder.cpp b/libwvdrmengine/cdm/core/test/provisioning_holder.cpp index 8155d61f..8cd7fa81 100644 --- a/libwvdrmengine/cdm/core/test/provisioning_holder.cpp +++ b/libwvdrmengine/cdm/core/test/provisioning_holder.cpp @@ -62,17 +62,16 @@ void ProvisioningHolder::Provision(CdmCertificateType cert_type, url_request.PostCertRequestInQueryString(request); // Receive and parse response. - std::string response; - ASSERT_NO_FATAL_FAILURE(url_request.AssertOkResponse(&response)) + ASSERT_NO_FATAL_FAILURE(url_request.AssertOkResponse(&response_)) << "Failed to fetch provisioning response. " - << DumpProvAttempt(request, response, cert_type); + << DumpProvAttempt(request, response_, cert_type); if (binary_provisioning) { // extract provisioning response from received message // Extracts signed response from JSON string, result is serialized // protobuf. std::string protobuf_response; - const bool extract_ok = ExtractSignedMessage(response, &protobuf_response); + const bool extract_ok = ExtractSignedMessage(response_, &protobuf_response); ASSERT_TRUE(extract_ok) << "Failed to extract signed serialized " "response from JSON response"; LOGV("Extracted response message: \n%s\n", protobuf_response.c_str()); @@ -87,21 +86,15 @@ void ProvisioningHolder::Provision(CdmCertificateType cert_type, << "Failed to decode base64 of response: response = " << protobuf_response; - const std::string binary_protobuf_response(response_vec.begin(), - response_vec.end()); - - ASSERT_EQ(NO_ERROR, cdm_engine_->HandleProvisioningResponse( - binary_protobuf_response, kLevelDefault, - &certificate_, &wrapped_key_)) - << "Binary provisioning failed. " - << DumpProvAttempt(request, response, cert_type); - } else { - ASSERT_EQ(NO_ERROR, - cdm_engine_->HandleProvisioningResponse( - response, kLevelDefault, &certificate_, &wrapped_key_)) - << "Non-binary provisioning failed. " - << DumpProvAttempt(request, response, cert_type); + response_.assign(response_vec.begin(), response_vec.end()); } + + ASSERT_EQ(NO_ERROR, + cdm_engine_->HandleProvisioningResponse( + response_, kLevelDefault, &certificate_, &wrapped_key_)) + << (binary_provisioning ? "Binary provisioning failed. " + : "Non-binary provisioning failed. ") + << DumpProvAttempt(request, response_, cert_type); } bool ProvisioningHolder::ExtractSignedMessage(const std::string& response, diff --git a/libwvdrmengine/cdm/core/test/provisioning_holder.h b/libwvdrmengine/cdm/core/test/provisioning_holder.h index 073b46a8..fe5acd2d 100644 --- a/libwvdrmengine/cdm/core/test/provisioning_holder.h +++ b/libwvdrmengine/cdm/core/test/provisioning_holder.h @@ -20,6 +20,10 @@ class ProvisioningHolder { provisioning_server_url_(provisioning_server_url), provisioning_service_certificate_(provisioning_service_certificate) {} void Provision(CdmCertificateType cert_type, bool binary_provisioning); + void Provision(bool binary_provisioning) { + Provision(kCertificateWidevine, binary_provisioning); + } + std::string response() const { return response_; } std::string certificate() const { return certificate_; } std::string wrapped_key() const { return wrapped_key_; } @@ -27,6 +31,7 @@ class ProvisioningHolder { TestCdmEngine* cdm_engine_; std::string provisioning_server_url_; std::string provisioning_service_certificate_; + std::string response_; std::string certificate_; std::string wrapped_key_; diff --git a/libwvdrmengine/cdm/test/Android.mk b/libwvdrmengine/cdm/test/Android.mk index 017d9076..965bd121 100644 --- a/libwvdrmengine/cdm/test/Android.mk +++ b/libwvdrmengine/cdm/test/Android.mk @@ -58,6 +58,11 @@ include $(LOCAL_PATH)/integration-test.mk # coverage-test.mk will define test name. include $(LOCAL_PATH)/coverage-test.mk +test_name := core_integration_test +test_src_dir := ../core/test +test_main := ../core/test/test_main.cpp +include $(LOCAL_PATH)/integration-test.mk + test_name := counter_metric_unittest test_src_dir := ../metrics/test include $(LOCAL_PATH)/unit-test.mk diff --git a/libwvdrmengine/run_all_unit_tests.sh b/libwvdrmengine/run_all_unit_tests.sh index 999579a4..c6364158 100755 --- a/libwvdrmengine/run_all_unit_tests.sh +++ b/libwvdrmengine/run_all_unit_tests.sh @@ -101,6 +101,7 @@ adb_shell_run cdm_engine_test adb_shell_run cdm_session_unittest adb_shell_run cdm_usage_table_unittest adb_shell_run certificate_provisioning_unittest +adb_shell_run core_integration_test adb_shell_run counter_metric_unittest adb_shell_run crypto_session_unittest adb_shell_run device_files_unittest @@ -128,3 +129,4 @@ adb_shell_run system_id_extractor_unittest adb_shell_run timer_unittest adb_shell_run value_metric_unittest adb_shell_run wv_cdm_metrics_test +adb_shell_run wv_plugin_test $PROVISIONING_ARG diff --git a/libwvdrmengine/test/Android.bp b/libwvdrmengine/test/Android.bp new file mode 100644 index 00000000..328b23ae --- /dev/null +++ b/libwvdrmengine/test/Android.bp @@ -0,0 +1,76 @@ +// Copyright 2020 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine +// License Agreement. + +// *** THIS PACKAGE HAS SPECIAL LICENSING CONDITIONS. PLEASE +// CONSULT THE OWNERS AND opensource-licensing@google.com BEFORE +// DEPENDING ON IT IN YOUR PROJECT. *** +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "vendor_widevine_license" + // to get the below license kinds: + // legacy_by_exception_only (by exception only) + // legacy_proprietary (by exception only) + default_applicable_licenses: ["vendor_widevine_license"], +} + +cc_test { + name: "wv_plugin_test", + test_suites: ["device-tests"], + gtest: true, + + srcs: [ + "WVPlugin_test.cpp", + ":libwvdrmengine_src", + ":vts_cdm_core_test_srcs", + ], + include_dirs: [ + "frameworks/av/include", + "frameworks/native/include", + "vendor/widevine/libwvdrmengine/cdm/core/include", + "vendor/widevine/libwvdrmengine/cdm/core/test", + "vendor/widevine/libwvdrmengine/cdm/include", + "vendor/widevine/libwvdrmengine/cdm/metrics/include/", + "vendor/widevine/libwvdrmengine/cdm/util/include", + "vendor/widevine/libwvdrmengine/include", + "vendor/widevine/libwvdrmengine/mediadrm/include", + "vendor/widevine/libwvdrmengine/oemcrypto/include", + ], + header_libs: [ + "libstagefright_foundation_headers", + "libutils_headers", + ], + static_libs: [ + "libcdm", + "libcdm_protos", + "libcdm_utils", + "libgmock", + "libgtest", + "libgtest_main", + "libjsmn", + "libjsoncpp", + "libprotobuf-cpp-lite", + "libutils", + "libwv_odk", + "libwvdrmdrmplugin_aidl", + "libwvlevel3", + ], + shared_libs: [ + "android.hardware.drm-V1-ndk", + "libbase", + "libbinder_ndk", + "libcrypto", + "libdl", + "liblog", + "libssl", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + + proprietary: true, + owner: "widevine", +} diff --git a/libwvdrmengine/test/WVPlugin_test.cpp b/libwvdrmengine/test/WVPlugin_test.cpp new file mode 100644 index 00000000..8bf11116 --- /dev/null +++ b/libwvdrmengine/test/WVPlugin_test.cpp @@ -0,0 +1,312 @@ +// 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 "utils/StrongPointer.h" +#include + +#include +#include + +#include "WVCDMSingleton.h" +#include "WVDrmPlugin.h" +#include "WVGenericCryptoInterface.h" +#include "certificate_provisioning.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "string_conversions.h" +#include "url_request.h" +#include "wv_cdm_constants.h" +#include "wv_cdm_types.h" +#include "wv_content_decryption_module.h" + +namespace { +static const std::string kDeviceUniqueId = "deviceUniqueId"; +static const std::string kSecurityLevel = "securityLevel"; +static const std::string kSystemId = "systemId"; +static const std::string kTestAppPackageName = "test_app_package"; +static const std::string kTestAppPackageName1 = "test_app_package_1"; +static const std::string kTestAppPackageName2 = "test_app_package_2"; +static const std::string kTestAppPackageName3 = "test_app_package_3"; +static const char* const kUnprovisionResponse = "unprovision"; + +// HTTP response codes. +const int kHttpOk = 200; +} // namespace + +namespace wvdrm { +namespace hardware { +namespace drm { +namespace widevine { + +class WVPluginTest : public testing::Test { + + public : + virtual void SetUp() { + mDrmPlugin = ::ndk::SharedRefBase::make( + getCDM(), kTestAppPackageName, &mOemCryptoInterface, true); + } + + std::shared_ptr createDrmPlugin( + const std::string& app_package_name) { + return ::ndk::SharedRefBase::make( + getCDM(), app_package_name, &mOemCryptoInterface, true); + } + + // This will create a provisioning request, send it to the service and + // process the response. The provisioning response will be passed back + // if needed for inspection. If first stage (for provisioning 4.0) + // or OKP is needed, the first roundtrip will be completed + // silently. The method will return after the second round trip. + // Verify the return value in case of errors. + bool ensureProvisioned( + std::shared_ptr& plugin, + wvcdm::CdmProvisioningResponse* provisioning_response) { + ::aidl::android::hardware::drm::ProvisionRequest request; + + std::string value; + auto status = + mDrmPlugin->getPropertyString(kSystemId, &value); + EXPECT_TRUE(status.isOk()) << "Failed to get System Id"; + + long val = std::strtol(value.c_str(), nullptr, 10); + EXPECT_NE(val, 0) << "System ID invalid: " << errno; + EXPECT_NE(val, LONG_MIN) << "System ID invalid: " << errno; + EXPECT_NE(val, LONG_MAX) << "System ID invalid: " << errno; + + uint32_t system_id = val; + + if (system_id == wvcdm::NULL_SYSTEM_ID) { + // Either first stage prov 4.0 or OKP + bool result = provision(plugin, provisioning_response); + if (!result) + return false; + } + + return provision(plugin, provisioning_response); + } + + void unprovision(const std::shared_ptr& plugin) { + std::vector unprovisionResponse; + unprovisionResponse + .assign(kUnprovisionResponse, + kUnprovisionResponse + strlen(kUnprovisionResponse)); + ::aidl::android::hardware::drm::ProvideProvisionResponseResult result; + auto status = + plugin->provideProvisionResponse(unprovisionResponse, &result); + ASSERT_TRUE(status.isOk()) << "Failed to unprovision"; + } + + bool isL1() { + std::string security_level; + auto status = + mDrmPlugin->getPropertyString(kSecurityLevel, &security_level); + EXPECT_TRUE(status.isOk()) << "Failed to get Security level"; + return security_level == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1; + } + + std::string getDeviceUniqueId(std::shared_ptr& plugin) { + std::vector device_unique_id; + auto status = + plugin->getPropertyByteArray(kDeviceUniqueId, &device_unique_id); + EXPECT_TRUE(status.isOk()) << "Failed to get Device Unique ID"; + return std::string(device_unique_id.begin(), device_unique_id.end()); + } + + bool getSerialNumber(const wvcdm::CdmProvisioningResponse& response, + std::string* serial_number) { + if (serial_number == nullptr) { + ADD_FAILURE(); + return false; + } + + wvcdm::CdmProvisioningResponse provisioning_response; + if (!wvcdm::CertificateProvisioning::ExtractAndDecodeSignedMessage( + response, &provisioning_response)) { + ADD_FAILURE(); + return false; + } + + video_widevine::SignedProvisioningMessage signed_response; + if (!signed_response.ParseFromString(provisioning_response) || + !signed_response.has_message()) { + ADD_FAILURE(); + return false; + } + + video_widevine::ProvisioningResponse prov_response; + if (!prov_response.ParseFromString(signed_response.message()) || + !prov_response.has_device_certificate()) { + ADD_FAILURE(); + return false; + } + + if (!wvcdm::CertificateProvisioning::ExtractDeviceInfo( + prov_response.device_certificate(), serial_number, nullptr, nullptr, + nullptr)) { + ADD_FAILURE(); + return false; + } + + return true; + } + + private: + // Create a Provisioning request, send it to the service and process the + // response. Pass back the provisioning response if needed for inspection + bool provision( + std::shared_ptr& plugin, + wvcdm::CdmProvisioningResponse* provisioning_response) { + + ::aidl::android::hardware::drm::ProvisionRequest request; + auto status = + plugin->getProvisionRequest(std::string(""), std::string(""), &request); + EXPECT_TRUE(status.isOk()) << "Failed to get Provisioning request"; + if (!status.isOk()) return false; + + wvcdm::CdmProvisioningRequest + provisioning_request(request.request.begin(), request.request.end()); + bool sts = GetProvisioningResponse(provisioning_request, + request.defaultUrl, + provisioning_response); + if (!sts) { + EXPECT_TRUE(sts) << "Failed to get provisioning response"; + return false; + } + + wvcdm::CdmProvisioningResponse prov_response; + + std::vector response(provisioning_response->begin(), + provisioning_response->end()); + ::aidl::android::hardware::drm::ProvideProvisionResponseResult result; + status = plugin->provideProvisionResponse(response, &result); + EXPECT_TRUE(status.isOk()) << "Failed to process provisioning response"; + if (!status.isOk()) return false; + return true; + } + + // Post a request and extract the signed provisioning message from + // the HTTP response. + // Setting a non-zero value to |duration_seconds| will request a + // limited duration DRM certificate. + bool GetProvisioningResponse(const wvcdm::CdmProvisioningRequest& request, + const std::string& url, + wvcdm::CdmProvisioningResponse* response) { + EXPECT_NE(response, nullptr) << "No |response| parameter provided"; + if (response == nullptr) + return false; + + // Use secure connection and chunk transfer coding. + wvcdm::UrlRequest url_request(url); + EXPECT_TRUE(url_request.is_connected()) << "Fail to connect to " << url; + url_request.PostCertRequestInQueryString(request); + EXPECT_TRUE(url_request.GetResponse(response)); + + int http_status_code = url_request.GetStatusCode(*response); + EXPECT_EQ(kHttpOk, http_status_code) << "provisioning_response = " + << *response; + if (kHttpOk != http_status_code) { + LogResponseError(*response, http_status_code); + return false; + } + return true; + } + + void LogResponseError(const std::string& message, int http_status_code) { + LOGD("HTTP Status code = %d", http_status_code); + LOGD("HTTP response(%zu): %s", message.size(), + wvutil::b2a_hex(message).c_str()); + } + + private: + std::shared_ptr mDrmPlugin; + wvdrm::WVGenericCryptoInterface mOemCryptoInterface; +}; + +// Verify that SPOIDs and DRM certificate serial number are stable between +// factory resets/provisioning attempts for the same app and different between +// different apps. Test using three different apps. +TEST_F(WVPluginTest, ProvisioningStableSpoidTestL1) { + std::shared_ptr drm_plugin1 = + createDrmPlugin(kTestAppPackageName1); + + if (!isL1()) // declare success if device does not natively support L1 + return; + + wvcdm::CdmProvisioningResponse provisioning_response; + + unprovision(drm_plugin1); + std::string spoid1_1 = getDeviceUniqueId(drm_plugin1); + ASSERT_TRUE(ensureProvisioned(drm_plugin1, &provisioning_response)); + std::string drm_cert_serial_number1_1; + ASSERT_TRUE(getSerialNumber(provisioning_response, + &drm_cert_serial_number1_1)); + + unprovision(drm_plugin1); + std::string spoid1_2 = getDeviceUniqueId(drm_plugin1); + ASSERT_TRUE(ensureProvisioned(drm_plugin1, &provisioning_response)); + std::string drm_cert_serial_number1_2; + ASSERT_TRUE(getSerialNumber(provisioning_response, + &drm_cert_serial_number1_2)); + + std::shared_ptr drm_plugin2 = + createDrmPlugin(kTestAppPackageName2); + + unprovision(drm_plugin2); + std::string spoid2_1 = getDeviceUniqueId(drm_plugin2); + ASSERT_TRUE(ensureProvisioned(drm_plugin2, &provisioning_response)); + std::string drm_cert_serial_number2_1; + ASSERT_TRUE(getSerialNumber(provisioning_response, + &drm_cert_serial_number2_1)); + + unprovision(drm_plugin2); + std::string spoid2_2 = getDeviceUniqueId(drm_plugin2); + ASSERT_TRUE(ensureProvisioned(drm_plugin2, &provisioning_response)); + std::string drm_cert_serial_number2_2; + ASSERT_TRUE(getSerialNumber(provisioning_response, + &drm_cert_serial_number2_2)); + + std::shared_ptr drm_plugin3 = + createDrmPlugin(kTestAppPackageName3); + + unprovision(drm_plugin3); + std::string spoid3_1 = getDeviceUniqueId(drm_plugin3); + ASSERT_TRUE(ensureProvisioned(drm_plugin3, &provisioning_response)); + std::string drm_cert_serial_number3_1; + ASSERT_TRUE(getSerialNumber(provisioning_response, + &drm_cert_serial_number3_1)); + + unprovision(drm_plugin3); + std::string spoid3_2 = getDeviceUniqueId(drm_plugin3); + ASSERT_TRUE(ensureProvisioned(drm_plugin3, &provisioning_response)); + std::string drm_cert_serial_number3_2; + ASSERT_TRUE(getSerialNumber(provisioning_response, + &drm_cert_serial_number3_2)); + + // Verify that the spoids are stable after a reprovision + EXPECT_EQ(spoid1_1, spoid1_2); + EXPECT_EQ(spoid2_1, spoid2_2); + EXPECT_EQ(spoid3_1, spoid3_2); + + // Verify that the spoids are different for different apps + EXPECT_NE(spoid1_1, spoid2_1); + EXPECT_NE(spoid1_1, spoid3_1); + EXPECT_NE(spoid2_1, spoid3_1); + + // Verify that the DRM certificate serial numbers are stable after a + // reprovision + EXPECT_EQ(drm_cert_serial_number1_1, drm_cert_serial_number1_2); + EXPECT_EQ(drm_cert_serial_number2_1, drm_cert_serial_number2_2); + EXPECT_EQ(drm_cert_serial_number3_1, drm_cert_serial_number3_2); + + // Verify that the DRM certificate serial numbers are different for different + // apps + EXPECT_NE(drm_cert_serial_number1_1, drm_cert_serial_number2_1); + EXPECT_NE(drm_cert_serial_number1_1, drm_cert_serial_number3_1); + EXPECT_NE(drm_cert_serial_number2_1, drm_cert_serial_number3_1); +} + +} // namespace widevine +} // namespace drm +} // namespace hardware +} // namespace wvdrm