diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..7514d046 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,92 @@ +# Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +# source code may only be used and distributed under the Widevine License +# Agreement. + +Checks: > + -*, + bugprone-*, + -bugprone-branch-clone, + -bugprone-easily-swappable-parameters, + -bugprone-implicit-widening-of-multiplication-result, + -bugprone-integer-division, + -bugprone-macro-parentheses, + -bugprone-narrowing-conversions, + -bugprone-not-null-terminated-result, + -bugprone-sizeof-expression, + -bugprone-unhandled-self-assignment, + clang-analyzer-*, + -clang-analyzer-core.CallAndMessage, + -clang-analyzer-core.NullDereference, + -clang-analyzer-core.UndefinedBinaryOperatorResult, + -clang-analyzer-cplusplus.NewDelete, + -clang-analyzer-cplusplus.NewDeleteLeaks, + -clang-analyzer-optin.cplusplus.VirtualCall, + -clang-analyzer-optin.performance.Padding, + -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, + -clang-analyzer-valist.Uninitialized, + clang-diagnostic-*, + google-build-explicit-make-pair, + google-build-namespaces, + google-global-names-in-headers, + google-readability-namespace-comments, + google-runtime-operator, + misc-*, + -misc-no-recursion, + -misc-non-private-member-variables-in-classes, + -misc-redundant-expression, + -misc-unconventional-assign-operator, + -misc-unused-parameters, + modernize-avoid-bind, + modernize-concat-nested-namespaces, + modernize-deprecated-ios-base-aliases, + modernize-make-shared, + modernize-replace-auto-ptr, + modernize-replace-disallow-copy-and-assign-macro, + modernize-replace-random-shuffle, + modernize-shrink-to-fit, + modernize-unary-static-assert, + modernize-use-bool-literals, + modernize-use-nodiscard, + modernize-use-noexcept, + modernize-use-transparent-functors, + modernize-use-uncaught-exceptions, + performance-faster-string-find, + performance-implicit-conversion-in-loop, + performance-inefficient-algorithm, + performance-move-const-arg, + performance-move-constructor-init, + performance-trivially-destructible, + performance-type-promotion-in-math-fn, + performance-unnecessary-copy-initialization, + readability-avoid-const-params-in-decls, + readability-container-contains, + readability-delete-null-pointer, + readability-duplicate-include, + readability-identifier-naming, + readability-misleading-indentation, + readability-misplaced-array-index, + readability-redundant-access-specifiers, + readability-redundant-control-flow, + readability-redundant-function-ptr-dereference, + readability-redundant-preprocessor, + readability-redundant-smartptr-get, + readability-redundant-string-cstr, + readability-simplify-boolean-expr, + readability-simplify-subscript-expr, + readability-static-definition-in-anonymous-namespace, + readability-string-compare, + readability-uniqueptr-delete-release +CheckOptions: +- key: bugprone-reserved-identifier.AllowedIdentifiers + value: > + _; + __assert_fail; + __bswap_constant_16; + __bswap_constant_32; + __bswap_constant_64 +- key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison + value: true +- key: bugprone-suspicious-string-compare.StringCompareLikeFunctions + value: crypto_memcmp +WarningsAsErrors: '*' +FormatStyle: file diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 9e00f191..c8bbe7ad 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -88,7 +88,7 @@ class CdmEngine { // request to. virtual CdmResponseType GenerateKeyRequest( const CdmSessionId& session_id, const CdmKeySetId& key_set_id, - const InitializationData& init_data, const CdmLicenseType license_type, + const InitializationData& init_data, CdmLicenseType license_type, CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request); // This API may // (a) accept license response, extract key info and load keys. @@ -388,6 +388,13 @@ class CdmEngine { virtual void SetDefaultOtaKeyboxFallbackDurationRules(); virtual void SetFastOtaKeyboxFallbackDurationRules(); + // A signing method specifically used by Cast. + // This method should not be used otherwise. + virtual CdmResponseType SignRsa(const std::string& wrapped_key, + const std::string& message, + std::string* signature, + RSA_Padding_Scheme padding_scheme); + protected: friend class CdmEngineFactory; diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index 3e4e7083..2c6018b0 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -219,6 +219,13 @@ class CdmSession { virtual metrics::SessionMetrics* GetMetrics() { return metrics_.get(); } + virtual CdmResponseType LoadCastPrivateKey( + const CryptoWrappedKey& private_key); + + virtual CdmResponseType GenerateRsaSignature(const std::string& message, + std::string* signature, + RSA_Padding_Scheme scheme); + private: friend class CdmSessionTest; diff --git a/libwvdrmengine/cdm/core/include/cdm_usage_table.h b/libwvdrmengine/cdm/core/include/cdm_usage_table.h index 5c9c874c..07cc3a3f 100644 --- a/libwvdrmengine/cdm/core/include/cdm_usage_table.h +++ b/libwvdrmengine/cdm/core/include/cdm_usage_table.h @@ -157,24 +157,24 @@ class CdmUsageTable { // will be deleted. // Threading: Method takes exclusive use of |lock_| // when required. - bool CreateNewTable(CryptoSession* const crypto_session); + bool CreateNewTable(CryptoSession* crypto_session); // Attempts to restore the usage table from persistent storage, and // loads the usage table header into OEMCrypto. // Note: No other OEMCrypto session should be opened before calling. // Threading: Method takes exclusive use of |lock_| // when required. - bool RestoreTable(CryptoSession* const crypto_session); + bool RestoreTable(CryptoSession* crypto_session); // Performs a check that there are no open OEMCrypto sessions for // the current security level of the usage table. // Threading: No special threading requirements. - bool OpenSessionCheck(CryptoSession* const crypto_session); + bool OpenSessionCheck(CryptoSession* crypto_session); // Performs a check that the OEMCrypto table can support at least // one more entry if the table is at or near the reported capacity. // If this check fails, a new usage table SHOULD be created. // Threading: Method requires caller to take exclusive use of // |lock_|. - bool CapacityCheck(CryptoSession* const crypto_session); + bool CapacityCheck(CryptoSession* crypto_session); // Attempts to determine the capacity of the OEMCrypto usage table. // Sets the result to |potential_table_capacity_|. @@ -189,7 +189,7 @@ class CdmUsageTable { // Creates a new entry for the provided crypto session. If the // entry is created successfully in OEMCrypto, then a new entry // info is added to the table's vector of entry info. - CdmResponseType CreateEntry(CryptoSession* const crypto_session, + CdmResponseType CreateEntry(CryptoSession* crypto_session, UsageEntryIndex* entry_index); // Attempts to relocate a newly created usage entry associated with @@ -198,27 +198,27 @@ class CdmUsageTable { // |entry_index| is treated as both an input and output. // Returns NO_ERROR so long as no internal operation fails, // regardless of whether the entry was moved or not. - CdmResponseType RelocateNewEntry(CryptoSession* const crypto_session, + CdmResponseType RelocateNewEntry(CryptoSession* crypto_session, UsageEntryIndex* entry_index); // Checks if the specified |entry_index| is known to be // unoccupied (released). - bool IsEntryUnoccupied(const UsageEntryIndex entry_index) const; + bool IsEntryUnoccupied(UsageEntryIndex entry_index) const; // SetOfflineEntryInfo() and SetUsageInfoEntryInfo() populate the // entry meta-data with the required information based on the type // of entry. - void SetOfflineEntryInfo(const UsageEntryIndex entry_index, + void SetOfflineEntryInfo(UsageEntryIndex entry_index, const std::string& key_set_id, const CdmKeyResponse& license_message); - void SetUsageInfoEntryInfo(const UsageEntryIndex entry_index, + void SetUsageInfoEntryInfo(UsageEntryIndex entry_index, const std::string& key_set_id, const std::string& usage_info_file_name); // Shrinks the table, removing all trailing unoccupied entries. // |entry_info_list_| will be resized appropriately. // Caller must store the table after a successful call. - CdmResponseType RefitTable(CryptoSession* const crypto_session); + CdmResponseType RefitTable(CryptoSession* crypto_session); virtual CdmResponseType InvalidateEntryInternal( UsageEntryIndex entry_index, bool defrag_table, DeviceFiles* device_files, diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index e74e736a..3abdebcb 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -347,6 +347,11 @@ class CryptoSession { virtual CdmResponseType LoadOtaProvisioning(bool use_test_key, const std::string& response); + // Cast specific generate signature method. + virtual CdmResponseType GenerateRsaSignature(const std::string& message, + std::string* signature, + RSA_Padding_Scheme scheme); + protected: // Creates an instance of CryptoSession with the given |crypto_metrics|. // |crypto_metrics| is owned by the caller, must NOT be null, and must @@ -385,8 +390,6 @@ class CryptoSession { // Note: This function will lock the global static field lock in write mode. bool SetUpUsageTable(RequestedSecurityLevel requested_security_level); - CdmResponseType GenerateRsaSignature(const std::string& message, - std::string* signature); size_t GetMaxSubsampleRegionSize(); bool SetDestinationBufferType(); diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index de2e81f7..e2125bea 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -275,7 +275,7 @@ class DeviceFiles { const CdmUsageData& usage_data); virtual bool StoreHlsAttributes(const std::string& key_set_id, - const CdmHlsMethod method, + CdmHlsMethod method, const std::vector& media_segment_iv); virtual bool RetrieveHlsAttributes(const std::string& key_set_id, CdmHlsMethod* method, diff --git a/libwvdrmengine/cdm/core/include/license_key_status.h b/libwvdrmengine/cdm/core/include/license_key_status.h index db9bd644..6bb6e0a5 100644 --- a/libwvdrmengine/cdm/core/include/license_key_status.h +++ b/libwvdrmengine/cdm/core/include/license_key_status.h @@ -143,7 +143,7 @@ class LicenseKeyStatus { using ConstraintList = ::google::protobuf::RepeatedPtrField; - LicenseKeyStatus(const KeyContainer& key, const CdmSecurityLevel level); + LicenseKeyStatus(const KeyContainer& key, CdmSecurityLevel level); virtual ~LicenseKeyStatus() {} diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index 6a9fcfe0..55a586b3 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -460,6 +460,7 @@ enum CdmResponseEnum : int32_t { STORE_ATSC_LICENSE_DEVICE_FILES_INIT_ERROR = 394, STORE_ATSC_LICENSE_ERROR = 395, SESSION_NOT_FOUND_GENERIC_CRYPTO = 396, + SESSION_NOT_FOUND_24 = 397, // Don't forget to add new values to // * core/src/wv_cdm_types.cpp // * android/include/mapErrors-inl.h @@ -520,13 +521,11 @@ class CdmResponseType { const char* crypto_session_method_ = nullptr; }; -static inline bool operator==(const CdmResponseEnum lhs, - const CdmResponseType& rhs) { +inline bool operator==(const CdmResponseEnum lhs, const CdmResponseType& rhs) { return lhs == rhs.code(); } -static inline bool operator!=(const CdmResponseEnum lhs, - const CdmResponseType& rhs) { +inline bool operator!=(const CdmResponseEnum lhs, const CdmResponseType& rhs) { return lhs != rhs.code(); } @@ -707,17 +706,14 @@ class CdmKeyAllowedUsage { } bool Equals(const CdmKeyAllowedUsage& other) { - if (!valid_ || !other.Valid() || - decrypt_to_clear_buffer != other.decrypt_to_clear_buffer || - decrypt_to_secure_buffer != other.decrypt_to_secure_buffer || - generic_encrypt != other.generic_encrypt || - generic_decrypt != other.generic_decrypt || - generic_sign != other.generic_sign || - generic_verify != other.generic_verify || - key_security_level_ != other.key_security_level_) { - return false; - } - return true; + return valid_ && other.Valid() && + decrypt_to_clear_buffer == other.decrypt_to_clear_buffer && + decrypt_to_secure_buffer == other.decrypt_to_secure_buffer && + generic_encrypt == other.generic_encrypt && + generic_decrypt == other.generic_decrypt && + generic_sign == other.generic_sign && + generic_verify == other.generic_verify && + key_security_level_ == other.key_security_level_; } bool decrypt_to_clear_buffer; diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 2f47e3ec..ccbe11f7 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -2340,4 +2340,54 @@ void CdmEngine::SetFastOtaKeyboxFallbackDurationRules() { } system_fallback_policy->SetFastBackoffDurationRules(); } + +CdmResponseType CdmEngine::SignRsa(const std::string& wrapped_key, + const std::string& message, + std::string* signature, + RSA_Padding_Scheme padding_scheme) { + // Try to open cdm session. + CdmSessionId session_id; + auto sts = OpenSession("com.widevine", nullptr, nullptr, &session_id); + if (sts != NO_ERROR) { + LOGE("OpenSession failed, status: %d", static_cast(sts)); + return sts; + } + + // Retrieve the cdm session + std::shared_ptr session; + { + std::unique_lock lock(session_map_lock_); + if (!session_map_.FindSession(session_id, &session)) { + LOGE("Session not found: session_id = %s", IdToString(session_id)); + CloseSession(session_id); + return CdmResponseType(SESSION_NOT_FOUND_24); + } + } + + // Load cast private key for signing + CryptoWrappedKey key(CryptoWrappedKey::kRsa, wrapped_key); + sts = session->LoadCastPrivateKey(key); + if (sts != NO_ERROR) { + LOGE("LoadCastPrivateKey failed, status: %d", static_cast(sts)); + CloseSession(session_id); + return sts; + } + + // Generate Rsa signature for cast message + sts = session->GenerateRsaSignature(message, signature, padding_scheme); + if (sts != NO_ERROR) { + LOGE("GenerateRsaSignature failed, status: %d", static_cast(sts)); + CloseSession(session_id); + return sts; + } + + // Try to close cdm session. + sts = CloseSession(session_id); + if (sts != NO_ERROR) { + LOGE("CloseSession failed, status: %d", static_cast(sts)); + return sts; + } + + return sts; +} } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 90024c50..783c0416 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -1249,7 +1249,7 @@ CdmResponseType CdmSession::LoadPrivateKey( DrmKeyTypeToMetricValue(private_key.type())); drm_certificate_ = drm_certificate; - wrapped_private_key_ = std::move(private_key); + wrapped_private_key_ = private_key; return CdmResponseType(NO_ERROR); case SESSION_LOST_STATE_ERROR: case SYSTEM_INVALIDATED_ERROR: @@ -1302,6 +1302,18 @@ bool CdmSession::HasRootOfTrustBeenRenewed() { return true; } +CdmResponseType CdmSession::LoadCastPrivateKey( + const CryptoWrappedKey& private_key) { + return crypto_session_->LoadCertificatePrivateKey(private_key); +} + +CdmResponseType CdmSession::GenerateRsaSignature(const std::string& message, + std::string* signature, + RSA_Padding_Scheme scheme) { + return crypto_session_->GenerateRsaSignature(message, signature, + scheme); +} + // For testing only - takes ownership of pointers void CdmSession::set_license_parser(CdmLicense* license_parser) { diff --git a/libwvdrmengine/cdm/core/src/cdm_usage_table.cpp b/libwvdrmengine/cdm/core/src/cdm_usage_table.cpp index b3e65827..f2dbe368 100644 --- a/libwvdrmengine/cdm/core/src/cdm_usage_table.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_usage_table.cpp @@ -317,8 +317,6 @@ CdmResponseType CdmUsageTable::LoadEntry(CryptoSession* crypto_session, return CdmResponseType(USAGE_INVALID_LOAD_ENTRY); } } - metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics(); - if (metrics == nullptr) metrics = &alternate_crypto_metrics_; const CdmResponseType status = crypto_session->LoadUsageEntry(entry_index, entry); @@ -590,14 +588,11 @@ CdmResponseType CdmUsageTable::RelocateNewEntry( } bool CdmUsageTable::IsEntryUnoccupied(const UsageEntryIndex entry_index) const { - if (entry_info_list_[entry_index].storage_type != kStorageTypeUnknown) { - return false; - } // TODO(sigquit): Check that entry is not in use by another session. // NOTE: The |storage_type| check will protect the integrity of the // entry. Attempting to use an entry index that is used by another // session is recoverable and will not affect any opened sessions. - return true; + return entry_info_list_[entry_index].storage_type == kStorageTypeUnknown; } void CdmUsageTable::SetOfflineEntryInfo(const UsageEntryIndex entry_index, diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index 304c41ff..44f40bc7 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -89,7 +89,6 @@ bool RetrieveOemCertificateAndLoadPrivateKey(CryptoSession& crypto_session, } // namespace // Protobuf generated classes. using video_widevine::DrmCertificate; -using video_widevine::EncryptedClientIdentification; using video_widevine::HashAlgorithmProto; using video_widevine::ProvisioningOptions; using video_widevine::ProvisioningRequest; diff --git a/libwvdrmengine/cdm/core/src/client_identification.cpp b/libwvdrmengine/cdm/core/src/client_identification.cpp index b3f74b56..08f252d9 100644 --- a/libwvdrmengine/cdm/core/src/client_identification.cpp +++ b/libwvdrmengine/cdm/core/src/client_identification.cpp @@ -62,11 +62,6 @@ using ClientCapabilities = video_widevine::ClientIdentification::ClientCapabilities; using AnalogOutputCapabilities = ClientCapabilities::AnalogOutputCapabilities; using video_widevine::ClientIdentification_NameValue; -using video_widevine::EncryptedClientIdentification; -using video_widevine::ProvisioningOptions; -using video_widevine::ProvisioningRequest; -using video_widevine::ProvisioningResponse; -using video_widevine::SignedProvisioningMessage; CdmResponseType ClientIdentification::InitForProvisioningRequest( const std::string& client_token, CryptoSession* crypto_session) { diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index b748e757..a46c49d5 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -1635,7 +1635,8 @@ CdmResponseType CryptoSession::GenerateDerivedKeys( } CdmResponseType CryptoSession::GenerateRsaSignature(const std::string& message, - std::string* signature) { + std::string* signature, + RSA_Padding_Scheme scheme) { LOGV("Generating RSA signature: id = %u", oec_session_id_); RETURN_IF_NULL(signature, PARAMETER_NULL); @@ -1652,7 +1653,7 @@ CdmResponseType CryptoSession::GenerateRsaSignature(const std::string& message, oec_session_id_, reinterpret_cast(message.data()), message.size(), reinterpret_cast(const_cast(signature->data())), - &length, kSign_RSASSA_PSS), + &length, scheme), metrics_, oemcrypto_generate_rsa_signature_, sts, metrics::Pow2Bucket(length)); }); @@ -3401,4 +3402,5 @@ CryptoSession* CryptoSessionFactory::MakeCryptoSession( metrics::CryptoMetrics* crypto_metrics) { return new CryptoSession(crypto_metrics); } + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index 4bbb786e..c3ca252a 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -693,9 +693,7 @@ bool DeviceFiles::RetrieveLegacyCertificate(std::string* certificate, const CertificateState state = RetrieveCertificate( kCertificateLegacy, certificate, private_key, serial_number, system_id); - if (state == kCertificateValid || state == kCertificateExpired) return true; - - return false; + return state == kCertificateValid || state == kCertificateExpired; } bool DeviceFiles::HasCertificate(bool atsc_mode_enabled) { diff --git a/libwvdrmengine/cdm/core/src/initialization_data.cpp b/libwvdrmengine/cdm/core/src/initialization_data.cpp index e819aef7..373e5a96 100644 --- a/libwvdrmengine/cdm/core/src/initialization_data.cpp +++ b/libwvdrmengine/cdm/core/src/initialization_data.cpp @@ -43,11 +43,8 @@ namespace wvcdm { // Protobuf generated classes. using video_widevine::WidevinePsshData; -using video_widevine::WidevinePsshData_Algorithm; using video_widevine::WidevinePsshData_Algorithm_AESCTR; -using video_widevine::WidevinePsshData_Type; using video_widevine::WidevinePsshData_Type_ENTITLED_KEY; -using video_widevine::WidevinePsshData_Type_SINGLE; InitializationData::InitializationData(const std::string& type, const CdmInitData& data, @@ -376,7 +373,7 @@ bool InitializationData::ExtractHlsAttributes(const std::string& attribute_list, std::vector versions = ExtractKeyFormatVersions(value); bool supported = false; for (size_t i = 0; i < versions.size(); ++i) { - if (versions[i].compare(HLS_KEYFORMAT_VERSION_VALUE_1) == 0) { + if (versions[i] == HLS_KEYFORMAT_VERSION_VALUE_1) { supported = true; break; } @@ -393,11 +390,11 @@ bool InitializationData::ExtractHlsAttributes(const std::string& attribute_list, return false; } - if (value.compare(HLS_METHOD_AES_128) == 0) { + if (value == HLS_METHOD_AES_128) { *method = kHlsMethodAes128; - } else if (value.compare(HLS_METHOD_SAMPLE_AES) == 0) { + } else if (value == HLS_METHOD_SAMPLE_AES) { *method = kHlsMethodSampleAes; - } else if (value.compare(HLS_METHOD_NONE) == 0) { + } else if (value == HLS_METHOD_NONE) { *method = kHlsMethodNone; } else { LOGV("HLS method unrecognized: value = %s", value.c_str()); @@ -734,7 +731,8 @@ void InitializationData::DumpToLogs() const { } for (int i = 0; i < pssh.entitled_keys_size(); i++) { - video_widevine::WidevinePsshData_EntitledKey key = pssh.entitled_keys(i); + const video_widevine::WidevinePsshData_EntitledKey& key = + pssh.entitled_keys(i); LOGD("InitData: entitlement_key_id %d: %s -> %s", i, wvutil::b2a_hex(key.entitlement_key_id()).c_str(), wvutil::b2a_hex(key.key_id()).c_str()); diff --git a/libwvdrmengine/cdm/core/src/license_key_status.cpp b/libwvdrmengine/cdm/core/src/license_key_status.cpp index 7326be0d..cbe69191 100644 --- a/libwvdrmengine/cdm/core/src/license_key_status.cpp +++ b/libwvdrmengine/cdm/core/src/license_key_status.cpp @@ -323,7 +323,7 @@ void LicenseKeyStatus::ParseContentKey(const KeyContainer& key, void LicenseKeyStatus::ParseOperatorSessionKey(const KeyContainer& key) { is_content_key_ = false; if (key.has_operator_session_key_permissions()) { - OperatorSessionKeyPermissions permissions = + const OperatorSessionKeyPermissions& permissions = key.operator_session_key_permissions(); if (permissions.has_allow_encrypt()) allowed_usage_.generic_encrypt = permissions.allow_encrypt(); diff --git a/libwvdrmengine/cdm/core/src/policy_timers.cpp b/libwvdrmengine/cdm/core/src/policy_timers.cpp index 6a17f810..d03dc8b5 100644 --- a/libwvdrmengine/cdm/core/src/policy_timers.cpp +++ b/libwvdrmengine/cdm/core/src/policy_timers.cpp @@ -10,8 +10,6 @@ #include "log.h" -using video_widevine::License; - namespace wvcdm { void PolicyTimers::SetLicense(const video_widevine::License& license) { @@ -68,7 +66,7 @@ bool PolicyTimers::GetSecondsSinceStarted(int64_t current_time, if (playback_start_time_ == 0) return false; *seconds_since_started = current_time - playback_start_time_; - return (*seconds_since_started >= 0) ? true : false; + return *seconds_since_started >= 0; } bool PolicyTimers::GetSecondsSinceLastPlayed( @@ -81,7 +79,7 @@ bool PolicyTimers::GetSecondsSinceLastPlayed( if (last_playback_time_ == 0) return false; *seconds_since_last_played = current_time - last_playback_time_; - return (*seconds_since_last_played >= 0) ? true : false; + return *seconds_since_last_played >= 0; } bool PolicyTimers::IsLicenseForFuture(int64_t current_time) { diff --git a/libwvdrmengine/cdm/core/src/policy_timers_v16.cpp b/libwvdrmengine/cdm/core/src/policy_timers_v16.cpp index 60f663f0..30e71b84 100644 --- a/libwvdrmengine/cdm/core/src/policy_timers_v16.cpp +++ b/libwvdrmengine/cdm/core/src/policy_timers_v16.cpp @@ -28,11 +28,8 @@ bool PolicyTimersV16::UpdateLicense(int64_t current_time, next_renewal_time_ = license.license_start_time() + policy_.renewal_delay_seconds(); - if (!policy_.can_play() || - HasLicenseOrRentalOrPlaybackDurationExpired(current_time)) - return false; - - return true; + return policy_.can_play() && + !HasLicenseOrRentalOrPlaybackDurationExpired(current_time); } void PolicyTimersV16::BeginDecryption(int64_t current_time) { diff --git a/libwvdrmengine/cdm/core/src/policy_timers_v18.cpp b/libwvdrmengine/cdm/core/src/policy_timers_v18.cpp index e112faf2..ad46cf80 100644 --- a/libwvdrmengine/cdm/core/src/policy_timers_v18.cpp +++ b/libwvdrmengine/cdm/core/src/policy_timers_v18.cpp @@ -65,11 +65,8 @@ bool PolicyTimersV18::UpdateLicense(int64_t current_time, license_renewal_ = true; } - if (!policy_.can_play() || - HasLicenseOrRentalOrPlaybackDurationExpired(current_time)) - return false; - - return true; + return policy_.can_play() && + !HasLicenseOrRentalOrPlaybackDurationExpired(current_time); } void PolicyTimersV18::BeginDecryption(int64_t current_time) { diff --git a/libwvdrmengine/cdm/core/src/service_certificate.cpp b/libwvdrmengine/cdm/core/src/service_certificate.cpp index 09ab4939..c89648a1 100644 --- a/libwvdrmengine/cdm/core/src/service_certificate.cpp +++ b/libwvdrmengine/cdm/core/src/service_certificate.cpp @@ -19,7 +19,7 @@ namespace { // as the root of a signing chain. // clang-format off -static const unsigned char kRootCertForProd[] = { +const unsigned char kRootCertForProd[] = { 0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00, 0x18, 0xdd, 0x94, 0x88, 0x8b, 0x05, 0x22, 0x8e, 0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, diff --git a/libwvdrmengine/cdm/core/src/wv_cdm_types.cpp b/libwvdrmengine/cdm/core/src/wv_cdm_types.cpp index adfed848..6015f1f7 100644 --- a/libwvdrmengine/cdm/core/src/wv_cdm_types.cpp +++ b/libwvdrmengine/cdm/core/src/wv_cdm_types.cpp @@ -867,6 +867,8 @@ const char* CdmResponseEnumToString(CdmResponseEnum cdm_response_enum) { return "STORE_ATSC_LICENSE_ERROR"; case SESSION_NOT_FOUND_GENERIC_CRYPTO: return "SESSION_NOT_FOUND_GENERIC_CRYPTO"; + case SESSION_NOT_FOUND_24: + return "SESSION_NOT_FOUND_24"; } return UnknownValueRep(cdm_response_enum); } diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_metrics_decorator_unittest.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_metrics_decorator_unittest.cpp index b254e871..b325578d 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_metrics_decorator_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_metrics_decorator_unittest.cpp @@ -20,7 +20,6 @@ #include "wv_metrics.pb.h" using ::testing::_; -using ::testing::ByRef; using ::testing::Eq; using ::testing::Matcher; using ::testing::Return; diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp index 8f266bed..9ab7598b 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -34,7 +34,6 @@ namespace wvcdm { using drm_metrics::DistributionMetric; using drm_metrics::WvCdmMetrics; -using metrics::EngineMetrics; namespace { @@ -153,7 +152,7 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest { WvCdmEngineTest() {} void SetUp() override { - WvCdmTestBase::SetUp(); + WvCdmEnginePreProvTest::SetUp(); session_opened_ = false; WvCdmEnginePreProvTest::OpenSession(); } @@ -275,12 +274,12 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest { engine_metrics.Serialize(&metrics_proto); bool has_request_type = false; for (int i = 0; i < metrics_proto.session_metrics_size(); i++) { - WvCdmMetrics::SessionMetrics session_metrics = + const WvCdmMetrics::SessionMetrics& session_metrics = metrics_proto.session_metrics(i); for (int j = 0; j < session_metrics.cdm_session_license_request_latency_ms_size(); j++) { - DistributionMetric latency_distribution = + const DistributionMetric& latency_distribution = session_metrics.cdm_session_license_request_latency_ms(j); if (latency_distribution.attributes().key_request_type() == key_request_type && @@ -362,7 +361,7 @@ TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) { TEST_F(WvCdmEngineTest, DISABLED_BaseWebmMessageTest) { // Extract the key ID from the PSSH box. InitializationData extractor(CENC_INIT_DATA_FORMAT, binary_key_id()); - KeyId key_id_unwrapped = extractor.data(); + const KeyId& key_id_unwrapped = extractor.data(); GenerateKeyRequest(key_id_unwrapped, kWebmMimeType); GetKeyRequestResponse(config_.license_server(), config_.client_auth()); } @@ -383,7 +382,7 @@ TEST_F(WvCdmEngineTest, ReturnsLicenseTypeDetailStreaming) { TEST_F(WvCdmEngineTest, DISABLED_NormalDecryptionWebm) { // Extract the key ID from the PSSH box. InitializationData extractor(CENC_INIT_DATA_FORMAT, binary_key_id()); - KeyId key_id_unwrapped = extractor.data(); + const KeyId& key_id_unwrapped = extractor.data(); GenerateKeyRequest(key_id_unwrapped, kWebmMimeType); VerifyNewKeyResponse(config_.license_server(), config_.client_auth()); } diff --git a/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp b/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp index 40e84c62..0e553f53 100644 --- a/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp @@ -30,7 +30,6 @@ using ::testing::NotNull; using ::testing::Return; using ::testing::Sequence; using ::testing::SetArgPointee; -using ::testing::StrEq; namespace wvcdm { @@ -216,7 +215,7 @@ class CdmSessionTest : public WvCdmTestBase { protected: void SetUp() override { WvCdmTestBase::SetUp(); - metrics_.reset(new metrics::SessionMetrics); + metrics_ = std::make_shared(); cdm_session_.reset(new CdmSession(nullptr, metrics_)); // Inject testing mocks. license_parser_ = new MockCdmLicense(cdm_session_->session_id()); diff --git a/libwvdrmengine/cdm/core/test/cdm_usage_table_unittest.cpp b/libwvdrmengine/cdm/core/test/cdm_usage_table_unittest.cpp index 369e2493..5d602796 100644 --- a/libwvdrmengine/cdm/core/test/cdm_usage_table_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_usage_table_unittest.cpp @@ -23,24 +23,16 @@ // gmock methods using ::testing::_; -using ::testing::AllOf; -using ::testing::AtMost; using ::testing::ContainerEq; using ::testing::Contains; using ::testing::DoAll; using ::testing::ElementsAre; -using ::testing::ElementsAreArray; -using ::testing::Ge; using ::testing::Invoke; -using ::testing::InvokeWithoutArgs; -using ::testing::Lt; using ::testing::NotNull; using ::testing::Return; using ::testing::SaveArg; using ::testing::SetArgPointee; using ::testing::SizeIs; -using ::testing::StrEq; -using ::testing::UnorderedElementsAre; using ::testing::UnorderedElementsAreArray; namespace wvcdm { @@ -561,9 +553,7 @@ class CdmUsageTableTest : public WvCdmTestBase { return mock_usage_table; } - void TearDown() override { - if (usage_table_ != nullptr) delete usage_table_; - } + void TearDown() override { delete usage_table_; } void Init(CdmSecurityLevel security_level, const UsageTableHeader& table_header, @@ -3703,7 +3693,7 @@ TEST_F(CdmUsageTableTest, LruUsageTableUpgrade_NoAction) { RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), SetArgPointee<1>(kUpgradableUsageEntryInfoList), - SetArgPointee<2>(/* lru_upgrade = */ false), + SetArgPointee<2>(false), // lru_upgrade Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader)) @@ -3733,7 +3723,7 @@ TEST_F(CdmUsageTableTest, LruUsageTableUpgrade_Succeed) { RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), SetArgPointee<1>(kUpgradableUsageEntryInfoList), - SetArgPointee<2>(/* lru_upgrade = */ true), + SetArgPointee<2>(true), // lru_upgrade Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader)) @@ -3802,7 +3792,7 @@ TEST_F(CdmUsageTableTest, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), SetArgPointee<1>(upgradable_usage_entry_info_list), - SetArgPointee<2>(/* lru_upgrade = */ true), + SetArgPointee<2>(true), // lru_upgrade Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader)) @@ -3865,7 +3855,7 @@ TEST_F(CdmUsageTableTest, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), SetArgPointee<1>(kUpgradableUsageEntryInfoList), - SetArgPointee<2>(/* lru_upgrade = */ true), + SetArgPointee<2>(true), // lru_upgrade Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader)) @@ -3923,7 +3913,7 @@ TEST_F(CdmUsageTableTest, LruUsageTableUpgrade_AllFailure) { RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), SetArgPointee<1>(kUpgradableUsageEntryInfoList), - SetArgPointee<2>(/* lru_upgrade = */ true), + SetArgPointee<2>(true), // lru_upgrade Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader)) diff --git a/libwvdrmengine/cdm/core/test/certificate_provisioning_unittest.cpp b/libwvdrmengine/cdm/core/test/certificate_provisioning_unittest.cpp index ce9173ab..9f80b2be 100644 --- a/libwvdrmengine/cdm/core/test/certificate_provisioning_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/certificate_provisioning_unittest.cpp @@ -87,9 +87,6 @@ class TestStubCryptoSessionFactory : public CryptoSessionFactory { } }; -// gmock methods -using ::testing::_; - class CertificateProvisioningTest : public WvCdmTestBase { protected: void SetUp() override { diff --git a/libwvdrmengine/cdm/core/test/config_test_env.h b/libwvdrmengine/cdm/core/test/config_test_env.h index bab2b3c9..9b3906b4 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.h +++ b/libwvdrmengine/cdm/core/test/config_test_env.h @@ -134,6 +134,11 @@ class ConfigTestEnv { // The QA service certificate, used for a local provisioning server. static std::string QAProvisioningServiceCertificate(); + bool dump_golden_data() const { return dump_golden_data_; } + void set_dump_golden_data(bool dump_golden_data) { + dump_golden_data_ = dump_golden_data; + } + private: void Init(ServerConfigurationId server_id); @@ -149,6 +154,9 @@ class ConfigTestEnv { int test_pass_; std::string test_data_path_; // Where to store test data for reboot tests. int server_version_ = 0; + // It dump_golden_data_ is true, message data is dumped to a file for help + // in generating golden test data. + bool dump_golden_data_ = false; }; // The default provisioning server URL for a default provisioning request. diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index 47cbc8c6..1e0f811a 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -29,7 +29,6 @@ using wvutil::ArraySize; using wvutil::b2a_hex; using wvutil::CdmRandom; using wvutil::File; -using wvutil::FileSystem; const uint32_t kCertificateLen = 700; const uint32_t kWrappedKeyLen = 500; @@ -4444,17 +4443,12 @@ class MockFileSystem : public wvutil::FileSystem { // gmock methods using ::testing::_; using ::testing::AllArgs; -using ::testing::AllOf; using ::testing::AtLeast; using ::testing::ByMove; using ::testing::DoAll; using ::testing::Eq; using ::testing::Expectation; -using ::testing::Gt; -using ::testing::HasSubstr; -using ::testing::InSequence; using ::testing::Invoke; -using ::testing::InvokeWithoutArgs; using ::testing::NotNull; using ::testing::Return; using ::testing::ReturnArg; diff --git a/libwvdrmengine/cdm/core/test/duration_use_case_test.cpp b/libwvdrmengine/cdm/core/test/duration_use_case_test.cpp index f0973e07..d2ffd2f3 100644 --- a/libwvdrmengine/cdm/core/test/duration_use_case_test.cpp +++ b/libwvdrmengine/cdm/core/test/duration_use_case_test.cpp @@ -141,6 +141,14 @@ class CdmDurationTest : public WvCdmTestBaseWithEngine, // All times in the license are relative to the rental clock. start_of_rental_clock_ = wvutil::Clock().GetCurrentTime(); license_holder_.FetchLicense(); + // Record the time it took to fetch the license. This will be used as + // some extra fudge when looking for playback cutoff at the end of the + // rental window. + extra_fudge_time_ = CurrentRentalTime(); + if (extra_fudge_time_ > kRoundTripTime) { + LOGE("FetchLicense took a long time: %" PRIu64 " seconds", + extra_fudge_time_); + } } void TearDown() override { @@ -182,16 +190,8 @@ class CdmDurationTest : public WvCdmTestBaseWithEngine, // Sleep until the specified time on the rental clock. void SleepUntil(uint64_t desired_rental_time) { - const uint64_t rental_time = CurrentRentalTime(); - if (desired_rental_time >= rental_time) { - const unsigned int sleep_time = - static_cast(desired_rental_time - rental_time); - wvutil::TestSleep::Sleep(sleep_time); - } else { - LOGW("Test Clock skew sleeping from rental clock time %" PRIu64 - " to %" PRIu64, - rental_time, desired_rental_time); - } + // We use rental clock in this file, but TestSleep uses the system clock. + wvutil::TestSleep::SleepUntil(desired_rental_time + start_of_rental_clock_); cdm_engine_.OnTimerEvent(); } @@ -232,7 +232,10 @@ class CdmDurationTest : public WvCdmTestBaseWithEngine, // a green check mark. Both |start| and |stop| are all system times. void AllowPlayback(uint64_t start, uint64_t stop) { ASSERT_LT(start, stop); - SleepUntil(start); + // If now is before start, then sleep until start. + // If we are already after start, don't try to sleep or a warning will be + // logged. Instead, we'll just start playback as soon as we can. + if (start > CurrentRentalTime()) SleepUntil(start); Decrypt(); const uint64_t mid = (start + stop) / 2; SleepUntil(mid); @@ -278,11 +281,21 @@ class CdmDurationTest : public WvCdmTestBaseWithEngine, void AllowLenience() { allow_lenience_ = true; } void Decrypt() { - const uint64_t now = CurrentRentalTime(); + const uint64_t start = CurrentRentalTime(); EXPECT_EQ(NO_ERROR, license_holder_.Decrypt(kKeyId)) - << "Failed to decrypt when rental clock = " << now + << "Failed to decrypt when rental clock = " << start << ", and playback clock = " - << ((now < start_of_playback_) ? 0 : (now - start_of_playback_)); + << ((start < start_of_playback_) ? 0 : (start - start_of_playback_)); + const uint64_t fudge = CurrentRentalTime() - start; + if (fudge > kFudge) { + LOGE("Decrypt took a long time: %" PRIu64 " seconds", fudge); + } + // We need extra_fudge_time_ to be at least as big as the first decrypt to + // allow extra fudge when looking for playback cutoff at the end of the + // playback window. + if (fudge > extra_fudge_time_) { + extra_fudge_time_ = fudge; + } } void FailDecrypt() { @@ -311,10 +324,15 @@ class CdmDurationTest : public WvCdmTestBaseWithEngine, << now << ", and playback clock = " << (now < start_of_playback_ ? 0 : now - start_of_playback_); // However, for those cases decided above, we also allow success. - EXPECT_TRUE(allow_success) + if (allow_success) return; + // Try again after allowing for the extra fudge time. See comment at the + // definition of extra_fudge_time_ for an explanation. + wvutil::TestSleep::Sleep(static_cast(extra_fudge_time_)); + ASSERT_EQ(NEED_KEY, license_holder_.Decrypt(kKeyId)) << "Device was unexpectedly lenient when rental clock = " << now << ", and playback clock = " - << (now < start_of_playback_ ? 0 : now - start_of_playback_); + << (now < start_of_playback_ ? 0 : now - start_of_playback_) + << ", extra fudge = " << extra_fudge_time_; } LicenseHolder license_holder_; @@ -331,6 +349,16 @@ class CdmDurationTest : public WvCdmTestBaseWithEngine, // If this is set, then the next time we expect a playback to be terminated, // we will allow lenient failure. bool allow_lenience_; + // Extra fudge accounts for the amount of extra time it takes to perform an + // operation that starts a clock. In some test environments under high load, + // creating the license request might take a second or more. The rental clock + // starts at the time the license request is signed -- this extra fudge + // accounts for the question of whether we mean at the beginning or the end of + // the siging operation. Similarly the playback clock starts at the first + // decrypt. This value, extra_fudge_time_, is the maximum of the license + // request time and the first decrypt time, so it can be used for testing + // cutoff for either rental duration or playback duration. + uint64_t extra_fudge_time_ = 0; }; /*****************************************************************************/ diff --git a/libwvdrmengine/cdm/core/test/fake_provisioning_server.cpp b/libwvdrmengine/cdm/core/test/fake_provisioning_server.cpp index 9b0b182b..359307e3 100644 --- a/libwvdrmengine/cdm/core/test/fake_provisioning_server.cpp +++ b/libwvdrmengine/cdm/core/test/fake_provisioning_server.cpp @@ -324,7 +324,7 @@ bool FakeProvisioningServer::MakeResponse( if (signed_request.has_oemcrypto_core_message()) { // If the request has a core message, then the response should also have a // core message. - std::string core_request = signed_request.oemcrypto_core_message(); + const std::string& core_request = signed_request.oemcrypto_core_message(); oemcrypto_core_message::ODK_ProvisioningRequest core_request_data; if (!oemcrypto_core_message::deserialize:: CoreProvisioningRequestFromMessage(core_request, diff --git a/libwvdrmengine/cdm/core/test/http_socket.cpp b/libwvdrmengine/cdm/core/test/http_socket.cpp index 9760a65e..51d0436d 100644 --- a/libwvdrmengine/cdm/core/test/http_socket.cpp +++ b/libwvdrmengine/cdm/core/test/http_socket.cpp @@ -67,8 +67,8 @@ SSL_CTX* InitSslContext() { return ctx; } -static int LogBoringSslError(const char* message, size_t /* length */, - void* /* user_data */) { +int LogBoringSslError(const char* message, size_t /* length */, + void* /* user_data */) { LOGE(" BoringSSL Error: %s", message); return 1; } diff --git a/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp b/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp index 86cfe36d..57d2265f 100644 --- a/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp @@ -24,9 +24,6 @@ using video_widevine::WidevinePsshData; namespace { -// Import names from ::testing for convenience -using ::testing::_; - // Constants for JSON formatting const std::string kLeftBrace = "{"; const std::string kRightBrace = "}"; @@ -807,12 +804,12 @@ TEST_P(HlsParseTest, Parse) { if (param.success_) { EXPECT_TRUE(init_data.is_hls()); EXPECT_FALSE(init_data.IsEmpty()); - if (param.key_.compare(HLS_METHOD_ATTRIBUTE) == 0) { - if (param.value_.compare(HLS_METHOD_SAMPLE_AES) == 0) { + if (param.key_ == HLS_METHOD_ATTRIBUTE) { + if (param.value_ == HLS_METHOD_SAMPLE_AES) { EXPECT_EQ(kHlsMethodSampleAes, init_data.hls_method()); - } else if (param.value_.compare(HLS_METHOD_AES_128) == 0) { + } else if (param.value_ == HLS_METHOD_AES_128) { EXPECT_EQ(kHlsMethodAes128, init_data.hls_method()); - } else if (param.value_.compare(HLS_METHOD_NONE) == 0) { + } else if (param.value_ == HLS_METHOD_NONE) { EXPECT_EQ(kHlsMethodNone, init_data.hls_method()); } } else { @@ -825,11 +822,11 @@ TEST_P(HlsParseTest, Parse) { CORE_UTIL_IGNORE_DEPRECATED EXPECT_EQ(video_widevine::WidevinePsshData_Algorithm_AESCTR, cenc_header.algorithm()); - if (param.key_.compare(kJsonProvider) == 0) { + if (param.key_ == kJsonProvider) { EXPECT_EQ(param.value_, cenc_header.provider()); - } else if (param.key_.compare(kJsonContentId) == 0) { + } else if (param.key_ == kJsonContentId) { EXPECT_EQ(param.value_, cenc_header.content_id()); - } else if (param.key_.compare(kJsonKeyIds) == 0) { + } else if (param.key_ == kJsonKeyIds) { EXPECT_EQ(param.value_, wvutil::b2a_hex(cenc_header.key_ids(0))); } CORE_UTIL_RESTORE_WARNINGS diff --git a/libwvdrmengine/cdm/core/test/keybox_ota_test.cpp b/libwvdrmengine/cdm/core/test/keybox_ota_test.cpp index fc5a2df9..efffd785 100644 --- a/libwvdrmengine/cdm/core/test/keybox_ota_test.cpp +++ b/libwvdrmengine/cdm/core/test/keybox_ota_test.cpp @@ -44,8 +44,7 @@ class CdmOtaKeyboxTest : public ::testing::Test { void Provision(TestCdmEngine* cdm_engine) { ConfigTestEnv config = *WvCdmTestBase::default_config_; - ProvisioningHolder provisioner(cdm_engine, config.provisioning_server(), - config.provisioning_service_certificate()); + ProvisioningHolder provisioner(cdm_engine, config); CdmCertificateType cert_type = kCertificateWidevine; constexpr bool binary_provisioning = false; provisioner.Provision(cert_type, binary_provisioning); diff --git a/libwvdrmengine/cdm/core/test/license_holder.cpp b/libwvdrmengine/cdm/core/test/license_holder.cpp index 317dfdd6..ceef5315 100644 --- a/libwvdrmengine/cdm/core/test/license_holder.cpp +++ b/libwvdrmengine/cdm/core/test/license_holder.cpp @@ -5,6 +5,7 @@ #include "license_holder.h" #include "license_request.h" +#include "message_dumper.h" #include "oec_device_features.h" #include "test_base.h" @@ -37,8 +38,14 @@ void LicenseHolder::FetchLicense() { CdmKeyRequest key_request; ASSERT_NO_FATAL_FAILURE(GenerateKeyRequest(init_data, &key_request)) << "Failed for " << content_id(); + if (config_.dump_golden_data()) { + MessageDumper::DumpLicenseRequest(key_request); + } ASSERT_NO_FATAL_FAILURE(GetKeyResponse(key_request)) << "Failed for " << content_id(); + if (config_.dump_golden_data()) { + MessageDumper::DumpLicense(key_response_); + } } void LicenseHolder::LoadLicense() { @@ -75,6 +82,9 @@ void LicenseHolder::GenerateAndPostRenewalRequest( const CdmResponseType result = cdm_engine_->GenerateRenewalRequest(session_id_, &request); ASSERT_EQ(KEY_MESSAGE, result) << "Failed for " << content_id(); + if (config_.dump_golden_data()) { + MessageDumper::DumpRenewalRequest(request); + } const std::string url = MakeUrl(config_.renewal_server(), policy_id); renewal_in_flight_.reset(new UrlRequest(url)); ASSERT_TRUE(renewal_in_flight_->is_connected()) @@ -91,6 +101,9 @@ void LicenseHolder::FetchRenewal() { void LicenseHolder::LoadRenewal() { LicenseRequest license_request; license_request.GetDrmMessage(renewal_response_, renewal_message_); + if (config_.dump_golden_data()) { + MessageDumper::DumpRenewal(renewal_message_); + } EXPECT_EQ(KEY_ADDED, cdm_engine_->RenewKey(session_id_, renewal_message_)) << "Failed for " << content_id(); } @@ -204,7 +217,7 @@ std::string LicenseHolder::MakeUrl(const std::string& server_url, } // If there is already a parameter, then we don't need to add another // question mark. - return path + ((path.find("?") == std::string::npos) ? "?" : "&") + + return path + ((path.find('?') == std::string::npos) ? '?' : '&') + video_query; } else { return path; diff --git a/libwvdrmengine/cdm/core/test/license_keys_unittest.cpp b/libwvdrmengine/cdm/core/test/license_keys_unittest.cpp index 8a8f7b91..1dc7991a 100644 --- a/libwvdrmengine/cdm/core/test/license_keys_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/license_keys_unittest.cpp @@ -12,59 +12,58 @@ namespace wvcdm { namespace { -static const uint32_t dev_lo_res = 200; -static const uint32_t dev_hi_res = 400; -static const uint32_t dev_top_res = 800; +const uint32_t dev_lo_res = 200; +const uint32_t dev_hi_res = 400; +const uint32_t dev_top_res = 800; -static const uint32_t key_lo_res_min = 151; -static const uint32_t key_lo_res_max = 300; -static const uint32_t key_hi_res_min = 301; -static const uint32_t key_hi_res_max = 450; -static const uint32_t key_top_res_min = 451; -static const uint32_t key_top_res_max = 650; +const uint32_t key_lo_res_min = 151; +const uint32_t key_lo_res_max = 300; +const uint32_t key_hi_res_min = 301; +const uint32_t key_hi_res_max = 450; +const uint32_t key_top_res_min = 451; +const uint32_t key_top_res_max = 650; // Content Keys -static const KeyId ck_sw_crypto = "c_key_SW_SECURE_CRYPTO"; -static const KeyId ck_sw_decode = "c_key_SW_SECURE_DECODE"; -static const KeyId ck_hw_crypto = "c_key_HW_SECURE_CRYPTO"; -static const KeyId ck_hw_decode = "c_key_HW_SECURE_DECODE"; -static const KeyId ck_hw_secure = "c_key_HW_SECURE_ALL"; +const KeyId ck_sw_crypto = "c_key_SW_SECURE_CRYPTO"; +const KeyId ck_sw_decode = "c_key_SW_SECURE_DECODE"; +const KeyId ck_hw_crypto = "c_key_HW_SECURE_CRYPTO"; +const KeyId ck_hw_decode = "c_key_HW_SECURE_DECODE"; +const KeyId ck_hw_secure = "c_key_HW_SECURE_ALL"; // Operator Session Keys -static const KeyId osk_decrypt = "os_key_generic_decrypt"; -static const KeyId osk_encrypt = "os_key_generic_encrypt"; -static const KeyId osk_sign = "os_key_generic_sign"; -static const KeyId osk_verify = "os_key_generic_verify"; -static const KeyId osk_encrypt_decrypt = "os_key_generic_encrypt_decrypt"; -static const KeyId osk_sign_verify = "os_key_generic_sign_verify"; -static const KeyId osk_all = "os_key_generic_all"; +const KeyId osk_decrypt = "os_key_generic_decrypt"; +const KeyId osk_encrypt = "os_key_generic_encrypt"; +const KeyId osk_sign = "os_key_generic_sign"; +const KeyId osk_verify = "os_key_generic_verify"; +const KeyId osk_encrypt_decrypt = "os_key_generic_encrypt_decrypt"; +const KeyId osk_sign_verify = "os_key_generic_sign_verify"; +const KeyId osk_all = "os_key_generic_all"; // HDCP test keys -static const KeyId ck_sw_crypto_NO_HDCP = "ck_sw_crypto_NO_HDCP"; -static const KeyId ck_hw_secure_NO_HDCP = "ck_hw_secure_NO_HDCP"; -static const KeyId ck_sw_crypto_HDCP_V2_1 = "ck_sw_crypto_HDCP_V2_1"; -static const KeyId ck_hw_secure_HDCP_V2_1 = "ck_hw_secure_HDCP_V2_1"; -static const KeyId ck_sw_crypto_HDCP_V2_2 = "ck_sw_crypto_HDCP_V2_2"; -static const KeyId ck_hw_secure_HDCP_V2_2 = "ck_hw_secure_HDCP_V2_2"; -static const KeyId ck_sw_crypto_HDCP_V2_3 = "ck_sw_crypto_HDCP_V2_3"; -static const KeyId ck_hw_secure_HDCP_V2_3 = "ck_hw_secure_HDCP_V2_3"; -static const KeyId ck_sw_crypto_HDCP_NO_OUTPUT = "ck_sw_crypto_HDCP_NO_OUT"; -static const KeyId ck_hw_secure_HDCP_NO_OUTPUT = "ck_hw_secure_HDCP_NO_OUT"; +const KeyId ck_sw_crypto_NO_HDCP = "ck_sw_crypto_NO_HDCP"; +const KeyId ck_hw_secure_NO_HDCP = "ck_hw_secure_NO_HDCP"; +const KeyId ck_sw_crypto_HDCP_V2_1 = "ck_sw_crypto_HDCP_V2_1"; +const KeyId ck_hw_secure_HDCP_V2_1 = "ck_hw_secure_HDCP_V2_1"; +const KeyId ck_sw_crypto_HDCP_V2_2 = "ck_sw_crypto_HDCP_V2_2"; +const KeyId ck_hw_secure_HDCP_V2_2 = "ck_hw_secure_HDCP_V2_2"; +const KeyId ck_sw_crypto_HDCP_V2_3 = "ck_sw_crypto_HDCP_V2_3"; +const KeyId ck_hw_secure_HDCP_V2_3 = "ck_hw_secure_HDCP_V2_3"; +const KeyId ck_sw_crypto_HDCP_NO_OUTPUT = "ck_sw_crypto_HDCP_NO_OUT"; +const KeyId ck_hw_secure_HDCP_NO_OUTPUT = "ck_hw_secure_HDCP_NO_OUT"; // Constraint test keys -static const KeyId ck_NO_HDCP_lo_res = "ck_NO_HDCP_lo_res"; -static const KeyId ck_HDCP_NO_OUTPUT_hi_res = "ck_HDCP_NO_OUTPUT_hi_res"; -static const KeyId ck_HDCP_V2_1_hi_res = "ck_HDCP_V2_1_hi_res"; -static const KeyId ck_HDCP_V2_2_max_res = "ck_HDCP_V2_2_max_res"; -static const KeyId ck_HDCP_V2_3_max_res = "ck_HDCP_V2_3_max_res"; -static const KeyId ck_NO_HDCP_dual_res = "ck_NO_HDCP_dual_res"; +const KeyId ck_NO_HDCP_lo_res = "ck_NO_HDCP_lo_res"; +const KeyId ck_HDCP_NO_OUTPUT_hi_res = "ck_HDCP_NO_OUTPUT_hi_res"; +const KeyId ck_HDCP_V2_1_hi_res = "ck_HDCP_V2_1_hi_res"; +const KeyId ck_HDCP_V2_2_max_res = "ck_HDCP_V2_2_max_res"; +const KeyId ck_HDCP_V2_3_max_res = "ck_HDCP_V2_3_max_res"; +const KeyId ck_NO_HDCP_dual_res = "ck_NO_HDCP_dual_res"; } // namespace // protobuf generated classes. using video_widevine::License; using video_widevine::LicenseIdentification; -using video_widevine::OFFLINE; using video_widevine::STREAMING; using KeyContainer = ::video_widevine::License::KeyContainer; diff --git a/libwvdrmengine/cdm/core/test/license_unittest.cpp b/libwvdrmengine/cdm/core/test/license_unittest.cpp index 87c3fb59..c45804a0 100644 --- a/libwvdrmengine/cdm/core/test/license_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/license_unittest.cpp @@ -190,7 +190,6 @@ using video_widevine::WidevinePsshData_EntitledKey; // gmock methods using ::testing::_; using ::testing::DoAll; -using ::testing::Eq; using ::testing::NotNull; using ::testing::PrintToStringParamName; using ::testing::Return; @@ -229,11 +228,11 @@ class CdmLicenseTest : public WvCdmTestBase { } void TearDown() override { - if (cdm_license_) delete cdm_license_; - if (policy_engine_) delete policy_engine_; - if (init_data_) delete init_data_; - if (crypto_session_) delete crypto_session_; - if (clock_) delete clock_; + delete cdm_license_; + delete policy_engine_; + delete init_data_; + delete crypto_session_; + delete clock_; } virtual void CreateCdmLicense() { diff --git a/libwvdrmengine/cdm/core/test/message_dumper.cpp b/libwvdrmengine/cdm/core/test/message_dumper.cpp new file mode 100644 index 00000000..75ad7999 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/message_dumper.cpp @@ -0,0 +1,218 @@ +// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. + +#include "message_dumper.h" + +#include "license_request.h" +#include "odk.h" +#include "odk_message.h" +#include "odk_serialize.h" +#include "odk_structs.h" +#include "odk_structs_priv.h" +#include "oec_device_features.h" +#include "test_base.h" + +using video_widevine::License; +using video_widevine::LicenseRequest; +using video_widevine::SignedMessage; +using video_widevine::SignedProvisioningMessage; + +namespace wvcdm { +namespace { +void DumpHeader(std::ofstream* out, const std::string& type) { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + const std::string suite = test_info->test_case_name(); + const std::string name = test_info->name(); + std::string new_test_name = suite + "_" + name; + // Replace the slashes with underscores so we can use it as a name again. + std::replace(new_test_name.begin(), new_test_name.end(), '/', '_'); + *out << "\nTEST_F(ODKGolden" << type << "V" << ODK_MAJOR_VERSION << ", " + << new_test_name << ") {\n"; +} + +void DumpHex(std::ofstream* out, const std::string& name, + const std::string& value) { + *out << "const uint8_t " << name << "_raw[] = {\n"; + *out << " "; + for (unsigned int i = 0; i < value.length(); i++) { + if ((i > 0) && (i % 10 == 0)) *out << "\n "; + uint8_t c = value[i]; + *out << "0x" << std::hex << std::setw(2) << std::setfill('0') << int(c) + << ", "; + } + *out << "\n};\n"; + *out << name << "_ = std::string (\n" + << " reinterpret_cast(" << name << "_raw), \n" + << " sizeof(" << name << "_raw));\n"; + *out << std::dec; // Turn off hex when we're done. +} +} // namespace + +std::ofstream MessageDumper::license_file; +std::ofstream MessageDumper::renewal_file; +std::ofstream MessageDumper::provision_file; + +void MessageDumper::SetUp() { + LOGD("Creating golden data files for ODK golden data tests."); + license_file.open("license_data.cpp"); + if (!license_file) LOGE("Could not open dump file license_data.cpp"); + renewal_file.open("renewal_data.cpp"); + if (!renewal_file) LOGE("Could not open dump file renewal_data.cpp"); + provision_file.open("provision_data.cpp"); + if (!provision_file) LOGE("Could not open dump file provision_data.cpp"); +} + +void MessageDumper::TearDown() { + LOGD("Closing golden data files."); + license_file.close(); + renewal_file.close(); + provision_file.close(); +} + +void MessageDumper::DumpLicenseRequest(const CdmKeyRequest& request) { + SignedMessage signed_message; + DumpHeader(&license_file, "License"); + EXPECT_TRUE(signed_message.ParseFromString(request.message)); + EXPECT_TRUE(signed_message.has_oemcrypto_core_message()); + DumpHex(&license_file, "core_request", + signed_message.oemcrypto_core_message()); + // Since this is run within a test, we can also verify that the + // request is valid. + video_widevine::LicenseRequest license_request; + EXPECT_TRUE(license_request.ParseFromString(signed_message.msg())); +} + +void MessageDumper::DumpLicense(const std::string& response) { + SignedMessage signed_response; + EXPECT_TRUE(signed_response.ParseFromString(response)); + EXPECT_TRUE(signed_response.has_oemcrypto_core_message()); + DumpHex(&license_file, "core_response", + signed_response.oemcrypto_core_message()); + + video_widevine::License license; + EXPECT_TRUE(license.ParseFromString(signed_response.msg())); + DumpHex(&license_file, "serialized_license", signed_response.msg()); + + std::string message = + signed_response.oemcrypto_core_message() + signed_response.msg(); + ODK_Message odk_msg = ODK_Message_Create( + reinterpret_cast(const_cast(message.c_str())), + message.length()); + ODK_Message_SetSize(&odk_msg, + signed_response.oemcrypto_core_message().length()); + ODK_ParsedLicense odk_parsed_license = {}; + ODK_LicenseResponse odk_license_response = {}; + odk_license_response.parsed_license = &odk_parsed_license; + Unpack_ODK_LicenseResponse(&odk_msg, &odk_license_response); + EXPECT_EQ(ODK_Message_GetStatus(&odk_msg), MESSAGE_STATUS_OK); + // Valid hash is only needed for v16 messages. + std::string hash(ODK_SHA256_HASH_SIZE, ' '); + DumpHex(&license_file, "core_request_sha256", hash); + license_file << " nonce_required_ = " + << (odk_parsed_license.nonce_required ? "true" : "false") + << ";\n"; + license_file << " RunTest();\n"; + license_file << "}\n\n"; +} + +void MessageDumper::DumpRenewalRequest(const CdmKeyRequest& request) { + DumpHeader(&renewal_file, "Renewal"); + SignedMessage signed_message; + EXPECT_TRUE(signed_message.ParseFromString(request.message)); + EXPECT_TRUE(signed_message.has_oemcrypto_core_message()); + DumpHex(&renewal_file, "core_request", + signed_message.oemcrypto_core_message()); + + video_widevine::LicenseRequest renewal_request; + EXPECT_TRUE(renewal_request.ParseFromString(signed_message.msg())); +} + +void MessageDumper::DumpRenewal(const std::string& response) { + SignedMessage signed_response; + EXPECT_TRUE(signed_response.ParseFromString(response)) + << "Response = " << wvutil::b2a_hex(response); + EXPECT_TRUE(signed_response.has_oemcrypto_core_message()); + DumpHex(&renewal_file, "core_response", + signed_response.oemcrypto_core_message()); + + video_widevine::License renewal; + EXPECT_TRUE(renewal.ParseFromString(signed_response.msg())); + DumpHex(&renewal_file, "renewal", signed_response.msg()); + + std::string message = + signed_response.oemcrypto_core_message() + signed_response.msg(); + ODK_Message odk_msg = ODK_Message_Create( + reinterpret_cast(const_cast(message.c_str())), + message.length()); + ODK_Message_SetSize(&odk_msg, + signed_response.oemcrypto_core_message().length()); + ODK_RenewalResponse odk_renewal_response = {}; + Unpack_ODK_RenewalResponse(&odk_msg, &odk_renewal_response); + EXPECT_EQ(ODK_Message_GetStatus(&odk_msg), MESSAGE_STATUS_OK); + renewal_file << " renewal_duration_seconds_ = " + << odk_renewal_response.renewal_duration_seconds << ";\n"; + renewal_file << " RunTest();\n"; + renewal_file << "}\n\n"; +} + +void MessageDumper::DumpProvisioningRequest( + const CdmProvisioningRequest& request) { + if (wvoec::global_features.derive_key_method == + wvoec::DeviceFeatures::TEST_PROVISION_40) { + LOGD("Provisioning 4.0 does not have a v17 or v18 core message."); + } else { + DumpHeader(&provision_file, "Provision"); + SignedProvisioningMessage signed_message; + EXPECT_TRUE(signed_message.ParseFromString(request)) + << "Request = " << wvutil::b2a_hex(request); + EXPECT_TRUE(signed_message.has_oemcrypto_core_message()); + DumpHex(&provision_file, "core_request", + signed_message.oemcrypto_core_message()); + } +} + +void MessageDumper::DumpProvisioning(const CdmProvisioningResponse& response) { + if (wvoec::global_features.derive_key_method == + wvoec::DeviceFeatures::TEST_PROVISION_40) { + LOGD("Provisioning 4.0 does not have a core message."); + } else { + SignedProvisioningMessage signed_response; + if (!signed_response.ParseFromString(response)) { + // A binary provisioning response is buried within a json structure. + std::string extracted_message; + EXPECT_TRUE(CertificateProvisioning::ExtractAndDecodeSignedMessage( + response, &extracted_message)); + EXPECT_TRUE(signed_response.ParseFromString(extracted_message)); + } + EXPECT_TRUE(signed_response.has_oemcrypto_core_message()); + DumpHex(&provision_file, "core_response", + signed_response.oemcrypto_core_message()); + DumpHex(&provision_file, "provisioning_response", + signed_response.message()); + // The choice of ECC or RSA key is decided at the server, based on + // information in the DCSL. We can only reproduce this by looking + // at the current response. + std::string message = + signed_response.oemcrypto_core_message() + signed_response.message(); + ODK_Message odk_msg = ODK_Message_Create( + reinterpret_cast(const_cast(message.c_str())), + message.length()); + ODK_Message_SetSize(&odk_msg, + signed_response.oemcrypto_core_message().length()); + ODK_ParsedProvisioning odk_parsed_response; + ODK_ProvisioningResponse provisioning_response; + provisioning_response.parsed_provisioning = &odk_parsed_response; + Unpack_ODK_ProvisioningResponse(&odk_msg, &provisioning_response); + EXPECT_EQ(ODK_Message_GetStatus(&odk_msg), MESSAGE_STATUS_OK); + provision_file << " device_key_type_ = " + << ((odk_parsed_response.key_type == + OEMCrypto_RSA_Private_Key) + ? "OEMCrypto_RSA_Private_Key;\n" + : "OEMCrypto_ECC_Private_Key;\n"); + provision_file << " RunTest();\n"; + provision_file << "}\n\n"; + } +} +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/message_dumper.h b/libwvdrmengine/cdm/core/test/message_dumper.h new file mode 100644 index 00000000..61843d2a --- /dev/null +++ b/libwvdrmengine/cdm/core/test/message_dumper.h @@ -0,0 +1,39 @@ +// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. + +#ifndef WVCDM_CORE_TEST_MESSAGE_DUMPER_H_ +#define WVCDM_CORE_TEST_MESSAGE_DUMPER_H_ + +#include +#include +#include + +#include + +#include "log.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +class MessageDumper : public ::testing::Environment { + // This dumps messages to a file so that the data can easily be turned + // into golden data tests for the ODK library. + public: + ~MessageDumper() override {} + void SetUp() override; + void TearDown() override; + + static void DumpLicenseRequest(const CdmKeyRequest& request); + static void DumpLicense(const std::string& response); + static void DumpRenewalRequest(const CdmKeyRequest& request); + static void DumpRenewal(const std::string& response); + static void DumpProvisioningRequest(const CdmProvisioningRequest& request); + static void DumpProvisioning(const CdmProvisioningResponse& response); + static std::ofstream license_file; + static std::ofstream renewal_file; + static std::ofstream provision_file; +}; +} // namespace wvcdm + +#endif // WVCDM_CORE_TEST_MESSAGE_DUMPER_H_ diff --git a/libwvdrmengine/cdm/core/test/policy_engine_constraints_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_constraints_unittest.cpp index 73b9285b..1158148f 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_constraints_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_constraints_unittest.cpp @@ -18,7 +18,6 @@ #include "wv_cdm_types.h" // protobuf generated classes. -using video_widevine::License; using video_widevine::License_Policy; using video_widevine::STREAMING; @@ -131,7 +130,6 @@ class PolicyEngineConstraintsTest : public WvCdmTestBase { id->set_type(STREAMING); License_Policy* policy = license_.mutable_policy(); - policy = license_.mutable_policy(); policy->set_can_play(true); policy->set_can_persist(false); policy->set_rental_duration_seconds(kRentalDuration); diff --git a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp index 5ec542cf..0cf9dae1 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -130,9 +130,7 @@ using video_widevine::STREAMING; // gmock methods using ::testing::_; -using ::testing::AtLeast; using ::testing::InSequence; -using ::testing::Invoke; using ::testing::MockFunction; using ::testing::Pair; using ::testing::Return; @@ -162,7 +160,6 @@ class PolicyEngineTest : public WvCdmTestBase { key->set_id(kKeyId); License_Policy* policy = license_.mutable_policy(); - policy = license_.mutable_policy(); policy->set_can_play(true); policy->set_can_persist(false); policy->set_can_renew(false); @@ -252,7 +249,7 @@ class PolicyEngineTestV16 : public PolicyEngineTest { protected: void SetUp() override { EXPECT_CALL(*crypto_session_, GetApiVersion(_)) - .WillOnce(DoAll(SetArgPointee<0>(kOemCryptoV16), Return(true))); + .WillRepeatedly(DoAll(SetArgPointee<0>(kOemCryptoV16), Return(true))); PolicyEngineTest::SetUp(); } }; @@ -1047,7 +1044,7 @@ TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageGeneric) { class PolicyEngineQueryTest : public PolicyEngineTestV16 { protected: void SetUp() override { - PolicyEngineTest::SetUp(); + PolicyEngineTestV16::SetUp(); policy_engine_.reset( new PolicyEngine(kSessionId, nullptr, crypto_session_.get())); InjectMockClock(); diff --git a/libwvdrmengine/cdm/core/test/policy_integration_test.cpp b/libwvdrmengine/cdm/core/test/policy_integration_test.cpp index f254e934..2f6f8d1c 100644 --- a/libwvdrmengine/cdm/core/test/policy_integration_test.cpp +++ b/libwvdrmengine/cdm/core/test/policy_integration_test.cpp @@ -114,101 +114,69 @@ TEST_F(CorePIGTest, OfflineHWSecureRequired) { ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); } -TEST_F(CorePIGTest, CastReceiverProvisioning) { - auto digest = wvutil::a2b_hex( // digest info header +TEST_F(CorePIGTest, CastReceiverProvisioningUsingCdm) { + const std::string digest_hex_str = + // digest info header "3021300906052b0e03021a05000414" // sha1 of kMessage - "d2662f893aaec72f3ca6decc2aa942f3949e8b21"); + "d2662f893aaec72f3ca6decc2aa942f3949e8b21"; + const auto digest = wvutil::a2b_hex(digest_hex_str); if (!wvoec::global_features.cast_receiver) { GTEST_SKIP() << "OEMCrypto does not support CAST Receiver functionality"; } // Provision x509 cert for CAST Receiver. - ProvisioningHolder provisioner(&cdm_engine_, config_.provisioning_server(), - config_.provisioning_service_certificate()); + ProvisioningHolder provisioner(&cdm_engine_, config_); provisioner.Provision(kCertificateX509, binary_provisioning_); - // cdm_engine_.OpenSession here is to load test keybox - // in order to successfully OEMCrypto_LoadDRMPrivateKey - std::string session_id; - CdmResponseType status = cdm_engine_.OpenSession( - config_.key_system(), nullptr, nullptr, &session_id); - ASSERT_EQ(NO_ERROR, status); - ASSERT_TRUE(cdm_engine_.IsOpenSession(session_id)); + // cdm_engine_.SignRsa + std::string signature_str; + const std::string digest_str(digest.begin(), digest.end()); + ASSERT_EQ(NO_ERROR, cdm_engine_.SignRsa(provisioner.wrapped_key(), digest_str, + &signature_str, kSign_PKCS1_Block1)); - std::string wrapped_key_str = provisioner.wrapped_key(); - std::vector wrapped_key(wrapped_key_str.begin(), - wrapped_key_str.end()); - OEMCrypto_SESSION oemcrypto_session; - OEMCryptoResult sts = - OEMCrypto_OpenSession(&oemcrypto_session, kLevelDefault); - if (sts != OEMCrypto_SUCCESS) { - LOGE("Fail in OEMCrypto_OpenSession"); - } - - // Stop test when OEMCrypto_LoadDRMPrivateKey fails. - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadDRMPrivateKey( - oemcrypto_session, OEMCrypto_RSA_Private_Key, - wrapped_key.data(), wrapped_key.size())); - - // Generate signature for the digest - size_t signatureSize = 0; - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, - OEMCrypto_GenerateRSASignature(oemcrypto_session, digest.data(), - digest.size(), nullptr, - &signatureSize, kSign_PKCS1_Block1)); - std::vector signature; - signature.resize(signatureSize); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_GenerateRSASignature(oemcrypto_session, digest.data(), - digest.size(), signature.data(), - &signatureSize, kSign_PKCS1_Block1)); + // Verify the generated signature + const std::vector signature(signature_str.begin(), signature_str.end()); LOGI("digest.size(): %zu, signature.size(): %zu", digest.size(), signature.size()); - // Verify the generated signature - std::string cert = provisioner.certificate(); - const char* cert_str_ptr = cert.c_str(); + const std::string cert = provisioner.certificate(); + const char* const cert_str_ptr = cert.c_str(); LOGI("cert: %s", cert_str_ptr); // Extract the public key from the x509 cert chain - BIO* bio = BIO_new(BIO_s_mem()); + std::unique_ptr bio(BIO_new(BIO_s_mem()), BIO_free_all); ASSERT_NE(bio, nullptr); - ASSERT_GT(BIO_puts(bio, cert_str_ptr), 0); - X509* x509 = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + ASSERT_GT(BIO_puts(bio.get(), cert_str_ptr), 0); + std::unique_ptr x509( + PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr), X509_free); ASSERT_NE(x509, nullptr); - EVP_PKEY* pubkey = X509_get_pubkey(x509); + std::unique_ptr pubkey( + X509_get_pubkey(x509.get()), EVP_PKEY_free); ASSERT_NE(pubkey, nullptr); // remove digest info header for verification // SHA1 is 20 bytes long - digest.erase(digest.begin(), digest.begin() + digest.size() - 20); + const std::vector sha1_digest(digest.begin() + digest.size() - 20, digest.end()); // Modified from openssl example // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_verify_init.html // Set RSA padding as RSA_PKCS1_PADDING and digest algo to SHA1. - EVP_PKEY_CTX* ctx; - unsigned char* md = digest.data(); - unsigned char* sig = signature.data(); - size_t mdlen = digest.size(); - size_t siglen = signature.size(); + const unsigned char* const md = sha1_digest.data(); + const unsigned char* const sig = signature.data(); + const size_t mdlen = sha1_digest.size(); + const size_t siglen = signature.size(); + + std::unique_ptr ctx( + EVP_PKEY_CTX_new(pubkey.get(), nullptr /* no engine */), EVP_PKEY_CTX_free); - ctx = EVP_PKEY_CTX_new(pubkey, nullptr /* no engine */); ASSERT_NE(ctx, nullptr); - ASSERT_GT(EVP_PKEY_verify_init(ctx), 0); - ASSERT_GT(EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING), 0); - ASSERT_GT(EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha1()), 0); + ASSERT_GT(EVP_PKEY_verify_init(ctx.get()), 0); + ASSERT_GT(EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_PADDING), 0); + ASSERT_GT(EVP_PKEY_CTX_set_signature_md(ctx.get(), EVP_sha1()), 0); /* Perform operation */ - EXPECT_EQ(1, EVP_PKEY_verify(ctx, sig, siglen, md, mdlen)); - - EVP_PKEY_CTX_free(ctx); - EVP_PKEY_free(pubkey); - BIO_free(bio); - X509_free(x509); - - OEMCrypto_CloseSession(oemcrypto_session); - cdm_engine_.CloseSession(session_id); + EXPECT_EQ(1, EVP_PKEY_verify(ctx.get(), sig, siglen, md, mdlen)); } } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/provisioning_holder.cpp b/libwvdrmengine/cdm/core/test/provisioning_holder.cpp index 8cd7fa81..954b2eff 100644 --- a/libwvdrmengine/cdm/core/test/provisioning_holder.cpp +++ b/libwvdrmengine/cdm/core/test/provisioning_holder.cpp @@ -10,6 +10,7 @@ #include "config_test_env.h" #include "log.h" +#include "message_dumper.h" #include "oec_device_features.h" #include "test_printers.h" #include "test_sleep.h" @@ -47,6 +48,12 @@ void ProvisioningHolder::Provision(CdmCertificateType cert_type, if (binary_provisioning) { request = wvutil::Base64SafeEncodeNoPad(request); } + if (config_.dump_golden_data()) { + std::vector binary_request = wvutil::Base64SafeDecode(request); + CdmProvisioningRequest binary_request_string(binary_request.begin(), + binary_request.end()); + MessageDumper::DumpProvisioningRequest(binary_request_string); + } LOGV("Provisioning request: req = %s", request.c_str()); // Ignore URL provided by CdmEngine. Use ours, as configured @@ -95,6 +102,9 @@ void ProvisioningHolder::Provision(CdmCertificateType cert_type, << (binary_provisioning ? "Binary provisioning failed. " : "Non-binary provisioning failed. ") << DumpProvAttempt(request, response_, cert_type); + if (config_.dump_golden_data()) { + MessageDumper::DumpProvisioning(response_); + } } bool ProvisioningHolder::ExtractSignedMessage(const std::string& response, diff --git a/libwvdrmengine/cdm/core/test/provisioning_holder.h b/libwvdrmengine/cdm/core/test/provisioning_holder.h index fe5acd2d..1b68c9cc 100644 --- a/libwvdrmengine/cdm/core/test/provisioning_holder.h +++ b/libwvdrmengine/cdm/core/test/provisioning_holder.h @@ -13,12 +13,12 @@ namespace wvcdm { class ProvisioningHolder { public: - ProvisioningHolder(TestCdmEngine* cdm_engine, - const std::string& provisioning_server_url, - const std::string& provisioning_service_certificate) + ProvisioningHolder(TestCdmEngine* cdm_engine, const ConfigTestEnv& config) : cdm_engine_(cdm_engine), - provisioning_server_url_(provisioning_server_url), - provisioning_service_certificate_(provisioning_service_certificate) {} + config_(config), + provisioning_server_url_(config.provisioning_server()), + provisioning_service_certificate_( + config.provisioning_service_certificate()) {} void Provision(CdmCertificateType cert_type, bool binary_provisioning); void Provision(bool binary_provisioning) { Provision(kCertificateWidevine, binary_provisioning); @@ -29,6 +29,7 @@ class ProvisioningHolder { protected: TestCdmEngine* cdm_engine_; + const ConfigTestEnv& config_; std::string provisioning_server_url_; std::string provisioning_service_certificate_; std::string response_; diff --git a/libwvdrmengine/cdm/core/test/test_base.cpp b/libwvdrmengine/cdm/core/test/test_base.cpp index 1c478342..59ab761e 100644 --- a/libwvdrmengine/cdm/core/test/test_base.cpp +++ b/libwvdrmengine/cdm/core/test/test_base.cpp @@ -22,6 +22,7 @@ #include "file_store.h" #include "license.h" #include "log.h" +#include "message_dumper.h" #include "oec_device_features.h" #include "oec_test_data.h" #include "platform.h" @@ -142,6 +143,10 @@ void show_menu(const char* prog_name, const std::string& extra_help_text) { std::cout << " --test_data_path=" << std::endl; std::cout << " Where to store test data for reboot tests." << std::endl; + std::cout << " --dump_golden_data" << std::endl; + std::cout << " Dump the license request and response from the server." + << std::endl; + std::cout << extra_help_text << std::endl; } } // namespace @@ -159,24 +164,22 @@ void WvCdmTestBase::StripeBuffer(std::vector* buffer, size_t size, std::string WvCdmTestBase::Aes128CbcEncrypt(std::vector key, const std::vector& clear, - const std::vector iv) { + std::vector iv) { std::vector encrypted(clear.size()); - std::vector iv_mod(iv.begin(), iv.end()); AES_KEY aes_key; AES_set_encrypt_key(&key[0], 128, &aes_key); - AES_cbc_encrypt(&clear[0], &encrypted[0], clear.size(), &aes_key, &iv_mod[0], + AES_cbc_encrypt(&clear[0], &encrypted[0], clear.size(), &aes_key, &iv[0], AES_ENCRYPT); return std::string(encrypted.begin(), encrypted.end()); } std::string WvCdmTestBase::Aes128CbcDecrypt(std::vector key, const std::vector& clear, - const std::vector iv) { + std::vector iv) { std::vector encrypted(clear.size()); - std::vector iv_mod(iv.begin(), iv.end()); AES_KEY aes_key; AES_set_decrypt_key(&key[0], 128, &aes_key); - AES_cbc_encrypt(&clear[0], &encrypted[0], clear.size(), &aes_key, &iv_mod[0], + AES_cbc_encrypt(&clear[0], &encrypted[0], clear.size(), &aes_key, &iv[0], AES_DECRYPT); return std::string(encrypted.begin(), encrypted.end()); } @@ -277,7 +280,7 @@ void WvCdmTestBase::SetUp() { CryptoSession::SetCryptoSessionFactory(factory); const char* const disable_test_keybox_flag = getenv("DISABLE_TEST_KEYBOX"); if (disable_test_keybox_flag != nullptr && - !strcmp(disable_test_keybox_flag, "yes")) { + strcmp(disable_test_keybox_flag, "yes") == 0) { factory->SetDisableTestKeybox(true); } // TODO(fredgc): Add a test version of DeviceFiles. @@ -320,9 +323,8 @@ void WvCdmTestBase::Provision() { const CdmCertificateType cert_type = kCertificateWidevine; std::unique_ptr file_system(CreateTestFileSystem()); TestCdmEngine cdm_engine(file_system.get(), - std::shared_ptr(new EngineMetrics)); - ProvisioningHolder provisioner(&cdm_engine, config_.provisioning_server(), - config_.provisioning_service_certificate()); + std::make_shared()); + ProvisioningHolder provisioner(&cdm_engine, config_); provisioner.Provision(cert_type, binary_provisioning_); } @@ -333,7 +335,7 @@ void WvCdmTestBase::EnsureProvisioned() { // GenerateKeyRequest will actually load the wrapped private key. // Either may return a NEED_PROVISIONING error, so both have to be checked. TestCdmEngine cdm_engine(file_system.get(), - std::shared_ptr(new EngineMetrics)); + std::make_shared()); CdmResponseType status = cdm_engine.OpenSession(config_.key_system(), nullptr, nullptr, &session_id); CdmAppParameterMap app_parameters; @@ -393,6 +395,9 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[], } else if (arg.find("--gtest") == 0) { // gtest arguments will be passed to gtest by the main program. continue; + } else if (arg == "--dump_golden_data") { + default_config_->set_dump_golden_data(true); + testing::AddGlobalTestEnvironment(new MessageDumper); } else { const auto index = arg.find('='); if (index == std::string::npos) { diff --git a/libwvdrmengine/cdm/core/test/test_base.h b/libwvdrmengine/cdm/core/test/test_base.h index c8d48776..8c41dd64 100644 --- a/libwvdrmengine/cdm/core/test/test_base.h +++ b/libwvdrmengine/cdm/core/test/test_base.h @@ -56,11 +56,11 @@ class WvCdmTestBase : public ::testing::Test { // Helper method for doing cryptography. static std::string Aes128CbcEncrypt(std::vector key, const std::vector& clear, - const std::vector iv); + std::vector iv); // Helper method for doing cryptography. static std::string Aes128CbcDecrypt(std::vector key, const std::vector& clear, - const std::vector iv); + std::vector iv); // Helper method for doing cryptography. static std::string SignHMAC(const std::string& message, const std::vector& key); diff --git a/libwvdrmengine/cdm/core/test/url_request.cpp b/libwvdrmengine/cdm/core/test/url_request.cpp index dc81da8d..302816b6 100644 --- a/libwvdrmengine/cdm/core/test/url_request.cpp +++ b/libwvdrmengine/cdm/core/test/url_request.cpp @@ -171,7 +171,7 @@ bool UrlRequest::GetDebugHeaderFields( // cases of parsing provisioning/license/renewal responses. for (size_t key_pos = find_next(0); key_pos != std::string::npos; key_pos = find_next(key_pos)) { - const size_t end_key_pos = response.find(":", key_pos); + const size_t end_key_pos = response.find(':', key_pos); const size_t end_value_pos = response.find(kCrLf, key_pos); // Skip if the colon cannot be found. Technically possible to find // "X-Google" inside the value of a nother header field. diff --git a/libwvdrmengine/cdm/metrics/include/metrics_collections.h b/libwvdrmengine/cdm/metrics/include/metrics_collections.h index 5cdbf3e8..655d9b13 100644 --- a/libwvdrmengine/cdm/metrics/include/metrics_collections.h +++ b/libwvdrmengine/cdm/metrics/include/metrics_collections.h @@ -61,7 +61,6 @@ namespace wvcdm { namespace metrics { -namespace { // Short name definitions to ease AttributeHandler definitions. // Internal namespace to help simplify declarations. const int kErrorCodeFieldNumber = @@ -91,7 +90,6 @@ const int kLicenseTypeFieldNumber = ::drm_metrics::Attributes::kLicenseTypeFieldNumber; const int kOemCryptoSignatureHashAlgorithmFieldNumber = ::drm_metrics::Attributes::kOemCryptoSignatureHashAlgorithmFieldNumber; -} // namespace // The maximum number of completed sessions that can be stored. More than this // will cause some metrics to be discarded. diff --git a/libwvdrmengine/cdm/metrics/include/value_metric.h b/libwvdrmengine/cdm/metrics/include/value_metric.h index 315bbe4f..d7113575 100644 --- a/libwvdrmengine/cdm/metrics/include/value_metric.h +++ b/libwvdrmengine/cdm/metrics/include/value_metric.h @@ -39,8 +39,6 @@ void SetValue(drm_metrics::ValueMetric* value_proto, const T& value); template class ValueMetric { public: - ValueMetric() {} - // Record the value of the metric. void Record(const T& value) { std::unique_lock lock(mutex_); @@ -103,7 +101,7 @@ class ValueMetric { private: enum ValueState { kNone, kHasValue, kHasError }; - T value_; + T value_ = T(); int error_code_ = 0; ValueState state_ = kNone; diff --git a/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp b/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp index 91c4ed84..ee054358 100644 --- a/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp +++ b/libwvdrmengine/cdm/metrics/src/metrics_collections.cpp @@ -10,10 +10,7 @@ namespace wvcdm { namespace metrics { -using ::drm_metrics::Attributes; using ::drm_metrics::WvCdmMetrics; -using ::google::protobuf::RepeatedPtrField; -using ::wvcdm::metrics::EventMetric; namespace { // Helper struct for comparing session ids. struct CompareSessionIds { @@ -217,7 +214,7 @@ void CryptoMetrics::Serialize( crypto_metrics->mutable_oemcrypto_get_signature_hash_algorithm()); } -SessionMetrics::SessionMetrics() : session_id_(""), completed_(false) {} +SessionMetrics::SessionMetrics() : session_id_(), completed_(false) {} void SessionMetrics::Serialize( WvCdmMetrics::SessionMetrics* session_metrics) const { diff --git a/libwvdrmengine/cdm/test/integration-test.mk b/libwvdrmengine/cdm/test/integration-test.mk index 16acbd0c..00f5140a 100644 --- a/libwvdrmengine/cdm/test/integration-test.mk +++ b/libwvdrmengine/cdm/test/integration-test.mk @@ -20,6 +20,7 @@ LOCAL_SRC_FILES := \ ../core/test/http_socket.cpp \ ../core/test/license_holder.cpp \ ../core/test/license_request.cpp \ + ../core/test/message_dumper.cpp \ ../core/test/provisioning_holder.cpp \ ../core/test/test_base.cpp \ ../core/test/test_printers.cpp \ diff --git a/libwvdrmengine/cdm/util/test/test_sleep.cpp b/libwvdrmengine/cdm/util/test/test_sleep.cpp index 281e0429..439a46d5 100644 --- a/libwvdrmengine/cdm/util/test/test_sleep.cpp +++ b/libwvdrmengine/cdm/util/test/test_sleep.cpp @@ -14,6 +14,7 @@ #endif #include +#include #include #include @@ -53,6 +54,18 @@ void TestSleep::Sleep(unsigned int seconds) { if (callback_ != nullptr) callback_->ElapseTime(milliseconds); } +void TestSleep::SleepUntil(int64_t desired_time) { + SyncFakeClock(); + const int64_t now = Clock().GetCurrentTime(); + if (desired_time < now) { + LOGE("Test Clock skew sleeping from time %" PRId64 " to %" PRId64, now, + desired_time); + return; + } + const unsigned int sleep_time = static_cast(desired_time - now); + TestSleep::Sleep(sleep_time); +} + void TestSleep::SyncFakeClock() { // Syncing can be done by sleeping 0 seconds. Sleep(0); diff --git a/libwvdrmengine/cdm/util/test/test_sleep.h b/libwvdrmengine/cdm/util/test/test_sleep.h index 155b89dc..f20e27a7 100644 --- a/libwvdrmengine/cdm/util/test/test_sleep.h +++ b/libwvdrmengine/cdm/util/test/test_sleep.h @@ -13,7 +13,9 @@ namespace wvutil { class TestSleep { public: - // The callback is called when the clock should be advanced. + // The callback is called when the test clock should be advanced. If the + // system uses a real clock, it is used to sync the real and test + // clock. Otherwise it is used to simulate sleep in the test clock. class CallBack { public: virtual void ElapseTime(int64_t milliseconds) = 0; @@ -27,6 +29,9 @@ class TestSleep { // callback exists, this calls the callback. static void Sleep(unsigned int seconds); + // Like sleep, above, except it sleeps until the specified time. + static void SleepUntil(int64_t desired_time); + // If we are using a real clock and a fake clock, then the real clock advances // a little while we are doing work, but the fake one only advances when we // sleep. This function advances the fake clock to be in sync with the real diff --git a/libwvdrmengine/include/mapErrors-inl.h b/libwvdrmengine/include/mapErrors-inl.h index 62b5cc4d..c61d1e0b 100644 --- a/libwvdrmengine/include/mapErrors-inl.h +++ b/libwvdrmengine/include/mapErrors-inl.h @@ -253,6 +253,7 @@ static inline WvStatus mapCdmResponseType(wvcdm::CdmResponseType res) { case wvcdm::SESSION_NOT_FOUND_14: case wvcdm::SESSION_NOT_FOUND_15: case wvcdm::SESSION_NOT_FOUND_16: + case wvcdm::SESSION_NOT_FOUND_24: case wvcdm::SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE: case wvcdm::STORAGE_PROHIBITED: case wvcdm::STORE_LICENSE_ERROR_2: diff --git a/libwvdrmengine/oemcrypto/test/Android.mk b/libwvdrmengine/oemcrypto/test/Android.mk index 73b6a3dc..98e9b4d3 100644 --- a/libwvdrmengine/oemcrypto/test/Android.mk +++ b/libwvdrmengine/oemcrypto/test/Android.mk @@ -6,8 +6,8 @@ LOCAL_C_INCLUDES := \ vendor/widevine/libwvdrmengine/cdm/util/include \ LOCAL_MODULE:=oemcrypto_test -LOCAL_LICENSE_KINDS:=legacy_by_exception_only -LOCAL_LICENSE_CONDITIONS:=by_exception_only +LOCAL_LICENSE_KINDS:=legacy_by_exception_only legacy_proprietary +LOCAL_LICENSE_CONDITIONS:=by_exception_only proprietary by_exception_only LOCAL_MODULE_TAGS := tests LOCAL_MODULE_OWNER := widevine