Revert "Cherry pick 18.4 changes to udc-widevine-dev"

This reverts commit 7186433edf.

Reason for revert: Build breakage - b/323194350

Change-Id: Ibba4f5289b8f0d5e835dfba9ceb1e601784af634
This commit is contained in:
Kensuke Miyagi
2024-01-31 18:13:05 +00:00
committed by Android (Google) Code Review
parent 7186433edf
commit 540c8dfd50
98 changed files with 258 additions and 135280 deletions

View File

@@ -1,121 +0,0 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include <assert.h>
#include <gtest/gtest.h>
#include <string.h>
#include <time.h>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#if defined(__linux__)
# include <sys/utsname.h>
#endif
#include "cdm.h"
#include "cdm_test_runner.h"
#include "clock.h"
#include "log.h"
#include "reboot_test.h"
#include "test_base.h"
#include "test_host.h"
using namespace widevine;
// TODO(b/195338975): document how a partner should modify this so that they can
// use a real host.
TestHost* g_host = nullptr;
namespace {
constexpr char kGlobalDumpFileName[] = "dumped_global_filesystem.dat";
constexpr char kPerOriginDumpFileName[] = "dumped_per_origin_filesystem.dat";
using StorageMap = TestHost::Storage::StorageMap;
// Load a TestHost file system from the real file system.
bool ReloadFileSystem(const char* file_name, StorageMap* map) {
std::ifstream input(file_name);
if (input.fail()) {
// This is OK for first pass, but an error for later passes.
LOGD("Could not read %s", file_name);
return false;
}
std::string dumped_file_system((std::istreambuf_iterator<char>(input)),
std::istreambuf_iterator<char>());
if (!wvcdm::RebootTest::ParseDump(dumped_file_system, map)) {
LOGE("Could not parse %s", file_name);
return false;
}
return true;
}
bool ReloadFileSystems() {
StorageMap global_map;
StorageMap per_origin_map;
if (!ReloadFileSystem(kGlobalDumpFileName, &global_map) ||
!ReloadFileSystem(kPerOriginDumpFileName, &per_origin_map)) {
return false;
}
g_host->global_storage().ResetFiles(global_map);
g_host->per_origin_storage().ResetFiles(per_origin_map);
return true;
}
// Dump a TestHost file system to the real file system. If the dump file
// cannot be written, this raises an exception and crashes the program. Since
// we usually build with exceptions turned off, what this means is that the test
// executable will halt if the file cannot be written.
void DumpFileSystem(const char* file_name, const StorageMap& map) {
std::string dump = wvcdm::RebootTest::DumpData(map);
std::ofstream output(file_name);
output << dump;
output.close();
}
void DumpFileSystems() {
DumpFileSystem(kGlobalDumpFileName, g_host->global_storage().files());
DumpFileSystem(kPerOriginDumpFileName, g_host->per_origin_storage().files());
}
} // namespace
int main(int argc, char** argv) {
// Set up a Host and initialize the library. This makes these services
// available to the tests. We would do this in the test suite itself, but the
// core & OEMCrypto tests don't know they depend on this for storage.
g_host = new TestHost();
ReloadFileSystems();
// Partners will want to replace this with a real IStorage.
Cdm::IStorage* const storage = &g_host->global_storage();
// Partners may also want to replace this with real implementations of IClock
// and ITimer. If so, make not to set the command line argument
// "--fake_sleep".
Cdm::IClock* const clock = g_host;
Cdm::ITimer* const timer = g_host;
// Partners who prefer their logs to go somewhere other than stderr may want
// to replace this implementation.
Cdm::ILogger* const logger = &g_stderr_logger;
// If the tests need a separate file system from that used by the host, that
// should be set up here. For the reference code, we use a default test file
// system, but save the data from the file system.
// A separate file system might be needed, for example, if the test data needs
// to be saved off-device. Or, for example, if the main file system limits the
// names and types of files it can save to certificates and offline licenses.
wvutil::FileSystem* file_system = nullptr;
wvcdm::RebootTest::set_file_system(file_system);
const int test_results = Main(storage, clock, timer, logger, argc, argv);
DumpFileSystems();
// This is used by the test driver to know what time to use for initializing
// the fake clock for the next pass.
std::cout << "END_OF_TEST " << wvutil::Clock().GetCurrentTime() << "\n";
return test_results;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,44 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include <assert.h>
#include <gtest/gtest.h>
#include <string.h>
#include <time.h>
#include <algorithm>
#include <string>
#include <vector>
#include "cdm.h"
#include "cdm_test_runner.h"
#include "log.h"
#include "test_base.h"
#include "test_host.h"
using namespace widevine;
TestHost* g_host = nullptr;
int main(int argc, char** argv) {
g_host = new TestHost();
// TODO(b/195338975): document that using a real IStorage means you should not
// run the tests in $CDM_DIR/cdm/test/cdm_test.cpp
// Partners will want to replace this with a real IStorage.
Cdm::IStorage* const storage = &g_host->global_storage();
// Partners may also want to replace this with real implementations of IClock
// and ITimer. If so, make not to set the command line argument
// "--fake_sleep".
Cdm::IClock* const clock = g_host;
Cdm::ITimer* const timer = g_host;
// Partners who prefer their logs to go somewhere other than stderr may want
// to replace this implementation.
Cdm::ILogger* const logger = &g_stderr_logger;
const int test_results = Main(storage, clock, timer, logger, argc, argv);
return test_results;
}

View File

@@ -1,118 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "cdm_test_printers.h"
namespace widevine {
void PrintTo(const Cdm::MessageType& value, ::std::ostream* os) {
switch (value) {
case Cdm::kLicenseRequest:
*os << "Cdm::kLicenseRequest";
break;
case Cdm::kLicenseRenewal:
*os << "Cdm::kLicenseRenewal";
break;
case Cdm::kLicenseRelease:
*os << "Cdm::kLicenseRelease";
break;
case Cdm::kIndividualizationRequest:
*os << "Cdm::kIndividualizationRequest";
break;
default:
*os << "Unknown Cdm::MessageType value " << value;
break;
}
}
void PrintTo(const Cdm::Status& value, ::std::ostream* os) {
switch (value) {
case Cdm::kSuccess:
*os << "Cdm::kSuccess";
break;
case Cdm::kTypeError:
*os << "Cdm::kTypeError";
break;
case Cdm::kNotSupported:
*os << "Cdm::kNotSupported";
break;
case Cdm::kInvalidState:
*os << "Cdm::kInvalidState";
break;
case Cdm::kQuotaExceeded:
*os << "Cdm::kQuotaExceeded";
break;
case Cdm::kNeedsDeviceCertificate:
*os << "Cdm::kNeedsDeviceCertificate";
break;
case Cdm::kSessionNotFound:
*os << "Cdm::kSessionNotFound";
break;
case Cdm::kDecryptError:
*os << "Cdm::kDecryptError";
break;
case Cdm::kNoKey:
*os << "Cdm::kNoKey";
break;
case Cdm::kKeyUsageBlockedByPolicy:
*os << "Cdm::kKeyUsageBlockedByPolicy";
break;
case Cdm::kRangeError:
*os << "Cdm::kRangeError";
break;
case Cdm::kResourceContention:
*os << "Cdm::kResourceContention";
break;
case Cdm::kSessionStateLost:
*os << "Cdm::kSessionStateLost";
break;
case Cdm::kSystemStateLost:
*os << "Cdm::kSystemStateLost";
break;
case Cdm::kOutputTooLarge:
*os << "Cdm::kOutputTooLarge";
break;
case Cdm::kNeedsServiceCertificate:
*os << "Cdm::kNeedsServiceCertificate";
break;
case Cdm::kUnexpectedError:
*os << "Cdm::kUnexpectedError";
break;
default:
*os << "Unknown Cdm::Status value " << value;
break;
}
}
void PrintTo(const Cdm::KeyStatus& value, ::std::ostream* os) {
switch (value) {
case Cdm::kUsable:
*os << "Cdm::kUsable";
break;
case Cdm::kExpired:
*os << "Cdm::kExpired";
break;
case Cdm::kOutputRestricted:
*os << "Cdm::kOutputRestricted";
break;
case Cdm::kStatusPending:
*os << "Cdm::kStatusPending";
break;
case Cdm::kInternalError:
*os << "Cdm::kInternalError";
break;
case Cdm::kReleased:
*os << "Cdm::kReleased";
break;
default:
*os << "Unknown Cdm::KeyStatus value " << value;
break;
}
}
} // namespace widevine

View File

@@ -1,21 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
// This file adds some print methods so that when unit tests fail, the
// will print the name of an enumeration instead of the numeric value.
#ifndef WVCDM_CDM_TEST_CDM_TEST_PRINTERS_H_
#define WVCDM_CDM_TEST_CDM_TEST_PRINTERS_H_
#include <iostream>
#include "cdm.h"
namespace widevine {
void PrintTo(const Cdm::MessageType& value, ::std::ostream* os);
void PrintTo(const Cdm::Status& value, ::std::ostream* os);
void PrintTo(const Cdm::KeyStatus& value, ::std::ostream* os);
} // namespace widevine
#endif // WVCDM_CDM_TEST_CDM_TEST_PRINTERS_H_

View File

@@ -1,69 +0,0 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include <assert.h>
#include <gtest/gtest.h>
#include <string.h>
#include <time.h>
#include <algorithm>
#include <string>
#include <vector>
#include "cdm.h"
#include "log.h"
#include "stderr_logger.h"
#include "test_base.h"
#include "test_host.h"
widevine::StderrLogger g_stderr_logger;
std::string g_sandbox_id;
namespace widevine {
namespace {
constexpr char kSandboxIdParam[] = "--sandbox_id=";
// Following the pattern established by help text in test_base.cpp
constexpr char kExtraHelpText[] =
" --sandbox_id=<sandbox_id>\n"
" Specifies the Sandbox ID that should be sent to OEMCrypto via\n"
" OEMCrypto_SetSandbox(). On most platforms, since Sandbox IDs are not\n"
" in use, this parameter should be omitted.\n";
} // namespace
int Main(Cdm::IStorage* storage, Cdm::IClock* clock, Cdm::ITimer* timer,
Cdm::ILogger* logger, int argc, char** argv) {
// Find and filter out the Sandbox ID, if any.
std::vector<std::string> args(argv, argv + argc);
auto sandbox_id_iter = std::find_if(std::begin(args) + 1, std::end(args),
[](const std::string& elem) -> bool {
return elem.find(kSandboxIdParam) == 0;
});
if (sandbox_id_iter != std::end(args)) {
g_sandbox_id = sandbox_id_iter->substr(strlen(kSandboxIdParam));
args.erase(sandbox_id_iter);
}
Cdm::Status status = Cdm::initialize(
Cdm::kOpaqueHandle, storage, clock, timer, logger,
static_cast<Cdm::LogLevel>(wvutil::g_cutoff), g_sandbox_id);
(void)status; // status is now used when assertions are turned off.
assert(status == Cdm::kSuccess);
std::vector<const char*> new_argv(args.size());
std::transform(
std::begin(args), std::end(args), std::begin(new_argv),
[](const std::string& arg) -> const char* { return arg.c_str(); });
// This must take place after the call to Cdm::initialize() because it makes
// calls that are only valid after the library is initialized.
if (!wvcdm::WvCdmTestBase::Initialize(static_cast<int>(new_argv.size()),
new_argv.data(), kExtraHelpText)) {
return 1;
}
// Init gtest after oemcrypto and cdm host have been initialized.
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
} // namespace widevine

View File

@@ -1,20 +0,0 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_CDM_TEST_CDM_TEST_RUNNER_H_
#define WVCDM_CDM_TEST_CDM_TEST_RUNNER_H_
#include <iostream>
#include "cdm.h"
namespace widevine {
// Run all CDM tests using the specified storage, clock, and timer. It parses
// standard command line arguments, sets some default test client info,
// initializes a CDM object, and then runs all the tests.
// Returns 0 on success.
int Main(Cdm::IStorage* storage, Cdm::IClock* clock, Cdm::ITimer* timer,
Cdm::ILogger* logger, int argc, char** argv);
} // namespace widevine
#endif // WVCDM_CDM_TEST_CDM_TEST_RUNNER_H_

View File

@@ -4,8 +4,4 @@
#include "create_test_file_system.h"
#include "test_host.h"
wvutil::FileSystem* CreateTestFileSystem() {
return new wvutil::FileSystem("", &g_host->per_origin_storage());
}
wvutil::FileSystem* CreateTestFileSystem() { return new wvutil::FileSystem(); }

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +0,0 @@
// Copyright 2019 Google LLC. All Rights Reserved.
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@end
@implementation AppDelegate {
}
@end
int main(int argc, char** argv) {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

View File

@@ -1,23 +0,0 @@
// Copyright 2019 Google LLC. All Rights Reserved.
#import <XCTest/XCTest.h>
#include <gtest/gtest.h>
// Defined in cdm_test_main.cpp.
int main(int argc, char** argv);
@interface GtestTests : XCTestCase
@end
@implementation GtestTests
- (void)testAll {
testing::GTEST_FLAG(filter) = GTEST_FILTER;
char arg0[] = "tests";
char* argv[] = {arg0, nullptr};
XCTAssertEqual(main(1, argv), 0);
}
@end

View File

@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>EQHXZ8M8AV.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
</plist>

View File

@@ -1,64 +0,0 @@
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
/**
The test file system for the Level 3 OEMCrypto sits on top of the host file
system. A real production version may either use the host file system or it
may write files directly to the file system.
*/
#include <algorithm>
#include <cstring>
#include <map>
#include <string>
#include "level3_file_system_ce_test.h"
#include "test_host.h"
namespace wvoec3 {
ssize_t OEMCrypto_Level3CETestFileSystem::Read(const char* filename,
void* buffer, size_t size) {
if (!g_host) return 0;
const std::string name(filename);
if (!g_host->global_storage().exists(name)) return 0;
std::string data;
if (!g_host->global_storage().read(name, &data)) return 0;
size_t bytes_read = std::min(size, data.size());
memcpy(buffer, data.data(), bytes_read);
return bytes_read;
}
ssize_t OEMCrypto_Level3CETestFileSystem::Write(const char* filename,
const void* buffer,
size_t size) {
if (!g_host) return 0;
std::string data(static_cast<const char*>(buffer), size);
const std::string name(filename);
if (!g_host->global_storage().write(name, data)) return 0;
return size;
}
bool OEMCrypto_Level3CETestFileSystem::Exists(const char* filename) {
if (!g_host) return false;
const std::string name(filename);
return g_host->global_storage().exists(name);
}
ssize_t OEMCrypto_Level3CETestFileSystem::FileSize(const char* filename) {
if (!g_host) return 0;
const std::string name(filename);
if (!g_host->global_storage().exists(name)) return 0;
return static_cast<ssize_t>(g_host->global_storage().size(name));
}
bool OEMCrypto_Level3CETestFileSystem::Remove(const char* filename) {
if (!g_host) return false;
const std::string name(filename);
if (!g_host->global_storage().exists(name)) return false;
return g_host->global_storage().remove(name);
}
} // namespace wvoec3

View File

@@ -1,33 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
/*********************************************************************
* level3_file_system_ce_test.h
*
* Test file system for CE CDM for OEMCrypto Level3 File Operations.
*********************************************************************/
#ifndef LEVEL3_FILE_SYSTEM_CE_TEST_H_
#define LEVEL3_FILE_SYSTEM_CE_TEST_H_
#include "cdm.h"
#include "file_store.h"
#include "level3_file_system.h"
namespace wvoec3 {
class OEMCrypto_Level3CETestFileSystem : public OEMCrypto_Level3FileSystem {
public:
OEMCrypto_Level3CETestFileSystem() {}
~OEMCrypto_Level3CETestFileSystem() override {}
ssize_t Read(const char* filename, void* buffer, size_t size) override;
ssize_t Write(const char* filename, const void* buffer, size_t size) override;
bool Exists(const char* filename) override;
ssize_t FileSize(const char* filename) override;
bool Remove(const char* filename) override;
};
} // namespace wvoec3
#endif

View File

@@ -1,17 +0,0 @@
#include "level3.h"
#include "level3_file_system_ce_test.h"
namespace wvoec3 {
OEMCrypto_Level3FileSystem* createLevel3FileSystem() {
return new OEMCrypto_Level3CETestFileSystem();
}
void deleteLevel3FileSystem(OEMCrypto_Level3FileSystem* file_system) {
if (file_system) {
delete file_system;
}
}
} // namespace wvoec3

View File

@@ -1,349 +0,0 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "perf_test.h"
#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>
#include <chrono>
#include <cmath>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "config_test_env.h"
#include "license_request.h"
#include "logger_global.h"
#include "test_host.h"
#include "url_request.h"
#define ASSERT_SUCCESS(code) ASSERT_EQ(code, Cdm::kSuccess)
#define EXPECT_SUCCESS(code) EXPECT_EQ(code, Cdm::kSuccess)
#define WALL_NOW std::chrono::high_resolution_clock::now()
TestHost* g_host = nullptr;
widevine::StderrLogger g_stderr_logger;
namespace widevine {
namespace {
constexpr const size_t kTestCount = 50;
const wvcdm::ConfigTestEnv kTestData(wvcdm::kContentProtectionUatServer);
CreateFuncType create_func = nullptr;
using TimeType = std::chrono::duration<double, std::milli>;
struct PerfInfo {
double mean;
double min;
double max;
double std_dev;
template <size_t Size>
PerfInfo(const double (&values)[Size]) {
static_assert(Size > 0, "Must pass at least one value");
// First pass to calculate min/max/mean.
bool first = true;
double sum = 0;
for (auto v : values) {
sum += v;
if (first) {
min = max = v;
first = false;
} else {
if (v < min) min = v;
if (v > max) max = v;
}
}
mean = sum / Size;
// Second pass to calculate standard deviation.
sum = 0;
for (auto v : values) {
sum += (v - mean) * (v - mean);
}
std_dev = std::sqrt(sum / Size);
}
};
std::ostream& operator<<(std::ostream& os, const PerfInfo& info) {
// mean=12.33442, std-dev=1.44421, min=1.22431, max=244.1133144
return os << "mean=" << info.mean << ", std-dev=" << info.std_dev
<< ", min=" << info.min << ", max=" << info.max;
}
class PerfTracker {
public:
class Test {
public:
Test(PerfTracker* tracker)
: wall_start_(WALL_NOW), cpu_start_(std::clock()), tracker_(tracker) {}
~Test() {
tracker_->wall_times_[tracker_->index_] =
TimeType(WALL_NOW - wall_start_).count();
tracker_->cpu_times_[tracker_->index_] =
(std::clock() - cpu_start_) * 1000.0 / CLOCKS_PER_SEC;
tracker_->index_++;
}
private:
std::chrono::high_resolution_clock::time_point wall_start_;
std::clock_t cpu_start_;
PerfTracker* tracker_;
};
void Print(const std::string& name, size_t block_size_bytes = 0) {
PerfInfo wall_perf(wall_times_);
PerfInfo cpu_perf(cpu_times_);
std::cout << name << " (wall, ms): " << wall_perf << "\n";
std::cout << name << " (cpu, ms): " << cpu_perf << "\n";
if (block_size_bytes) {
// |mean| is in milliseconds.
std::cout << name << " (wall, MBit/sec): "
<< (block_size_bytes * 8 * 1000 / wall_perf.mean / 1024 / 1024)
<< "\n";
std::cout << name << " (cpu, MBit/sec): "
<< (block_size_bytes * 8 * 1000 / cpu_perf.mean / 1024 / 1024)
<< "\n";
}
}
private:
double wall_times_[kTestCount];
double cpu_times_[kTestCount];
size_t index_ = 0;
};
#define MEASURE_PERF(tracker, code) \
{ \
PerfTracker::Test test(&(tracker)); \
code; \
}
class EventListener : public Cdm::IEventListener {
public:
struct MessageInfo {
std::string session_id;
std::string message;
Cdm::MessageType message_type;
std::string url;
};
void onMessage(const std::string& session_id, Cdm::MessageType message_type,
const std::string& message, const std::string& url) override {
messages.push_back({session_id, message, message_type, url});
}
void onKeyStatusesChange(const std::string& session_id,
bool has_new_usable_key) override {}
void onExpirationChange(const std::string& session_id,
int64_t new_expiration) override {}
void onRemoveComplete(const std::string& session_id) override {}
std::vector<MessageInfo> messages;
};
bool SendPost(const std::string& message, std::string* response) {
wvcdm::UrlRequest req(kTestData.license_server());
std::string raw_response;
if (!req.is_connected() || !req.PostRequest(message) ||
!req.GetResponse(&raw_response)) {
return false;
}
wvcdm::LicenseRequest helper;
helper.GetDrmMessage(raw_response, *response);
return true;
}
std::unique_ptr<Cdm> CreateCdm(EventListener* event_listener) {
std::unique_ptr<Cdm> ret(
create_func(event_listener, &g_host->per_origin_storage(), true));
if (ret) {
EXPECT_SUCCESS(ret->setServiceCertificate(
Cdm::kProvisioningService,
kTestData.provisioning_service_certificate()));
EXPECT_SUCCESS(ret->setServiceCertificate(
Cdm::kLicensingService, kTestData.license_service_certificate()));
}
return ret;
}
class GlobalEnv : public testing::Environment {
public:
GlobalEnv(InitFuncType init_func, const std::string& cert)
: init_func_(init_func), cert_(cert) {}
void SetUp() override {
// Manually set the logger because `TestHost` makes logging calls before
// the global logger is set in |init_func_|.
g_logger = &g_stderr_logger;
g_host = new TestHost;
if (!cert_.empty()) g_host->per_origin_storage().write("cert.bin", cert_);
Cdm::LogLevel log_level = Cdm::kErrors;
if (const char* verbose = getenv("VERBOSE_OUTPUT")) {
if (std::strcmp(verbose, "1") == 0) log_level = Cdm::kVerbose;
}
ASSERT_SUCCESS(init_func_(Cdm::kNoSecureOutput, &g_host->global_storage(),
g_host, g_host, &g_stderr_logger, log_level));
}
private:
const InitFuncType init_func_;
const std::string cert_;
};
} // namespace
class PerfTest : public testing::Test {};
TEST_F(PerfTest, LicenseExchange) {
EventListener event_listener;
auto cdm = CreateCdm(&event_listener);
ASSERT_TRUE(cdm);
ASSERT_EQ(cdm->getProvisioningStatus(), Cdm::kProvisioned);
PerfTracker create;
PerfTracker generate;
PerfTracker update;
PerfTracker close;
for (size_t i = 0; i < kTestCount; i++) {
std::string session_id;
MEASURE_PERF(create, ASSERT_SUCCESS(
cdm->createSession(Cdm::kTemporary, &session_id)));
MEASURE_PERF(
generate,
ASSERT_SUCCESS(cdm->generateRequest(
session_id, Cdm::kCenc,
wvcdm::ConfigTestEnv::GetInitData(wvcdm::kContentIdStreaming))));
std::string response;
ASSERT_TRUE(SendPost(event_listener.messages[0].message, &response));
MEASURE_PERF(update, ASSERT_SUCCESS(cdm->update(session_id, response)));
MEASURE_PERF(close, ASSERT_SUCCESS(cdm->close(session_id)));
event_listener.messages.pop_back();
}
create.Print("Create ");
generate.Print("Generate");
update.Print("Update ");
close.Print("Close ");
}
class DecryptPerfTest : public PerfTest,
public testing::WithParamInterface<bool> {};
TEST_P(DecryptPerfTest, Decrypt) {
EventListener event_listener;
auto cdm = CreateCdm(&event_listener);
ASSERT_TRUE(cdm);
ASSERT_EQ(cdm->getProvisioningStatus(), Cdm::kProvisioned);
std::string session_id;
ASSERT_SUCCESS(cdm->createSession(Cdm::kTemporary, &session_id));
ASSERT_SUCCESS(cdm->generateRequest(
session_id, Cdm::kCenc,
wvcdm::ConfigTestEnv::GetInitData(wvcdm::kContentIdStreaming)));
std::string response;
ASSERT_TRUE(SendPost(event_listener.messages[0].message, &response));
ASSERT_SUCCESS(cdm->update(session_id, response));
Cdm::KeyStatusMap statuses;
ASSERT_SUCCESS(cdm->getKeyStatuses(session_id, &statuses));
ASSERT_GT(statuses.size(), 0u);
const std::string key_id = statuses.begin()->first;
// Use in-place decrypt to avoid allocations. We don't care about the data,
// so we can just decrypt the same buffer again.
constexpr const size_t k16M = 16 * 1024 * 1024;
std::vector<uint8_t> buffer(k16M);
uint8_t iv[16];
for (auto& b : buffer) b = rand();
Cdm::DecryptionBatch batch;
batch.key_id = reinterpret_cast<const uint8_t*>(key_id.data());
batch.key_id_length = static_cast<uint32_t>(key_id.size());
if (GetParam()) {
batch.pattern.encrypted_blocks = batch.pattern.clear_blocks = 0;
} else {
batch.pattern.encrypted_blocks = 1;
batch.pattern.clear_blocks = 9;
}
batch.is_secure = false;
batch.encryption_scheme = GetParam() ? Cdm::kAesCtr : Cdm::kAesCbc;
batch.is_video = true;
Cdm::Subsample subsample;
subsample.clear_bytes = 0;
// subsample.encrypted_bytes set in the test.
Cdm::Sample sample;
sample.input.iv = iv;
sample.input.iv_length = 16;
sample.input.data = buffer.data();
// sample.data_length set in the test.
sample.input.subsamples = &subsample;
sample.input.subsamples_length = 1;
sample.output.data = buffer.data();
sample.output.data_offset = 0;
sample.output.data_length = static_cast<uint32_t>(buffer.size());
batch.samples = &sample;
batch.samples_length = 1;
constexpr const size_t block_sizes[] = {8 * 1024, 256 * 1024, k16M};
constexpr const size_t sizes_count =
sizeof(block_sizes) / sizeof(block_sizes[0]);
const std::string block_names[] = {" 8k", "256k", " 16M"};
PerfTracker perf[sizes_count];
for (size_t i = 0; i < sizes_count; i++) {
subsample.protected_bytes = sample.input.data_length =
sample.output.data_length = static_cast<uint32_t>(block_sizes[i]);
for (size_t j = 0; j < kTestCount; j++) {
MEASURE_PERF(perf[i], ASSERT_SUCCESS(cdm->decrypt(batch)));
}
}
for (size_t i = 0; i < sizes_count; i++) {
perf[i].Print("Decrypt " + block_names[i], block_sizes[i]);
}
}
std::string PrintDecryptParam(const testing::TestParamInfo<bool>& info) {
return info.param ? "CTR" : "CBC";
}
INSTANTIATE_TEST_SUITE_P(Decrypt, DecryptPerfTest, testing::Bool(),
PrintDecryptParam);
int PerfTestMain(InitFuncType init_func, CreateFuncType create,
const std::string& cert) {
#ifdef _DEBUG
// Don't use #error since we build all targets and we don't want to fail the
// debug build (and we can't have configuration-specific targets).
fprintf(stderr, "Don't run performance tests in Debug mode\n");
return 1;
#else
create_func = create;
testing::AddGlobalTestEnvironment(new GlobalEnv(init_func, cert));
return RUN_ALL_TESTS();
#endif
}
} // namespace widevine

View File

@@ -1,24 +0,0 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_CDM_TEST_PERF_TEST_H_
#define WVCDM_CDM_TEST_PERF_TEST_H_
#include <string>
#include "cdm.h"
namespace widevine {
using InitFuncType = Cdm::Status (*)(Cdm::SecureOutputType, Cdm::IStorage*,
Cdm::IClock*, Cdm::ITimer*, Cdm::ILogger*,
Cdm::LogLevel);
using CreateFuncType = Cdm* (*)(Cdm::IEventListener*, Cdm::IStorage*, bool);
int PerfTestMain(InitFuncType init_func, CreateFuncType create_func,
const std::string& cert);
} // namespace widevine
#endif // WVCDM_CDM_TEST_PERF_TEST_H_

View File

@@ -1,84 +0,0 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
// This uses dlopen() which is only usable on POSIX platforms. The function
// names assume GCC/Clang.
#include <dlfcn.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <fstream>
#include <tuple>
#include "clock.h"
#include "perf_test.h"
#include "test_host.h"
namespace widevine {
constexpr char kInitName[] =
"_ZN8widevine3Cdm10initializeENS0_16SecureOutputTypeEPNS0_8IStorageEPNS0_"
"6IClockEPNS0_6ITimerEPNS0_7ILoggerENS0_8LogLevelE";
constexpr char kCreateName[] =
"_ZN8widevine3Cdm6createEPNS0_14IEventListenerEPNS0_8IStorageEbb";
bool ReadFile(const std::string& path, std::string* output) {
constexpr size_t kReadSize = 8 * 1024;
std::ifstream fs(path, std::ios::in | std::ios::binary);
if (!fs) return false;
while (true) {
const size_t offset = output->size();
output->resize(output->size() + kReadSize);
fs.read(&output->at(offset), kReadSize);
if (fs.eof()) {
output->resize(offset + fs.gcount());
return true;
} else if (!fs) {
fprintf(stderr, "Error reading from cert file\n");
return false;
}
}
}
std::tuple<InitFuncType, CreateFuncType> LoadCdm(const char* path) {
// Note we will leak the library object; but this is just for tests and will
// be unloaded when we exit anyway.
auto dll = dlopen(path, RTLD_NOW);
if (!dll) {
fprintf(stderr, "Error loading so file: %s\n", dlerror());
exit(1);
}
auto init = reinterpret_cast<InitFuncType>(dlsym(dll, kInitName));
auto create = reinterpret_cast<CreateFuncType>(dlsym(dll, kCreateName));
if (!init || !create) {
fprintf(stderr, "Error finding CDM functions: %s\n", dlerror());
exit(1);
}
return std::make_tuple(init, create);
}
} // namespace widevine
namespace wvutil {
int64_t Clock::GetCurrentTime() { return g_host->now() / 1000; }
} // namespace wvutil
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
if (argc != 3) {
fprintf(stderr, "Usage: %s <CDM_SO_PATH> <CERT_PATH>\n", argv[0]);
return 1;
}
std::string cert;
if (!widevine::ReadFile(argv[2], &cert))
return 1;
auto funcs = widevine::LoadCdm(argv[1]);
return widevine::PerfTestMain(std::get<0>(funcs), std::get<1>(funcs), cert);
}

View File

@@ -1,26 +0,0 @@
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include <gtest/gtest.h>
#import <XCTest/XCTest.h>
#include "cdm.h"
#include "perf_test.h"
@interface GtestTests : XCTestCase
@end
@implementation GtestTests
- (void)testAll {
testing::GTEST_FLAG(filter) = GTEST_FILTER;
char arg0[] = "tests";
char* argv[] = {arg0, nullptr};
int argc = 1;
testing::InitGoogleTest(&argc, argv);
XCTAssertEqual(widevine::PerfTestMain(&widevine::Cdm::initialize, &widevine::Cdm::create, ""), 0);
}
@end

View File

@@ -1,185 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "test_host.h"
#include <gtest/gtest.h>
#include <chrono>
#include <unordered_set>
#include "cdm_version.h"
#include "file_store.h"
#include "log.h"
using namespace widevine;
namespace {
// Some files are expected to go in global storage. All other files are expected
// to go in per-origin storage. To help us enforce this in tests, this set
// tracks the filenames that belong in global storage. TestHost::Storage will
// reject attempts to access these files via per-origin storage or to access
// files not in this list via global storage.
const std::unordered_set<std::string> kGlobalFilenames = {
"usgtable.bin", // CDM usage table data
"StoredUsageTime.dat", // Reference OEMCrypto usage table data
"GenerationNumber.dat", // Reference OEMCrypto master generation number
"persistent.dat", // Persistent data storage for certain TEE
// implementations
"keybox.dat", // Legacy file for storing keybox in non-secure storage.
// CDM data for OTA keybox renewal.
"okp.bin",
"debug_ignore_keybox_count.txt",
"debug_allow_test_keybox.txt",
// Widevine L3 data files.
"ay64.dat",
"ay64.dat2",
"ay64.dat3",
"ay64.dat4",
"ay64.dat5",
"ay64.dat6",
"l3_failure_file",
wvutil::kOemCertificateFileName,
};
} // namespace
TestHost::TestHost() : global_storage_(true), per_origin_storage_(false) {
Reset();
}
TestHost::~TestHost() { wvutil::TestSleep::set_callback(nullptr); }
void TestHost::Reset() {
auto now = std::chrono::system_clock().now();
now_ = now.time_since_epoch() / std::chrono::milliseconds(1);
wvutil::TestSleep::set_callback(this);
// Surprisingly, std::priority_queue has no clear().
while (!timers_.empty()) {
timers_.pop();
}
global_storage_.Reset();
per_origin_storage_.Reset();
}
void TestHost::ElapseTime(int64_t milliseconds) {
// Note that, during the time rollback tests, milliseconds will be negative,
// so we cannot assume goal_time > now_.
int64_t goal_time = now_ + milliseconds;
// Walk forward from now_ to goal_time, stepping at each timer along the way
// to fire its callback.
while (!timers_.empty() && now_ < goal_time) {
Timer t = timers_.top();
ASSERT_GE(t.expiry_time(), now_);
if (t.expiry_time() <= goal_time) {
timers_.pop();
now_ = t.expiry_time();
t.client()->onTimerExpired(t.context());
} else {
// The next timer is further in the future than goal_time, so we are done
// processing the timers.
break;
}
}
// No matter what happened with the timers, update now_ to the goal_time.
now_ = goal_time;
}
size_t TestHost::NumTimers() const { return timers_.size(); }
int64_t TestHost::now() { return now_; }
void TestHost::setTimeout(int64_t delay_ms, IClient* client, void* context) {
int64_t expiry_time = now_ + delay_ms;
timers_.push(Timer(expiry_time, client, context));
}
void TestHost::cancel(IClient* client) {
// Filter out the timers for this client and put the rest into |others|.
std::priority_queue<Timer> others;
while (timers_.size()) {
Timer t = timers_.top();
timers_.pop();
if (t.client() != client) {
others.push(t);
}
}
// Now swap the queues.
std::swap(timers_, others);
}
TestHost::Storage::Storage(bool is_global) : is_global_(is_global) { Reset(); }
void TestHost::Storage::Reset() {
files_.clear();
}
bool TestHost::Storage::read(const std::string& name, std::string* data) {
StorageMap::iterator it = files_.find(name);
bool ok = it != files_.end();
LOGV("read file: %s: %s", name.c_str(), ok ? "ok" : "fail");
if (!CheckFilename(name) || !ok) return false;
*data = it->second;
return true;
}
bool TestHost::Storage::write(const std::string& name,
const std::string& data) {
LOGV("write file: %s", name.c_str());
if (!CheckFilename(name)) return false;
files_[name] = data;
return true;
}
bool TestHost::Storage::exists(const std::string& name) {
StorageMap::iterator it = files_.find(name);
bool ok = it != files_.end();
LOGV("exists? %s: %s", name.c_str(), ok ? "true" : "false");
if (!CheckFilename(name)) return false;
return ok;
}
bool TestHost::Storage::remove(const std::string& name) {
if (name.empty()) {
// If no name, delete all files (see DeviceFiles::DeleteAllFiles())
LOGV("remove all files");
files_.clear();
return true;
}
LOGV("remove: %s", name.c_str());
if (!CheckFilename(name)) return false;
return files_.erase(name) > 0;
}
int32_t TestHost::Storage::size(const std::string& name) {
StorageMap::iterator it = files_.find(name);
bool ok = (it != files_.end());
LOGV("size? %s: %s", name.c_str(), ok ? "ok" : "fail");
if (!CheckFilename(name) || !ok) return -1;
return static_cast<int32_t>(it->second.size());
}
bool TestHost::Storage::list(std::vector<std::string>* names) {
names->clear();
for (StorageMap::iterator it = files_.begin(); it != files_.end(); it++) {
names->push_back(it->first);
}
return true;
}
bool TestHost::Storage::CheckFilename(const std::string& name) {
const bool is_global_filename =
(kGlobalFilenames.find(name) != kGlobalFilenames.end());
if (is_global_ != is_global_filename) {
LOGE("Attempt to access %s in %s storage rejected.", name.c_str(),
is_global_ ? "global" : "per-origin");
return false;
}
return true;
}

View File

@@ -1,101 +0,0 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_CDM_TEST_TEST_HOST_H_
#define WVCDM_CDM_TEST_TEST_HOST_H_
#include <map>
#include <queue>
#include <string>
#include <vector>
#include "cdm.h"
#include "stderr_logger.h"
#include "test_sleep.h"
// This provides a host environment for running CDM tests. It implements the
// IStorage, IClock and ITimer interfaces that a host would normally implement,
// while allowing them to be manipulated by the test.
class TestHost : public widevine::Cdm::IClock,
public widevine::Cdm::ITimer,
public wvutil::TestSleep::CallBack {
public:
class Storage : public widevine::Cdm::IStorage {
public:
typedef std::map<std::string, std::string> StorageMap;
explicit Storage(bool is_global);
~Storage() override {}
void Reset();
// Reset the file system to contain the specified files.
void ResetFiles(const StorageMap& files) { files_ = files; };
const StorageMap& files() const { return files_; }
// widevine::Cdm::IStorage
bool read(const std::string& name, std::string* data) override;
bool write(const std::string& name, const std::string& data) override;
bool exists(const std::string& name) override;
bool remove(const std::string& name) override;
int32_t size(const std::string& name) override;
bool list(std::vector<std::string>* names) override;
private:
bool is_global_;
StorageMap files_;
bool CheckFilename(const std::string& name);
};
TestHost();
~TestHost() override;
void Reset();
// Used for manipulating and inspecting timer states during testing.
void ElapseTime(int64_t milliseconds) override;
size_t NumTimers() const;
Storage& global_storage() { return global_storage_; }
Storage& per_origin_storage() { return per_origin_storage_; }
// widevine::Cdm::IClock
int64_t now() override;
// widevine::Cdm::ITimer
void setTimeout(int64_t delay_ms, IClient* client, void* context) override;
void cancel(IClient* client) override;
private:
struct Timer {
Timer(int64_t expiry_time, IClient* client, void* context)
: expiry_time_(expiry_time), client_(client), context_(context) {}
bool operator<(const Timer& other) const {
// We want to reverse the order so that the smallest expiry times go to
// the top of the priority queue.
return expiry_time_ > other.expiry_time_;
}
int64_t expiry_time() { return expiry_time_; }
IClient* client() { return client_; }
void* context() { return context_; }
private:
int64_t expiry_time_;
IClient* client_;
void* context_;
};
int64_t now_;
std::priority_queue<Timer> timers_;
Storage global_storage_;
Storage per_origin_storage_;
};
// Owned and managed by the test runner.
extern TestHost* g_host;
extern widevine::StderrLogger g_stderr_logger;
extern std::string g_sandbox_id;
#endif // WVCDM_CDM_TEST_TEST_HOST_H_