Source release 19.6.0

GitOrigin-RevId: 13a33e34413c19da1bfe76abcc66be519c9ac9d1
This commit is contained in:
Googler
2025-05-30 14:47:25 -07:00
committed by mattfedd
parent f7ec4fdeff
commit 6d36a0c93d
59 changed files with 3327 additions and 1491 deletions

View File

@@ -140,6 +140,7 @@ const CdmUsageEntryInfo kDummyUsageEntryInfo = {
/* offline_license_expiry_time = */ kDefaultExpireDuration};
const std::vector<std::string> kEmptyLicenseList;
const std::string kNoExportedData;
const std::string kLicenseArray[] = {
kUsageEntryInfoOfflineLicense1.key_set_id,
@@ -1892,7 +1893,8 @@ TEST_F(CdmUsageTableTest,
kUsageEntry,
static_cast<UsageEntryIndex>(3) /* Mismatch */,
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense3.key_set_id,
NotNull(), NotNull()))
@@ -1914,7 +1916,8 @@ TEST_F(CdmUsageTableTest,
kUsageEntry,
static_cast<UsageEntryIndex>(2) /* Mismatch */,
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense2.key_set_id,
NotNull(), NotNull()))
@@ -2079,7 +2082,8 @@ TEST_F(CdmUsageTableTest, InvalidateEntry_LastEntriesAreStorageTypeUnknown) {
kUsageEntry,
static_cast<UsageEntryIndex>(3),
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense2.key_set_id,
NotNull(), NotNull()))
@@ -2180,7 +2184,8 @@ TEST_F(CdmUsageTableTest,
kUsageEntry,
/* usage_entry_index = */ 4,
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense3.key_set_id,
NotNull(), NotNull()))
@@ -2356,7 +2361,8 @@ TEST_F(CdmUsageTableTest,
kUsageEntry,
4,
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense3.key_set_id,
NotNull(), NotNull()))
@@ -2382,7 +2388,8 @@ TEST_F(CdmUsageTableTest,
kUsageEntry,
3,
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense2.key_set_id,
NotNull(), NotNull()))
@@ -2573,7 +2580,8 @@ TEST_F(CdmUsageTableTest, InvalidateEntry_LastEntryIsOffline) {
kUsageEntry,
static_cast<UsageEntryIndex>(4),
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense3.key_set_id,
NotNull(), NotNull()))
@@ -2602,7 +2610,8 @@ TEST_F(CdmUsageTableTest, InvalidateEntry_LastEntryIsOffline) {
kUsageEntry,
static_cast<UsageEntryIndex>(3),
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense2.key_set_id,
NotNull(), NotNull()))
@@ -2824,7 +2833,8 @@ TEST_F(CdmUsageTableTest, InvalidateEntry_LastEntriesAreOfflineAndUnknknown) {
kUsageEntry,
static_cast<UsageEntryIndex>(4),
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense3.key_set_id,
NotNull(), NotNull()))
@@ -2853,7 +2863,8 @@ TEST_F(CdmUsageTableTest, InvalidateEntry_LastEntriesAreOfflineAndUnknknown) {
kUsageEntry,
static_cast<UsageEntryIndex>(3),
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense2.key_set_id,
NotNull(), NotNull()))
@@ -3127,7 +3138,8 @@ TEST_F(CdmUsageTableTest, InvalidateEntry_MaxSessionReached) {
kUsageEntry,
static_cast<UsageEntryIndex>(1),
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense1.key_set_id,
NotNull(), NotNull()))
@@ -3206,7 +3218,8 @@ TEST_F(CdmUsageTableTest, InvalidateEntry_FirstEntry_MaxSessionReached) {
kUsageEntry,
static_cast<UsageEntryIndex>(1),
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense1.key_set_id,
NotNull(), NotNull()))
@@ -3283,7 +3296,8 @@ TEST_F(CdmUsageTableTest, InvalidateEntry_SystemInvalidation_OnMove) {
kUsageEntry,
static_cast<UsageEntryIndex>(1),
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense1.key_set_id,
NotNull(), NotNull()))
@@ -3365,7 +3379,8 @@ TEST_F(CdmUsageTableTest, InvalidateEntry_SessionInvalidation_OnMove) {
kUsageEntry,
static_cast<UsageEntryIndex>(1),
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense1.key_set_id,
NotNull(), NotNull()))
@@ -3444,7 +3459,8 @@ TEST_F(CdmUsageTableTest, InvalidateEntry_ShrinkFails) {
kUsageEntry,
static_cast<UsageEntryIndex>(1),
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense1.key_set_id,
NotNull(), NotNull()))
@@ -3536,7 +3552,8 @@ TEST_F(CdmUsageTableTest, InvalidateEntry_DestinationInUse_OnMove) {
kUsageEntry,
static_cast<UsageEntryIndex>(1),
kDrmCertificate,
kCryptoWrappedKey};
kCryptoWrappedKey,
kNoExportedData};
EXPECT_CALL(*device_files_,
RetrieveLicense(kUsageEntryInfoOfflineLicense1.key_set_id,
NotNull(), NotNull()))

View File

@@ -413,6 +413,9 @@ TEST_P(CertificateProvisioningTest, ProvisioningRequestFailsEmptySignature) {
TEST_P(CertificateProvisioningTest,
ProvisioningResponseFailsWithEmptyResponse) {
certificate_provisioning_->Init("");
// Must set state if not generating request.
certificate_provisioning_->SetStateForTesting(
CertificateProvisioning::kDrmRequestSent);
MockFileSystem file_system;
std::string certificate;
@@ -425,6 +428,9 @@ TEST_P(CertificateProvisioningTest,
TEST_P(CertificateProvisioningTest,
ProvisioningResponseFailsIfDeviceIsRevoked) {
certificate_provisioning_->Init("");
// Must set state if not generating request.
certificate_provisioning_->SetStateForTesting(
CertificateProvisioning::kDrmRequestSent);
MockFileSystem file_system;
std::string response_certificate;
@@ -445,6 +451,10 @@ TEST_P(CertificateProvisioningTest,
TEST_P(CertificateProvisioningTest, ProvisioningResponseSuccess) {
certificate_provisioning_->Init("");
// Must set state if not generating request.
certificate_provisioning_->SetStateForTesting(
CertificateProvisioning::kDrmRequestSent);
std::string expected_certificate;
std::string response;
ASSERT_TRUE(MakeSignedDrmCertificate(kFakePublicKey, kSerialNumber, kSystemId,

View File

@@ -301,4 +301,539 @@ TEST_F(CoreIntegrationTest, NeedKeyBeforeLicenseLoad) {
EXPECT_EQ(NEED_KEY, holder.Decrypt(key_id));
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
}
class Prov40IntegrationTest : public WvCdmTestBaseWithEngine {
public:
void SetUp() override {
WvCdmTestBaseWithEngine::SetUp();
// Ensure CDM is operating using Provisioning 4.0.
std::string prov_model;
CdmResponseType status = cdm_engine_.QueryStatus(
kLevelDefault, QUERY_KEY_PROVISIONING_MODEL, &prov_model);
ASSERT_EQ(status, NO_ERROR) << "Failed to determine provisioning model";
if (prov_model != QUERY_VALUE_BOOT_CERTIFICATE_CHAIN) {
GTEST_SKIP() << "Test is for Prov4.0 only";
return;
}
// Ensure CDM is not provisioned.
if (IsProvisioned()) {
status = cdm_engine_.Unprovision(kSecurityLevelL1);
ASSERT_EQ(status, NO_ERROR) << "Failed to unprovision DRM cert";
status = cdm_engine_.UnprovisionOemCert(kSecurityLevelL1);
ASSERT_EQ(status, NO_ERROR) << "Failed to unprovision OEM cert";
ASSERT_EQ(GetProvisioningStatus(), kNeedsOemCertProvisioning);
}
}
CdmProvisioningStatus GetProvisioningStatus() {
return cdm_engine_.GetProvisioningStatus(kSecurityLevelL1);
}
bool IsProvisioned() { return cdm_engine_.IsProvisioned(kSecurityLevelL1); }
void PreDrmProvisioningCheck() {
ASSERT_EQ(GetProvisioningStatus(), kNeedsOemCertProvisioning)
<< "Not in valid state for pre DRM provisioning check";
ProvisioningHolder provisioner(&cdm_engine_, config_);
// OEM provisioning.
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_))
<< "OEM Certificate provisioning attempt failed";
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning)
<< "OEM Certificate provisioning was not completed";
}
void PostIncompleteOemProvisioningCheck() {
ASSERT_EQ(GetProvisioningStatus(), kNeedsOemCertProvisioning)
<< "Not in valid state for post incomplete OEM provisioning check";
ProvisioningHolder provisioner(&cdm_engine_, config_);
// OEM provisioning.
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_))
<< "OEM Certificate provisioning attempt failed";
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning)
<< "OEM Certificate provisioning was not completed";
// DRM provisioning.
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_))
<< "DRM Certificate provisioning attempt failed";
ASSERT_EQ(GetProvisioningStatus(), kProvisioned)
<< "DRM Certificate provisioning was not completed";
// Remaining is the same as post DRM provisioning.
ASSERT_NO_FATAL_FAILURE(PostDrmProvisioningCheck())
<< "Failed post incomplete OEM provisioning check after DRM "
"provisioning";
}
void PostOemProvisioningCheck() {
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning)
<< "Not in valid state for post OEM provisioning check";
ProvisioningHolder provisioner(&cdm_engine_, config_);
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_))
<< "DRM Certificate provisioning failed";
ASSERT_EQ(GetProvisioningStatus(), kProvisioned)
<< "DRM Certificate provisioning was not completed";
// Remaining is the same as post DRM provisioning.
ASSERT_NO_FATAL_FAILURE(PostDrmProvisioningCheck())
<< "Failed post OEM provisioning check after DRM provisioning";
}
void PostIncompleteDrmProvisioningCheck() {
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning)
<< "Not in valid state for post incomplete DRM provisioning check";
ProvisioningHolder provisioner(&cdm_engine_, config_);
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_))
<< "DRM Certificate provisioning failed";
ASSERT_EQ(GetProvisioningStatus(), kProvisioned)
<< "DRM Certificate provisioning was not completed";
// Remaining is the same as post DRM provisioning.
ASSERT_NO_FATAL_FAILURE(PostDrmProvisioningCheck())
<< "Failed post incomplete DRM provisioning check after DRM "
"provisioning";
}
void PostDrmProvisioningCheck() {
ASSERT_EQ(GetProvisioningStatus(), kProvisioned)
<< "Not in valid state for post DRM provisioning check";
LicenseHolder holder("CDM_Streaming", &cdm_engine_, config_);
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder.FetchLicense());
ASSERT_NO_FATAL_FAILURE(holder.LoadLicense());
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
}
}; // class Prov40IntegrationTest
// Expected flow of an app; 1 OEM request-response, 1 DRM request-response.
//
// Case: OemReq1, OemResp1, DrmReq1, DrmResp1
//
// Notes:
// This is Widevine's expected behavior by an app.
//
// Post-Case: Load license
TEST_F(Prov40IntegrationTest, UsualOrder_LoadOem1_LoadDrm1) {
ProvisioningHolder provisioner(&cdm_engine_, config_);
ASSERT_EQ(GetProvisioningStatus(), kNeedsOemCertProvisioning);
// Round 1 - OEM provisioning (OemReq1, OemResp1).
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_))
<< "OEM Certificate provisioning failed";
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning);
// Round 2 - DRM provisioning (DrmReq1, DrmResp1).
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_))
<< "DRM Certificate provisioning failed";
ASSERT_EQ(GetProvisioningStatus(), kProvisioned);
ASSERT_NO_FATAL_FAILURE(PostDrmProvisioningCheck());
}
// Case: OemReq1, OemReq2, OemResp1 (OemResp2 is never acquired)
// Expectation:
// CDM handles OemResp1, but does not complete OEM
// provisioning.
//
// Notes:
// This is undesirable behavior by the app, but can be partially
// handle by the CDM.
// Apps that encounter this situation are likely generating many
// provisioning requests and loading them in whatever order they
// arrive.
//
// Post-Case: OEM provisioning, DRM provisioning, load license
TEST_F(Prov40IntegrationTest, UnusualOrder_DropOem2_LoadOem1) {
ProvisioningHolder provisioner(&cdm_engine_, config_);
// OEM provisioning.
// Generate first request (OemReq1).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
const std::string oem_request1 = provisioner.request();
// Generate second request (OemReq2).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
// Never send for the second request.
// Use first request for fetching/loading response (OemResp1).
// CDM may or may not return an error, but OEM provisioning is still
// needed.
provisioner.set_request(oem_request1);
ASSERT_NO_FATAL_FAILURE(provisioner.FetchResponse());
// Do not enforce any particular error (including NO_ERROR).
provisioner.LoadResponseReturnStatus(binary_provisioning_);
ASSERT_EQ(GetProvisioningStatus(), kNeedsOemCertProvisioning);
ASSERT_NO_FATAL_FAILURE(PostIncompleteOemProvisioningCheck());
}
// Case: OemReq1, OemReq2, OemResp2 (OemResp1 is never acquired)
// Expectation:
// CDM handles OemReq2 (NO_ERROR), and OEM provisioning is
// completed.
//
// Notes:
// This is OK behavior by the app.
// Only the OEM response from the most recent OEM request will
// complete provisioning.
//
// Post-Case: OEM provisioning, DRM provisioning, load license
TEST_F(Prov40IntegrationTest, UnusualOrder_DropOem1_LoadOem2) {
ProvisioningHolder provisioner(&cdm_engine_, config_);
// OEM provisioning.
// Generate first request (OemReq1).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
// Never send for the first request.
// Generate, fetch and load second request (OemReq2, OemResp2).
// This should complete OEM provisioning.
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_))
<< "OEM Certificate provisioning failed";
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning);
ASSERT_NO_FATAL_FAILURE(PostOemProvisioningCheck());
}
// Case: OemReq1, OemReq2, OemResp1, OemResp2
// Expectation:
// OemResp1 is handled by the CDM, but does not complete
// provisioning. OemResp2 is accepted by the CDM
// and completes provisioning.
//
// Notes:
// This is undesirable behavior by the app, but can be partially
// handle by the CDM.
// Only the OEM response from the most recent OEM request will
// complete provisioning.
//
// Post-Case: DRM provisioning, load license
TEST_F(Prov40IntegrationTest, UnusualOrder_LoadOem1_LoadOem2) {
ProvisioningHolder provisioner(&cdm_engine_, config_);
// OEM provisioning.
// Generate first request, store it for later (OemReq1).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
const std::string oem_request1 = provisioner.request();
// Generate second request, store it for later (OemReq2).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
const std::string oem_request2 = provisioner.request();
// Use first request for fetching/loading response (OemResp1).
// CDM may or may not return an error, but OEM provisioning is still
// needed.
provisioner.set_request(oem_request1);
ASSERT_NO_FATAL_FAILURE(provisioner.FetchResponse());
// Do not enforce any particular error (including NO_ERROR).
provisioner.LoadResponseReturnStatus(binary_provisioning_);
ASSERT_EQ(GetProvisioningStatus(), kNeedsOemCertProvisioning);
// Use second request for fetching/loading response (OemResp2).
// CDM should accept the second response as valid (so long as
// a third was not generated).
provisioner.set_request(oem_request2);
ASSERT_NO_FATAL_FAILURE(provisioner.FetchResponse());
ASSERT_NO_FATAL_FAILURE(provisioner.LoadResponse(binary_provisioning_))
<< "OEM Certificate provisioning failed";
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning);
ASSERT_NO_FATAL_FAILURE(PostOemProvisioningCheck());
}
// Case: OemReq1, OemReq2, OemResp2, OemResp1
// Expectation:
// OemResp2 is accepted by the CDM and comletes OEM provisioning.
// OemResp1 does not cause the CDM to be corrupted.
//
// Notes:
// This is undesirable behavior by the app, cannot be handle
// by the CDM.
// In single-staged provisioning, the CDM silently drops
// any additional provisioning responses; but in two-stage
// this cannot easily by determine that the response is a
// late OEM response.
//
// Post-Case: DRM provisioning, load license
TEST_F(Prov40IntegrationTest, UnusualOrder_LoadOem2_LoadOem1) {
ProvisioningHolder provisioner(&cdm_engine_, config_);
// OEM provisioning.
// Generate first request, store it for later (OemReq1).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
const std::string oem_request1 = provisioner.request();
// Generate, fetch and load second request (OemReq2, OemResp2).
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_))
<< "OEM Certificate provisioning failed";
// Provisioning should be complete.
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning);
// Use first request for fetching/loading response (OemResp1).
// CDM may or may not return an error, but DRM provisioning
// should still be allowed after.
provisioner.set_request(oem_request1);
ASSERT_NO_FATAL_FAILURE(provisioner.FetchResponse());
// Do not enforce any particular error (including NO_ERROR).
provisioner.LoadResponseReturnStatus(binary_provisioning_);
// Should not effect existing provisioning state.
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning)
<< "Late OEM Certificate response invalidated original response";
ASSERT_NO_FATAL_FAILURE(PostOemProvisioningCheck());
}
// Case: DrmReq1, DrmReq2, DrmResp1, (DrmResp2 is never acquired)
// Expectation:
// DrmResp1 is handled by the CDM, but does not complete
// provisioning.
//
// Notes:
// This is undesirable behavior by the app, but can be partially
// handle by the CDM.
// Apps that encounter this situation are likely generating many
// provisioning requests and loading them in whatever order they
// arrive.
// For single-stage, this situation usually returns a signature
// failure.
//
// Pre-Case: OEM provisioning
// Post-Case: DRM provisioning, load license
TEST_F(Prov40IntegrationTest, UnusualOrder_DropDrm2_LoadDrm1) {
ASSERT_NO_FATAL_FAILURE(PreDrmProvisioningCheck());
ProvisioningHolder provisioner(&cdm_engine_, config_);
// DRM provisioning.
// Generate first request, store it for later (DrmReq1).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
const std::string drm_request1 = provisioner.request();
// Generate second request (DrmReq2).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
// Never send for the second request.
// Use first request for fetching/loading response (DrmResp1).
// CDM may or may not return an error, but DRM provisioning is still
// needed.
provisioner.set_request(drm_request1);
ASSERT_NO_FATAL_FAILURE(provisioner.FetchResponse());
// Do not enforce any particular error (including NO_ERROR).
provisioner.LoadResponseReturnStatus(binary_provisioning_);
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning);
ASSERT_NO_FATAL_FAILURE(PostIncompleteDrmProvisioningCheck());
}
// Case: DrmReq1, DrmReq2, DrmResp2 (DrmResp1 is never acquired)
// Expectation:
// CDM accepts DrmReq2 (NO_ERROR), and DRM provisioning is
// completed.
//
// Notes:
// This is OK behavior by the app.
// Only the DRM response from the most recent DRM request will
// complete provisioning.
//
// Pre-Case: OEM provisioning
// Post-Case: Load license
TEST_F(Prov40IntegrationTest, UnusualOrder_DropDrm1_LoadDrm2) {
ASSERT_NO_FATAL_FAILURE(PreDrmProvisioningCheck());
ProvisioningHolder provisioner(&cdm_engine_, config_);
// DRM provisioning.
// Generate first request (DrmReq1).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
// Never send for the first request.
// Generate, fetch and load second request (DrmReq2, DrmResp2).
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_))
<< "DRM Certificate provisioning failed";
ASSERT_TRUE(IsProvisioned());
ASSERT_NO_FATAL_FAILURE(PostDrmProvisioningCheck());
}
// Case: DrmReq1, DrmReq2, DrmResp1, DrmResp2
// Expectation:
// DrmResp1 is handled by the CDM, but does not complete
// provisioning. DrmResp2 is accepted by the CDM and
// completes provisioning.
//
// Notes:
// This is undesirable behavior by the app, but can be partially
// handle by the CDM.
// Only the DRM response from the most recent DRM request will
// complete provisioning.
//
// Pre-Case: OEM provisioning
// Post-Case: Load license
TEST_F(Prov40IntegrationTest, UnusualOrder_LoadDrm1_LoadDrm2) {
ASSERT_NO_FATAL_FAILURE(PreDrmProvisioningCheck());
ProvisioningHolder provisioner(&cdm_engine_, config_);
// DRM provisioning.
// Generate first request, store it for later (DrmReq1).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
const std::string drm_request1 = provisioner.request();
// Generate second request, store it for later (DrmReq2).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
const std::string drm_request2 = provisioner.request();
// Use first request for fetching/loading response (DrmResp1).
// CDM may or may not return an error, but DRM provisioning is still
// needed.
provisioner.set_request(drm_request1);
ASSERT_NO_FATAL_FAILURE(provisioner.FetchResponse());
// Do not enforce any particular error (including NO_ERROR).
provisioner.LoadResponseReturnStatus(binary_provisioning_);
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning);
// Use second request for fetching/loading response (DrmResp2).
// CDM should accept the second response as valid (so long as
// a third was not generated).
provisioner.set_request(drm_request2);
ASSERT_NO_FATAL_FAILURE(provisioner.FetchResponse());
ASSERT_NO_FATAL_FAILURE(provisioner.LoadResponse(binary_provisioning_))
<< "DRM Certificate provisioning failed";
ASSERT_TRUE(IsProvisioned());
ASSERT_NO_FATAL_FAILURE(PostDrmProvisioningCheck());
}
// Case: DrmReq1, DrmReq2, DrmResp2, DrmResp1
// Expectation:
// DrmResp2 is accepted by the CDM (NO_ERROR) and completes
// provisioning. DrmResp1 is handled by the CDM, but is dropped
// without causing issues with existing certificates.
//
// Notes:
// This is undesirable behavior by the app, but can be partially
// handle by the CDM.
//
// Pre-Case: OEM provisioning
// Post-Case: Load license
TEST_F(Prov40IntegrationTest, UnusualOrder_LoadDrm2_LoadDrm1) {
ASSERT_NO_FATAL_FAILURE(PreDrmProvisioningCheck());
ProvisioningHolder provisioner(&cdm_engine_, config_);
// DRM provisioning.
// Generate first request, store it for later (DrmReq1).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
const std::string drm_request1 = provisioner.request();
// Generate, fetch and load second request (DrmReq2, DrmResp2).
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_))
<< "DRM Certificate provisioning failed";
ASSERT_TRUE(IsProvisioned());
// Use first request for fetching/loading response (DrmResp1).
// CDM may or may not return an error, and the CDM should still
// be considered provisioned.
provisioner.set_request(drm_request1);
ASSERT_NO_FATAL_FAILURE(provisioner.FetchResponse());
// Do not enforce any particular error (including NO_ERROR).
provisioner.LoadResponseReturnStatus(binary_provisioning_);
// Should not effect existing provisioning state.
ASSERT_TRUE(IsProvisioned())
<< "Late DRM Certificate response invalidated original response";
ASSERT_NO_FATAL_FAILURE(PostDrmProvisioningCheck());
}
// Case: OemReq1, OemReq2, OemResp2, DrmReq1, OemResp1, DrmResp1
// Expectation:
// OemResp2 will complete OEM provisioning, allowing the
// creation of DrmReq1.
// OemResp1 (being received after OEM provisioning is completed,
// and DRM provisioning initiated) is handled by the CDM
// and does not prevent the completion of DRM provisioning.
//
//
// Notes:
// This is undesirable behavior by the app, but can be partially
// handle by the CDM.
// Stale OEM responses should not interrupt DRM provisioning in
// progress.
//
// Post-Case: Load license
TEST_F(Prov40IntegrationTest, UnusualOrder_LoadOem2_LoadDrm1_LoadOem1AsDrm) {
ProvisioningHolder provisioner(&cdm_engine_, config_);
// Round 1 - OEM provisioning.
// Generated and stored first OEM request (OemReq1)
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
const std::string oem_request1 = provisioner.request();
// Complete provisioning on the second attempt (OemReq2, OemResp2).
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_));
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning);
// Round 2 - DRM provisioning.
// Generate DRM certificate request (DrmReq1).
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
const std::string drm_request1 = provisioner.request();
// Use OEM request 1 to get an OEM response (OemResp1).
// CDM should detect that the OEM response is no longer needed
// and should drop the response with or without errors.
provisioner.set_request(oem_request1);
ASSERT_NO_FATAL_FAILURE(provisioner.FetchResponse());
// Do not enforce any particular error (including NO_ERROR).
provisioner.LoadResponseReturnStatus(binary_provisioning_);
// Should not effect existing provisioning state.
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning);
// Use DRM request 1 to get a DRM response (DrmResp1).
provisioner.set_request(drm_request1);
ASSERT_NO_FATAL_FAILURE(provisioner.FetchResponse());
ASSERT_NO_FATAL_FAILURE(provisioner.LoadResponse(binary_provisioning_))
<< "Real DRM Certificate provisioning failed";
ASSERT_TRUE(IsProvisioned());
ASSERT_NO_FATAL_FAILURE(PostDrmProvisioningCheck());
}
// Case: OemReq1, OemReq2, OemResp2, DrmReq1, DrmResp1, OemResp1
// Expectation:
// OemResp2 will complete OEM provisioning, allowing the
// creation of DrmReq1.
// DrmResp1 will complete DRM provisioning.
// OemResp1 (being received after OEM provisioning is completed,
// and after DRM provisioning is complete) is handled by the CDM
// and does not cause any other issue.
//
// Notes:
// This is undesirable behavior by the app, but can be partially
// handle by the CDM.
// Any provisioning response received after DRM provisioning
// is completed is ignored.
//
// Post-Case: Load license
TEST_F(Prov40IntegrationTest, UnusualOrder_LoadOem2_LoadOem1AsDrm_LoadDrm1) {
ProvisioningHolder provisioner(&cdm_engine_, config_);
// Round 1 - OEM provisioning.
// Generated and stored first OEM request (OemReq1)
ASSERT_NO_FATAL_FAILURE(provisioner.GenerateRequest(binary_provisioning_));
const std::string oem_request1 = provisioner.request();
// Complete provisioning on the second attempt (OemReq2, OemResp2).
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_));
ASSERT_EQ(GetProvisioningStatus(), kNeedsDrmCertProvisioning);
// Round 2 - DRM provisioning (DrmReq1, DrmReq2).
ASSERT_NO_FATAL_FAILURE(provisioner.Provision(binary_provisioning_));
ASSERT_TRUE(IsProvisioned());
// Use OEM request 1 to get an OEM response (OemResp2).
// CDM should detect that CDM is fully provisioned and should drop
// the response with or without errors.
provisioner.set_request(oem_request1);
ASSERT_NO_FATAL_FAILURE(provisioner.FetchResponse());
// Do not enforce any particular error (including NO_ERROR).
provisioner.LoadResponseReturnStatus(binary_provisioning_);
// Should not effect existing provisioning state.
ASSERT_TRUE(IsProvisioned())
<< "Late OEM Certificate response invalidated DRM certificate";
;
ASSERT_NO_FATAL_FAILURE(PostDrmProvisioningCheck());
}
} // namespace wvcdm

View File

@@ -5231,7 +5231,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) {
kLicenseTestData[license_num].usage_entry_index,
kLicenseTestData[license_num].drm_certificate,
CryptoWrappedKey(kLicenseTestData[license_num].key_type,
kLicenseTestData[license_num].private_key)};
kLicenseTestData[license_num].private_key),
/* exported_license_data= */ ""};
EXPECT_TRUE(device_files.StoreLicense(license_data, &sub_error_code));
EXPECT_EQ(DeviceFiles::kNoError, sub_error_code);
}
@@ -5300,7 +5301,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
kLicenseTestData[i].usage_entry_index,
kLicenseTestData[i].drm_certificate,
CryptoWrappedKey(kLicenseTestData[i].key_type,
kLicenseTestData[i].private_key)};
kLicenseTestData[i].private_key),
/* exported_license_data= */ ""};
EXPECT_TRUE(device_files.StoreLicense(license_data, &sub_error_code));
EXPECT_EQ(DeviceFiles::kNoError, sub_error_code);
}
@@ -5466,7 +5468,8 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
kLicenseUpdateTestData[0].usage_entry_index,
kLicenseUpdateTestData[0].drm_certificate,
CryptoWrappedKey(kLicenseTestData[0].key_type,
kLicenseTestData[0].private_key)};
kLicenseTestData[0].private_key),
/* exported_license_data= */ ""};
DeviceFiles::ResponseType sub_error_code;
EXPECT_TRUE(device_files.StoreLicense(license_data, &sub_error_code));
EXPECT_EQ(DeviceFiles::kNoError, sub_error_code);

View File

@@ -139,6 +139,54 @@ void ProvisioningHolder::LoadResponse(bool binary_provisioning) {
if (log_core_message_) MessageDumper::PrintProvisioningResponse(response_);
}
CdmResponseType ProvisioningHolder::LoadResponseReturnStatus(
bool binary_provisioning) {
// Preconditions.
if (response_.empty()) {
ADD_FAILURE() << "No response was fetched";
return CdmResponseType(UNKNOWN_ERROR);
}
std::string cdm_prov_response;
if (binary_provisioning) {
// CDM is expecting the response to be in binary form, response
// must be extracted and decoded.
std::string base_64_response;
if (!ExtractSignedMessage(response_, &base_64_response)) {
ADD_FAILURE()
<< "Failed to extract signed serialized response from JSON response";
return CdmResponseType(UNKNOWN_ERROR);
}
if (base_64_response.empty()) {
ADD_FAILURE()
<< "Base64 encoded provisioning response is unexpectedly empty";
return CdmResponseType(UNKNOWN_ERROR);
}
LOGV("Extracted response message: \n%s\n", base_64_response.c_str());
const std::vector<uint8_t> response_vec =
wvutil::Base64SafeDecode(base_64_response);
if (response_vec.empty()) {
ADD_FAILURE() << "Failed to decode base64 response: " << base_64_response;
return CdmResponseType(UNKNOWN_ERROR);
}
cdm_prov_response.assign(response_vec.begin(), response_vec.end());
} else {
cdm_prov_response = response_;
}
// HandleProvisioningResponse() may or may not succeed,
// left to caller to determine if this is considered a
// test failure.
const CdmResponseType status = cdm_engine_->HandleProvisioningResponse(
cdm_prov_response, kLevelDefault, &certificate_, &wrapped_key_);
if (status == NO_ERROR) {
// Only dump data if successful.
if (config_.dump_golden_data()) MessageDumper::DumpProvisioning(response_);
if (log_core_message_) MessageDumper::PrintProvisioningResponse(response_);
}
return status;
}
bool ProvisioningHolder::ExtractSignedMessage(const std::string& response,
std::string* result) {
static const std::string kMessageStart = "\"signedResponse\": \"";

View File

@@ -40,7 +40,11 @@ class ProvisioningHolder {
// JSON message of the response to |response_|.
void FetchResponse();
// Loads the response into the |cdm_engine_|, expecting success.
void LoadResponse(bool binary_provisioning);
// Loads the response into the |cdm_engine_|, returning the
// result from CDM.
CdmResponseType LoadResponseReturnStatus(bool binary_provisioning);
const std::string& request() const { return request_; }
// Sets the request to be used on next call to FetchResponse().