diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index 50cb0582..c4ad340f 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -299,11 +299,29 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { const std::string& signature); private: - struct CdmInfo { - CdmInfo(); + class CdmInfo { + public: + // This should never be used. + CdmInfo() = delete; + // It is expected that the filesystem loaded into |cdm_engine| + // is the same instance as |file_system|. + CdmInfo(std::unique_ptr&& file_system, + std::unique_ptr&& cdm_engine); + // No copy operators. + CdmInfo(const CdmInfo&) = delete; + CdmInfo& operator=(const CdmInfo&) = delete; + // Move operators OK. + CdmInfo(CdmInfo&&) = default; + CdmInfo& operator==(CdmInfo&& other); - wvutil::FileSystem file_system; - std::unique_ptr cdm_engine; + wvutil::FileSystem* file_system() { return file_system_.get(); } + CdmEngine* cdm_engine() { return cdm_engine_.get(); } + + private: + // Order matters, |cdm_engine_| is expected to contain a pointer + // to |file_system_|. + std::unique_ptr file_system_; + std::unique_ptr cdm_engine_; }; // Finds the CdmEngine instance for the given identifier, creating one if diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 11e62770..e0857c7d 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -566,7 +566,7 @@ CdmResponseType WvContentDecryptionModule::GetCurrentMetricsInternal( // TODO(blueeyes): Add a better error. return CdmResponseType(UNKNOWN_ERROR); } - return it->second.cdm_engine->GetMetricsSnapshot(metrics) + return it->second.cdm_engine()->GetMetricsSnapshot(metrics) ? CdmResponseType(NO_ERROR) : CdmResponseType(UNKNOWN_ERROR); } @@ -582,31 +582,52 @@ void WvContentDecryptionModule::SaveMetrics( WvMetricsSnapshot::MakeSnapshot(identifier, std::move(metrics))); } -WvContentDecryptionModule::CdmInfo::CdmInfo() - : cdm_engine(CdmEngineFactory::CreateCdmEngine(&file_system)) {} +WvContentDecryptionModule::CdmInfo::CdmInfo( + std::unique_ptr&& file_system, + std::unique_ptr&& cdm_engine) + : file_system_(std::move(file_system)), + cdm_engine_(std::move(cdm_engine)) {} + +WvContentDecryptionModule::CdmInfo& +WvContentDecryptionModule::CdmInfo::operator==( + WvContentDecryptionModule::CdmInfo&& other) { + // Must move |cdm_engine_| first; the current value depends + // on the current value of |file_system_|. + cdm_engine_ = std::move(other.cdm_engine_); + file_system_ = std::move(other.file_system_); + return *this; +} CdmEngine* WvContentDecryptionModule::EnsureCdmForIdentifier( const CdmIdentifier& identifier) { - CdmEngine* cdm_engine; + CdmEngine* cdm_engine = nullptr; bool enable_timer = false; { std::unique_lock auto_lock(cdms_lock_); - if (cdms_.size() == 0) enable_timer = true; - if (cdms_.find(identifier) == cdms_.end()) { - // Accessing the map entry will create a new instance using the default - // constructor. We then need to provide it with two pieces of info: The + if (cdms_.empty()) enable_timer = true; + auto it = cdms_.find(identifier); + if (it != cdms_.end()) { + // Already exists. + cdm_engine = it->second.cdm_engine(); + } else { + std::unique_ptr fs = + std::make_unique(); + // Provide it with two pieces of info: The // origin provided by the app and an identifier that uniquely identifies // this CDM. We concatenate all pieces of the CdmIdentifier in order to // create an ID that is unique to that identifier. - cdms_[identifier].file_system.set_origin(identifier.origin); - cdms_[identifier].file_system.set_identifier(identifier.spoid + - identifier.origin); - cdms_[identifier].cdm_engine->SetAppPackageName( - identifier.app_package_name); - cdms_[identifier].cdm_engine->SetSpoid(identifier.spoid); - cdms_[identifier].cdm_engine->SetUserId(identifier.user_id); + fs->set_origin(identifier.origin); + fs->set_identifier(identifier.spoid + identifier.origin); + + std::unique_ptr engine( + CdmEngineFactory::CreateCdmEngine(fs.get())); + engine->SetAppPackageName(identifier.app_package_name); + engine->SetSpoid(identifier.spoid); + engine->SetUserId(identifier.user_id); + + cdm_engine = engine.get(); + cdms_.emplace(identifier, CdmInfo(std::move(fs), std::move(engine))); } - cdm_engine = cdms_[identifier].cdm_engine.get(); } // Do not enable timer while holding on to the |cdms_lock_| if (enable_timer) EnableTimer(); @@ -649,7 +670,7 @@ CdmResponseType WvContentDecryptionModule::CloseCdm( // TODO(blueeyes): Create a better error. return CdmResponseType(UNKNOWN_ERROR); } - CdmEngine* cdm_engine = cdm_it->second.cdm_engine.get(); + CdmEngine* cdm_engine = cdm_it->second.cdm_engine(); // Save metrics snapshot. drm_metrics::WvCdmMetrics metrics; const bool success = cdm_engine->GetMetricsSnapshot(&metrics); @@ -738,7 +759,7 @@ void WvContentDecryptionModule::OnTimerEvent() { std::unique_lock auto_lock(cdms_lock_); for (auto it = cdms_.begin(); it != cdms_.end(); ++it) { LoggingUidSetter set_uid(it->first.user_id); - it->second.cdm_engine->OnTimerEvent(); + it->second.cdm_engine()->OnTimerEvent(); } if (cdms_.empty()) { // The following code cannot be attributed to any app uid. diff --git a/libwvdrmengine/oemcrypto/odk/include/core_message_features.h b/libwvdrmengine/oemcrypto/odk/include/core_message_features.h index b2dda6a9..36d9d83d 100644 --- a/libwvdrmengine/oemcrypto/odk/include/core_message_features.h +++ b/libwvdrmengine/oemcrypto/odk/include/core_message_features.h @@ -26,9 +26,9 @@ struct CoreMessageFeatures { // This is the published version of the ODK Core Message library. The default // behavior is for the server to restrict messages to at most this version - // number. The default is 19.1. + // number. The default is 19.2. uint32_t maximum_major_version = 19; - uint32_t maximum_minor_version = 1; + uint32_t maximum_minor_version = 2; bool operator==(const CoreMessageFeatures &other) const; bool operator!=(const CoreMessageFeatures &other) const { diff --git a/libwvdrmengine/oemcrypto/odk/include/odk_structs.h b/libwvdrmengine/oemcrypto/odk/include/odk_structs.h index 3335f95d..de081b9a 100644 --- a/libwvdrmengine/oemcrypto/odk/include/odk_structs.h +++ b/libwvdrmengine/oemcrypto/odk/include/odk_structs.h @@ -16,7 +16,7 @@ extern "C" { /* The version of this library. */ #define ODK_MAJOR_VERSION 19 -#define ODK_MINOR_VERSION 1 +#define ODK_MINOR_VERSION 2 /* ODK Version string. Date changed automatically on each release. */ #define ODK_RELEASE_DATE "ODK v19.1 2024-03-25" diff --git a/libwvdrmengine/oemcrypto/odk/src/core_message_features.cpp b/libwvdrmengine/oemcrypto/odk/src/core_message_features.cpp index 4143d140..249bfdf3 100644 --- a/libwvdrmengine/oemcrypto/odk/src/core_message_features.cpp +++ b/libwvdrmengine/oemcrypto/odk/src/core_message_features.cpp @@ -33,7 +33,7 @@ CoreMessageFeatures CoreMessageFeatures::DefaultFeatures( features.maximum_minor_version = 4; // 18.4 break; case 19: - features.maximum_minor_version = 1; // 19.1 + features.maximum_minor_version = 2; // 19.2 break; default: features.maximum_minor_version = 0; diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_serialize.c b/libwvdrmengine/oemcrypto/odk/src/odk_serialize.c index 04982fa4..1f2e48ed 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_serialize.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk_serialize.c @@ -8,6 +8,8 @@ #include "odk_serialize.h" +#include "odk_message.h" +#include "odk_overflow.h" #include "odk_structs_priv.h" #include "serialization_base.h" @@ -237,6 +239,36 @@ static void Unpack_OEMCrypto_KeyObject(ODK_Message* msg, Unpack_OEMCrypto_Substring(msg, &obj->key_data); Unpack_OEMCrypto_Substring(msg, &obj->key_control_iv); Unpack_OEMCrypto_Substring(msg, &obj->key_control); + + /* + Edge case for servers that incorrectly process protocol VERSION_2_2 padding. + Key data in proto is present, but each key's position in the core + message is missing. + + Use the key_data_iv offset to determine if the key_data is present. + This assumes that the serialized protobuf is deterministically ordered, and + that the content key is always 16 bytes. These assumptions should hold true + for v16 and older servers. + */ + if (ODK_Message_GetStatus(msg) == MESSAGE_STATUS_OK && + obj->key_data.offset == 0 && obj->key_data.length == 0) { + const size_t kKeyDataProtoHeaderSize = 2; + obj->key_data.offset = obj->key_data_iv.offset + obj->key_data_iv.length + + kKeyDataProtoHeaderSize; + obj->key_data.length = 16u; // assume 16-byte key + + // Check for overflow. The offset is relative to the end of the core + // message, so add that length to the calculation. + size_t substring_end = 0; // offset + length + size_t end = 0; // offset + length + message_length + if (odk_add_overflow_ux(obj->key_data.offset, obj->key_data.length, + &substring_end) || + odk_add_overflow_ux(substring_end, ODK_Message_GetSize(msg), &end) || + end > ODK_Message_GetCapacity(msg)) { + ODK_Message_SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR); + return; + } + } } static void Unpack_ODK_TimerLimits(ODK_Message* msg, ODK_TimerLimits* obj) { diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_timer.c b/libwvdrmengine/oemcrypto/odk/src/odk_timer.c index c76fc5ab..95ba194a 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_timer.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk_timer.c @@ -277,7 +277,7 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits, nonce_values->api_minor_version = 4; break; case 19: - nonce_values->api_minor_version = 1; + nonce_values->api_minor_version = 2; break; default: nonce_values->api_minor_version = 0; diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp index 420b25fd..1e881353 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp @@ -1275,7 +1275,7 @@ std::vector TestCases() { {16, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 16, 5}, {17, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 17, 2}, {18, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 18, 4}, - {19, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 19, 1}, + {19, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 19, 2}, // Here are some known good versions. Make extra sure they work. {ODK_MAJOR_VERSION, 16, 3, 16, 3}, {ODK_MAJOR_VERSION, 16, 4, 16, 4}, @@ -1288,6 +1288,7 @@ std::vector TestCases() { {ODK_MAJOR_VERSION, 18, 4, 18, 4}, {ODK_MAJOR_VERSION, 19, 0, 19, 0}, {ODK_MAJOR_VERSION, 19, 1, 19, 1}, + {ODK_MAJOR_VERSION, 19, 2, 19, 2}, {0, 16, 3, 16, 3}, {0, 16, 4, 16, 4}, {0, 16, 5, 16, 5}, @@ -1297,6 +1298,7 @@ std::vector TestCases() { {0, 18, 4, 18, 4}, {0, 19, 0, 19, 0}, {0, 19, 1, 19, 1}, + {0, 19, 2, 19, 2}, }; return test_cases; } diff --git a/libwvdrmengine/version.txt b/libwvdrmengine/version.txt index 57fbb1f5..49f62afc 100644 --- a/libwvdrmengine/version.txt +++ b/libwvdrmengine/version.txt @@ -1 +1 @@ -AV1A.250627.002 +AV1A.250714.001