diff --git a/deps/libpietendo b/deps/libpietendo index 994e702..40d9332 160000 --- a/deps/libpietendo +++ b/deps/libpietendo @@ -1 +1 @@ -Subproject commit 994e70282691fb8cdb9276906500fd873ac51270 +Subproject commit 40d9332a2d5ffc96dda91dc41b73cd979ffd8d7e diff --git a/src/NcaProcess.cpp b/src/NcaProcess.cpp index 041b414..cd406ed 100644 --- a/src/NcaProcess.cpp +++ b/src/NcaProcess.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,11 @@ void nstool::NcaProcess::setInputFile(const std::shared_ptr& fi mFile = file; } +void nstool::NcaProcess::setBaseNCAPath(const tc::Optional& baseNCA) +{ + baseNcaPath = baseNCA; +} + void nstool::NcaProcess::setKeyCfg(const KeyBag& keycfg) { mKeyCfg = keycfg; @@ -211,6 +217,26 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys() } } +nstool::NcaProcess nstool::NcaProcess::readBaseNCA() { + if (baseNcaPath.isNull()) { + throw tc::Exception(mModuleName, "Base NCA not supplied. Necessary for update NCA."); + } + std::shared_ptr base_stream = std::make_shared(tc::io::FileStream(baseNcaPath.get(), tc::io::FileMode::Open, tc::io::FileAccess::Read)); + + NcaProcess obj; + nstool::CliOutputMode cliOutput; + cliOutput.show_basic_info = false; + cliOutput.show_extended_info = false; + cliOutput.show_keydata = false; + cliOutput.show_layout = false; + obj.setCliOutputMode(cliOutput); + obj.setVerifyMode(true); + obj.setKeyCfg(mKeyCfg); + obj.setInputFile(base_stream); + obj.process(); + return obj; +} + void nstool::NcaProcess::generatePartitionConfiguration() { for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++) @@ -266,13 +292,13 @@ void nstool::NcaProcess::generatePartitionConfiguration() else { // create raw partition - info.reader = std::make_shared(tc::io::SubStream(mFile, info.offset, info.size)); + info.raw_reader = std::make_shared(tc::io::SubStream(mFile, info.offset, info.size)); // handle encryption if required reader based on encryption type if (info.enc_type == pie::hac::nca::EncryptionType_None) { // no encryption so do nothing - //info.reader = info.reader; + info.decrypt_reader = info.raw_reader; } else if (info.enc_type == pie::hac::nca::EncryptionType_AesCtr) { @@ -287,10 +313,40 @@ void nstool::NcaProcess::generatePartitionConfiguration() tc::crypto::IncrementCounterAes128Ctr(partition_ctr.data(), info.offset>>4); // create decryption stream - info.reader = std::make_shared(tc::crypto::Aes128CtrEncryptedStream(info.reader, partition_key, partition_ctr)); + info.decrypt_reader = std::make_shared(tc::crypto::Aes128CtrEncryptedStream(info.raw_reader, partition_key, partition_ctr)); } - else if (info.enc_type == pie::hac::nca::EncryptionType_AesXts || info.enc_type == pie::hac::nca::EncryptionType_AesCtrEx) + else if (info.enc_type == pie::hac::nca::EncryptionType_AesCtrEx) + { + if (mContentKey.aes_ctr.isNull()) + throw tc::Exception(mModuleName, "AES-CTR Key was not determined"); + + // get partition key + pie::hac::detail::aes128_key_t partition_key = mContentKey.aes_ctr.get(); + + // get partition counter + pie::hac::detail::aes_iv_t partition_ctr = info.aes_ctr; + tc::crypto::IncrementCounterAes128Ctr(partition_ctr.data(), info.offset >> 4); + + NcaProcess nca_base = readBaseNCA(); + if (nca_base.mHdr.getProgramId() != mHdr.getProgramId()) { + throw tc::Exception(mModuleName, "Invalid base nca. ProgramID diferent."); + } + std::shared_ptr base_reader; + for (auto& partition_base : nca_base.mPartitions) { + if (partition_base.format_type == pie::hac::nca::FormatType::FormatType_RomFs && partition_base.raw_reader != nullptr) + { + base_reader = partition_base.decrypt_reader; + } + } + if (base_reader == nullptr) { + throw tc::Exception(mModuleName, "Cannot determine RomFs from base nca."); + } + + // create decryption stream + info.decrypt_reader = std::make_shared(pie::hac::BKTREncryptedStream(info.raw_reader, partition_key, partition_ctr, fs_header.patch_info, base_reader)); + } + else if (info.enc_type == pie::hac::nca::EncryptionType_AesXts) { throw tc::Exception(mModuleName, fmt::format("EncryptionType({:s}): UNSUPPORTED", pie::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type))); } @@ -306,10 +362,10 @@ void nstool::NcaProcess::generatePartitionConfiguration() case (pie::hac::nca::HashType_None): break; case (pie::hac::nca::HashType_HierarchicalSha256): - info.reader = std::make_shared(pie::hac::HierarchicalSha256Stream(info.reader, info.hierarchicalsha256_hdr)); + info.reader = std::make_shared(pie::hac::HierarchicalSha256Stream(info.decrypt_reader, info.hierarchicalsha256_hdr)); break; case (pie::hac::nca::HashType_HierarchicalIntegrity): - info.reader = std::make_shared(pie::hac::HierarchicalIntegrityStream(info.reader, info.hierarchicalintegrity_hdr)); + info.reader = std::make_shared(pie::hac::HierarchicalIntegrityStream(info.decrypt_reader, info.hierarchicalintegrity_hdr)); break; default: throw tc::Exception(mModuleName, fmt::format("HashType({:s}): UNKNOWN", pie::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type))); diff --git a/src/NcaProcess.h b/src/NcaProcess.h index c943f91..4527968 100644 --- a/src/NcaProcess.h +++ b/src/NcaProcess.h @@ -21,6 +21,8 @@ public: void setKeyCfg(const KeyBag& keycfg); void setCliOutputMode(CliOutputMode type); void setVerifyMode(bool verify); + void setBaseNCAPath(const tc::Optional& keycfg); + // fs specific void setShowFsTree(bool show_fs_tree); @@ -39,6 +41,7 @@ private: KeyBag mKeyCfg; CliOutputMode mCliOutputMode; bool mVerify; + tc::Optional baseNcaPath; // fs processing std::shared_ptr mFileSystem; @@ -93,6 +96,8 @@ private: // raw partition data struct sPartitionInfo { + std::shared_ptr raw_reader; + std::shared_ptr decrypt_reader; std::shared_ptr reader; tc::io::VirtualFileSystem::FileSystemSnapshot fs_snapshot; std::shared_ptr fs_reader; @@ -126,6 +131,8 @@ private: void displayHeader(); void processPartitions(); + NcaProcess readBaseNCA(); + std::string getContentTypeForMountStr(pie::hac::nca::ContentType cont_type) const; }; diff --git a/src/Settings.cpp b/src/Settings.cpp index 9cfa51a..bf615f2 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -645,6 +645,8 @@ void nstool::SettingsInitializer::parse_args(const std::vector& arg opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part2" }, tc::io::Path("/2/")))); opts.registerOptionHandler(std::shared_ptr(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part3" }, tc::io::Path("/3/")))); + opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(nca.base_nca_path, { "--basenca" }))); + // kip options opts.registerOptionHandler(std::shared_ptr(new SingleParamPathOptionHandler(kip.extract_path, { "--kipdir" }))); @@ -796,7 +798,7 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" --normal Extract \"normal\" partition to directory. (Alias for \"-x /normal \")\n"); fmt::print(" --secure Extract \"secure\" partition to directory. (Alias for \"-x /secure \")\n"); fmt::print("\n NCA (Nintendo Content Archive)\n"); - fmt::print(" {:s} [--fstree] [-x [] ] [--bodykey --titlekey -tik ] <.nca file>\n", BIN_NAME); + fmt::print(" {:s} [--fstree] [-x [] ] [--bodykey --titlekey -tik --basenca <.nca file>] <.nca file>\n", BIN_NAME); fmt::print(" --fstree Print filesystem tree.\n"); fmt::print(" -x, --extract Extract a file or directory to local filesystem.\n"); fmt::print(" --titlekey Specify (encrypted) title key extracted from ticket.\n"); @@ -807,6 +809,7 @@ void nstool::SettingsInitializer::usage_text() const fmt::print(" --part1 Extract partition \"1\" to directory. (Alias for \"-x /1 \")\n"); fmt::print(" --part2 Extract partition \"2\" to directory. (Alias for \"-x /2 \")\n"); fmt::print(" --part3 Extract partition \"3\" to directory. (Alias for \"-x /3 \")\n"); + fmt::print(" --basenca Specify base NCA file for update NCA files.\n"); fmt::print("\n NSO (Nintendo Shared Object), NRO (Nintendo Relocatable Object)\n"); fmt::print(" {:s} [--listapi --listsym] [--insttype ] \n", BIN_NAME); fmt::print(" --listapi Print SDK API List.\n"); diff --git a/src/Settings.h b/src/Settings.h index 2406f54..ee1246a 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -76,6 +76,7 @@ struct Settings tc::Optional part1_extract_path; tc::Optional part2_extract_path; tc::Optional part3_extract_path; + tc::Optional base_nca_path; } nca; // KIP options @@ -110,6 +111,8 @@ struct Settings kip.extract_path = tc::Optional(); + nca.base_nca_path = tc::Optional(); + aset.icon_extract_path = tc::Optional(); aset.nacp_extract_path = tc::Optional(); } diff --git a/src/main.cpp b/src/main.cpp index 7e400ea..ccfdc75 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -75,6 +75,7 @@ int umain(const std::vector& args, const std::vector& nstool::NcaProcess obj; obj.setInputFile(infile_stream); + obj.setBaseNCAPath(set.nca.base_nca_path); obj.setKeyCfg(set.opt.keybag); obj.setCliOutputMode(set.opt.cli_output_mode); obj.setVerifyMode(set.opt.verify);