From d9d53dee3b16858252d090095f4cd0ce2871fe58 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Wed, 31 Jan 2018 00:19:00 -0800 Subject: [PATCH] Correct request_license_test failures [ Merge of http://go/wvgerrit/42103 ] * While deprecating keyboxes as identification, some code to restore a license was mistakenly removed in http:://go/wvgerrit/36740, http://ag/3442777 * Corrections to keep track of cipher mode, call SelectKeys when cipher mode changes and to use the backward compatible LoadKeys call in case OEMCrypto is v13. Bug: 70160032 Test: Ran WV unit/integration tests. Request license test failures have been addressed. Change-Id: Id03c50874085af6d9985d10c19a74a02efb7a1f5 --- .../cdm/core/include/content_key_session.h | 9 +- .../cdm/core/include/crypto_session.h | 4 +- .../cdm/core/src/content_key_session.cpp | 19 +- .../cdm/core/src/crypto_session.cpp | 30 ++- libwvdrmengine/cdm/core/src/license.cpp | 2 + .../cdm/core/src/sublicense_key_session.cpp | 18 +- .../cdm/test/request_license_test.cpp | 230 +++++++++++++++++- 7 files changed, 279 insertions(+), 33 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/content_key_session.h b/libwvdrmengine/cdm/core/include/content_key_session.h index 9d51f2c4..c2f87cdf 100644 --- a/libwvdrmengine/cdm/core/include/content_key_session.h +++ b/libwvdrmengine/cdm/core/include/content_key_session.h @@ -10,7 +10,9 @@ class ContentKeySession : public KeySession { public: ContentKeySession(CryptoSessionId oec_session_id, metrics::CryptoMetrics* metrics) - : KeySession(metrics), oec_session_id_(oec_session_id) {} + : KeySession(metrics), + oec_session_id_(oec_session_id), + cipher_mode_(kCipherModeCtr) {} virtual ~ContentKeySession() {} KeySessionType Type() { return kDefault; } @@ -32,7 +34,7 @@ class ContentKeySession : public KeySession { CdmCipherMode* cipher_mode, const std::string& srm_requirement); - OEMCryptoResult LoadEntitledContentKeys(const std::vector& keys) { + OEMCryptoResult LoadEntitledContentKeys(const std::vector&) { return OEMCrypto_ERROR_INVALID_CONTEXT; } @@ -56,8 +58,9 @@ class ContentKeySession : public KeySession { private: KeyId cached_key_id_; + CdmCipherMode cipher_mode_; }; } // namespace wvcdm -#endif // WVCDM_CORE_CONTENT_KEY_SESSSION_H_ \ No newline at end of file +#endif // WVCDM_CORE_CONTENT_KEY_SESSSION_H_ diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index 35f169a3..84da0724 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -29,6 +29,7 @@ void GenerateMacContext(const std::string& input_context, void GenerateEncryptContext(const std::string& input_context, std::string* deriv_context); size_t GetOffset(std::string message, std::string field); +OEMCryptoCipherMode ToOEMCryptoCipherMode(CdmCipherMode cipher_mode); class CryptoSession { public: @@ -272,8 +273,6 @@ class CryptoSession { bool is_destination_buffer_type_valid_; SecurityLevel requested_security_level_; - KeyId cached_key_id_; - bool is_usage_support_type_valid_; CdmUsageSupportType usage_support_type_; UsageTableHeader* usage_table_header_; @@ -284,6 +283,7 @@ class CryptoSession { static uint64_t request_id_index_; CdmCipherMode cipher_mode_; + uint32_t api_version_; CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession); }; diff --git a/libwvdrmengine/cdm/core/src/content_key_session.cpp b/libwvdrmengine/cdm/core/src/content_key_session.cpp index 5075fd9c..dc9f22c0 100644 --- a/libwvdrmengine/cdm/core/src/content_key_session.cpp +++ b/libwvdrmengine/cdm/core/src/content_key_session.cpp @@ -74,12 +74,14 @@ OEMCryptoResult ContentKeySession::LoadKeys( OEMCryptoResult ContentKeySession::SelectKey(const std::string& key_id, CdmCipherMode cipher_mode) { // Crypto session lock already locked. - if (!cached_key_id_.empty() && cached_key_id_ == key_id) { - // Already using the desired key. + if (!cached_key_id_.empty() && cached_key_id_ == key_id && + cipher_mode_ == cipher_mode) { + // Already using the desired key and cipher mode. return OEMCrypto_SUCCESS; } cached_key_id_ = key_id; + cipher_mode_ = cipher_mode; const uint8_t* key_id_string = reinterpret_cast(cached_key_id_.data()); @@ -87,7 +89,7 @@ OEMCryptoResult ContentKeySession::SelectKey(const std::string& key_id, OEMCryptoResult sts; M_TIME(sts = OEMCrypto_SelectKey( oec_session_id_, key_id_string, cached_key_id_.size(), - static_cast(cipher_mode)), + ToOEMCryptoCipherMode(cipher_mode)), metrics_, oemcrypto_select_key_, sts); if (OEMCrypto_SUCCESS != sts) { @@ -120,16 +122,17 @@ OEMCryptoResult ContentKeySession::LoadKeys( const uint8_t* msg = reinterpret_cast(message.data()); const uint8_t* enc_mac_key = NULL; const uint8_t* enc_mac_key_iv = NULL; + cached_key_id_.clear(); if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { enc_mac_key = msg + GetOffset(message, mac_key); enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); } else { LOGV("ContentKeySession::LoadKeys: enc_mac_key not set"); } - std::vector load_keys(keys.size()); + std::vector load_keys(keys.size()); for (size_t i = 0; i < keys.size(); ++i) { const CryptoKey* ki = &keys[i]; - OEMCrypto_KeyObject* ko = &load_keys[i]; + OEMCrypto_KeyObject_V13* ko = &load_keys[i]; ko->key_id = msg + GetOffset(message, ki->key_id()); ko->key_id_length = ki->key_id().length(); ko->key_data_iv = msg + GetOffset(message, ki->key_data_iv()); @@ -144,6 +147,8 @@ OEMCryptoResult ContentKeySession::LoadKeys( ko->key_control_iv = NULL; ko->key_control = NULL; } + ko->cipher_mode = ToOEMCryptoCipherMode(ki->cipher_mode()); + // TODO(jfore): Is returning the cipher needed. If not drop this. *cipher_mode = ki->cipher_mode(); } @@ -162,7 +167,7 @@ OEMCryptoResult ContentKeySession::LoadKeys( LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_); OEMCryptoResult sts; M_TIME( - sts = OEMCrypto_LoadKeys( + sts = ::OEMCrypto_LoadKeys_Back_Compat( oec_session_id_, msg, message.size(), reinterpret_cast(signature.data()), signature.size(), enc_mac_key_iv, enc_mac_key, keys.size(), &load_keys[0], pst, @@ -171,4 +176,4 @@ OEMCryptoResult ContentKeySession::LoadKeys( return sts; } -} // namespace wvcdm \ No newline at end of file +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index acda18e3..f167a22a 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -41,6 +41,9 @@ const uint32_t kRsaSignatureLength = 256; const size_t kMaximumChunkSize = 100 * 1024; // 100 KiB const size_t kEstimatedInitialUsageTableHeader = 40; const size_t kOemCryptoApiVersionSupportsBigUsageTables = 13; +// Ability to switch cipher modes in SelectKey() was introduced in this +// OEMCrypto version +const size_t kOemCryptoApiVersionSupportsSwitchingCipherMode = 14; // Constants and utility objects relating to OEM Certificates const int kExtensionOidSize = 64; @@ -126,6 +129,11 @@ OEMCrypto_LicenseType OEMCryptoLicenseType(CdmLicenseKeyType cdm_license_type) { : OEMCrypto_EntitlementLicense; } +OEMCryptoCipherMode ToOEMCryptoCipherMode(CdmCipherMode cipher_mode) { + return cipher_mode == kCipherModeCtr + ? OEMCrypto_CipherMode_CTR : OEMCrypto_CipherMode_CBC; +} + CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics) : metrics_(metrics), system_id_(-1), @@ -137,7 +145,8 @@ CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics) usage_support_type_(kUnknownUsageSupport), usage_table_header_(NULL), request_id_base_(0), - cipher_mode_(kCipherModeCtr) { + cipher_mode_(kCipherModeCtr), + api_version_(0) { assert(metrics); Init(); life_span_.Start(); @@ -664,6 +673,11 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { metrics_->oemcrypto_get_random_.Increment(random_sts); ++request_id_index_; + if (!GetApiVersion(&api_version_)) { + LOGE("CryptoSession::Open: GetApiVersion failed"); + return USAGE_SUPPORT_GET_API_FAILED; + } + CdmUsageSupportType usage_support_type; CdmResponseType result = GetUsageSupportType(&usage_support_type); if (result == NO_ERROR) { @@ -1071,8 +1085,10 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) { sts = CopyBufferInChunks(params, buffer_descriptor); } } - if (params.is_encrypted && params.cipher_mode != cipher_mode_) { - return INCORRECT_CRYPTO_MODE; + if (api_version_ < kOemCryptoApiVersionSupportsSwitchingCipherMode) { + if (params.is_encrypted && params.cipher_mode != cipher_mode_) { + return INCORRECT_CRYPTO_MODE; + } } if (params.is_encrypted || sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) { OEMCrypto_CENCEncryptPatternDesc pattern_descriptor; @@ -1928,14 +1944,8 @@ CdmResponseType CryptoSession::GetUsageSupportType( return NO_ERROR; } - uint32_t api_version = 0; - if (!GetApiVersion(&api_version)) { - LOGE("GetUsageSupportType: GetApiVersion failed"); - return USAGE_SUPPORT_GET_API_FAILED; - } - *usage_support_type = usage_support_type_ = - (api_version >= kOemCryptoApiVersionSupportsBigUsageTables) + (api_version_ >= kOemCryptoApiVersionSupportsBigUsageTables) ? kUsageEntrySupport : kUsageTableSupport; return NO_ERROR; diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index a9f1fdc1..18e5d729 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -955,6 +955,8 @@ bool CdmLicense::RestoreLicenseForRelease( if (!crypto_session_->GenerateDerivedKeys(key_request_, signed_response.session_key())) return false; + } else { + return KEY_ADDED == HandleKeyResponse(license_response); } if (license.policy().has_renewal_server_url()) diff --git a/libwvdrmengine/cdm/core/src/sublicense_key_session.cpp b/libwvdrmengine/cdm/core/src/sublicense_key_session.cpp index 57357d05..f839f6d2 100644 --- a/libwvdrmengine/cdm/core/src/sublicense_key_session.cpp +++ b/libwvdrmengine/cdm/core/src/sublicense_key_session.cpp @@ -100,7 +100,7 @@ OEMCryptoResult SubLicenseKeySession::SelectKey(const std::string& key_id, it->second, reinterpret_cast(keys_[i].key_id().data()), keys_[i].key_id().size(), - static_cast(cipher_mode)), + ToOEMCryptoCipherMode(cipher_mode)), metrics_, oemcrypto_select_key_, status); if (OEMCrypto_SUCCESS != status) { return status; @@ -184,7 +184,7 @@ OEMCryptoResult SubLicenseKeySession::DoLoadKeys( } for (size_t i = 0; i < keys.size(); i++) { - OEMCrypto_KeyObject key_object; + OEMCrypto_KeyObject_V13 key_object; const CryptoKey& key_data = keys[i]; key_object.key_id = msg + GetOffset(message, key_data.key_id()); key_object.key_id_length = key_data.key_id().length(); @@ -201,6 +201,8 @@ OEMCryptoResult SubLicenseKeySession::DoLoadKeys( key_object.key_control_iv = NULL; key_object.key_control = NULL; } + key_object.cipher_mode = ToOEMCryptoCipherMode(key_data.cipher_mode()); + // TODO(jfore): Does returning the cipher mode serve any purpose? // If not drop. *cipher_mode = key_data.cipher_mode(); @@ -215,7 +217,7 @@ OEMCryptoResult SubLicenseKeySession::DoLoadKeys( OEMCryptoResult sts; M_TIME( - sts = OEMCrypto_LoadKeys( + sts = OEMCrypto_LoadKeys_Back_Compat( oec_session_id->second, msg, message.size(), reinterpret_cast(signature.data()), signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, pst, @@ -230,7 +232,7 @@ OEMCryptoResult SubLicenseKeySession::DoLoadKeys( oec_session_id->second, reinterpret_cast(key_data.key_id().data()), key_data.key_id().size(), - static_cast(key_data.cipher_mode())), + ToOEMCryptoCipherMode(key_data.cipher_mode())), metrics_, oemcrypto_select_key_, sts); if (sts != OEMCrypto_SUCCESS) { @@ -312,7 +314,7 @@ OEMCryptoResult SubLicenseKeySession::DoSubLicenseLoadKeys( return sts; } - OEMCrypto_KeyObject key_object; + OEMCrypto_KeyObject_V13 key_object; key_object.key_id = msg + GetOffset(message, keys_[key_index].key_id()); key_object.key_id_length = keys_[key_index].key_id().length(); key_object.key_data_iv = @@ -325,9 +327,11 @@ OEMCryptoResult SubLicenseKeySession::DoSubLicenseLoadKeys( key_object.key_control = msg + GetOffset(message, keys_[key_index].key_control()); } + key_object.cipher_mode = + ToOEMCryptoCipherMode(keys_[key_index].cipher_mode()); M_TIME( - sts = OEMCrypto_LoadKeys( + sts = OEMCrypto_LoadKeys_Back_Compat( it->second, msg, message.size(), reinterpret_cast(signature.data()), signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, pst, @@ -342,7 +346,7 @@ OEMCryptoResult SubLicenseKeySession::DoSubLicenseLoadKeys( it->second, reinterpret_cast(keys_[key_index].key_id().data()), keys_[key_index].key_id().size(), - static_cast(keys_[key_index].cipher_mode())), + ToOEMCryptoCipherMode(keys_[key_index].cipher_mode())), metrics_, oemcrypto_select_key_, sts); return sts; diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 5590a431..8cf33cc7 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -871,6 +871,165 @@ HlsDecryptionInfo kHlsFourCCBackwardCompatibilityTestVectors[] = { {true, 1, &kSampleAes160ByteSegmentInfo[0], kHlsPsshSampleAesFourCC}, }; +const std::string kFullCencPssh = wvcdm::a2bs_hex( + "00000044" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000024" // pssh data size + "08011201321a0d7769646576696e655f" // pssh data + "74657374220a323031355f7465617273" + "2a024844"); + +const std::string kFullCbc1Pssh = wvcdm::a2bs_hex( + "00000053" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000033" // pssh data size + "12103030303030303030303030303030" // pssh data + "30321a0d7769646576696e655f746573" + "74220a323031355f746561727348b1c6" + "899b06"); + +struct Cenc30SampleInfo { + bool is_encrypted; + wvcdm::KeyId key_id; + std::string iv; + std::string clear_data; + std::string encrypted_data; + uint8_t subsample_flags; + wvcdm::CdmCipherMode cipher_mode; +}; + +Cenc30SampleInfo kCenc30CencKey33Sample = { + true, wvcdm::a2bs_hex("30303030303030303030303030303033"), + wvcdm::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), + wvcdm::a2bs_hex( + "011E88387D58EBB8E5CDCC38D431EEF4B6094B9201F200932F8EB5E1A94FB0B977" + "FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203" + "4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056" + "E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827" + "CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC868701CB8BE038" + "15BBFFB95FD3A86F142127720A35234070799173B37219127141922CBA8CB2DC48" + "EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B" + "7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"), + wvcdm::a2bs_hex( + "E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977" + "FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203" + "4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056" + "E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827" + "CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD" + "A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48" + "EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B" + "7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"), + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample, + wvcdm::kCipherModeCtr, +}; + +Cenc30SampleInfo kCenc30CencKey32Sample = { + true, wvcdm::a2bs_hex("30303030303030303030303030303032"), + wvcdm::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), + wvcdm::a2bs_hex( + "1B605E32B31D6245BCCC01C4E7720725B6094B9201F200932F8EB5E1A94FB0B977" + "FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203" + "4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056" + "E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827" + "CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687DBFDF7F684" + "3A552DCB7C38E461EDF5F3720A35234070799173B37219127141922CBA8CB2DC48" + "EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B" + "7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"), + wvcdm::a2bs_hex( + "E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977" + "FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203" + "4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056" + "E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827" + "CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD" + "A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48" + "EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B" + "7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"), + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample, + wvcdm::kCipherModeCtr, +}; + +Cenc30SampleInfo kCenc30Cbc1Key33Sample = { + true, wvcdm::a2bs_hex("30303030303030303030303030303033"), + wvcdm::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), + wvcdm::a2bs_hex( + "E300F37FEB0CDD9F276E67B971FF423003F3BF21DCF6100BA453A473A4522A19A8" + "2E098AA25511011D386FC7092FE3B407DF2BEB3AD57D5E1178F041E3FCABE25193" + "3F5EE35670CEB96BA3DAF922484F9A37773EF75D4B17FACC80B475004A6229AB4C" + "DFFA426468E578DE6A0285D942CDE476E06FF907D03F382552C2E14399C3FC2D21" + "9A59819FFF837EBC88A9F83A42D37F48ED8564EB40AC3BA8A6D2634A81F04FC2F1" + "379A45869036FD72B39C88222646AB7486A8416D78AB75951EB87ED1E16DF69209" + "A6966AC93C7BB65B85E429357A732CBC75F6EFB1781859FB771D60D11EB381D9CA" + "63F793507B02207810773FCABED0240E5BEEAD30116014E481"), + wvcdm::a2bs_hex( + "E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977" + "FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203" + "4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056" + "E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827" + "CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD" + "A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48" + "EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B" + "7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"), + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample, + wvcdm::kCipherModeCbc, +}; + +Cenc30SampleInfo kCenc30Cbc1Key32Sample = { + true, wvcdm::a2bs_hex("30303030303030303030303030303032"), + wvcdm::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), + wvcdm::a2bs_hex( + "4392E38BAE263267ED15394DE349AD1577F37B7D906C3A61536EE5A288F66F22F2" + "F5098964B7F2860A848C3C4FD30E538B3BCD2E700DC3FBC1657A6E9EAE44DE97C4" + "6F27C82A49198EE185D92931F093C3342FDBF6CF8203E18CCDC4B88E79C95EC052" + "3FD10F9409945349169FAA8F6A37179D2BEDC04A158A09BCBF742DA05245428788" + "E972B9B465FED5849AEDDB74B8919673C0C8829B5B062A38B3146CB8D497F03A4D" + "5C0A1D463504C1F116A811EF32503695B8FF78D9E93CDF7B2F7493E8043D4DE110" + "FE1D342D1C0175BF1466A544FC0D02DD0E314098256DD65B48098323C3AED9B7E0" + "CF260DBC5A0F09A46E39AE5E26A66ABFA52CBA26FBA83975E4"), + wvcdm::a2bs_hex( + "E7C566D86E98C36D2DCA54A966E7B469B6094B9201F200932F8EB5E1A94FB0B977" + "FAB8DFDAD57C677E279615F4EAFA872CA8EFF83179E4DE2AB78E6B41A860C42203" + "4B875AC282406E03AC01F2E407A55DE38C6C35707F34B3319646FA016A01CE9056" + "E55D28C48ED72F10FA6625656ED62B758CBADA757DDC52533C9CBD54FC1A46F827" + "CC7B69BA66AE19A15D725FCBB972B23C83F95C0F00F481A7C9AC8687B3A4AD15AD" + "A2ABBB84D8E3CBEC3E8771720A35234070799173B37219127141922CBA8CB2DC48" + "EC2477832D1AE477942DCDA93C0886AF72D71E56DA3D7737E49670B024639A195B" + "7377C7F45A797C6E5DBB1BB2843DA3FC76043E33687BEF3172"), + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample, + wvcdm::kCipherModeCbc, +}; + +struct Cenc30SwitchCipherInfo { + const std::string pssh; + Cenc30SampleInfo sample_info[4]; +}; + +Cenc30SwitchCipherInfo kCenc30SwitchCipherData[8] = { + // Switch between cipher modes + { kFullCencPssh, { kCenc30CencKey33Sample, kCenc30Cbc1Key33Sample, + kCenc30CencKey33Sample, kCenc30Cbc1Key33Sample, } }, + { kFullCbc1Pssh, { kCenc30Cbc1Key33Sample, kCenc30CencKey33Sample, + kCenc30Cbc1Key33Sample, kCenc30CencKey33Sample, } }, + // Switch between cipher modes, but the first sample has a cipher mode + // that differs with the protection scheme in the pssh + { kFullCencPssh, { kCenc30Cbc1Key33Sample, kCenc30CencKey33Sample, + kCenc30Cbc1Key33Sample, kCenc30CencKey33Sample, } }, + { kFullCbc1Pssh, { kCenc30CencKey33Sample, kCenc30Cbc1Key33Sample, + kCenc30CencKey33Sample, kCenc30Cbc1Key33Sample, } }, + // Switch between cipher modes and keys + { kFullCencPssh, { kCenc30CencKey33Sample, kCenc30CencKey32Sample, + kCenc30Cbc1Key33Sample, kCenc30Cbc1Key32Sample, } }, + { kFullCencPssh, { kCenc30Cbc1Key33Sample, kCenc30Cbc1Key32Sample, + kCenc30CencKey33Sample, kCenc30CencKey32Sample, } }, + { kFullCbc1Pssh, { kCenc30Cbc1Key33Sample, kCenc30Cbc1Key32Sample, + kCenc30CencKey33Sample, kCenc30CencKey32Sample, } }, + { kFullCbc1Pssh, { kCenc30CencKey33Sample, kCenc30CencKey32Sample, + kCenc30Cbc1Key33Sample, kCenc30Cbc1Key32Sample, } }, +}; + } // namespace namespace wvcdm { @@ -1055,7 +1214,6 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase { // when appropriate. std::string init_data; wvcdm::CdmAppParameterMap app_parameters; - wvcdm::CdmKeyRequestType key_request_type; CdmKeyRequest key_request; EXPECT_EQ(wvcdm::KEY_MESSAGE, decryptor_.GenerateKeyRequest( @@ -2496,7 +2654,6 @@ TEST_P(WvCdmUsageTest, WithClientId) { decrypt_buffer.begin())); decryptor_.CloseSession(session_id_); - uint32_t num_usage_info = 0; CdmUsageInfo usage_info; CdmUsageInfoReleaseMessage release_msg; CdmResponseType status = decryptor_.GetUsageInfo( @@ -2582,7 +2739,6 @@ TEST_F(WvCdmRequestLicenseTest, UsageInfoRetryTest) { decrypt_buffer.begin())); decryptor_.CloseSession(session_id_); - uint32_t num_usage_info = 0; CdmUsageInfo usage_info; CdmUsageInfoReleaseMessage release_msg; CdmResponseType status = decryptor_.GetUsageInfo( @@ -2680,7 +2836,6 @@ TEST_P(WvCdmUsageInfoTest, UsageInfo) { decryptor_.CloseSession(session_id_); } - uint32_t num_usage_info = 0; CdmUsageInfo usage_info; CdmUsageInfoReleaseMessage release_msg; CdmResponseType status = @@ -3705,6 +3860,73 @@ INSTANTIATE_TEST_CASE_P( ::testing::Range(&kHlsFourCCBackwardCompatibilityTestVectors[0], &kHlsFourCCBackwardCompatibilityTestVectors[4])); +class WvCenc30SwitchCipherModeTest + : public WvCdmRequestLicenseTest, + public ::testing::WithParamInterface {}; + +TEST_P(WvCenc30SwitchCipherModeTest, DecryptionTest) { + Provision(kLevel3); + TestWvCdmClientPropertySet client_property_set; + client_property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); + + std::string value; + EXPECT_EQ(wvcdm::NO_ERROR, + decryptor_.QueryStatus( + kLevel3, wvcdm::QUERY_KEY_OEMCRYPTO_API_VERSION, &value)); + std::istringstream ss(value); + uint32_t api_version; + ss >> api_version; + ASSERT_FALSE(ss.fail()); + EXPECT_TRUE(ss.eof()); + + // Ability to switch between cipher modes without re-requesting a license + // was introduced in OEMCrypto v14 + if (api_version < 14) + return; + + Cenc30SwitchCipherInfo* info = GetParam(); + + TestWvCdmHlsEventListener listener; + decryptor_.OpenSession(g_key_system, &client_property_set, + kDefaultCdmIdentifier, &listener, &session_id_); + CdmAppParameterMap app_parameters; + GenerateKeyRequest(info->pssh, app_parameters, + kLicenseTypeStreaming, NULL); + VerifyKeyRequestResponse(g_license_server, g_client_auth); + CdmKeyStatusMap key_status_map = listener.GetKeyStatusMap(); + EXPECT_EQ(8u, key_status_map.size()); + + for (size_t i = 0; i < N_ELEM(info->sample_info); ++i) { + Cenc30SampleInfo* data = &info->sample_info[i]; + std::vector output_buffer(data->encrypted_data.size(), 0); + std::vector iv(data->iv.begin(), data->iv.end()); + CdmDecryptionParameters decryption_parameters( + &data->key_id, + reinterpret_cast(data->encrypted_data.c_str()), + data->encrypted_data.size(), &iv, 0, &output_buffer[0]); + decryption_parameters.is_encrypted = data->is_encrypted; + decryption_parameters.is_secure = false; + decryption_parameters.cipher_mode = data->cipher_mode; + if (data->cipher_mode == kCipherModeCtr) { + decryption_parameters.pattern_descriptor.encrypt_blocks = 1; + decryption_parameters.pattern_descriptor.skip_blocks = 9; + } + decryption_parameters.subsample_flags = data->subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, false, + decryption_parameters)); + EXPECT_EQ(data->clear_data, + std::string(reinterpret_cast(&output_buffer[0]), + output_buffer.size())); + } + + decryptor_.CloseSession(session_id_); +} + +INSTANTIATE_TEST_CASE_P( + Cdm, WvCenc30SwitchCipherModeTest, + ::testing::Range(&kCenc30SwitchCipherData[0], + &kCenc30SwitchCipherData[8])); + } // namespace wvcdm void show_menu(char* prog_name) {