mirror of
https://github.com/jakcron/nstool
synced 2024-11-15 02:06:40 +00:00
[nstool] Add inital NCA support. Reads header and decrypts key area.
This commit is contained in:
parent
10655e828e
commit
bf1273e5ba
3 changed files with 189 additions and 11 deletions
172
programs/nstool/source/NcaProcess.cpp
Normal file
172
programs/nstool/source/NcaProcess.cpp
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
#include "NcaProcess.h"
|
||||||
|
#include <fnd/SimpleTextOutput.h>
|
||||||
|
#include <nx/NcaUtils.h>
|
||||||
|
|
||||||
|
std::string kFormatVersionStr[]
|
||||||
|
{
|
||||||
|
"NCA2",
|
||||||
|
"NCA3"
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string kDistributionTypeStr[]
|
||||||
|
{
|
||||||
|
"Download",
|
||||||
|
"Game Card"
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string kContentTypeStr[]
|
||||||
|
{
|
||||||
|
"Program",
|
||||||
|
"Meta",
|
||||||
|
"Control",
|
||||||
|
"Manual",
|
||||||
|
"Data"
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string kEncryptionTypeStr[]
|
||||||
|
{
|
||||||
|
"Auto",
|
||||||
|
"None",
|
||||||
|
"AesXts",
|
||||||
|
"AesCtr",
|
||||||
|
"AesCtrEx"
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string kHashTypeStr[]
|
||||||
|
{
|
||||||
|
"Auto",
|
||||||
|
"UNKNOWN_1",
|
||||||
|
"HierarchicalSha256",
|
||||||
|
"HierarchicalIntegrity"
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string kFormatTypeStr[]
|
||||||
|
{
|
||||||
|
"RomFs",
|
||||||
|
"PartitionFs"
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string kKaekIndexStr[]
|
||||||
|
{
|
||||||
|
"Application",
|
||||||
|
"Ocean",
|
||||||
|
"System"
|
||||||
|
};
|
||||||
|
|
||||||
|
void NcaProcess::displayHeader()
|
||||||
|
{
|
||||||
|
crypto::aes::sAes128Key zero_key;
|
||||||
|
memset(zero_key.key, 0, sizeof(zero_key));
|
||||||
|
|
||||||
|
printf("[NCA Header]\n");
|
||||||
|
printf(" Format Type: %s\n", kFormatVersionStr[mHdr.getFormatVersion()].c_str());
|
||||||
|
printf(" Dist. Type: %s\n", kDistributionTypeStr[mHdr.getDistributionType()].c_str());
|
||||||
|
printf(" Content Type: %s\n", kContentTypeStr[mHdr.getContentType()].c_str());
|
||||||
|
printf(" Key Generation: %d\n", mHdr.getKeyGeneration());
|
||||||
|
printf(" Kaek Index: %s (%d)\n", kKaekIndexStr[mHdr.getKaekIndex()].c_str(), mHdr.getKaekIndex());
|
||||||
|
printf(" Size: 0x%" PRIx64 "\n", mHdr.getContentSize());
|
||||||
|
printf(" ProgID: 0x%016" PRIx64 "\n", mHdr.getProgramId());
|
||||||
|
printf(" Content Index: %" PRIu32 "\n", mHdr.getContentIndex());
|
||||||
|
uint32_t ver = mHdr.getSdkAddonVersion();
|
||||||
|
printf(" SdkAddon Ver.: v%d.%d.%d (v%" PRIu32 ")\n", (ver>>24 & 0xff),(ver>>16 & 0xff),(ver>>8 & 0xff), ver);
|
||||||
|
printf(" RightsId: ");
|
||||||
|
fnd::SimpleTextOutput::hexDump(mHdr.getRightsId(), nx::nca::kRightsIdLen);
|
||||||
|
printf(" Key Area Keys: (Encrypted)\n");
|
||||||
|
for (size_t i = 0; i < mHdr.getEncAesKeys().getSize(); i++)
|
||||||
|
{
|
||||||
|
if (mHdr.getEncAesKeys()[i] != zero_key)
|
||||||
|
{
|
||||||
|
printf(" %2lu: ", i);
|
||||||
|
fnd::SimpleTextOutput::hexDump(mHdr.getEncAesKeys()[i].key, crypto::aes::kAes128KeySize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mBodyKeyList.getSize() > 0)
|
||||||
|
{
|
||||||
|
printf(" Key Area Keys:\n");
|
||||||
|
for (size_t i = 0; i < mBodyKeyList.getSize(); i++)
|
||||||
|
{
|
||||||
|
printf(" %2lu: ", i);
|
||||||
|
fnd::SimpleTextOutput::hexDump(mBodyKeyList[i].key, crypto::aes::kAes128KeySize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::decryptBodyKeyList()
|
||||||
|
{
|
||||||
|
crypto::aes::sAes128Key zero_key;
|
||||||
|
memset(zero_key.key, 0, sizeof(zero_key));
|
||||||
|
|
||||||
|
byte_t masterkey_rev = nx::NcaUtils::getMasterKeyRevisionFromKeyGeneration(mHdr.getKeyGeneration());
|
||||||
|
byte_t keak_index = mHdr.getKaekIndex();
|
||||||
|
|
||||||
|
crypto::aes::sAes128Key tmp_key;
|
||||||
|
if (mKeyset->nca.key_area_key[keak_index][masterkey_rev] != zero_key)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
crypto::aes::AesEcbDecrypt(mHdr.getEncAesKeys()[i].key, 0x10, mKeyset->nca.key_area_key[keak_index][masterkey_rev].key, tmp_key.key);
|
||||||
|
mBodyKeyList.addElement(tmp_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NcaProcess::NcaProcess() :
|
||||||
|
mReader(nullptr),
|
||||||
|
mOffset(0),
|
||||||
|
mKeyset(nullptr),
|
||||||
|
mCliOutputType(OUTPUT_NORMAL),
|
||||||
|
mVerify(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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, mOffset, sizeof(nx::sNcaHeaderBlock));
|
||||||
|
|
||||||
|
// decrypt header block
|
||||||
|
nx::NcaUtils::decryptNcaHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, mKeyset->nca.header_key);
|
||||||
|
|
||||||
|
// proccess main header
|
||||||
|
mHdr.importBinary((byte_t*)&mHdrBlock.header, sizeof(nx::sNcaHeader));
|
||||||
|
|
||||||
|
// decrypt key area
|
||||||
|
decryptBodyKeyList();
|
||||||
|
|
||||||
|
if (mCliOutputType >= OUTPUT_NORMAL)
|
||||||
|
displayHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setInputFile(fnd::IFile& reader)
|
||||||
|
{
|
||||||
|
mReader = &reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setInputFileOffset(size_t offset)
|
||||||
|
{
|
||||||
|
mOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setKeyset(const sKeyset* keyset)
|
||||||
|
{
|
||||||
|
mKeyset = keyset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setCliOutputMode(CliOutputType type)
|
||||||
|
{
|
||||||
|
mCliOutputType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NcaProcess::setVerifyMode(bool verify)
|
||||||
|
{
|
||||||
|
mVerify = verify;
|
||||||
|
}
|
|
@ -2,7 +2,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fnd/types.h>
|
#include <fnd/types.h>
|
||||||
#include <fnd/SimpleFile.h>
|
#include <fnd/SimpleFile.h>
|
||||||
#include <nx/nca.h>
|
|
||||||
#include <nx/NcaHeader.h>
|
#include <nx/NcaHeader.h>
|
||||||
|
|
||||||
#include "nstool.h"
|
#include "nstool.h"
|
||||||
|
@ -11,7 +10,6 @@ class NcaProcess
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NcaProcess();
|
NcaProcess();
|
||||||
~NcaProcess();
|
|
||||||
|
|
||||||
void process();
|
void process();
|
||||||
|
|
||||||
|
@ -27,7 +25,19 @@ public:
|
||||||
private:
|
private:
|
||||||
const std::string kModuleName = "NcaProcess";
|
const std::string kModuleName = "NcaProcess";
|
||||||
|
|
||||||
byte_t mRawHeader[nx::nca::kHeaderSize];
|
fnd::IFile* mReader;
|
||||||
std::string mPath;
|
size_t mOffset;
|
||||||
const sKeyset* mKeyset;
|
const sKeyset* mKeyset;
|
||||||
|
CliOutputType mCliOutputType;
|
||||||
|
bool mVerify;
|
||||||
|
|
||||||
|
nx::sNcaHeaderBlock mHdrBlock;
|
||||||
|
nx::NcaHeader mHdr;
|
||||||
|
|
||||||
|
fnd::List<crypto::aes::sAes128Key> mBodyKeyList;
|
||||||
|
|
||||||
|
|
||||||
|
void displayHeader();
|
||||||
|
|
||||||
|
void decryptBodyKeyList();
|
||||||
};
|
};
|
|
@ -4,7 +4,7 @@
|
||||||
#include "XciProcess.h"
|
#include "XciProcess.h"
|
||||||
#include "PfsProcess.h"
|
#include "PfsProcess.h"
|
||||||
#include "RomfsProcess.h"
|
#include "RomfsProcess.h"
|
||||||
//#include "NcaProcess.h"
|
#include "NcaProcess.h"
|
||||||
#include "NpdmProcess.h"
|
#include "NpdmProcess.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,7 +54,6 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
else if (user_set.getFileType() == FILE_ROMFS)
|
else if (user_set.getFileType() == FILE_ROMFS)
|
||||||
{
|
{
|
||||||
|
|
||||||
RomfsProcess romfs;
|
RomfsProcess romfs;
|
||||||
|
|
||||||
romfs.setInputFile(inputFile);
|
romfs.setInputFile(inputFile);
|
||||||
|
@ -67,20 +66,17 @@ int main(int argc, char** argv)
|
||||||
romfs.setListFs(user_set.isListFs());
|
romfs.setListFs(user_set.isListFs());
|
||||||
|
|
||||||
romfs.process();
|
romfs.process();
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (user_set.getFileType() == FILE_NCA)
|
else if (user_set.getFileType() == FILE_NCA)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
NcaProcess nca;
|
NcaProcess nca;
|
||||||
|
|
||||||
nca.setNcaPath(user_set.getInputPath());
|
nca.setInputFile(inputFile);
|
||||||
nca.setKeyset(user_set.getKeyset());
|
nca.setKeyset(&user_set.getKeyset());
|
||||||
nca.setCliOutputMode(user_set.getCliOutputType());
|
nca.setCliOutputMode(user_set.getCliOutputType());
|
||||||
nca.setVerifyMode(user_set.isVerifyFile());
|
nca.setVerifyMode(user_set.isVerifyFile());
|
||||||
|
|
||||||
nca.process();
|
nca.process();
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
else if (user_set.getFileType() == FILE_NPDM)
|
else if (user_set.getFileType() == FILE_NPDM)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue