diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index 2c927e42..2517ffea 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -82,30 +82,6 @@ class FuzzedData { size_t source_size_; }; -// Encrypt a block of data using CTR mode. -void EncryptCTR(const vector& in_buffer, const uint8_t* key, - const uint8_t* starting_iv, vector* out_buffer) { - ASSERT_NE(nullptr, key); - ASSERT_NE(nullptr, starting_iv); - ASSERT_NE(nullptr, out_buffer); - AES_KEY aes_key; - AES_set_encrypt_key(key, AES_BLOCK_SIZE * 8, &aes_key); - out_buffer->resize(in_buffer.size()); - - uint8_t iv[AES_BLOCK_SIZE]; // Current iv. - - memcpy(iv, &starting_iv[0], AES_BLOCK_SIZE); - size_t l = 0; // byte index into encrypted subsample. - while (l < in_buffer.size()) { - uint8_t aes_output[AES_BLOCK_SIZE]; - AES_encrypt(iv, aes_output, &aes_key); - for (size_t n = 0; n < AES_BLOCK_SIZE && l < in_buffer.size(); n++, l++) { - (*out_buffer)[l] = aes_output[n] ^ in_buffer[l]; - } - ctr128_inc64(1, iv); - } -} - // Uses OEMCrypto to decrypt some random data in 'cenc' mode. This function // assumes that the correct key is already selected in the session. It requires // the plaintext of that key so that it can encrypt the test data. It resizes @@ -138,6 +114,31 @@ OEMCryptoResult DecryptCTR(const vector& key_handle, } // namespace + +// Encrypt a block of data using CTR mode. +void EncryptCTR(const vector& in_buffer, const uint8_t* key, + const uint8_t* starting_iv, vector* out_buffer) { + ASSERT_NE(nullptr, key); + ASSERT_NE(nullptr, starting_iv); + ASSERT_NE(nullptr, out_buffer); + AES_KEY aes_key; + AES_set_encrypt_key(key, AES_BLOCK_SIZE * 8, &aes_key); + out_buffer->resize(in_buffer.size()); + + uint8_t iv[AES_BLOCK_SIZE]; // Current iv. + + memcpy(iv, &starting_iv[0], AES_BLOCK_SIZE); + size_t l = 0; // byte index into encrypted subsample. + while (l < in_buffer.size()) { + uint8_t aes_output[AES_BLOCK_SIZE]; + AES_encrypt(iv, aes_output, &aes_key); + for (size_t n = 0; n < AES_BLOCK_SIZE && l < in_buffer.size(); n++, l++) { + (*out_buffer)[l] = aes_output[n] ^ in_buffer[l]; + } + ctr128_inc64(1, iv); + } +} + int GetRandBytes(unsigned char* buf, size_t num) { // returns 1 on success, -1 if not supported, or 0 if other failure. return RAND_bytes(buf, static_cast(num)); @@ -1231,6 +1232,12 @@ void EntitledMessage::MakeOneKey(size_t entitlement_key_index) { sizeof(key_data->content_key_data_iv))); offsets->content_key_data_iv = FindSubstring( key_data->content_key_data_iv, sizeof(key_data->content_key_data_iv)); + + EXPECT_EQ(1, GetRandBytes(key_data->content_iv, + sizeof(key_data->content_iv))); + key_data->content_iv_length = sizeof(key_data->content_iv); + offsets->content_iv = FindSubstring( + key_data->content_iv, key_data->content_iv_length); } OEMCrypto_EntitledContentKeyObject* EntitledMessage::entitled_key_array() { @@ -1373,14 +1380,14 @@ void EntitledMessage::LoadCasKeys(bool load_even, bool load_odd, even_key.content_key_id = entitled_key_array_[0].content_key_id; even_key.content_key_data_iv = entitled_key_array_[0].content_key_data_iv; even_key.content_key_data = entitled_key_array_[0].content_key_data; - even_key.content_iv.length = 0; + even_key.content_iv = entitled_key_array_[0].content_iv; } if (has_odd) { odd_key.entitlement_key_id = entitled_key_array_[1].entitlement_key_id; odd_key.content_key_id = entitled_key_array_[1].content_key_id; odd_key.content_key_data_iv = entitled_key_array_[1].content_key_data_iv; odd_key.content_key_data = entitled_key_array_[1].content_key_data; - odd_key.content_iv.length = 0; + even_key.content_iv = entitled_key_array_[1].content_iv; } OEMCryptoResult sts = OEMCrypto_LoadCasECMKeys( diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.h b/libwvdrmengine/oemcrypto/test/oec_session_util.h index 3d7839c4..ce3adfc9 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.h +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.h @@ -110,6 +110,8 @@ struct EntitledContentKeyData { uint8_t content_key_data_iv[KEY_IV_SIZE]; uint8_t content_key_data[KEY_SIZE]; uint8_t encrypted_content_key_data[KEY_SIZE]; + uint8_t content_iv[KEY_IV_SIZE]; + size_t content_iv_length; size_t key_index; // Index into the license's key array. Only for testing. }; @@ -121,6 +123,10 @@ void GenerateSimpleSampleDescription(const std::vector& in, OEMCrypto_SampleDescription* sample, OEMCrypto_SubSampleDescription* subsample); +// Encrypt a block of data using CTR mode. +void EncryptCTR(const vector& in_buffer, const uint8_t* key, + const uint8_t* starting_iv, vector* out_buffer); + // Increment counter for AES-CTR. The CENC spec specifies we increment only // the low 64 bits of the IV counter, and leave the high 64 bits alone. This // is different from the OpenSSL implementation, so we implement the CTR loop diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index e1088274..05bc48a1 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -1718,4 +1718,90 @@ TEST_F(OEMCryptoLoadsCertificate, /// @{ /// @} + +#ifdef CAS_TEST + +# include "tuner_hal.h" + +class OEMCryptoCasDemoTest : public OEMCryptoEntitlementLicenseTest {}; + +TEST_P(OEMCryptoCasDemoTest, BasicFlow) { + // License contains entitlement keys, function reused from + // OEMCryptoEntitlementLicenseTest + LoadEntitlementLicense(); + uint32_t key_session_id = 0; + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( + session_.session_id(), &key_session_id)); + + EntitledMessage entitled_message(&license_messages_); + + // Randomly generate entitled content keys + entitled_message.FillKeyArray(); + if (session_.session_id() == key_session_id) { + GTEST_SKIP() + << "Skipping test because entitled and entitlement sessions are both " + << key_session_id << "."; + } + entitled_message.SetEntitledKeySession(key_session_id); + + // Encrypt and load 0th key (even key) into OEMCrypto + ASSERT_NO_FATAL_FAILURE(entitled_message.LoadCasKeys( + /*load_even=*/true, /*load_odd=*/false, OEMCrypto_SUCCESS)); + + // + // Perform DecryptCTR() but for CAS + // + vector unencrypted_data(256, 0); + vector encrypted_data(256, 0); + vector output_buffer(256, 0); + unencrypted_data.resize(encrypted_data.size()); + output_buffer.resize(encrypted_data.size()); + + OEMCrypto_SampleDescription sample_description; + OEMCrypto_SubSampleDescription subsample_description; + GenerateSimpleSampleDescription(encrypted_data, output_buffer, + &sample_description, &subsample_description); + + // Use 0th entitled content key and IV to encrypt test data + EncryptCTR(unencrypted_data, + entitled_message.entitled_key_data()->content_key_data, + entitled_message.entitled_key_data()->content_iv, &encrypted_data); + + // Assume 0,0 pattern for CTR example + OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; + + // Demo only -- copy IV into sample description so we can use + // WTPI_DecryptSample() in the Tuner decrypt impl. A real implementation would + // use the IV from the entitled content key, but the demo relies on the + // existing decrypt which uses SampleDescription IV. + memcpy(sample_description.iv, + entitled_message.entitled_key_data()->content_iv, 16); + + // Get key token to send to Tuner for decrypt + std::vector key_token; + size_t key_token_length = key_token.size(); + OEMCryptoResult res = OEMCrypto_GetOEMKeyToken( + key_session_id, key_token.data(), &key_token_length); + if (res == OEMCrypto_ERROR_SHORT_BUFFER) { + key_token.resize(key_token_length); + res = OEMCrypto_GetOEMKeyToken(key_session_id, key_token.data(), + &key_token_length); + } + ASSERT_EQ(OEMCrypto_SUCCESS, res); + + // Decrypt the data + ASSERT_EQ(TUNER_HAL_SUCCESS, + TunerHal_Decrypt(key_token.data(), key_token_length, + TunerHal_KeyParityType_EvenKey, + &sample_description, // an array of samples. + 1, // the number of samples. + &pattern)); + + ASSERT_EQ(unencrypted_data, output_buffer); +} + +INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoCasDemoTest, + Range(kCoreMessagesAPI, kCurrentAPI + 1)); + +#endif } // namespace wvoec