Validate decryption with entitled keys in OEC tests
(This change is merged from http://go/wvgerrit/124825) 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 Test: x86-64 platform Test: opk_ta platform Test: build_and_run_all_unit_tests Change-Id: I15156882907b0987215087aaf43b4666fedc171a
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user