From ee5aff7706ed191c1ab9f061dd5b241556c08938 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Fri, 27 Jan 2017 02:44:38 -0800 Subject: [PATCH] Correct setting of service certificate. [ Merge of http://go/wvgerrit/23380 ] The service certificate was setup correctly if specified in mediadrm properties. If instead the service certificate was later fetched from the license service, it would not be marked as valid. This led to an infinite loop of service certificate fetches and processing. This prevented the license from being fetched and playback failures. b/34638410 Test: Verified by new service certificate unittests + Hulu playback using fugu. Change-Id: I2a4f8754614fccdad3c80d3e13fba0b44d177d61 --- .../build_and_run_all_unit_tests.sh | 1 + .../cdm/core/src/service_certificate.cpp | 27 +-- .../test/service_certificate_unittest.cpp | 176 ++++++++++++++++++ libwvdrmengine/cdm/test/Android.mk | 4 + libwvdrmengine/run_all_unit_tests.sh | 1 + 5 files changed, 198 insertions(+), 11 deletions(-) create mode 100644 libwvdrmengine/cdm/core/test/service_certificate_unittest.cpp diff --git a/libwvdrmengine/build_and_run_all_unit_tests.sh b/libwvdrmengine/build_and_run_all_unit_tests.sh index b9f26aa8..31c0d78a 100755 --- a/libwvdrmengine/build_and_run_all_unit_tests.sh +++ b/libwvdrmengine/build_and_run_all_unit_tests.sh @@ -64,6 +64,7 @@ try_adb_push $OUT/system/bin/license_unittest try_adb_push $OUT/system/bin/license_keys_unittest try_adb_push $OUT/system/bin/initialization_data_unittest try_adb_push $OUT/system/bin/device_files_unittest +try_adb_push $OUT/system/bin/service_certificate_unittest try_adb_push $OUT/system/bin/timer_unittest try_adb_push $OUT/system/bin/libwvdrmengine_test try_adb_push $OUT/system/bin/buffer_reader_test diff --git a/libwvdrmengine/cdm/core/src/service_certificate.cpp b/libwvdrmengine/cdm/core/src/service_certificate.cpp index 18ed3f8e..bc8d9837 100644 --- a/libwvdrmengine/cdm/core/src/service_certificate.cpp +++ b/libwvdrmengine/cdm/core/src/service_certificate.cpp @@ -95,18 +95,20 @@ bool ServiceCertificate::IsAvailable() { CdmResponseType ServiceCertificate::VerifyAndSet( const std::string& signed_service_certificate) { CdmResponseType status; - std::string certificate; bool has_provider_id; status = VerifyAndExtractFromSignedCertificate(signed_service_certificate, - &certificate, &has_provider_id, + &certificate_, + &has_provider_id, NULL); - if (status == NO_ERROR) { - Properties::SetServiceCertificate(session_id_, certificate); - } else { + if (status != NO_ERROR) { LOGE("ServiceCertificate::VerifyAndSet: verify and extract failed with " "status %d", status); + return status; } - return status; + + Properties::SetServiceCertificate(session_id_, signed_service_certificate); + valid_ = true; + return NO_ERROR; } bool ServiceCertificate::PrepareServiceCertificateRequest( @@ -130,7 +132,7 @@ bool ServiceCertificate::PrepareServiceCertificateRequest( CdmResponseType ServiceCertificate::VerifyAndExtractFromSignedCertificate( const std::string& signed_certificate, std::string* certificate, - bool* has_provider_id, std::string* provider_id) { + bool* /* has_provider_id */, std::string* /* provider_id */) { SignedDrmDeviceCertificate signed_service_certificate; if (!signed_service_certificate.ParseFromString(signed_certificate)) { LOGE( @@ -185,7 +187,8 @@ CdmResponseType ServiceCertificate::VerifyAndExtractFromSignedCertificate( } #endif - if (certificate != NULL) { + if (certificate != NULL && + !signed_service_certificate.drm_certificate().empty()) { *certificate = signed_service_certificate.drm_certificate(); } return NO_ERROR; @@ -246,11 +249,16 @@ bool ServiceCertificate::SetupServiceCertificate() { certificate_.clear(); if (!Properties::GetServiceCertificate(session_id_, &signed_certificate)) { + LOGV("ServiceCertificate::SetupServiceCertificate: no signed service " + "certificate set"); return false; } if (signed_certificate.empty()) { + LOGV("ServiceCertificate::SetupServiceCertificate: service certificate " + "empty"); return false; } + std::string extracted_certificate; std::string extracted_provider_id; bool has_provider_id; @@ -259,9 +267,6 @@ bool ServiceCertificate::SetupServiceCertificate() { &has_provider_id, &extracted_provider_id)) { return false; } - if (extracted_certificate.empty()) { - return false; - } has_provider_id_ = has_provider_id; if (has_provider_id_) { provider_id_ = extracted_provider_id; diff --git a/libwvdrmengine/cdm/core/test/service_certificate_unittest.cpp b/libwvdrmengine/cdm/core/test/service_certificate_unittest.cpp new file mode 100644 index 00000000..94549057 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/service_certificate_unittest.cpp @@ -0,0 +1,176 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "service_certificate.h" +#include +#include +#include +#include "crypto_session.h" +#include "properties.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +namespace { + +const CdmSessionId kTestSessionId1 = "sid1"; +const CdmSessionId kTestSessionId2 = "sid2"; +const std::string kAppId = "com.example.test"; + +const std::string kTestSignedCertificate = a2bs_hex( + "0AC102080312101705B917CC1204868B06333A2F772A8C1882B4829205228E023082010A02" + "8201010099ED5B3B327DAB5E24EFC3B62A95B598520AD5BCCB37503E0645B814D876B8DF40" + "510441AD8CE3ADB11BB88C4E725A5E4A9E0795291D58584023A7E1AF0E38A9127939300861" + "0B6F158C878C7E21BFFBFEEA77E1019E1E5781E8A45F46263D14E60E8058A8607ADCE04FAC" + "8457B137A8D67CCDEB33705D983A21FB4EECBD4A10CA47490CA47EAA5D438218DDBAF1CADE" + "3392F13D6FFB6442FD31E1BF40B0C604D1C4BA4C9520A4BF97EEBD60929AFCEEF55BBAF564" + "E2D0E76CD7C55C73A082B996120B8359EDCE24707082680D6F67C6D82C4AC5F3134490A74E" + "EC37AF4B2F010C59E82843E2582F0B6B9F5DB0FC5E6EDF64FBD308B4711BCF1250019C9F5A" + "0902030100013A146C6963656E73652E7769646576696E652E636F6D128003AE347314B5A8" + "35297F271388FB7BB8CB5277D249823CDDD1DA30B93339511EB3CCBDEA04B944B927C12134" + "6EFDBDEAC9D413917E6EC176A10438460A503BC1952B9BA4E4CE0FC4BFC20A9808AAAF4BFC" + "D19C1DCFCDF574CCAC28D1B410416CF9DE8804301CBDB334CAFCD0D40978423A642E54613D" + "F0AFCF96CA4A9249D855E42B3A703EF1767F6A9BD36D6BF82BE76BBF0CBA4FDE59D2ABCC76" + "FEB64247B85C431FBCA52266B619FC36979543FCA9CBBDBBFAFA0E1A55E755A3C7BCE655F9" + "646F582AB9CF70AA08B979F867F63A0B2B7FDB362C5BC4ECD555D85BCAA9C593C383C857D4" + "9DAAB77E40B7851DDFD24998808E35B258E75D78EAC0CA16F7047304C20D93EDE4E8FF1C6F" + "17E6243E3F3DA8FC1709870EC45FBA823A263F0CEFA1F7093B1909928326333705043A29BD" + "A6F9B4342CC8DF543CB1A1182F7C5FFF33F10490FACA5B25360B76015E9C5A06AB8EE02F00" + "D2E8D5986104AACC4DD475FD96EE9CE4E326F21B83C7058577B38732CDDABC6A6BED13FB0D" + "49D38A45EB87A5F4"); + +} // unnamed namespace + +class MockCryptoSession : public CryptoSession { + public: + MOCK_METHOD2(GetRandom, bool(size_t, uint8_t*)); +}; + +class ServiceCertificateTest : public ::testing::Test { + protected: + virtual void SetUp() { crypto_session_ = new MockCryptoSession(); } + + virtual void TearDown() { + if (crypto_session_) delete crypto_session_; + } + + void CreateServiceCertificate() { + service_certificate_ = new ServiceCertificate(); + } + + ServiceCertificate* service_certificate_; + MockCryptoSession* crypto_session_; +}; + +class StubCdmClientPropertySet : public CdmClientPropertySet { + public: + StubCdmClientPropertySet() + : security_level_(QUERY_VALUE_SECURITY_LEVEL_L1), + use_privacy_mode_(false), + is_session_sharing_enabled_(false), + session_sharing_id_(0), + app_id_(kAppId) {} + + virtual const std::string& security_level() const { return security_level_; } + + virtual bool use_privacy_mode() const { return use_privacy_mode_; } + + virtual const std::string& service_certificate() const { + return service_certificate_; + } + + virtual void set_service_certificate(const std::string& cert) { + service_certificate_ = cert; + } + + virtual const std::string& device_provisioning_service_certificate() const { + return device_provisioning_service_certificate_; + } + + virtual void set_device_provisioning_service_certificate( + const std::string& cert) { + device_provisioning_service_certificate_ = cert; + } + virtual bool is_session_sharing_enabled() const { + return is_session_sharing_enabled_; + } + + virtual uint32_t session_sharing_id() const { return session_sharing_id_; } + + virtual void set_session_sharing_id(uint32_t id) { session_sharing_id_ = id; } + + virtual const std::string& app_id() const { return app_id_; } + + void enable_privacy_mode() { use_privacy_mode_ = true; } + + private: + std::string security_level_; + std::string service_certificate_; + std::string device_provisioning_service_certificate_; + bool use_privacy_mode_; + bool is_session_sharing_enabled_; + uint32_t session_sharing_id_; + std::string app_id_; +}; + +TEST_F(ServiceCertificateTest, InitSuccess) { + MockCryptoSession crypto_session; + + CreateServiceCertificate(); + service_certificate_->Init(kTestSessionId1, &crypto_session); + EXPECT_FALSE(service_certificate_->IsRequired()); + EXPECT_FALSE(service_certificate_->IsAvailable()); +} + +TEST_F(ServiceCertificateTest, InitPrivacyModeRequired) { + MockCryptoSession crypto_session; + StubCdmClientPropertySet property_set; + + property_set.enable_privacy_mode(); + + Properties::Init(); + Properties::AddSessionPropertySet(kTestSessionId1, &property_set); + + CreateServiceCertificate(); + service_certificate_->Init(kTestSessionId1, &crypto_session); + EXPECT_TRUE(service_certificate_->IsRequired()); + EXPECT_FALSE(service_certificate_->IsAvailable()); +} + +TEST_F(ServiceCertificateTest, InitServiceCertificatePresent) { + MockCryptoSession crypto_session; + StubCdmClientPropertySet property_set; + + property_set.enable_privacy_mode(); + property_set.set_service_certificate(kTestSignedCertificate); + + Properties::Init(); + Properties::AddSessionPropertySet(kTestSessionId1, &property_set); + + CreateServiceCertificate(); + service_certificate_->Init(kTestSessionId1, &crypto_session); + EXPECT_TRUE(service_certificate_->IsRequired()); + EXPECT_TRUE(service_certificate_->IsAvailable()); +} + +TEST_F(ServiceCertificateTest, SetServiceCertificate) { + MockCryptoSession crypto_session; + StubCdmClientPropertySet property_set; + + property_set.enable_privacy_mode(); + + Properties::Init(); + Properties::AddSessionPropertySet(kTestSessionId1, &property_set); + + CreateServiceCertificate(); + service_certificate_->Init(kTestSessionId1, &crypto_session); + EXPECT_TRUE(service_certificate_->IsRequired()); + EXPECT_FALSE(service_certificate_->IsAvailable()); + + EXPECT_EQ(NO_ERROR, + service_certificate_->VerifyAndSet(kTestSignedCertificate)); + EXPECT_TRUE(service_certificate_->IsRequired()); + EXPECT_TRUE(service_certificate_->IsAvailable()); +} +} diff --git a/libwvdrmengine/cdm/test/Android.mk b/libwvdrmengine/cdm/test/Android.mk index e764770f..b76ab09e 100644 --- a/libwvdrmengine/cdm/test/Android.mk +++ b/libwvdrmengine/cdm/test/Android.mk @@ -63,6 +63,10 @@ test_name := request_license_test test_src_dir := . include $(LOCAL_PATH)/unit-test.mk +test_name := service_certificate_unittest +test_src_dir := ../core/test +include $(LOCAL_PATH)/unit-test.mk + test_name := timer_unittest test_src_dir := . include $(LOCAL_PATH)/unit-test.mk diff --git a/libwvdrmengine/run_all_unit_tests.sh b/libwvdrmengine/run_all_unit_tests.sh index 3e932b3a..039e1c36 100755 --- a/libwvdrmengine/run_all_unit_tests.sh +++ b/libwvdrmengine/run_all_unit_tests.sh @@ -80,6 +80,7 @@ adb_shell_run license_unittest adb_shell_run license_keys_unittest adb_shell_run initialization_data_unittest adb_shell_run device_files_unittest +adb_shell_run service_certificate_unittest adb_shell_run timer_unittest adb_shell_run buffer_reader_test