From 2ce71b64be1b318c658ded8b4e372e8b025bfcaf Mon Sep 17 00:00:00 2001 From: Akshata Kadam Date: Wed, 11 Jan 2023 12:09:55 +0530 Subject: [PATCH 1/2] Added policy_engine_fuzzer exec/s: 600 Test: ./policy_engine_fuzzer Bug: 265234582 Change-Id: I1be1f040cfef4d8c7f80de92f26b2e086327658e --- fuzzer/Android.bp | 71 +++++++ fuzzer/README.md | 28 +++ fuzzer/policy_engine_fuzzer.cpp | 353 ++++++++++++++++++++++++++++++++ 3 files changed, 452 insertions(+) create mode 100644 fuzzer/Android.bp create mode 100644 fuzzer/README.md create mode 100644 fuzzer/policy_engine_fuzzer.cpp diff --git a/fuzzer/Android.bp b/fuzzer/Android.bp new file mode 100644 index 00000000..67be8bec --- /dev/null +++ b/fuzzer/Android.bp @@ -0,0 +1,71 @@ +/* + * 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. + */ + +cc_defaults { + name: "libcdm_fuzzer_defaults", + cflags: [ + "-DDYNAMIC_ADAPTER", + ], + static_libs: [ + "libcdm", + "libcdm_protos", + "libcdm_utils", + "libwvlevel3", + "libwv_odk", + "libjsmn", + ], + shared_libs: [ + "libcrypto", + "libprotobuf-cpp-lite", + "libbinder_ndk", + "liblog", + "libbase", + "libutils", + ], + include_dirs: [ + "vendor/widevine/libwvdrmengine/cdm/core/include", + "vendor/widevine/libwvdrmengine/cdm/metrics/include", + "vendor/widevine/libwvdrmengine/cdm/util/include", + "vendor/widevine/libwvdrmengine/cdm/include", + "vendor/widevine/libwvdrmengine/oemcrypto/include", + "vendor/widevine/libwvdrmengine/oemcrypto/odk/include", + "external/jsmn", + "external/protobuf/src" + ], + header_libs: ["libutils_headers"], + fuzz_config: { + cc: [ + "android-media-fuzzing-reports@google.com", + ], + componentid: 59148, + untrusted_data: true, + critical: false, + hotlists: ["4593311"], + description: "The fuzzers target the APIs of all widevine modules", + vector: "local_no_privileges_required", + service_privilege: "privileged", + users: "single_user", + fuzzed_code_usage: "shipped" + }, + proprietary: true, + vendor: true, +} + +cc_fuzz { + name: "policy_engine_fuzzer", + srcs: ["policy_engine_fuzzer.cpp"], + defaults: ["libcdm_fuzzer_defaults"], +} diff --git a/fuzzer/README.md b/fuzzer/README.md new file mode 100644 index 00000000..fc910aa6 --- /dev/null +++ b/fuzzer/README.md @@ -0,0 +1,28 @@ +# Fuzzers for libcdm + +## Table of contents ++ [policy_engine_fuzzer](#PolicyEngine) + +# Fuzzer for PolicyEngine + +PolicyEngine supports the following parameters: +1. SigningKeyId (parameter name: "kSigningKeyId") +2. RenewalServerUrl (parameter name: "kRenewalServerUrl") +3. EntitlementKeyId (parameter name: "kEntitlementKeyId") + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +|`kSigningKeyId`| `String` |Value obtained from FuzzedDataProvider| +|`kRenewalServerUrl`| `String` |Value obtained from FuzzedDataProvider| +|`kEntitlementKeyId`| `String` |Value obtained from FuzzedDataProvider| + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) policy_engine_fuzzer +``` +2. Run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/policy_engine_fuzzer/vendor/policy_engine_fuzzer +``` diff --git a/fuzzer/policy_engine_fuzzer.cpp b/fuzzer/policy_engine_fuzzer.cpp new file mode 100644 index 00000000..435b7f97 --- /dev/null +++ b/fuzzer/policy_engine_fuzzer.cpp @@ -0,0 +1,353 @@ +/* + * 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) : mFdp(data, size){}; + void process(); + +private: + FuzzedDataProvider mFdp; + KeyId mKeyId; + License mLicense; + 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 = mLicense.add_key(); + int32_t keyType = mFdp.ConsumeIntegralInRange( + License::KeyContainer::SIGNING, License::KeyContainer::ENTITLEMENT); + key->set_type((License_KeyContainer_KeyType)keyType); + mKeyId = mFdp.ConsumeRandomLengthString(kMaxByte); + + LicenseIdentification *id = mLicense.mutable_id(); + if (mFdp.ConsumeBool()) { + id->set_version(kLicenseIDVersion); + } + if (mFdp.ConsumeBool()) { + id->set_type(mFdp.ConsumeBool() ? STREAMING : OFFLINE); + } + if (mFdp.ConsumeBool()) { + id->set_provider_session_token(mFdp.ConsumeRandomLengthString(kMaxByte)); + } + + if (mFdp.ConsumeBool()) { + mLicense.set_license_start_time(mFdp.ConsumeIntegral()); + } + + if (mFdp.ConsumeBool()) { + License_KeyContainer_OutputProtection *outputProtection = + key->mutable_required_protection(); + License_KeyContainer_OutputProtection_HDCP hdcpLevel = + (License_KeyContainer_OutputProtection_HDCP)mFdp + .ConsumeIntegralInRange(HDCP_NONE, HDCP_NO_DIGITAL_OUTPUT); + outputProtection->set_hdcp(hdcpLevel); + } + + if (mFdp.ConsumeBool()) { + const std::string ivString = mFdp.ConsumeRandomLengthString(kMaxByte); + key->set_iv(ivString); + } + + switch (keyType) { + case License::KeyContainer::SIGNING: { + if (mFdp.ConsumeBool()) { + mKeyId = mFdp.ConsumeBool() ? kSigningKeyId : mKeyId; + key->set_id(mKeyId); + } + break; + } + case License::KeyContainer::CONTENT: { + if (mFdp.ConsumeBool()) { + key->set_id(mKeyId); + } + if (mFdp.ConsumeBool()) { + int32_t level = mFdp.ConsumeIntegralInRange( + License::KeyContainer::SW_SECURE_CRYPTO, + License::KeyContainer::HW_SECURE_ALL); + key->set_level((License_KeyContainer_SecurityLevel)level); + } + + License_Policy *policy = mLicense.mutable_policy(); + policySetBool( + std::bind(&License_Policy::set_can_play, policy, std::placeholders::_1), + &mFdp); + policySetBool(std::bind(&License_Policy::set_can_persist, policy, + std::placeholders::_1), &mFdp); + policySetBool(std::bind(&License_Policy::set_can_renew, policy, + std::placeholders::_1), &mFdp); + policySetBool(std::bind(&License_Policy::set_renew_with_usage, policy, + std::placeholders::_1), &mFdp); + policySetBool(std::bind(&License_Policy::set_soft_enforce_rental_duration, + policy, std::placeholders::_1), &mFdp); + policySetBool(std::bind(&License_Policy::set_soft_enforce_playback_duration, + policy, std::placeholders::_1), &mFdp); + + policySetInt64(std::bind(&License_Policy::set_rental_duration_seconds, + policy, std::placeholders::_1), &mFdp); + policySetInt64(std::bind(&License_Policy::set_playback_duration_seconds, + policy, std::placeholders::_1), &mFdp); + policySetInt64(std::bind(&License_Policy::set_license_duration_seconds, + policy, std::placeholders::_1), &mFdp); + policySetInt64( + std::bind(&License_Policy::set_renewal_recovery_duration_seconds, + policy, std::placeholders::_1), &mFdp); + policySetInt64( + std::bind(&License_Policy::set_renewal_retry_interval_seconds, policy, + std::placeholders::_1), &mFdp); + policySetInt64( + std::bind(&License_Policy::set_renewal_delay_seconds, policy, + std::placeholders::_1), &mFdp); + + if (mFdp.ConsumeBool()) { + video_widevine::License::Policy::TimerDelayBase timeDelayBase = + (video_widevine::License::Policy::TimerDelayBase)mFdp.ConsumeIntegralInRange( + License_Policy_TimerDelayBase_TIMER_DELAY_BASE_UNSPECIFIED , + License_Policy_TimerDelayBase_FIRST_DECRYPT); + policy->set_initial_renewal_delay_base(timeDelayBase); + } + + if (mFdp.ConsumeBool()) { + std::string serverUrl = mFdp.ConsumeBool() + ? kRenewalServerUrl + : mFdp.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 (mFdp.ConsumeBool()) { + key->set_id(mKeyId); + } + + if (mFdp.ConsumeBool()) { + int32_t level = mFdp.ConsumeIntegralInRange( + License::KeyContainer::SW_SECURE_CRYPTO, + License::KeyContainer::HW_SECURE_ALL); + key->set_level((License_KeyContainer_SecurityLevel)level); + } + + if (mFdp.ConsumeBool()) { + policySetBool(std::bind(&License_Policy::set_can_persist, mLicense.mutable_policy(), + std::placeholders::_1), &mFdp); + } + + License::KeyContainer::OperatorSessionKeyPermissions *permissions = + key->mutable_operator_session_key_permissions(); + policySetBool( + std::bind(&License::KeyContainer::OperatorSessionKeyPermissions:: + set_allow_encrypt, + permissions, std::placeholders::_1), &mFdp); + policySetBool( + std::bind(&License::KeyContainer::OperatorSessionKeyPermissions:: + set_allow_decrypt, + permissions, std::placeholders::_1), &mFdp); + policySetBool(std::bind(&License::KeyContainer:: + OperatorSessionKeyPermissions::set_allow_sign, + permissions, std::placeholders::_1), &mFdp); + policySetBool( + std::bind(&License::KeyContainer::OperatorSessionKeyPermissions:: + set_allow_signature_verify, + permissions, std::placeholders::_1), &mFdp); + break; + } + case License::KeyContainer::ENTITLEMENT: + default: { + if (mFdp.ConsumeBool()) { + mKeyId = mFdp.ConsumeBool() ? kEntitlementKeyId : mKeyId; + key->set_id(mKeyId); + } + if (mFdp.ConsumeBool()) { + int32_t level = mFdp.ConsumeIntegralInRange( + License::KeyContainer::SW_SECURE_CRYPTO, + License::KeyContainer::HW_SECURE_ALL); + key->set_level((License_KeyContainer_SecurityLevel)level); + } + + License_Policy *policy = mLicense.mutable_policy(); + policySetBool( + std::bind(&License_Policy::set_can_play, policy, std::placeholders::_1), + &mFdp); + policySetBool(std::bind(&License_Policy::set_can_persist, policy, + std::placeholders::_1), &mFdp); + policySetBool(std::bind(&License_Policy::set_can_renew, policy, + std::placeholders::_1), &mFdp); + policySetBool(std::bind(&License_Policy::set_renew_with_usage, policy, + std::placeholders::_1), &mFdp); + policySetBool(std::bind(&License_Policy::set_soft_enforce_rental_duration, + policy, std::placeholders::_1), &mFdp); + policySetBool(std::bind(&License_Policy::set_soft_enforce_playback_duration, + policy, std::placeholders::_1), &mFdp); + + policySetInt64(std::bind(&License_Policy::set_rental_duration_seconds, + policy, std::placeholders::_1), &mFdp); + policySetInt64(std::bind(&License_Policy::set_playback_duration_seconds, + policy, std::placeholders::_1), &mFdp); + policySetInt64(std::bind(&License_Policy::set_license_duration_seconds, + policy, std::placeholders::_1), &mFdp); + policySetInt64( + std::bind(&License_Policy::set_renewal_recovery_duration_seconds, + policy, std::placeholders::_1), &mFdp); + policySetInt64( + std::bind(&License_Policy::set_renewal_retry_interval_seconds, policy, + std::placeholders::_1), &mFdp); + policySetInt64( + std::bind(&License_Policy::set_renewal_delay_seconds, policy, + std::placeholders::_1), &mFdp); + + if (mFdp.ConsumeBool()) { + video_widevine::License::Policy::TimerDelayBase timeDelayBase = (video_widevine::License::Policy::TimerDelayBase)mFdp.ConsumeIntegralInRange( + License_Policy_TimerDelayBase_TIMER_DELAY_BASE_UNSPECIFIED , + License_Policy_TimerDelayBase_FIRST_DECRYPT); + policy->set_initial_renewal_delay_base(timeDelayBase); + } + break; + } + } +} + +void PolicyEngineFuzzer::process() { + setUp(); + FuzzWvCdmEventListener fuzzWvCdmEventListener; + CdmSessionId sessionId = mFdp.ConsumeRandomLengthString(kMaxByte); + metrics::CryptoMetrics crypto_metrics; + std::unique_ptr cryptoSession( + wvcdm::CryptoSession::MakeCryptoSession(&crypto_metrics)); + std::unique_ptr policyEngine(new PolicyEngine( + sessionId, &fuzzWvCdmEventListener, cryptoSession.get())); + + while (mFdp.remaining_bytes()) { + auto invokePolicyEngineAPI = + mFdp.PickValueInArray>({ + [&]() { policyEngine->CanDecryptContent(mKeyId); }, + [&]() { policyEngine->CanUseKeyForSecurityLevel(mKeyId); }, + [&]() { policyEngine->OnTimerEvent(); }, + [&]() { + policyEngine->SetLicense( + mLicense, mFdp.ConsumeBool() /*defer_license_state_update*/); + }, + [&]() { + int32_t maxEntitledKey = + mFdp.ConsumeIntegralInRange(1, kMaxEntitledKey); + std::vector entitled_keys( + maxEntitledKey); + for (int i = 0; i < maxEntitledKey; ++i) { + entitled_keys[i].set_entitlement_key_id( + mFdp.ConsumeRandomLengthString(kMaxByte)); + entitled_keys[i].set_key_id( + mFdp.ConsumeBool() + ? mFdp.ConsumeRandomLengthString(kMaxByte) + : mKeyId); + } + policyEngine->SetEntitledLicenseKeys(entitled_keys); + }, + [&]() { policyEngine->SetLicenseForRelease(mLicense); }, + [&]() { policyEngine->BeginDecryption(); }, + [&]() { policyEngine->DecryptionEvent(); }, + [&]() { + policyEngine->UpdateLicense( + mLicense, mFdp.ConsumeBool() /*defer_license_state_update*/); + }, + [&]() { + policyEngine->UpdateLicenseState( + mFdp.ConsumeIntegral() /*current_time*/); + }, + [&]() { + int32_t width = mFdp.ConsumeIntegralInRange( + kMinResolution, kMaxResolution); + int32_t height = mFdp.ConsumeIntegralInRange( + kMinResolution, kMaxResolution); + policyEngine->NotifyResolution(width, height); + }, + [&]() { policyEngine->NotifySessionExpiration(); }, + [&]() { + CdmQueryMap query_info; + policyEngine->Query(&query_info); + }, + [&]() { + CdmKeyAllowedUsage key_usage; + policyEngine->QueryKeyAllowedUsage( + mKeyId, (mFdp.ConsumeBool() ? nullptr : &key_usage)); + }, + [&]() { + policyEngine->RestorePlaybackTimes( + mFdp.ConsumeIntegral() /*playback_start_time*/, + mFdp.ConsumeIntegral() /*last_playback_time*/, + mFdp.ConsumeIntegral() /*grace_period_end_time*/); + }, + [&]() { + policyEngine->HasLicenseOrRentalOrPlaybackDurationExpired( + mFdp.ConsumeIntegral()); + }, + }); + invokePolicyEngineAPI(); + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + PolicyEngineFuzzer policyEngineFuzzer(data, size); + policyEngineFuzzer.process(); + return 0; +} From 79bcb645f5fe0013bf79e59514f0758d2b97cb1c Mon Sep 17 00:00:00 2001 From: Akshata Kadam Date: Wed, 1 Feb 2023 12:15:08 +0530 Subject: [PATCH 2/2] Added content_decryption_fuzzer exec/s: 10 Test: ./content_decryption_fuzzer Bug: 265234582 Change-Id: If8132ed6c5db5794a444c03e7f37682674f26148 --- fuzzer/Android.bp | 6 + fuzzer/README.md | 25 ++ fuzzer/content_decryption_fuzzer.cpp | 342 +++++++++++++++++++++++++++ 3 files changed, 373 insertions(+) create mode 100644 fuzzer/content_decryption_fuzzer.cpp diff --git a/fuzzer/Android.bp b/fuzzer/Android.bp index 67be8bec..5d1da141 100644 --- a/fuzzer/Android.bp +++ b/fuzzer/Android.bp @@ -69,3 +69,9 @@ cc_fuzz { srcs: ["policy_engine_fuzzer.cpp"], defaults: ["libcdm_fuzzer_defaults"], } + +cc_fuzz { + name: "content_decryption_fuzzer", + srcs: ["content_decryption_fuzzer.cpp"], + defaults: ["libcdm_fuzzer_defaults"], +} diff --git a/fuzzer/README.md b/fuzzer/README.md index fc910aa6..adce4395 100644 --- a/fuzzer/README.md +++ b/fuzzer/README.md @@ -2,6 +2,7 @@ ## Table of contents + [policy_engine_fuzzer](#PolicyEngine) ++ [content_decryption_fuzzer](#ContentDecryption) # Fuzzer for PolicyEngine @@ -26,3 +27,27 @@ PolicyEngine supports the following parameters: $ adb sync data $ adb shell /data/fuzz/arm64/policy_engine_fuzzer/vendor/policy_engine_fuzzer ``` + +# Fuzzer for ContentDecryption + +ContentDecryption supports the following parameters: +1. Cert Authority (parameter name: "certAuthority") +2. Server Url (parameter name: "serverUrl") +3. Service Certificate (parameter name: "serviceCertificate") + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +|`certAuthority`| `String` |Value obtained from FuzzedDataProvider| +|`serverUrl`| `String` |Value obtained from FuzzedDataProvider| +|`serviceCertificate`| `String` |Value obtained from FuzzedDataProvider| + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) content_decryption_fuzzer +``` +2. Run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/content_decryption_fuzzer/vendor/content_decryption_fuzzer +``` diff --git a/fuzzer/content_decryption_fuzzer.cpp b/fuzzer/content_decryption_fuzzer.cpp new file mode 100644 index 00000000..68b22b7b --- /dev/null +++ b/fuzzer/content_decryption_fuzzer.cpp @@ -0,0 +1,342 @@ +/* + * 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 "cdm_client_property_set.h" +#include "cdm_identifier.h" +#include "wv_cdm_constants.h" +#include "wv_cdm_event_listener.h" +#include "wv_content_decryption_module.h" +#include + +using namespace wvcdm; +using namespace android; + +const std::string kAppId = "com.example.test"; +static constexpr int32_t kMaxByte = 256; +static constexpr int32_t kMinSetId = 1; +static constexpr int32_t kMaxSetId = 100; +static constexpr int32_t kMinSize = 1; +static constexpr int32_t kMaxSize = 1024; +static constexpr int32_t kMaxAppParamSize = 10; + +const std::string kSecurityLevel[] = {"QUERY_VALUE_SECURITY_LEVEL_L1", + "QUERY_VALUE_SECURITY_LEVEL_L2", + "QUERY_VALUE_SECURITY_LEVEL_L3"}; + +const std::string kQueryLevel[] = { + "QUERY_KEY_SECURITY_LEVEL", + "QUERY_KEY_CURRENT_HDCP_LEVEL", + "QUERY_KEY_MAX_HDCP_LEVEL", + "QUERY_KEY_USAGE_SUPPORT", + "QUERY_KEY_NUMBER_OF_OPEN_SESSIONS", + "QUERY_KEY_MAX_NUMBER_OF_SESSIONS", + "QUERY_KEY_OEMCRYPTO_API_VERSION", + "QUERY_KEY_CURRENT_SRM_VERSION", + "QUERY_KEY_SRM_UPDATE_SUPPORT", + "QUERY_KEY_WVCDM_VERSION", + "QUERY_KEY_RESOURCE_RATING_TIER", + "QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION", + "QUERY_KEY_DECRYPT_HASH_SUPPORT", + "QUERY_KEY_PROVISIONING_MODEL", + "QUERY_KEY_MAX_USAGE_TABLE_ENTRIES", + "QUERY_KEY_OEMCRYPTO_API_MINOR_VERSION", + "QUERY_KEY_ANALOG_OUTPUT_CAPABILITIES", + "QUERY_KEY_CAN_DISABLE_ANALOG_OUTPUT", + "QUERY_KEY_WATERMARKING_SUPPORT", + "QUERY_KEY_PRODUCTION_READY", + "QUERY_KEY_SYSTEM_ID", + "QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN", + "QUERY_KEY_DEVICE_ID", + "QUERY_KEY_PROVISIONING_ID", +}; +const std::string kInitDataTypes[] = { + HLS_INIT_DATA_FORMAT, ISO_BMFF_VIDEO_MIME_TYPE, ISO_BMFF_AUDIO_MIME_TYPE, + CENC_INIT_DATA_FORMAT, WEBM_VIDEO_MIME_TYPE, WEBM_AUDIO_MIME_TYPE, + WEBM_INIT_DATA_FORMAT, +}; + +class FuzzWvCdmEventListener : public WvCdmEventListener { +public: + 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 FuzzCdmClientPropertySet : public CdmClientPropertySet { +public: + FuzzCdmClientPropertySet(FuzzedDataProvider *fdp) { + mFdp = fdp; + mSecurityLevel = fdp->ConsumeBool() + ? fdp->ConsumeRandomLengthString(kMaxByte) + : fdp->PickValueInArray(kSecurityLevel); + mUsePrivacyMode = fdp->ConsumeBool(); + mIsSessionSharingEnabled = fdp->ConsumeBool(); + mSessionSharingId = fdp->ConsumeIntegral(); + mAppId = + fdp->ConsumeBool() ? kAppId : fdp->ConsumeRandomLengthString(kMaxByte); + } + + const std::string &security_level() const override { return mSecurityLevel; } + + bool use_privacy_mode() const override { return mUsePrivacyMode; } + + const std::string &service_certificate() const override { + return mRawServiceCertificate; + } + + void set_service_certificate(const std::string &cert) override { + if (mFdp->ConsumeBool()) { + mRawServiceCertificate = cert; + } + } + + bool is_session_sharing_enabled() const override { + return mIsSessionSharingEnabled; + } + + uint32_t session_sharing_id() const override { + uint32_t sessionId = 0; + if (mFdp->ConsumeBool()) { + sessionId = mSessionSharingId; + } + return sessionId; + } + + bool use_atsc_mode() const override { return false; } + + void set_session_sharing_id(uint32_t id) override { + if (mFdp->ConsumeBool()) { + mSessionSharingId = id; + } + } + + const std::string &app_id() const override { return mAppId; } + + void enable_privacy_mode() { + if (mFdp->ConsumeBool()) { + mUsePrivacyMode = true; + } + } + +private: + FuzzedDataProvider *mFdp; + std::string mSecurityLevel; + std::string mRawServiceCertificate; + std::string mAppId; + uint32_t mSessionSharingId; + bool mUsePrivacyMode; + bool mIsSessionSharingEnabled; +}; + +class ContentDecryptionFuzzer { +public: + ContentDecryptionFuzzer(const uint8_t *data, size_t size) : mFdp(data, size) { + mDecryptor = sp::make(); + FuzzWvCdmEventListener fuzzWvCdmEventListener; + mFuzzCdmClientPropertySet.reset(new FuzzCdmClientPropertySet(&mFdp)); + + if (mFdp.ConsumeBool()) { + mCdmIdentifier = kDefaultCdmIdentifier; + } else { + mCdmIdentifier.spoid = mFdp.ConsumeRandomLengthString(kMaxByte); + mCdmIdentifier.origin = mFdp.ConsumeRandomLengthString(kMaxByte); + mCdmIdentifier.app_package_name = + mFdp.ConsumeRandomLengthString(kMaxByte); + mCdmIdentifier.unique_id = mFdp.ConsumeIntegral(); + mCdmIdentifier.user_id = mFdp.ConsumeIntegral(); + } + mDecryptor->OpenSession(KEY_SYSTEM, mFuzzCdmClientPropertySet.get(), + mCdmIdentifier, &fuzzWvCdmEventListener, + &mSessionId); + }; + ~ContentDecryptionFuzzer() { mDecryptor->CloseSession(mSessionId); } + void process(); + +private: + FuzzedDataProvider mFdp; + std::unique_ptr mFuzzCdmClientPropertySet; + CdmSessionId mSessionId; + CdmIdentifier mCdmIdentifier; + sp mDecryptor; + void invokeDecryptorSessionAPIs(); + void invokeProvisionAPIs(); +}; + +void ContentDecryptionFuzzer::invokeDecryptorSessionAPIs() { + CdmKeySetId keySetId = mFdp.ConsumeRandomLengthString(kMaxByte); + while (mFdp.remaining_bytes()) { + auto invokeDecryptionSessionAPI = + mFdp.PickValueInArray>({ + [&]() { mDecryptor->IsOpenSession(mSessionId); }, + [&]() { + CdmAppParameterMap appParameters; + int32_t maxSize = + mFdp.ConsumeIntegralInRange(0, kMaxAppParamSize); + for (size_t i = 0; i < maxSize; ++i) { + std::string key = mFdp.ConsumeRandomLengthString(kMaxByte); + std::string value = mFdp.ConsumeRandomLengthString(kMaxByte); + std::string cdmKey(key.c_str(), key.size()); + std::string cdmValue(value.c_str(), value.size()); + appParameters[cdmKey] = cdmValue; + } + + std::string initDataType = mFdp.PickValueInArray(kInitDataTypes); + + CdmInitData initData = mFdp.ConsumeRandomLengthString(kMaxByte); + + CdmLicenseType licenseType = + (CdmLicenseType)mFdp.ConsumeIntegralInRange( + kLicenseTypeOffline, kLicenseTypeEmbeddedKeyData); + + CdmKeyRequest keyRequest; + keyRequest.message = mFdp.ConsumeRandomLengthString(kMaxByte); + + keyRequest.type = + (CdmKeyRequestType)mFdp.ConsumeIntegralInRange( + kKeyRequestTypeUnknown, kKeyRequestTypeUpdate); + + keyRequest.url = mFdp.ConsumeRandomLengthString(kMaxByte); + + FuzzCdmClientPropertySet *propertySet = + mFdp.ConsumeBool() ? mFuzzCdmClientPropertySet.get() + : nullptr; + + mDecryptor->GenerateKeyRequest( + mSessionId, keySetId, initDataType, initData, licenseType, + appParameters, propertySet, mCdmIdentifier, + (mFdp.ConsumeBool() ? nullptr : &keyRequest)); + }, + [&]() { + CdmKeyResponse response = + mFdp.ConsumeRandomLengthString(kMaxByte); + mDecryptor->AddKey(mSessionId, response, &keySetId); + }, + [&]() { mDecryptor->RestoreKey(mSessionId, keySetId); }, + [&]() { mDecryptor->RemoveKeys(mSessionId); }, + [&]() { + RequestedSecurityLevel securityLevel = + (RequestedSecurityLevel)mFdp.ConsumeIntegral(); + std::string outputStr; + std::string queryToken = + mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxByte) + : mFdp.PickValueInArray(kQueryLevel); + ; + mDecryptor->QueryStatus( + securityLevel, queryToken.c_str(), + (mFdp.ConsumeBool() ? nullptr : &outputStr)); + }, + [&]() { + CdmQueryMap keyInfo; + mDecryptor->QuerySessionStatus( + mSessionId, (mFdp.ConsumeBool() ? nullptr : &keyInfo)); + }, + [&]() { + CdmQueryMap keyInfo; + mDecryptor->QueryKeyStatus( + mSessionId, (mFdp.ConsumeBool() ? nullptr : &keyInfo)); + }, + [&]() { + CdmQueryMap keyInfo; + mDecryptor->QueryOemCryptoSessionId( + mSessionId, (mFdp.ConsumeBool() ? nullptr : &keyInfo)); + }, + [&]() { + CdmSecurityLevel level = + (CdmSecurityLevel)mFdp.ConsumeIntegralInRange( + kSecurityLevelUninitialized, kSecurityLevelUnknown); + mDecryptor->IsSecurityLevelSupported(level); + }, + + }); + invokeDecryptionSessionAPI(); + } +} + +void ContentDecryptionFuzzer::invokeProvisionAPIs() { + + while (mFdp.remaining_bytes()) { + auto invokeProvisionAPI = + mFdp.PickValueInArray>({ + [&]() { + CdmSecurityLevel level = + (CdmSecurityLevel)mFdp.ConsumeIntegralInRange( + kSecurityLevelUninitialized, kSecurityLevelUnknown); + mDecryptor->Unprovision(level, mCdmIdentifier); + }, + [&]() { + CdmKeyMessage keyMsg; + CdmCertificateType certificateType = + (CdmCertificateType)mFdp.ConsumeIntegral(); + + std::string certAuthority = + mFdp.ConsumeRandomLengthString(kMaxByte); + + RequestedSecurityLevel securityLevel = + (RequestedSecurityLevel)mFdp.ConsumeIntegral(); + + std::string serverUrl = mFdp.ConsumeRandomLengthString(kMaxByte); + + std::string serviceCertificate = + mFdp.ConsumeRandomLengthString(kMaxByte); + + mDecryptor->GetProvisioningRequest( + certificateType, certAuthority, mCdmIdentifier, + serviceCertificate, securityLevel, + (mFdp.ConsumeBool() ? nullptr : &keyMsg), + (mFdp.ConsumeBool() ? nullptr : &serverUrl)); + }, + [&]() { + std::string response = mFdp.ConsumeRandomLengthString(kMaxByte); + RequestedSecurityLevel securityLevel = + (RequestedSecurityLevel)mFdp.ConsumeIntegral(); + std::string cert, wrappedKey; + mDecryptor->HandleProvisioningResponse( + mCdmIdentifier, response, securityLevel, + (mFdp.ConsumeBool() ? nullptr : &cert), + (mFdp.ConsumeBool() ? nullptr : &wrappedKey)); + }, + [&]() { + CdmSecurityLevel level = + (CdmSecurityLevel)mFdp.ConsumeIntegralInRange( + kSecurityLevelUninitialized, kSecurityLevelUnknown); + std::string origin = mFdp.ConsumeRandomLengthString(kMaxByte); + std::string spoid = mFdp.ConsumeRandomLengthString(kMaxByte); + mDecryptor->IsProvisioned( + level, origin, spoid, + mFdp.ConsumeBool() /*atsc_mode_enabled*/); + }, + }); + invokeProvisionAPI(); + } +} + +void ContentDecryptionFuzzer::process() { + auto invokeContentDecryptionAPI = + mFdp.PickValueInArray>({ + [&]() { invokeDecryptorSessionAPIs(); }, + [&]() { invokeProvisionAPIs(); }, + }); + invokeContentDecryptionAPI(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + ContentDecryptionFuzzer contentDecryptionFuzzer(data, size); + contentDecryptionFuzzer.process(); + return 0; +}