Merge pull request #88 from sagumamugas/nca-patches

Support for nca patches
This commit is contained in:
Jack 2023-01-15 22:58:28 +08:00 committed by GitHub
commit 21e1e33e55
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 78 additions and 8 deletions

2
deps/libpietendo vendored

@ -1 +1 @@
Subproject commit 994e70282691fb8cdb9276906500fd873ac51270
Subproject commit 40d9332a2d5ffc96dda91dc41b73cd979ffd8d7e

View file

@ -6,6 +6,7 @@
#include <pietendo/hac/AesKeygen.h>
#include <pietendo/hac/HierarchicalSha256Stream.h>
#include <pietendo/hac/HierarchicalIntegrityStream.h>
#include <pietendo/hac/BKTREncryptedStream.h>
#include <pietendo/hac/PartitionFsSnapshotGenerator.h>
#include <pietendo/hac/RomFsSnapshotGenerator.h>
#include <pietendo/hac/CombinedFsSnapshotGenerator.h>
@ -48,6 +49,11 @@ void nstool::NcaProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& fi
mFile = file;
}
void nstool::NcaProcess::setBaseNCAPath(const tc::Optional<tc::io::Path>& 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<tc::io::IStream> base_stream = std::make_shared<tc::io::FileStream>(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>(tc::io::SubStream(mFile, info.offset, info.size));
info.raw_reader = std::make_shared<tc::io::SubStream>(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>(tc::crypto::Aes128CtrEncryptedStream(info.reader, partition_key, partition_ctr));
info.decrypt_reader = std::make_shared<tc::crypto::Aes128CtrEncryptedStream>(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<tc::io::IStream> 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>(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>(pie::hac::HierarchicalSha256Stream(info.reader, info.hierarchicalsha256_hdr));
info.reader = std::make_shared<pie::hac::HierarchicalSha256Stream>(pie::hac::HierarchicalSha256Stream(info.decrypt_reader, info.hierarchicalsha256_hdr));
break;
case (pie::hac::nca::HashType_HierarchicalIntegrity):
info.reader = std::make_shared<pie::hac::HierarchicalIntegrityStream>(pie::hac::HierarchicalIntegrityStream(info.reader, info.hierarchicalintegrity_hdr));
info.reader = std::make_shared<pie::hac::HierarchicalIntegrityStream>(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)));

View file

@ -21,6 +21,8 @@ public:
void setKeyCfg(const KeyBag& keycfg);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
void setBaseNCAPath(const tc::Optional<tc::io::Path>& keycfg);
// fs specific
void setShowFsTree(bool show_fs_tree);
@ -39,6 +41,7 @@ private:
KeyBag mKeyCfg;
CliOutputMode mCliOutputMode;
bool mVerify;
tc::Optional<tc::io::Path> baseNcaPath;
// fs processing
std::shared_ptr<tc::io::IFileSystem> mFileSystem;
@ -93,6 +96,8 @@ private:
// raw partition data
struct sPartitionInfo
{
std::shared_ptr<tc::io::IStream> raw_reader;
std::shared_ptr<tc::io::IStream> decrypt_reader;
std::shared_ptr<tc::io::IStream> reader;
tc::io::VirtualFileSystem::FileSystemSnapshot fs_snapshot;
std::shared_ptr<tc::io::IFileSystem> fs_reader;
@ -126,6 +131,8 @@ private:
void displayHeader();
void processPartitions();
NcaProcess readBaseNCA();
std::string getContentTypeForMountStr(pie::hac::nca::ContentType cont_type) const;
};

View file

@ -645,6 +645,8 @@ void nstool::SettingsInitializer::parse_args(const std::vector<std::string>& arg
opts.registerOptionHandler(std::shared_ptr<CustomExtractDataPathOptionHandler>(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part2" }, tc::io::Path("/2/"))));
opts.registerOptionHandler(std::shared_ptr<CustomExtractDataPathOptionHandler>(new CustomExtractDataPathOptionHandler(fs.extract_jobs, { "--part3" }, tc::io::Path("/3/"))));
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(new SingleParamPathOptionHandler(nca.base_nca_path, { "--basenca" })));
// kip options
opts.registerOptionHandler(std::shared_ptr<SingleParamPathOptionHandler>(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 <out path>\")\n");
fmt::print(" --secure Extract \"secure\" partition to directory. (Alias for \"-x /secure <out path>\")\n");
fmt::print("\n NCA (Nintendo Content Archive)\n");
fmt::print(" {:s} [--fstree] [-x [<virtual path>] <out path>] [--bodykey <key> --titlekey <key> -tik <tik path>] <.nca file>\n", BIN_NAME);
fmt::print(" {:s} [--fstree] [-x [<virtual path>] <out path>] [--bodykey <key> --titlekey <key> -tik <tik path> --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 <out path>\")\n");
fmt::print(" --part2 Extract partition \"2\" to directory. (Alias for \"-x /2 <out path>\")\n");
fmt::print(" --part3 Extract partition \"3\" to directory. (Alias for \"-x /3 <out path>\")\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 <inst. type>] <file>\n", BIN_NAME);
fmt::print(" --listapi Print SDK API List.\n");

View file

@ -76,6 +76,7 @@ struct Settings
tc::Optional<tc::io::Path> part1_extract_path;
tc::Optional<tc::io::Path> part2_extract_path;
tc::Optional<tc::io::Path> part3_extract_path;
tc::Optional<tc::io::Path> base_nca_path;
} nca;
// KIP options
@ -110,6 +111,8 @@ struct Settings
kip.extract_path = tc::Optional<tc::io::Path>();
nca.base_nca_path = tc::Optional<tc::io::Path>();
aset.icon_extract_path = tc::Optional<tc::io::Path>();
aset.nacp_extract_path = tc::Optional<tc::io::Path>();
}

View file

@ -75,6 +75,7 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
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);