CAS demo app
Adds a new `cas` directory to the ports/linux project. This contains an end-to-end demo of OEMCrypto CAS functionality, using the Linux tee_simulator as a base. Test: from ports/linux/cas dir: `CDM_DIR=~/work/cdm-dupe ./scripts/build.sh && CDM_DIR=~/work/cdm-dupe ./scripts/run.sh` Merged from https://widevine-internal-review.googlesource.com/178250 Change-Id: I781b403100ad2e069d99650d9ddae8e7acbc309a
This commit is contained in:
committed by
Robert Shih
parent
7d989e3448
commit
0dbc42f10e
@@ -82,30 +82,6 @@ class FuzzedData {
|
|||||||
size_t source_size_;
|
size_t source_size_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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
|
// 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
|
// 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 plaintext of that key so that it can encrypt the test data. It resizes
|
||||||
@@ -138,6 +114,31 @@ OEMCryptoResult DecryptCTR(const vector<uint8_t>& key_handle,
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int GetRandBytes(unsigned char* buf, size_t num) {
|
int GetRandBytes(unsigned char* buf, size_t num) {
|
||||||
// returns 1 on success, -1 if not supported, or 0 if other failure.
|
// returns 1 on success, -1 if not supported, or 0 if other failure.
|
||||||
return RAND_bytes(buf, static_cast<int>(num));
|
return RAND_bytes(buf, static_cast<int>(num));
|
||||||
@@ -1231,6 +1232,12 @@ void EntitledMessage::MakeOneKey(size_t entitlement_key_index) {
|
|||||||
sizeof(key_data->content_key_data_iv)));
|
sizeof(key_data->content_key_data_iv)));
|
||||||
offsets->content_key_data_iv = FindSubstring(
|
offsets->content_key_data_iv = FindSubstring(
|
||||||
key_data->content_key_data_iv, sizeof(key_data->content_key_data_iv));
|
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() {
|
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_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_iv = entitled_key_array_[0].content_key_data_iv;
|
||||||
even_key.content_key_data = entitled_key_array_[0].content_key_data;
|
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) {
|
if (has_odd) {
|
||||||
odd_key.entitlement_key_id = entitled_key_array_[1].entitlement_key_id;
|
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_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_iv = entitled_key_array_[1].content_key_data_iv;
|
||||||
odd_key.content_key_data = entitled_key_array_[1].content_key_data;
|
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(
|
OEMCryptoResult sts = OEMCrypto_LoadCasECMKeys(
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ struct EntitledContentKeyData {
|
|||||||
uint8_t content_key_data_iv[KEY_IV_SIZE];
|
uint8_t content_key_data_iv[KEY_IV_SIZE];
|
||||||
uint8_t content_key_data[KEY_SIZE];
|
uint8_t content_key_data[KEY_SIZE];
|
||||||
uint8_t encrypted_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.
|
size_t key_index; // Index into the license's key array. Only for testing.
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -121,6 +123,10 @@ void GenerateSimpleSampleDescription(const std::vector<uint8_t>& in,
|
|||||||
OEMCrypto_SampleDescription* sample,
|
OEMCrypto_SampleDescription* sample,
|
||||||
OEMCrypto_SubSampleDescription* subsample);
|
OEMCrypto_SubSampleDescription* subsample);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
// Increment counter for AES-CTR. The CENC spec specifies we increment only
|
// 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
|
// 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
|
// is different from the OpenSSL implementation, so we implement the CTR loop
|
||||||
|
|||||||
@@ -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<uint8_t> unencrypted_data(256, 0);
|
||||||
|
vector<uint8_t> encrypted_data(256, 0);
|
||||||
|
vector<uint8_t> 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<uint8_t> 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<uint32_t>(kCoreMessagesAPI, kCurrentAPI + 1));
|
||||||
|
|
||||||
|
#endif
|
||||||
} // namespace wvoec
|
} // namespace wvoec
|
||||||
|
|||||||
Reference in New Issue
Block a user