Update OEMCrypto test comments and logs

Merge from Widevine repo of http://go/wvgerrit/121886

This CL merges some changes from branch rvc-dev to sc-dev
that prepared it for merge.

One change is that the unit tests now say they are part of
Android S instead of R.

Bug: 180546871
Change-Id: I2ebbd8f7b8586389ebb75f3743a2dc2ad8caa214
This commit is contained in:
Fred Gylys-Colwell
2021-04-10 11:11:31 -07:00
parent d265a5fe73
commit 6628c7f693
8 changed files with 427 additions and 60 deletions

View File

@@ -137,6 +137,11 @@ void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f,
!check_status);
buffer_length *= 2) {
sts = f(buffer_length);
if (check_status && sts != OEMCrypto_SUCCESS &&
sts != OEMCrypto_ERROR_SHORT_BUFFER) {
LOGI("Test exits huge buffer loop for length:%zu, status:%d",
buffer_length, sts);
}
}
}
@@ -170,6 +175,17 @@ const size_t kLargeMessageSize[] = { 8*KiB, 8*KiB, 16*KiB, 32*KiB};
// const size_t kAV1NumberSubsamples[] = { 72, 144, 288, 576};
// clang-format on
// Return a printable string from data. If all the characters are printable,
// then just use the string. Otherwise, convert to hex.
std::string MaybeHex(const uint8_t* data, size_t length) {
for (size_t i = 0; i < length; i++) {
if (!isprint(data[i])) return "0x" + wvcdm::HexEncode(data, length);
}
return std::string(reinterpret_cast<const char*>(data), length);
}
std::string MaybeHex(const std::vector<uint8_t>& data) {
return MaybeHex(data.data(), data.size());
}
} // namespace
class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
@@ -202,6 +218,17 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
}
};
TEST_F(OEMCryptoClientTest, FreeUnallocatedSecureBufferNoFailure) {
Session s;
s.open();
OEMCrypto_DestBufferDesc output_descriptor;
int secure_fd = kHugeRandomNumber;
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_FreeSecureBuffer(s.session_id(), &output_descriptor,
secure_fd));
s.close();
}
/// @addtogroup basic
/// @{
@@ -212,15 +239,20 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
*/
TEST_F(OEMCryptoClientTest, VersionNumber) {
const std::string log_message =
"OEMCrypto unit tests for API 16.4. Tests last updated 2020-10-07";
"OEMCrypto unit tests for API 16.3 or 4. Tests last updated 2021-02-22";
cout << " " << log_message << "\n";
cout << " "
<< "These tests are part of Android S."
<< "\n";
LOGI("%s", log_message.c_str());
// If any of the following fail, then it is time to update the log message
// above.
EXPECT_EQ(ODK_MAJOR_VERSION, 16);
// Note on minor versions. Widevine requires version 16.3 or greater for CE
// CDM and Android devices. For CE CDM devices that do not support usage
// tables, we strongly recommend 16.4.
// tables, we strongly recommend 16.4. Note: This is the version of the ODK
// library built into the tests, which might be different from the version
// that is pre-compiled into liboemcrypto.so.
EXPECT_GE(ODK_MINOR_VERSION, 3);
EXPECT_LE(ODK_MINOR_VERSION, 4);
EXPECT_EQ(kCurrentAPI, 16u);
@@ -229,7 +261,9 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
ASSERT_EQ('L', level[0]);
cout << " OEMCrypto Security Level is " << level << endl;
uint32_t version = OEMCrypto_APIVersion();
cout << " OEMCrypto API version is " << version << endl;
uint32_t minor_version = OEMCrypto_MinorAPIVersion();
cout << " OEMCrypto API version is " << version << "."
<< minor_version << endl;
if (OEMCrypto_SupportsUsageTable()) {
cout << " OEMCrypto supports usage tables" << endl;
} else {
@@ -661,8 +695,8 @@ TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) {
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample));
dest_buffer_descriptor.buffer.clear.address = output_buffer.data();
dest_buffer_descriptor.buffer.clear.address_length = output_buffer.size() - 1;
ASSERT_EQ(
OEMCrypto_ERROR_SHORT_BUFFER,
ASSERT_NE(
OEMCrypto_SUCCESS,
OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(),
input_buffer.size(), &dest_buffer_descriptor,
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample));
@@ -896,8 +930,8 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) {
uint8_t dev_id[128] = {0};
size_t dev_id_len = 128;
sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len);
cout << " NormalGetDeviceId: dev_id = " << dev_id
<< " len = " << dev_id_len << endl;
cout << " NormalGetDeviceId: dev_id = "
<< MaybeHex(dev_id, dev_id_len) << " len = " << dev_id_len << endl;
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
@@ -1039,7 +1073,7 @@ TEST_F(OEMCryptoProv30Test, GetDeviceId) {
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
dev_id.resize(dev_id_len);
cout << " NormalGetDeviceId: dev_id = " << dev_id.data()
cout << " NormalGetDeviceId: dev_id = " << MaybeHex(dev_id)
<< " len = " << dev_id_len << endl;
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
@@ -2355,6 +2389,39 @@ TEST_P(OEMCryptoLicenseTest,
!kCheckStatus);
}
TEST_P(OEMCryptoLicenseTest,
DecryptCENCForNumBytesClearPlusEncryptedOverflowsSize) {
LoadLicense();
OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length,
OEMCrypto_CipherMode_CTR);
size_t input_buffer_size = 1;
vector<uint8_t> in_buffer(input_buffer_size);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
OEMCrypto_SubSampleDescription* sub_samples =
const_cast<OEMCrypto_SubSampleDescription*>(
sample_description.subsamples);
// If Decrypt cenc API does not check for overflow on clear + encrypted
// addition operation. This will result in 1 which will match with input data
// length, which causes validation to pass.
sub_samples[0].num_bytes_clear = 2;
sub_samples[0].num_bytes_encrypted = 0xFFFFFFFFFFFFFFFF;
// Create the pattern description (always 0,0 for CTR)
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
// Try to decrypt the data
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1,
&pattern));
}
TEST_P(OEMCryptoLicenseTest,
OEMCryptoMemoryDecryptCENCForHugeNumBytesEncryptedAndBuffers) {
TestDecryptCENCForHugeBufferLengths(
@@ -3850,50 +3917,17 @@ class OEMCryptoSessionTestsDecryptTests
OEMCrypto_SUCCESS);
}
void TestDecryptCENC() {
OEMCryptoResult sts;
void TestDecryptCENC() { ASSERT_EQ(DecryptCENC(), OEMCrypto_SUCCESS); }
// OEMCrypto only supports providing a decrypt hash for one sample.
if (samples_.size() > 1) verify_crc_ = false;
// If supported, check the decrypt hashes.
if (verify_crc_) {
const TestSample& sample = samples_[0];
uint32_t hash =
wvcrc32(sample.truth_buffer.data(), sample.truth_buffer.size());
ASSERT_EQ(OEMCrypto_SetDecryptHash(
session_.session_id(), 1,
reinterpret_cast<const uint8_t*>(&hash), sizeof(hash)),
OEMCrypto_SUCCESS);
}
// Build an array of just the sample descriptions.
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
sample_descriptions.reserve(samples_.size());
for (TestSample& sample : samples_) {
// This must be deferred until this point in case the test modifies the
// buffer before testing decrypt.
sample.description.buffers.input_data = sample.encrypted_buffer.data();
// Append to the description array.
sample_descriptions.push_back(sample.description);
}
// Perform decryption using the test data that was previously set up.
sts = DecryptFallbackChain::Decrypt(
session_.session_id(), sample_descriptions.data(),
sample_descriptions.size(), cipher_mode_, &pattern_);
ASSERT_EQ(sts, OEMCrypto_SUCCESS);
// Validate the decrypted data.
void ValidateDecryptedData() {
for (TestSample& sample : samples_) {
if (sample.description.buffers.output_descriptor.type ==
OEMCrypto_BufferType_Clear) {
const size_t total_size = sample.description.buffers.input_data_length;
// To verify there is no buffer overrun after decrypting, look at the
// padded bytes just after the data buffer that was written. It should
// not have changed from the original 0xaa that we set in MakeBuffer
// function.
// padded bytes just after the data buffer that was written. It
// should not have changed from the original 0xaa that we set in
// MakeBuffer function.
if (decrypt_inplace_) {
EXPECT_EQ(std::count(sample.encrypted_buffer.begin() + total_size,
sample.encrypted_buffer.end(), 0xaa),
@@ -3919,6 +3953,41 @@ class OEMCryptoSessionTestsDecryptTests
}
}
OEMCryptoResult DecryptCENC() {
// OEMCrypto only supports providing a decrypt hash for one sample.
if (samples_.size() > 1) verify_crc_ = false;
// If supported, check the decrypt hashes.
if (verify_crc_) {
const TestSample& sample = samples_[0];
uint32_t hash =
wvcrc32(sample.truth_buffer.data(), sample.truth_buffer.size());
OEMCrypto_SetDecryptHash(session_.session_id(), 1,
reinterpret_cast<const uint8_t*>(&hash),
sizeof(hash));
}
// Build an array of just the sample descriptions.
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
sample_descriptions.reserve(samples_.size());
for (TestSample& sample : samples_) {
// This must be deferred until this point in case the test modifies the
// buffer before testing decrypt.
sample.description.buffers.input_data = sample.encrypted_buffer.data();
// Append to the description array.
sample_descriptions.push_back(sample.description);
}
// Perform decryption using the test data that was previously set up.
OEMCryptoResult result = DecryptFallbackChain::Decrypt(
session_.session_id(), sample_descriptions.data(),
sample_descriptions.size(), cipher_mode_, &pattern_);
if (result != OEMCrypto_SUCCESS) return result;
ValidateDecryptedData();
return result;
}
// Parameters of test case
OEMCrypto_CENCEncryptPatternDesc pattern_;
OEMCryptoCipherMode cipher_mode_;
@@ -4094,7 +4163,6 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSampleAPI16) {
// max_sample_size.
const size_t subsample_size =
std::min(max_sample_size / max_num_subsamples + 1, max_subsample_size);
size_t bytes_remaining = max_sample_size;
std::vector<SubsampleSize> subsample_sizes;
while (bytes_remaining > 0 && subsample_sizes.size() < max_num_subsamples) {
@@ -4113,6 +4181,103 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSampleAPI16) {
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
TEST_P(OEMCryptoSessionTestsDecryptTests,
OEMCryptoMemoryDecryptCENCForHugeNumberOfSubSamples) {
auto oemcrypto_function = [&](size_t number_of_subsamples) {
std::vector<SubsampleSize> subsample_sizes;
while (number_of_subsamples-- > 0) {
subsample_sizes.push_back({1, 1});
}
SetSubsampleSizes(subsample_sizes);
LoadLicense();
MakeBuffers();
EncryptData();
OEMCryptoResult result = DecryptCENC();
// Closing the session and opening it for next iteration.
// If it is last iteration, session will be closed in teardown method of
// class.
session_.close();
session_.open();
InstallTestRSAKey(&session_);
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, 1, 2 * MiB, kCheckStatus);
}
TEST_P(OEMCryptoSessionTestsDecryptTests,
OEMCryptoMemoryCheckDecryptCENCStatusForHugeNumberOfSubSamples) {
size_t number_of_subsamples = 10000;
std::vector<SubsampleSize> subsample_sizes;
while (number_of_subsamples-- > 0) {
subsample_sizes.push_back({100, 100});
}
SetSubsampleSizes(subsample_sizes);
LoadLicense();
MakeBuffers();
EncryptData();
// Build an array of just the sample descriptions.
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
sample_descriptions.reserve(samples_.size());
for (TestSample& sample : samples_) {
// This must be deferred until this point in case the test modifies the
// buffer before testing decrypt.
sample.description.buffers.input_data = sample.encrypted_buffer.data();
// Append to the description array.
sample_descriptions.push_back(sample.description);
}
OEMCryptoResult result = OEMCrypto_DecryptCENC(
session_.session_id(), sample_descriptions.data(), 1, &pattern_);
LOGD("Large number of subsamples test has return code %d", result);
}
TEST_P(OEMCryptoSessionTestsDecryptTests,
OEMCryptoMemoryCheckDecryptCENCStatusForHugeSubSample) {
std::vector<SubsampleSize> subsample_sizes;
subsample_sizes.push_back({100000, 100000});
SetSubsampleSizes(subsample_sizes);
LoadLicense();
MakeBuffers();
EncryptData();
// Build an array of just the sample descriptions.
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
sample_descriptions.reserve(samples_.size());
for (TestSample& sample : samples_) {
// This must be deferred until this point in case the test modifies the
// buffer before testing decrypt.
sample.description.buffers.input_data = sample.encrypted_buffer.data();
// Append to the description array.
sample_descriptions.push_back(sample.description);
}
OEMCryptoResult result = OEMCrypto_DecryptCENC(
session_.session_id(), sample_descriptions.data(), 1, &pattern_);
LOGD("Large subsample test has return code %d", result);
}
TEST_P(OEMCryptoSessionTestsDecryptTests,
OEMCryptoMemoryDecryptCENCForHugeNumberOfSamples) {
auto oemcrypto_function = [&](size_t number_of_samples) {
std::vector<std::vector<SubsampleSize>> samples;
std::vector<SubsampleSize> subsample_sizes;
subsample_sizes.push_back({1, 1});
while (number_of_samples-- > 0) {
samples.push_back(subsample_sizes);
}
SetSampleSizes(samples);
LoadLicense();
MakeBuffers();
EncryptData();
OEMCryptoResult result = DecryptCENC();
// Closing the session and opening it for next iteration.
// If it is last iteration, session will be closed in teardown method of
// class.
session_.close();
session_.open();
InstallTestRSAKey(&session_);
return result;
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, 1, 2 * MiB, kCheckStatus);
}
// Based on the resource rating, OEMCrypto should be able to handle the maximum
// subsample size.
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) {