diff --git a/libwvdrmengine/build_and_run_all_unit_tests.sh b/libwvdrmengine/build_and_run_all_unit_tests.sh index 0ee23c09..ec278f10 100755 --- a/libwvdrmengine/build_and_run_all_unit_tests.sh +++ b/libwvdrmengine/build_and_run_all_unit_tests.sh @@ -49,6 +49,7 @@ adb root && adb wait-for-device remount adb push $OUT/system/bin/oemcrypto_test /system/bin adb push $OUT/system/bin/request_license_test /system/bin +adb push $OUT/system/bin/cdm_extended_duration_test /system/bin adb push $OUT/system/bin/policy_engine_unittest /system/bin adb push $OUT/system/bin/libwvdrmmediacrypto_test /system/bin adb push $OUT/system/bin/libwvdrmdrmplugin_test /system/bin diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 6d084716..91db0f50 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -17,6 +17,7 @@ namespace wvcdm { class CdmClientPropertySet; class CryptoEngine; +class UsagePropertySet; class WvCdmEventListener; typedef std::map CdmSessionMap; @@ -117,6 +118,8 @@ class CdmEngine { private: // private methods bool ValidateKeySystem(const CdmKeySystem& key_system); + CdmResponseType GetUsageInfo(SecurityLevel requested_security_level, + CdmUsageInfo* usage_info); void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); @@ -132,7 +135,8 @@ class CdmEngine { // usage related variables scoped_ptr usage_session_; - int64_t last_usage_information_update_time; + scoped_ptr usage_property_set_; + int64_t last_usage_information_update_time_; CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine); }; diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index 9aee1060..2fb203f1 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -88,8 +88,10 @@ class CdmSession { virtual CdmResponseType UpdateUsageInformation(); + virtual bool is_initial_usage_update() { return is_initial_usage_update_; } virtual bool is_usage_update_needed() { return is_usage_update_needed_; } - virtual void reset_is_usage_update_needed() { + virtual void reset_usage_flags() { + is_initial_usage_update_ = false; is_usage_update_needed_ = false; } @@ -117,10 +119,13 @@ class CdmSession { bool license_received_; bool is_offline_; bool is_release_; - bool is_usage_update_needed_; + CdmSecurityLevel security_level_; + + // decryption and usage flags bool is_initial_decryption_; bool has_decrypted_recently_; - CdmSecurityLevel security_level_; + bool is_initial_usage_update_; + bool is_usage_update_needed_; // information useful for offline and usage scenarios CdmKeyMessage key_request_; diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index e2f674d1..b67890fa 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -84,6 +84,7 @@ class CryptoSession { // Media data path virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); + // Usage related methods virtual bool UsageInformationSupport(bool* has_support); virtual CdmResponseType UpdateUsageInformation(); virtual CdmResponseType DeactivateUsageInformation( @@ -95,6 +96,7 @@ class CryptoSession { virtual CdmResponseType ReleaseUsageInformation( const std::string& message, const std::string& signature, const std::string& provider_session_token); + virtual CdmResponseType DeleteAllUsageReports(); virtual bool GetHdcpCapabilities(OemCryptoHdcpVersion* current, OemCryptoHdcpVersion* max); diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index 898cdbd6..8e53fee5 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -97,7 +97,7 @@ class DeviceFiles { FRIEND_TEST(DeviceFilesUsageInfoTest, Store); FRIEND_TEST(WvCdmRequestLicenseTest, UnprovisionTest); FRIEND_TEST(WvCdmRequestLicenseTest, ForceL3Test); - FRIEND_TEST(WvCdmUsageInfoTest, DISABLED_UsageInfo); + FRIEND_TEST(WvCdmUsageInfoTest, UsageInfo); #endif scoped_ptr file_; diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index 01013a64..ac4c502b 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -47,8 +47,8 @@ class CdmLicense { const CdmKeyResponse& license_renewal_response, int64_t playback_start_time, int64_t last_playback_time); - virtual bool RestoreUsageLicense(const CdmKeyMessage& license_request, - const CdmKeyResponse& license_response); + virtual bool RestoreLicenseForRelease(const CdmKeyMessage& license_request, + const CdmKeyResponse& license_response); virtual bool HasInitData() { return !stored_init_data_.empty(); } virtual bool IsKeyLoaded(const KeyId& key_id); diff --git a/libwvdrmengine/cdm/core/include/string_conversions.h b/libwvdrmengine/cdm/core/include/string_conversions.h index 3d3ba38f..d0c7ea2f 100644 --- a/libwvdrmengine/cdm/core/include/string_conversions.h +++ b/libwvdrmengine/cdm/core/include/string_conversions.h @@ -22,6 +22,7 @@ std::string HexEncode(const uint8_t* bytes, unsigned size); std::string IntToString(int value); std::string UintToString(unsigned int value); int64_t htonll64(int64_t x); +int64_t ntohll64(int64_t x); }; // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index bde19b07..94051bb7 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -25,13 +25,36 @@ namespace { namespace wvcdm { +class UsagePropertySet : public CdmClientPropertySet { + public: + UsagePropertySet() {} + virtual ~UsagePropertySet() {} + void set_security_level(SecurityLevel security_level) { + if (kLevel3 == security_level) + security_level_ = QUERY_VALUE_SECURITY_LEVEL_L3; + else + security_level_.clear(); + } + virtual const std::string& security_level() const { return security_level_; } + virtual bool use_privacy_mode() const { return false; } + virtual const std::string& service_certificate() const { return empty_; } + virtual bool is_session_sharing_enabled() const { return false; } + virtual uint32_t session_sharing_id() const { return 0; } + virtual void set_session_sharing_id(uint32_t id) { + id; // noop to suppress warning + } + private: + std::string security_level_; + const std::string empty_; +}; + bool CdmEngine::seeded_ = false; CdmEngine::CdmEngine() : cert_provisioning_(NULL), cert_provisioning_requested_security_level_(kLevelDefault), usage_session_(NULL), - last_usage_information_update_time(0) { + last_usage_information_update_time_(0) { Properties::Init(); if (!seeded_) { Clock clock; @@ -269,7 +292,7 @@ CdmResponseType CdmEngine::RestoreKey( LOGI("CdmEngine::RestoreKey"); if (key_set_id.empty()) { - LOGI("CdmEngine::RestoreKey: invalid key set id"); + LOGE("CdmEngine::RestoreKey: invalid key set id"); return KEY_ERROR; } @@ -286,6 +309,9 @@ CdmResponseType CdmEngine::RestoreKey( cert_provisioning_requested_security_level_ = iter->second->GetRequestedSecurityLevel(); } + if (KEY_ADDED != sts) { + LOGE("CdmEngine::RestoreKey: restore offline session error = %d", sts); + } return sts; } @@ -539,11 +565,46 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) { LOGE("CdmEngine::Unprovision: unable to delete files"); return UNKNOWN_ERROR; } - return NO_ERROR; + + scoped_ptr crypto_session(new CryptoSession()); + CdmResponseType status = crypto_session->Open( + security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault); + if (NO_ERROR != status) { + LOGE("CdmEngine::Unprovision: error opening crypto session: %d", status); + return UNKNOWN_ERROR; + } + status = crypto_session->DeleteAllUsageReports(); + if (status != NO_ERROR) { + LOGE("CdmEngine::Unprovision: error deleteing usage reports: %d", status); + } + return status; } CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) { - usage_session_.reset(new CdmSession(NULL)); + // Return a random usage report from a random security level + SecurityLevel security_level = ((rand() % 2) == 0) ? kLevelDefault : kLevel3; + CdmResponseType status = GetUsageInfo(security_level, usage_info); + + if (KEY_MESSAGE == status && !usage_info->empty()) + return status; + + security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3; + status = GetUsageInfo(security_level, usage_info); + if (NEED_PROVISIONING == status) + return NO_ERROR; // Valid scenario that one of the security + // levels has not been provisioned + return status; +} + +CdmResponseType CdmEngine::GetUsageInfo( + SecurityLevel requested_security_level, + CdmUsageInfo* usage_info) { + if (NULL == usage_property_set_.get()) { + usage_property_set_.reset(new UsagePropertySet()); + } + usage_property_set_->set_security_level(requested_security_level); + + usage_session_.reset(new CdmSession(usage_property_set_.get())); CdmResponseType status = usage_session_->Init(); if (NO_ERROR != status) { @@ -573,7 +634,7 @@ CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) { uint32_t index = rand() % license_info.size(); status = usage_session_->RestoreUsageSession(license_info[index].first, - license_info[index].second); + license_info[index].second); if (KEY_ADDED != status) { LOGE("CdmEngine::GetUsageInfo: restore usage session (%u) error %d", index, status); @@ -600,6 +661,7 @@ CdmResponseType CdmEngine::ReleaseUsageInfo( } CdmResponseType status = usage_session_->ReleaseKey(message); + usage_session_.reset(NULL); if (NO_ERROR != status) { LOGE("CdmEngine::ReleaseUsageInfo: release key error: %d", status); } @@ -724,29 +786,44 @@ bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) { void CdmEngine::OnTimerEvent() { Clock clock; uint64_t current_time = clock.GetCurrentTime(); - bool update_usage_information = false; + bool usage_update_period_expired = false; - if (current_time - last_usage_information_update_time > + if (current_time - last_usage_information_update_time_ > kUpdateUsageInformationPeriod) { - update_usage_information = true; - last_usage_information_update_time = current_time; + usage_update_period_expired = true; + last_usage_information_update_time_ = current_time; } + bool is_initial_usage_update = false; + bool is_usage_update_needed = false; + for (CdmSessionMap::iterator iter = sessions_.begin(); iter != sessions_.end(); ++iter) { - iter->second->OnTimerEvent(update_usage_information); + is_initial_usage_update = is_initial_usage_update || + iter->second->is_initial_usage_update(); + is_usage_update_needed = is_usage_update_needed || + iter->second->is_usage_update_needed(); - if (update_usage_information && iter->second->is_usage_update_needed()) { - // usage is updated for all sessions so this needs to be - // called only once per update usage information period - CdmResponseType status = iter->second->UpdateUsageInformation(); - if (NO_ERROR != status) { - LOGW("Update usage information failed: %d", status); - } else { - update_usage_information = false; + iter->second->OnTimerEvent(usage_update_period_expired); + } + + if (is_usage_update_needed && + (usage_update_period_expired || is_initial_usage_update)) { + bool has_usage_been_updated = false; + for (CdmSessionMap::iterator iter = sessions_.begin(); + iter != sessions_.end(); ++iter) { + iter->second->reset_usage_flags(); + if (!has_usage_been_updated) { + // usage is updated for all sessions so this needs to be + // called only once per update usage information period + CdmResponseType status = iter->second->UpdateUsageInformation(); + if (NO_ERROR != status) { + LOGW("Update usage information failed: %d", status); + } else { + has_usage_been_updated = true; + } } } - iter->second->reset_is_usage_update_needed(); } } diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index c10adedf..95c432cf 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -72,6 +72,7 @@ void CdmSession::Create( license_received_ = false; is_offline_ = false; is_release_ = false; + is_initial_usage_update_ = true; is_usage_update_needed_ = false; is_initial_decryption_ = true; has_decrypted_recently_ = false; @@ -152,11 +153,18 @@ CdmResponseType CdmSession::RestoreOfflineSession( return UNKNOWN_ERROR; } - if (!license_parser_->RestoreOfflineLicense(key_request_, key_response_, - offline_key_renewal_response_, - playback_start_time, - last_playback_time)) { - return UNKNOWN_ERROR; + if (license_type == kLicenseTypeRelease) { + if (!license_parser_->RestoreLicenseForRelease(key_request_, + key_response_)) { + return UNKNOWN_ERROR; + } + } else { + if (!license_parser_->RestoreOfflineLicense(key_request_, key_response_, + offline_key_renewal_response_, + playback_start_time, + last_playback_time)) { + return UNKNOWN_ERROR; + } } license_received_ = true; @@ -172,7 +180,7 @@ CdmResponseType CdmSession::RestoreUsageSession( key_request_ = key_request; key_response_ = key_response; - if (!license_parser_->RestoreUsageLicense(key_request_, key_response_)) { + if (!license_parser_->RestoreLicenseForRelease(key_request_, key_response_)) { return UNKNOWN_ERROR; } diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index b6012414..3514985b 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -421,10 +421,19 @@ CdmResponseType CryptoSession::LoadKeys( provider_session_token.length()); if (OEMCrypto_SUCCESS == sts) { + if (!provider_session_token.empty()) { + sts = OEMCrypto_UpdateUsageTable(); + if (sts != OEMCrypto_SUCCESS) { + LOGW("CryptoSession::LoadKeys: OEMCrypto_UpdateUsageTable error=%ld", + sts); + } + } return KEY_ADDED; } else if (OEMCrypto_ERROR_TOO_MANY_KEYS == sts) { + LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts); return INSUFFICIENT_CRYPTO_RESOURCES; } else { + LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts); return KEY_ERROR; } } @@ -439,7 +448,7 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) { wrapped_key.size()); if (OEMCrypto_SUCCESS != sts) { - LOGD("LoadCertificatePrivateKey: OEMCrypto_LoadDeviceRSAKey error=%d", sts); + LOGE("LoadCertificatePrivateKey: OEMCrypto_LoadDeviceRSAKey error=%d", sts); return false; } @@ -509,7 +518,7 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message) { enc_deriv_message.size()); if (OEMCrypto_SUCCESS != sts) { - LOGD("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts); + LOGE("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts); return false; } @@ -533,7 +542,7 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message, enc_deriv_message.size()); if (OEMCrypto_SUCCESS != sts) { - LOGD("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts); + LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts); return false; } @@ -554,7 +563,7 @@ bool CryptoSession::GenerateSignature(const std::string& message, if (OEMCrypto_SUCCESS != sts) { if (OEMCrypto_ERROR_SHORT_BUFFER != sts) { - LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts); + LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts); return false; } @@ -567,7 +576,7 @@ bool CryptoSession::GenerateSignature(const std::string& message, &length); if (OEMCrypto_SUCCESS != sts) { - LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts); + LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts); return false; } } @@ -593,7 +602,7 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message, if (OEMCrypto_SUCCESS != sts) { if (OEMCrypto_ERROR_SHORT_BUFFER != sts) { - LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts); + LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts); return false; } @@ -606,7 +615,7 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message, &length, kSign_RSASSA_PSS); if (OEMCrypto_SUCCESS != sts) { - LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts); + LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts); return false; } } @@ -774,10 +783,32 @@ CdmResponseType CryptoSession::GenerateUsageReport( *usage_duration_status = kUsageDurationPlaybackNotBegun; return NO_ERROR; } + LOGV("OEMCrypto_PST_Report.status: %d\n", pst_report.status); + LOGV("OEMCrypto_PST_Report.clock_security_level: %d\n", + pst_report.clock_security_level); + LOGV("OEMCrypto_PST_Report.pst_length: %d\n", pst_report.pst_length); + LOGV("OEMCrypto_PST_Report.padding: %d\n", pst_report.padding); + LOGV("OEMCrypto_PST_Report.seconds_since_license_received: %lld\n", + ntohll64(pst_report.seconds_since_license_received)); + LOGV("OEMCrypto_PST_Report.seconds_since_first_decrypt: %lld\n", + ntohll64(pst_report.seconds_since_first_decrypt)); + LOGV("OEMCrypto_PST_Report.seconds_since_last_decrypt: %lld\n", + ntohll64(pst_report.seconds_since_last_decrypt)); + LOGV("OEMCrypto_PST_Report: %s\n", b2a_hex(*usage_report).c_str()); + + // When usage report state is inactive, we have to deduce whether the + // license was ever used. + if (kInactive == pst_report.status && + (0 > ntohll64(pst_report.seconds_since_first_decrypt) || + ntohll64(pst_report.seconds_since_license_received) < + ntohll64(pst_report.seconds_since_first_decrypt))) { + *usage_duration_status = kUsageDurationPlaybackNotBegun; + return NO_ERROR; + } *usage_duration_status = kUsageDurationsValid; - *seconds_since_started = pst_report.seconds_since_first_decrypt; - *seconds_since_last_played = pst_report.seconds_since_last_decrypt; + *seconds_since_started = ntohll64(pst_report.seconds_since_first_decrypt); + *seconds_since_last_played = ntohll64(pst_report.seconds_since_last_decrypt); return NO_ERROR; } @@ -799,6 +830,31 @@ CdmResponseType CryptoSession::ReleaseUsageInformation( status); return UNKNOWN_ERROR; } + status = OEMCrypto_UpdateUsageTable(); + if (status != OEMCrypto_SUCCESS) { + LOGW("CryptoSession::ReleaseUsageInformation: OEMCrypto_UpdateUsageTable: " + "error=%ld", + status); + } + + return NO_ERROR; +} + +CdmResponseType CryptoSession::DeleteAllUsageReports() { + LOGV("DeleteAllUsageReports"); + OEMCryptoResult status = OEMCrypto_DeleteUsageTable(); + + if (OEMCrypto_SUCCESS != status) { + LOGE("CryptoSession::DeleteAllUsageReports: Delete Usage Table error =%ld", + status); + } + + status = OEMCrypto_UpdateUsageTable(); + if (status != OEMCrypto_SUCCESS) { + LOGE("CryptoSession::ReleaseUsageInformation: OEMCrypto_UpdateUsageTable: " + "error=%ld", + status); + } return NO_ERROR; } @@ -840,7 +896,7 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message, const std::string& enc_rsa_key, const std::string& rsa_key_iv, std::string* wrapped_rsa_key) { - LOGD("CryptoSession::RewrapDeviceRSAKey, session id=%ld", + LOGV("CryptoSession::RewrapDeviceRSAKey, session id=%ld", static_cast(oec_session_id_)); const uint8_t* signed_msg = reinterpret_cast(message.data()); diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index f73df395..655b8bb4 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -755,7 +755,6 @@ bool CdmLicense::RestoreOfflineLicense( const CdmKeyResponse& license_renewal_response, int64_t playback_start_time, int64_t last_playback_time) { - if (license_request.empty() || license_response.empty()) { LOGE( "CdmLicense::RestoreOfflineLicense: key_request or response empty: " @@ -827,26 +826,28 @@ bool CdmLicense::RestoreOfflineLicense( return true; } -bool CdmLicense::RestoreUsageLicense(const CdmKeyMessage& license_request, - const CdmKeyResponse& license_response) { +bool CdmLicense::RestoreLicenseForRelease( + const CdmKeyMessage& license_request, + const CdmKeyResponse& license_response) { if (license_request.empty() || license_response.empty()) { LOGE( - "CdmLicense::RestoreUsageLicense: key_request or response empty: %u %u", + "CdmLicense::RestoreLicenseForRelease: key_request or response empty:" + " %u %u", license_request.size(), license_response.size()); return false; } SignedMessage signed_request; if (!signed_request.ParseFromString(license_request)) { - LOGE("CdmLicense::RestoreUsageLicense: license_request parse failed"); + LOGE("CdmLicense::RestoreLicenseForRelease: license_request parse failed"); return false; } if (signed_request.type() != SignedMessage::LICENSE_REQUEST) { LOGE( - "CdmLicense::RestoreUsageLicense: license request type: expected = %d," - " actual = %d", + "CdmLicense::RestoreLicenseForRelease: license request type: expected " + "= %d, actual = %d", SignedMessage::LICENSE_REQUEST, signed_request.type()); return false; } @@ -855,20 +856,21 @@ bool CdmLicense::RestoreUsageLicense(const CdmKeyMessage& license_request, SignedMessage signed_response; if (!signed_response.ParseFromString(license_response)) { - LOGE("CdmLicense::RestoreUsageLicense: unable to parse signed license" + LOGE("CdmLicense::RestoreLicenseForRelease: unable to parse signed license" " response"); return false; } if (SignedMessage::LICENSE != signed_response.type()) { - LOGE("CdmLicense::RestoreUsageLicense: unrecognized signed message type: %d" + LOGE("CdmLicense::RestoreLicenseForRelease: unrecognized signed message " + "type: %d" , signed_response.type()); return false; } if (Properties::use_certificates_as_identification()) { if (!signed_response.has_session_key()) { - LOGE("CdmLicense::RestoreUsageLicense: no session keys present"); + LOGE("CdmLicense::RestoreLicenseForRelease: no session keys present"); return false; } @@ -880,16 +882,24 @@ bool CdmLicense::RestoreUsageLicense(const CdmKeyMessage& license_request, } if (!signed_response.has_signature()) { - LOGE("CdmLicense::RestoreUsageLicense: license response is not signed"); + LOGE("CdmLicense::RestoreLicenseForRelease: license response is not" + " signed"); return false; } License license; if (!license.ParseFromString(signed_response.msg())) { - LOGE("CdmLicense::RestoreUsageLicense: unable to parse license response"); + LOGE("CdmLicense::RestoreLicenseForRelease: unable to parse license" + " response"); return false; } + if (license.id().has_provider_session_token()) + provider_session_token_ = license.id().provider_session_token(); + + if (license.policy().has_renewal_server_url()) + server_url_ = license.policy().renewal_server_url(); + policy_engine_->SetLicense(license); return true; } diff --git a/libwvdrmengine/cdm/core/src/string_conversions.cpp b/libwvdrmengine/cdm/core/src/string_conversions.cpp index 8fc04124..0f8e2d91 100644 --- a/libwvdrmengine/cdm/core/src/string_conversions.cpp +++ b/libwvdrmengine/cdm/core/src/string_conversions.cpp @@ -174,14 +174,31 @@ int64_t htonll64(int64_t x) { // Convert to big endian (network-byte-order) int64_t number; } mixed; mixed.number = 1; - if (mixed.array[0] == 1) { - mixed.number = x; // Little Endian. + if (mixed.array[0] == 1) { // Little Endian. + mixed.number = x; uint32_t temp = mixed.array[0]; mixed.array[0] = htonl(mixed.array[1]); mixed.array[1] = htonl(temp); return mixed.number; - } else { - return x; // Big Endian. + } else { // Big Endian. + return x; + } +} + +int64_t ntohll64(int64_t x) { // Convert from big endian (network-byte-order) + union { + uint32_t array[2]; + int64_t number; + } mixed; + mixed.number = 1; + if (mixed.array[0] == 1) { // Little Endian. + mixed.number = x; + uint32_t temp = mixed.array[0]; + mixed.array[0] = ntohl(mixed.array[1]); + mixed.array[1] = ntohl(temp); + return mixed.number; + } else { // Big Endian. + return x; } } diff --git a/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp b/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp new file mode 100644 index 00000000..42f2bfa4 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/cdm_session_unittest.cpp @@ -0,0 +1,240 @@ +// Copyright 2014 Google Inc. All Rights Reserved. + +#include "cdm_session.h" +#include "crypto_key.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "properties.h" +#include "string_conversions.h" + +namespace { +const std::string kToken = wvcdm::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"); +const std::string kWrappedKey = wvcdm::a2bs_hex( + "3B84252DD84F1A710365014A114507FFFA3DD404625D61D1EEC7C3A39D72CB8D9318ADE9DA" + "05D69F9776DAFDA49A97BC30E84CA275925DFD98CA04F7DB23465103A224852192DE232902" + "99FF82024F5CCA7716ACA9BE0B56348BA16B9E3136D73789C842CB2ECA4820DDAAF59CCB9B" + "FCF2B4B0E2E5199FDCEC8DEBFFE50BB03041D8E767EA3FE6834C2E79E261ABF17B68EA66E1" + "45AD0A6B056F39C06531A9038C996BADD524E57AE7D5339F13C574E7A398C03D65FD730BAC" + "36F25347350DD2AD69EFA4DC040DC2D9DD4F53A729839FA3496CF580F2CBD51C3522DD67BC" + "BA4A91E89E2BD70449F28E026638920A6DF7B9A0B2C977ACC65AE845E76EF81CADAA746DAF" + "51D4D6FCBC083BE50DA1874D6EB1A30579B23C30881D94A8E5181FE20BF8F8C5F2522B1E7D" + "092B1E20BDE5373F40286DE15267247F88C564BD4EBF4F69B889A03C9892584DC340D87EE1" + "DFF2942D1B7E7EBD846349575F2DE6FDEF71BB005CFBEA845D87937BEBCAFEAC785A092C0A" + "76CE7F7A4FE2F8E43045DED5202A2A55F547BA5DE67AF9E6B2B7DC89EFAD34AC0B40BF4B8F" + "F82F8706B9A88FB9C7A0972E4A4B6CA970BF4F086573D595E5DB8ED0FDA4F9446ACD4B119C" + "1E949C194B042A5CFFC13043FF79F049068A67CC1EB671A10EF7DA927753C4D149E9D0000D" + "4307008BA0AED576ACCADF0CE6758F683087F26C2E38297B8C7D78DC3F1E8F24D7B3A0BED9" + "C066F8348FD19CDB54A92C4E944EE11E11B3B44344E0DB0E1B4BD6CF9295AB66C05454776A" + "8FE33AF659F67718AB43ACB52E83F8C29129DCE9654E8F1EBF9DAB9E933955E24389A37DBE" + "17BA89AC8C750B025CB2F65D5C8BF32FED87EF368F15751AA2114159B6C9C6C814D0720DA4" + "6E885BBB764ADC250D05F70306C3190991C31439BF273A33B6D1773E4FD089F32E753FA3C7" + "7B5ED7DB28407D87396F1F8C83B58176EDFE1F923BDB7DA0ADE58CA2BCD6E76F9463BE7A5A" + "909BE2731241BF1436F3E6A639FC7C717445D89AA5812E4532405B0FB368FE736E22D10FFD" + "15FACCF69FAC468B5552C7887763B96578038CCF154F333E2095BBFF71D5C1235E032174FB" + "44EAB4A753E7A917666A400EBE4F3D2C90100155C27F4B30C8ACFEDA6EFC763EF3556874E5" + "8A5AB0AEBBF39990F79EF4D65EC4697D7BBEEF4F32AE8C4A8A94814A9BE532B5AC902BC0C5" + "FB0A3E661AFF5961B6E79C82CC32FA7B7B48297347503FD58B110B93208167CC1FB96AD822" + "42F60B9D2BF9CCEE8E778A3D3A3302303FB4E33F607D46AACE49D3546A993EDC6FBEE6E19D" + "36831D85877013C57FE335F38D5CD9C3E09C1CEE28BC92C53A364663A7C031DF43B89BAAB4" + "AA8176900FD483AD70E3844BD15EE4F01D8BE72186BBF9E019FCEE5961166696854D1A901F" + "9D71B69B05F75FF233DB3C37F18DCADA640F68C4386F2E528CD77B93521A4574EF399375CD" + "2BE7B9FDC0AE62249717B7E0022BF55C0D023669DD09355EAA90E9DF9BEA309DF7561423BF" + "1DAD177F07A442E1591553924C0F67C2E86774009825490322A6B74319B4C77AA6195CA393" + "03A311F762FB0FD445278D9ACF26A9049C5031BE91F2C4A6BE994CA5A3CEBC2ACCF93AB1EB" + "993A6AA6DEB152DE8C9BB0E6B37B478393B50D1AAE99C086A0ED6D93BA7DD2DEEAB58EE34B" + "C5EE06BE238E8DE6CB44211097C5C90D5C04857918856F86B7036986C20A43153892ED9093" + "33EF70621A98184DDAB5E14BC971CF98CF6C91A37FFA83B00AD3BCABBAAB2DEF1C52F43003" + "E74C92B44F9205D22262FB47948654229DE1920F8EDF96A19A88A1CA1552F8856FB4CBF83B" + "AA3348419159D207F65FCE9C1A500C6818"); +} // namespace + +namespace wvcdm { + +// gmock methods +using ::testing::_; +using ::testing::Eq; +using ::testing::NotNull; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::StrEq; + +class MockDeviceFiles : public DeviceFiles { + public: + MOCK_METHOD1(Init, bool(CdmSecurityLevel)); + MOCK_METHOD2(RetrieveCertificate, bool(std::string*, std::string*)); +}; + +class MockCryptoSession : public CryptoSession { + public: + MOCK_METHOD1(GetToken, bool(std::string*)); + MOCK_METHOD0(GetSecurityLevel, CdmSecurityLevel()); + MOCK_METHOD0(Open, CdmResponseType()); + MOCK_METHOD1(Open, CdmResponseType(SecurityLevel)); + MOCK_METHOD1(LoadCertificatePrivateKey, bool(std::string&)); + MOCK_METHOD0(DeleteAllUsageReports, CdmResponseType()); +}; + +class MockPolicyEngine : public PolicyEngine { + public: + // Leaving a place holder for when PolicyEngine methods need to be mocked +}; + +class MockCdmLicense : public CdmLicense { + public: + MOCK_METHOD3(Init, bool(const std::string&, CryptoSession*, PolicyEngine*)); +}; + +class CdmSessionTest : public ::testing::Test { + protected: + virtual void SetUp() { + license_parser_ = new MockCdmLicense(); + crypto_session_ = new MockCryptoSession(); + policy_engine_ = new MockPolicyEngine(); + file_handle_ = new MockDeviceFiles(); + } + + virtual void TearDown() { + if (cdm_session_) delete cdm_session_; + } + + void CreateSession() { + cdm_session_ = new CdmSession(license_parser_, crypto_session_, + policy_engine_, file_handle_, NULL); + } + + void CreateSession(const CdmClientPropertySet* cdm_client_property_set) { + cdm_session_ = + new CdmSession(license_parser_, crypto_session_, policy_engine_, + file_handle_, cdm_client_property_set); + } + + CdmSession* cdm_session_; + MockCdmLicense* license_parser_; + MockCryptoSession* crypto_session_; + MockPolicyEngine* policy_engine_; + MockDeviceFiles* file_handle_; +}; + +TEST_F(CdmSessionTest, InitWithCertificate) { + CdmSecurityLevel level = kSecurityLevelL1; + EXPECT_CALL(*crypto_session_, GetSecurityLevel()).WillOnce(Return(level)); + EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault))) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true)); + EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey), + Return(true))); + EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey))) + .WillOnce(Return(true)); + EXPECT_CALL(*license_parser_, + Init(Eq(kToken), Eq(crypto_session_), Eq(policy_engine_))) + .WillOnce(Return(true)); + + Properties::set_use_certificates_as_identification(true); + + CreateSession(); + ASSERT_EQ(NO_ERROR, cdm_session_->Init()); +} + +TEST_F(CdmSessionTest, InitWithKeybox) { + CdmSecurityLevel level = kSecurityLevelL1; + EXPECT_CALL(*crypto_session_, GetSecurityLevel()).WillOnce(Return(level)); + EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault))) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, GetToken(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kToken), Return(true))); + EXPECT_CALL(*license_parser_, + Init(Eq(kToken), Eq(crypto_session_), Eq(policy_engine_))) + .WillOnce(Return(true)); + + Properties::set_use_certificates_as_identification(false); + + CreateSession(); + ASSERT_EQ(NO_ERROR, cdm_session_->Init()); +} + +TEST_F(CdmSessionTest, ReInitFail) { + CdmSecurityLevel level = kSecurityLevelL1; + EXPECT_CALL(*crypto_session_, GetSecurityLevel()).WillOnce(Return(level)); + EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault))) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true)); + EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey), + Return(true))); + EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey))) + .WillOnce(Return(true)); + EXPECT_CALL(*license_parser_, + Init(Eq(kToken), Eq(crypto_session_), Eq(policy_engine_))) + .WillOnce(Return(true)); + + Properties::set_use_certificates_as_identification(true); + + CreateSession(); + ASSERT_EQ(NO_ERROR, cdm_session_->Init()); + ASSERT_EQ(UNKNOWN_ERROR, cdm_session_->Init()); +} + +TEST_F(CdmSessionTest, InitFailCryptoError) { + CdmSecurityLevel level = kSecurityLevelL1; + EXPECT_CALL(*crypto_session_, GetSecurityLevel()).WillOnce(Return(level)); + EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault))) + .WillOnce(Return(UNKNOWN_ERROR)); + + Properties::set_use_certificates_as_identification(true); + + CreateSession(); + ASSERT_EQ(UNKNOWN_ERROR, cdm_session_->Init()); +} + +TEST_F(CdmSessionTest, InitNeedsProvisioning) { + CdmSecurityLevel level = kSecurityLevelL1; + EXPECT_CALL(*crypto_session_, GetSecurityLevel()).WillOnce(Return(level)); + EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault))) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true)); + EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull())) + .WillOnce(Return(false)); + + Properties::set_use_certificates_as_identification(true); + + CreateSession(); + ASSERT_EQ(NEED_PROVISIONING, cdm_session_->Init()); +} + +} // wvcdm diff --git a/libwvdrmengine/cdm/core/test/config_test_env.cpp b/libwvdrmengine/cdm/core/test/config_test_env.cpp index d289fa2f..8acf357e 100644 --- a/libwvdrmengine/cdm/core/test/config_test_env.cpp +++ b/libwvdrmengine/cdm/core/test/config_test_env.cpp @@ -27,7 +27,7 @@ const std::string kCpOfflineKeyId = "00000020" // pssh data size // pssh data: "08011a0d7769646576696e655f746573" - "74220d6f66666c696e655f636c697031"; + "74220d6f66666c696e655f636c697032"; // Google Play license server data const std::string kGpLicenseServer = diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index ca589647..de7ed587 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -106,7 +106,10 @@ CdmResponseType WvContentDecryptionModule::AddKey( CdmResponseType WvContentDecryptionModule::RestoreKey( const CdmSessionId& session_id, const CdmKeySetId& key_set_id) { - return cdm_engine_->RestoreKey(session_id, key_set_id); + CdmResponseType sts = cdm_engine_->RestoreKey(session_id, key_set_id); + if (sts == KEY_ADDED) + EnablePolicyTimer(); + return sts; } CdmResponseType WvContentDecryptionModule::CancelKeyRequest( diff --git a/libwvdrmengine/cdm/test/Android.mk b/libwvdrmengine/cdm/test/Android.mk index 66700ced..9e66bac2 100644 --- a/libwvdrmengine/cdm/test/Android.mk +++ b/libwvdrmengine/cdm/test/Android.mk @@ -11,6 +11,10 @@ test_name := cdm_engine_test test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk +test_name := cdm_extended_duration_test +test_src_dir := . +include $(LOCAL_PATH)/unit-test.mk + test_name := cdm_session_unittest test_src_dir := ../core/test include $(LOCAL_PATH)/unit-test.mk diff --git a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp new file mode 100644 index 00000000..47fed1e1 --- /dev/null +++ b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp @@ -0,0 +1,1278 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include +#include +#include + +#include "clock.h" +#include "config_test_env.h" +#include "device_files.h" +#include "file_store.h" +#include "license_request.h" +#include "license_protocol.pb.h" +#include "log.h" +#include "gtest/gtest.h" +#include "OEMCryptoCENC.h" +#include "oemcrypto_adapter.h" +#include "properties.h" +#include "string_conversions.h" +#include "url_request.h" +#include "wv_cdm_constants.h" +#include "wv_cdm_event_listener.h" +#include "wv_content_decryption_module.h" + +namespace { + +// HTTP response codes. +const int kHttpOk = 200; +const int kHttpBadRequest = 400; +const int kHttpInternalServerError = 500; + +const uint32_t kMinute = 60; +const uint32_t kClockTolerance = 5; +const uint32_t kTwoMinutes = 120; + +// Default license server, can be configured using --server command line option +// Default key id (pssh), can be configured using --keyid command line option +std::string g_client_auth; +wvcdm::ConfigTestEnv* g_config = NULL; +wvcdm::KeyId g_key_id; +wvcdm::CdmKeySystem g_key_system; +std::string g_license_server; +wvcdm::KeyId g_wrong_key_id; +wvcdm::LicenseServerId g_license_server_id = wvcdm::kContentProtectionServer; + +std::string kServiceCertificate = + "0803121028703454C008F63618ADE7443DB6C4C8188BE7F99005228E023082010" + "A0282010100B52112B8D05D023FCC5D95E2C251C1C649B4177CD8D2BEEF355BB0" + "6743DE661E3D2ABC3182B79946D55FDC08DFE95407815E9A6274B322A2C7F5E06" + "7BB5F0AC07A89D45AEA94B2516F075B66EF811D0D26E1B9A6B894F2B9857962AA" + "171C4F66630D3E4C602718897F5E1EF9B6AAF5AD4DBA2A7E14176DF134A1D3185" + "B5A218AC05A4C41F081EFFF80A3A040C50B09BBC740EEDCD8F14D675A91980F92" + "CA7DDC646A06ADAD5101F74A0E498CC01F00532BAC217850BD905E90923656B7D" + "FEFEF42486767F33EF6283D4F4254AB72589390BEE55808F1D668080D45D893C2" + "BCA2F74D60A0C0D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD" + "94302030100013A1273746167696E672E676F6F676C652E636F6D"; + +// TODO(rfrias): refactor to print out the decryption test names +struct SubSampleInfo { + bool retrieve_key; + size_t num_of_subsamples; + bool validate_key_id; + bool is_encrypted; + bool is_secure; + wvcdm::KeyId key_id; + std::vector encrypt_data; + std::vector decrypt_data; + std::vector iv; + size_t block_offset; + uint8_t subsample_flags; +}; + +SubSampleInfo kEncryptedStreamingNoPstSubSample = { + // key SD, encrypted, 256b + true, 1, true, true, false, + wvcdm::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"), + wvcdm::a2b_hex( + "64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36" + "17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d" + "7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab" + "ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c" + "2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4" + "9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4" + "6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012" + "c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685"), + wvcdm::a2b_hex( + "217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c" + "942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca" + "595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747" + "8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6" + "ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91" + "029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd" + "4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed" + "08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659"), + wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0, 3}; + +SubSampleInfo kEncryptedStreamingClip1SubSample = { + true, 1, true, true, false, + wvcdm::a2bs_hex("E82DDD1D07CBB31CDD31EBAAE0894609"), + wvcdm::a2b_hex( + "fe8670a01c86906c056b4bf85ad278464c4eb79c60de1da8480e66e78561350e" + "a25ae19a001f834c43aaeadf900b3c5a6745e885a4d1d1ae5bafac08dc1d60e5" + "f3465da303909ec4b09023490471f670b615d77db844192854fdab52b7806203" + "89b374594bbb6a2f2fcc31036d7cb8a3f80c0e27637b58a7650028fbf2470d68" + "1bbd77934af165d215ef325c74438c9d99a20fc628792db28c05ed5deff7d9d4" + "dba02ddb6cf11dc6e78cb5200940af9a2321c3a7c4c79be67b54a744dae1209c" + "fa02fc250ce18d30c7da9c3a4a6c9619bf8561a42ff1e55a7b14fa3c8de69196" + "c2b8e3ff672fc37003b479da5d567b7199917dbe5aa402890ebb066bce140b33"), + wvcdm::a2b_hex( + "d08733bd0ef671f467906b50ff8322091400f86fd6f016fea2b86e33923775b3" + "ebb4c8c6f3ba8b78dd200a74d3872a40264ab99e1d422e4f819abb7f249114aa" + "b334420b37c86ce81938615ab9d3a6b2de8db545cd88e35091031e73016fb386" + "1b754298329b52dbe483de3a532277815e659f3e05e89257333225b933d92e15" + "ef2deff287a192d2c8fc942a29a5f3a1d54440ac6385de7b34bb650b889e4ae9" + "58c957b5f5ff268f445c0a6b825fcad55290cb7b5c9814bc4c72984dcf4c8fd7" + "5f511c173b2e0a3163b18a1eac58539e5c188aeb0751b946ad4dcd08ea777a7f" + "37326df26fa509343faa98dff667629f557873f1284903202e451227ef465a62"), + wvcdm::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0, 3}; + +SubSampleInfo kEncryptedOfflineClip2SubSample = { + true, 1, true, true, false, + wvcdm::a2bs_hex("3260F39E12CCF653529990168A3583FF"), + wvcdm::a2b_hex( + "3b2cbde084973539329bd5656da22d20396249bf4a18a51c38c4743360cc9fea" + "a1c78d53de1bd7e14dc5d256fd20a57178a98b83804258c239acd7aa38f2d7d2" + "eca614965b3d22049e19e236fc1800e60965d8b36415677bf2f843d50a6943c4" + "683c07c114a32f5e5fbc9939c483c3a1b2ecd3d82b554d649798866191724283" + "f0ab082eba2da79aaca5c4eaf186f9ee9a0c568f621f705a578f30e4e2ef7b96" + "5e14cc046ce6dbf272ee5558b098f332333e95fc879dea6c29bf34acdb649650" + "f08201b9e649960f2493fd7677cc3abf5ae70e5445845c947ba544456b431646" + "d95a133bff5f57614dda5e4446cd8837901d074149dadf4b775b5b07bb88ca20"), + wvcdm::a2b_hex( + "D3EE543581F16AB2EABFA13468133314D19CB6A14A42229BE83543828D801475" + "FAE1C2C5D193DA8445B9C4B1598E8FCBDF42EFF1FBB54EBC6A4815E2836C2848" + "15094DEDE76FE4658A2D6EA3E775A872CA71835CF274676C18556C665EC7F32A" + "4DBB04C10BA988B42758E37DCEFD99D9CE3AFFB1E816C412B4013890E1A31E25" + "64EBF2125BC54D66FECDF8A31956830121546DC183B3DAC103E223778875B590" + "3961448C287B367C585E510DA43BF9242B8E9A27B9F6F3EC7E4B5A0A74A1742B" + "F5CD65EA66D7D9E79C02C7E7D5CD02DB182DDD8EAC3525B0834F1F2822AD0006" + "944B5080B998BB0FE6E566AAFAE2328B37FD189F1920A964434ECF18E11AC81E"), + wvcdm::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0, 3}; + +std::string kStreamingClip1PstInitData = wvcdm::a2bs_hex( + "000000427073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id + "08011a0d7769646576696e655f74657374220f73" // pssh data + "747265616d696e675f636c697033"); + +std::string kOfflineClip2PstInitData = wvcdm::a2bs_hex( + "000000427073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id + "08011a0d7769646576696e655f74657374220d6f" // pssh data + "66666c696e655f636c697032"); +} // namespace + +namespace wvcdm { +// Protobuf generated classes +using video_widevine_server::sdk::LicenseIdentification; +using video_widevine_server::sdk::LicenseRequest_ContentIdentification; +using video_widevine_server::sdk::ClientIdentification; +using video_widevine_server::sdk::SignedMessage; + +class TestWvCdmClientPropertySet : public CdmClientPropertySet { + public: + TestWvCdmClientPropertySet() + : use_privacy_mode_(false), + is_session_sharing_enabled_(false), + session_sharing_id_(0) {} + virtual ~TestWvCdmClientPropertySet() {} + + virtual const std::string& security_level() const { return security_level_; } + virtual const std::string& service_certificate() const { + return service_certificate_; + } + virtual bool use_privacy_mode() const { return use_privacy_mode_; } + virtual bool is_session_sharing_enabled() const { + return is_session_sharing_enabled_; + } + virtual uint32_t session_sharing_id() const { return session_sharing_id_; } + + void set_security_level(const std::string& security_level) { + if (!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) || + !security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)) { + security_level_ = security_level; + } + } + void set_service_certificate(const std::string& service_certificate) { + service_certificate_ = service_certificate; + } + void set_use_privacy_mode(bool use_privacy_mode) { + use_privacy_mode_ = use_privacy_mode; + } + void set_session_sharing_mode(bool enable) { + is_session_sharing_enabled_ = enable; + } + void set_session_sharing_id(uint32_t id) { session_sharing_id_ = id; } + + private: + std::string security_level_; + std::string service_certificate_; + bool use_privacy_mode_; + bool is_session_sharing_enabled_; + uint32_t session_sharing_id_; +}; + +class TestWvCdmEventListener : public WvCdmEventListener { + public: + TestWvCdmEventListener() : WvCdmEventListener() {} + virtual void OnEvent(const CdmSessionId& id, CdmEventType event) { + session_id_ = id; + event_type_ = event; + } + CdmSessionId session_id() { return session_id_; } + CdmEventType event_type() { return event_type_; } + + private: + CdmSessionId session_id_; + CdmEventType event_type_; +}; + +class WvCdmExtendedDurationTest : public testing::Test { + public: + WvCdmExtendedDurationTest() {} + ~WvCdmExtendedDurationTest() {} + + protected: + void GetOfflineConfiguration(std::string* key_id, std::string* client_auth) { + ConfigTestEnv config(g_license_server_id, false); + if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0) + key_id->assign(wvcdm::a2bs_hex(config.key_id())); + else + key_id->assign(g_key_id); + + if (g_client_auth.compare(g_config->client_auth()) == 0) + client_auth->assign(config.client_auth()); + else + client_auth->assign(g_client_auth); + } + + void GenerateKeyRequest(const std::string& init_data, + CdmLicenseType license_type) { + wvcdm::CdmAppParameterMap app_parameters; + std::string server_url; + EXPECT_EQ(wvcdm::KEY_MESSAGE, + decryptor_.GenerateKeyRequest( + session_id_, key_set_id_, "video/mp4", init_data, + license_type, app_parameters, &key_msg_, &server_url)); + EXPECT_EQ(0u, server_url.size()); + } + + void GenerateRenewalRequest(CdmLicenseType license_type, + std::string* server_url) { + // TODO application makes a license request, CDM will renew the license + // when appropriate. + std::string init_data; + wvcdm::CdmAppParameterMap app_parameters; + EXPECT_EQ(wvcdm::KEY_MESSAGE, + decryptor_.GenerateKeyRequest( + session_id_, key_set_id_, "video/mp4", init_data, + license_type, app_parameters, &key_msg_, server_url)); + // TODO(edwinwong, rfrias): Add tests cases for when license server url + // is empty on renewal. Need appropriate key id at the server. + EXPECT_NE(0u, server_url->size()); + } + + void GenerateKeyRelease(CdmKeySetId key_set_id) { + CdmSessionId session_id; + CdmInitData init_data; + wvcdm::CdmAppParameterMap app_parameters; + std::string server_url; + EXPECT_EQ(wvcdm::KEY_MESSAGE, + decryptor_.GenerateKeyRequest( + session_id, key_set_id, "video/mp4", init_data, + kLicenseTypeRelease, app_parameters, &key_msg_, &server_url)); + } + + // Post a request and extract the drm message from the response + std::string GetKeyRequestResponse(const std::string& server_url, + const std::string& client_auth) { + // Use secure connection and chunk transfer coding. + UrlRequest url_request(server_url + client_auth); + if (!url_request.is_connected()) { + return ""; + } + url_request.PostRequest(key_msg_); + std::string message; + int resp_bytes = url_request.GetResponse(&message); + + int status_code = url_request.GetStatusCode(message); + EXPECT_EQ(kHttpOk, status_code); + + std::string drm_msg; + if (kHttpOk == status_code) { + LicenseRequest lic_request; + lic_request.GetDrmMessage(message, drm_msg); + LOGV("HTTP response body: (%u bytes)", drm_msg.size()); + } + key_response_ = drm_msg; + return drm_msg; + } + + // Post a request and extract the signed provisioning message from + // the HTTP response. + std::string GetCertRequestResponse(const std::string& server_url) { + // Use secure connection and chunk transfer coding. + UrlRequest url_request(server_url); + if (!url_request.is_connected()) { + return ""; + } + + url_request.PostCertRequestInQueryString(key_msg_); + std::string message; + int resp_bytes = url_request.GetResponse(&message); + LOGD("end %d bytes response dump", resp_bytes); + + int status_code = url_request.GetStatusCode(message); + EXPECT_EQ(kHttpOk, status_code); + return message; + } + + // Post a request and extract the signed provisioning message from + // the HTTP response. + std::string GetUsageInfoResponse(const std::string& server_url, + const std::string& client_auth, + const std::string& usage_info_request) { + // Use secure connection and chunk transfer coding. + UrlRequest url_request(server_url + client_auth); + if (!url_request.is_connected()) { + return ""; + } + url_request.PostRequest(usage_info_request); + std::string message; + int resp_bytes = url_request.GetResponse(&message); + + int status_code = url_request.GetStatusCode(message); + EXPECT_EQ(kHttpOk, status_code); + + std::string usage_info; + if (kHttpOk == status_code) { + LicenseRequest license; + license.GetDrmMessage(message, usage_info); + LOGV("HTTP response body: (%u bytes)", usage_info.size()); + } + return usage_info; + } + + void VerifyKeyRequestResponse(const std::string& server_url, + const std::string& client_auth, + bool is_renewal) { + std::string resp = GetKeyRequestResponse(server_url, client_auth); + + if (is_renewal) { + // TODO application makes a license request, CDM will renew the license + // when appropriate + EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_), + wvcdm::KEY_ADDED); + } else { + EXPECT_EQ(decryptor_.AddKey(session_id_, resp, &key_set_id_), + wvcdm::KEY_ADDED); + } + } + + void Unprovision() { + EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(kSecurityLevelL1)); + EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(kSecurityLevelL3)); + } + + void Provision() { + CdmResponseType status = + decryptor_.OpenSession(g_key_system, NULL, &session_id_); + switch (status) { + case NO_ERROR: + decryptor_.CloseSession(session_id_); + return; + case NEED_PROVISIONING: + break; + default: + EXPECT_EQ(NO_ERROR, status); + return; + } + + std::string provisioning_server_url; + CdmCertificateType cert_type = kCertificateWidevine; + std::string cert_authority, cert, wrapped_key; + + status = decryptor_.GetProvisioningRequest( + cert_type, cert_authority, &key_msg_, &provisioning_server_url); + EXPECT_EQ(wvcdm::NO_ERROR, status); + if (NO_ERROR != status) return; + EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); + + std::string response = + GetCertRequestResponse(g_config->provisioning_server_url()); + EXPECT_NE(0, static_cast(response.size())); + EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse( + response, &cert, &wrapped_key)); + EXPECT_EQ(0, static_cast(cert.size())); + EXPECT_EQ(0, static_cast(wrapped_key.size())); + decryptor_.CloseSession(session_id_); + return; + } + + void ValidateResponse(video_widevine_server::sdk::LicenseType license_type, + bool has_provider_session_token) { + // Validate signed response + SignedMessage signed_message; + EXPECT_TRUE(signed_message.ParseFromString(key_response_)); + EXPECT_EQ(SignedMessage::LICENSE, signed_message.type()); + EXPECT_TRUE(signed_message.has_signature()); + EXPECT_TRUE(!signed_message.msg().empty()); + + // Verify license + video_widevine_server::sdk::License license; + EXPECT_TRUE(license.ParseFromString(signed_message.msg())); + + // Verify license identification + license_id_ = license.id(); + EXPECT_EQ(license_type, license_id_.type()); + EXPECT_EQ(has_provider_session_token, + license_id_.has_provider_session_token()); + } + + void ValidateRenewalRequest(int64_t expected_seconds_since_started, + int64_t expected_seconds_since_last_played, + bool has_provider_session_token) { + // Validate signed renewal request + SignedMessage signed_message; + EXPECT_TRUE(signed_message.ParseFromString(key_msg_)); + EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type()); + EXPECT_TRUE(signed_message.has_signature()); + EXPECT_TRUE(!signed_message.msg().empty()); + + // Verify license request + video_widevine_server::sdk::LicenseRequest license_renewal; + EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg())); + + // Verify Content Identification + const LicenseRequest_ContentIdentification& content_id = + license_renewal.content_id(); + EXPECT_FALSE(content_id.has_cenc_id()); + EXPECT_FALSE(content_id.has_webm_id()); + EXPECT_TRUE(content_id.has_license()); + + const ::video_widevine_server::sdk:: + LicenseRequest_ContentIdentification_ExistingLicense& existing_license = + content_id.license(); + + const LicenseIdentification& id = existing_license.license_id(); + EXPECT_TRUE(std::equal(id.request_id().begin(), id.request_id().end(), + license_id_.request_id().begin())); + EXPECT_TRUE(std::equal(id.session_id().begin(), id.session_id().end(), + license_id_.session_id().begin())); + EXPECT_TRUE(std::equal(id.purchase_id().begin(), id.purchase_id().end(), + license_id_.purchase_id().begin())); + EXPECT_EQ(license_id_.type(), id.type()); + EXPECT_EQ(license_id_.version(), id.version()); + EXPECT_EQ(has_provider_session_token, !id.provider_session_token().empty()); + + EXPECT_LT(expected_seconds_since_started - kClockTolerance, + existing_license.seconds_since_started()); + EXPECT_LT(existing_license.seconds_since_started(), + expected_seconds_since_started + kClockTolerance); + EXPECT_LT(expected_seconds_since_last_played - kClockTolerance, + existing_license.seconds_since_last_played()); + EXPECT_LT(existing_license.seconds_since_last_played(), + expected_seconds_since_last_played + kClockTolerance); + EXPECT_TRUE(existing_license.session_usage_table_entry().empty()); + + EXPECT_EQ(::video_widevine_server::sdk::LicenseRequest_RequestType_RENEWAL, + license_renewal.type()); + EXPECT_LT(0, license_renewal.request_time()); + EXPECT_EQ(video_widevine_server::sdk::VERSION_2_1, + license_renewal.protocol_version()); + EXPECT_TRUE(license_renewal.has_key_control_nonce()); + } + + void ValidateReleaseRequest(std::string& usage_msg, + int64_t expected_seconds_since_license_received, + int64_t expected_seconds_since_first_playback, + int64_t expected_seconds_since_last_playback) { + // Validate signed renewal request + SignedMessage signed_message; + EXPECT_TRUE(signed_message.ParseFromString(usage_msg)); + EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type()); + EXPECT_TRUE(signed_message.has_signature()); + EXPECT_TRUE(!signed_message.msg().empty()); + + // Verify license request + video_widevine_server::sdk::LicenseRequest license_renewal; + EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg())); + + // Verify Content Identification + const LicenseRequest_ContentIdentification& content_id = + license_renewal.content_id(); + EXPECT_FALSE(content_id.has_cenc_id()); + EXPECT_FALSE(content_id.has_webm_id()); + EXPECT_TRUE(content_id.has_license()); + + const ::video_widevine_server::sdk:: + LicenseRequest_ContentIdentification_ExistingLicense& existing_license = + content_id.license(); + + const LicenseIdentification& id = existing_license.license_id(); + EXPECT_TRUE(std::equal(id.request_id().begin(), id.request_id().end(), + license_id_.request_id().begin())); + EXPECT_TRUE(std::equal(id.session_id().begin(), id.session_id().end(), + license_id_.session_id().begin())); + EXPECT_TRUE(std::equal(id.purchase_id().begin(), id.purchase_id().end(), + license_id_.purchase_id().begin())); + EXPECT_EQ(license_id_.type(), id.type()); + EXPECT_EQ(license_id_.version(), id.version()); + EXPECT_TRUE(!id.provider_session_token().empty()); + + EXPECT_LT(expected_seconds_since_first_playback - kClockTolerance, + existing_license.seconds_since_started()); + EXPECT_LT(existing_license.seconds_since_started(), + expected_seconds_since_first_playback + kClockTolerance); + EXPECT_LT(expected_seconds_since_last_playback - kClockTolerance, + existing_license.seconds_since_last_played()); + EXPECT_LT(existing_license.seconds_since_last_played(), + expected_seconds_since_last_playback + kClockTolerance); + EXPECT_TRUE(!existing_license.session_usage_table_entry().empty()); + + // Verify usage report + OEMCrypto_PST_Report usage_report; + memcpy(&usage_report, existing_license.session_usage_table_entry().data(), + sizeof(usage_report)); + EXPECT_EQ(sizeof(usage_report) + usage_report.pst_length, + existing_license.session_usage_table_entry().size()); + EXPECT_EQ(kInactive, usage_report.status); + EXPECT_EQ(id.provider_session_token().size(), usage_report.pst_length); + std::string pst(existing_license.session_usage_table_entry().data() + + sizeof(OEMCrypto_PST_Report), + usage_report.pst_length); + EXPECT_EQ(id.provider_session_token(), pst); + EXPECT_LE(kInsecureClock, usage_report.clock_security_level); + + int64_t seconds_since_license_received = + htonll64(usage_report.seconds_since_license_received); + int64_t seconds_since_first_decrypt = + htonll64(usage_report.seconds_since_first_decrypt); + int64_t seconds_since_last_decrypt = + htonll64(usage_report.seconds_since_last_decrypt); + // Detect licenses that were never used + if (seconds_since_first_decrypt < 0 || + seconds_since_first_decrypt > seconds_since_license_received) { + seconds_since_first_decrypt = 0; + seconds_since_last_decrypt = 0; + } + EXPECT_LT(expected_seconds_since_license_received - kClockTolerance, + seconds_since_license_received); + EXPECT_LT(seconds_since_license_received, + expected_seconds_since_license_received + kClockTolerance); + EXPECT_LT(expected_seconds_since_first_playback - kClockTolerance, + seconds_since_first_decrypt); + EXPECT_LT(seconds_since_first_decrypt, + expected_seconds_since_first_playback + kClockTolerance); + EXPECT_LT(expected_seconds_since_last_playback - kClockTolerance, + seconds_since_last_decrypt); + EXPECT_LT(seconds_since_last_decrypt, + expected_seconds_since_last_playback + kClockTolerance); + + EXPECT_EQ(::video_widevine_server::sdk::LicenseRequest_RequestType_RELEASE, + license_renewal.type()); + EXPECT_LT(0, license_renewal.request_time()); + EXPECT_EQ(video_widevine_server::sdk::VERSION_2_1, + license_renewal.protocol_version()); + EXPECT_TRUE(license_renewal.has_key_control_nonce()); + } + + void QueryKeyStatus(bool streaming, int64_t* license_duration_remaining, + int64_t* playback_duration_remaining) { + CdmQueryMap query_info; + CdmQueryMap::iterator itr; + EXPECT_EQ(wvcdm::NO_ERROR, + decryptor_.QueryKeyStatus(session_id_, &query_info)); + + itr = query_info.find(wvcdm::QUERY_KEY_LICENSE_TYPE); + ASSERT_TRUE(itr != query_info.end()); + if (streaming) { + EXPECT_EQ(wvcdm::QUERY_VALUE_STREAMING, itr->second); + } else { + EXPECT_EQ(wvcdm::QUERY_VALUE_OFFLINE, itr->second); + } + itr = query_info.find(wvcdm::QUERY_KEY_PLAY_ALLOWED); + ASSERT_TRUE(itr != query_info.end()); + EXPECT_EQ(wvcdm::QUERY_VALUE_TRUE, itr->second); + itr = query_info.find(wvcdm::QUERY_KEY_PERSIST_ALLOWED); + ASSERT_TRUE(itr != query_info.end()); + if (streaming) { + EXPECT_EQ(wvcdm::QUERY_VALUE_FALSE, itr->second); + } else { + EXPECT_EQ(wvcdm::QUERY_VALUE_TRUE, itr->second); + } + itr = query_info.find(wvcdm::QUERY_KEY_RENEW_ALLOWED); + ASSERT_TRUE(itr != query_info.end()); + EXPECT_EQ(wvcdm::QUERY_VALUE_TRUE, itr->second); + + std::istringstream ss; + itr = query_info.find(wvcdm::QUERY_KEY_LICENSE_DURATION_REMAINING); + ASSERT_TRUE(itr != query_info.end()); + ss.str(itr->second); + ASSERT_TRUE(ss >> *license_duration_remaining); + EXPECT_LT(0, *license_duration_remaining); + itr = query_info.find(wvcdm::QUERY_KEY_PLAYBACK_DURATION_REMAINING); + ASSERT_TRUE(itr != query_info.end()); + ss.clear(); + ss.str(itr->second); + ASSERT_TRUE(ss >> *playback_duration_remaining); + EXPECT_LT(0, *playback_duration_remaining); + + itr = query_info.find(wvcdm::QUERY_KEY_RENEWAL_SERVER_URL); + ASSERT_TRUE(itr != query_info.end()); + EXPECT_LT(0u, itr->second.size()); + } + + std::string GetSecurityLevel(TestWvCdmClientPropertySet* property_set) { + decryptor_.OpenSession(g_key_system, property_set, &session_id_); + CdmQueryMap query_info; + EXPECT_EQ(wvcdm::NO_ERROR, + decryptor_.QuerySessionStatus(session_id_, &query_info)); + CdmQueryMap::iterator itr = + query_info.find(wvcdm::QUERY_KEY_SECURITY_LEVEL); + EXPECT_TRUE(itr != query_info.end()); + decryptor_.CloseSession(session_id_); + return itr->second; + } + + CdmSecurityLevel GetDefaultSecurityLevel() { + std::string level = GetSecurityLevel(NULL).c_str(); + CdmSecurityLevel security_level = kSecurityLevelUninitialized; + if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) == 0) { + security_level = kSecurityLevelL1; + } else if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0) { + security_level = kSecurityLevelL3; + } else { + EXPECT_TRUE(false); + } + return security_level; + } + + wvcdm::WvContentDecryptionModule decryptor_; + CdmKeyMessage key_msg_; + CdmKeyResponse key_response_; + CdmSessionId session_id_; + CdmKeySetId key_set_id_; + video_widevine_server::sdk::LicenseIdentification license_id_; +}; + +TEST_F(WvCdmExtendedDurationTest, VerifyLicenseRequestTest) { + Provision(); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); + GenerateKeyRequest(g_key_id, kLicenseTypeStreaming); + + EXPECT_TRUE(!key_msg_.empty()); + + // Validate signed request + SignedMessage signed_message; + EXPECT_TRUE(signed_message.ParseFromString(key_msg_)); + EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type()); + EXPECT_TRUE(signed_message.has_signature()); + EXPECT_TRUE(!signed_message.msg().empty()); + + // Verify license request + video_widevine_server::sdk::LicenseRequest license_request; + EXPECT_TRUE(license_request.ParseFromString(signed_message.msg())); + + // Verify Client Identification + const ClientIdentification& client_id = license_request.client_id(); + EXPECT_EQ(video_widevine_server::sdk:: + ClientIdentification_TokenType_DEVICE_CERTIFICATE, + client_id.type()); + + EXPECT_LT(0, client_id.client_info_size()); + for (int i = 0; i < client_id.client_info_size(); ++i) { + const ::video_widevine_server::sdk::ClientIdentification_NameValue& + name_value = client_id.client_info(i); + EXPECT_TRUE(!name_value.name().empty()); + EXPECT_TRUE(!name_value.value().empty()); + } + + EXPECT_FALSE(client_id.has_provider_client_token()); + EXPECT_FALSE(client_id.has_license_counter()); + + const ::video_widevine_server::sdk::ClientIdentification_ClientCapabilities& + client_capabilities = client_id.client_capabilities(); + EXPECT_FALSE(client_capabilities.has_client_token()); + EXPECT_TRUE(client_capabilities.has_session_token()); + EXPECT_FALSE(client_capabilities.video_resolution_constraints()); + EXPECT_TRUE(client_capabilities.has_max_hdcp_version()); + EXPECT_TRUE(client_capabilities.has_oem_crypto_api_version()); + + // Verify Content Identification + const LicenseRequest_ContentIdentification& content_id = + license_request.content_id(); + EXPECT_TRUE(content_id.has_cenc_id()); + EXPECT_FALSE(content_id.has_webm_id()); + EXPECT_FALSE(content_id.has_license()); + + const ::video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC& + cenc_id = content_id.cenc_id(); + EXPECT_TRUE(std::equal(cenc_id.pssh(0).begin(), cenc_id.pssh(0).end(), + g_key_id.begin() + 32)); + EXPECT_EQ(video_widevine_server::sdk::STREAMING, cenc_id.license_type()); + EXPECT_TRUE(cenc_id.has_request_id()); + + // Verify other license request fields + EXPECT_EQ(::video_widevine_server::sdk::LicenseRequest_RequestType_NEW, + license_request.type()); + EXPECT_LT(0, license_request.request_time()); + EXPECT_EQ(video_widevine_server::sdk::VERSION_2_1, + license_request.protocol_version()); + EXPECT_TRUE(license_request.has_key_control_nonce()); + + decryptor_.CloseSession(session_id_); +} + +TEST_F(WvCdmExtendedDurationTest, VerifyLicenseRenewalTest) { + Provision(); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); + GenerateKeyRequest(g_key_id, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, g_client_auth, false); + + // Validate signed response + SignedMessage signed_message; + EXPECT_TRUE(signed_message.ParseFromString(key_response_)); + EXPECT_EQ(SignedMessage::LICENSE, signed_message.type()); + EXPECT_TRUE(signed_message.has_signature()); + EXPECT_TRUE(!signed_message.msg().empty()); + + // Verify license + video_widevine_server::sdk::License license; + EXPECT_TRUE(license.ParseFromString(signed_message.msg())); + + // Verify license identification + video_widevine_server::sdk::LicenseIdentification license_id = license.id(); + EXPECT_LT(0, license_id.request_id().size()); + EXPECT_LT(0, license_id.session_id().size()); + EXPECT_EQ(video_widevine_server::sdk::STREAMING, license_id.type()); + EXPECT_FALSE(license_id.has_provider_session_token()); + + // Create renewal request + std::string license_server; + GenerateRenewalRequest(kLicenseTypeStreaming, &license_server); + EXPECT_TRUE(!license_server.empty()); + EXPECT_TRUE(!key_msg_.empty()); + + // Validate signed renewal request + signed_message.Clear(); + EXPECT_TRUE(signed_message.ParseFromString(key_msg_)); + EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type()); + EXPECT_TRUE(signed_message.has_signature()); + EXPECT_TRUE(!signed_message.msg().empty()); + + // Verify license request + video_widevine_server::sdk::LicenseRequest license_renewal; + EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg())); + + // Client Identification not filled in in renewal + + // Verify Content Identification + const LicenseRequest_ContentIdentification& content_id = + license_renewal.content_id(); + EXPECT_FALSE(content_id.has_cenc_id()); + EXPECT_FALSE(content_id.has_webm_id()); + EXPECT_TRUE(content_id.has_license()); + + const ::video_widevine_server::sdk:: + LicenseRequest_ContentIdentification_ExistingLicense& existing_license = + content_id.license(); + + const LicenseIdentification& id = existing_license.license_id(); + EXPECT_TRUE(std::equal(id.request_id().begin(), id.request_id().end(), + license_id.request_id().begin())); + EXPECT_TRUE(std::equal(id.session_id().begin(), id.session_id().end(), + license_id.session_id().begin())); + EXPECT_TRUE(std::equal(id.purchase_id().begin(), id.purchase_id().end(), + license_id.purchase_id().begin())); + EXPECT_EQ(license_id.type(), id.type()); + EXPECT_EQ(license_id.version(), id.version()); + EXPECT_TRUE(id.provider_session_token().empty()); + + EXPECT_EQ(0, existing_license.seconds_since_started()); + EXPECT_EQ(0, existing_license.seconds_since_last_played()); + EXPECT_TRUE(existing_license.session_usage_table_entry().empty()); + + EXPECT_EQ(::video_widevine_server::sdk::LicenseRequest_RequestType_RENEWAL, + license_renewal.type()); + EXPECT_LT(0, license_renewal.request_time()); + EXPECT_EQ(video_widevine_server::sdk::VERSION_2_1, + license_renewal.protocol_version()); + EXPECT_TRUE(license_renewal.has_key_control_nonce()); + + decryptor_.CloseSession(session_id_); +} + +class WvCdmStreamingNoPstTest : public WvCdmExtendedDurationTest, + public ::testing::WithParamInterface {}; + +TEST_P(WvCdmStreamingNoPstTest, UsageTest) { + Unprovision(); + Provision(); + + decryptor_.OpenSession(g_key_system, NULL, &session_id_); + GenerateKeyRequest(g_key_id, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, g_client_auth, false); + + ValidateResponse(video_widevine_server::sdk::STREAMING, false); + + int64_t initial_license_duration_remaining = 0; + int64_t initial_playback_duration_remaining = 0; + QueryKeyStatus(true, &initial_license_duration_remaining, + &initial_playback_duration_remaining); + + sleep(kMinute); + int64_t expected_seconds_since_license_received = kMinute; + int64_t expected_seconds_since_initial_playback = 0; + int64_t expected_seconds_since_last_playback = 0; + + for (size_t i = 0; i < GetParam(); ++i) { + // Decrypt data + SubSampleInfo* data = &kEncryptedStreamingNoPstSubSample; + for (size_t i = 0; i < data->num_of_subsamples; i++) { + std::vector decrypt_buffer((data + i)->encrypt_data.size()); + CdmDecryptionParameters decryption_parameters( + &(data + i)->key_id, &(data + i)->encrypt_data.front(), + (data + i)->encrypt_data.size(), &(data + i)->iv, + (data + i)->block_offset, &decrypt_buffer[0]); + decryption_parameters.is_encrypted = (data + i)->is_encrypted; + decryption_parameters.is_secure = (data + i)->is_secure; + decryption_parameters.subsample_flags = (data + i)->subsample_flags; + EXPECT_EQ(NO_ERROR, + decryptor_.Decrypt(session_id_, (data + i)->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal((data + i)->decrypt_data.begin(), + (data + i)->decrypt_data.end(), + decrypt_buffer.begin())); + } + + sleep(kMinute); + expected_seconds_since_license_received += kMinute; + expected_seconds_since_initial_playback += kMinute; + expected_seconds_since_last_playback = kMinute; + } + + // Create renewal request and validate + std::string license_server; + GenerateRenewalRequest(kLicenseTypeStreaming, &license_server); + EXPECT_TRUE(!license_server.empty()); + EXPECT_TRUE(!key_msg_.empty()); + + ValidateRenewalRequest(expected_seconds_since_initial_playback, + expected_seconds_since_last_playback, false); + + // Query and validate usage information + int64_t license_duration_remaining = 0; + int64_t playback_duration_remaining = 0; + QueryKeyStatus(true, &license_duration_remaining, + &playback_duration_remaining); + + int64_t delta = + initial_license_duration_remaining - license_duration_remaining; + EXPECT_LT(expected_seconds_since_license_received - kClockTolerance, delta); + EXPECT_LT(delta, expected_seconds_since_license_received + kClockTolerance); + delta = initial_playback_duration_remaining - playback_duration_remaining; + EXPECT_LT(expected_seconds_since_initial_playback - kClockTolerance, delta); + EXPECT_LT(delta, expected_seconds_since_initial_playback + kClockTolerance); + + decryptor_.CloseSession(session_id_); +} + +INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingNoPstTest, + ::testing::Values(0, 1, 2)); + +class WvCdmStreamingPstTest : public WvCdmExtendedDurationTest, + public ::testing::WithParamInterface {}; + +TEST_P(WvCdmStreamingPstTest, UsageTest) { + Unprovision(); + Provision(); + + decryptor_.OpenSession(g_key_system, NULL, &session_id_); + GenerateKeyRequest(kStreamingClip1PstInitData, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, g_client_auth, false); + + ValidateResponse(video_widevine_server::sdk::STREAMING, true); + + int64_t initial_license_duration_remaining = 0; + int64_t initial_playback_duration_remaining = 0; + QueryKeyStatus(true, &initial_license_duration_remaining, + &initial_playback_duration_remaining); + + sleep(kMinute); + int64_t expected_seconds_since_license_received = kMinute; + int64_t expected_seconds_since_initial_playback = 0; + int64_t expected_seconds_since_last_playback = 0; + + for (size_t i = 0; i < GetParam(); ++i) { + // Decrypt data + SubSampleInfo* data = &kEncryptedStreamingClip1SubSample; + for (size_t i = 0; i < data->num_of_subsamples; i++) { + std::vector decrypt_buffer((data + i)->encrypt_data.size()); + CdmDecryptionParameters decryption_parameters( + &(data + i)->key_id, &(data + i)->encrypt_data.front(), + (data + i)->encrypt_data.size(), &(data + i)->iv, + (data + i)->block_offset, &decrypt_buffer[0]); + decryption_parameters.is_encrypted = (data + i)->is_encrypted; + decryption_parameters.is_secure = (data + i)->is_secure; + decryption_parameters.subsample_flags = (data + i)->subsample_flags; + EXPECT_EQ(NO_ERROR, + decryptor_.Decrypt(session_id_, (data + i)->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal((data + i)->decrypt_data.begin(), + (data + i)->decrypt_data.end(), + decrypt_buffer.begin())); + } + + sleep(kMinute); + expected_seconds_since_license_received += kMinute; + expected_seconds_since_initial_playback += kMinute; + expected_seconds_since_last_playback = kMinute; + } + + // Create renewal request and validate + std::string license_server; + GenerateRenewalRequest(kLicenseTypeStreaming, &license_server); + EXPECT_TRUE(!license_server.empty()); + EXPECT_TRUE(!key_msg_.empty()); + + ValidateRenewalRequest(expected_seconds_since_initial_playback, + expected_seconds_since_last_playback, true); + + // Query and validate usage information + int64_t license_duration_remaining = 0; + int64_t playback_duration_remaining = 0; + QueryKeyStatus(true, &license_duration_remaining, + &playback_duration_remaining); + + int64_t delta = + initial_license_duration_remaining - license_duration_remaining; + EXPECT_LT(expected_seconds_since_license_received - kClockTolerance, delta); + EXPECT_LT(delta, expected_seconds_since_license_received + kClockTolerance); + delta = initial_playback_duration_remaining - playback_duration_remaining; + EXPECT_LT(expected_seconds_since_initial_playback - kClockTolerance, delta); + EXPECT_LT(delta, expected_seconds_since_initial_playback + kClockTolerance); + + decryptor_.CloseSession(session_id_); +} + +INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingPstTest, ::testing::Values(0, 1, 2)); + +class WvCdmStreamingUsageReportTest + : public WvCdmExtendedDurationTest, + public ::testing::WithParamInterface {}; + +TEST_P(WvCdmStreamingUsageReportTest, UsageTest) { + Unprovision(); + Provision(); + + decryptor_.OpenSession(g_key_system, NULL, &session_id_); + GenerateKeyRequest(kStreamingClip1PstInitData, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, g_client_auth, false); + + ValidateResponse(video_widevine_server::sdk::STREAMING, true); + + int64_t initial_license_duration_remaining = 0; + int64_t initial_playback_duration_remaining = 0; + QueryKeyStatus(true, &initial_license_duration_remaining, + &initial_playback_duration_remaining); + + sleep(kMinute); + int64_t expected_seconds_since_license_received = kMinute; + int64_t expected_seconds_since_initial_playback = 0; + int64_t expected_seconds_since_last_playback = 0; + + for (size_t i = 0; i < GetParam(); ++i) { + // Decrypt data + SubSampleInfo* data = &kEncryptedStreamingClip1SubSample; + for (size_t i = 0; i < data->num_of_subsamples; i++) { + std::vector decrypt_buffer((data + i)->encrypt_data.size()); + CdmDecryptionParameters decryption_parameters( + &(data + i)->key_id, &(data + i)->encrypt_data.front(), + (data + i)->encrypt_data.size(), &(data + i)->iv, + (data + i)->block_offset, &decrypt_buffer[0]); + decryption_parameters.is_encrypted = (data + i)->is_encrypted; + decryption_parameters.is_secure = (data + i)->is_secure; + decryption_parameters.subsample_flags = (data + i)->subsample_flags; + EXPECT_EQ(NO_ERROR, + decryptor_.Decrypt(session_id_, (data + i)->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal((data + i)->decrypt_data.begin(), + (data + i)->decrypt_data.end(), + decrypt_buffer.begin())); + } + + sleep(kMinute); + expected_seconds_since_license_received += kMinute; + expected_seconds_since_initial_playback += kMinute; + expected_seconds_since_last_playback = kMinute; + } + + // Query and validate usage information + int64_t license_duration_remaining = 0; + int64_t playback_duration_remaining = 0; + QueryKeyStatus(true, &license_duration_remaining, + &playback_duration_remaining); + + int64_t delta = + initial_license_duration_remaining - license_duration_remaining; + EXPECT_LT(expected_seconds_since_license_received - kClockTolerance, delta); + EXPECT_LT(delta, expected_seconds_since_license_received + kClockTolerance); + delta = initial_playback_duration_remaining - playback_duration_remaining; + EXPECT_LT(expected_seconds_since_initial_playback - kClockTolerance, delta); + EXPECT_LT(delta, expected_seconds_since_initial_playback + kClockTolerance); + + decryptor_.CloseSession(session_id_); + + // Create usage report and validate + uint32_t num_usage_info = 0; + CdmUsageInfo usage_info; + CdmUsageInfoReleaseMessage release_msg; + CdmResponseType status = decryptor_.GetUsageInfo(&usage_info); + EXPECT_EQ(usage_info.empty() ? NO_ERROR : KEY_MESSAGE, status); + while (usage_info.size() > 0) { + for (size_t i = 0; i < usage_info.size(); ++i) { + ValidateReleaseRequest(usage_info[i], + expected_seconds_since_license_received, + expected_seconds_since_initial_playback, + expected_seconds_since_last_playback); + release_msg = + GetUsageInfoResponse(g_license_server, g_client_auth, usage_info[i]); + EXPECT_EQ(NO_ERROR, decryptor_.ReleaseUsageInfo(release_msg)); + } + status = decryptor_.GetUsageInfo(&usage_info); + switch (status) { + case KEY_MESSAGE: + EXPECT_FALSE(usage_info.empty()); + break; + case NO_ERROR: + EXPECT_TRUE(usage_info.empty()); + break; + default: + FAIL() << "GetUsageInfo failed with error " << static_cast(status); + break; + } + } +} + +INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingUsageReportTest, + ::testing::Values(0, 1, 2)); + +class WvCdmOfflineUsageReportTest + : public WvCdmExtendedDurationTest, + public ::testing::WithParamInterface {}; + +TEST_P(WvCdmOfflineUsageReportTest, UsageTest) { + Unprovision(); + Provision(); + + decryptor_.OpenSession(g_key_system, NULL, &session_id_); + GenerateKeyRequest(kOfflineClip2PstInitData, kLicenseTypeOffline); + VerifyKeyRequestResponse(g_license_server, g_client_auth, false); + + ValidateResponse(video_widevine_server::sdk::OFFLINE, true); + + CdmKeySetId key_set_id = key_set_id_; + EXPECT_FALSE(key_set_id_.empty()); + + int64_t initial_license_duration_remaining = 0; + int64_t initial_playback_duration_remaining = 0; + QueryKeyStatus(false, &initial_license_duration_remaining, + &initial_playback_duration_remaining); + + decryptor_.CloseSession(session_id_); + + sleep(kMinute); + int64_t expected_seconds_since_license_received = kMinute; + int64_t expected_seconds_since_initial_playback = 0; + int64_t expected_seconds_since_last_playback = 0; + + for (size_t i = 0; i < GetParam(); ++i) { + session_id_.clear(); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); + EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); + + // Query and validate usage information + int64_t license_duration_remaining = 0; + int64_t playback_duration_remaining = 0; + QueryKeyStatus(false, &license_duration_remaining, + &playback_duration_remaining); + + int64_t delta = + initial_license_duration_remaining - license_duration_remaining; + EXPECT_LT(expected_seconds_since_license_received - kClockTolerance, delta); + EXPECT_LT(delta, expected_seconds_since_license_received + kClockTolerance); + delta = initial_playback_duration_remaining - playback_duration_remaining; + EXPECT_LT(expected_seconds_since_initial_playback - kClockTolerance, delta); + EXPECT_LT(delta, expected_seconds_since_initial_playback + kClockTolerance); + + // Decrypt data + SubSampleInfo* data = &kEncryptedOfflineClip2SubSample; + for (size_t i = 0; i < data->num_of_subsamples; i++) { + std::vector decrypt_buffer((data + i)->encrypt_data.size()); + CdmDecryptionParameters decryption_parameters( + &(data + i)->key_id, &(data + i)->encrypt_data.front(), + (data + i)->encrypt_data.size(), &(data + i)->iv, + (data + i)->block_offset, &decrypt_buffer[0]); + decryption_parameters.is_encrypted = (data + i)->is_encrypted; + decryption_parameters.is_secure = (data + i)->is_secure; + decryption_parameters.subsample_flags = (data + i)->subsample_flags; + EXPECT_EQ(NO_ERROR, + decryptor_.Decrypt(session_id_, (data + i)->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal((data + i)->decrypt_data.begin(), + (data + i)->decrypt_data.end(), + decrypt_buffer.begin())); + } + + sleep(10); + decryptor_.CloseSession(session_id_); + + sleep(kMinute - 10); + expected_seconds_since_license_received += kMinute; + expected_seconds_since_initial_playback += kMinute; + expected_seconds_since_last_playback = kMinute; + } + + session_id_.clear(); + decryptor_.OpenSession(g_key_system, NULL, &session_id_); + EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); + + // Query and validate usage information + int64_t license_duration_remaining = 0; + int64_t playback_duration_remaining = 0; + QueryKeyStatus(false, &license_duration_remaining, + &playback_duration_remaining); + + int64_t delta = + initial_license_duration_remaining - license_duration_remaining; + EXPECT_LT(expected_seconds_since_license_received - kClockTolerance, delta); + EXPECT_LT(delta, expected_seconds_since_license_received + kClockTolerance); + delta = initial_playback_duration_remaining - playback_duration_remaining; + EXPECT_LT(expected_seconds_since_initial_playback - kClockTolerance, delta); + EXPECT_LT(delta, expected_seconds_since_initial_playback + kClockTolerance); + + decryptor_.CloseSession(session_id_); + + session_id_.clear(); + key_set_id_.clear(); + GenerateKeyRelease(key_set_id); + ValidateReleaseRequest(key_msg_, expected_seconds_since_license_received, + expected_seconds_since_initial_playback, + expected_seconds_since_last_playback); + key_set_id_ = key_set_id; + VerifyKeyRequestResponse(g_license_server, g_client_auth, false); +} + +INSTANTIATE_TEST_CASE_P(Cdm, WvCdmOfflineUsageReportTest, + ::testing::Values(0, 1, 2)); +} // namespace wvcdm + +void show_menu(char* prog_name) { + std::cout << std::endl; + std::cout << "usage: " << prog_name << " [options]" << std::endl << std::endl; + std::cout << " enclose multiple arguments in '' when using adb shell" + << std::endl; + std::cout << " e.g. adb shell '" << prog_name << " --server=\"url\"'" + << std::endl; + std::cout << " or adb shell '" << prog_name << " -u\"url\"'" << std::endl + << std::endl; + + std::cout << std::setw(35) << std::left << " -f/--use_full_path"; + std::cout << "specify server url is not a proxy server" << std::endl; + std::cout << std::endl; + + std::cout << std::setw(35) << std::left << " -i/--license_server_id="; + std::cout << "specifies which default server settings to use: " << std::endl; + std::cout << std::setw(35) << std::left << " "; + std::cout << "gp (case sensitive) for GooglePlay server" << std::endl; + std::cout << std::setw(35) << std::left << " "; + std::cout << "cp (case sensitive) for Content Protection server" << std::endl + << std::endl; + + std::cout << std::setw(35) << std::left << " -k/--keyid="; + std::cout << "configure the key id or pssh, in hex format" << std::endl + << std::endl; + + std::cout << std::setw(35) << std::left << " -p/--port="; + std::cout << "specifies the connection port" << std::endl << std::endl; + + std::cout << std::setw(35) << std::left << " -s/--secure_transfer"; + std::cout << "use https transfer protocol" << std::endl << std::endl; + + std::cout << std::setw(35) << std::left << " -u/--server="; + std::cout + << "configure the license server url, please include http[s] in the url" + << std::endl << std::endl; +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + bool show_usage = false; + static const struct option long_options[] = { + {"keyid", required_argument, NULL, 'k'}, + {"license_server_id", required_argument, NULL, 'i'}, + {"license_server_url", required_argument, NULL, 'u'}, + {NULL, 0, NULL, '\0'}}; + + int option_index = 0; + int opt = 0; + while ((opt = getopt_long(argc, argv, "i:k:u:", long_options, + &option_index)) != -1) { + switch (opt) { + case 'i': { + std::string license_id(optarg); + if (!license_id.compare("gp")) { + g_license_server_id = wvcdm::kGooglePlayServer; + } else if (!license_id.compare("cp")) { + g_license_server_id = wvcdm::kContentProtectionServer; + } else { + std::cout << "Invalid license server id" << optarg << std::endl; + show_usage = true; + } + break; + } + case 'k': { + g_key_id.clear(); + g_key_id.assign(optarg); + break; + } + case 'u': { + g_license_server.clear(); + g_license_server.assign(optarg); + break; + } + case '?': { + show_usage = true; + break; + } + } + } + + if (show_usage) { + show_menu(argv[0]); + return 0; + } + + g_config = new wvcdm::ConfigTestEnv(g_license_server_id); + g_client_auth.assign(g_config->client_auth()); + g_key_system.assign(g_config->key_system()); + g_wrong_key_id.assign(g_config->wrong_key_id()); + + // The following variables are configurable through command line + // options. If the command line arguments are absent, use the settings + // in kLicenseServers[] pointed to by g_config. + if (g_key_id.empty()) { + g_key_id.assign(g_config->key_id()); + } + if (g_license_server.empty()) { + g_license_server.assign(g_config->license_server()); + } + + // Displays server url, port and key Id being used + std::cout << std::endl; + std::cout << "Server: " << g_license_server << std::endl; + std::cout << "KeyID: " << g_key_id << std::endl << std::endl; + + g_key_id = wvcdm::a2bs_hex(g_key_id); + g_config->set_license_server(g_license_server); + + int status = RUN_ALL_TESTS(); + delete g_config; + return status; +} diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 9eb33715..d6e1f25c 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -581,8 +581,49 @@ class WvCdmRequestLicenseTest : public testing::Test { } } + void Unprovision() { + EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(kSecurityLevelL1)); + EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(kSecurityLevelL3)); + } + + void Provision() { + CdmResponseType status = + decryptor_.OpenSession(g_key_system, NULL, &session_id_); + switch (status) { + case NO_ERROR: + decryptor_.CloseSession(session_id_); + return; + case NEED_PROVISIONING: + break; + default: + EXPECT_EQ(NO_ERROR, status); + return; + } + + std::string provisioning_server_url; + CdmCertificateType cert_type = kCertificateWidevine; + std::string cert_authority, cert, wrapped_key; + + status = decryptor_.GetProvisioningRequest( + cert_type, cert_authority, &key_msg_, &provisioning_server_url); + EXPECT_EQ(wvcdm::NO_ERROR, status); + if (NO_ERROR != status) return; + EXPECT_EQ(provisioning_server_url, g_config->provisioning_server_url()); + + std::string response = + GetCertRequestResponse(g_config->provisioning_server_url()); + EXPECT_NE(0, static_cast(response.size())); + EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.HandleProvisioningResponse( + response, &cert, &wrapped_key)); + EXPECT_EQ(0, static_cast(cert.size())); + EXPECT_EQ(0, static_cast(wrapped_key.size())); + decryptor_.CloseSession(session_id_); + return; + } + std::string GetSecurityLevel(TestWvCdmClientPropertySet* property_set) { - decryptor_.OpenSession(g_key_system, property_set, &session_id_); + EXPECT_EQ(NO_ERROR, + decryptor_.OpenSession(g_key_system, property_set, &session_id_)); CdmQueryMap query_info; EXPECT_EQ(wvcdm::NO_ERROR, decryptor_.QuerySessionStatus(session_id_, &query_info)); @@ -864,6 +905,9 @@ TEST_F(WvCdmRequestLicenseTest, AddStreamingKeyTest) { } TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) { + Unprovision(); + Provision(); + // override default settings unless configured through the command line std::string key_id; std::string client_auth; @@ -876,6 +920,9 @@ TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) { } TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) { + Unprovision(); + Provision(); + // override default settings unless configured through the command line std::string key_id; std::string client_auth; @@ -896,6 +943,9 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) { } TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) { + Unprovision(); + Provision(); + // override default settings unless configured through the command line std::string key_id; std::string client_auth; @@ -923,6 +973,9 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) { } TEST_F(WvCdmRequestLicenseTest, ReleaseRetryOfflineKeyTest) { + Unprovision(); + Provision(); + // override default settings unless configured through the command line std::string key_id; std::string client_auth; @@ -959,6 +1012,9 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseRetryOfflineKeyTest) { } TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) { + Unprovision(); + Provision(); + // override default settings unless configured through the command line std::string key_id; std::string client_auth; @@ -1034,7 +1090,7 @@ class WvCdmUsageInfoTest : public WvCdmRequestLicenseTest, public ::testing::WithParamInterface {}; -TEST_P(WvCdmUsageInfoTest, DISABLED_UsageInfo) { +TEST_P(WvCdmUsageInfoTest, UsageInfo) { CdmSecurityLevel security_level = GetDefaultSecurityLevel(); DeviceFiles handle; EXPECT_TRUE(handle.Init(security_level));