First pass NcaProcess impl

This commit is contained in:
jakcron 2021-10-15 17:29:29 +08:00
parent 91ac1937bf
commit 83e2403dbc
4 changed files with 251 additions and 246 deletions

@ -1 +1 @@
Subproject commit 16aa32f02e7a3c5f0580b59ae5ab330a62a0b61d Subproject commit 6fc9b94f377d41efceef9713215f5c7be9042d9d

View file

@ -4,30 +4,30 @@
#include "RomfsProcess.h" #include "RomfsProcess.h"
#include "MetaProcess.h" #include "MetaProcess.h"
#include <iostream> //#include <iostream>
#include <iomanip> //#include <iomanip>
#include <sstream> //#include <sstream>
#include <fnd/SimpleTextOutput.h> //#include <fnd/SimpleTextOutput.h>
#include <fnd/OffsetAdjustedIFile.h> //#include <fnd/OffsetAdjustedIFile.h>
#include <fnd/AesCtrWrappedIFile.h> //#include <fnd/AesCtrWrappedIFile.h>
#include <fnd/LayeredIntegrityWrappedIFile.h> //#include <fnd/LayeredIntegrityWrappedIFile.h>
#include <nn/hac/ContentArchiveUtil.h> #include <nn/hac/ContentArchiveUtil.h>
#include <nn/hac/AesKeygen.h> #include <nn/hac/AesKeygen.h>
#include <nn/hac/HierarchicalSha256Header.h> #include <nn/hac/HierarchicalSha256Header.h>
#include <nn/hac/HierarchicalIntegrityHeader.h> #include <nn/hac/HierarchicalIntegrityHeader.h>
#include <nn/hac/PartitionFsMetaGenerator.h>
#include <nn/hac/RomFsMetaGenerator.h>
nstool::NcaProcess::NcaProcess() : nstool::NcaProcess::NcaProcess() :
mModuleName("nstool::NcaProcess"),
mFile(), mFile(),
mCliOutputMode(true, false, false, false), mCliOutputMode(true, false, false, false),
mVerify(false), mVerify(false),
mListFs(false) mFileSystem(),
mFsProcess()
{ {
for (size_t i = 0; i < nn::hac::nca::kPartitionNum; i++)
{
mPartitionPath[i].doExtract = false;
}
} }
void nstool::NcaProcess::process() void nstool::NcaProcess::process()
@ -73,52 +73,54 @@ void nstool::NcaProcess::setVerifyMode(bool verify)
mVerify = verify; mVerify = verify;
} }
void nstool::NcaProcess::setPartition0ExtractPath(const std::string& path) void nstool::NcaProcess::setShowFsTree(bool show_fs_tree)
{ {
mPartitionPath[0].path = path; mFsProcess.setShowFsTree(show_fs_tree);
mPartitionPath[0].doExtract = true;
} }
void nstool::NcaProcess::setPartition1ExtractPath(const std::string& path) void nstool::NcaProcess::setFsRootLabel(const std::string& root_label)
{ {
mPartitionPath[1].path = path; mFsProcess.setFsRootLabel(root_label);
mPartitionPath[1].doExtract = true;
} }
void nstool::NcaProcess::setPartition2ExtractPath(const std::string& path) void nstool::NcaProcess::setExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs)
{ {
mPartitionPath[2].path = path; mFsProcess.setExtractJobs(extract_jobs);
mPartitionPath[2].doExtract = true;
} }
void nstool::NcaProcess::setPartition3ExtractPath(const std::string& path) const std::shared_ptr<tc::io::IStorage>& nstool::NcaProcess::getFileSystem() const
{ {
mPartitionPath[3].path = path; return mFileSystem;
mPartitionPath[3].doExtract = true;
}
void nstool::NcaProcess::setListFs(bool list_fs)
{
mListFs = list_fs;
} }
void nstool::NcaProcess::importHeader() void nstool::NcaProcess::importHeader()
{ {
if (*mFile == nullptr) if (mFile == nullptr)
{ {
throw tc::Exception(kModuleName, "No file reader set."); throw tc::Exception(mModuleName, "No file reader set.");
} }
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
// read header block // read header block
(*mFile)->read((byte_t*)&mHdrBlock, 0, sizeof(nn::hac::sContentArchiveHeaderBlock)); if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(nn::hac::sContentArchiveHeaderBlock)))
{
throw tc::Exception(mModuleName, "Corrupt NCA: File too small.");
}
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read((byte_t*)(&mHdrBlock), sizeof(nn::hac::sContentArchiveHeaderBlock));
// decrypt header block // decrypt header block
fnd::aes::sAesXts128Key header_key; if (mKeyCfg.nca_header_key.isNull())
mKeyCfg.getContentArchiveHeaderKey(header_key); {
nn::hac::ContentArchiveUtil::decryptContentArchiveHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, header_key); throw tc::Exception(mModuleName, "Failed to decrypt NCA header. (nca_header_key could not be loaded)");
}
nn::hac::ContentArchiveUtil::decryptContentArchiveHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, mKeyCfg.nca_header_key.get());
// generate header hash // generate header hash
fnd::sha::Sha256((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sContentArchiveHeader), mHdrHash.bytes); tc::crypto::GenerateSha256Hash(mHdrHash.data(), (byte_t*)&mHdrBlock.header, sizeof(nn::hac::sContentArchiveHeader));
// proccess main header // proccess main header
mHdr.fromBytes((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sContentArchiveHeader)); mHdr.fromBytes((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sContentArchiveHeader));
@ -128,7 +130,7 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys()
{ {
// create zeros key // create zeros key
KeyBag::aes128_key_t zero_aesctr_key; KeyBag::aes128_key_t zero_aesctr_key;
memset(zero_aesctr_key.key, 0, sizeof(zero_aesctr_key)); memset(zero_aesctr_key.data(), 0, zero_aesctr_key.size());
// get key data from header // get key data from header
byte_t masterkey_rev = nn::hac::ContentArchiveUtil::getMasterKeyRevisionFromKeyGeneration(mHdr.getKeyGeneration()); byte_t masterkey_rev = nn::hac::ContentArchiveUtil::getMasterKeyRevisionFromKeyGeneration(mHdr.getKeyGeneration());
@ -136,7 +138,7 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys()
// process key area // process key area
sKeys::sKeyAreaKey kak; sKeys::sKeyAreaKey kak;
KeyBag::aes128_key_t key_area_enc_key; //KeyBag::aes128_key_t key_area_enc_key;
const KeyBag::aes128_key_t* key_area = (const KeyBag::aes128_key_t*) mHdr.getKeyArea(); const KeyBag::aes128_key_t* key_area = (const KeyBag::aes128_key_t*) mHdr.getKeyArea();
for (size_t i = 0; i < nn::hac::nca::kKeyAreaKeyNum; i++) for (size_t i = 0; i < nn::hac::nca::kKeyAreaKeyNum; i++)
@ -145,17 +147,18 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys()
{ {
kak.index = (byte_t)i; kak.index = (byte_t)i;
kak.enc = key_area[i]; kak.enc = key_area[i];
kak.decrypted = false;
// key[0-3] // key[0-3]
if (i < 4 && mKeyCfg.getNcaKeyAreaEncryptionKey(masterkey_rev, keak_index, key_area_enc_key) == true) if (i < 4 && mKeyCfg.nca_key_area_encryption_key[keak_index].find(masterkey_rev) != mKeyCfg.nca_key_area_encryption_key[keak_index].end())
{ {
kak.decrypted = true; kak.decrypted = true;
nn::hac::AesKeygen::generateKey(kak.dec.key, kak.enc.key, key_area_enc_key.key); nn::hac::AesKeygen::generateKey(kak.dec.data(), kak.enc.data(), mKeyCfg.nca_key_area_encryption_key[keak_index][masterkey_rev].data());
} }
// key[KEY_AESCTR_HW] // key[KEY_AESCTR_HW]
else if (i == nn::hac::nca::KEY_AESCTR_HW && mKeyCfg.getNcaKeyAreaEncryptionKeyHw(masterkey_rev, keak_index, key_area_enc_key) == true) else if (i == nn::hac::nca::KEY_AESCTR_HW && mKeyCfg.nca_key_area_encryption_key_hw[keak_index].find(masterkey_rev) != mKeyCfg.nca_key_area_encryption_key_hw[keak_index].end())
{ {
kak.decrypted = true; kak.decrypted = true;
nn::hac::AesKeygen::generateKey(kak.dec.key, kak.enc.key, key_area_enc_key.key); nn::hac::AesKeygen::generateKey(kak.dec.data(), kak.enc.data(), mKeyCfg.nca_key_area_encryption_key_hw[keak_index][masterkey_rev].data());
} }
else else
{ {
@ -165,69 +168,64 @@ void nstool::NcaProcess::generateNcaBodyEncryptionKeys()
} }
} }
// set flag to indicate that the keys are not available // clear content key
mContentKey.aes_ctr.isSet = false; mContentKey.aes_ctr = tc::Optional<nn::hac::detail::aes128_key_t>();
// if this has a rights id, the key needs to be sourced from a ticket // if this has a rights id, the key needs to be sourced from a ticket
if (mHdr.hasRightsId() == true) if (mHdr.hasRightsId() == true)
{ {
KeyBag::aes128_key_t tmp_key; KeyBag::aes128_key_t tmp_key;
if (mKeyCfg.getNcaExternalContentKey(mHdr.getRightsId(), tmp_key) == true) if (mKeyCfg.external_content_keys.find(mHdr.getRightsId()) != mKeyCfg.external_content_keys.end())
{ {
mContentKey.aes_ctr = tmp_key; mContentKey.aes_ctr = mKeyCfg.external_content_keys[mHdr.getRightsId()];
} }
else if (mKeyCfg.getNcaExternalContentKey(kDummyRightsIdForUserTitleKey, tmp_key) == true) else if (mKeyCfg.fallback_content_key.isSet())
{ {
KeyBag::aes128_key_t common_key; mContentKey.aes_ctr = mKeyCfg.fallback_content_key.get();
if (mKeyCfg.getETicketCommonKey(masterkey_rev, common_key) == true) }
else if (mKeyCfg.fallback_enc_content_key.isSet())
{
tmp_key = mKeyCfg.fallback_enc_content_key.get();
if (mKeyCfg.etik_common_key.find(masterkey_rev) != mKeyCfg.etik_common_key.end())
{ {
nn::hac::AesKeygen::generateKey(tmp_key.key, tmp_key.key, common_key.key); nn::hac::AesKeygen::generateKey(tmp_key.data(), tmp_key.data(), mKeyCfg.etik_common_key[masterkey_rev].data());
mContentKey.aes_ctr = tmp_key;
} }
mContentKey.aes_ctr = tmp_key;
} }
} }
// otherwise decrypt key area // otherwise used decrypt key area
else else
{ {
KeyBag::aes128_key_t kak_aes_ctr = zero_aesctr_key;
for (size_t i = 0; i < mContentKey.kak_list.size(); i++) for (size_t i = 0; i < mContentKey.kak_list.size(); i++)
{ {
if (mContentKey.kak_list[i].index == nn::hac::nca::KEY_AESCTR && mContentKey.kak_list[i].decrypted) if (mContentKey.kak_list[i].index == nn::hac::nca::KEY_AESCTR && mContentKey.kak_list[i].decrypted)
{ {
kak_aes_ctr = mContentKey.kak_list[i].dec; mContentKey.aes_ctr = mContentKey.kak_list[i].dec;
} }
} }
if (kak_aes_ctr != zero_aesctr_key)
{
mContentKey.aes_ctr = kak_aes_ctr;
}
} }
// if the keys weren't generated, check if the keys were supplied by the user // if the keys weren't generated, check if the keys were supplied by the user
if (mContentKey.aes_ctr.isNull()) if (mContentKey.aes_ctr.isNull())
{ {
if (mKeyCfg.getNcaExternalContentKey(kDummyRightsIdForUserBodyKey, mContentKey.aes_ctr.get()) == true) if (mKeyCfg.fallback_content_key.isSet())
mContentKey.aes_ctr.isSet = true; {
mContentKey.aes_ctr = mKeyCfg.fallback_content_key.get();
}
} }
if (mCliOutputMode.show_keydata) if (mCliOutputMode.show_keydata)
{ {
if (mContentKey.aes_ctr.isSet()) if (mContentKey.aes_ctr.isSet())
{ {
std::cout << "[NCA Content Key]" << std::endl; fmt::print("[NCA Content Key]\n");
std::cout << " AES-CTR Key: " << fnd::SimpleTextOutput::arrayToString(mContentKey.aes_ctr.get().key, sizeof(mContentKey.aes_ctr.get()), true, ":") << std::endl; fmt::print(" AES-CTR Key: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mContentKey.aes_ctr.get().data(), mContentKey.aes_ctr.get().size(), true, ":"));
} }
} }
} }
void nstool::NcaProcess::generatePartitionConfiguration() void nstool::NcaProcess::generatePartitionConfiguration()
{ {
std::stringstream error;
for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++) for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++)
{ {
// get reference to relevant structures // get reference to relevant structures
@ -238,147 +236,143 @@ void nstool::NcaProcess::generatePartitionConfiguration()
sPartitionInfo& info = mPartitions[partition.header_index]; sPartitionInfo& info = mPartitions[partition.header_index];
// validate header hash // validate header hash
fnd::sha::sSha256Hash fs_header_hash; nn::hac::detail::sha256_hash_t fs_header_hash;
fnd::sha::Sha256((const byte_t*)&mHdrBlock.fs_header[partition.header_index], sizeof(nn::hac::sContentArchiveFsHeader), fs_header_hash.bytes); tc::crypto::GenerateSha256Hash(fs_header_hash.data(), (const byte_t*)&mHdrBlock.fs_header[partition.header_index], sizeof(nn::hac::sContentArchiveFsHeader));
if (fs_header_hash.compare(partition.fs_header_hash) == false) if (fs_header_hash != partition.fs_header_hash)
{ {
error.clear(); throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Hash: FAIL", partition.header_index));
error << "NCA FS Header [" << partition.header_index << "] Hash: FAIL \n";
throw tc::Exception(kModuleName, error.str());
} }
if (fs_header.version.get() != nn::hac::nca::kDefaultFsHeaderVersion) if (fs_header.version.unwrap() != nn::hac::nca::kDefaultFsHeaderVersion)
{ {
error.clear(); throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Version({:d}): UNSUPPORTED", partition.header_index, fs_header.version.unwrap()));
error << "NCA FS Header [" << partition.header_index << "] Version(" << fs_header.version.get() << "): UNSUPPORTED";
throw tc::Exception(kModuleName, error.str());
} }
// setup AES-CTR // setup AES-CTR
nn::hac::ContentArchiveUtil::getNcaPartitionAesCtr(&fs_header, info.aes_ctr.iv); nn::hac::ContentArchiveUtil::getNcaPartitionAesCtr(&fs_header, info.aes_ctr.data());
// save partition config // save partition config
if (tc::is_uint64_t_too_large_for_int64_t(partition.offset))
{
throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Offset({:x}): Too large", partition.header_index, partition.offset));
}
if (tc::is_uint64_t_too_large_for_int64_t(partition.size))
{
throw tc::Exception(mModuleName, fmt::format("NCA FS Header [{:d}] Size({:x}): Too large", partition.header_index, partition.size));
}
info.reader = nullptr; info.reader = nullptr;
info.offset = partition.offset; info.offset = int64_t(partition.offset);
info.size = partition.size; info.size = int64_t(partition.size);
info.format_type = (nn::hac::nca::FormatType)fs_header.format_type; info.format_type = (nn::hac::nca::FormatType)fs_header.format_type;
info.hash_type = (nn::hac::nca::HashType)fs_header.hash_type; info.hash_type = (nn::hac::nca::HashType)fs_header.hash_type;
info.enc_type = (nn::hac::nca::EncryptionType)fs_header.encryption_type; info.enc_type = (nn::hac::nca::EncryptionType)fs_header.encryption_type;
if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256) if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256)
{ {
// info.hash_tree_meta.importData(fs_header.hash_info, nn::hac::nca::kHashInfoLen, LayeredIntegrityMetadata::HASH_TYPE_SHA256);
nn::hac::HierarchicalSha256Header hdr; nn::hac::HierarchicalSha256Header hdr;
std::vector<fnd::LayeredIntegrityMetadata::sLayer> hash_layers;
fnd::LayeredIntegrityMetadata::sLayer data_layer;
std::vector<fnd::sha::sSha256Hash> master_hash_list;
// import raw data // import raw data
hdr.fromBytes(fs_header.hash_info, nn::hac::nca::kHashInfoLen); hdr.fromBytes(fs_header.hash_info.data(), fs_header.hash_info.size());
for (size_t i = 0; i < hdr.getLayerInfo().size(); i++) for (size_t i = 0; i < hdr.getLayerInfo().size(); i++)
{ {
fnd::LayeredIntegrityMetadata::sLayer layer; nn::hac::HierarchicalValidatedStream::StreamInfo::LayerInfo layer;
layer.offset = hdr.getLayerInfo()[i].offset; layer.offset = hdr.getLayerInfo()[i].offset;
layer.size = hdr.getLayerInfo()[i].size; layer.size = hdr.getLayerInfo()[i].size;
layer.block_size = hdr.getHashBlockSize(); layer.block_size = hdr.getHashBlockSize();
if (i + 1 == hdr.getLayerInfo().size()) if (i + 1 == hdr.getLayerInfo().size())
{ {
data_layer = layer; info.hashed_stream_info.data_layer_info = layer;
} }
else else
{ {
hash_layers.push_back(layer); info.hashed_stream_info.hash_layer_info.push_back(layer);
} }
} }
master_hash_list.push_back(hdr.getMasterHash()); info.hashed_stream_info.master_hash_data = tc::ByteData(hdr.getMasterHash().data(), hdr.getMasterHash().size());
info.hashed_stream_info.align_hash_layer_to_block = false;
// write data into metadata
info.layered_intergrity_metadata.setAlignHashToBlock(false);
info.layered_intergrity_metadata.setHashLayerInfo(hash_layers);
info.layered_intergrity_metadata.setDataLayerInfo(data_layer);
info.layered_intergrity_metadata.setMasterHashList(master_hash_list);
} }
else if (info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity) else if (info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity)
{ {
// info.hash_tree_meta.importData(fs_header.hash_info, nn::hac::nca::kHashInfoLen, LayeredIntegrityMetadata::HASH_TYPE_INTEGRITY); // info.hash_tree_meta.importData(fs_header.hash_info, nn::hac::nca::kHashInfoLen, LayeredIntegrityMetadata::HASH_TYPE_INTEGRITY);
nn::hac::HierarchicalIntegrityHeader hdr; nn::hac::HierarchicalIntegrityHeader hdr;
std::vector<fnd::LayeredIntegrityMetadata::sLayer> hash_layers;
fnd::LayeredIntegrityMetadata::sLayer data_layer;
std::vector<fnd::sha::sSha256Hash> master_hash_list;
hdr.fromBytes(fs_header.hash_info, nn::hac::nca::kHashInfoLen); hdr.fromBytes(fs_header.hash_info.data(), fs_header.hash_info.size());
for (size_t i = 0; i < hdr.getLayerInfo().size(); i++) for (size_t i = 0; i < hdr.getLayerInfo().size(); i++)
{ {
fnd::LayeredIntegrityMetadata::sLayer layer; nn::hac::HierarchicalValidatedStream::StreamInfo::LayerInfo layer;
layer.offset = hdr.getLayerInfo()[i].offset; layer.offset = hdr.getLayerInfo()[i].offset;
layer.size = hdr.getLayerInfo()[i].size; layer.size = hdr.getLayerInfo()[i].size;
layer.block_size = (1 << hdr.getLayerInfo()[i].block_size); layer.block_size = (1 << hdr.getLayerInfo()[i].block_size);
if (i + 1 == hdr.getLayerInfo().size()) if (i + 1 == hdr.getLayerInfo().size())
{ {
data_layer = layer; info.hashed_stream_info.data_layer_info = layer;
} }
else else
{ {
hash_layers.push_back(layer); info.hashed_stream_info.hash_layer_info.push_back(layer);
} }
} }
info.hashed_stream_info.master_hash_data = tc::ByteData(hdr.getMasterHashList().size() * sizeof(nn::hac::detail::sha256_hash_t));
// write data into metadata for (size_t i = 0; i < hdr.getMasterHashList().size(); i++)
info.layered_intergrity_metadata.setAlignHashToBlock(true); {
info.layered_intergrity_metadata.setHashLayerInfo(hash_layers); ((nn::hac::detail::sha256_hash_t*)info.hashed_stream_info.master_hash_data.data())[i] = hdr.getMasterHashList()[i];
info.layered_intergrity_metadata.setDataLayerInfo(data_layer); }
info.layered_intergrity_metadata.setMasterHashList(hdr.getMasterHashList()); info.hashed_stream_info.align_hash_layer_to_block = false;
} }
// create reader // create reader
try try
{ {
// filter out unrecognised format types
switch (info.format_type)
{
case (nn::hac::nca::FormatType::PartitionFs):
case (nn::hac::nca::FormatType::RomFs):
break;
default:
error.clear();
error << "FormatType(" << nn::hac::ContentArchiveUtil::getFormatTypeAsString(info.format_type) << "): UNKNOWN";
throw tc::Exception(kModuleName, error.str());
}
// create reader based on encryption type0 // create reader based on encryption type0
if (info.enc_type == nn::hac::nca::EncryptionType::None) if (info.enc_type == nn::hac::nca::EncryptionType::None)
{ {
info.reader = new fnd::OffsetAdjustedIFile(mFile, info.offset, info.size); info.reader = std::make_shared<tc::io::SubStream>(tc::io::SubStream(mFile, info.offset, info.size));
} }
else if (info.enc_type == nn::hac::nca::EncryptionType::AesCtr) else if (info.enc_type == nn::hac::nca::EncryptionType::AesCtr)
{ {
if (mContentKey.aes_ctr.isNull()) if (mContentKey.aes_ctr.isNull())
throw tc::Exception(kModuleName, "AES-CTR Key was not determined"); throw tc::Exception(mModuleName, "AES-CTR Key was not determined");
info.reader = new fnd::OffsetAdjustedIFile(new fnd::AesCtrWrappedIFile(mFile, mContentKey.aes_ctr.get(), info.aes_ctr), info.offset, info.size); //info.reader = new fnd::OffsetAdjustedIFile(new fnd::AesCtrWrappedIFile(mFile, mContentKey.aes_ctr.get(), info.aes_ctr), info.offset, info.size);
info.reader = std::make_shared<tc::crypto::Aes128CtrEncryptedStream>(tc::crypto::Aes128CtrEncryptedStream(mFile, mContentKey.aes_ctr.get(), info.aes_ctr));
info.reader = std::make_shared<tc::io::SubStream>(tc::io::SubStream(info.reader, info.offset, info.size));
} }
else if (info.enc_type == nn::hac::nca::EncryptionType::AesXts || info.enc_type == nn::hac::nca::EncryptionType::AesCtrEx) else if (info.enc_type == nn::hac::nca::EncryptionType::AesXts || info.enc_type == nn::hac::nca::EncryptionType::AesCtrEx)
{ {
error.clear(); throw tc::Exception(mModuleName, fmt::format("EncryptionType({:s}): UNSUPPORTED", nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type)));
error << "EncryptionType(" << nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type) << "): UNSUPPORTED";
throw tc::Exception(kModuleName, error.str());
} }
else else
{ {
error.clear(); throw tc::Exception(mModuleName, fmt::format("EncryptionType({:s}): UNKNOWN", nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type)));
error << "EncryptionType(" << nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type) << "): UNKNOWN";
throw tc::Exception(kModuleName, error.str());
} }
// filter out unrecognised hash types, and hash based readers // filter out unrecognised hash types, and hash based readers
if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256 || info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity) if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256 || info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity)
{ {
info.reader = new fnd::LayeredIntegrityWrappedIFile(info.reader, info.layered_intergrity_metadata); //info.reader = new fnd::LayeredIntegrityWrappedIFile(info.reader, info.layered_intergrity_metadata);
info.reader = std::make_shared<nn::hac::HierarchicalValidatedStream>(nn::hac::HierarchicalValidatedStream(info.reader, info.hashed_stream_info));
} }
else if (info.hash_type != nn::hac::nca::HashType::None) else if (info.hash_type != nn::hac::nca::HashType::None)
{ {
error.clear(); throw tc::Exception(mModuleName, fmt::format("HashType({:s}): UNKNOWN", nn::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type)));
error << "HashType(" << nn::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type) << "): UNKNOWN"; }
throw tc::Exception(kModuleName, error.str());
// filter out unrecognised format types
switch (info.format_type)
{
case (nn::hac::nca::FormatType::PartitionFs):
info.fs_meta = nn::hac::PartitionFsMetaGenerator(info.reader);
info.fs_reader = std::make_shared<tc::io::VirtualFileSystem>(tc::io::VirtualFileSystem(info.fs_meta));
break;
case (nn::hac::nca::FormatType::RomFs):
info.fs_meta = nn::hac::RomFsMetaGenerator(info.reader);
info.fs_reader = std::make_shared<tc::io::VirtualFileSystem>(tc::io::VirtualFileSystem(info.fs_meta));
break;
default:
throw tc::Exception(mModuleName, fmt::format("FormatType({:s}): UNKNOWN", nn::hac::ContentArchiveUtil::getFormatTypeAsString(info.format_type)));
} }
} }
catch (const tc::Exception& e) catch (const tc::Exception& e)
@ -391,160 +385,163 @@ void nstool::NcaProcess::generatePartitionConfiguration()
void nstool::NcaProcess::validateNcaSignatures() void nstool::NcaProcess::validateNcaSignatures()
{ {
// validate signature[0] // validate signature[0]
fnd::rsa::sRsa2048Key sign0_key; if (mKeyCfg.nca_header_sign0_key.find(mHdr.getSignatureKeyGeneration()) != mKeyCfg.nca_header_sign0_key.end())
mKeyCfg.getContentArchiveHeader0SignKey(sign0_key, mHdr.getSignatureKeyGeneration());
if (fnd::rsa::pss::rsaVerify(sign0_key, fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrBlock.signature_main) != 0)
{ {
std::cout << "[WARNING] NCA Header Main Signature: FAIL" << std::endl; if (tc::crypto::VerifyRsa2048PssSha256(mHdrBlock.signature_main.data(), mHdrHash.data(), mKeyCfg.nca_header_sign0_key[mHdr.getSignatureKeyGeneration()]) == false)
{
fmt::print("[WARNING] NCA Header Main Signature: FAIL\n");
}
} }
else
{
fmt::print("[WARNING] NCA Header Main Signature: FAIL (could not load header key)\n");
}
// validate signature[1] // validate signature[1]
if (mHdr.getContentType() == nn::hac::nca::ContentType::Program) if (mHdr.getContentType() == nn::hac::nca::ContentType::Program)
{ {
if (mPartitions[nn::hac::nca::PARTITION_CODE].format_type == nn::hac::nca::FormatType::PartitionFs) try {
{ if (mPartitions[nn::hac::nca::PARTITION_CODE].format_type == nn::hac::nca::FormatType::PartitionFs)
if (*mPartitions[nn::hac::nca::PARTITION_CODE].reader != nullptr)
{ {
PfsProcess exefs; if (mPartitions[nn::hac::nca::PARTITION_CODE].fs_reader != nullptr)
exefs.setInputFile(mPartitions[nn::hac::nca::PARTITION_CODE].reader);
exefs.setCliOutputMode(0);
exefs.process();
// open main.npdm
if (exefs.getPfsHeader().getFileList().hasElement(kNpdmExefsPath) == true)
{ {
const nn::hac::PartitionFsHeader::sFile& file = exefs.getPfsHeader().getFileList().getElement(kNpdmExefsPath); std::shared_ptr<tc::io::IStream> npdm_file;
try {
mPartitions[nn::hac::nca::PARTITION_CODE].fs_reader->openFile(tc::io::Path(kNpdmExefsPath), tc::io::FileMode::Open, tc::io::FileAccess::Read, npdm_file);
}
catch (tc::io::FileNotFoundException&) {
throw tc::Exception(fmt::format("\"{:s}\" not present in ExeFs", kNpdmExefsPath));
}
MetaProcess npdm; MetaProcess npdm;
npdm.setInputFile(new fnd::OffsetAdjustedIFile(mPartitions[nn::hac::nca::PARTITION_CODE].reader, file.offset, file.size)); npdm.setInputFile(npdm_file);
npdm.setKeyCfg(mKeyCfg); npdm.setKeyCfg(mKeyCfg);
npdm.setVerifyMode(true); npdm.setVerifyMode(true);
npdm.setCliOutputMode(0); npdm.setCliOutputMode(CliOutputMode(false, false, false, false));
npdm.process(); npdm.process();
if (fnd::rsa::pss::rsaVerify(npdm.getMeta().getAccessControlInfoDesc().getContentArchiveHeaderSignature2Key(), fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrBlock.signature_acid) != 0) if (tc::crypto::VerifyRsa2048PssSha256(mHdrBlock.signature_acid.data(), mHdrHash.data(), npdm.getMeta().getAccessControlInfoDesc().getContentArchiveHeaderSignature2Key()) == false)
{ {
std::cout << "[WARNING] NCA Header ACID Signature: FAIL" << std::endl; throw tc::Exception("Bad signature");
} }
} }
else else
{ {
std::cout << "[WARNING] NCA Header ACID Signature: FAIL (\"" << kNpdmExefsPath << "\" not present in ExeFs)" << std::endl; throw tc::Exception("ExeFs was not mounted");
} }
} }
else else
{ {
std::cout << "[WARNING] NCA Header ACID Signature: FAIL (ExeFs unreadable)" << std::endl; throw tc::Exception("No ExeFs partition");
} }
} }
else catch (tc::Exception& e) {
{ fmt::print("[WARNING] NCA Header ACID Signature: FAIL ({:s})\n", e.error());
std::cout << "[WARNING] NCA Header ACID Signature: FAIL (No ExeFs partition)" << std::endl;
} }
} }
} }
void nstool::NcaProcess::displayHeader() void nstool::NcaProcess::displayHeader()
{ {
std::cout << "[NCA Header]" << std::endl; fmt::print("[NCA Header]\n");
std::cout << " Format Type: " << nn::hac::ContentArchiveUtil::getFormatHeaderVersionAsString((nn::hac::nca::HeaderFormatVersion)mHdr.getFormatVersion()) << std::endl; fmt::print(" Format Type: {:s}\n", nn::hac::ContentArchiveUtil::getFormatHeaderVersionAsString((nn::hac::nca::HeaderFormatVersion)mHdr.getFormatVersion()));
std::cout << " Dist. Type: " << nn::hac::ContentArchiveUtil::getDistributionTypeAsString(mHdr.getDistributionType()) << std::endl; fmt::print(" Dist. Type: {:s}\n", nn::hac::ContentArchiveUtil::getDistributionTypeAsString(mHdr.getDistributionType()));
std::cout << " Content Type: " << nn::hac::ContentArchiveUtil::getContentTypeAsString(mHdr.getContentType()) << std::endl; fmt::print(" Content Type: {:s}\n", nn::hac::ContentArchiveUtil::getContentTypeAsString(mHdr.getContentType()));
std::cout << " Key Generation: " << std::dec << (uint32_t)mHdr.getKeyGeneration() << std::endl; fmt::print(" Key Generation: {:d}\n", mHdr.getKeyGeneration());
std::cout << " Sig. Generation: " << std::dec << (uint32_t)mHdr.getSignatureKeyGeneration() << std::endl; fmt::print(" Sig. Generation: {:d}\n", mHdr.getSignatureKeyGeneration());
std::cout << " Kaek Index: " << nn::hac::ContentArchiveUtil::getKeyAreaEncryptionKeyIndexAsString((nn::hac::nca::KeyAreaEncryptionKeyIndex)mHdr.getKeyAreaEncryptionKeyIndex()) << " (" << std::dec << (uint32_t)mHdr.getKeyAreaEncryptionKeyIndex() << ")" << std::endl; fmt::print(" Kaek Index: {:s} ({:d})\n", nn::hac::ContentArchiveUtil::getKeyAreaEncryptionKeyIndexAsString((nn::hac::nca::KeyAreaEncryptionKeyIndex)mHdr.getKeyAreaEncryptionKeyIndex()), mHdr.getKeyAreaEncryptionKeyIndex());
std::cout << " Size: 0x" << std::hex << mHdr.getContentSize() << std::endl; fmt::print(" Size: 0x{:x}\n", mHdr.getContentSize());
std::cout << " ProgID: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getProgramId() << std::endl; fmt::print(" ProgID: 0x{:016x}\n", mHdr.getProgramId());
std::cout << " Content Index: " << std::dec << mHdr.getContentIndex() << std::endl; fmt::print(" Content Index: {:d}\n", mHdr.getContentIndex());
std::cout << " SdkAddon Ver.: " << nn::hac::ContentArchiveUtil::getSdkAddonVersionAsString(mHdr.getSdkAddonVersion()) << " (v" << std::dec << mHdr.getSdkAddonVersion() << ")" << std::endl; fmt::print(" SdkAddon Ver.: {:s} (v{:d})\n", nn::hac::ContentArchiveUtil::getSdkAddonVersionAsString(mHdr.getSdkAddonVersion()), mHdr.getSdkAddonVersion());
if (mHdr.hasRightsId()) if (mHdr.hasRightsId())
{ {
std::cout << " RightsId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getRightsId(), nn::hac::nca::kRightsIdLen, true, "") << std::endl; fmt::print(" RightsId: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getRightsId().data(), mHdr.getRightsId().size(), true, ""));
} }
if (mContentKey.kak_list.size() > 0 && mCliOutputMode.show_keydata) if (mContentKey.kak_list.size() > 0 && mCliOutputMode.show_keydata)
{ {
std::cout << " Key Area:" << std::endl; fmt::print(" Key Area:\n");
std::cout << " <--------------------------------------------------------------------------------------------------------->" << std::endl; fmt::print(" <--------------------------------------------------------------------------------------------------------->\n");
std::cout << " | IDX | ENCRYPTED KEY | DECRYPTED KEY |" << std::endl; fmt::print(" | IDX | ENCRYPTED KEY | DECRYPTED KEY |\n");
std::cout << " |-----|-------------------------------------------------|-------------------------------------------------|" << std::endl; fmt::print(" |-----|-------------------------------------------------|-------------------------------------------------|\n");
for (size_t i = 0; i < mContentKey.kak_list.size(); i++) for (size_t i = 0; i < mContentKey.kak_list.size(); i++)
{ {
std::cout << " | " << std::dec << std::setw(3) << std::setfill(' ') << (uint32_t)mContentKey.kak_list[i].index << " | "; fmt::print(" | {:3d} | {:s} | ", mContentKey.kak_list[i].index, tc::cli::FormatUtil::formatBytesAsString(mContentKey.kak_list[i].enc.data(), mContentKey.kak_list[i].enc.size(), true, ":"));
std::cout << fnd::SimpleTextOutput::arrayToString(mContentKey.kak_list[i].enc.key, 16, true, ":") << " | ";
if (mContentKey.kak_list[i].decrypted) if (mContentKey.kak_list[i].decrypted)
std::cout << fnd::SimpleTextOutput::arrayToString(mContentKey.kak_list[i].dec.key, 16, true, ":"); fmt::print("{:s}", tc::cli::FormatUtil::formatBytesAsString(mContentKey.kak_list[i].dec.data(), mContentKey.kak_list[i].dec.size(), true, ":"));
else else
std::cout << "<unable to decrypt> "; fmt::print("<unable to decrypt> ");
std::cout << " |" << std::endl; fmt::print(" |\n");
} }
std::cout << " <--------------------------------------------------------------------------------------------------------->" << std::endl; fmt::print(" <--------------------------------------------------------------------------------------------------------->\n");
} }
if (mCliOutputMode.show_layout) if (mCliOutputMode.show_layout)
{ {
std::cout << " Partitions:" << std::endl; fmt::print(" Partitions:\n");
for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++) for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++)
{ {
uint32_t index = mHdr.getPartitionEntryList()[i].header_index; uint32_t index = mHdr.getPartitionEntryList()[i].header_index;
sPartitionInfo& info = mPartitions[index]; sPartitionInfo& info = mPartitions[index];
if (info.size == 0) continue; if (info.size == 0) continue;
std::cout << " " << std::dec << index << ":" << std::endl; fmt::print(" {:d}:\n", index);
std::cout << " Offset: 0x" << std::hex << (uint64_t)info.offset << std::endl; fmt::print(" Offset: 0x{:x}\n", info.offset);
std::cout << " Size: 0x" << std::hex << (uint64_t)info.size << std::endl; fmt::print(" Size: 0x{:x}\n", info.size);
std::cout << " Format Type: " << nn::hac::ContentArchiveUtil::getFormatTypeAsString(info.format_type) << std::endl; fmt::print(" Format Type: {:s}\n", nn::hac::ContentArchiveUtil::getFormatTypeAsString(info.format_type));
std::cout << " Hash Type: " << nn::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type) << std::endl; fmt::print(" Hash Type: {:s}\n", nn::hac::ContentArchiveUtil::getHashTypeAsString(info.hash_type));
std::cout << " Enc. Type: " << nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type) << std::endl; fmt::print(" Enc. Type: {:s}\n", nn::hac::ContentArchiveUtil::getEncryptionTypeAsString(info.enc_type));
if (info.enc_type == nn::hac::nca::EncryptionType::AesCtr) if (info.enc_type == nn::hac::nca::EncryptionType::AesCtr)
{ {
fnd::aes::sAesIvCtr ctr; nn::hac::detail::aes_iv_t aes_ctr;
fnd::aes::AesIncrementCounter(info.aes_ctr.iv, info.offset>>4, ctr.iv); //fnd::aes::AesIncrementCounter(info.aes_ctr.iv, info.offset>>4, ctr.iv);
std::cout << " AesCtr Counter:" << std::endl; fmt::print(" AesCtr Counter:\n");
std::cout << " " << fnd::SimpleTextOutput::arrayToString(ctr.iv, sizeof(fnd::aes::sAesIvCtr), true, ":") << std::endl; fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(aes_ctr.data(), aes_ctr.size(), true, ":"));
} }
if (info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity) if (info.hash_type == nn::hac::nca::HashType::HierarchicalIntegrity)
{ {
fnd::LayeredIntegrityMetadata& hash_hdr = info.layered_intergrity_metadata; auto hash_hdr = info.hashed_stream_info;
std::cout << " HierarchicalIntegrity Header:" << std::endl; fmt::print(" HierarchicalIntegrity Header:\n");
for (size_t j = 0; j < hash_hdr.getHashLayerInfo().size(); j++) for (size_t j = 0; j < hash_hdr.hash_layer_info.size(); j++)
{ {
std::cout << " Hash Layer " << std::dec << j << ":" << std::endl; fmt::print(" Hash Layer {:d}:\n", j);
std::cout << " Offset: 0x" << std::hex << (uint64_t)hash_hdr.getHashLayerInfo()[j].offset << std::endl; fmt::print(" Offset: 0x{:x}\n", hash_hdr.hash_layer_info[j].offset);
std::cout << " Size: 0x" << std::hex << (uint64_t)hash_hdr.getHashLayerInfo()[j].size << std::endl; fmt::print(" Size: 0x{:x}\n", hash_hdr.hash_layer_info[j].size);
std::cout << " BlockSize: 0x" << std::hex << (uint32_t)hash_hdr.getHashLayerInfo()[j].block_size << std::endl; fmt::print(" BlockSize: 0x{:x}\n", hash_hdr.hash_layer_info[j].block_size);
} }
std::cout << " Data Layer:" << std::endl; fmt::print(" Data Layer:\n");
std::cout << " Offset: 0x" << std::hex << (uint64_t)hash_hdr.getDataLayer().offset << std::endl; fmt::print(" Offset: 0x{:x}\n", hash_hdr.data_layer_info.offset);
std::cout << " Size: 0x" << std::hex << (uint64_t)hash_hdr.getDataLayer().size << std::endl; fmt::print(" Size: 0x{:x}\n", hash_hdr.data_layer_info.size);
std::cout << " BlockSize: 0x" << std::hex << (uint32_t)hash_hdr.getDataLayer().block_size << std::endl; fmt::print(" BlockSize: 0x{:x}\n", hash_hdr.data_layer_info.block_size);
for (size_t j = 0; j < hash_hdr.getMasterHashList().size(); j++) size_t master_hash_num = hash_hdr.master_hash_data.size() / sizeof(nn::hac::detail::sha256_hash_t);
nn::hac::detail::sha256_hash_t* master_hash_table = (nn::hac::detail::sha256_hash_t*)hash_hdr.master_hash_data.data();
for (size_t j = 0; j < master_hash_num; j++)
{ {
std::cout << " Master Hash " << std::dec << j << ":" << std::endl; fmt::print(" Master Hash {:d}:\n", j);
std::cout << " " << fnd::SimpleTextOutput::arrayToString(hash_hdr.getMasterHashList()[j].bytes, 0x10, true, ":") << std::endl; fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(master_hash_table[j].data(), 0x10, true, ":"));
std::cout << " " << fnd::SimpleTextOutput::arrayToString(hash_hdr.getMasterHashList()[j].bytes+0x10, 0x10, true, ":") << std::endl; fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(master_hash_table[j].data()+0x10, 0x10, true, ":"));
} }
} }
else if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256) else if (info.hash_type == nn::hac::nca::HashType::HierarchicalSha256)
{ {
fnd::LayeredIntegrityMetadata& hash_hdr = info.layered_intergrity_metadata; auto hash_hdr = info.hashed_stream_info;
std::cout << " HierarchicalSha256 Header:" << std::endl; fmt::print(" HierarchicalSha256 Header:\n");
std::cout << " Master Hash:" << std::endl; fmt::print(" Master Hash:\n");
std::cout << " " << fnd::SimpleTextOutput::arrayToString(hash_hdr.getMasterHashList()[0].bytes, 0x10, true, ":") << std::endl; fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.master_hash_data.data(), 0x10, true, ":"));
std::cout << " " << fnd::SimpleTextOutput::arrayToString(hash_hdr.getMasterHashList()[0].bytes+0x10, 0x10, true, ":") << std::endl; fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(hash_hdr.master_hash_data.data()+0x10, 0x10, true, ":"));
std::cout << " HashBlockSize: 0x" << std::hex << (uint32_t)hash_hdr.getDataLayer().block_size << std::endl; fmt::print(" HashBlockSize: 0x{:x}\n", hash_hdr.data_layer_info.block_size);
std::cout << " Hash Layer:" << std::endl; fmt::print(" Hash Layer:\n");
std::cout << " Offset: 0x" << std::hex << (uint64_t)hash_hdr.getHashLayerInfo()[0].offset << std::endl; fmt::print(" Offset: 0x{:x}\n", hash_hdr.hash_layer_info[0].offset);
std::cout << " Size: 0x" << std::hex << (uint64_t)hash_hdr.getHashLayerInfo()[0].size << std::endl; fmt::print(" Size: 0x{:x}\n", hash_hdr.hash_layer_info[0].size);
std::cout << " Data Layer:" << std::endl; fmt::print(" Data Layer:\n");
std::cout << " Offset: 0x" << std::hex << (uint64_t)hash_hdr.getDataLayer().offset << std::endl; fmt::print(" Offset: 0x{:x}\n", hash_hdr.data_layer_info.offset);
std::cout << " Size: 0x" << std::hex << (uint64_t)hash_hdr.getDataLayer().size << std::endl; fmt::print(" Size: 0x{:x}\n", hash_hdr.data_layer_info.size);
} }
} }
} }
@ -559,17 +556,17 @@ void nstool::NcaProcess::processPartitions()
struct sPartitionInfo& partition = mPartitions[index]; struct sPartitionInfo& partition = mPartitions[index];
// if the reader is null, skip // if the reader is null, skip
if (*partition.reader == nullptr) if (partition.fs_reader == nullptr)
{ {
std::cout << "[WARNING] NCA Partition " << std::dec << index << " not readable."; fmt::print("[WARNING] NCA Partition {:d} not readable.", index);
if (partition.fail_reason.empty() == false) if (partition.fail_reason.empty() == false)
{ {
std::cout << " (" << partition.fail_reason << ")"; fmt::print(" ({:s})", partition.fail_reason);
} }
std::cout << std::endl; fmt::print("\n");
continue; continue;
} }
/*
try { try {
if (partition.format_type == nn::hac::nca::FormatType::PartitionFs) if (partition.format_type == nn::hac::nca::FormatType::PartitionFs)
{ {
@ -612,6 +609,7 @@ void nstool::NcaProcess::processPartitions()
} catch (const tc::Exception& e) { } catch (const tc::Exception& e) {
std::cout << "[WARNING] NCA Partition " << std::dec << index << " not readable (" << e.error() << ")." << std::endl; std::cout << "[WARNING] NCA Partition " << std::dec << index << " not readable (" << e.error() << ")." << std::endl;
} }
*/
} }
} }

View file

@ -1,8 +1,10 @@
#pragma once #pragma once
#include "types.h" #include "types.h"
#include "KeyBag.h" #include "KeyBag.h"
#include "FsProcess.h"
#include <nn/hac/ContentArchiveHeader.h> #include <nn/hac/ContentArchiveHeader.h>
#include <nn/hac/HierarchicalValidatedStream.h>
namespace nstool { namespace nstool {
@ -19,16 +21,17 @@ public:
void setCliOutputMode(CliOutputMode type); void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
// nca specfic // fs specific
void setPartition0ExtractPath(const tc::io::Path& path); void setShowFsTree(bool show_fs_tree);
void setPartition1ExtractPath(const tc::io::Path& path); void setFsRootLabel(const std::string& root_label);
void setPartition2ExtractPath(const tc::io::Path& path); void setExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs);
void setPartition3ExtractPath(const tc::io::Path& path);
void setListFs(bool list_fs);
// post process() get FS out
const std::shared_ptr<tc::io::IStorage>& getFileSystem() const;
private: private:
const std::string kModuleName = "NcaProcess"; const std::string kNpdmExefsPath = "/main.npdm";
const std::string kNpdmExefsPath = "main.npdm";
std::string mModuleName;
// user options // user options
std::shared_ptr<tc::io::IStream> mFile; std::shared_ptr<tc::io::IStream> mFile;
@ -36,13 +39,13 @@ private:
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;
std::array<tc::Optional<tc::io::Path>, nn::hac::nca::kPartitionNum> mPartitionPath; // fs processing
std::shared_ptr<tc::io::IStorage> mFileSystem;
FsProcess mFsProcess;
bool mListFs; // nca data
// data
nn::hac::sContentArchiveHeaderBlock mHdrBlock; nn::hac::sContentArchiveHeaderBlock mHdrBlock;
fnd::sha::sSha256Hash mHdrHash; nn::hac::detail::sha256_hash_t mHdrHash;
nn::hac::ContentArchiveHeader mHdr; nn::hac::ContentArchiveHeader mHdr;
// crypto // crypto
@ -81,9 +84,12 @@ private:
tc::Optional<nn::hac::detail::aes128_key_t> aes_ctr; tc::Optional<nn::hac::detail::aes128_key_t> aes_ctr;
} mContentKey; } mContentKey;
// raw partition data
struct sPartitionInfo struct sPartitionInfo
{ {
std::shared_ptr<tc::io::IStream> reader; std::shared_ptr<tc::io::IStream> reader;
tc::io::VirtualFileSystem::FileSystemMeta fs_meta;
std::shared_ptr<tc::io::IStorage> fs_reader;
std::string fail_reason; std::string fail_reason;
int64_t offset; int64_t offset;
int64_t size; int64_t size;
@ -92,12 +98,14 @@ private:
nn::hac::nca::FormatType format_type; nn::hac::nca::FormatType format_type;
nn::hac::nca::HashType hash_type; nn::hac::nca::HashType hash_type;
nn::hac::nca::EncryptionType enc_type; nn::hac::nca::EncryptionType enc_type;
nn::hac::HierarchicalValidatedStream::StreamInfo hashed_stream_info;
//fnd::LayeredIntegrityMetadata layered_intergrity_metadata; //fnd::LayeredIntegrityMetadata layered_intergrity_metadata;
nn::hac::detail::aes_iv_t aes_ctr; nn::hac::detail::aes_iv_t aes_ctr;
} };
std::array<sPartitionInfo, nn::hac::nca::kPartitionNum> mPartitions; std::array<sPartitionInfo, nn::hac::nca::kPartitionNum> mPartitions;
void importHeader(); void importHeader();
void generateNcaBodyEncryptionKeys(); void generateNcaBodyEncryptionKeys();
void generatePartitionConfiguration(); void generatePartitionConfiguration();

View file

@ -6,7 +6,7 @@
#include "GameCardProcess.h" #include "GameCardProcess.h"
#include "PfsProcess.h" #include "PfsProcess.h"
#include "RomfsProcess.h" #include "RomfsProcess.h"
//#include "NcaProcess.h" #include "NcaProcess.h"
#include "MetaProcess.h" #include "MetaProcess.h"
#include "CnmtProcess.h" #include "CnmtProcess.h"
#include "NsoProcess.h" #include "NsoProcess.h"
@ -70,7 +70,6 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
obj.process(); obj.process();
} }
/*
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NCA) else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NCA)
{ {
nstool::NcaProcess obj; nstool::NcaProcess obj;
@ -80,7 +79,7 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
obj.setCliOutputMode(set.opt.cli_output_mode); obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify); obj.setVerifyMode(set.opt.verify);
/*
if (set.nca.part0_extract_path.isSet()) if (set.nca.part0_extract_path.isSet())
obj.setPartition0ExtractPath(set.nca.part0_extract_path.get()); obj.setPartition0ExtractPath(set.nca.part0_extract_path.get());
if (set.nca.part1_extract_path.isSet()) if (set.nca.part1_extract_path.isSet())
@ -90,10 +89,10 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
if (set.nca.part3_extract_path.isSet()) if (set.nca.part3_extract_path.isSet())
obj.setPartition3ExtractPath(set.nca.part3_extract_path.get()); obj.setPartition3ExtractPath(set.nca.part3_extract_path.get());
obj.setListFs(set.fs.show_fs_tree); obj.setListFs(set.fs.show_fs_tree);
*/
obj.process(); obj.process();
} }
*/
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_META) else if (set.infile.filetype == nstool::Settings::FILE_TYPE_META)
{ {
nstool::MetaProcess obj; nstool::MetaProcess obj;