/* * 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_random.h" #include "cdm_session.h" #include "cdm_session_map.h" #include "device_files.h" #include "license.h" #include "metrics_collections.h" #include "timer_metric.h" #include "vendor_widevine_fuzz_helper.h" #include "wv_cdm_event_listener.h" #include "wv_cdm_types.h" #include using namespace wvcdm; using namespace wvcdm::metrics; using namespace wvutil; static constexpr int32_t kMinByte = 0; static constexpr int32_t kMaxByte = 256; static constexpr int32_t kMaxId = 100; static constexpr int32_t kMinId = 0; static constexpr int8_t kMaxSize = 16; static std::string kSessionId = "sid"; static std::string kKeyId = "k" + kSessionId; std::string kInitType[] = {ISO_BMFF_VIDEO_MIME_TYPE, ISO_BMFF_AUDIO_MIME_TYPE, WEBM_VIDEO_MIME_TYPE, WEBM_AUDIO_MIME_TYPE, CENC_INIT_DATA_FORMAT, HLS_INIT_DATA_FORMAT, WEBM_INIT_DATA_FORMAT}; class FuzzEventListener : 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 CdmSessionFuzzer { public: CdmSessionFuzzer(const uint8_t *data, size_t size) : fdp_(data, size){}; void Process(); private: FuzzedDataProvider fdp_; void InvokeCdmSessionAPIs(CdmSession *cdm_session); void InvokeCdmSessionMapAPIs(); CdmResponseType InitCdmSession(CdmSession *cdm_session, FuzzCdmClientPropertySet *fuzz_cdm_client_property_set, CdmSessionId forced_session_id, FuzzEventListener *fuzz_event_listener, bool forced_level); void CreateResponse(CdmKeyResponse *response); void SetLicenseMsg(std::string *msg); void StoreLicense(DeviceFiles::CdmLicenseData license_data); }; class FuzzFile : public File { public: FuzzFile(FuzzedDataProvider *fdp) : fdp_(fdp) {} ssize_t Read(char *buffer, size_t bytes) { std::vector init_data = fdp_->ConsumeBytes( fdp_->ConsumeIntegralInRange(kMinByte, bytes)); buffer = init_data.data(); return init_data.size(); }; ssize_t Write(const char * /* buffer*/, size_t bytes) { return fdp_->ConsumeIntegralInRange(kMinByte, bytes); }; private: FuzzedDataProvider *fdp_; }; class FuzzFileSystem : public FileSystem { public: FuzzFileSystem(FuzzedDataProvider *fdp) : fdp_(fdp) {} std::unique_ptr Open(const std::string &, int) { return std::unique_ptr(new FuzzFile(fdp_)); } bool Exists(const std::string &) { return fdp_->ConsumeBool(); } bool Remove(const std::string &) { return fdp_->ConsumeBool(); } ssize_t FileSize(const std::string &) { return fdp_->ConsumeIntegralInRange(kMinByte, kMaxByte); } bool List(const std::string &, std::vector *) { return fdp_->ConsumeBool(); } private: FuzzedDataProvider *fdp_; }; void policySetBool(std::function function, FuzzedDataProvider *fdp) { if (fdp->ConsumeBool()) { function(fdp->ConsumeBool()); } } void CdmSessionFuzzer::SetLicenseMsg(std::string *msg) { video_widevine::License license; if (fdp_.ConsumeBool()) { std::string provider_client_token = fdp_.ConsumeRandomLengthString(kMaxByte); license.set_provider_client_token(provider_client_token); } if (fdp_.ConsumeBool()) { uint32_t scheme = fdp_.ConsumeIntegral(); license.set_protection_scheme(scheme); } video_widevine::License::KeyContainer *container = license.add_key(); if (fdp_.ConsumeBool()) { uint32_t key_type = fdp_.ConsumeIntegralInRange( video_widevine::License::KeyContainer::SIGNING, video_widevine::License::KeyContainer::ENTITLEMENT); container->set_type((video_widevine::License_KeyContainer_KeyType)key_type); } if (fdp_.ConsumeBool()) { container->mutable_iv(); } if (fdp_.ConsumeBool()) { std::string key_string = fdp_.ConsumeRandomLengthString(kMaxByte); container->set_key(key_string); } if (fdp_.ConsumeBool()) { container->mutable_id(); } if (fdp_.ConsumeBool()) { container->mutable_track_label(); } video_widevine::License_KeyContainer_KeyControl *key_control = container->mutable_key_control(); if (fdp_.ConsumeBool()) { key_control->mutable_key_control_block(); } if (fdp_.ConsumeBool()) { key_control->mutable_iv(); } if (fdp_.ConsumeBool()) { video_widevine::License_Policy *policy = license.mutable_policy(); policySetBool(std::bind(&video_widevine::License_Policy::set_can_persist, policy, std::placeholders::_1), &fdp_); if (fdp_.ConsumeBool()) { std::string server_url = fdp_.ConsumeRandomLengthString(kMaxByte); policy->set_renewal_server_url(server_url); } policySetBool( std::bind(&video_widevine::License_Policy::set_always_include_client_id, policy, std::placeholders::_1), &fdp_); } LicenseIdentification *id = license.mutable_id(); if (fdp_.ConsumeBool()) { id->set_type(fdp_.ConsumeBool() ? video_widevine::STREAMING : video_widevine::OFFLINE); } if (fdp_.ConsumeBool()) { std::string provider_session_token = fdp_.ConsumeRandomLengthString(kMaxByte); id->set_provider_session_token(provider_session_token); } license.SerializeToString(msg); } void CdmSessionFuzzer::CreateResponse(CdmKeyResponse *response) { video_widevine::SignedMessage signed_message; video_widevine::SignedMessage type; signed_message.set_type(video_widevine::SignedMessage::LICENSE); signed_message.mutable_service_version_info(); std::string sign = fdp_.ConsumeRandomLengthString(kMaxByte); sign.resize(kMaxId, '0'); signed_message.set_signature(sign); std::string key = fdp_.ConsumeRandomLengthString(kMaxByte); key.resize(kMaxId, '0'); signed_message.set_session_key(key); std::string message = fdp_.ConsumeRandomLengthString(kMaxByte); message.resize(kMaxId, '0'); signed_message.set_oemcrypto_core_message(message); signed_message.set_using_secondary_key(fdp_.ConsumeBool()); std::string set_msg = fdp_.ConsumeRandomLengthString(kMaxByte); signed_message.set_msg(set_msg); signed_message.SerializeToString(response); } CdmResponseType CdmSessionFuzzer::InitCdmSession( CdmSession *cdm_session, FuzzCdmClientPropertySet *fuzz_cdm_client_property_set, CdmSessionId forced_session_id, FuzzEventListener *fuzz_event_listener, bool forced_level) { CdmResponseType result; if (fdp_.ConsumeBool()) { result = cdm_session->Init(fuzz_cdm_client_property_set /*cdm_client_property_set*/, &forced_session_id /*forced_session_id*/, fuzz_event_listener /*event_listener*/, forced_level); } else { result = cdm_session->Init(fuzz_cdm_client_property_set /*cdm_client_property_set*/); } return result; } void CdmSessionFuzzer::StoreLicense(DeviceFiles::CdmLicenseData license_data) { FuzzFileSystem file_system_dev(&fdp_); DeviceFiles deviceFiles(fdp_.ConsumeBool() ? &file_system_dev : nullptr); deviceFiles.Init((CdmSecurityLevel)fdp_.ConsumeIntegralInRange( kSecurityLevelL1, kSecurityLevelL3)); std::map app_parameters; app_parameters[fdp_.ConsumeBytesAsString(kMaxByte)] = fdp_.ConsumeBytesAsString(kMaxByte); license_data.key_set_id = std::to_string(fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); license_data.state = (CdmOfflineLicenseState)fdp_.ConsumeIntegralInRange( kLicenseStateActive, kLicenseStateUnknown); license_data.pssh_data = fdp_.ConsumeRandomLengthString(kMaxByte); license_data.license_request = fdp_.ConsumeRandomLengthString(kMaxByte); license_data.license = fdp_.ConsumeRandomLengthString(kMaxByte); license_data.license_renewal_request = fdp_.ConsumeRandomLengthString(kMaxByte); license_data.license_renewal = fdp_.ConsumeRandomLengthString(kMaxByte); license_data.release_server_url = fdp_.ConsumeRandomLengthString(kMaxByte); license_data.playback_start_time = fdp_.ConsumeIntegral(); license_data.last_playback_time = fdp_.ConsumeIntegral(); license_data.grace_period_end_time = fdp_.ConsumeIntegral(); license_data.app_parameters = app_parameters; license_data.usage_entry = fdp_.ConsumeRandomLengthString(kMaxByte); license_data.usage_entry_index = fdp_.ConsumeIntegral(); license_data.drm_certificate = fdp_.ConsumeRandomLengthString(kMaxByte); license_data.wrapped_private_key = CryptoWrappedKey( (CryptoWrappedKey::Type)fdp_.ConsumeIntegralInRange( CryptoWrappedKey::kUninitialized, CryptoWrappedKey::kEcc), fdp_.ConsumeRandomLengthString(kMaxByte)); DeviceFiles::ResponseType result; deviceFiles.StoreLicense(license_data, &result); } void CdmSessionFuzzer::InvokeCdmSessionAPIs(CdmSession *cdm_session) { CdmKeyResponse response; CreateResponse(&response); CdmKeyRequest key_request; CdmKeyMessage signed_request; key_request.message = signed_request; key_request.type = (CdmKeyRequestType)fdp_.ConsumeIntegralInRange( kKeyRequestTypeUnknown, kKeyRequestTypeRelease); std::string url; key_request.url = url; while (fdp_.remaining_bytes()) { auto invokeCdmSessionAPI = fdp_.PickValueInArray< const std::function>( {[&]() { cdm_session->AddKey(response /*key_response*/); }, [&]() { FuzzCdmClientPropertySet property_set(&fdp_); property_set.enable_privacy_mode(); property_set.set_service_certificate(kTestSignedCertificate); PropertiesTestPeer::ForceReinit(); PropertiesTestPeer::AddSessionPropertySet( fdp_.ConsumeBool() ? kTestSessionId1 : fdp_.ConsumeRandomLengthString(kMaxByte), &property_set); std::string raw_service_certificate; PropertiesTestPeer::GetServiceCertificate( fdp_.ConsumeBool() ? kTestSessionId1 : fdp_.ConsumeRandomLengthString(kMaxByte), &raw_service_certificate); cdm_session->SetServiceCertificate( raw_service_certificate /*service_certificate*/); }, [&]() { CdmQueryMap query_response; cdm_session->QueryKeyStatus(&query_response /*query_response*/); }, [&]() { const std::string oec_version = std::to_string( fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); std::string data_type = fdp_.PickValueInArray(kInitType); std::unique_ptr init_data(new InitializationData( data_type, a2bs_hex(fdp_.ConsumeRandomLengthString(kMaxByte)), oec_version)); CdmLicenseType license_type = (CdmLicenseType)fdp_.ConsumeIntegralInRange( kLicenseTypeOffline, kLicenseTypeEmbeddedKeyData); CdmAppParameterMap app_parameters; cdm_session->GenerateKeyRequest( *init_data /*init_data*/, license_type /*license_type*/, app_parameters /*app_parameters*/, &key_request /*key_request*/); }, [&]() { CdmQueryMap query_response; cdm_session->QueryStatus(&query_response /*query_response*/); }, [&]() { DeviceFiles::CdmLicenseData license_data; StoreLicense(license_data); std::string random_data = fdp_.ConsumeRandomLengthString(kMaxByte); const CdmKeySetId key_set_id = fdp_.ConsumeBool() ? (kKeyId + wvutil::b2a_hex(random_data)) : std::to_string( fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); CdmLicenseType license_type = (CdmLicenseType)fdp_.ConsumeIntegralInRange( kLicenseTypeOffline, kLicenseTypeEmbeddedKeyData); int error_detail = fdp_.ConsumeIntegral(); cdm_session->RestoreOfflineSession(key_set_id /*key_set_id*/, license_type /*license_type*/, &error_detail /*error_detail*/); }, [&]() { DeviceFiles::CdmUsageData usage_Data; usage_Data.provider_session_token = fdp_.ConsumeRandomLengthString(kMaxByte); usage_Data.license_request = fdp_.ConsumeRandomLengthString(kMaxByte); usage_Data.license = fdp_.ConsumeRandomLengthString(kMaxByte); std::string random_data = fdp_.ConsumeRandomLengthString(kMaxByte); usage_Data.key_set_id = fdp_.ConsumeBool() ? (kKeyId + wvutil::b2a_hex(random_data)) : std::to_string( fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); usage_Data.usage_entry = fdp_.ConsumeRandomLengthString(kMaxByte); usage_Data.usage_entry_index = fdp_.ConsumeIntegral(); usage_Data.drm_certificate = fdp_.ConsumeRandomLengthString(kMaxByte); usage_Data.wrapped_private_key = CryptoWrappedKey( (CryptoWrappedKey::Type)fdp_.ConsumeIntegralInRange( CryptoWrappedKey::kUninitialized, CryptoWrappedKey::kEcc), fdp_.ConsumeRandomLengthString(kMaxByte)); int error_detail = fdp_.ConsumeIntegral(); cdm_session->RestoreUsageSession(usage_Data /*usage_data*/, &error_detail /*error_detail*/); }, [&]() { const std::string key_id = std::to_string( fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); CdmKeyAllowedUsage key_usage; cdm_session->QueryKeyAllowedUsage(key_id /*key_id*/, &key_usage /*key_usage*/); }, [&]() { CdmQueryMap query_response; cdm_session->QueryOemCryptoSessionId( &query_response /*query_response*/); }, [&]() { const KeyId key_id = std::to_string( fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); CdmDecryptionParametersV16 params(key_id); const uint8_t encrypt_buffer_param = fdp_.ConsumeIntegral(); void *decrypt_buffer_param; size_t decrypt_buffer_offset_param = fdp_.ConsumeIntegral(); size_t length = fdp_.ConsumeIntegral(); const std::vector iv = fdp_.ConsumeBytes(kMaxSize); CdmDecryptionSample sample(&encrypt_buffer_param, decrypt_buffer_param, decrypt_buffer_offset_param, length, iv); CdmDecryptionSubsample sub_sample(fdp_.ConsumeIntegral(), fdp_.ConsumeIntegral()); sample.subsamples.push_back(sub_sample); params.samples.push_back(sample); cdm_session->Decrypt(params /*parameters*/); }, [&]() { cdm_session->GenerateRenewalRequest(&key_request /*key_request*/); }, [&]() { cdm_session->RenewKey(response /*key_response*/); }, [&]() { const KeyId key_id = std::to_string( fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); cdm_session->IsKeyLoaded(key_id /*key_id*/); }, [&]() { cdm_session->GetDurationRemaining(); }, [&]() { cdm_session->NotifyResolution( fdp_.ConsumeIntegral() /*width*/, fdp_.ConsumeIntegral() /*height*/); }, [&]() { cdm_session->OnTimerEvent(fdp_.ConsumeBool() /*update_usage*/); }, [&]() { const CdmKeySetId key_set_id = kKeyId + fdp_.ConsumeRandomLengthString(kMaxByte); cdm_session->OnKeyReleaseEvent(key_set_id /*key_set_id*/); }, [&]() { std::string app_id = kSessionId + std::to_string( fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); cdm_session->GetApplicationId(&app_id /*app_id*/); }, [&]() { cdm_session->UpdateUsageEntryInformation(); }, [&]() { cdm_session->GenerateSessionId(); }, [&]() { const std::string in_buffer = fdp_.ConsumeRandomLengthString(kMaxByte); const std::string key_id = std::to_string( fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); const std::string iv = fdp_.ConsumeRandomLengthString(kMaxByte); CdmEncryptionAlgorithm algorithm = fdp_.ConsumeBool() ? CdmEncryptionAlgorithm::kEncryptionAlgorithmAesCbc128 : CdmEncryptionAlgorithm::kEncryptionAlgorithmUnknown; std::string out_buffer = fdp_.ConsumeRandomLengthString(kMaxByte); cdm_session->GenericEncrypt(in_buffer /*in_buffer*/, key_id /*key_id*/, iv, algorithm, &out_buffer /*out_buffer*/); }, [&]() { const std::string in_buffer = fdp_.ConsumeRandomLengthString(kMaxByte); const std::string key_id = std::to_string( fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); const std::string iv = fdp_.ConsumeRandomLengthString(kMaxByte); CdmEncryptionAlgorithm algorithm = fdp_.ConsumeBool() ? CdmEncryptionAlgorithm::kEncryptionAlgorithmAesCbc128 : CdmEncryptionAlgorithm::kEncryptionAlgorithmUnknown; std::string out_buffer = fdp_.ConsumeRandomLengthString(kMaxByte); cdm_session->GenericDecrypt(in_buffer /*in_buffer*/, key_id /*key_id*/, iv, algorithm, &out_buffer /*out_buffer*/); }, [&]() { const std::string message = fdp_.ConsumeRandomLengthString(kMaxByte); const std::string key_id = std::to_string( fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); CdmSigningAlgorithm algorithm = fdp_.ConsumeBool() ? CdmSigningAlgorithm::kSigningAlgorithmHmacSha256 : CdmSigningAlgorithm::kSigningAlgorithmUnknown; std::string signature = fdp_.ConsumeRandomLengthString(kMaxByte); cdm_session->GenericSign(message, key_id /*key_id*/, algorithm, &signature); }, [&]() { const std::string message = fdp_.ConsumeRandomLengthString(kMaxByte); const std::string key_id = std::to_string( fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); CdmSigningAlgorithm algorithm = fdp_.ConsumeBool() ? CdmSigningAlgorithm::kSigningAlgorithmHmacSha256 : CdmSigningAlgorithm::kSigningAlgorithmUnknown; const std::string signature = fdp_.ConsumeRandomLengthString(kMaxByte); cdm_session->GenericVerify(message, key_id /*key_id*/, algorithm, signature); }, [&]() { uint32_t frame_number = fdp_.ConsumeIntegral(); const std::string hash = fdp_.ConsumeRandomLengthString(kMaxByte); cdm_session->SetDecryptHash(frame_number /*frame_number*/, hash); }, [&]() { std::string hash_error_string = fdp_.ConsumeRandomLengthString(kMaxByte); cdm_session->GetDecryptHashError( &hash_error_string /*hash_error_string*/); }, [&]() { cdm_session->ReleaseKey(response /*key_response*/); }, [&]() { cdm_session->GenerateReleaseRequest(&key_request /*key_request*/); }}); invokeCdmSessionAPI(); } UsageEntryIndex entry_index = fdp_.ConsumeIntegral(); cdm_session->DeleteUsageEntry(entry_index /*usage_entry_index*/); cdm_session->DeleteLicenseFile(); cdm_session->RemoveKeys(); cdm_session->RemoveLicense(); } void CdmSessionFuzzer::InvokeCdmSessionMapAPIs() { FuzzFileSystem file_system_map(&fdp_); CdmSessionMap cdm_session_map; std::shared_ptr cdm_metrics_map( new metrics::SessionMetrics); CdmSession *session = new CdmSession(&file_system_map, cdm_metrics_map); const CdmSessionId forced_session_id = kSessionId + std::to_string(fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); cdm_session_map.Add(forced_session_id /*id*/, session /*session*/); while (fdp_.remaining_bytes()) { auto invokeCdmSessionMapAPI = fdp_.PickValueInArray>({ [&]() { cdm_session_map.Exists(forced_session_id /*id*/); }, [&]() { cdm_session_map.Size(); }, [&]() { std::shared_ptr cdm_session_shared; cdm_session_map.FindSession(forced_session_id /*id*/, &cdm_session_shared /*session*/); }, [&]() { CdmSessionList sessions; cdm_session_map.GetSessionList(sessions); }, }); invokeCdmSessionMapAPI(); cdm_session_map.CloseSession(forced_session_id); } } void CdmSessionFuzzer::Process() { if (fdp_.ConsumeBool()) { FuzzFileSystem file_system(&fdp_); FuzzEventListener fuzz_event_listener; std::shared_ptr metrics = std::make_shared(); std::unique_ptr cdm_session = std::make_unique(&file_system, metrics); std::unique_ptr fuzz_cdm_client_property_set( new FuzzCdmClientPropertySet(&fdp_)); const CdmSessionId forced_session_id = kSessionId + std::to_string(fdp_.ConsumeIntegralInRange(kMinId, kMaxId)); bool forced_level = fdp_.ConsumeBool(); CdmResponseType result = InitCdmSession(cdm_session.get(), fuzz_cdm_client_property_set.get(), forced_session_id, &fuzz_event_listener, forced_level); if (result == NO_ERROR) { InvokeCdmSessionAPIs(cdm_session.get()); } } else { InvokeCdmSessionMapAPIs(); } } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { CdmSessionFuzzer cdm_session_fuzzer(data, size); cdm_session_fuzzer.Process(); return 0; }