Correct stability issues for SPOIDs for provisioning 4.0
[ Merge of http://go/wvgerrit/183472 ] For provisioning 4.0 devices, the DRM certificate serial number was changing on a reprovisioning attempt or factory reset. The app parameters sent up in the client identification name-value pair field were being filtered out in provisioning requests. This has been corrected for provisioning 4.0 stage 2 (DRM certificate request). There is no need to include them for stage 1 (OEM certificate request). The test case WvCdmRequestLicenseTest.ProvisioningSpoidTest was created earlier to ensure that SPOIDs and DRM certificates are stable. Unfortunately due to another bug b/250099615, the RKP service was holding a connection to the Widevine TA for provisioning 4.0 devices. When native tests ran as their own process, L1 would fail to load due to a connection failure and the test would run as L3. The tests passed for provisioning 4.0 devices Pixel 7 and 8 when they should have failed. This gave us a false sense of confidence that the SPOIDs were stable. For now a workaround is to run a shell command to kill the widevine TA before running native tests. $ adb shell pkill -f -9 widevine New tests have been introduced to provide integration coverage WVPluginTest at the WV plugin level and CoreIntegrationTest for core. GTS tests are also being written in b/295538002. Bug: 294451432 Bug: 293950895 Test: WVPluginTest.ProvisioningStableSpoidTestL1, WVTS tests Change-Id: Ib9ace4387866ea38bb1840feb69cea78d2d2c09c
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
237
libwvdrmengine/cdm/core/test/core_integration_test.cpp
Normal file
237
libwvdrmengine/cdm/core/test/core_integration_test.cpp
Normal file
@@ -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<uint32_t>::max()) {
|
||||
ADD_FAILURE() << "System ID invalid: " << val;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t system_id = static_cast<uint32_t>(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
|
||||
@@ -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,
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user