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
This commit is contained in:
Fred Gylys-Colwell
2021-08-04 21:59:18 +00:00
parent 18f9284d88
commit 1dc1ff7e7c
2 changed files with 96 additions and 57 deletions

View File

@@ -51,12 +51,67 @@ void PrintTo(const vector<uint8_t>& 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<uint8_t>& in_buffer, const uint8_t* key,
const uint8_t* starting_iv, vector<uint8_t>* 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<uint8_t>* expected_data,
vector<uint8_t>* actual_data) {
vector<uint8_t> 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<uint8_t>& 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<uint8_t> expected_data;
vector<uint8_t> 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<uint8_t>& data, const vector<uint8_t>& 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<uint8_t>& in_buffer, const uint8_t* key,
const uint8_t* starting_iv,
vector<uint8_t>* 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<uint8_t> unencrypted_data(kTestSubsampleSectionSize);
vector<uint8_t> encrypted_data(unencrypted_data.size());
vector<uint8_t> 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<uint8_t> unencrypted_data;
vector<uint8_t> 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))

View File

@@ -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<uint8_t>& in_buffer, const uint8_t* key,
const uint8_t* starting_iv, vector<uint8_t>* 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,