From 2517da4c2f9f625a9148e47d37b64048176ee2e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Israelson?= <57065102+israpps@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:53:21 -0300 Subject: [PATCH] Extended keystore and customizable KELF header data (#9) * implement extended Keystore * Rename arcade Kbit and Kc to Override Kbit and Kc * fix typo in readme * Support detection of system 2x6 bootfile header * add predefined macros for kelf related constants * add dnasload user header a * add args to override specific KELF header data * add missing header * fix conditions on header check --- README.md | 5 +- src/inipp.h | 279 +++++++++++++++++++++++++++++++++++++++++++++++ src/kelf.cpp | 67 +++++++----- src/kelf.h | 20 ++++ src/kelftool.cpp | 92 +++++++++++++--- src/keystore.cpp | 79 ++++++-------- src/keystore.h | 11 +- 7 files changed, 462 insertions(+), 91 deletions(-) create mode 100644 src/inipp.h diff --git a/README.md b/README.md index 4f58e82..c65b4ac 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Place them in your home directory (%USERPROFILE%) in the "PS2KEYS.dat" file as a decrypt - decrypt and check the signature of kelf files encrypt - encrypt and sign kelf files : fmcb, fhdb, mbr fmcb - for retail PS2 memory cards + dnasload - for retail PS2 memory cards (PSX Whitelist) fhdb - for retail PS2 HDD (HDD OSD / BB Navigator) mbr - for retail PS2 HDD (mbr injection). Note: for mbr, elf should load from 0x100000 and should be without headers: @@ -21,7 +22,7 @@ headerless elf creation: examples: kelftool encrypt fhdb input.elf output.kelf - kelftool decrypot input.kelf output.elf + kelftool decrypt input.kelf output.elf *decrypt* command will also print useful information about kelf @@ -44,7 +45,7 @@ examples: #### Arcade -Note: for arcade units (Namco System 246/256 and Konami Python 1) it is necessary to provide different keys and also additional keys: **ARCADE_KBIT** and **ARCADE_KC** +Note: for arcade units (Namco System 246/256 and Konami Python 1) it is necessary to provide different keys and also additional keys: **OVERRIDE_KBIT** and **OVERRIDE_KC** #### Dev and proto diff --git a/src/inipp.h b/src/inipp.h new file mode 100644 index 0000000..217d0bf --- /dev/null +++ b/src/inipp.h @@ -0,0 +1,279 @@ +/* +MIT License +Copyright (c) 2017-2020 Matthias C. M. Troffaes +https://github.com/mcmtroffaes/inipp/tree/develop +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace inipp { + +namespace detail { + +// trim functions based on http://stackoverflow.com/a/217605 + +template +inline void ltrim(std::basic_string & s, const std::locale & loc) { + s.erase(s.begin(), + std::find_if(s.begin(), s.end(), + [&loc](CharT ch) { return !std::isspace(ch, loc); })); +} + +template +inline void rtrim(std::basic_string & s, const std::locale & loc) { + s.erase(std::find_if(s.rbegin(), s.rend(), + [&loc](CharT ch) { return !std::isspace(ch, loc); }).base(), + s.end()); +} + +template +inline void rtrim2(std::basic_string& s, UnaryPredicate pred) { + s.erase(std::find_if(s.begin(), s.end(), pred), s.end()); +} + +// string replacement function based on http://stackoverflow.com/a/3418285 + +template +inline bool replace(std::basic_string & str, const std::basic_string & from, const std::basic_string & to) { + auto changed = false; + size_t start_pos = 0; + while ((start_pos = str.find(from, start_pos)) != std::basic_string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + changed = true; + } + return changed; +} + +} // namespace detail + +template +inline bool extract(const std::basic_string & value, T & dst) { + CharT c; + std::basic_istringstream is{ value }; + T result; + if ((is >> std::boolalpha >> result) && !(is >> c)) { + dst = result; + return true; + } + else { + return false; + } +} + +template +inline bool extract(const std::basic_string & value, std::basic_string & dst) { + dst = value; + return true; +} + +template +inline bool get_value(const std::map, std::basic_string> & sec, const std::basic_string & key, T & dst) { + const auto it = sec.find(key); + if (it == sec.end()) return false; + return extract(it->second, dst); +} + +template +inline bool get_value(const std::map, std::basic_string>& sec, const CharT* key, T& dst) { + return get_value(sec, std::basic_string(key), dst); +} + +template +class Format +{ +public: + // used for generating + const CharT char_section_start; + const CharT char_section_end; + const CharT char_assign; + const CharT char_comment; + + // used for parsing + virtual bool is_section_start(CharT ch) const { return ch == char_section_start; } + virtual bool is_section_end(CharT ch) const { return ch == char_section_end; } + virtual bool is_assign(CharT ch) const { return ch == char_assign; } + virtual bool is_comment(CharT ch) const { return ch == char_comment; } + + // used for interpolation + const CharT char_interpol; + const CharT char_interpol_start; + const CharT char_interpol_sep; + const CharT char_interpol_end; + + Format(CharT section_start, CharT section_end, CharT assign, CharT comment, CharT interpol, CharT interpol_start, CharT interpol_sep, CharT interpol_end) + : char_section_start(section_start) + , char_section_end(section_end) + , char_assign(assign) + , char_comment(comment) + , char_interpol(interpol) + , char_interpol_start(interpol_start) + , char_interpol_sep(interpol_sep) + , char_interpol_end(interpol_end) {} + + Format() : Format('[', ']', '=', ';', '$', '{', ':', '}') {} + + const std::basic_string local_symbol(const std::basic_string& name) const { + return char_interpol + (char_interpol_start + name + char_interpol_end); + } + + const std::basic_string global_symbol(const std::basic_string& sec_name, const std::basic_string& name) const { + return local_symbol(sec_name + char_interpol_sep + name); + } +}; + +template +class Ini +{ +public: + using String = std::basic_string; + using Section = std::map; + using Sections = std::map; + + Sections sections; + std::list errors; + std::shared_ptr> format; + + static const int max_interpolation_depth = 10; + + Ini() : format(std::make_shared>()) {}; + Ini(std::shared_ptr> fmt) : format(fmt) {}; + + void generate(std::basic_ostream& os) const { + for (auto const & sec : sections) { + os << format->char_section_start << sec.first << format->char_section_end << std::endl; + for (auto const & val : sec.second) { + os << val.first << format->char_assign << val.second << std::endl; + } + os << std::endl; + } + } + + void parse(std::basic_istream & is) { + String line; + String section; + const std::locale loc{"C"}; + while (std::getline(is, line)) { + detail::ltrim(line, loc); + detail::rtrim(line, loc); + const auto length = line.length(); + if (length > 0) { + const auto pos = std::find_if(line.begin(), line.end(), [this](CharT ch) { return format->is_assign(ch); }); + const auto & front = line.front(); + if (format->is_comment(front)) { + } + else if (format->is_section_start(front)) { + if (format->is_section_end(line.back())) + section = line.substr(1, length - 2); + else + errors.push_back(line); + } + else if (pos != line.begin() && pos != line.end()) { + String variable(line.begin(), pos); + String value(pos + 1, line.end()); + detail::rtrim(variable, loc); + detail::ltrim(value, loc); + auto & sec = sections[section]; + if (sec.find(variable) == sec.end()) + sec.emplace(variable, value); + else + errors.push_back(line); + } + else { + errors.push_back(line); + } + } + } + } + + void interpolate() { + int global_iteration = 0; + auto changed = false; + // replace each "${variable}" by "${section:variable}" + for (auto & sec : sections) + replace_symbols(local_symbols(sec.first, sec.second), sec.second); + // replace each "${section:variable}" by its value + do { + changed = false; + const auto syms = global_symbols(); + for (auto & sec : sections) + changed |= replace_symbols(syms, sec.second); + } while (changed && (max_interpolation_depth > global_iteration++)); + } + + void default_section(const Section & sec) { + for (auto & sec2 : sections) + for (const auto & val : sec) + sec2.second.insert(val); + } + + void strip_trailing_comments() { + const std::locale loc{ "C" }; + for (auto & sec : sections) + for (auto & val : sec.second) { + detail::rtrim2(val.second, [this](CharT ch) { return format->is_comment(ch); }); + detail::rtrim(val.second, loc); + } + } + + void clear() { + sections.clear(); + errors.clear(); + } + +private: + using Symbols = std::vector>; + + const Symbols local_symbols(const String & sec_name, const Section & sec) const { + Symbols result; + for (const auto & val : sec) + result.emplace_back(format->local_symbol(val.first), format->global_symbol(sec_name, val.first)); + return result; + } + + const Symbols global_symbols() const { + Symbols result; + for (const auto & sec : sections) + for (const auto & val : sec.second) + result.emplace_back(format->global_symbol(sec.first, val.first), val.second); + return result; + } + + bool replace_symbols(const Symbols & syms, Section & sec) const { + auto changed = false; + for (auto & sym : syms) + for (auto & val : sec) + changed |= detail::replace(val.second, sym.first, sym.second); + return changed; + } +}; + +} // namespace inipp \ No newline at end of file diff --git a/src/kelf.cpp b/src/kelf.cpp index 05bfabd..0efa230 100644 --- a/src/kelf.cpp +++ b/src/kelf.cpp @@ -84,6 +84,11 @@ void xor_bit(const void *a, const void *b, void *Result, size_t Length) } } +extern uint8_t GSystemtype; +extern uint8_t GMGZones; +extern uint16_t GFlags; +extern uint8_t GApplicationType; + int Kelf::LoadKelf(const std::string &filename) { FILE *f = fopen(filename.c_str(), "rb"); @@ -108,6 +113,10 @@ int Kelf::LoadKelf(const std::string &filename) printf(" %02X", header.UserDefined[i]); if (!memcmp(header.UserDefined, USER_HEADER_FMCB, 16)) printf(" (FMCB)\n"); + else if (!memcmp(header.UserDefined, USER_HEADER_DNASLOAD, 16)) + printf(" (DNASLOAD)\n"); + else if (!memcmp(header.UserDefined, USER_HEADER_NAMCO_SECURITY_DONGLE_BOOTFILE, 16)) + printf(" (System 2x6 Dongle BootFile)\n"); else if (!memcmp(header.UserDefined, USER_HEADER_FHDB, 16)) printf(" (FHDB)\n"); else if (!memcmp(header.UserDefined, USER_HEADER_MBR, 16)) @@ -132,19 +141,19 @@ int Kelf::LoadKelf(const std::string &filename) break; } switch (header.ApplicationType) { - case 0: + case KELFTYPE_DISC_WOOBLE: printf("header.ApplicationType = 0 (disc wobble \?)\n"); break; - case 1: + case KELFTYPE_XOSDMAIN: printf("header.ApplicationType = 1 (xosdmain)\n"); break; - case 5: + case KELFTYPE_DVDPLAYER_KIRX: printf("header.ApplicationType = 5 (dvdplayer kirx)\n"); break; - case 7: + case KELFTYPE_DVDPLAYER_KELF: printf("header.ApplicationType = 7 (dvdplayer kelf)\n"); break; - case 11: + case KELFTYPE_EARLY_MBR: printf("header.ApplicationType = 11 (early mbr \?)\n"); break; default: @@ -155,9 +164,9 @@ int Kelf::LoadKelf(const std::string &filename) break; } printf("header.Flags = %#X", header.Flags); - if (header.Flags == 0x022c) + if (header.Flags == HDR_PREDEF_KELF) printf(" - kelf:"); - else if (header.Flags == 0x021c) + else if (header.Flags == HDR_PREDEF_KIRX) printf(" - kirx:"); else printf(" - unknown:"); @@ -199,7 +208,7 @@ int Kelf::LoadKelf(const std::string &filename) printf("header.MGZones = %#X |", header.MGZones); if (header.MGZones == 0) printf("All regions blocked (useless)|"); - else if (header.MGZones == 0xFF) + else if (header.MGZones == REGION_ALL_ALLOWED ) printf("All regions allowed|"); else { if (header.MGZones & REGION_JP) @@ -248,7 +257,7 @@ int Kelf::LoadKelf(const std::string &filename) fread(Kc.data(), 1, Kc.size(), f); DecryptKeys(KEK); - printf("\nKbit ="); + printf("Kbit ="); for (size_t i = 0; i < 16; ++i) printf(" %02X", (unsigned char)Kbit[i]); @@ -257,9 +266,9 @@ int Kelf::LoadKelf(const std::string &filename) printf(" %02X", (unsigned char)Kc[i]); // arcade - if (ks.GetArcadeKbit().size() && ks.GetArcadeKc().size()) { - memcpy(Kbit.data(), ks.GetArcadeKbit().data(), 16); - memcpy(Kc.data(), ks.GetArcadeKc().data(), 16); + if (ks.GetOverrideKbit().size() && ks.GetOverrideKc().size()) { + memcpy(Kbit.data(), ks.GetOverrideKbit().data(), 16); + memcpy(Kc.data(), ks.GetOverrideKc().data(), 16); } int BitTableSize = header.HeaderSize - ftell(f) - 8 - 8; @@ -362,18 +371,22 @@ int Kelf::SaveKelf(const std::string &filename, int headerid) static uint8_t *USER_HEADER; switch (headerid) { - case 0: + case HEADER::FMCB: USER_HEADER = USER_HEADER_FMCB; break; - case 1: + case HEADER::FHDB: USER_HEADER = USER_HEADER_FHDB; break; - case 2: + case HEADER::MBR: USER_HEADER = USER_HEADER_MBR; break; + case HEADER::DNASLOAD: + USER_HEADER = USER_HEADER_DNASLOAD; + break; + default: USER_HEADER = USER_HEADER_FHDB; break; @@ -382,11 +395,11 @@ int Kelf::SaveKelf(const std::string &filename, int headerid) memcpy(header.UserDefined, USER_HEADER, 16); header.ContentSize = Content.size(); // sometimes zero header.HeaderSize = bitTable.HeaderSize; // header + header signature + kbit + kc + bittable + bittable signature + root signature - header.SystemType = SYSTEM_TYPE_PS2; // same for COH (arcade) - header.ApplicationType = 1; // 1 = xosdmain, 5 = dvdplayer kirx 7 = dvdplayer kelf 0xB - ?? 0x00 - ?? + header.SystemType = GSystemtype; // same for COH (arcade) + header.ApplicationType = GApplicationType; // 1 = xosdmain, 5 = dvdplayer kirx 7 = dvdplayer kelf 0xB - ?? 0x00 - ?? // TODO: implement and check 3DES/1DES difference based on header.Flags. In both - encryption and decryption. - header.Flags = 0x022C; // ?? 00000010 00101100 binary, 0x021C for kirx - header.MGZones = 0xFF; // region bit, 1 - allowed + header.Flags = GFlags; // ?? 00000010 00101100 binary, 0x021C for kirx + header.MGZones = GMGZones; // region bit, 1 - allowed header.BitCount = 0; // ?? balika, wisi: strange value, represents number of blacklisted iLinkID, ConsoleID // iLinkID (8 bytes), consoleID (8 bytes) placed between header.MGZones and HeaderSignature @@ -447,15 +460,19 @@ int Kelf::LoadContent(const std::string &filename, int headerid) // TODO: encrypted Kbit hold some useful data static uint8_t *USER_Kbit; switch (headerid) { - case 0: + case HEADER::FMCB: + case HEADER::DNASLOAD: USER_Kbit = USER_Kbit_FMCB; break; - case 1: + + case HEADER::FHDB: USER_Kbit = USER_Kbit_FHDB; break; - case 2: + + case HEADER::MBR: USER_Kbit = USER_Kbit_MBR; break; + default: USER_Kbit = USER_Kbit_FHDB; break; @@ -471,9 +488,9 @@ int Kelf::LoadContent(const std::string &filename, int headerid) std::fill(Kc.data(), Kc.data() + 16, 0x00); // arcade - if (ks.GetArcadeKbit().size() && ks.GetArcadeKc().size()) { - memcpy(Kbit.data(), ks.GetArcadeKbit().data(), 16); - memcpy(Kc.data(), ks.GetArcadeKc().data(), 16); + if (ks.GetOverrideKbit().size() && ks.GetOverrideKc().size()) { + memcpy(Kbit.data(), ks.GetOverrideKbit().data(), 16); + memcpy(Kc.data(), ks.GetOverrideKc().data(), 16); } std::fill(bitTable.gap, bitTable.gap + 3, 0); diff --git a/src/kelf.h b/src/kelf.h index e9b9986..31eb0ee 100644 --- a/src/kelf.h +++ b/src/kelf.h @@ -30,9 +30,20 @@ #define SYSTEM_TYPE_PS2 0 // same for COH (arcade) #define SYSTEM_TYPE_PSX 1 +enum HEADER { + INVALID = -1, + FMCB = 0, + FHDB, + MBR, + DNASLOAD, +}; + static uint8_t USER_HEADER_FMCB[16] = {0x01, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x4A, 0x00, 0x01, 0x02, 0x19, 0x00, 0x00, 0x00, 0x56}; static uint8_t USER_HEADER_FHDB[16] = {0x01, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x4A, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x1B}; static uint8_t USER_HEADER_MBR[16] = {0x01, 0x00, 0x00, 0x04, 0x00, 0x02, 0x01, 0x57, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A}; +static uint8_t USER_HEADER_DNASLOAD[16] = {0x01, 0x00, 0x00, 0x04, 0x00, 0x06, 0x00, 0x4A, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02}; + +static uint8_t USER_HEADER_NAMCO_SECURITY_DONGLE_BOOTFILE[16] = {0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00}; static uint8_t USER_Kbit_MBR[16] = {0x6f, 0x6f, 0x40, 0x07, 0x59, 0x23, 0x2a, 0x48, 0x03, 0x45, 0xf6, 0xee, 0x9f, 0x24, 0xfe, 0xf1}; static uint8_t USER_Kbit_FHDB[16] = {0xcc, 0x3a, 0x5a, 0x4e, 0x5c, 0x7f, 0x7c, 0x23, 0xb7, 0x5e, 0x9b, 0xf6, 0xa0, 0x44, 0x4e, 0x05}; @@ -74,6 +85,8 @@ struct KELFHeader #define HDR_FLAG13 0x2000 // Unset. ?? #define HDR_FLAG14 0x4000 // Unset. ?? #define HDR_FLAG15 0x8000 // Unset. ?? +#define HDR_PREDEF_KELF 0x022c +#define HDR_PREDEF_KIRX 0x021c // MGZones region flags. If unset - blocked in that region #define REGION_JP 0x1 // Japan @@ -84,6 +97,13 @@ struct KELFHeader #define REGION_RU 0x20 // Russia #define REGION_CH 0x40 // China #define REGION_MX 0x80 // Mexico +#define REGION_ALL_ALLOWED 0xFF + +#define KELFTYPE_DISC_WOOBLE 0 +#define KELFTYPE_XOSDMAIN 1 +#define KELFTYPE_DVDPLAYER_KIRX 5 +#define KELFTYPE_DVDPLAYER_KELF 7 +#define KELFTYPE_EARLY_MBR 11 struct BitTable { diff --git a/src/kelftool.cpp b/src/kelftool.cpp index 89b74df..0c87e07 100644 --- a/src/kelftool.cpp +++ b/src/kelftool.cpp @@ -16,10 +16,16 @@ */ #include #include +#include #include "keystore.h" #include "kelf.h" +uint8_t GSystemtype = SYSTEM_TYPE_PS2; +uint8_t GMGZones = REGION_ALL_ALLOWED; +uint16_t GFlags = HDR_PREDEF_KELF; +uint8_t GApplicationType = KELFTYPE_XOSDMAIN; + // TODO: implement load/save kelf header configuration for byte-perfect encryption, decryption std::string getKeyStorePath() @@ -33,16 +39,25 @@ std::string getKeyStorePath() int decrypt(int argc, char **argv) { + std::string KeyStoreEntry = "default"; + if (argc < 3) { printf("%s decrypt \n", argv[0]); return -1; } + for (int x = 3; x < argc; x++) + { + if (!strncmp("--keys=", argv[x], strlen("--keys="))) { + KeyStoreEntry = &argv[x][7]; + } + } + KeyStore ks; - int ret = ks.Load(getKeyStorePath()); + int ret = ks.Load("./PS2KEYS.dat", KeyStoreEntry); if (ret != 0) { // try to load keys from working directory - ret = ks.Load("./PS2KEYS.dat"); + ret = ks.Load(getKeyStorePath(), KeyStoreEntry); if (ret != 0) { printf("Failed to load keystore: %d - %s\n", ret, KeyStore::getErrorString(ret).c_str()); return ret; @@ -66,7 +81,7 @@ int decrypt(int argc, char **argv) int encrypt(int argc, char **argv) { - + std::string KeyStoreEntry = "default"; int headerid = -1; if (argc < 4) { @@ -75,26 +90,72 @@ int encrypt(int argc, char **argv) return -1; } + for (int x = 4; x < argc; x++) + { + if (!strncmp("--keys=", argv[x], strlen("--keys="))) { + printf("- Custom keyset %s\n", &argv[x][7]); + KeyStoreEntry = &argv[x][7]; + } else if (!strncmp("--systemtype=", argv[x], strlen("--systemtype="))) { + const char* a = &argv[x][13]; + long t; + if (!strcmp(a, "PS2")) { + GSystemtype = SYSTEM_TYPE_PS2; + } else if (!strcmp(a, "PSX")) { + GSystemtype = SYSTEM_TYPE_PSX; + } else if ((t = strtoul(a, NULL, 10)) <= std::numeric_limits::max()) { + GSystemtype = (uint8_t)t; + } + } else if (!strncmp("--kflags=", argv[x], strlen("--kflags="))) { + const char* a = &argv[x][9]; + unsigned long t; + if (!strcmp(a, "KELF")) { + GFlags = HDR_PREDEF_KELF; + } else if (!strcmp(a, "KIRX")) { + GFlags = HDR_PREDEF_KIRX; + } else if ((t = strtoul(a, NULL, 16)) <= std::numeric_limits::max()) { + GFlags = (uint16_t)t; + if ((GFlags & HDR_FLAG4_1DES) && (GFlags & HDR_FLAG4_3DES)) { + printf("WARNING: 0x%x specifies both Single and Triple DES. only one should be defined\n", GFlags); + } + } + } else if (!strncmp("--mgzone=", argv[x], strlen("--mgzone="))) { + const char* a = &argv[x][9]; + long t; + if ((t = strtoul(a, NULL, 16))::max()) { + GMGZones = (uint8_t)t; + } + } else if (!strncmp("--apptype=", argv[x], strlen("--apptype="))) { + const char* a = &argv[x][10]; + long t; + if ((t = strtoul(a, NULL, 16)) <= std::numeric_limits::max()) { + GApplicationType = (uint8_t)t; + } + } + } + if (strcmp("fmcb", argv[1]) == 0) - headerid = 0; + headerid = HEADER::FMCB; if (strcmp("fhdb", argv[1]) == 0) - headerid = 1; + headerid = HEADER::FHDB; if (strcmp("mbr", argv[1]) == 0) - headerid = 2; + headerid = HEADER::MBR; + + if (strcmp("dnasload", argv[1]) == 0) + headerid = HEADER::DNASLOAD; - if (headerid == -1) { + if (headerid == HEADER::INVALID) { printf("Invalid header: %s\n", argv[1]); return -1; } KeyStore ks; - int ret = ks.Load(getKeyStorePath()); + int ret = ks.Load("./PS2KEYS.dat", KeyStoreEntry); if (ret != 0) { // try to load keys from working directory - ret = ks.Load("./PS2KEYS.dat"); + ret = ks.Load(getKeyStorePath(), KeyStoreEntry); if (ret != 0) { printf("Failed to load keystore: %d - %s\n", ret, KeyStore::getErrorString(ret).c_str()); return ret; @@ -124,12 +185,13 @@ int main(int argc, char **argv) printf("Available submodules:\n"); printf("\tdecrypt - decrypt and check signature of kelf files\n"); printf("\tencrypt - encrypt and sign kelf files : fmcb, fhdb, mbr\n"); - printf("\t\tfmcb - for retail PS2 memory cards\n"); - printf("\t\tfhdb - for retail PS2 HDD (HDD OSD / BB Navigator)\n"); - printf("\t\tmbr - for retail PS2 HDD (mbr injection).\n"); - printf("\t\t Note: for mbr elf should load from 0x100000 and should be without headers:\n"); - printf("\t\t readelf -h should show 0x100000 or 0x100008\n"); - printf("\t\t $(EE_OBJCOPY) -O binary -v \n"); + printf("\t\tfmcb - for retail PS2 memory cards\n"); + printf("\t\tdnasload - for retail PS2 memory cardsfor retail PS2 memory cards (PSX Whitelist)\n"); + printf("\t\tfhdb - for retail PS2 HDD (HDD OSD / BB Navigator)\n"); + printf("\t\tmbr - for retail PS2 HDD (mbr injection).\n"); + printf("\t\t Note: for mbr elf should load from 0x100000 and should be without headers:\n"); + printf("\t\t readelf -h should show 0x100000 or 0x100008\n"); + printf("\t\t $(EE_OBJCOPY) -O binary -v \n"); return -1; } diff --git a/src/keystore.cpp b/src/keystore.cpp index ec7ca5d..ae7803b 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -15,6 +15,7 @@ * along with this program. If not, see . */ #include "keystore.h" +#include "inipp.h" #include #include #include @@ -52,55 +53,43 @@ std::string hex2bin(const std::string &src) return hex; } -int KeyStore::Load(std::string filename) +int KeyStore::Load(std::string filename, std::string KeySet = "default") { + inipp::Ini ini; std::ifstream infile(filename); if (infile.fail()) return KEYSTORE_ERROR_OPEN_FAILED; - - std::string line; - while (std::getline(infile, line)) { - std::vector tokens = split(line, '='); - - - if (tokens.size() != 2) - - return KEYSTORE_ERROR_LINE_NOT_KEY_VALUE; - - - std::string key = tokens[0]; - std::string value = tokens[1]; - - if (value.size() % 2 != 0) - return KEYSTORE_ERROR_ODD_LEN_VALUE; - - value = hex2bin(value); - - if (key == "MG_SIG_MASTER_KEY") - SignatureMasterKey = value; - if (key == "MG_SIG_HASH_KEY") - SignatureHashKey = value; - if (key == "MG_KBIT_MASTER_KEY") - KbitMasterKey = value; - if (key == "MG_KBIT_IV") - KbitIV = value; - if (key == "MG_KC_MASTER_KEY") - KcMasterKey = value; - if (key == "MG_KC_IV") - KcIV = value; - if (key == "MG_ROOTSIG_MASTER_KEY") - RootSignatureMasterKey = value; - if (key == "MG_ROOTSIG_HASH_KEY") - RootSignatureHashKey = value; - if (key == "MG_CONTENT_TABLE_IV") - ContentTableIV = value; - if (key == "MG_CONTENT_IV") - ContentIV = value; - if (key == "ARCADE_KBIT") - ArcadeKbit = value; - if (key == "ARCADE_KC") - ArcadeKc = value; + ini.parse(infile); + ini.strip_trailing_comments(); + ini.default_section(ini.sections["default"]); + ini.interpolate(); + if (ini.sections.find(KeySet) == ini.sections.end()) { + return KEYSTORE_SECTION_MISSING; } + inipp::get_value(ini.sections[KeySet], "MG_SIG_MASTER_KEY", SignatureMasterKey); + inipp::get_value(ini.sections[KeySet], "MG_SIG_HASH_KEY", SignatureHashKey); + inipp::get_value(ini.sections[KeySet], "MG_KBIT_MASTER_KEY", KbitMasterKey); + inipp::get_value(ini.sections[KeySet], "MG_KBIT_IV", KbitIV); + inipp::get_value(ini.sections[KeySet], "MG_KC_MASTER_KEY", KcMasterKey); + inipp::get_value(ini.sections[KeySet], "MG_KC_IV", KcIV); + inipp::get_value(ini.sections[KeySet], "MG_ROOTSIG_MASTER_KEY", RootSignatureMasterKey); + inipp::get_value(ini.sections[KeySet], "MG_ROOTSIG_HASH_KEY", RootSignatureHashKey); + inipp::get_value(ini.sections[KeySet], "MG_CONTENT_TABLE_IV", ContentTableIV); + inipp::get_value(ini.sections[KeySet], "MG_CONTENT_IV", ContentIV); + inipp::get_value(ini.sections[KeySet], "OVERRIDE_KBIT", OverrideKbit); + inipp::get_value(ini.sections[KeySet], "OVERRIDE_KC", OverrideKc); + SignatureMasterKey = hex2bin(SignatureMasterKey); + SignatureHashKey = hex2bin(SignatureHashKey); + KbitMasterKey = hex2bin(KbitMasterKey); + KbitIV = hex2bin(KbitIV); + KcMasterKey = hex2bin(KcMasterKey); + KcIV = hex2bin(KcIV); + RootSignatureMasterKey = hex2bin(RootSignatureMasterKey); + RootSignatureHashKey = hex2bin(RootSignatureHashKey); + ContentTableIV = hex2bin(ContentTableIV); + ContentIV = hex2bin(ContentIV); + OverrideKbit = hex2bin(OverrideKbit); + OverrideKc = hex2bin(OverrideKc); if (SignatureMasterKey.size() == 0 || SignatureHashKey.size() == 0 || KbitMasterKey.size() == 0 || KbitIV.size() == 0 || @@ -125,6 +114,8 @@ std::string KeyStore::getErrorString(int err) return "Odd length hex value in keystore!"; case KEYSTORE_ERROR_MISSING_KEY: return "Some keys are missing from the keystore!"; + case KEYSTORE_SECTION_MISSING: + return "Cant find requested section in keystore!"; default: return "Unknown error"; } diff --git a/src/keystore.h b/src/keystore.h index 436a983..24ea688 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -23,6 +23,7 @@ #define KEYSTORE_ERROR_LINE_NOT_KEY_VALUE -2 #define KEYSTORE_ERROR_ODD_LEN_VALUE -3 #define KEYSTORE_ERROR_MISSING_KEY -4 +#define KEYSTORE_SECTION_MISSING -5 class KeyStore { @@ -36,11 +37,11 @@ class KeyStore std::string RootSignatureHashKey; std::string ContentTableIV; std::string ContentIV; - std::string ArcadeKbit; - std::string ArcadeKc; + std::string OverrideKbit; + std::string OverrideKc; public: - int Load(std::string filename); + int Load(std::string filename, std::string KeyStoreEntry); std::string GetSignatureMasterKey() { return SignatureMasterKey; } std::string GetSignatureHashKey() { return SignatureHashKey; } @@ -52,8 +53,8 @@ class KeyStore std::string GetRootSignatureHashKey() { return RootSignatureHashKey; } std::string GetContentTableIV() { return ContentTableIV; } std::string GetContentIV() { return ContentIV; } - std::string GetArcadeKbit() { return ArcadeKbit; } - std::string GetArcadeKc() { return ArcadeKc; } + std::string GetOverrideKbit() { return OverrideKbit; } + std::string GetOverrideKc() { return OverrideKc; } static std::string getErrorString(int err); };