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:
182
api/license_whitebox_decrypt_benchmark.cc
Normal file
182
api/license_whitebox_decrypt_benchmark.cc
Normal file
@@ -0,0 +1,182 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
#include "api/license_whitebox_benchmark.h"
|
||||
#include "api/result.h"
|
||||
#include "api/test_data.h"
|
||||
#include "api/test_license_builder.h"
|
||||
#include "base/logging.h"
|
||||
#include "benchmarking/data_source.h"
|
||||
#include "benchmarking/measurements.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace widevine {
|
||||
namespace {
|
||||
// The mask size can be any size (depends on the implementation), so 256 should
|
||||
// be more than enough for any implementation.
|
||||
constexpr size_t kMaskSize = 256;
|
||||
} // namespace
|
||||
|
||||
// Test Parameter:
|
||||
// - The number of blocks given to each decrypt call. Each block will be 16
|
||||
// bytes.
|
||||
// - The number of iterations to run.
|
||||
class LicenseWhiteboxDecryptBenchmark
|
||||
: public LicenseWhiteboxBenchmark,
|
||||
public testing::WithParamInterface<std::tuple<size_t, size_t>> {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
LicenseWhiteboxBenchmark::SetUp();
|
||||
|
||||
// Extract all parameters.
|
||||
size_t blocks_per_call;
|
||||
std::tie(blocks_per_call, iterations_) = GetParam();
|
||||
|
||||
// We are using AES with no padding, the input and output will be the same
|
||||
// size.
|
||||
const size_t bytes_per_call = blocks_per_call * 16;
|
||||
ciphertext_ = Data().Get(bytes_per_call);
|
||||
masked_text_.resize(bytes_per_call);
|
||||
plaintext_.resize(bytes_per_call);
|
||||
|
||||
const auto init_data = GetLicenseInitData();
|
||||
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
|
||||
WB_RESULT_OK);
|
||||
|
||||
const auto license = CreateLicense();
|
||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
||||
whitebox_, license.message.data(), license.message.size(),
|
||||
license.signature.data(), license.signature.size(),
|
||||
license.session_key.data(), license.session_key.size(),
|
||||
license.request.data(), license.request.size()),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
void TearDown() override { WB_License_Delete(whitebox_); }
|
||||
|
||||
WB_License_Whitebox* whitebox_;
|
||||
|
||||
std::vector<uint8_t> ciphertext_;
|
||||
std::vector<uint8_t> masked_text_;
|
||||
std::vector<uint8_t> plaintext_;
|
||||
|
||||
size_t iterations_;
|
||||
};
|
||||
|
||||
TEST_P(LicenseWhiteboxDecryptBenchmark, DecryptCBCThroughput) {
|
||||
Timer timer;
|
||||
timer.Reset();
|
||||
|
||||
for (size_t i = 0; i < iterations_; i++) {
|
||||
size_t plaintext_size = plaintext_.size();
|
||||
ASSERT_EQ(WB_RESULT_OK,
|
||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
ContentKeyId().data(), ContentKeyId().size(),
|
||||
ciphertext_.data(), ciphertext_.size(),
|
||||
ContentIV().data(), ContentIV().size(),
|
||||
plaintext_.data(), &plaintext_size));
|
||||
}
|
||||
|
||||
Throughput throughput(timer.Get(), iterations_ * ciphertext_.size());
|
||||
PrettyPrint("License Decrypt CBC Throughput", throughput, ciphertext_.size());
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxDecryptBenchmark, DecryptCTRThroughput) {
|
||||
Timer timer;
|
||||
timer.Reset();
|
||||
|
||||
for (size_t i = 0; i < iterations_; i++) {
|
||||
size_t plaintext_size = plaintext_.size();
|
||||
ASSERT_EQ(WB_RESULT_OK,
|
||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR,
|
||||
ContentKeyId().data(), ContentKeyId().size(),
|
||||
ciphertext_.data(), ciphertext_.size(),
|
||||
ContentIV().data(), ContentIV().size(),
|
||||
plaintext_.data(), &plaintext_size));
|
||||
}
|
||||
|
||||
Throughput throughput(timer.Get(), iterations_ * ciphertext_.size());
|
||||
PrettyPrint("License Decrypt CTR Throughput", throughput, ciphertext_.size());
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxDecryptBenchmark, MaskedDecryptCBCThroughput) {
|
||||
std::vector<uint8_t> mask(kMaskSize);
|
||||
size_t mask_size = mask.size();
|
||||
ASSERT_EQ(WB_RESULT_OK,
|
||||
WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, ContentKeyId().data(),
|
||||
ContentKeyId().size(), mask.data(), &mask_size));
|
||||
mask.resize(mask_size);
|
||||
|
||||
Timer timer;
|
||||
timer.Reset();
|
||||
|
||||
for (size_t i = 0; i < iterations_; i++) {
|
||||
// Include the unmask in the timing as it will be part of the flow from
|
||||
// encrypted to consumption.
|
||||
size_t plaintext_size = plaintext_.size();
|
||||
ASSERT_EQ(WB_RESULT_OK,
|
||||
WB_License_MaskedDecrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, ContentKeyId().data(),
|
||||
ContentKeyId().size(), ciphertext_.data(), ciphertext_.size(),
|
||||
ContentIV().data(), ContentIV().size(), masked_text_.data(),
|
||||
&plaintext_size));
|
||||
|
||||
WB_License_Unmask(masked_text_.data(), 0, plaintext_size, mask.data(),
|
||||
mask.size(), plaintext_.data());
|
||||
}
|
||||
|
||||
Throughput throughput(timer.Get(), iterations_ * ciphertext_.size());
|
||||
PrettyPrint("License Masked Decrypt CBC Throughput", throughput,
|
||||
ciphertext_.size());
|
||||
}
|
||||
|
||||
TEST_P(LicenseWhiteboxDecryptBenchmark, MaskedDecryptCTRThroughput) {
|
||||
std::vector<uint8_t> mask(kMaskSize);
|
||||
size_t mask_size = mask.size();
|
||||
ASSERT_EQ(WB_RESULT_OK,
|
||||
WB_License_GetSecretString(
|
||||
whitebox_, WB_CIPHER_MODE_CTR, ContentKeyId().data(),
|
||||
ContentKeyId().size(), mask.data(), &mask_size));
|
||||
mask.resize(mask_size);
|
||||
|
||||
Timer timer;
|
||||
timer.Reset();
|
||||
|
||||
for (size_t i = 0; i < iterations_; i++) {
|
||||
// Include the unmask in the timing as it will be part of the flow from
|
||||
// encrypted to consumption.
|
||||
size_t plaintext_size = plaintext_.size();
|
||||
ASSERT_EQ(WB_RESULT_OK,
|
||||
WB_License_MaskedDecrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CTR, ContentKeyId().data(),
|
||||
ContentKeyId().size(), ciphertext_.data(), ciphertext_.size(),
|
||||
ContentIV().data(), ContentIV().size(), masked_text_.data(),
|
||||
&plaintext_size));
|
||||
|
||||
WB_License_Unmask(masked_text_.data(), 0, plaintext_size, mask.data(),
|
||||
mask.size(), plaintext_.data());
|
||||
}
|
||||
|
||||
Throughput throughput(timer.Get(), iterations_ * ciphertext_.size());
|
||||
PrettyPrint("License Masked Decrypt CTR Throughput", throughput,
|
||||
ciphertext_.size());
|
||||
}
|
||||
|
||||
// The first value is the number of blocks. Each block is 16 bytes.
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
DifferentBytesPerCall,
|
||||
LicenseWhiteboxDecryptBenchmark,
|
||||
::testing::Values(std::make_tuple(1, 100), // 16 B
|
||||
std::make_tuple(16, 100), // 256 B
|
||||
std::make_tuple(64, 100), // 1 KB
|
||||
std::make_tuple(1024, 100), // 16 KB
|
||||
std::make_tuple(16 * 1024, 25), // 256 KB
|
||||
std::make_tuple(64 * 1024, 10))); // 1 MB
|
||||
} // namespace widevine
|
||||
Reference in New Issue
Block a user