From cc35c647572d1efaf3cdb11cc4616a511f8980fd Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Fri, 15 Apr 2022 20:14:51 -0700 Subject: [PATCH] Specify DRM certificate duration in tests. [ Merge of http://go/wvgerrit/150350 ] Certain integration tests were depending on the provisioning server only sending short duration (~2 minute) certificates when testing the CDM's ability to enforce expiration periods. This behavior of the server was not reliable, resulting in device test failures from server behavior changes. The DRM certificate provisioning server allows the requester to optionally provide a desired certificate duration in the request URL. The tests which test the CDM's ability to enforce certificate durations will now explicitly specify the certificate duration required by the test. Bug: 228547158 Test: request_license_test Change-Id: Ib9ebfdba1451104be0e59baca0b2f23a94cd51e6 --- .../include/wv_content_decryption_module.h | 2 +- .../cdm/src/wv_content_decryption_module.cpp | 2 +- .../cdm/test/request_license_test.cpp | 92 ++++++++++++++----- .../mediadrm/test/WVDrmPlugin_test.cpp | 2 +- 4 files changed, 71 insertions(+), 27 deletions(-) diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index 511a88bf..bc6145f8 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -92,7 +92,7 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { CdmProvisioningRequest* request, std::string* default_url); virtual CdmResponseType HandleProvisioningResponse( - const CdmIdentifier& identifier, CdmProvisioningResponse& response, + const CdmIdentifier& identifier, const CdmProvisioningResponse& response, RequestedSecurityLevel requested_security_level, std::string* cert, std::string* wrapped_key); diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 2ff88768..a0f6edae 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -227,7 +227,7 @@ CdmResponseType WvContentDecryptionModule::GetProvisioningRequest( } CdmResponseType WvContentDecryptionModule::HandleProvisioningResponse( - const CdmIdentifier& identifier, CdmProvisioningResponse& response, + const CdmIdentifier& identifier, const CdmProvisioningResponse& response, RequestedSecurityLevel requested_security_level, std::string* cert, std::string* wrapped_key) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 21cff53d..b960cbcb 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -52,7 +52,11 @@ namespace { // HTTP response codes. const int kHttpOk = 200; -const int kDrmCertificateExpiryPeriod = 150; + +// For tests checking functionality of expiring. +constexpr uint32_t kDrmCertificateExpiryPeriod = 120; +constexpr uint32_t kDrmCertificateExpirySleepPeriod = + kDrmCertificateExpiryPeriod + 30; const wvcdm::CdmIdentifier kExampleIdentifier = { wvcdm::EMPTY_SPOID, "com.example", "com.example", 7, 9}; @@ -1868,11 +1872,26 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase { // Post a request and extract the signed provisioning message from // the HTTP response. - std::string GetCertRequestResponse(const std::string& server_url) { + // Setting a non-zero value to |duration_seconds| will request a + // limited duration DRM certificate. + std::string GetCertRequestResponse(const std::string& server_url, + uint32_t duration_seconds = 0) { // Use secure connection and chunk transfer coding. - UrlRequest url_request(server_url); + std::string actual_url = server_url; + if (duration_seconds > 0) { + const auto query_start = actual_url.find('?'); + if (query_start == std::string::npos) { + actual_url.push_back('?'); + } else { + actual_url.push_back('&'); + } + actual_url.append("options.expiration_delta_seconds="); + actual_url.append(std::to_string(duration_seconds)); + } + + UrlRequest url_request(actual_url); EXPECT_TRUE(url_request.is_connected()) - << "Fail to connect to " << server_url; + << "Fail to connect to " << actual_url; url_request.PostCertRequestInQueryString(key_msg_); std::string message; EXPECT_TRUE(url_request.GetResponse(&message)); @@ -2573,6 +2592,10 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRevocationTest) { } } +// Test checks that the CDM correctly reports provisioning status when +// a certificate has expired. It is expected that the CDM reports that +// the specified CDM engine is provisioned only during the certificate's +// validity period. TEST_F(WvCdmRequestLicenseTest, ProvisioningWithExpiringCertTest) { EXPECT_EQ(NO_ERROR, decryptor_->Unprovision(kSecurityLevelL1, kExampleIdentifier)); @@ -2584,7 +2607,7 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningWithExpiringCertTest) { // Provision std::string provisioning_server; - CdmCertificateType cert_type = kCertificateWidevine; + const CdmCertificateType cert_type = kCertificateWidevine; std::string cert_authority, cert, wrapped_key; EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->GetProvisioningRequest( @@ -2592,18 +2615,19 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningWithExpiringCertTest) { kEmptyServiceCertificate, kLevelDefault, &key_msg_, &provisioning_server)); - std::string response = GetCertRequestResponse(config.provisioning_server()); - EXPECT_NE(0, static_cast(response.size())); + const std::string response = GetCertRequestResponse( + config.provisioning_server(), kDrmCertificateExpiryPeriod); + EXPECT_FALSE(response.empty()) << "Failed to get DRM provisioning response"; EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse( kExampleIdentifier, response, kLevelDefault, &cert, &wrapped_key)); - EXPECT_EQ(0, static_cast(cert.size())); - EXPECT_EQ(0, static_cast(wrapped_key.size())); - decryptor_->CloseSession(session_id_); + EXPECT_TRUE(cert.empty()) << "Widevine certs should not be returned"; + EXPECT_TRUE(wrapped_key.empty()) + << "Keys from Widevine certs should not be returned"; // Make sure it is provisioned, then wait for certificate expiry period EXPECT_TRUE(IsProvisioned(kExampleIdentifier, kLevelDefault)); - sleep(kDrmCertificateExpiryPeriod); + sleep(kDrmCertificateExpirySleepPeriod); // Verify that it is no longer provisioned after the certificate expires EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault)); @@ -2781,6 +2805,8 @@ TEST_F(WvCdmRequestLicenseTest, AddStreamingKeyTest) { decryptor_->CloseSession(session_id_); } +// Test checks that the CDM does not allow key request generations +// if the DRM certificate is expired. TEST_F(WvCdmRequestLicenseTest, StreamingWithExpiringCertTest) { EXPECT_EQ(NO_ERROR, decryptor_->Unprovision(kSecurityLevelL1, kExampleIdentifier)); @@ -2800,13 +2826,15 @@ TEST_F(WvCdmRequestLicenseTest, StreamingWithExpiringCertTest) { kEmptyServiceCertificate, kLevelDefault, &key_msg_, &provisioning_server)); - std::string response = GetCertRequestResponse(config.provisioning_server()); - EXPECT_NE(0, static_cast(response.size())); + const std::string response = GetCertRequestResponse( + config.provisioning_server(), kDrmCertificateExpiryPeriod); + EXPECT_FALSE(response.empty()) << "Failed to get DRM provisioning response"; EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse( kExampleIdentifier, response, kLevelDefault, &cert, &wrapped_key)); - EXPECT_EQ(0, static_cast(cert.size())); - EXPECT_EQ(0, static_cast(wrapped_key.size())); + EXPECT_TRUE(cert.empty()) << "Widevine certs should not be returned"; + EXPECT_TRUE(wrapped_key.empty()) + << "Keys from Widevine certs should not be returned"; EXPECT_TRUE(IsProvisioned(kExampleIdentifier, kLevelDefault)); @@ -2821,6 +2849,17 @@ TEST_F(WvCdmRequestLicenseTest, StreamingWithExpiringCertTest) { nullptr); VerifyKeyRequestResponse(config_.license_server(), config_.client_auth()); decryptor_->CloseSession(session_id_); + + sleep(kDrmCertificateExpirySleepPeriod); + + // Fetch another stream license, after expiry. + EXPECT_EQ(NO_ERROR, + decryptor_->OpenSession(config_.key_system(), nullptr, + kExampleIdentifier, nullptr, &session_id_)); + GenerateKeyRequest(NEED_PROVISIONING, ISO_BMFF_VIDEO_MIME_TYPE, + binary_key_id(), app_parameters, kLicenseTypeStreaming, + kExampleIdentifier, nullptr); + decryptor_->CloseSession(session_id_); } TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) { @@ -2864,6 +2903,8 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) { decryptor_->CloseSession(session_id_); } +// Test checks that the CDM allows reloading of previously acquired offline +// licenses even if the DRM certificate has expired. TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeysWithExpiringCertTest) { EXPECT_EQ(NO_ERROR, decryptor_->Unprovision(kSecurityLevelL1, kExampleIdentifier)); @@ -2875,7 +2916,7 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeysWithExpiringCertTest) { // Provision std::string provisioning_server; - CdmCertificateType cert_type = kCertificateWidevine; + const CdmCertificateType cert_type = kCertificateWidevine; std::string cert_authority, cert, wrapped_key; EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->GetProvisioningRequest( @@ -2883,13 +2924,15 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeysWithExpiringCertTest) { kEmptyServiceCertificate, kLevelDefault, &key_msg_, &provisioning_server)); - std::string response = GetCertRequestResponse(config.provisioning_server()); - EXPECT_NE(0, static_cast(response.size())); + const std::string response = GetCertRequestResponse( + config.provisioning_server(), kDrmCertificateExpiryPeriod); + EXPECT_FALSE(response.empty()) << "Failed to get DRM provisioning response"; EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse( kExampleIdentifier, response, kLevelDefault, &cert, &wrapped_key)); - EXPECT_EQ(0, static_cast(cert.size())); - EXPECT_EQ(0, static_cast(wrapped_key.size())); + EXPECT_TRUE(cert.empty()) << "Widevine certs should not be returned"; + EXPECT_TRUE(wrapped_key.empty()) + << "Keys from Widevine certs should not be returned"; EXPECT_TRUE(IsProvisioned(kExampleIdentifier, kLevelDefault)); @@ -2908,19 +2951,20 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeysWithExpiringCertTest) { nullptr); VerifyKeyRequestResponse(config_.license_server(), client_auth); - CdmKeySetId key_set_id = key_set_id_; + const CdmKeySetId key_set_id = key_set_id_; EXPECT_FALSE(key_set_id_.empty()); decryptor_->CloseSession(session_id_); session_id_.clear(); // Wait till certificate expires - sleep(kDrmCertificateExpiryPeriod); + sleep(kDrmCertificateExpirySleepPeriod); - // Make sure the certificate has expired and the device is not provisioned + // Make sure the certificate has expired and the device is not provisioned. EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault)); - // Restore offline license + // Restore offline license. Key restoration should not be prevented + // by the expired DRM certificate. decryptor_->OpenSession(config_.key_system(), nullptr, kExampleIdentifier, nullptr, &session_id_); EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id)); diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index 8e567b32..b74ad900 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -194,7 +194,7 @@ class MockCDM : public WvContentDecryptionModule { std::string*), (override)); MOCK_METHOD(CdmResponseType, HandleProvisioningResponse, - (const CdmIdentifier&, CdmProvisioningResponse&, + (const CdmIdentifier&, const CdmProvisioningResponse&, wvcdm::RequestedSecurityLevel, std::string*, std::string*), (override)); MOCK_METHOD(CdmResponseType, Unprovision, (CdmSecurityLevel,