Source release 19.6.0
GitOrigin-RevId: 13a33e34413c19da1bfe76abcc66be519c9ac9d1
This commit is contained in:
@@ -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()))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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\": \"";
|
||||
|
||||
@@ -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().
|
||||
|
||||
Reference in New Issue
Block a user