[nstool] Add inital NCA support. Reads header and decrypts key area.

This commit is contained in:
jakcron 2018-04-25 23:21:47 +08:00
parent 10655e828e
commit bf1273e5ba
3 changed files with 189 additions and 11 deletions

View 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;
}

View file

@ -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();
}; };

View file

@ -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)
{ {