diff --git a/src/Settings.cpp b/src/Settings.cpp index 4e15cbb..a8ff451 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -1,11 +1,15 @@ +#include "Settings.h" +#include "types.h" +#include "version.h" +#include "util.h" + #include #include #include #include #include -#include "types.h" -#include "version.h" -#include "Settings.h" + + #include #include @@ -408,6 +412,12 @@ nstool::SettingsInitializer::SettingsInitializer(const std::vector& opt.keybag.fallback_enc_content_key = mTitleKey; opt.keybag.fallback_content_key = mBodyKey; + // dump keys if requires + if (opt.cli_output_mode.show_keydata) + { + dump_keys(); + } + // determine filetype if not manually specified if (infile.filetype == FILE_TYPE_ERROR) { @@ -614,7 +624,7 @@ void nstool::SettingsInitializer::determine_filetype() #undef _ASSERT_FILE_SIZE } -void nstool::SettingsInitializer::usage_text() +void nstool::SettingsInitializer::usage_text() const { fmt::print("{:s} v{:d}.{:d}.{:d} (C) {:s}\n", APP_NAME, VER_MAJOR, VER_MINOR, VER_PATCH, AUTHORS); fmt::print("Built: {:s} {:s}\n\n", __TIME__, __DATE__); @@ -666,6 +676,222 @@ void nstool::SettingsInitializer::usage_text() fmt::print(" --fsdir Extract RomFS partition to directory.\n"); } +void nstool::SettingsInitializer::dump_keys() const +{ + fmt::print("[KeyConfiguration]\n"); + fmt::print(" NCA Keys:\n"); + for (auto itr = opt.keybag.nca_header_sign0_key.begin(); itr != opt.keybag.nca_header_sign0_key.end(); itr++) + { + dump_rsa_key(itr->second, fmt::format("Header0-SignatureKey-{:02x}", itr->first), 4, opt.cli_output_mode.show_extended_info); + } + for (auto itr = opt.keybag.acid_sign_key.begin(); itr != opt.keybag.acid_sign_key.end(); itr++) + { + dump_rsa_key(itr->second, fmt::format("Acid-SignatureKey-{:02x}", itr->first), 4, opt.cli_output_mode.show_extended_info); + } + if (opt.keybag.nca_header_key.isSet()) + { + fmt::print(" Header-EncryptionKey:\n"); + fmt::print(" Key0: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(opt.keybag.nca_header_key.get()[0].data(), opt.keybag.nca_header_key.get()[0].size(), true, ":")); + fmt::print(" Key1: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(opt.keybag.nca_header_key.get()[1].data(), opt.keybag.nca_header_key.get()[1].size(), true, ":")); + } + std::vector kaek_label = {"Application", "Ocean", "System"}; + for (size_t kaek_index = 0; kaek_index < opt.keybag.nca_key_area_encryption_key.size(); kaek_index++) + { + for (auto itr = opt.keybag.nca_key_area_encryption_key[kaek_index].begin(); itr != opt.keybag.nca_key_area_encryption_key[kaek_index].end(); itr++) + { + fmt::print(" KeyAreaEncryptionKey-{:s}-{:02x}:\n {:s}\n", kaek_label[kaek_index], itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + } + } + for (size_t kaek_index = 0; kaek_index < opt.keybag.nca_key_area_encryption_key_hw.size(); kaek_index++) + { + for (auto itr = opt.keybag.nca_key_area_encryption_key_hw[kaek_index].begin(); itr != opt.keybag.nca_key_area_encryption_key_hw[kaek_index].end(); itr++) + { + fmt::print(" KeyAreaEncryptionKeyHw-{:s}-{:02x}:\n {:s}\n", kaek_label[kaek_index], itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + } + } + fmt::print(" NRR Keys:\n"); + for (auto itr = opt.keybag.nrr_certificate_sign_key.begin(); itr != opt.keybag.nrr_certificate_sign_key.end(); itr++) + { + dump_rsa_key(itr->second, fmt::format("Certificate-SignatureKey-{:02x}", itr->first), 4, opt.cli_output_mode.show_extended_info); + } + fmt::print(" XCI Keys:\n"); + if (opt.keybag.xci_header_sign_key.isSet()) + { + dump_rsa_key(opt.keybag.xci_header_sign_key.get(), fmt::format("Header-SignatureKey"), 4, opt.cli_output_mode.show_extended_info); + } + for (auto itr = opt.keybag.xci_header_key.begin(); itr != opt.keybag.xci_header_key.end(); itr++) + { + fmt::print(" ExtendedHeader-EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + } + + fmt::print(" Package1 Keys:\n"); + for (auto itr = opt.keybag.pkg1_key.begin(); itr != opt.keybag.pkg1_key.end(); itr++) + { + fmt::print(" EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + } + + fmt::print(" Package2 Keys:\n"); + if (opt.keybag.pkg2_sign_key.isSet()) + { + dump_rsa_key(opt.keybag.pkg2_sign_key.get(), fmt::format("Header-SignatureKey"), 4, opt.cli_output_mode.show_extended_info); + } + for (auto itr = opt.keybag.pkg2_key.begin(); itr != opt.keybag.pkg2_key.end(); itr++) + { + fmt::print(" EncryptionKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + } + + fmt::print(" ETicket Keys:\n"); + for (auto itr = opt.keybag.etik_common_key.begin(); itr != opt.keybag.etik_common_key.end(); itr++) + { + fmt::print(" CommonKey-{:02x}:\n {:s}\n", itr->first, tc::cli::FormatUtil::formatBytesAsString(itr->second.data(), itr->second.size(), true, ":")); + } + + fmt::print(" BroadOn Signer Profiles:\n"); + for (auto itr = opt.keybag.broadon_signer.begin(); itr != opt.keybag.broadon_signer.end(); itr++) + { + fmt::print(" {:s}:\n", itr->first); + fmt::print(" SignType: "); + switch(itr->second.key_type) { + case nn::pki::sign::SIGN_ALGO_RSA2048: + fmt::print("RSA-2048\n"); + break; + case nn::pki::sign::SIGN_ALGO_RSA4096: + fmt::print("RSA-4096\n"); + break; + case nn::pki::sign::SIGN_ALGO_ECDSA240: + fmt::print("ECDSA-240\n"); + break; + default: + fmt::print("Unknown\n"); + } + switch(itr->second.key_type) { + case nn::pki::sign::SIGN_ALGO_RSA2048: + case nn::pki::sign::SIGN_ALGO_RSA4096: + dump_rsa_key(itr->second.rsa_key, "RsaKey", 6, opt.cli_output_mode.show_extended_info); + break; + case nn::pki::sign::SIGN_ALGO_ECDSA240: + default: + break; + } + } + + /* + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getContentArchiveHeader0SignKey(rsa2048_key, byte_t(i)) == true) + dumpRsa2048Key(rsa2048_key, "Header0-SignatureKey-" + kKeyIndex[i], 2); + } + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getAcidSignKey(rsa2048_key, byte_t(i)) == true) + dumpRsa2048Key(rsa2048_key, "Acid-SignatureKey-" + kKeyIndex[i], 2); + } + + if (mKeyCfg.getContentArchiveHeaderKey(aesxts_key) == true) + dumpAesXtsKey(aesxts_key, "Header-EncryptionKey", 2); + + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getNcaKeyAreaEncryptionKey(byte_t(i), 0, aes_key) == true) + dumpAesKey(aes_key, "KeyAreaEncryptionKey-Application-" + kKeyIndex[i], 2); + if (mKeyCfg.getNcaKeyAreaEncryptionKey(byte_t(i), 1, aes_key) == true) + dumpAesKey(aes_key, "KeyAreaEncryptionKey-Ocean-" + kKeyIndex[i], 2); + if (mKeyCfg.getNcaKeyAreaEncryptionKey(byte_t(i), 2, aes_key) == true) + dumpAesKey(aes_key, "KeyAreaEncryptionKey-System-" + kKeyIndex[i], 2); + } + + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(byte_t(i), 0, aes_key) == true) + dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-Application-" + kKeyIndex[i], 2); + if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(byte_t(i), 1, aes_key) == true) + dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-Ocean-" + kKeyIndex[i], 2); + if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(byte_t(i), 2, aes_key) == true) + dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-System-" + kKeyIndex[i], 2); + } + + std::cout << " NRR Keys:" << std::endl; + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getNrrCertificateSignKey(rsa2048_key, byte_t(i)) == true) + dumpRsa2048Key(rsa2048_key, "Certificate-SignatureKey-" + kKeyIndex[i], 2); + } + + std::cout << " XCI Keys:" << std::endl; + if (mKeyCfg.getXciHeaderSignKey(rsa2048_key) == true) + dumpRsa2048Key(rsa2048_key, "Header-SignatureKey", 2); + if (mKeyCfg.getXciHeaderKey(aes_key) == true) + dumpAesKey(aes_key, "ExtendedHeader-EncryptionKey", 2); + + + + + std::cout << " Package1 Keys:" << std::endl; + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getPkg1Key(byte_t(i), aes_key) == true) + dumpAesKey(aes_key, "EncryptionKey-" + kKeyIndex[i], 2); + } + + std::cout << " Package2 Keys:" << std::endl; + if (mKeyCfg.getPkg2SignKey(rsa2048_key) == true) + dumpRsa2048Key(rsa2048_key, "Signature Key", 2); + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getPkg2Key(byte_t(i), aes_key) == true) + dumpAesKey(aes_key, "EncryptionKey-" + kKeyIndex[i], 2); + } + + std::cout << " ETicket Keys:" << std::endl; + for (size_t i = 0; i < kMasterKeyNum; i++) + { + if (mKeyCfg.getETicketCommonKey(byte_t(i), aes_key) == true) + dumpAesKey(aes_key, "CommonKey-" + kKeyIndex[i], 2); + } + + if (mKeyCfg.getPkiRootSignKey("Root", rsa4096_key) == true) + dumpRsa4096Key(rsa4096_key, "NNPKI Root Key", 1); + */ +} + +void nstool::SettingsInitializer::dump_rsa_key(const KeyBag::rsa_key_t& key, const std::string& label, size_t indent, bool expanded_key_data) const +{ + std::string indent_str; + + indent_str.clear(); + for (size_t i = 0; i < indent; i++) + { + indent_str += " "; + } + + fmt::print("{:s}{:s}:\n", indent_str, label); + if (key.n.size() > 0) + { + if (expanded_key_data) + { + fmt::print("{:s} Modulus:\n", indent_str); + fmt::print("{:s} {:s}", indent_str, tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(key.n.data(), key.n.size(), true, ":", 0x10, indent + 4, false)); + } + else + { + fmt::print("{:s} Modulus: {:s}\n", indent_str, getTruncatedBytesString(key.n.data(), key.n.size())); + } + } + if (key.d.size() > 0) + { + if (expanded_key_data) + { + fmt::print("{:s} Private Exponent:\n", indent_str); + fmt::print("{:s} {:s}", indent_str, tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(key.d.data(), key.d.size(), true, ":", 0x10, indent + 4, false)); + } + else + { + fmt::print("{:s} Private Exponent: {:s}\n", indent_str, getTruncatedBytesString(key.d.data(), key.d.size())); + } + } +} + + bool nstool::SettingsInitializer::determineValidNcaFromSample(const tc::ByteData& sample) const { if (sample.size() < nn::hac::nca::kHeaderSize) diff --git a/src/Settings.h b/src/Settings.h index 6ba3f5c..c0a1a27 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -132,7 +132,9 @@ public: private: void parse_args(const std::vector& args); void determine_filetype(); - void usage_text(); + void usage_text() const; + void dump_keys() const; + void dump_rsa_key(const KeyBag::rsa_key_t& key, const std::string& label, size_t indent, bool expanded_key_data) const; std::string mModuleLabel; diff --git a/src/util.cpp b/src/util.cpp index b8fb0cc..435fc2d 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -99,6 +99,24 @@ void nstool::writeStreamToStream(const std::shared_ptr& in_stre } } +std::string nstool::getTruncatedBytesString(const byte_t* data, size_t len) +{ + if (data == nullptr) { return fmt::format(""); } + + std::string str = ""; + + if (len <= 8) + { + str = tc::cli::FormatUtil::formatBytesAsString(data, len, true, ""); + } + else + { + str = fmt::format("{:02X}{:02X}{:02X}{:02X}...{:02X}{:02X}{:02X}{:02X}", data[0], data[1], data[2], data[3], data[len-4], data[len-3], data[len-2], data[len-1]); + } + + return str; +} + std::string nstool::getTruncatedBytesString(const byte_t* data, size_t len, bool do_not_truncate) { if (data == nullptr) { return fmt::format(""); } diff --git a/src/util.h b/src/util.h index 2611acc..aa1d962 100644 --- a/src/util.h +++ b/src/util.h @@ -10,6 +10,7 @@ void writeSubStreamToFile(const std::shared_ptr& in_stream, int void writeStreamToFile(const std::shared_ptr& in_stream, const tc::io::Path& out_path, size_t cache_size = 0x10000); void writeStreamToStream(const std::shared_ptr& in_stream, const std::shared_ptr& out_stream, size_t cache_size = 0x10000); +std::string getTruncatedBytesString(const byte_t* data, size_t len); std::string getTruncatedBytesString(const byte_t* data, size_t len, bool do_not_truncate); } \ No newline at end of file