/* * 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) : mFdp(data, size){}; void process(); private: FuzzedDataProvider mFdp; void invokeCdmSessionAPIs(CdmSession *cdmSession); void invokeCdmSessionMapAPIs(); CdmResponseType initCdmSession(CdmSession *cdmSession, FuzzCdmClientPropertySet *fuzzCdmClientPropertySet, CdmSessionId forcedsessionId, FuzzEventListener *fuzzeventListener, bool forcedLevel); void createResponse(CdmKeyResponse *response); void setLicenseMsg(std::string *msg); void storeLicense(DeviceFiles::CdmLicenseData licenseData); }; class FuzzFile : public File { public: FuzzFile(FuzzedDataProvider *fdp) : mFdp(fdp) {} ssize_t Read(char *buffer, size_t bytes) { std::vector initData = mFdp->ConsumeBytes( mFdp->ConsumeIntegralInRange(kMinByte, bytes)); buffer = initData.data(); return initData.size(); }; ssize_t Write(const char * /* buffer*/, size_t bytes) { return mFdp->ConsumeIntegralInRange(kMinByte, bytes); }; private: FuzzedDataProvider *mFdp; }; class FuzzFileSystem : public FileSystem { public: FuzzFileSystem(FuzzedDataProvider *fdp) : mFdp(fdp) {} std::unique_ptr Open(const std::string &, int) { return std::unique_ptr(new FuzzFile(mFdp)); } bool Exists(const std::string &) { return mFdp->ConsumeBool(); } bool Remove(const std::string &) { return mFdp->ConsumeBool(); } ssize_t FileSize(const std::string &) { return mFdp->ConsumeIntegralInRange(kMinByte, kMaxByte); } bool List(const std::string &, std::vector *) { return mFdp->ConsumeBool(); } private: FuzzedDataProvider *mFdp; }; void policySetBool(std::function function, FuzzedDataProvider *fdp) { if (fdp->ConsumeBool()) { function(fdp->ConsumeBool()); } } void CdmSessionFuzzer::setLicenseMsg(std::string *msg) { video_widevine::License license; if (mFdp.ConsumeBool()) { std::string provider_client_token = mFdp.ConsumeRandomLengthString(kMaxByte); license.set_provider_client_token(provider_client_token); } if (mFdp.ConsumeBool()) { uint32_t scheme = mFdp.ConsumeIntegral(); license.set_protection_scheme(scheme); } video_widevine::License::KeyContainer *container = license.add_key(); if (mFdp.ConsumeBool()) { uint32_t keyType = mFdp.ConsumeIntegralInRange( video_widevine::License::KeyContainer::SIGNING, video_widevine::License::KeyContainer::ENTITLEMENT); container->set_type((video_widevine::License_KeyContainer_KeyType)keyType); } if (mFdp.ConsumeBool()) { container->mutable_iv(); } if (mFdp.ConsumeBool()) { std::string keyString = mFdp.ConsumeRandomLengthString(kMaxByte); container->set_key(keyString); } if (mFdp.ConsumeBool()) { container->mutable_id(); } if (mFdp.ConsumeBool()) { container->mutable_track_label(); } video_widevine::License_KeyContainer_KeyControl *keyControl = container->mutable_key_control(); if (mFdp.ConsumeBool()) { keyControl->mutable_key_control_block(); } if (mFdp.ConsumeBool()) { keyControl->mutable_iv(); } if (mFdp.ConsumeBool()) { video_widevine::License_Policy *policy = license.mutable_policy(); policySetBool(std::bind(&video_widevine::License_Policy::set_can_persist, policy, std::placeholders::_1), &mFdp); if (mFdp.ConsumeBool()) { std::string server_url = mFdp.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), &mFdp); } LicenseIdentification *id = license.mutable_id(); if (mFdp.ConsumeBool()) { id->set_type(mFdp.ConsumeBool() ? video_widevine::STREAMING : video_widevine::OFFLINE); } if (mFdp.ConsumeBool()) { std::string provider_session_token = mFdp.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 = mFdp.ConsumeRandomLengthString(kMaxByte); sign.resize(kMaxId, '0'); signed_message.set_signature(sign); std::string key = mFdp.ConsumeRandomLengthString(kMaxByte); key.resize(kMaxId, '0'); signed_message.set_session_key(key); std::string message = mFdp.ConsumeRandomLengthString(kMaxByte); message.resize(kMaxId, '0'); signed_message.set_oemcrypto_core_message(message); signed_message.set_using_secondary_key(mFdp.ConsumeBool()); std::string setMsg = mFdp.ConsumeRandomLengthString(kMaxByte); signed_message.set_msg(setMsg); signed_message.SerializeToString(response); } CdmResponseType CdmSessionFuzzer::initCdmSession( CdmSession *cdmSession, FuzzCdmClientPropertySet *fuzzCdmClientPropertySet, CdmSessionId forcedsessionId, FuzzEventListener *fuzzeventListener, bool forcedLevel) { CdmResponseType result; if (mFdp.ConsumeBool()) { result = cdmSession->Init(fuzzCdmClientPropertySet /*cdm_client_property_set*/, &forcedsessionId /*forced_session_id*/, fuzzeventListener /*event_listener*/, forcedLevel); } else { result = cdmSession->Init(fuzzCdmClientPropertySet /*cdm_client_property_set*/); } return result; } void CdmSessionFuzzer::storeLicense(DeviceFiles::CdmLicenseData licenseData) { FuzzFileSystem fileSystemdev(&mFdp); DeviceFiles deviceFiles(mFdp.ConsumeBool() ? &fileSystemdev : nullptr); deviceFiles.Init((CdmSecurityLevel)mFdp.ConsumeIntegralInRange( kSecurityLevelL1, kSecurityLevelL3)); std::map appParameters; appParameters[mFdp.ConsumeBytesAsString(kMaxByte)] = mFdp.ConsumeBytesAsString(kMaxByte); licenseData.key_set_id = std::to_string(mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); licenseData.state = (CdmOfflineLicenseState)mFdp.ConsumeIntegralInRange( kLicenseStateActive, kLicenseStateUnknown); licenseData.pssh_data = mFdp.ConsumeRandomLengthString(kMaxByte); licenseData.license_request = mFdp.ConsumeRandomLengthString(kMaxByte); licenseData.license = mFdp.ConsumeRandomLengthString(kMaxByte); licenseData.license_renewal_request = mFdp.ConsumeRandomLengthString(kMaxByte); licenseData.license_renewal = mFdp.ConsumeRandomLengthString(kMaxByte); licenseData.release_server_url = mFdp.ConsumeRandomLengthString(kMaxByte); licenseData.playback_start_time = mFdp.ConsumeIntegral(); licenseData.last_playback_time = mFdp.ConsumeIntegral(); licenseData.grace_period_end_time = mFdp.ConsumeIntegral(); licenseData.app_parameters = appParameters; licenseData.usage_entry = mFdp.ConsumeRandomLengthString(kMaxByte); licenseData.usage_entry_index = mFdp.ConsumeIntegral(); licenseData.drm_certificate = mFdp.ConsumeRandomLengthString(kMaxByte); licenseData.wrapped_private_key = CryptoWrappedKey( (CryptoWrappedKey::Type)mFdp.ConsumeIntegralInRange( CryptoWrappedKey::kUninitialized, CryptoWrappedKey::kEcc), mFdp.ConsumeRandomLengthString(kMaxByte)); DeviceFiles::ResponseType result; deviceFiles.StoreLicense(licenseData, &result); } void CdmSessionFuzzer::invokeCdmSessionAPIs(CdmSession *cdmSession) { CdmKeyResponse response; createResponse(&response); CdmKeyRequest keyRequest; CdmKeyMessage signedRequest; keyRequest.message = signedRequest; keyRequest.type = (CdmKeyRequestType)mFdp.ConsumeIntegralInRange( kKeyRequestTypeUnknown, kKeyRequestTypeRelease); std::string url; keyRequest.url = url; while (mFdp.remaining_bytes()) { auto invokeCdmSessionAPI = mFdp.PickValueInArray< const std::function>( {[&]() { cdmSession->AddKey(response /*key_response*/); }, [&]() { FuzzCdmClientPropertySet propertyset(&mFdp); propertyset.enable_privacy_mode(); propertyset.set_service_certificate(kTestSignedCertificate); PropertiesTestPeer::ForceReinit(); PropertiesTestPeer::AddSessionPropertySet( mFdp.ConsumeBool() ? kTestSessionId1 : mFdp.ConsumeRandomLengthString(kMaxByte), &propertyset); std::string rawServiceCertificate; PropertiesTestPeer::GetServiceCertificate( mFdp.ConsumeBool() ? kTestSessionId1 : mFdp.ConsumeRandomLengthString(kMaxByte), &rawServiceCertificate); cdmSession->SetServiceCertificate( rawServiceCertificate /*service_certificate*/); }, [&]() { CdmQueryMap queryResponse; cdmSession->QueryKeyStatus(&queryResponse /*query_response*/); }, [&]() { const std::string oecVersion = std::to_string( mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); std::string dataType = mFdp.PickValueInArray(kInitType); std::unique_ptr initData(new InitializationData( dataType, a2bs_hex(mFdp.ConsumeRandomLengthString(kMaxByte)), oecVersion)); CdmLicenseType licenseType = (CdmLicenseType)mFdp.ConsumeIntegralInRange( kLicenseTypeOffline, kLicenseTypeEmbeddedKeyData); CdmAppParameterMap appParameters; cdmSession->GenerateKeyRequest( *initData /*init_data*/, licenseType /*license_type*/, appParameters /*app_parameters*/, &keyRequest /*key_request*/); }, [&]() { CdmQueryMap queryResponse; cdmSession->QueryStatus(&queryResponse /*query_response*/); }, [&]() { DeviceFiles::CdmLicenseData licenseData; storeLicense(licenseData); std::string randomData = mFdp.ConsumeRandomLengthString(kMaxByte); const CdmKeySetId keySetId = mFdp.ConsumeBool() ? (kKeyId + wvutil::b2a_hex(randomData)) : std::to_string( mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); CdmLicenseType licenseType = (CdmLicenseType)mFdp.ConsumeIntegralInRange( kLicenseTypeOffline, kLicenseTypeEmbeddedKeyData); int errorDetail = mFdp.ConsumeIntegral(); cdmSession->RestoreOfflineSession(keySetId /*key_set_id*/, licenseType /*license_type*/, &errorDetail /*error_detail*/); }, [&]() { DeviceFiles::CdmUsageData usageData; usageData.provider_session_token = mFdp.ConsumeRandomLengthString(kMaxByte); usageData.license_request = mFdp.ConsumeRandomLengthString(kMaxByte); usageData.license = mFdp.ConsumeRandomLengthString(kMaxByte); std::string randomData = mFdp.ConsumeRandomLengthString(kMaxByte); usageData.key_set_id = mFdp.ConsumeBool() ? (kKeyId + wvutil::b2a_hex(randomData)) : std::to_string( mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); usageData.usage_entry = mFdp.ConsumeRandomLengthString(kMaxByte); usageData.usage_entry_index = mFdp.ConsumeIntegral(); usageData.drm_certificate = mFdp.ConsumeRandomLengthString(kMaxByte); usageData.wrapped_private_key = CryptoWrappedKey( (CryptoWrappedKey::Type)mFdp.ConsumeIntegralInRange( CryptoWrappedKey::kUninitialized, CryptoWrappedKey::kEcc), mFdp.ConsumeRandomLengthString(kMaxByte)); int errorDetail = mFdp.ConsumeIntegral(); cdmSession->RestoreUsageSession(usageData /*usage_data*/, &errorDetail /*error_detail*/); }, [&]() { const std::string keyId = std::to_string( mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); CdmKeyAllowedUsage keyUsage; cdmSession->QueryKeyAllowedUsage(keyId /*key_id*/, &keyUsage /*key_usage*/); }, [&]() { CdmQueryMap queryResponse; cdmSession->QueryOemCryptoSessionId( &queryResponse /*query_response*/); }, [&]() { const KeyId keyId = std::to_string( mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); CdmDecryptionParametersV16 params(keyId); const uint8_t encryptBufferParam = mFdp.ConsumeIntegral(); void *decryptBufferParam; size_t decryptbufferOffsetParam = mFdp.ConsumeIntegral(); size_t length = mFdp.ConsumeIntegral(); const std::vector iv = mFdp.ConsumeBytes(kMaxSize); CdmDecryptionSample sample(&encryptBufferParam, decryptBufferParam, decryptbufferOffsetParam, length, iv); CdmDecryptionSubsample subsample(mFdp.ConsumeIntegral(), mFdp.ConsumeIntegral()); sample.subsamples.push_back(subsample); params.samples.push_back(sample); cdmSession->Decrypt(params /*parameters*/); }, [&]() { cdmSession->GenerateRenewalRequest(&keyRequest /*key_request*/); }, [&]() { cdmSession->RenewKey(response /*key_response*/); }, [&]() { const KeyId keyId = std::to_string( mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); cdmSession->IsKeyLoaded(keyId /*key_id*/); }, [&]() { cdmSession->GetDurationRemaining(); }, [&]() { cdmSession->NotifyResolution( mFdp.ConsumeIntegral() /*width*/, mFdp.ConsumeIntegral() /*height*/); }, [&]() { cdmSession->OnTimerEvent(mFdp.ConsumeBool() /*update_usage*/); }, [&]() { const CdmKeySetId keySetId = kKeyId + mFdp.ConsumeRandomLengthString(kMaxByte); cdmSession->OnKeyReleaseEvent(keySetId /*key_set_id*/); }, [&]() { std::string appId = kSessionId + std::to_string( mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); cdmSession->GetApplicationId(&appId /*app_id*/); }, [&]() { cdmSession->UpdateUsageEntryInformation(); }, [&]() { cdmSession->GenerateSessionId(); }, [&]() { const std::string inBuffer = mFdp.ConsumeRandomLengthString(kMaxByte); const std::string keyId = std::to_string( mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); const std::string iv = mFdp.ConsumeRandomLengthString(kMaxByte); CdmEncryptionAlgorithm algorithm = mFdp.ConsumeBool() ? CdmEncryptionAlgorithm::kEncryptionAlgorithmAesCbc128 : CdmEncryptionAlgorithm::kEncryptionAlgorithmUnknown; std::string outBuffer = mFdp.ConsumeRandomLengthString(kMaxByte); cdmSession->GenericEncrypt(inBuffer /*in_buffer*/, keyId /*key_id*/, iv, algorithm, &outBuffer /*out_buffer*/); }, [&]() { const std::string inBuffer = mFdp.ConsumeRandomLengthString(kMaxByte); const std::string keyId = std::to_string( mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); const std::string iv = mFdp.ConsumeRandomLengthString(kMaxByte); CdmEncryptionAlgorithm algorithm = mFdp.ConsumeBool() ? CdmEncryptionAlgorithm::kEncryptionAlgorithmAesCbc128 : CdmEncryptionAlgorithm::kEncryptionAlgorithmUnknown; std::string outBuffer = mFdp.ConsumeRandomLengthString(kMaxByte); cdmSession->GenericDecrypt(inBuffer /*in_buffer*/, keyId /*key_id*/, iv, algorithm, &outBuffer /*out_buffer*/); }, [&]() { const std::string message = mFdp.ConsumeRandomLengthString(kMaxByte); const std::string keyId = std::to_string( mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); CdmSigningAlgorithm algorithm = mFdp.ConsumeBool() ? CdmSigningAlgorithm::kSigningAlgorithmHmacSha256 : CdmSigningAlgorithm::kSigningAlgorithmUnknown; std::string signature = mFdp.ConsumeRandomLengthString(kMaxByte); cdmSession->GenericSign(message, keyId /*key_id*/, algorithm, &signature); }, [&]() { const std::string message = mFdp.ConsumeRandomLengthString(kMaxByte); const std::string keyId = std::to_string( mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); CdmSigningAlgorithm algorithm = mFdp.ConsumeBool() ? CdmSigningAlgorithm::kSigningAlgorithmHmacSha256 : CdmSigningAlgorithm::kSigningAlgorithmUnknown; const std::string signature = mFdp.ConsumeRandomLengthString(kMaxByte); cdmSession->GenericVerify(message, keyId /*key_id*/, algorithm, signature); }, [&]() { uint32_t frameNumber = mFdp.ConsumeIntegral(); const std::string hash = mFdp.ConsumeRandomLengthString(kMaxByte); cdmSession->SetDecryptHash(frameNumber /*frame_number*/, hash); }, [&]() { std::string hashErrorString = mFdp.ConsumeRandomLengthString(kMaxByte); cdmSession->GetDecryptHashError( &hashErrorString /*hash_error_string*/); }, [&]() { cdmSession->ReleaseKey(response /*key_response*/); }, [&]() { cdmSession->GenerateReleaseRequest(&keyRequest /*key_request*/); }}); invokeCdmSessionAPI(); } UsageEntryIndex entryIndex = mFdp.ConsumeIntegral(); cdmSession->DeleteUsageEntry(entryIndex /*usage_entry_index*/); cdmSession->DeleteLicenseFile(); cdmSession->RemoveKeys(); cdmSession->RemoveLicense(); } void CdmSessionFuzzer::invokeCdmSessionMapAPIs() { FuzzFileSystem fileSystemMap(&mFdp); CdmSessionMap cdmsessionMap; std::shared_ptr CdmMetricsMap( new metrics::SessionMetrics); CdmSession *session = new CdmSession(&fileSystemMap, CdmMetricsMap); const CdmSessionId forcedSessionId = kSessionId + std::to_string(mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); cdmsessionMap.Add(forcedSessionId /*id*/, session /*session*/); while (mFdp.remaining_bytes()) { auto invokeCdmSessionMapAPI = mFdp.PickValueInArray>({ [&]() { cdmsessionMap.Exists(forcedSessionId /*id*/); }, [&]() { cdmsessionMap.Size(); }, [&]() { std::shared_ptr cdmsessionShared; cdmsessionMap.FindSession(forcedSessionId /*id*/, &cdmsessionShared /*session*/); }, [&]() { CdmSessionList sessions; cdmsessionMap.GetSessionList(sessions); }, }); invokeCdmSessionMapAPI(); cdmsessionMap.CloseSession(forcedSessionId); } } void CdmSessionFuzzer::process() { if (mFdp.ConsumeBool()) { FuzzFileSystem fileSystem(&mFdp); FuzzEventListener fuzzeventListener; std::shared_ptr metrics = std::make_shared(); std::unique_ptr cdmSession = std::make_unique(&fileSystem, metrics); std::unique_ptr fuzzCdmClientPropertySet( new FuzzCdmClientPropertySet(&mFdp)); const CdmSessionId forcedSessionId = kSessionId + std::to_string(mFdp.ConsumeIntegralInRange(kMinId, kMaxId)); bool forcedLevel = mFdp.ConsumeBool(); CdmResponseType result = initCdmSession(cdmSession.get(), fuzzCdmClientPropertySet.get(), forcedSessionId, &fuzzeventListener, forcedLevel); if (result == NO_ERROR) { invokeCdmSessionAPIs(cdmSession.get()); } } else { invokeCdmSessionMapAPIs(); } } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { CdmSessionFuzzer cdmSessionFuzzer(data, size); cdmSessionFuzzer.process(); return 0; }