/* * 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 #include "cdm_client_property_set.h" #include "cdm_identifier.h" #include "cdm_session.h" #include "license.h" #include "policy_engine.h" #include "string_conversions.h" #include "vendor_widevine_fuzz_helper.h" #include "wv_cdm_constants.h" #include "wv_cdm_event_listener.h" using namespace wvcdm; using wvutil::a2bs_hex; using namespace video_widevine; const static constexpr int32_t kMaxByte = 256; const static constexpr int32_t kAppVersion = 16; static constexpr int32_t kMaxAppParamSize = 10; const static constexpr uint32_t kProtectionScheme[] = { 0x63626331, 0x63626373, 0x31636263, 0x73636263, 0x63656e63}; const std::string kInitDataType[] = {"video/mp4", "video/webm", "cenc", "hls", "webm"}; const std::string kRequestId = "request_id"; const static constexpr int32_t kSignedType[] = { SignedMessage::LICENSE, SignedMessage::SERVICE_CERTIFICATE, SignedMessage::ERROR_RESPONSE}; const std::string kDefaultServiceCertificate = a2bs_hex( "0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522" "8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417" "7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781" "5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D" "0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6" "AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0" "40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A" "0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6" "283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0" "D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301" "00013A1273746167696E672E676F6F676C652E636F6D128003983E303526" "75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA" "EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA" "97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91" "5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0" "32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0" "EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28" "8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932" "1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784" "C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742" "BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940" "383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8" "38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A" "250A4EB9C84AB3E6539F6B6FDF56899EA29914"); const std::string kToken = a2bs_hex( "0AAE02080212107E0A892DEEB021E7AF696B938BB1D5B1188B85AD9D05228E023082010A02" "82010100DBEDF2BFB0EC98213766E65049B9AB176FA4B1FBFBB2A0C96C87D9F2B895E0ED77" "93BDA057E6BC3E0CA2348BC6831E03609445CA4D418CB98EAC98FFC87AB2364CE76BA26BEE" "CDB0C45BD2A6FE9FD38CC5A1C26303AEEB7E9C3CAFAB0D10E46C07E50BEDAB42BF21F40BD2" "E055DB0B455191D6B4CEEB11B3F1AFA42B5C0CE4C96B75A5283C0E3AE049AA7CF86D1C4EF6" "6A9088B53BCF320ABC9B98A22C219DC109014EFEA72DA5FF2ED5D655DE7AE06EAC6C6B4191" "523B2CD2DC1EBFF5F08B11CFE056F2826C1323F12704EC7EBBC1AF935129E5543804492AF9" "23B848F4AF47B4BFB131C39DDDC99DBAEEE0F30AD2ADBBC63E60793D0876E37391008BB4DB" "F7020301000128DD22128002A9E571776EA9D22A1BD14248DA88E12FD859989F613360B8D2" "DA40AF31CC071C7A138466F0EB745E3FD664C0E1A5E4F01098B8D56C34A0158DF9916D192F" "841ADCA17FD630E1C0CBE652CAC6A52B6A1581BE4029CE6FAE0E04D2D2C7861187AF8299D8" "3E008DB9A2789672CA1DED903773D7E82B234CE2C799EB73CF80600C08F17EEDDDF369D2B8" "4A08292F22D1F18FE89521905E713BA674F2217881DBD7711B8C48D5FDCE6FAB51F935E293" "CB29191AB012B115FD2F5F23164B063D0A929C3E254BF0F4FA60051EB6B3498ED99FF77C19" "68E8CD83A35CEB054D05433FD0EA6AAE43C87DDA377591D1DCC1831EE130BFFF6D139A5ADA" "738B0F257CCE2649E71AB4050AAE020801121017DCBC27D11341D497135442A188DAA6188F" "89809105228E023082010A0282010100D21ADD7549D2748B3494526A9C3FB86C79376BBE8C" "8856F601B8D10461F77ACC7331B10DEBF365120056CDB5662D25907B74F12382F0F4A0CA47" "5EEA9562815C6228F6F698ADA27879D8890F2A2D96A746DDEF5316301C003519C2A2250354" "674169FDDA41CE14D3C52BEA7A20384515012D5952B38AA19E15E8563CC7AAA81C2122880A" "A370A64FEA23C53FB83AC3DB5753214730A349E07F64BF32BE7EAD30D02612AF110BB44FB0" "8E1D308173B327EF64D40C41639542B2D1A73C98A6607EC6C683B513A58470514106EF87AE" "1E7B9C695B93A104DF7437BFC4167789748A43ED208F2C1FA710793C688885EAE732A8BFDF" "5B423B23D75B88FC0ADC8FBDB5020301000128DD2212800372D2FB88098BA3B85B6B4354E0" "3767DBE2D7724663FB0A62ABF7704EA910E01F221349EE16D0152C769384050CE78520668C" "06CCFD3D789AF3EB69FF163615CD609169FDBE2E15A029D34AD2605625BC81844C9D1E2CE0" "519039F3799ADAEF86641E20B033DC16DF2E5B9A1A2A417B8BB3B7A4D9AD1A99367448587D" "A13DDE05A3ED9D62FA42078973B4AA40263D7BFA23F1072E94CDF323FA45F78408823E55C4" "F4C5C723819CF44CE6D98E50C04EC24D93B1AAB8877B9108B9CA391308E1A3645EBB0E7CAC" "BB40B5451560ED799421873BFB5ABB917FA60DB9C77CB8606AF7E3142626F5EA40E5CB8AA0" "89D8E7D6A9361935C426A4450EA8BC2E57290D3BF0A0962991D2A91B752FC80C3E7E4E5503" "3D71C94B325307A68815F026448F56A2741CEBEFC18E8C142F5F62BFAA67A291517DDE982D" "8CD5A9DF6E3D3A99B806F6D60991358C5BE77117D4F3168F3348E9A048539F892F4D783152" "C7A8095224AA56B78C5CF7BD1AB1B179C0C0D11E3C3BAC84C141A00191321E3ACC17242E68" "3C"); class FuzzCryptoSession : public CryptoSession { public: FuzzCryptoSession(metrics::CryptoMetrics *metrics, FuzzedDataProvider *fdp) : CryptoSession(metrics), fdp_(fdp){}; bool IsOpen() override { return fdp_->ConsumeBool(); }; const std::string &request_id() override { return kRequestId; }; bool GetApiVersion(uint32_t *api_version) override { *api_version = kAppVersion; return fdp_->ConsumeBool(); }; CdmResponseType GenerateNonce(uint32_t *license_nonce) override { *license_nonce = fdp_->ConsumeIntegral(); return (fdp_->ConsumeBool() ? CdmResponseType(NO_ERROR) : CdmResponseType(SESSION_LOST_STATE_ERROR)); }; CdmResponseType PrepareAndSignLicenseRequest( const std::string & /*message*/, std::string *core_message, std::string *license_request_signature, bool & /*shouldSpecifyAlgorithm*/, OEMCrypto_SignatureHashAlgorithm & /*algorithm*/) override { *core_message = fdp_->ConsumeRandomLengthString(kMaxByte); *license_request_signature = fdp_->ConsumeRandomLengthString(kMaxByte); return (fdp_->ConsumeBool() ? CdmResponseType(NO_ERROR) : CdmResponseType(NEED_PROVISIONING)); }; CdmResponseType LoadEntitledContentKeys( const std::vector & /*key_array*/) override { return (fdp_->ConsumeBool() ? CdmResponseType(KEY_ADDED) : CdmResponseType(INSUFFICIENT_CRYPTO_RESOURCES)); }; CdmResponseType GenerateDerivedKeys( const std::string & /*message*/, const std::string & /*session_key*/) override { return (fdp_->ConsumeBool() ? CdmResponseType(NO_ERROR) : CdmResponseType(INSUFFICIENT_CRYPTO_RESOURCES)); }; CdmResponseType DeactivateUsageInformation( const std::string & /*provider_session_token*/) override { return (fdp_->ConsumeBool() ? CdmResponseType(NO_ERROR) : CdmResponseType(SYSTEM_INVALIDATED_ERROR)); }; CdmResponseType GenerateUsageReport( const std::string & /*provider_session_token*/, std::string *usage_report, UsageDurationStatus *usage_duration_status, int64_t *seconds_since_started, int64_t *seconds_since_last_played) override { *usage_report = fdp_->ConsumeRandomLengthString(kMaxByte); *usage_duration_status = fdp_->ConsumeBool() ? CryptoSession::kUsageDurationPlaybackNotBegun : CryptoSession::kUsageDurationsValid; *seconds_since_started = fdp_->ConsumeIntegral(); *seconds_since_last_played = fdp_->ConsumeIntegral(); return (fdp_->ConsumeBool() ? CdmResponseType(NO_ERROR) : CdmResponseType(SYSTEM_INVALIDATED_ERROR)); }; CdmResponseType PrepareAndSignRenewalRequest(const std::string & /*message*/, std::string *core_message, std::string *signature) { *core_message = fdp_->ConsumeRandomLengthString(kMaxByte); *signature = fdp_->ConsumeRandomLengthString(kMaxByte); return (fdp_->ConsumeBool() ? CdmResponseType(NO_ERROR) : CdmResponseType(PARAMETER_NULL)); }; CdmResponseType UseSecondaryKey(bool /*dual_key*/) override { return (fdp_->ConsumeBool() ? CdmResponseType(NO_ERROR) : CdmResponseType(SYSTEM_INVALIDATED_ERROR)); }; CdmResponseType LoadRenewal(const std::string & /*signed_message*/, const std::string & /*core_message*/, const std::string & /*signature*/) override { return (fdp_->ConsumeBool() ? CdmResponseType(KEY_ADDED) : CdmResponseType(INSUFFICIENT_CRYPTO_RESOURCES)); }; CdmResponseType LoadLicense(const std::string & /*signed_message*/, const std::string & /*core_message*/, const std::string & /*signature*/, CdmLicenseKeyType /*key_type*/) override { return (fdp_->ConsumeBool() ? CdmResponseType(KEY_ADDED) : CdmResponseType(INSUFFICIENT_CRYPTO_RESOURCES)); }; CdmSecurityLevel GetSecurityLevel() override { return (CdmSecurityLevel)fdp_->ConsumeIntegralInRange( kSecurityLevelUninitialized, kSecurityLevelUnknown); } private: FuzzedDataProvider *fdp_; }; class FuzzPolicyEngine : public PolicyEngine { public: FuzzPolicyEngine(CryptoSession *crypto, FuzzedDataProvider *fdp) : PolicyEngine("mock_session_id", nullptr, crypto), fdp_(fdp){}; void SetEntitledLicenseKeys(const std::vector & /*entitled_keys*/) override{}; bool CanRenew() { return fdp_->ConsumeBool(); } bool GetSecondsSinceStarted(int64_t *seconds_since_started) { *seconds_since_started = fdp_->ConsumeIntegral(); return fdp_->ConsumeBool(); } bool GetSecondsSinceLastPlayed(int64_t *seconds_since_last_played) { *seconds_since_last_played = fdp_->ConsumeIntegral(); return fdp_->ConsumeBool(); }; void UpdateLicense(const License & /*license*/, bool /*defer_license_state_update*/){}; void RestorePlaybackTimes(int64_t /*playback_start_time*/, int64_t /*last_playback_time*/, int64_t /*grace_period_end_time*/){}; void SetLicenseForRelease(const License & /*license*/){}; void SetLicense(const License & /*license*/, bool /*defer_license_state_update*/){}; private: FuzzedDataProvider *fdp_; }; class CdmLicenseFuzzer { public: CdmLicenseFuzzer(const uint8_t *data, size_t size) : fdp_(data, size){}; void Process(); private: FuzzedDataProvider fdp_; void CreateResponse(CdmKeyResponse *response); void SetLicenseMsg(std::string *msg); }; void PolicySetBool(std::function function, FuzzedDataProvider *fdp) { if (fdp->ConsumeBool()) { function(fdp->ConsumeBool()); } } void CdmLicenseFuzzer::SetLicenseMsg(std::string *msg) { 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_.ConsumeBool() ? fdp_.PickValueInArray(kProtectionScheme) : fdp_.ConsumeIntegral(); license.set_protection_scheme(scheme); } License::KeyContainer *container = license.add_key(); if (fdp_.ConsumeBool()) { uint32_t key_type = fdp_.ConsumeIntegralInRange( License::KeyContainer::SIGNING, License::KeyContainer::ENTITLEMENT); container->set_type((License_KeyContainer_KeyType)key_type); } if (fdp_.ConsumeBool()) { const std::string iv_string = fdp_.ConsumeRandomLengthString(kMaxByte); container->set_iv(iv_string); } else if (fdp_.ConsumeBool()) { container->mutable_iv(); } if (fdp_.ConsumeBool()) { std::string key_string = fdp_.ConsumeRandomLengthString(kMaxByte); container->set_key(key_string); } else if (fdp_.ConsumeBool()) { container->mutable_key(); } if (fdp_.ConsumeBool()) { const std::string id_string = fdp_.ConsumeRandomLengthString(kMaxByte); container->set_id(id_string); } else if (fdp_.ConsumeBool()) { container->mutable_id(); } if (fdp_.ConsumeBool()) { const std::string track_label = fdp_.ConsumeRandomLengthString(kMaxByte); container->set_track_label(track_label); } else if (fdp_.ConsumeBool()) { container->mutable_track_label(); } License_KeyContainer_KeyControl *key_control = container->mutable_key_control(); if (fdp_.ConsumeBool()) { std::string key_control_msg = fdp_.ConsumeRandomLengthString(kMaxByte); key_control->set_key_control_block(key_control_msg); } else if (fdp_.ConsumeBool()) { key_control->mutable_key_control_block(); } if (fdp_.ConsumeBool()) { const std::string iv_string = fdp_.ConsumeRandomLengthString(kMaxByte); key_control->set_iv(iv_string); } else if (fdp_.ConsumeBool()) { key_control->mutable_iv(); } if (fdp_.ConsumeBool()) { License_Policy *policy = license.mutable_policy(); PolicySetBool(std::bind(&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(&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() ? STREAMING : OFFLINE); } if (fdp_.ConsumeBool()) { std::string provider_session_token = fdp_.ConsumeRandomLengthString(kMaxByte); id->set_provider_session_token(provider_session_token); } license.SerializeToString(msg); } void CdmLicenseFuzzer::CreateResponse(CdmKeyResponse *response) { SignedMessage signed_message; SignedMessage type; signed_message.set_type( (SignedMessage_MessageType)fdp_.PickValueInArray(kSignedType)); if (fdp_.ConsumeBool()) { signed_message.mutable_service_version_info(); } if (fdp_.ConsumeBool()) { signed_message.set_signature(fdp_.ConsumeRandomLengthString(kMaxByte)); } if (fdp_.ConsumeBool()) { signed_message.set_session_key(fdp_.ConsumeRandomLengthString(kMaxByte)); } if (fdp_.ConsumeBool()) { signed_message.set_oemcrypto_core_message( fdp_.ConsumeRandomLengthString(kMaxByte)); } if (fdp_.ConsumeBool()) { signed_message.set_using_secondary_key(fdp_.ConsumeBool()); } if (fdp_.ConsumeBool()) { std::string set_msg; SetLicenseMsg(&set_msg); signed_message.set_msg(set_msg); } signed_message.SerializeToString(response); } void CdmLicenseFuzzer::Process() { CdmSessionId session_id = fdp_.ConsumeRandomLengthString(kMaxByte); CdmLicense cdm_license(session_id); metrics::CryptoMetrics crypto_metrics; std::unique_ptr crypto_session( new FuzzCryptoSession(&crypto_metrics, &fdp_)); std::unique_ptr policy_engine( new FuzzPolicyEngine(crypto_session.get(), &fdp_)); bool status = cdm_license.Init(fdp_.ConsumeBool(), kDefaultServiceCertificate, crypto_session.get(), policy_engine.get()); if (status) { std::shared_ptr cdm_metrics( new metrics::SessionMetrics); wvutil::FileSystem file_system; std::unique_ptr cdm_session( new CdmSession(&file_system, cdm_metrics)); std::unique_ptr fuzz_cdm_client_property_set( new FuzzCdmClientPropertySet(&fdp_)); cdm_session->Init(fuzz_cdm_client_property_set.get()); std::string data_type = fdp_.ConsumeBool() ? fdp_.PickValueInArray(kInitDataType) : fdp_.ConsumeRandomLengthString(kMaxByte); std::unique_ptr init_data(new InitializationData( data_type, a2bs_hex(fdp_.ConsumeRandomLengthString(kMaxByte)))); CdmKeyResponse response; if (fdp_.ConsumeBool()) { CreateResponse(&response); } else { response = fdp_.ConsumeBool() ? "" : fdp_.ConsumeRandomLengthString(kMaxByte); } CdmKeyMessage signed_request = fdp_.ConsumeRandomLengthString(kMaxByte); while (fdp_.remaining_bytes()) { auto invoke_license_API = fdp_.PickValueInArray>({ [&]() { CdmLicenseType license_type = (CdmLicenseType)fdp_.ConsumeIntegralInRange( kLicenseTypeOffline, kLicenseTypeEmbeddedKeyData); std::string server_url; CdmAppParameterMap param; int32_t max_size = fdp_.ConsumeIntegralInRange(0, kMaxAppParamSize); for (size_t i = 0; i < max_size; ++i) { std::string key = fdp_.ConsumeRandomLengthString(kMaxByte); std::string value = fdp_.ConsumeRandomLengthString(kMaxByte); std::string cdm_key(key.c_str(), key.size()); std::string cdm_value(value.c_str(), value.size()); param[cdm_key] = cdm_value; } std::string client_token = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxByte) : kToken; cdm_license.PrepareKeyRequest( *init_data, client_token, license_type /*kLicenseTypeStreaming*/, param, (fdp_.ConsumeBool() ? &signed_request : nullptr), (fdp_.ConsumeBool() ? &server_url : nullptr)); }, [&]() { std::string signed_service_certificate = fdp_.ConsumeRandomLengthString(kMaxByte); cdm_license.SetServiceCertificate(signed_service_certificate); }, [&]() { std::string server_url; CdmAppParameterMap param; int32_t max_size = fdp_.ConsumeIntegralInRange(0, kMaxAppParamSize); for (size_t i = 0; i < max_size; ++i) { std::string key = fdp_.ConsumeRandomLengthString(kMaxByte); std::string value = fdp_.ConsumeRandomLengthString(kMaxByte); std::string cdm_key(key.c_str(), key.size()); std::string cdm_value(value.c_str(), value.size()); param[cdm_key] = cdm_value; } cdm_license.PrepareKeyUpdateRequest( fdp_.ConsumeBool(), param, cdm_session.get(), (fdp_.ConsumeBool() ? &signed_request : nullptr), (fdp_.ConsumeBool() ? &server_url : nullptr)); }, [&]() { cdm_license.HandleKeyResponse(fdp_.ConsumeBool(), response); }, [&]() { cdm_license.HandleKeyUpdateResponse( fdp_.ConsumeBool() /*is_renewal*/, fdp_.ConsumeBool() /*is_restore*/, response); }, [&]() { cdm_license.HandleEmbeddedKeyData(*init_data); }, [&]() { CdmKeyResponse renew_response; CreateResponse(&renew_response); std::string client_token = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxByte) : kToken; cdm_license.RestoreOfflineLicense( client_token, signed_request, response, renew_response, fdp_.ConsumeIntegral() /*playback_start_time*/, fdp_.ConsumeIntegral() /*last_playback_time*/, fdp_.ConsumeIntegral() /*grace_period_end_time*/, cdm_session.get()); }, [&]() { std::string client_token = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxByte) : kToken; cdm_license.RestoreLicenseForRelease(client_token, signed_request, response); }, [&]() { KeyId key_id = fdp_.ConsumeRandomLengthString(kMaxByte); cdm_license.IsKeyLoaded(key_id); }, [&]() { std::string session_token; cdm_license.ExtractProviderSessionToken(response, &session_token); }, }); invoke_license_API(); } } } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { CdmLicenseFuzzer cdm_license_fuzzer(data, size); cdm_license_fuzzer.Process(); return 0; }