From 9019e22b118d1799fa96690bace42acf951a3b10 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Fri, 17 May 2013 11:23:54 -0700 Subject: [PATCH] Key derivation failure on key release Signing and encryption keys are not correctly setup in OEMCrypto, when an offline license is restored, before generating a key release message. This results in key release failures. Playing back the license response causes keys to be derived and allows the key release message to be constructed. b/9016545 Merge of https://widevine-internal-review.googlesource.com/#/c/5682/ from the Widevine CDM repository Change-Id: Ica9f13acc7c87e3125fa706f3a56e95b77a14a3c --- libwvdrmengine/cdm/core/src/cdm_session.cpp | 9 +- .../cdm/test/request_license_test.cpp | 178 +++++++++++++++++- 2 files changed, 179 insertions(+), 8 deletions(-) diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 23368aa5..452d1f7b 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -98,11 +98,10 @@ CdmResponseType CdmSession::RestoreOfflineSession( } } - if (license_type == kLicenseTypeOffline) { - if (!license_parser_.RestoreOfflineLicense(offline_key_request_, - offline_key_response_, - offline_key_renewal_response_)) - return UNKNOWN_ERROR; + if (!license_parser_.RestoreOfflineLicense(offline_key_request_, + offline_key_response_, + offline_key_renewal_response_)) { + return UNKNOWN_ERROR; } license_received_ = true; diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 84963354..ee64410b 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -11,6 +11,7 @@ #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 { @@ -60,6 +61,20 @@ typedef struct DecryptionData { std::vector decrypt_data; } DecryptionData; +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 WvCdmRequestLicenseTest : public testing::Test { public: WvCdmRequestLicenseTest() {} @@ -91,6 +106,8 @@ class WvCdmRequestLicenseTest : public testing::Test { decryptor_.GenerateKeyRequest(session_id_, key_set_id, 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(0, static_cast(server_url.size())); } @@ -103,7 +120,6 @@ class WvCdmRequestLicenseTest : public testing::Test { decryptor_.GenerateKeyRequest(session_id, key_set_id, init_data, kLicenseTypeRelease, app_parameters, &key_msg_, &server_url)); - EXPECT_EQ(0, static_cast(server_url.size())); } void DumpResponse(const std::string& description, @@ -377,7 +393,7 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) { decryptor_.CloseSession(session_id_); } -TEST_F(WvCdmRequestLicenseTest, DISABLED_ReleaseOfflineKeyTest) { +TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) { decryptor_.OpenSession(g_key_system, &session_id_); GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, @@ -395,11 +411,43 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_ReleaseOfflineKeyTest) { session_id_.clear(); key_set_id_.clear(); GenerateKeyRelease(key_set_id); + key_set_id_ = key_set_id; VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, kServerSdkKeyId, false); } -TEST_F(WvCdmRequestLicenseTest, LicenseRenewal) { +TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); + VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, + kServerSdkKeyId, false); + CdmKeySetId key_set_id = key_set_id_; + EXPECT_FALSE(key_set_id_.empty()); + decryptor_.CloseSession(session_id_); + + session_id_.clear(); + key_set_id_.clear(); + decryptor_.OpenSession(g_key_system, &session_id_); + CdmSessionId restore_session_id = session_id_; + TestWvCdmEventListener listener; + EXPECT_TRUE(decryptor_.AttachEventListener(restore_session_id, &listener)); + EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(restore_session_id, + key_set_id)); + + session_id_.clear(); + key_set_id_.clear(); + EXPECT_TRUE(listener.session_id().size() == 0); + GenerateKeyRelease(key_set_id); + key_set_id_ = key_set_id; + EXPECT_TRUE(listener.session_id().size() != 0); + EXPECT_TRUE(listener.session_id().compare(restore_session_id) == 0); + EXPECT_TRUE(listener.event_type() == LICENSE_EXPIRED_EVENT); + VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, + kServerSdkKeyId, false); + decryptor_.CloseSession(restore_session_id); +} + +TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) { decryptor_.OpenSession(g_key_system, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); @@ -409,6 +457,18 @@ TEST_F(WvCdmRequestLicenseTest, LicenseRenewal) { decryptor_.CloseSession(session_id_); } +TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); + VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, + kServerSdkKeyId, false); + + GenerateRenewalRequest(g_key_system, kLicenseTypeOffline); + VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, + kServerSdkKeyId, true); + decryptor_.CloseSession(session_id_); +} + TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) { decryptor_.OpenSession(g_key_system, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); @@ -648,6 +708,118 @@ TEST_F(WvCdmRequestLicenseTest, DecryptionTest) { decryptor_.CloseSession(session_id_); } +TEST_F(WvCdmRequestLicenseTest, OfflineLicenseDecryptionTest) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); + VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, + kServerSdkKeyId, false); + + // key 1, encrypted, 256b + DecryptionData data; + data.is_encrypted = true; + data.is_secure = false; + data.key_id = wvcdm::a2bs_hex("30313233343536373839414243444546"); + data.encrypt_data = wvcdm::a2b_hex( + "b6d7d2430aa82b1cb8bd32f02e1f3b2a8d84f9eddf935ced5a6a98022cbb4561" + "8346a749fdb336858a64d7169fd0aa898a32891d14c24bed17fdc17fd62b8771" + "a8e22e9f093fa0f2aacd293d471b8e886d5ed8d0998ab2fde2d908580ff88c93" + "c0f0bbc14867267b3a3955bb6e7d05fca734a3aec3463d786d555cad83536ebe" + "4496d934d40df2aba5aea98c1145a2890879568ae31bb8a85d74714a4ad75785" + "7488523e697f5fd370eac746d56990a81cc76a178e3d6d65743520cdbc669412" + "9e73b86214256c67430cf78662346cab3e2bdd6f095dddf75b7fb3868c5ff5ff" + "3e1bbf08d456532ffa9df6e21a8bb2664c2d2a6d47ee78f9a6d53b2f2c8c087c"); + data.iv = wvcdm::a2b_hex("86856b9409743ca107b043e82068c7b6"); + data.block_offset = 0; + data.decrypt_data = wvcdm::a2b_hex( + "cc4a7fed8c5ac6e316e45317805c43e6d62a383ad738219c65e7a259dc12b46a" + "d50a3f8ce2facec8eeadff9cfa6b649212b88602b41f6d4c510c05af07fd523a" + "e7032634d9f8db5dd652d35f776376c5fc56e7031ed7cb28b72427fd4b367b6d" + "8c4eb6e46ed1249de5d24a61aeb08ebd60984c10581042ca8b0ef6bc44ec34a0" + "d4a77d68125c9bb1ace6f650e8716540f5b20d6482f7cfdf1b57a9ee9802160c" + "a632ce42934347410abc61bb78fba11b093498572de38bca96101ecece455e3b" + "5fef6805c44a2609cf97ce0dac7f15695c8058c590eda517f845108b90dfb29c" + "e73f3656000399f2fd196bc6fc225f3a7b8f578237751fd485ff070b5289e5cf"); + + std::vector decrypt_buffer; + size_t encrypt_length = data.encrypt_data.size(); + decrypt_buffer.resize(encrypt_length); + + EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, + data.is_encrypted, + data.is_secure, + data.key_id, + &data.encrypt_data.front(), + encrypt_length, + data.iv, + data.block_offset, + &decrypt_buffer.front(), + 0)); + + EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_.CloseSession(session_id_); +} + +TEST_F(WvCdmRequestLicenseTest, RestoreOfflineLicenseDecryptionTest) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, kServerSdkKeyId, kLicenseTypeOffline); + VerifyKeyRequestResponse(kServerSdkLicenseServer, kServerSdkClientAuth, + kServerSdkKeyId, false); + CdmKeySetId key_set_id = key_set_id_; + EXPECT_FALSE(key_set_id_.empty()); + decryptor_.CloseSession(session_id_); + + session_id_.clear(); + decryptor_.OpenSession(g_key_system, &session_id_); + EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); + + // key 1, encrypted, 256b + DecryptionData data; + data.is_encrypted = true; + data.is_secure = false; + data.key_id = wvcdm::a2bs_hex("30313233343536373839414243444546"); + data.encrypt_data = wvcdm::a2b_hex( + "b6d7d2430aa82b1cb8bd32f02e1f3b2a8d84f9eddf935ced5a6a98022cbb4561" + "8346a749fdb336858a64d7169fd0aa898a32891d14c24bed17fdc17fd62b8771" + "a8e22e9f093fa0f2aacd293d471b8e886d5ed8d0998ab2fde2d908580ff88c93" + "c0f0bbc14867267b3a3955bb6e7d05fca734a3aec3463d786d555cad83536ebe" + "4496d934d40df2aba5aea98c1145a2890879568ae31bb8a85d74714a4ad75785" + "7488523e697f5fd370eac746d56990a81cc76a178e3d6d65743520cdbc669412" + "9e73b86214256c67430cf78662346cab3e2bdd6f095dddf75b7fb3868c5ff5ff" + "3e1bbf08d456532ffa9df6e21a8bb2664c2d2a6d47ee78f9a6d53b2f2c8c087c"); + data.iv = wvcdm::a2b_hex("86856b9409743ca107b043e82068c7b6"); + data.block_offset = 0; + data.decrypt_data = wvcdm::a2b_hex( + "cc4a7fed8c5ac6e316e45317805c43e6d62a383ad738219c65e7a259dc12b46a" + "d50a3f8ce2facec8eeadff9cfa6b649212b88602b41f6d4c510c05af07fd523a" + "e7032634d9f8db5dd652d35f776376c5fc56e7031ed7cb28b72427fd4b367b6d" + "8c4eb6e46ed1249de5d24a61aeb08ebd60984c10581042ca8b0ef6bc44ec34a0" + "d4a77d68125c9bb1ace6f650e8716540f5b20d6482f7cfdf1b57a9ee9802160c" + "a632ce42934347410abc61bb78fba11b093498572de38bca96101ecece455e3b" + "5fef6805c44a2609cf97ce0dac7f15695c8058c590eda517f845108b90dfb29c" + "e73f3656000399f2fd196bc6fc225f3a7b8f578237751fd485ff070b5289e5cf"); + + std::vector decrypt_buffer; + size_t encrypt_length = data.encrypt_data.size(); + decrypt_buffer.resize(encrypt_length); + + EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, + data.is_encrypted, + data.is_secure, + data.key_id, + &data.encrypt_data.front(), + encrypt_length, + data.iv, + data.block_offset, + &decrypt_buffer.front(), + 0)); + + EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), + decrypt_buffer.begin())); + + decryptor_.CloseSession(session_id_); +} + TEST_F(WvCdmRequestLicenseTest, SwitchKeyDecryptionTest) { decryptor_.OpenSession(g_key_system, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);