/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include "policy_engine.h" #include "wv_cdm_event_listener.h" #include using namespace wvcdm; using namespace wvcdm::metrics; using namespace video_widevine; static constexpr int32_t kLicenseIdVersion = 1; static constexpr int32_t kMaxByte = 256; static constexpr int32_t kMinResolution = 1; static constexpr int32_t kMaxResolution = 1024; static constexpr int32_t kMaxEntitledKey = 5; const KeyId kSigningKeyId = "signing_key"; const KeyId kEntitlementKeyId = "entitlementkeyid"; const char *kRenewalServerUrl = "https://test.google.com/license/GetCencLicense"; class FuzzWvCdmEventListener : public WvCdmEventListener { void OnSessionRenewalNeeded(const CdmSessionId & /*session_id*/) {} void OnSessionKeysChange(const CdmSessionId & /*session_id*/, const CdmKeyStatusMap & /*keys_status*/, bool /*has_new_usable_key*/) {} void OnExpirationUpdate(const CdmSessionId & /*session_id*/, int64_t /*new_expiry_time_seconds*/) {} }; class PolicyEngineFuzzer { public: PolicyEngineFuzzer(const uint8_t *data, size_t size) : fdp_(data, size){}; void Process(); private: FuzzedDataProvider fdp_; KeyId key_id_; License license_; void SetUp(); }; void PolicySetBool(std::function function, FuzzedDataProvider *fdp) { if (fdp->ConsumeBool()) { function(fdp->ConsumeBool()); } } void PolicySetInt64(std::function function, FuzzedDataProvider *fdp) { if (fdp->ConsumeBool()) { function(fdp->ConsumeIntegral()); } } void PolicyEngineFuzzer::SetUp() { License::KeyContainer *key = license_.add_key(); int32_t key_type = fdp_.ConsumeIntegralInRange( License::KeyContainer::SIGNING, License::KeyContainer::ENTITLEMENT); key->set_type((License_KeyContainer_KeyType)key_type); key_id_ = fdp_.ConsumeRandomLengthString(kMaxByte); LicenseIdentification *id = license_.mutable_id(); if (fdp_.ConsumeBool()) { id->set_version(kLicenseIdVersion); } if (fdp_.ConsumeBool()) { id->set_type(fdp_.ConsumeBool() ? STREAMING : OFFLINE); } if (fdp_.ConsumeBool()) { id->set_provider_session_token(fdp_.ConsumeRandomLengthString(kMaxByte)); } if (fdp_.ConsumeBool()) { license_.set_license_start_time(fdp_.ConsumeIntegral()); } if (fdp_.ConsumeBool()) { License_KeyContainer_OutputProtection *output_protection = key->mutable_required_protection(); License_KeyContainer_OutputProtection_HDCP hdcp_level = (License_KeyContainer_OutputProtection_HDCP)fdp_ .ConsumeIntegralInRange(HDCP_NONE, HDCP_NO_DIGITAL_OUTPUT); output_protection->set_hdcp(hdcp_level); } if (fdp_.ConsumeBool()) { const std::string iv_string = fdp_.ConsumeRandomLengthString(kMaxByte); key->set_iv(iv_string); } switch (key_type) { case License::KeyContainer::SIGNING: { if (fdp_.ConsumeBool()) { key_id_ = fdp_.ConsumeBool() ? kSigningKeyId : key_id_; key->set_id(key_id_); } break; } case License::KeyContainer::CONTENT: { if (fdp_.ConsumeBool()) { key->set_id(key_id_); } if (fdp_.ConsumeBool()) { int32_t level = fdp_.ConsumeIntegralInRange( License::KeyContainer::SW_SECURE_CRYPTO, License::KeyContainer::HW_SECURE_ALL); key->set_level((License_KeyContainer_SecurityLevel)level); } License_Policy *policy = license_.mutable_policy(); PolicySetBool( std::bind(&License_Policy::set_can_play, policy, std::placeholders::_1), &fdp_); PolicySetBool(std::bind(&License_Policy::set_can_persist, policy, std::placeholders::_1), &fdp_); PolicySetBool(std::bind(&License_Policy::set_can_renew, policy, std::placeholders::_1), &fdp_); PolicySetBool(std::bind(&License_Policy::set_renew_with_usage, policy, std::placeholders::_1), &fdp_); PolicySetBool(std::bind(&License_Policy::set_soft_enforce_rental_duration, policy, std::placeholders::_1), &fdp_); PolicySetBool(std::bind(&License_Policy::set_soft_enforce_playback_duration, policy, std::placeholders::_1), &fdp_); PolicySetInt64(std::bind(&License_Policy::set_rental_duration_seconds, policy, std::placeholders::_1), &fdp_); PolicySetInt64(std::bind(&License_Policy::set_playback_duration_seconds, policy, std::placeholders::_1), &fdp_); PolicySetInt64(std::bind(&License_Policy::set_license_duration_seconds, policy, std::placeholders::_1), &fdp_); PolicySetInt64(std::bind(&License_Policy::set_renewal_recovery_duration_seconds, policy, std::placeholders::_1), &fdp_); PolicySetInt64(std::bind(&License_Policy::set_renewal_retry_interval_seconds, policy, std::placeholders::_1), &fdp_); PolicySetInt64(std::bind(&License_Policy::set_renewal_delay_seconds, policy, std::placeholders::_1), &fdp_); if (fdp_.ConsumeBool()) { video_widevine::License::Policy::TimerDelayBase time_delay_base = (video_widevine::License::Policy::TimerDelayBase) fdp_.ConsumeIntegralInRange( License_Policy_TimerDelayBase_TIMER_DELAY_BASE_UNSPECIFIED, License_Policy_TimerDelayBase_FIRST_DECRYPT); policy->set_initial_renewal_delay_base(time_delay_base); } if (fdp_.ConsumeBool()) { std::string serverUrl = fdp_.ConsumeBool() ? kRenewalServerUrl : fdp_.ConsumeRandomLengthString(kMaxByte); policy->set_renewal_server_url(serverUrl); } break; } case License::KeyContainer::KEY_CONTROL: { key->set_type(License::KeyContainer::KEY_CONTROL); // No control bits for this type of key container used for license break; } case License::KeyContainer::OPERATOR_SESSION: { key->set_type(License::KeyContainer::OPERATOR_SESSION); if (fdp_.ConsumeBool()) { key->set_id(key_id_); } if (fdp_.ConsumeBool()) { int32_t level = fdp_.ConsumeIntegralInRange( License::KeyContainer::SW_SECURE_CRYPTO, License::KeyContainer::HW_SECURE_ALL); key->set_level((License_KeyContainer_SecurityLevel)level); } if (fdp_.ConsumeBool()) { PolicySetBool(std::bind(&License_Policy::set_can_persist,license_.mutable_policy(), std::placeholders::_1),&fdp_); } License::KeyContainer::OperatorSessionKeyPermissions *permissions = key->mutable_operator_session_key_permissions(); PolicySetBool( std::bind(&License::KeyContainer::OperatorSessionKeyPermissions:: set_allow_encrypt, permissions, std::placeholders::_1), &fdp_); PolicySetBool( std::bind(&License::KeyContainer::OperatorSessionKeyPermissions:: set_allow_decrypt, permissions, std::placeholders::_1), &fdp_); PolicySetBool(std::bind(&License::KeyContainer:: OperatorSessionKeyPermissions::set_allow_sign, permissions, std::placeholders::_1), &fdp_); PolicySetBool( std::bind(&License::KeyContainer::OperatorSessionKeyPermissions:: set_allow_signature_verify, permissions, std::placeholders::_1), &fdp_); break; } case License::KeyContainer::ENTITLEMENT: default: { if (fdp_.ConsumeBool()) { key_id_ = fdp_.ConsumeBool() ? kEntitlementKeyId : key_id_; key->set_id(key_id_); } if (fdp_.ConsumeBool()) { int32_t level = fdp_.ConsumeIntegralInRange( License::KeyContainer::SW_SECURE_CRYPTO, License::KeyContainer::HW_SECURE_ALL); key->set_level((License_KeyContainer_SecurityLevel)level); } License_Policy *policy = license_.mutable_policy(); PolicySetBool( std::bind(&License_Policy::set_can_play, policy, std::placeholders::_1), &fdp_); PolicySetBool(std::bind(&License_Policy::set_can_persist, policy, std::placeholders::_1), &fdp_); PolicySetBool(std::bind(&License_Policy::set_can_renew, policy, std::placeholders::_1), &fdp_); PolicySetBool(std::bind(&License_Policy::set_renew_with_usage, policy, std::placeholders::_1), &fdp_); PolicySetBool(std::bind(&License_Policy::set_soft_enforce_rental_duration, policy, std::placeholders::_1), &fdp_); PolicySetBool(std::bind(&License_Policy::set_soft_enforce_playback_duration, policy, std::placeholders::_1), &fdp_); PolicySetInt64(std::bind(&License_Policy::set_rental_duration_seconds, policy, std::placeholders::_1), &fdp_); PolicySetInt64(std::bind(&License_Policy::set_playback_duration_seconds, policy, std::placeholders::_1), &fdp_); PolicySetInt64(std::bind(&License_Policy::set_license_duration_seconds, policy, std::placeholders::_1), &fdp_); PolicySetInt64( std::bind(&License_Policy::set_renewal_recovery_duration_seconds, policy, std::placeholders::_1), &fdp_); PolicySetInt64( std::bind(&License_Policy::set_renewal_retry_interval_seconds, policy, std::placeholders::_1), &fdp_); PolicySetInt64( std::bind(&License_Policy::set_renewal_delay_seconds, policy, std::placeholders::_1), &fdp_); if (fdp_.ConsumeBool()) { video_widevine::License::Policy::TimerDelayBase time_delay_base = (video_widevine::License::Policy::TimerDelayBase) fdp_.ConsumeIntegralInRange( License_Policy_TimerDelayBase_TIMER_DELAY_BASE_UNSPECIFIED, License_Policy_TimerDelayBase_FIRST_DECRYPT); policy->set_initial_renewal_delay_base(time_delay_base); } break; } } } void PolicyEngineFuzzer::Process() { SetUp(); FuzzWvCdmEventListener fuzz_wv_cdm_event_listener; CdmSessionId session_id = fdp_.ConsumeRandomLengthString(kMaxByte); metrics::CryptoMetrics crypto_metrics; std::unique_ptr cryptoSession( wvcdm::CryptoSession::MakeCryptoSession(&crypto_metrics)); std::unique_ptr policy_engine(new PolicyEngine( session_id, &fuzz_wv_cdm_event_listener, cryptoSession.get())); while (fdp_.remaining_bytes()) { auto invokePolicyEngineAPI = fdp_.PickValueInArray>({ [&]() { policy_engine->CanDecryptContent(key_id_); }, [&]() { policy_engine->CanUseKeyForSecurityLevel(key_id_); }, [&]() { policy_engine->OnTimerEvent(); }, [&]() { policy_engine->SetLicense( license_, fdp_.ConsumeBool() /*defer_license_state_update**/); }, [&]() { int32_t max_entitled_key= fdp_.ConsumeIntegralInRange(1, kMaxEntitledKey); std::vector entitled_keys( max_entitled_key); for (int i = 0; i < max_entitled_key; ++i) { entitled_keys[i].set_entitlement_key_id( fdp_.ConsumeRandomLengthString(kMaxByte)); entitled_keys[i].set_key_id( fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxByte) : key_id_); } policy_engine->SetEntitledLicenseKeys(entitled_keys); }, [&]() { policy_engine->SetLicenseForRelease(license_); }, [&]() { policy_engine->BeginDecryption(); }, [&]() { policy_engine->DecryptionEvent(); }, [&]() { policy_engine->UpdateLicense( license_, fdp_.ConsumeBool() /*defer_license_state_update*/); }, [&]() { policy_engine->UpdateLicenseState( fdp_.ConsumeIntegral() /*current_time*/); }, [&]() { int32_t width = fdp_.ConsumeIntegralInRange( kMinResolution, kMaxResolution); int32_t height = fdp_.ConsumeIntegralInRange( kMinResolution, kMaxResolution); policy_engine->NotifyResolution(width, height); }, [&]() { policy_engine->NotifySessionExpiration(); }, [&]() { CdmQueryMap query_info; policy_engine->Query(&query_info); }, [&]() { CdmKeyAllowedUsage key_usage; policy_engine->QueryKeyAllowedUsage( key_id_, (fdp_.ConsumeBool() ? nullptr : &key_usage)); }, [&]() { policy_engine->RestorePlaybackTimes( fdp_.ConsumeIntegral() /*playback_start_time*/, fdp_.ConsumeIntegral() /*last_playback_time*/, fdp_.ConsumeIntegral() /*grace_period_end_time*/); }, [&]() { policy_engine->HasLicenseOrRentalOrPlaybackDurationExpired( fdp_.ConsumeIntegral()); }, }); invokePolicyEngineAPI(); } } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { PolicyEngineFuzzer policy_engine_fuzzer(data, size); policy_engine_fuzzer.Process(); return 0; }