[nstool] Add NCA partition decryption/extraction.

This commit is contained in:
jakcron 2018-05-11 17:51:14 +08:00
parent 2fef609bbd
commit 00e1571ec7
11 changed files with 552 additions and 23 deletions

View file

@ -0,0 +1,71 @@
#include "AesCtrWrappedIFile.h"
AesCtrWrappedIFile::AesCtrWrappedIFile(fnd::IFile& file, const crypto::aes::sAes128Key& key, const crypto::aes::sAesIvCtr& ctr) :
mFile(file),
mKey(key),
mBaseCtr(ctr)
{
mScratch.alloc(kAesCtrScratchAllocSize);
}
size_t AesCtrWrappedIFile::size()
{
return mFile.size();
}
void AesCtrWrappedIFile::seek(size_t offset)
{
mFile.seek(offset);
crypto::aes::AesIncrementCounter(mBaseCtr.iv, offset>>4, mCurrentCtr.iv);
mBlockOffset = offset & 0xf;
}
void AesCtrWrappedIFile::read(byte_t* out, size_t len)
{
for (size_t i = 0; i < (len / kAesCtrScratchSize); i++)
{
mFile.read(mScratch.getBytes() + mBlockOffset, kAesCtrScratchSize);
crypto::aes::AesCtr(mScratch.getBytes(), kAesCtrScratchAllocSize, mKey.key, mCurrentCtr.iv, mScratch.getBytes());
memcpy(out + (i * kAesCtrScratchSize), mScratch.getBytes() + mBlockOffset, kAesCtrScratchSize);
}
if (len % kAesCtrScratchSize)
{
size_t read_len = len % kAesCtrScratchSize;
size_t read_pos = ((len / kAesCtrScratchSize) * kAesCtrScratchSize);
mFile.read(mScratch.getBytes() + mBlockOffset, read_len);
crypto::aes::AesCtr(mScratch.getBytes(), kAesCtrScratchAllocSize, mKey.key, mCurrentCtr.iv, mScratch.getBytes());
memcpy(out + read_pos, mScratch.getBytes() + mBlockOffset, read_len);
}
}
void AesCtrWrappedIFile::read(byte_t* out, size_t offset, size_t len)
{
seek(offset);
read(out, len);
}
void AesCtrWrappedIFile::write(const byte_t* out, size_t len)
{
for (size_t i = 0; i < (len / kAesCtrScratchSize); i++)
{
memcpy(mScratch.getBytes() + mBlockOffset, out + (i * kAesCtrScratchSize), kAesCtrScratchSize);
crypto::aes::AesCtr(mScratch.getBytes(), kAesCtrScratchAllocSize, mKey.key, mCurrentCtr.iv, mScratch.getBytes());
mFile.write(mScratch.getBytes() + mBlockOffset, kAesCtrScratchSize);
}
if (len % kAesCtrScratchSize)
{
size_t write_len = len % kAesCtrScratchSize;
size_t write_pos = ((len / kAesCtrScratchSize) * kAesCtrScratchSize);
memcpy(mScratch.getBytes() + mBlockOffset, out + write_pos, write_len);
crypto::aes::AesCtr(mScratch.getBytes(), kAesCtrScratchAllocSize, mKey.key, mCurrentCtr.iv, mScratch.getBytes());
mFile.write(mScratch.getBytes() + mBlockOffset, write_len);
}
}
void AesCtrWrappedIFile::write(const byte_t* out, size_t offset, size_t len)
{
seek(offset);
write(out, len);
}

View file

@ -0,0 +1,27 @@
#include <fnd/IFile.h>
#include <fnd/MemoryBlob.h>
#include <crypto/aes.h>
class AesCtrWrappedIFile : public fnd::IFile
{
public:
AesCtrWrappedIFile(fnd::IFile& file, const crypto::aes::sAes128Key& key, const crypto::aes::sAesIvCtr& ctr);
size_t size();
void seek(size_t offset);
void read(byte_t* out, size_t len);
void read(byte_t* out, size_t offset, size_t len);
void write(const byte_t* out, size_t len);
void write(const byte_t* out, size_t offset, size_t len);
private:
const std::string kModuleName = "AesCtrWrappedIFile";
static const size_t kAesCtrScratchSize = 0x1000000;
static const size_t kAesCtrScratchAllocSize = kAesCtrScratchSize + crypto::aes::kAesBlockSize;
fnd::IFile& mFile;
crypto::aes::sAes128Key mKey;
crypto::aes::sAesIvCtr mBaseCtr, mCurrentCtr;
size_t mBlockOffset;
fnd::MemoryBlob mScratch;
};

View file

@ -0,0 +1,16 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/SimpleFile.h>
#include <nx/NcaHeader.h>
#include "nstool.h"
class NcaPartitionProcess
{
public:
NcaPartitionProcess();
private:
const std::string kModuleName = "NcaPartitionProcess";
};

View file

@ -1,6 +1,11 @@
#include "NcaProcess.h"
#include <fnd/SimpleTextOutput.h>
#include <nx/NcaUtils.h>
#include <nx/AesKeygen.h>
#include "NcaProcess.h"
#include "PfsProcess.h"
#include "RomfsProcess.h"
#include "OffsetAdjustedIFile.h"
#include "AesCtrWrappedIFile.h"
std::string kFormatVersionStr[]
{
@ -20,7 +25,8 @@ std::string kContentTypeStr[]
"Meta",
"Control",
"Manual",
"Data"
"Data",
"PublicData"
};
std::string kEncryptionTypeStr[]
@ -35,7 +41,7 @@ std::string kEncryptionTypeStr[]
std::string kHashTypeStr[]
{
"Auto",
"UNKNOWN_1",
"None",
"HierarchicalSha256",
"HierarchicalIntegrity"
};
@ -81,6 +87,7 @@ void NcaProcess::displayHeader()
}
}
/*
if (mBodyKeyList.getSize() > 0)
{
printf(" Key Area Keys:\n");
@ -90,23 +97,221 @@ void NcaProcess::displayHeader()
fnd::SimpleTextOutput::hexDump(mBodyKeyList[i].key, crypto::aes::kAes128KeySize);
}
}
*/
printf(" Partitions:\n");
for (size_t i = 0; i < mHdr.getPartitions().getSize(); i++)
{
const nx::NcaHeader::sPartition& partition = mHdr.getPartitions()[i];
nx::sNcaFsHeader& fs_header = mHdrBlock.fs_header[partition.index];
printf(" %lu:\n", i);
printf(" Index: %d\n", partition.index);
printf(" Offset: 0x%" PRIx64 "\n", partition.offset);
printf(" Size: 0x%" PRIx64 "\n", partition.size);
crypto::sha::sSha256Hash ncaFsHeaderHash;
crypto::sha::Sha256((byte_t*)&fs_header, sizeof(nx::sNcaFsHeader), ncaFsHeaderHash.bytes);
if (partition.hash.compare(ncaFsHeaderHash) == false)
{
throw fnd::Exception(kModuleName, "NcaFsHeader has bad sha256 hash");
}
//fnd::SimpleTextOutput::hxdStyleDump((byte_t*)&fs_header, sizeof(nx::sNcaFsHeader));
printf(" FsHeader:\n");
printf(" Version: 0x%d\n", fs_header.version.get());
printf(" Format Type: %s\n", kFormatTypeStr[fs_header.format_type].c_str());
printf(" Hash Type: %s\n", kHashTypeStr[fs_header.hash_type].c_str());
printf(" Enc. Type: %s\n", kEncryptionTypeStr[fs_header.encryption_type].c_str());
if (fs_header.encryption_type == nx::nca::CRYPT_AESCTR)
{
printf(" CTR: ");
crypto::aes::sAesIvCtr ctr;
nx::NcaUtils::getNcaPartitionAesCtr(&fs_header, ctr.iv);
crypto::aes::AesIncrementCounter(ctr.iv, partition.offset>>4, ctr.iv);
fnd::SimpleTextOutput::hexDump(ctr.iv, sizeof(crypto::aes::sAesIvCtr));
}
if (fs_header.hash_type == nx::nca::HASH_HIERARCHICAL_INTERGRITY)
{
printf(" HierarchicalIntegrity Header:\n");
printf(" Id: 0x%x\n", fs_header.ivfc_header.id.get());
printf(" MasterHashSize: 0x%x\n", fs_header.ivfc_header.master_hash_size.get());
printf(" LevelNum: %d\n", fs_header.ivfc_header.level_num.get());
for (size_t i = 0; i < fs_header.ivfc_header.level_num.get(); i++)
{
printf(" Level %d:\n", i);
printf(" LogicalOffset: 0x%" PRIx64 "\n", fs_header.ivfc_header.level_header[i].logical_offset.get());
printf(" HashDataSize: 0x%" PRIx64 "\n", fs_header.ivfc_header.level_header[i].hash_data_size.get());
printf(" BlockSize: 0x%" PRIx32 "\n", fs_header.ivfc_header.level_header[i].block_size.get());
}
printf(" Master Hash: ");
fnd::SimpleTextOutput::hexDump(fs_header.ivfc_header.master_hash.bytes, 0x20);
}
else if (fs_header.hash_type == nx::nca::HASH_HIERARCHICAL_SHA256)
{
nx::sHierarchicalSha256Header& hash_hdr = fs_header.hierarchicalsha256_header;
printf(" HierarchicalSha256 Header:\n");
printf(" Master Hash: ");
fnd::SimpleTextOutput::hexDump(hash_hdr.master_hash.bytes, 0x20);
printf(" HashBlockSize: 0x%x\n", hash_hdr.hash_block_size.get());
printf(" HashLevelNum: 0x%x\n", hash_hdr.hash_level_num.get());
printf(" HashDataOffset: 0x%" PRIx64 "\n", hash_hdr.hash_data.offset.get());
printf(" HashDataSize: 0x%" PRIx64 "\n", hash_hdr.hash_data.size.get());
printf(" HashTargetOffset: 0x%" PRIx64 "\n", hash_hdr.hash_target.offset.get());
printf(" HashTargetSize: 0x%" PRIx64 "\n", hash_hdr.hash_target.size.get());
}
else
{
printf(" Hash Superblock:\n");
fnd::SimpleTextOutput::hxdStyleDump(fs_header.hash_superblock, nx::nca::kFsHeaderHashSuperblockLen);
}
}
}
void NcaProcess::decryptBodyKeyList()
void NcaProcess::generateNcaBodyEncryptionKeys()
{
crypto::aes::sAes128Key zero_key;
memset(zero_key.key, 0, sizeof(zero_key));
// create zeros key
crypto::aes::sAes128Key zero_aesctr_key;
memset(zero_aesctr_key.key, 0, sizeof(zero_aesctr_key));
crypto::aes::sAesXts128Key zero_aesxts_key;
memset(zero_aesxts_key.key, 0, sizeof(zero_aesxts_key));
// get key data from header
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)
// set flag to indicate that the keys are not available
mBodyKeys.aes_ctr.isSet = false;
mBodyKeys.aes_xts.isSet = false;
// if this has a rights id, the key needs to be sourced from a ticket
if (mHdr.hasRightsId() == true)
{
for (size_t i = 0; i < 4; i++)
// if the titlekey_kek is available
if (mKeyset->ticket.titlekey_kek[masterkey_rev] != zero_aesctr_key)
{
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);
// the title key is provided (sourced from ticket)
if (mKeyset->nca.manual_title_key_aesctr != zero_aesctr_key)
{
nx::AesKeygen::generateKey(mBodyKeys.aes_ctr.var.key, mKeyset->nca.manual_title_key_aesctr.key, mKeyset->ticket.titlekey_kek[masterkey_rev].key);
mBodyKeys.aes_ctr.isSet = true;
}
if (mKeyset->nca.manual_title_key_aesxts != zero_aesxts_key)
{
nx::AesKeygen::generateKey(mBodyKeys.aes_xts.var.key[0], mKeyset->nca.manual_title_key_aesxts.key[0], mKeyset->ticket.titlekey_kek[masterkey_rev].key);
nx::AesKeygen::generateKey(mBodyKeys.aes_xts.var.key[1], mKeyset->nca.manual_title_key_aesxts.key[1], mKeyset->ticket.titlekey_kek[masterkey_rev].key);
mBodyKeys.aes_xts.isSet = true;
}
}
}
// otherwise decrypt key area
else
{
// if the titlekey_kek is available
if (mKeyset->nca.key_area_key[keak_index][masterkey_rev] != zero_aesctr_key)
{
nx::AesKeygen::generateKey(mBodyKeys.aes_ctr.var.key, mHdr.getEncAesKeys()[nx::nca::KEY_AESCTR].key, mKeyset->nca.key_area_key[keak_index][masterkey_rev].key);
mBodyKeys.aes_ctr.isSet = true;
nx::AesKeygen::generateKey(mBodyKeys.aes_xts.var.key[0], mHdr.getEncAesKeys()[nx::nca::KEY_AESXTS_0].key, mKeyset->nca.key_area_key[keak_index][masterkey_rev].key);
nx::AesKeygen::generateKey(mBodyKeys.aes_xts.var.key[1], mHdr.getEncAesKeys()[nx::nca::KEY_AESXTS_1].key, mKeyset->nca.key_area_key[keak_index][masterkey_rev].key);
mBodyKeys.aes_xts.isSet = true;
}
}
// if the keys weren't generated, check if the keys were supplied by the user
if (mBodyKeys.aes_ctr.isSet == false && mKeyset->nca.manual_body_key_aesctr != zero_aesctr_key)
{
mBodyKeys.aes_ctr = mKeyset->nca.manual_body_key_aesctr;
}
if (mBodyKeys.aes_xts.isSet == false && mKeyset->nca.manual_body_key_aesxts != zero_aesxts_key)
{
mBodyKeys.aes_xts = mKeyset->nca.manual_body_key_aesxts;
}
}
void NcaProcess::processPartitions()
{
for (size_t i = 0; i < mHdr.getPartitions().getSize(); i++)
{
const nx::NcaHeader::sPartition& partition = mHdr.getPartitions()[i];
nx::sNcaFsHeader& fs_header = mHdrBlock.fs_header[partition.index];
crypto::aes::sAesIvCtr ctr;
nx::NcaUtils::getNcaPartitionAesCtr(&fs_header, ctr.iv);
// create reader
fnd::IFile* partitionReader = nullptr;
AesCtrWrappedIFile aesCtrFile = AesCtrWrappedIFile(*mReader, mBodyKeys.aes_ctr.var, ctr);
switch(fs_header.encryption_type)
{
case (nx::nca::CRYPT_AESXTS):
case (nx::nca::CRYPT_AESCTREX):
partitionReader = nullptr;
break;
case (nx::nca::CRYPT_AESCTR):
partitionReader = mBodyKeys.aes_ctr.isSet? &aesCtrFile : nullptr;
break;
case (nx::nca::CRYPT_NONE):
partitionReader = mReader;
break;
}
// if the reader is null, skip
if (partitionReader == nullptr)
{
printf("[WARNING] NCA Partition %d not readable\n", partition.index);
continue;
}
size_t data_offset = 0;
switch (fs_header.hash_type)
{
case (nx::nca::HASH_HIERARCHICAL_SHA256):
data_offset = fs_header.hierarchicalsha256_header.hash_target.offset.get();
break;
case (nx::nca::HASH_HIERARCHICAL_INTERGRITY):
data_offset = fs_header.ivfc_header.level_header[5].logical_offset.get();
break;
case (nx::nca::HASH_NONE):
data_offset = 0;
break;
default:
throw fnd::Exception(kModuleName, "Unknown hash type");
}
if (fs_header.format_type == nx::nca::FORMAT_PFS0)
{
PfsProcess pfs;
pfs.setInputFile(*partitionReader);
pfs.setInputFileOffset(partition.offset + data_offset);
pfs.setCliOutputMode(mCliOutputType);
pfs.setListFs(mListFs);
if (mPartitionPath[partition.index].doExtract)
pfs.setExtractPath(mPartitionPath[partition.index].path);
pfs.process();
}
else if (fs_header.format_type == nx::nca::FORMAT_ROMFS)
{
RomfsProcess romfs;
romfs.setInputFile(*partitionReader);
romfs.setInputFileOffset(partition.offset + data_offset);
romfs.setCliOutputMode(mCliOutputType);
romfs.setListFs(mListFs);
if (mPartitionPath[partition.index].doExtract)
romfs.setExtractPath(mPartitionPath[partition.index].path);
romfs.process();
}
else
{
throw fnd::Exception(kModuleName, "Unknown format type");
}
}
}
@ -116,7 +321,16 @@ NcaProcess::NcaProcess() :
mOffset(0),
mKeyset(nullptr),
mCliOutputType(OUTPUT_NORMAL),
mVerify(false)
mVerify(false),
mListFs(false)
{
mPartitionPath[0].doExtract = false;
mPartitionPath[1].doExtract = false;
mPartitionPath[2].doExtract = false;
mPartitionPath[3].doExtract = false;
}
NcaProcess::~NcaProcess()
{
}
@ -136,19 +350,80 @@ void NcaProcess::process()
// 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);
// validate signature[0]
if (mVerify)
{
if (crypto::rsa::pss::rsaVerify(mKeyset->nca.header_sign_key, crypto::sha::HASH_SHA256, mHdrHash.bytes, mHdrBlock.signature_main) != 0)
{
// this is minimal even though it's a warning because it's a validation method
if (mCliOutputType >= OUTPUT_MINIMAL)
printf("[WARNING] NCA Header Main Signature: FAIL \n");
}
}
// proccess main header
mHdr.importBinary((byte_t*)&mHdrBlock.header, sizeof(nx::sNcaHeader));
// validate fs headers
if (mVerify)
{
crypto::sha::sSha256Hash calc_hash;
for (size_t i = 0; i < mHdr.getPartitions().getSize(); i++)
{
const nx::NcaHeader::sPartition& partition = mHdr.getPartitions()[i];
crypto::sha::Sha256((const byte_t*)&mHdrBlock.fs_header[partition.index], sizeof(nx::sNcaFsHeader), calc_hash.bytes);
if (calc_hash.compare(partition.hash) == false)
{
// this is minimal even though it's a warning because it's a validation method
if (mCliOutputType >= OUTPUT_MINIMAL)
printf("[WARNING] NCA FsHeader[%d] Hash: FAIL \n", partition.index);
}
}
}
// determine keys
/*
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
*/
// decrypt key area
decryptBodyKeyList();
generateNcaBodyEncryptionKeys();
if (mCliOutputType >= OUTPUT_NORMAL)
displayHeader();
processPartitions();
}
void NcaProcess::setInputFile(fnd::IFile& reader)
void NcaProcess::setInputFile(fnd::IFile* reader)
{
mReader = &reader;
mReader = reader;
}
void NcaProcess::setInputFileOffset(size_t offset)
@ -170,3 +445,32 @@ 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;
}

View file

@ -10,34 +10,59 @@ class NcaProcess
{
public:
NcaProcess();
~NcaProcess();
void process();
// generic
void setInputFile(fnd::IFile& reader);
void setInputFile(fnd::IFile* reader);
void setInputFileOffset(size_t offset);
void setKeyset(const sKeyset* keyset);
void setCliOutputMode(CliOutputType type);
void setVerifyMode(bool verify);
// nca specfic
void setPartition0ExtractPath(const std::string& path);
void setPartition1ExtractPath(const std::string& path);
void setPartition2ExtractPath(const std::string& path);
void setPartition3ExtractPath(const std::string& path);
void setListFs(bool list_fs);
private:
const std::string kModuleName = "NcaProcess";
// user options
fnd::IFile* mReader;
size_t mOffset;
const sKeyset* mKeyset;
CliOutputType mCliOutputType;
bool mVerify;
struct sExtract
{
std::string path;
bool doExtract;
} mPartitionPath[nx::nca::kPartitionNum];
bool mListFs;
// data
nx::sNcaHeaderBlock mHdrBlock;
crypto::sha::sSha256Hash mHdrHash;
nx::NcaHeader mHdr;
fnd::List<crypto::aes::sAes128Key> mBodyKeyList;
// crypto
struct sKeys
{
sOptional<crypto::aes::sAes128Key> aes_ctr;
sOptional<crypto::aes::sAesXts128Key> aes_xts;
} mBodyKeys;
void displayHeader();
void decryptBodyKeyList();
void generateNcaBodyEncryptionKeys();
void processPartitions();
};

View file

@ -0,0 +1,45 @@
#include "OffsetAdjustedIFile.h"
OffsetAdjustedIFile::OffsetAdjustedIFile(fnd::IFile& file, size_t offset, size_t size) :
mFile(file),
mBaseOffset(offset),
mCurrentOffset(0),
mSize(size)
{
}
size_t OffsetAdjustedIFile::size()
{
return mSize;
}
void OffsetAdjustedIFile::seek(size_t offset)
{
mCurrentOffset = offset;
mFile.seek(offset + mBaseOffset);
}
void OffsetAdjustedIFile::read(byte_t* out, size_t len)
{
seek(mCurrentOffset);
mFile.read(out, len);
}
void OffsetAdjustedIFile::read(byte_t* out, size_t offset, size_t len)
{
seek(offset);
read(out, len);
}
void OffsetAdjustedIFile::write(const byte_t* out, size_t len)
{
seek(mCurrentOffset);
mFile.write(out, len);
}
void OffsetAdjustedIFile::write(const byte_t* out, size_t offset, size_t len)
{
seek(offset);
write(out, len);
}

View file

@ -0,0 +1,18 @@
#include <fnd/IFile.h>
class OffsetAdjustedIFile : public fnd::IFile
{
public:
OffsetAdjustedIFile(fnd::IFile& file, size_t offset, size_t size);
size_t size();
void seek(size_t offset);
void read(byte_t* out, size_t len);
void read(byte_t* out, size_t offset, size_t len);
void write(const byte_t* out, size_t len);
void write(const byte_t* out, size_t offset, size_t len);
private:
fnd::IFile& mFile;
size_t mBaseOffset, mCurrentOffset;
size_t mSize;
};

View file

@ -429,7 +429,14 @@ void UserSettings::populateKeyset(sCmdArgs& args)
if (args.nca_titlekey.isSet)
{
decodeHexStringToBytes("--titlekey", args.nca_titlekey.var, mKeyset.nca.manual_title_key.key, sizeof(crypto::aes::sAes128Key));
if (args.nca_bodykey.var.length() == (sizeof(crypto::aes::sAes128Key)*2))
{
decodeHexStringToBytes("--titlekey", args.nca_titlekey.var, mKeyset.nca.manual_title_key_aesctr.key, sizeof(crypto::aes::sAes128Key));
}
else
{
decodeHexStringToBytes("--titlekey", args.nca_titlekey.var, mKeyset.nca.manual_title_key_aesxts.key[0], sizeof(crypto::aes::sAesXts128Key));
}
}
#undef _SAVE_KEYDATA
@ -511,6 +518,10 @@ void UserSettings::populateUserSettings(sCmdArgs& args)
mNormalPath = args.normal_path;
mSecurePath = args.secure_path;
mFsPath = args.fs_path;
mPart0Path = args.part0_path;
mPart1Path = args.part1_path;
mPart2Path = args.part2_path;
mPart3Path = args.part3_path;
// determine output path
if (args.verbose_output.isSet)

View file

@ -71,11 +71,22 @@ int main(int argc, char** argv)
{
NcaProcess nca;
nca.setInputFile(inputFile);
nca.setInputFile(&inputFile);
nca.setKeyset(&user_set.getKeyset());
nca.setCliOutputMode(user_set.getCliOutputType());
nca.setVerifyMode(user_set.isVerifyFile());
if (user_set.getPart0Path().isSet)
nca.setPartition0ExtractPath(user_set.getPart0Path().var);
if (user_set.getPart1Path().isSet)
nca.setPartition1ExtractPath(user_set.getPart1Path().var);
if (user_set.getPart2Path().isSet)
nca.setPartition2ExtractPath(user_set.getPart2Path().var);
if (user_set.getPart3Path().isSet)
nca.setPartition3ExtractPath(user_set.getPart3Path().var);
nca.setListFs(user_set.isListFs());
nca.process();
}
else if (user_set.getFileType() == FILE_NPDM)

View file

@ -58,7 +58,8 @@ struct sKeyset
crypto::aes::sAesXts128Key header_key;
crypto::aes::sAes128Key key_area_key[kNcaKeakNum][kMasterKeyNum];
crypto::aes::sAes128Key manual_title_key;
crypto::aes::sAes128Key manual_title_key_aesctr;
crypto::aes::sAesXts128Key manual_title_key_aesxts;
crypto::aes::sAes128Key manual_body_key_aesctr;
crypto::aes::sAesXts128Key manual_body_key_aesxts;
} nca;