diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 90a74b8c..a29c46b9 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -212,6 +212,15 @@ class CdmEngine { // system. This will force the device to reprovision itself. virtual CdmResponseType Unprovision(CdmSecurityLevel security_level); + // Remove the system's REE-side OEM certificate for the specified + // |security_level|. + // Only effects two-stage provisioning devices which have an OEM cert + // in the REE side file system. + // Removing the OEM certificate will cause all DRM certificates tied to + // the OEM certificate to be invalidated and unloadable to future + // sessions. + virtual CdmResponseType UnprovisionOemCert(CdmSecurityLevel security_level); + // Return the list of key_set_ids stored on the current (origin-specific) // file system. virtual CdmResponseType ListStoredLicenses( diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 3d8ed126..cb189cfb 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -1332,8 +1332,7 @@ CdmProvisioningStatus CdmEngine::GetProvisioningStatus( return kUnknownProvisionStatus; } - UsagePropertySet property_set; - if (handle.HasCertificate(property_set.use_atsc_mode())) { + if (handle.HasCertificate(/* atsc_mode_enabled = */ false)) { return kProvisioned; } if (crypto_session->GetPreProvisionTokenType() == kClientTokenBootCertChain) { @@ -1356,8 +1355,8 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) { LOGD("OKP fallback to L3"); security_level = kSecurityLevelL3; } - // Devices with baked-in DRM certs cannot be reprovisioned and therefore must - // not be unprovisioned. + // Devices with baked-in DRM certs cannot be reprovisioned + // and therefore must not be unprovisioned. std::unique_ptr crypto_session( CryptoSession::MakeCryptoSession(metrics_->GetCryptoMetrics())); CdmClientTokenType token_type = kClientTokenUninitialized; @@ -1376,18 +1375,78 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) { LOGE("Unable to initialize device files"); return CdmResponseType(UNPROVISION_ERROR_1); } - - // TODO(b/141705730): Remove usage entries during unprovisioning. - if (!file_system_->IsGlobal()) { - if (!handle.RemoveCertificate() || !handle.RemoveOemCertificate()) { - LOGE("Unable to delete certificate"); + // This if statement is misleading. There is no consistent + // concept of "global" vs "per-app/origin" storage in the + // core library. Android vs CE CDM behave very different. + // On CE device: + // file_system_->IsGlobal() is always true, even if app/origin + // specific. + // On Android: + // file_system_->IsGlobal() is always false, except for some C++ + // test code. + // TODO(b/142280599): Refactor this once CE CDM SPOIDs are supported + // by the file system. May require moving platform-dependent behavior + // to the platform-dependent layer. Only have this remove the + // certificate and nothing else. + if (!file_system_->IsGlobal()) { // AKA is Android + // TODO(b/141705730): Remove usage entries during unprovisioning. + // Not considered an error if no certificate exists. + if (handle.HasCertificate(/* atsc_mode_enabled = */ false) && + !handle.RemoveCertificate()) { + LOGE("Unable to delete DRM certificate"); return CdmResponseType(UNPROVISION_ERROR_2); } + // Maintaining old behavior expected by Android. + const CdmResponseType oem_cert_status = UnprovisionOemCert(security_level); + if (oem_cert_status != NO_ERROR) return oem_cert_status; + } else { // AKA is CE CDM (or some Android tests) + // On CE CDM, deleting all files only deletes the app/origin + // specific files. + // On Android, this will delete all files (only possible + // during testing). + if (!handle.DeleteAllFiles()) { + LOGE("Unable to delete files"); + return CdmResponseType(UNPROVISION_ERROR_3); + } + } + return CdmResponseType(NO_ERROR); +} + +CdmResponseType CdmEngine::UnprovisionOemCert(CdmSecurityLevel security_level) { + LOGI("security_level = %s", CdmSecurityLevelToString(security_level)); + if (security_level == kSecurityLevelL1 && OkpIsInFallbackMode()) { + LOGD("OKP fallback to L3"); + security_level = kSecurityLevelL3; + } + // Only BCC-based system have an OEM certificate that can + // unprovisioned. + // Prov 3.0 system's OEM certs are built into the TEE. + std::unique_ptr crypto_session( + CryptoSession::MakeCryptoSession(metrics_->GetCryptoMetrics())); + CdmClientTokenType token_type = kClientTokenUninitialized; + const CdmResponseType res = crypto_session->GetProvisioningMethod( + security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault, + &token_type); + if (res != NO_ERROR) { + return res; + } + if (token_type != kClientTokenBootCertChain) { + LOGD("Device does not support OEM certificate unprovisioning"); return CdmResponseType(NO_ERROR); } - if (!handle.DeleteAllFiles()) { - LOGE("Unable to delete files"); - return CdmResponseType(UNPROVISION_ERROR_3); + // For Prov 4.0 devices, this will cause every app/origin client + // to lose their offline content for the same TEE security level. + wvutil::FileSystem global_file_system; + DeviceFiles global_handle(&global_file_system); + if (!global_handle.Init(security_level)) { + LOGE("Unable to initialize global device files"); + return CdmResponseType(UNPROVISION_ERROR_1); + } + // Not considered an error if no certificate exists. + if (global_handle.HasOemCertificate() && + !global_handle.RemoveOemCertificate()) { + LOGE("Unable to delete OEM certificate"); + return CdmResponseType(UNPROVISION_ERROR_2); } return CdmResponseType(NO_ERROR); } diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index c3ca252a..efe61c8e 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -709,17 +709,26 @@ bool DeviceFiles::RemoveCertificate() { RETURN_FALSE_IF_UNINITIALIZED() std::string certificate_file_name; - if (GetCertificateFileName(kCertificateLegacy, &certificate_file_name)) - RemoveFile(certificate_file_name); - if (GetCertificateFileName(kCertificateDefault, &certificate_file_name)) - return RemoveFile(certificate_file_name); - return true; + // Return true so long as at least one certificate was removed. + // This is to compliment the behavior of HasCertificate() which + // returns true if at least one certificate exists. + bool result = false; + if (GetCertificateFileName(kCertificateLegacy, &certificate_file_name)) { + LOGI("Removing legacy DRM cert"); + result |= RemoveFile(certificate_file_name); + } + if (GetCertificateFileName(kCertificateDefault, &certificate_file_name)) { + LOGI("Removing DRM cert"); + result |= RemoveFile(certificate_file_name); + } + return result; } bool DeviceFiles::RemoveOemCertificate() { RETURN_FALSE_IF_UNINITIALIZED() std::string certificate_file_name; if (GetOemCertificateFileName(&certificate_file_name)) { + LOGI("Removing OEM certificate"); return RemoveFile(certificate_file_name); } return true; diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index b21a075c..11e62770 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -346,6 +346,8 @@ CdmResponseType WvContentDecryptionModule::Unprovision( // Enable immediate OEMCrypto termination and re-initalization on // unprovisioning. CryptoSession::DisableDelayedTermination(); + // Android unprovisioning has historically allowed for both + // DRM (app/origin-specific) and OEM (global) unprovisioning. return cdm_engine->Unprovision(level); }