From 1dc1ff7e7c988725d17afbcdfd7debe14caccb8a Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Wed, 4 Aug 2021 21:59:18 +0000 Subject: [PATCH] Validate decryption with entitled keys in OEC tests The OEMCrypto tests have tests that verify that entitled keys can be loaded but not that they can be successfully used for decrypt. This patch adds a decrypt portion to the existing tests. As part of this, the existing Session::EncryptCTR() method and portions of Session::TestDecryptCTR() are lifted to be static functions so they can be shared across unrelated classes in oec_session_util.cpp. EncryptCTR() had no dependence on its enclosing class and is unchanged other than being moved outside the class. To reduce ambiguity with the new decrypt verification, this patch also renames EntitledMessage::VerifyEntitlementTestKeys() to the more-specific EntitledMessage::VerifyKCBs(). Its behavior is unchanged. Bug: 186782279 --- oemcrypto/test/oec_session_util.cpp | 143 ++++++++++++++++++---------- oemcrypto/test/oec_session_util.h | 10 +- 2 files changed, 96 insertions(+), 57 deletions(-) diff --git a/oemcrypto/test/oec_session_util.cpp b/oemcrypto/test/oec_session_util.cpp index 88c6787..6a3b85c 100644 --- a/oemcrypto/test/oec_session_util.cpp +++ b/oemcrypto/test/oec_session_util.cpp @@ -51,12 +51,67 @@ void PrintTo(const vector& value, ostream* os) { } } // namespace std -namespace { -constexpr size_t kTestSubsampleSectionSize = 256; -} // namespace - namespace wvoec { +namespace { + +constexpr size_t kTestSubsampleSectionSize = 256; + +// 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 +// the provided vectors and fills them with the expected and actual decrypt +// results. Returns the result of OEMCrypto_DecryptCENC(). +OEMCryptoResult DecryptCTR(OEMCrypto_SESSION session_id, const uint8_t* key, + vector* expected_data, + vector* actual_data) { + vector encrypted_data(kTestSubsampleSectionSize); + expected_data->resize(encrypted_data.size()); + actual_data->resize(encrypted_data.size()); + + // Create test sample description + OEMCrypto_SampleDescription sample_description; + OEMCrypto_SubSampleDescription subsample_description; + GenerateSimpleSampleDescription(encrypted_data, *actual_data, + &sample_description, &subsample_description); + + // Generate test data + EXPECT_EQ(GetRandBytes(expected_data->data(), expected_data->size()), 1); + EncryptCTR(*expected_data, key, &sample_description.iv[0], &encrypted_data); + + // Create the pattern description (always 0,0 for 'cenc') + OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; + + // Decrypt the data + return OEMCrypto_DecryptCENC(session_id, &sample_description, 1, &pattern); +} + +} // namespace + int GetRandBytes(unsigned char* buf, int num) { // returns 1 on success, -1 if not supported, or 0 if other failure. return RAND_bytes(buf, num); @@ -947,7 +1002,8 @@ void EntitledMessage::LoadKeys(OEMCryptoResult expected_sts) { if (expected_sts != OEMCrypto_SUCCESS) { return; } - VerifyEntitlementTestKeys(); + VerifyKCBs(); + VerifyDecrypt(); } OEMCryptoResult EntitledMessage::LoadKeys(const vector& message) { @@ -994,11 +1050,11 @@ void EntitledMessage::EncryptContentKey() { } } -// This function verifies that the key control block reported by OEMCrypto agree -// with the truth key control block. Failures in this function probably +// This function verifies that the key control blocks reported by OEMCrypto +// agree with the truth key control block. Failures in this function probably // indicate the OEMCrypto_LoadEntitledKeys did not correctly process the key // control block. -void EntitledMessage::VerifyEntitlementTestKeys() { +void EntitledMessage::VerifyKCBs() { for (unsigned int i = 0; i < num_keys_; i++) { EntitledContentKeyData* key_data = &entitled_key_data_[i]; const size_t entitlement_key_index = key_data->key_index; @@ -1025,6 +1081,28 @@ void EntitledMessage::VerifyEntitlementTestKeys() { } } +void EntitledMessage::VerifyDecrypt() { + const OEMCrypto_SESSION session_id = + license_messages_->session()->session_id(); + + // Loop through all the keys and try decrypt with each one. + for (unsigned int i = 0; i < num_keys_; i++) { + const EntitledContentKeyData* const key_data = &entitled_key_data_[i]; + + OEMCryptoResult result = OEMCrypto_SelectKey( + session_id, key_data->content_key_id, key_data->content_key_id_length, + OEMCrypto_CipherMode_CTR); + ASSERT_EQ(result, OEMCrypto_SUCCESS) << "For key " << i; + + vector expected_data; + vector actual_data; + result = DecryptCTR(session_id, key_data->content_key_data, &expected_data, + &actual_data); + EXPECT_EQ(result, OEMCrypto_SUCCESS) << "For key " << i; + EXPECT_EQ(actual_data, expected_data) << "For key " << i; + } +} + void RenewalRoundTrip::VerifyRequestSignature( const vector& data, const vector& generated_signature, size_t core_message_length) { @@ -1294,30 +1372,6 @@ void Session::GenerateDerivedKeysFromSessionKey() { key_deriver_.DeriveKeys(session_key.data(), mac_context, enc_context); } -void Session::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); - } -} - void Session::TestDecryptCTR(bool select_key_first, OEMCryptoResult expected_result, int key_index) { OEMCryptoResult select_result = OEMCrypto_SUCCESS; @@ -1328,27 +1382,12 @@ void Session::TestDecryptCTR(bool select_key_first, license_.keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR); } - // Create test sample description - vector unencrypted_data(kTestSubsampleSectionSize); - vector encrypted_data(unencrypted_data.size()); - vector output_buffer(unencrypted_data.size()); - OEMCrypto_SampleDescription sample_description; - OEMCrypto_SubSampleDescription subsample_description; - - GenerateSimpleSampleDescription(encrypted_data, output_buffer, - &sample_description, &subsample_description); - - // Generate test data - EXPECT_EQ(GetRandBytes(unencrypted_data.data(), unencrypted_data.size()), 1); - EncryptCTR(unencrypted_data, license_.keys[key_index].key_data, - &sample_description.iv[0], &encrypted_data); - - // Create the pattern description (always 0,0 for CTR) - OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; - - // Decrypt the data + vector unencrypted_data; + vector output_buffer; const OEMCryptoResult decrypt_result = - OEMCrypto_DecryptCENC(session_id(), &sample_description, 1, &pattern); + DecryptCTR(session_id(), license_.keys[key_index].key_data, + &unencrypted_data, &output_buffer); + // We only have a few errors that we test are reported. ASSERT_NO_FATAL_FAILURE( TestDecryptResult(expected_result, select_result, decrypt_result)) diff --git a/oemcrypto/test/oec_session_util.h b/oemcrypto/test/oec_session_util.h index 272e85e..d2bde5e 100644 --- a/oemcrypto/test/oec_session_util.h +++ b/oemcrypto/test/oec_session_util.h @@ -476,8 +476,6 @@ class EntitledMessage { void set_num_keys(uint32_t num_keys) { num_keys_ = num_keys; } uint32_t num_keys() const { return num_keys_; } void SetEntitlementKeyId(unsigned int index, const std::string& key_id); - // Verify that key control blocks of the loaded keys. - void VerifyEntitlementTestKeys(); OEMCrypto_EntitledContentKeyObject* entitled_key_array(); // Returns entitled_key_data_ which is used as input message buffer to // load entitled content keys API. @@ -487,6 +485,11 @@ class EntitledMessage { private: // Find the offset of the give pointer, relative to |entitled_key_data_|. OEMCrypto_Substring FindSubstring(const void* ptr, size_t size); + // Verify that key control blocks of the loaded keys matches their entitlement + // key. + void VerifyKCBs(); + // Verify that decryption with the entitled keys works. + void VerifyDecrypt(); LicenseRoundTrip* license_messages_; uint32_t num_keys_; @@ -528,9 +531,6 @@ class Session { // Generate known mac and enc keys using OEMCrypto_DeriveKeysFromSessionKey // and also fill out enc_key_, mac_key_server_, and mac_key_client_. void GenerateDerivedKeysFromSessionKey(); - // 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); // Encrypt some data and pass to OEMCrypto_DecryptCENC to verify decryption. void TestDecryptCTR(bool select_key_first = true, OEMCryptoResult expected_result = OEMCrypto_SUCCESS,