Merge "Ignore certain errors on RemoveOfflineLicense()." into vic-widevine-dev

This commit is contained in:
Alex Dale
2025-02-05 13:12:15 -08:00
committed by Android (Google) Code Review
6 changed files with 250 additions and 41 deletions

View File

@@ -381,7 +381,8 @@ enum CdmResponseEnum : int32_t {
OUTPUT_TOO_LARGE_ERROR = 318,
SESSION_LOST_STATE_ERROR = 319,
GENERATE_DERIVED_KEYS_ERROR_2 = 320,
LOAD_DEVICE_RSA_KEY_ERROR = 321,
/* previously LOAD_DEVICE_RSA_KEY_ERROR = 321, */
LOAD_DRM_PRIVATE_KEY_ERROR = 321,
NONCE_GENERATION_ERROR = 322,
GENERATE_SIGNATURE_ERROR = 323,
UNKNOWN_CLIENT_TOKEN_TYPE = 324,

View File

@@ -1504,20 +1504,38 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
CdmResponseType sts = OpenKeySetSession(key_set_id, &property_set,
nullptr /* event listener */);
if (sts == NEED_PROVISIONING) {
// It is possible that an app unprovisioned after requesting some
// licenses. RemoveOfflineLicense() should be allowed to work
// with or without provisioning.
LOGW("Not provisioned, deleting license: %s", IdToString(key_set_id));
// TODO(b/372105842): Mark usage entry as unused.
handle.DeleteLicense(key_set_id);
return CdmResponseType(NO_ERROR);
}
if (sts != NO_ERROR) {
LOGE("OpenKeySetSession failed: status = %d", static_cast<int>(sts));
handle.DeleteLicense(key_set_id);
return sts;
}
// Attempt to lock out the license's usage entry to prevent file
// restoration by generating a release request.
// This step should not directly effect the app, as the app is not
// requesting the release. See enumerated errors below.
CdmSessionId session_id;
CdmAppParameterMap dummy_app_params;
const InitializationData dummy_init_data("", "", "");
CdmKeyRequest key_request;
// Calling with no session_id is okay
CdmKeyRequest release_request_unused;
// Calling with no session_id is okay.
// License will be restored by GenerateKeyRequest().
sts = GenerateKeyRequest(session_id, key_set_id, dummy_init_data,
kLicenseTypeRelease, dummy_app_params, &key_request);
kLicenseTypeRelease, dummy_app_params,
&release_request_unused);
if (sts == KEY_MESSAGE) {
// Release was successfully generated, use CDM session
// to properly delete all license data.
std::unique_lock<std::mutex> lock(release_key_sets_lock_);
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
if (iter == release_key_sets_.end()) {
@@ -1535,10 +1553,32 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
LOGW("License usage entry is missing, deleting license file");
handle.DeleteLicense(key_set_id);
sts = CdmResponseType(NO_ERROR);
} else if (sts == LOAD_LICENSE_ERROR || sts == LOAD_DRM_PRIVATE_KEY_ERROR ||
sts == RELEASE_LICENSE_ERROR_1 || sts == NEED_PROVISIONING) {
// It is possible that the DRM certificate associated with this
// license has been replaced/updated or that the root of trust has
// been updated (invalidating the DRM certificate).
// it will no longer be possible to load the license for release;
// and the file should simply be deleted.
LOGW("License could not be restored, deleting license file: status = %s",
sts.ToString().c_str());
handle.DeleteLicense(key_set_id);
sts = CdmResponseType(NO_ERROR);
} else if (sts == GENERATE_SIGNATURE_ERROR) {
// It is possible that OEMCrypto does not have a key to generate
// a signature for release request. The app is trying to remove
// not release, so failure related to generating the release
// request should not cause this to fail.
LOGW("License could not be released, deleting license file: status = %s",
sts.ToString().c_str());
handle.DeleteLicense(key_set_id);
sts = CdmResponseType(NO_ERROR);
}
if (sts != NO_ERROR) {
LOGE("GenerateKeyRequest failed: status = %d", static_cast<int>(sts));
// Errors not caught above should be treated as an error,
// and the license file should be deleted.
LOGE("GenerateKeyRequest failed: status = %s", sts.ToString().c_str());
handle.DeleteLicense(key_set_id);
}
CloseKeySetSession(key_set_id);

View File

@@ -1391,7 +1391,7 @@ CdmResponseType CryptoSession::LoadCertificatePrivateKey(
metrics_, oemcrypto_load_device_drm_key_, sts);
});
return MapOEMCryptoResult(sts, LOAD_DEVICE_RSA_KEY_ERROR,
return MapOEMCryptoResult(sts, LOAD_DRM_PRIVATE_KEY_ERROR,
"LoadCertificatePrivateKey");
}

View File

@@ -719,8 +719,8 @@ const char* CdmResponseEnumToString(CdmResponseEnum cdm_response_enum) {
return "SESSION_LOST_STATE_ERROR";
case GENERATE_DERIVED_KEYS_ERROR_2:
return "GENERATE_DERIVED_KEYS_ERROR_2";
case LOAD_DEVICE_RSA_KEY_ERROR:
return "LOAD_DEVICE_RSA_KEY_ERROR";
case LOAD_DRM_PRIVATE_KEY_ERROR:
return "LOAD_DRM_PRIVATE_KEY_ERROR";
case NONCE_GENERATION_ERROR:
return "NONCE_GENERATION_ERROR";
case GENERATE_SIGNATURE_ERROR:

View File

@@ -2129,11 +2129,13 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
return result;
}
void Unprovision() {
void Unprovision() { Unprovision(kDefaultCdmIdentifier); }
void Unprovision(const CdmIdentifier& identifier) {
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->Unprovision(kSecurityLevelL1, kDefaultCdmIdentifier));
decryptor_->Unprovision(kSecurityLevelL1, identifier));
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->Unprovision(kSecurityLevelL3, kDefaultCdmIdentifier));
decryptor_->Unprovision(kSecurityLevelL3, identifier));
}
bool IsProvisioned(const CdmIdentifier& identifier,
@@ -2231,16 +2233,22 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
}
CdmSecurityLevel GetDefaultSecurityLevel() {
std::string level = GetSecurityLevel(nullptr).c_str();
CdmSecurityLevel security_level = kSecurityLevelUninitialized;
if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) == 0) {
security_level = kSecurityLevelL1;
} else if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0) {
security_level = kSecurityLevelL3;
} else {
EXPECT_TRUE(false) << "Default Security level is undefined: " << level;
std::string security_level;
const CdmResponseType status = decryptor_->QueryStatus(
kLevelDefault, wvcdm::QUERY_KEY_SECURITY_LEVEL, &security_level);
if (status != NO_ERROR) {
ADD_FAILURE() << "Failed to obtain default security level: "
<< status.ToString();
return kSecurityLevelUninitialized;
}
return security_level;
if (security_level == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) {
return kSecurityLevelL1;
}
if (security_level == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) {
return kSecurityLevelL3;
}
ADD_FAILURE() << "Undefined security level: " << security_level;
return kSecurityLevelUninitialized;
}
uint32_t QueryStatus(RequestedSecurityLevel security_level,
@@ -6552,9 +6560,12 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_DecryptPathTest) {
// not match the license's key set ID (possible for entry to have
// been overwritten).
TEST_F(WvCdmRequestLicenseTest, RemoveOfflineLicenseWithMissingUsageEntry) {
Unprovision();
Provision();
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
const RequestedSecurityLevel requested_security_level =
(security_level == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
Unprovision(kExampleIdentifier);
Provision(kExampleIdentifier, requested_security_level);
FileSystem file_system;
DeviceFiles handle(&file_system);
@@ -6568,9 +6579,9 @@ TEST_F(WvCdmRequestLicenseTest, RemoveOfflineLicenseWithMissingUsageEntry) {
// This license will be used to test how the CDM handles request to
// remove it when its entry has been deleted.
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
decryptor_->OpenSession(config_.key_system(), nullptr, kExampleIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(key_id, kLicenseTypeOffline);
GenerateKeyRequest(key_id, kLicenseTypeOffline, kExampleIdentifier);
VerifyKeyRequestResponse(config_.license_server(), client_auth);
// Save the key set ID for check below.
const std::string original_key_set_id(key_set_id_);
@@ -6584,9 +6595,9 @@ TEST_F(WvCdmRequestLicenseTest, RemoveOfflineLicenseWithMissingUsageEntry) {
EXPECT_EQ(DeviceFiles::kNoError, sub_result);
// Re-provision.
Unprovision();
Unprovision(kExampleIdentifier);
handle.DeleteAllFiles();
Provision();
Provision(kExampleIdentifier, requested_security_level);
// Part 1: Test when usage entry is out of range of the table.
@@ -6597,26 +6608,26 @@ TEST_F(WvCdmRequestLicenseTest, RemoveOfflineLicenseWithMissingUsageEntry) {
std::vector<CdmKeySetId> key_set_ids;
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->ListStoredLicenses(
security_level, kDefaultCdmIdentifier, &key_set_ids));
decryptor_->ListStoredLicenses(security_level, kExampleIdentifier,
&key_set_ids));
// Note: It is possible that future changes to the CDM will cause this
// check to fail (such by filtering results from ListStoreLicenses).
EXPECT_THAT(key_set_ids, Contains(original_key_set_id));
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->RemoveOfflineLicense(
original_key_set_id, security_level, kDefaultCdmIdentifier));
original_key_set_id, security_level, kExampleIdentifier));
// Verify license has been removed.
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->ListStoredLicenses(
security_level, kDefaultCdmIdentifier, &key_set_ids));
decryptor_->ListStoredLicenses(security_level, kExampleIdentifier,
&key_set_ids));
EXPECT_THAT(key_set_ids, Not(Contains(original_key_set_id)));
// Re-provision.
Unprovision();
Unprovision(kExampleIdentifier);
handle.DeleteAllFiles();
Provision();
Provision(kExampleIdentifier, requested_security_level);
// Part 2: Test when the entry does not match the license's key set ID.
@@ -6624,26 +6635,183 @@ TEST_F(WvCdmRequestLicenseTest, RemoveOfflineLicenseWithMissingUsageEntry) {
EXPECT_EQ(DeviceFiles::kNoError, sub_result);
// Request another license so that the usage table is not empty.
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
decryptor_->OpenSession(config_.key_system(), nullptr, kExampleIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(key_id, kLicenseTypeOffline);
GenerateKeyRequest(key_id, kLicenseTypeOffline, kExampleIdentifier);
VerifyKeyRequestResponse(config_.license_server(), client_auth);
decryptor_->CloseSession(session_id_);
// Get list of existing offline licenses.
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->ListStoredLicenses(
security_level, kDefaultCdmIdentifier, &key_set_ids));
decryptor_->ListStoredLicenses(security_level, kExampleIdentifier,
&key_set_ids));
EXPECT_THAT(key_set_ids, Contains(original_key_set_id));
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->RemoveOfflineLicense(
original_key_set_id, security_level, kDefaultCdmIdentifier));
original_key_set_id, security_level, kExampleIdentifier));
// Verify license has been removed.
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->ListStoredLicenses(
security_level, kDefaultCdmIdentifier, &key_set_ids));
decryptor_->ListStoredLicenses(security_level, kExampleIdentifier,
&key_set_ids));
EXPECT_THAT(key_set_ids, Not(Contains(original_key_set_id)));
}
TEST_F(WvCdmRequestLicenseTest, RemoveOfflineLicenseAfterUnprovisioning) {
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
const RequestedSecurityLevel requested_security_level =
(security_level == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
Unprovision(kExampleIdentifier);
Provision(kExampleIdentifier, requested_security_level);
std::string key_id;
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
// Setup: Request an offline license to create a valid license file.
// This license will be used to test how the CDM handles request to
// remove when the device has been unprovisioned.
decryptor_->OpenSession(config_.key_system(), nullptr, kExampleIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(key_id, kLicenseTypeOffline, kExampleIdentifier);
VerifyKeyRequestResponse(config_.license_server(), client_auth);
// Save the key set ID for check below.
const std::string original_key_set_id(key_set_id_);
decryptor_->CloseSession(session_id_);
// Unprovision, making the old DRM certificate unavailable.
Unprovision(kExampleIdentifier);
std::vector<CdmKeySetId> key_set_ids;
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->ListStoredLicenses(security_level, kExampleIdentifier,
&key_set_ids));
// Note: It is possible that future changes to the CDM will cause this
// check to fail (such by filtering results from ListStoreLicenses).
EXPECT_THAT(key_set_ids, Contains(original_key_set_id));
// Remove offline license, without causing a provisioning error.
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->RemoveOfflineLicense(
original_key_set_id, security_level, kExampleIdentifier));
// Verify license has been removed.
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->ListStoredLicenses(security_level, kExampleIdentifier,
&key_set_ids));
EXPECT_THAT(key_set_ids, Not(Contains(original_key_set_id)));
}
TEST_F(WvCdmRequestLicenseTest, RemoveOfflineLicenseAfterReprovisioning) {
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
const RequestedSecurityLevel requested_security_level =
(security_level == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
Unprovision(kExampleIdentifier);
Provision(kExampleIdentifier, requested_security_level);
std::string key_id;
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
// Setup: Request an offline license to create a valid license file.
// This license will be used to test how the CDM handles request to
// remove when the device has been unprovisioned.
decryptor_->OpenSession(config_.key_system(), nullptr, kExampleIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(key_id, kLicenseTypeOffline, kExampleIdentifier);
VerifyKeyRequestResponse(config_.license_server(), client_auth);
// Save the key set ID for check below.
const std::string original_key_set_id(key_set_id_);
decryptor_->CloseSession(session_id_);
// Reprovision, making the old DRM certificate unavailable.
Unprovision(kExampleIdentifier);
Provision(kExampleIdentifier, requested_security_level);
std::vector<CdmKeySetId> key_set_ids;
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->ListStoredLicenses(security_level, kExampleIdentifier,
&key_set_ids));
// Note: It is possible that future changes to the CDM will cause this
// check to fail (such by filtering results from ListStoreLicenses).
EXPECT_THAT(key_set_ids, Contains(original_key_set_id));
// Remove offline license, without causing a license load error.
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->RemoveOfflineLicense(
original_key_set_id, security_level, kExampleIdentifier));
// Verify license has been removed.
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->ListStoredLicenses(security_level, kExampleIdentifier,
&key_set_ids));
EXPECT_THAT(key_set_ids, Not(Contains(original_key_set_id)));
}
TEST_F(WvCdmRequestLicenseTest, RemoveUnlimitedOfflineLicense) {
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
const RequestedSecurityLevel requested_security_level =
(security_level == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
Unprovision(kExampleIdentifier);
Provision(kExampleIdentifier, requested_security_level);
std::string key_id;
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
// Override content ID.
key_id = wvutil::a2bs_hex(
"0000004d" // size = 77
"70737368" // type = "pssh"
"00000000" // flags = None
"edef8ba979d64acea3c827dcd51d21ed" // system_id = Widevine
"0000002d" // pssh_data_size = 45
// WidevinePsshData(
// algorithm = AESCTR,
// provider = "widevine_test"
// content_id = b"GTS_CAN_PERSIST_LICENSE_0S")
"08011a0d7769646576696e655f7465"
"7374221a4754535f43414e5f504552"
"534953545f4c4943454e53455f3053");
// Setup: Request an offline license to create a valid license file.
// This license will be used to test how the CDM handles request to
// remove when the device has been unprovisioned.
decryptor_->OpenSession(config_.key_system(), nullptr, kExampleIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(key_id, kLicenseTypeOffline, kExampleIdentifier);
VerifyKeyRequestResponse(config_.license_server(), client_auth);
// Save the key set ID for check below.
const std::string original_key_set_id(key_set_id_);
decryptor_->CloseSession(session_id_);
std::vector<CdmKeySetId> key_set_ids;
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->ListStoredLicenses(security_level, kExampleIdentifier,
&key_set_ids));
// Note: It is possible that future changes to the CDM will cause this
// check to fail (such by filtering results from ListStoreLicenses).
EXPECT_THAT(key_set_ids, Contains(original_key_set_id));
// Remove offline license, without causing a generate signature error.
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->RemoveOfflineLicense(
original_key_set_id, security_level, kExampleIdentifier));
// Verify license has been removed.
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_->ListStoredLicenses(security_level, kExampleIdentifier,
&key_set_ids));
EXPECT_THAT(key_set_ids, Not(Contains(original_key_set_id)));
}

View File

@@ -386,7 +386,7 @@ static inline WvStatus mapCdmResponseType(wvcdm::CdmResponseType res) {
case wvcdm::PRIVACY_MODE_ERROR_3:
err = Status::MISSING_CERTIFICATE;
break;
case wvcdm::LOAD_DEVICE_RSA_KEY_ERROR:
case wvcdm::LOAD_DRM_PRIVATE_KEY_ERROR:
err = Status::PROVISIONING_CERTIFICATE_ERROR;
break;
case wvcdm::CERT_PROVISIONING_EMPTY_SERVICE_CERTIFICATE: