mirror of
https://github.com/jakcron/nstool.git
synced 2024-12-23 03:05:27 +00:00
[nstool] NCA processing more robust.
This commit is contained in:
parent
60eda9e42e
commit
3437031128
|
@ -81,6 +81,146 @@ std::string kProgramPartitionNameStr[]
|
||||||
"logo"
|
"logo"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
NcaProcess::NcaProcess() :
|
||||||
|
mReader(nullptr),
|
||||||
|
mKeyset(nullptr),
|
||||||
|
mCliOutputType(OUTPUT_NORMAL),
|
||||||
|
mVerify(false),
|
||||||
|
mListFs(false)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < nx::nca::kPartitionNum; i++)
|
||||||
|
{
|
||||||
|
mPartitionPath[i].doExtract = false;
|
||||||
|
mPartitions[i].reader = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NcaProcess::~NcaProcess()
|
||||||
|
{
|
||||||
|
if (mReader != nullptr)
|
||||||
|
{
|
||||||
|
delete mReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nx::nca::kPartitionNum; i++)
|
||||||
|
{
|
||||||
|
if (mPartitions[i].reader != nullptr)
|
||||||
|
{
|
||||||
|
delete mPartitions[i].reader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::process()
|
||||||
|
{
|
||||||
|
fnd::MemoryBlob scratch;
|
||||||
|
|
||||||
|
if (mReader == nullptr)
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "No file reader set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// read header block
|
||||||
|
mReader->read((byte_t*)&mHdrBlock, 0, sizeof(nx::sNcaHeaderBlock));
|
||||||
|
|
||||||
|
// decrypt header block
|
||||||
|
nx::NcaUtils::decryptNcaHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, mKeyset->nca.header_key);
|
||||||
|
|
||||||
|
// generate header hash
|
||||||
|
crypto::sha::Sha256((byte_t*)&mHdrBlock.header, sizeof(nx::sNcaHeader), mHdrHash.bytes);
|
||||||
|
|
||||||
|
// proccess main header
|
||||||
|
mHdr.importBinary((byte_t*)&mHdrBlock.header, sizeof(nx::sNcaHeader));
|
||||||
|
|
||||||
|
// determine keys
|
||||||
|
generateNcaBodyEncryptionKeys();
|
||||||
|
|
||||||
|
// import/generate fs header data
|
||||||
|
generatePartitionConfiguration();
|
||||||
|
|
||||||
|
// validate signatures
|
||||||
|
if (mVerify)
|
||||||
|
validateNcaSignatures();
|
||||||
|
|
||||||
|
// display header
|
||||||
|
if (mCliOutputType >= OUTPUT_NORMAL)
|
||||||
|
displayHeader();
|
||||||
|
|
||||||
|
// process partition
|
||||||
|
processPartitions();
|
||||||
|
|
||||||
|
/*
|
||||||
|
NCA is a file container
|
||||||
|
A hashed and signed file container
|
||||||
|
|
||||||
|
To verify a NCA: (R=regular step)
|
||||||
|
1 - decrypt header (R)
|
||||||
|
2 - verify signature[0]
|
||||||
|
3 - validate hashes of fs_headers
|
||||||
|
4 - determine how to read/decrypt the partitions (R)
|
||||||
|
5 - validate the partitions depending on their hash method
|
||||||
|
6 - if this NCA is a Program or Patch, open main.npdm from partition0
|
||||||
|
7 - validate ACID
|
||||||
|
8 - use public key in ACID to verify NCA signature[1]
|
||||||
|
|
||||||
|
Things to consider
|
||||||
|
* because of the manditory steps between verifcation steps
|
||||||
|
the NCA should be ready to be pulled to pieces before any printing is done
|
||||||
|
so the verification text can be presented without interuption
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setInputFile(fnd::IFile* file, size_t offset, size_t size)
|
||||||
|
{
|
||||||
|
mReader = new OffsetAdjustedIFile(file, offset, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setKeyset(const sKeyset* keyset)
|
||||||
|
{
|
||||||
|
mKeyset = keyset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setCliOutputMode(CliOutputType type)
|
||||||
|
{
|
||||||
|
mCliOutputType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setVerifyMode(bool verify)
|
||||||
|
{
|
||||||
|
mVerify = verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setPartition0ExtractPath(const std::string& path)
|
||||||
|
{
|
||||||
|
mPartitionPath[0].path = path;
|
||||||
|
mPartitionPath[0].doExtract = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setPartition1ExtractPath(const std::string& path)
|
||||||
|
{
|
||||||
|
mPartitionPath[1].path = path;
|
||||||
|
mPartitionPath[1].doExtract = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setPartition2ExtractPath(const std::string& path)
|
||||||
|
{
|
||||||
|
mPartitionPath[2].path = path;
|
||||||
|
mPartitionPath[2].doExtract = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setPartition3ExtractPath(const std::string& path)
|
||||||
|
{
|
||||||
|
mPartitionPath[3].path = path;
|
||||||
|
mPartitionPath[3].doExtract = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setListFs(bool list_fs)
|
||||||
|
{
|
||||||
|
mListFs = list_fs;
|
||||||
|
}
|
||||||
|
|
||||||
void NcaProcess::generateNcaBodyEncryptionKeys()
|
void NcaProcess::generateNcaBodyEncryptionKeys()
|
||||||
{
|
{
|
||||||
// create zeros key
|
// create zeros key
|
||||||
|
@ -147,6 +287,7 @@ void NcaProcess::generateNcaBodyEncryptionKeys()
|
||||||
printf("AES-CTR Key: ");
|
printf("AES-CTR Key: ");
|
||||||
fnd::SimpleTextOutput::hexDump(mBodyKeys.aes_ctr.var.key, sizeof(mBodyKeys.aes_ctr.var));
|
fnd::SimpleTextOutput::hexDump(mBodyKeys.aes_ctr.var.key, sizeof(mBodyKeys.aes_ctr.var));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mBodyKeys.aes_xts.isSet)
|
if (mBodyKeys.aes_xts.isSet)
|
||||||
{
|
{
|
||||||
printf("AES-XTS Key0: ");
|
printf("AES-XTS Key0: ");
|
||||||
|
@ -198,7 +339,13 @@ void NcaProcess::generatePartitionConfiguration()
|
||||||
info.format_type = (nx::nca::FormatType)fs_header.format_type;
|
info.format_type = (nx::nca::FormatType)fs_header.format_type;
|
||||||
info.hash_type = (nx::nca::HashType)fs_header.hash_type;
|
info.hash_type = (nx::nca::HashType)fs_header.hash_type;
|
||||||
info.enc_type = (nx::nca::EncryptionType)fs_header.encryption_type;
|
info.enc_type = (nx::nca::EncryptionType)fs_header.encryption_type;
|
||||||
|
if (info.hash_type == nx::nca::HASH_HIERARCHICAL_SHA256)
|
||||||
|
info.hash_tree_meta.importHierarchicalSha256Header(nx::HierarchicalSha256Header(fs_header.hash_superblock, nx::nca::kFsHeaderHashSuperblockLen));
|
||||||
|
else if (info.hash_type == nx::nca::HASH_HIERARCHICAL_INTERGRITY)
|
||||||
|
info.hash_tree_meta.importHierarchicalIntergityHeader(nx::HierarchicalIntegrityHeader(fs_header.hash_superblock, nx::nca::kFsHeaderHashSuperblockLen));
|
||||||
|
|
||||||
|
|
||||||
|
// create reader
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// filter out unrecognised format types
|
// filter out unrecognised format types
|
||||||
|
@ -240,16 +387,6 @@ void NcaProcess::generatePartitionConfiguration()
|
||||||
// filter out unrecognised hash types, and hash based readers
|
// filter out unrecognised hash types, and hash based readers
|
||||||
if (info.hash_type == nx::nca::HASH_HIERARCHICAL_SHA256 || info.hash_type == nx::nca::HASH_HIERARCHICAL_INTERGRITY)
|
if (info.hash_type == nx::nca::HASH_HIERARCHICAL_SHA256 || info.hash_type == nx::nca::HASH_HIERARCHICAL_INTERGRITY)
|
||||||
{
|
{
|
||||||
switch(info.hash_type)
|
|
||||||
{
|
|
||||||
case (nx::nca::HASH_HIERARCHICAL_SHA256):
|
|
||||||
info.hash_tree_meta.importHierarchicalSha256Header(nx::HierarchicalSha256Header(fs_header.hash_superblock, nx::nca::kFsHeaderHashSuperblockLen));
|
|
||||||
break;
|
|
||||||
case (nx::nca::HASH_HIERARCHICAL_INTERGRITY):
|
|
||||||
info.hash_tree_meta.importHierarchicalIntergityHeader(nx::HierarchicalIntegrityHeader(fs_header.hash_superblock, nx::nca::kFsHeaderHashSuperblockLen));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fnd::IFile* tmp = info.reader;
|
fnd::IFile* tmp = info.reader;
|
||||||
info.reader = nullptr;
|
info.reader = nullptr;
|
||||||
info.reader = new HashTreeWrappedIFile(tmp, true, info.hash_tree_meta);
|
info.reader = new HashTreeWrappedIFile(tmp, true, info.hash_tree_meta);
|
||||||
|
@ -263,24 +400,12 @@ void NcaProcess::generatePartitionConfiguration()
|
||||||
}
|
}
|
||||||
catch (const fnd::Exception& e)
|
catch (const fnd::Exception& e)
|
||||||
{
|
{
|
||||||
|
printf("ugh\n");
|
||||||
info.fail_reason = std::string(e.error());
|
info.fail_reason = std::string(e.error());
|
||||||
if (info.reader != nullptr)
|
if (info.reader != nullptr)
|
||||||
delete info.reader;
|
delete info.reader;
|
||||||
info.reader = nullptr;
|
info.reader = nullptr;
|
||||||
info.hash_tree_meta = HashTreeMeta();
|
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
if (info.reader != nullptr)
|
|
||||||
{
|
|
||||||
fnd::MemoryBlob sss;
|
|
||||||
sss.alloc(0x100);
|
|
||||||
info.reader->read(sss.getBytes(), 0x100);
|
|
||||||
printf("[%d] START\n", i);
|
|
||||||
fnd::SimpleTextOutput::hxdStyleDump(sss.getBytes(), 0x100);
|
|
||||||
printf("[%d] END\n", i);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,142 +645,3 @@ void NcaProcess::processPartitions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NcaProcess::NcaProcess() :
|
|
||||||
mReader(nullptr),
|
|
||||||
mKeyset(nullptr),
|
|
||||||
mCliOutputType(OUTPUT_NORMAL),
|
|
||||||
mVerify(false),
|
|
||||||
mListFs(false)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < nx::nca::kPartitionNum; i++)
|
|
||||||
{
|
|
||||||
mPartitionPath[i].doExtract = false;
|
|
||||||
mPartitions[i].reader = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NcaProcess::~NcaProcess()
|
|
||||||
{
|
|
||||||
if (mReader != nullptr)
|
|
||||||
{
|
|
||||||
delete mReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < nx::nca::kPartitionNum; i++)
|
|
||||||
{
|
|
||||||
if (mPartitions[i].reader != nullptr)
|
|
||||||
{
|
|
||||||
delete mPartitions[i].reader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NcaProcess::process()
|
|
||||||
{
|
|
||||||
fnd::MemoryBlob scratch;
|
|
||||||
|
|
||||||
if (mReader == nullptr)
|
|
||||||
{
|
|
||||||
throw fnd::Exception(kModuleName, "No file reader set.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// read header block
|
|
||||||
mReader->read((byte_t*)&mHdrBlock, 0, sizeof(nx::sNcaHeaderBlock));
|
|
||||||
|
|
||||||
// decrypt header block
|
|
||||||
nx::NcaUtils::decryptNcaHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, mKeyset->nca.header_key);
|
|
||||||
|
|
||||||
// generate header hash
|
|
||||||
crypto::sha::Sha256((byte_t*)&mHdrBlock.header, sizeof(nx::sNcaHeader), mHdrHash.bytes);
|
|
||||||
|
|
||||||
// proccess main header
|
|
||||||
mHdr.importBinary((byte_t*)&mHdrBlock.header, sizeof(nx::sNcaHeader));
|
|
||||||
|
|
||||||
// determine keys
|
|
||||||
generateNcaBodyEncryptionKeys();
|
|
||||||
|
|
||||||
// import/generate fs header data
|
|
||||||
generatePartitionConfiguration();
|
|
||||||
|
|
||||||
// validate signatures
|
|
||||||
if (mVerify)
|
|
||||||
validateNcaSignatures();
|
|
||||||
|
|
||||||
// display header
|
|
||||||
if (mCliOutputType >= OUTPUT_NORMAL)
|
|
||||||
displayHeader();
|
|
||||||
|
|
||||||
// process partition
|
|
||||||
processPartitions();
|
|
||||||
|
|
||||||
/*
|
|
||||||
NCA is a file container
|
|
||||||
A hashed and signed file container
|
|
||||||
|
|
||||||
To verify a NCA: (R=regular step)
|
|
||||||
1 - decrypt header (R)
|
|
||||||
2 - verify signature[0]
|
|
||||||
3 - validate hashes of fs_headers
|
|
||||||
4 - determine how to read/decrypt the partitions (R)
|
|
||||||
5 - validate the partitions depending on their hash method
|
|
||||||
6 - if this NCA is a Program or Patch, open main.npdm from partition0
|
|
||||||
7 - validate ACID
|
|
||||||
8 - use public key in ACID to verify NCA signature[1]
|
|
||||||
|
|
||||||
Things to consider
|
|
||||||
* because of the manditory steps between verifcation steps
|
|
||||||
the NCA should be ready to be pulled to pieces before any printing is done
|
|
||||||
so the verification text can be presented without interuption
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void NcaProcess::setInputFile(fnd::IFile* file, size_t offset, size_t size)
|
|
||||||
{
|
|
||||||
mReader = new OffsetAdjustedIFile(file, offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NcaProcess::setKeyset(const sKeyset* keyset)
|
|
||||||
{
|
|
||||||
mKeyset = keyset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NcaProcess::setCliOutputMode(CliOutputType type)
|
|
||||||
{
|
|
||||||
mCliOutputType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NcaProcess::setVerifyMode(bool verify)
|
|
||||||
{
|
|
||||||
mVerify = verify;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NcaProcess::setPartition0ExtractPath(const std::string& path)
|
|
||||||
{
|
|
||||||
mPartitionPath[0].path = path;
|
|
||||||
mPartitionPath[0].doExtract = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NcaProcess::setPartition1ExtractPath(const std::string& path)
|
|
||||||
{
|
|
||||||
mPartitionPath[1].path = path;
|
|
||||||
mPartitionPath[1].doExtract = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NcaProcess::setPartition2ExtractPath(const std::string& path)
|
|
||||||
{
|
|
||||||
mPartitionPath[2].path = path;
|
|
||||||
mPartitionPath[2].doExtract = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NcaProcess::setPartition3ExtractPath(const std::string& path)
|
|
||||||
{
|
|
||||||
mPartitionPath[3].path = path;
|
|
||||||
mPartitionPath[3].doExtract = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NcaProcess::setListFs(bool list_fs)
|
|
||||||
{
|
|
||||||
mListFs = list_fs;
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue