Benchmarking and Unmasking
In this code drop we introduce the benchmarking tests that allow us to
compare the performance of different implementations. Like the other
tests, any implementation can link with them to create their own
binary.
There are two types of benchmarks:
1 - Throughput, which measures the speed that a function can process
information (bits per second). These are used for AEAD decrypt
and license white-box decrypt functions.
2 - Samples, which measures the min, 25% percentile, median, 75%
percentile, and max observed values. These is used for all other
functions as a way to measure the execute duration of a call.
The other change in this code drop is the update to the unmasking
function to only unmask a subset of the bytes in the masked buffer.
This was added to better align with the decoder behaviour in the CDM.
This commit is contained in:
@@ -59,6 +59,16 @@ cc_test(
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "aead_whitebox_benchmark",
|
||||
size = "small",
|
||||
deps = [
|
||||
":aead_whitebox",
|
||||
":test_data",
|
||||
"//api:aead_whitebox_benchmark",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "license_whitebox_test",
|
||||
size = "small",
|
||||
@@ -69,6 +79,16 @@ cc_test(
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "license_whitebox_benchmark",
|
||||
size = "small",
|
||||
deps = [
|
||||
":license_whitebox",
|
||||
":test_data",
|
||||
"//api:license_whitebox_benchmark",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "memory_util",
|
||||
srcs = ["memory_util.cc"],
|
||||
|
||||
@@ -54,31 +54,14 @@ struct WB_License_Whitebox {
|
||||
};
|
||||
|
||||
namespace {
|
||||
// Secret string value. For simplicity the pattern will just be 0xAAAAAAAA which
|
||||
// is xor'd to the buffer, and removed by xor'ing it a second time.
|
||||
// For simplicity we use a basic pad but we use a different byte for each
|
||||
// position as we need to support abirtary indexes and we want to make sure that
|
||||
// a wrong index will actually trigger a failure.
|
||||
const uint8_t kSecretStringPattern[] = {
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
};
|
||||
const size_t kSecretStringPatternSize = sizeof(kSecretStringPattern);
|
||||
static_assert(kSecretStringPatternSize == AES_BLOCK_SIZE,
|
||||
"Secret string must be AES_BLOCK_SIZE.");
|
||||
|
||||
void ApplyPattern(const uint8_t* input_buffer,
|
||||
size_t input_buffer_size,
|
||||
const uint8_t* pattern,
|
||||
size_t pattern_size,
|
||||
uint8_t* output_buffer) {
|
||||
DCHECK(input_buffer);
|
||||
DCHECK(pattern);
|
||||
DCHECK(output_buffer);
|
||||
DCHECK_GT(pattern_size, 0u);
|
||||
DCHECK_EQ(input_buffer_size % pattern_size, 0u);
|
||||
|
||||
for (size_t i = 0; i < input_buffer_size; ++i) {
|
||||
output_buffer[i] = input_buffer[i] ^ pattern[i % pattern_size];
|
||||
}
|
||||
}
|
||||
|
||||
const ContentKey* FindKey(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* id,
|
||||
@@ -681,22 +664,31 @@ WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
|
||||
|
||||
// Trivial implementation that simply takes the decrypted output and XORs it
|
||||
// with a fixed pattern. |output|'s size is based on |masked_output_data| so
|
||||
// we shouldn't need to worry about overflow.
|
||||
ApplyPattern(output.data(), output.size(), kSecretStringPattern,
|
||||
kSecretStringPatternSize, masked_output_data);
|
||||
// we shouldn't need to worry about overflow. This logic must be mirrored in
|
||||
// Unmask().
|
||||
const uint8_t* mask = kSecretStringPattern;
|
||||
const size_t mask_size = kSecretStringPatternSize;
|
||||
for (size_t i = 0; i < output.size(); ++i) {
|
||||
masked_output_data[i] = output[i] ^ mask[i % mask_size];
|
||||
}
|
||||
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
void WB_License_Unmask(const uint8_t* secret_string,
|
||||
void WB_License_Unmask(const uint8_t* masked_data,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
const uint8_t* secret_string,
|
||||
size_t secret_string_size,
|
||||
uint8_t* buffer,
|
||||
size_t buffer_size) {
|
||||
uint8_t* unmasked_data) {
|
||||
// No return code, so only check if parameters are valid.
|
||||
DCHECK(masked_data);
|
||||
DCHECK(secret_string);
|
||||
DCHECK(secret_string_size);
|
||||
DCHECK(buffer);
|
||||
DCHECK(buffer_size);
|
||||
DCHECK(unmasked_data);
|
||||
|
||||
ApplyPattern(buffer, buffer_size, secret_string, secret_string_size, buffer);
|
||||
for (size_t local_i = 0; local_i < size; local_i++) {
|
||||
const size_t global_i = offset + local_i;
|
||||
const uint8_t mask = secret_string[global_i % secret_string_size];
|
||||
unmasked_data[local_i] = masked_data[global_i] ^ mask;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user