Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -485,3 +485,26 @@ if(PSPRECOMP_BUILD_TESTS)
set_target_properties(portablekit_io_filter_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
add_test(NAME portablekit_io_filter_tests COMMAND portablekit_io_filter_tests)
endif()

# The desktop app's keys file as extension modules look keys up in it. The
# values of PortableKit's own keys come from crypto_keys_builtin.cpp, compiled
# under other names (tests/builtin_keys_renamed.cpp) beside the app's own.
if(PSPRECOMP_BUILD_TESTS)
add_executable(portablekit_keys_file_tests
"${PORTABLEKIT_ROOT}/tests/keys_file_tests.cpp"
"${PORTABLEKIT_ROOT}/tests/builtin_keys_renamed.cpp"
"${PORTABLEKIT_ROOT}/apps/portablekit/src/keys_file.cpp"
"${PORTABLEKIT_ROOT}/apps/portablekit/src/app_home.cpp"
"${PORTABLEKIT_ROOT}/apps/portablekit/src/text_format.cpp"
"${PORTABLEKIT_ROOT}/host/app_paths.cpp"
"${PORTABLEKIT_ROOT}/host/install/user_data.cpp"
"${PORTABLEKIT_ROOT}/host/extension_keys.cpp"
"${PORTABLEKIT_ROOT}/host/profile.cpp"
"${PORTABLEKIT_ROOT}/tests/test_profile.cpp")
target_compile_features(portablekit_keys_file_tests PRIVATE cxx_std_20)
target_include_directories(portablekit_keys_file_tests PRIVATE
"${PORTABLEKIT_ROOT}/host" "${PORTABLEKIT_ROOT}/apps/portablekit/src" "${PORTABLEKIT_ROOT}/include")
target_link_libraries(portablekit_keys_file_tests PRIVATE psprecomp_core)
set_target_properties(portablekit_keys_file_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
add_test(NAME portablekit_keys_file_tests COMMAND portablekit_keys_file_tests)
endif()
3 changes: 3 additions & 0 deletions apps/portablekit/src/keys_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ KeysReport read_keys_file(const std::filesystem::path &path) {
report.problems.push_back(raw_name + " is not the right key: its fingerprint does not match.");
continue;
}
// Every key that passed is also kept by its name, so a look-up by
// name (hle_extension::key) finds it whatever structure it goes to.
report.keys.named[name] = *bytes;
if (name.starts_with("kirk.aes.")) {
const unsigned long slot = std::strtoul(name.substr(9).c_str(), nullptr, 16);
report.keys.kirk_aes[static_cast<std::uint8_t>(slot)] = to_key(*bytes);
Expand Down
6 changes: 4 additions & 2 deletions host/crypto_keys.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ struct CryptoKeys {
// The AES key of KIRK command 1 (signed and encrypted blocks), which wraps
// an executable's payload key.
std::optional<Key16> kirk_cmd1;
// Keys HLE extension modules declare (extension_keys.hpp), by their
// keys-file name in lower case, of any length.
// Every key the player's keys file gave, by its keys-file name in lower
// case and of any length: PortableKit's own (also kept in the fields
// here) and those HLE extension modules declare (extension_keys.hpp).
// hle_extension::key() looks here first.
std::map<std::string, std::vector<std::uint8_t>> named;
// Save-data keys 2 to 7, in the order they are usually published.
std::map<int, Key16> savedata;
Expand Down
6 changes: 6 additions & 0 deletions tests/builtin_keys_renamed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// host/crypto_keys_builtin.cpp under other names, for a test that also links
// the desktop app's keys file (which defines the usual ones): the values stay
// written in exactly one place.
#define crypto_keys builtin_crypto_keys
#define keys_come_from_keys_file builtin_keys_come_from_keys_file
#include "crypto_keys_builtin.cpp"
92 changes: 92 additions & 0 deletions tests/keys_file_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// The desktop app's keys file as HLE extension modules see it: every key the
// file gives can be looked up by its name (hle_extension::key goes through
// key_value), PortableKit's own included, whether or not a module also
// declares it. The values of PortableKit's own keys come from
// host/crypto_keys_builtin.cpp, compiled here under another name, so no key
// is written in this file.

#include "extension_keys.hpp"
#include "keys_file.hpp"

#include <cstdio>
#include <filesystem>
#include <fstream>
#include <string>
#include <vector>

namespace portablekit {
const CryptoKeys *builtin_crypto_keys(); // crypto_keys_builtin.cpp, renamed

// The keys the modules of this test declare: one PortableKit checks itself,
// and one of the module's own.
const std::vector<DeclaredKey> &declared_extension_keys() {
static const std::vector<DeclaredKey> keys{{"kirk.aes.5d", "", 16u, "Test module"},
{"vendor.key", "", 16u, "Test module"}};
return keys;
}
} // namespace portablekit

namespace {

int g_failures = 0;

void check(bool condition, const char *what) {
std::printf("%s %s\n", condition ? "ok " : "FAIL", what);
if (!condition) ++g_failures;
}

std::string hex(const portablekit::Key16 &key) {
std::string text;
char digits[3];
for (const std::uint8_t byte : key) {
std::snprintf(digits, sizeof digits, "%02X", byte);
text += digits;
}
return text;
}

std::vector<std::uint8_t> bytes(const portablekit::Key16 &key) { return {key.begin(), key.end()}; }

} // namespace

int main() {
const portablekit::CryptoKeys &builtin = *portablekit::builtin_crypto_keys();
const portablekit::Key16 kirk5d = *builtin.kirk(0x5Du);
const portablekit::Key16 save2 = *builtin.savedata_key(2);
const portablekit::Key16 cmd1 = *builtin.kirk_cmd1;

const auto path = std::filesystem::temp_directory_path() / "portablekit_keys_file_test.txt";
{
std::ofstream out(path);
out << "# checked by PortableKit, declared by the module too\n"
<< "kirk.aes.5D = " << hex(kirk5d) << "\n"
<< "# checked by PortableKit, declared by nobody\n"
<< "savedata.2 = " << hex(save2) << "\n"
<< "KIRK.CMD1 = " << hex(cmd1) << "\n"
<< "vendor.key = 000102030405060708090A0B0C0D0E0F\n"
<< "unknown.name = 00\n";
}
const portablekit::app::KeysReport report = portablekit::app::read_keys_file(path);
std::filesystem::remove(path);

for (const std::string &problem : report.problems) std::printf(" problem: %s\n", problem.c_str());
check(report.file_found && report.problems.empty(), "the file is read without problems");
check(report.warnings.size() == 1u, "a name nothing reads is a warning");
check(report.can_decrypt_executables, "PortableKit's own keys still do their work");

// What hle_extension::key(name) answers from (hle_extension_host.cpp).
const portablekit::CryptoKeys *keys = &report.keys;
check(portablekit::key_value(keys, "kirk.aes.5D") == bytes(kirk5d) && report.keys.named.contains("kirk.aes.5d"),
"a name PortableKit checks and a module declares is found by name");
check(portablekit::key_value(keys, "savedata.2") == bytes(save2) && report.keys.named.contains("savedata.2"),
"a name PortableKit checks and no module declares is found by name");
check(report.keys.named.contains("kirk.cmd1"), "every key PortableKit accepts is kept by its name");
check(portablekit::key_value(keys, "vendor.key").value_or(std::vector<std::uint8_t>{}).size() == 16u,
"a module's own key is found");
bool listed = report.declared.size() == 2u;
for (const auto &key : report.declared) listed = listed && key.present;
check(listed, "keys status lists both declared keys as present");

if (g_failures != 0) std::printf("%d failed\n", g_failures);
return g_failures == 0 ? 0 : 1;
}