From 650a0fdead17df522476db059ce6fec7a3538f41 Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Fri, 27 Jan 2017 15:20:03 -0800 Subject: [PATCH] Add Shared License bit to key control block Merge from widevine of http://go/wvgerrit/23184 This adds the shared license bit to the key control block for the reference code and the unit tests. b/31458046 Change-Id: I4e360ea5dd2e6cee145663d4ab4f384b65cac427 --- .../mock/src/oemcrypto_engine_mock.cpp | 18 +++++- .../mock/src/oemcrypto_engine_mock.h | 3 +- .../oemcrypto/mock/src/oemcrypto_key_mock.cpp | 2 + .../oemcrypto/mock/src/oemcrypto_key_mock.h | 1 + .../oemcrypto/test/oemcrypto_test.cpp | 62 ++++++++++++++----- 5 files changed, 68 insertions(+), 18 deletions(-) diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp index 282c0545..84bc20b9 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp @@ -433,6 +433,10 @@ OEMCryptoResult SessionContext::LoadKeys( StartTimer(); + // If there are already keys installed in this session, then we can load + // a shared license. + bool second_license = (session_keys_.size() > 0); + // Decrypt and install keys in key object // Each key will have a key control block. They will all have the same nonce. OEMCryptoResult status = OEMCrypto_SUCCESS; @@ -459,7 +463,7 @@ OEMCryptoResult SessionContext::LoadKeys( OEMCryptoResult result = InstallKey( key_id, enc_key_data, key_data_iv, key_control, key_control_iv, - key_array[i].cipher_mode == OEMCrypto_CipherMode_CTR); + key_array[i].cipher_mode == OEMCrypto_CipherMode_CTR, second_license); if (result != OEMCrypto_SUCCESS) { status = result; break; @@ -505,7 +509,7 @@ OEMCryptoResult SessionContext::LoadKeys( return OEMCrypto_ERROR_WRONG_PST; } if (!usage_entry_->VerifyMacKeys(mac_key_server_, mac_key_client_)) { - LOGE("LoadKeys: Usage table entry does not match.\n"); + LOGE("LoadKeys: Usage table entry mac keys do not match.\n"); return OEMCrypto_ERROR_WRONG_KEYS; } if (usage_entry_->Inactive()) return OEMCrypto_ERROR_LICENSE_INACTIVE; @@ -519,7 +523,8 @@ OEMCryptoResult SessionContext::InstallKey( const KeyId& key_id, const std::vector& key_data, const std::vector& key_data_iv, const std::vector& key_control, - const std::vector& key_control_iv, bool ctr_mode) { + const std::vector& key_control_iv, bool ctr_mode, + bool second_license) { // Decrypt encrypted key_data using derived encryption key and offered iv std::vector content_key; std::vector key_control_str; @@ -579,6 +584,13 @@ OEMCryptoResult SessionContext::InstallKey( return result; } + if (key_control_block.control_bits() & kSharedLicense) { + if (!second_license) { + LOGE("LoadKeys: Shared License, but no keys previously loaded."); + return OEMCrypto_ERROR_MISSING_MASTER; + } + } + Key key(content_key, key_control_block, ctr_mode); session_keys_.Insert(key_id, key); return OEMCrypto_SUCCESS; diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h index 49e5ad66..aeff4f68 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h @@ -44,6 +44,7 @@ class SessionKeyTable { Key* Find(const KeyId key_id); void Remove(const KeyId key_id); void UpdateDuration(const KeyControlBlock& control); + size_t size() const { return keys_.size(); } private: KeyMap keys_; @@ -144,7 +145,7 @@ class SessionContext { const std::vector& key_data_iv, const std::vector& key_control, const std::vector& key_control_iv, - bool ctr_mode); + bool ctr_mode, bool second_license); bool InstallRSAEncryptedKey(const uint8_t* encrypted_message_key, size_t encrypted_message_key_length); bool DecryptRSAKey(const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp index afba52c7..8bfafb96 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp @@ -59,6 +59,8 @@ KeyControlBlock::KeyControlBlock( LOGD(" nonce: %08X", nonce()); LOGD(" magic: %08X", verification()); LOGD(" bits: %08X", control_bits()); + LOGD(" bit kSharedLicense %s.", + (control_bits() & kSharedLicense) ? "set" : "unset"); LOGD(" bit kControlSRMVersionRequired %s.", (control_bits() & kControlSRMVersionRequired) ? "set" : "unset"); LOGD(" bit kControlDisableAnalogOutput %s.", diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h index 0312327c..925fa517 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h @@ -15,6 +15,7 @@ const uint32_t kControlObserveDataPath = (1<<31); const uint32_t kControlObserveHDCP = (1<<30); const uint32_t kControlObserveCGMS = (1<<29); const uint32_t kControlRequireAntiRollbackHardware = (1<<28); +const uint32_t kSharedLicense = (1<<23); const uint32_t kControlSRMVersionRequired = (1<<22); const uint32_t kControlDisableAnalogOutput = (1<<21); const uint32_t kControlSecurityPatchLevelShift = 15; diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 2dd4feca..e0d9a89d 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -5244,14 +5244,37 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { // This is a special case where a collection of licenses can be shared with // multiple devices. In order for this to work, a single session must first // load a device specific license, and then a shared content license. -#if 0 // TODO(fredgc,jfore): fix this in http://go/wvgerrit/23184/ TEST_F(UsageTableTest, LoadSharedLicense) { - // session_.generatersasignature. - // session_.GenerateNonce - // DeriveKeysFromSessionKey - (specify enc/mac keys. - // LoadKeys replay control = 2. loads new mac keys. - // LoadKeys replay control = 0. uses same mac key. - // check second loadkeys without first fails. + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // We will reuse the encrypted and signed message, so we don't call + // FillSimpleMessage again. + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, true)); + + // The second set of keys are in the shared license. They will have the + // same mac keys as the original license, so we leave that alone. + // We given them different key ids so we can test that they were loaded. + // For this test, we leave the key content the same -- in real life it + // will be different. + for (unsigned int i = 0; i < s.num_keys(); i++) { + memset(s.license().keys[i].key_id, 'A' + i, + s.license().keys[i].key_id_length); + s.license().keys[i].control.nonce = 0; + s.license().keys[i].control.control_bits = + htonl(wvoec_mock::kSharedLicense); + } + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, false)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_F(UsageTableTest, LoadSharedLicenseWithNoMaster) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); @@ -5259,21 +5282,32 @@ TEST_F(UsageTableTest, LoadSharedLicense) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, true)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); + // This time, we do NOT load the master license. This should + // generate an error below. + // ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, true)); + // The second set of keys are in the shared license. + // We given them different key ids so we can test that they were loaded. + // For this test, we leave the key content the same -- in real life it + // will be different. for (unsigned int i = 0; i < s.num_keys(); i++) { memset(s.license().keys[i].key_id, 'A' + i, s.license().keys[i].key_id_length); + s.license().keys[i].control.nonce = 0; + s.license().keys[i].control.control_bits = + htonl(wvoec_mock::kSharedLicense); } - // TODO(fredgc,jfore): Decide if first set of keys need to stay loaded, or if - // they are replaced. ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, false)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + uint8_t* pst_ptr = s.encrypted_license().pst; + ASSERT_EQ(OEMCrypto_ERROR_MISSING_MASTER, + OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), + s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, + s.num_keys(), s.key_array(), pst_ptr, pst.length(), NULL)); ASSERT_NO_FATAL_FAILURE(s.close()); } -#endif TEST_F(UsageTableTest, PSTLargeBuffer) { std::string pst(kMaxPSTLength, 'a'); // A large PST.