[hac|nstool] Rename NcaHeader to ContentArchiveHeader.

This commit is contained in:
jakcron 2018-10-25 20:25:31 +08:00
parent 4e6f7fed80
commit dfe877b959
16 changed files with 482 additions and 455 deletions

View file

@ -57,8 +57,8 @@ namespace hac
// variables
void clear();
const fnd::rsa::sRsa2048Key& getNcaHeaderSignature2Key() const;
void setNcaHeaderSignature2Key(const fnd::rsa::sRsa2048Key& key);
const fnd::rsa::sRsa2048Key& getContentArchiveHeaderSignature2Key() const;
void setContentArchiveHeaderSignature2Key(const fnd::rsa::sRsa2048Key& key);
const fnd::List<aci::Flag>& getFlagList() const;
void setFlagList(const fnd::List<aci::Flag>& flags);
@ -81,7 +81,7 @@ namespace hac
fnd::Vec<byte_t> mRawBinary;
// variables
fnd::rsa::sRsa2048Key mNcaHeaderSignature2Key;
fnd::rsa::sRsa2048Key mContentArchiveHeaderSignature2Key;
fnd::List<aci::Flag> mFlags;
sProgramIdRestrict mProgramIdRestrict;
nn::hac::FileSystemAccessControl mFileSystemAccessControl;

View file

@ -7,53 +7,47 @@ namespace nn
{
namespace hac
{
class NcaHeader :
class ContentArchiveHeader :
public fnd::IByteModel
{
public:
enum FormatVersion
struct sPartitionEntry
{
NCA2_FORMAT,
NCA3_FORMAT
};
struct sPartition
{
byte_t index;
byte_t header_index;
uint64_t offset;
uint64_t size;
fnd::sha::sSha256Hash hash;
fnd::sha::sSha256Hash fs_header_hash;
const sPartition& operator=(const sPartition& other)
const sPartitionEntry& operator=(const sPartitionEntry& other)
{
index = other.index;
header_index = other.header_index;
offset = other.offset;
size = other.size;
hash = other.hash;
fs_header_hash = other.fs_header_hash;
return *this;
}
bool operator==(const sPartition& other) const
bool operator==(const sPartitionEntry& other) const
{
return (index == other.index) \
return (header_index == other.header_index) \
&& (offset == other.offset) \
&& (size == other.size) \
&& (hash == other.hash);
&& (fs_header_hash == other.fs_header_hash);
}
bool operator!=(const sPartition& other) const
bool operator!=(const sPartitionEntry& other) const
{
return !operator==(other);
}
};
NcaHeader();
NcaHeader(const NcaHeader& other);
ContentArchiveHeader();
ContentArchiveHeader(const ContentArchiveHeader& other);
void operator=(const NcaHeader& other);
bool operator==(const NcaHeader& other) const;
bool operator!=(const NcaHeader& other) const;
void operator=(const ContentArchiveHeader& other);
bool operator==(const ContentArchiveHeader& other) const;
bool operator!=(const ContentArchiveHeader& other) const;
// IByteModel
void toBytes();
@ -62,40 +56,52 @@ namespace hac
// variables
void clear();
FormatVersion getFormatVersion() const;
void setFormatVersion(FormatVersion ver);
byte_t getFormatVersion() const;
void setFormatVersion(byte_t ver);
nca::DistributionType getDistributionType() const;
void setDistributionType(nca::DistributionType type);
nca::ContentType getContentType() const;
void setContentType(nca::ContentType type);
byte_t getKeyGeneration() const;
void setKeyGeneration(byte_t gen);
byte_t getKaekIndex() const;
void setKaekIndex(byte_t index);
byte_t getKeyAreaEncryptionKeyIndex() const;
void setKeyAreaEncryptionKeyIndex(byte_t index);
uint64_t getContentSize() const;
void setContentSize(uint64_t size);
uint64_t getProgramId() const;
void setProgramId(uint64_t program_id);
uint32_t getContentIndex() const;
void setContentIndex(uint32_t index);
uint32_t getSdkAddonVersion() const;
void setSdkAddonVersion(uint32_t version);
bool hasRightsId() const;
const byte_t* getRightsId() const;
void setRightsId(const byte_t* rights_id);
const fnd::List<sPartition>& getPartitions() const;
void setPartitions(const fnd::List<sPartition>& partitions);
const fnd::List<fnd::aes::sAes128Key>& getEncAesKeys() const;
void setEncAesKeys(const fnd::List<fnd::aes::sAes128Key>& keys);
const fnd::List<sPartitionEntry>& getPartitionEntryList() const;
void setPartitionEntryList(const fnd::List<sPartitionEntry>& partition_entry_list);
const byte_t* getKeyArea() const;
void setKeyArea(const byte_t* key_area);
private:
const std::string kModuleName = "NCA_HEADER";
const std::string kModuleName = "CONTENT_ARCHIVE_HEADER";
// binary
fnd::Vec<byte_t> mRawBinary;
// data
FormatVersion mFormatVersion;
byte_t mFormatVersion;
nca::DistributionType mDistributionType;
nca::ContentType mContentType;
byte_t mKeyGeneration;
@ -104,9 +110,9 @@ namespace hac
uint64_t mProgramId;
uint32_t mContentIndex;
uint32_t mSdkAddonVersion;
byte_t mRightsId[nca::kRightsIdLen];
fnd::List<sPartition> mPartitions;
fnd::List<fnd::aes::sAes128Key> mEncAesKeys;
fnd::Vec<byte_t> mRightsId;
fnd::List<sPartitionEntry> mPartitionEntryList;
fnd::Vec<byte_t> mKeyArea;
uint64_t blockNumToSize(uint32_t block_num) const;
uint32_t sizeToBlockNum(uint64_t real_size) const;

View file

@ -9,7 +9,7 @@ namespace hac
{
public:
static inline size_t sectorToOffset(size_t sector_index) { return sector_index * nn::hac::nca::kSectorSize; }
static void decryptNcaHeader(const byte_t* src, byte_t* dst, const fnd::aes::sAesXts128Key& key);
static void decryptContentArchiveHeader(const byte_t* src, byte_t* dst, const fnd::aes::sAesXts128Key& key);
static byte_t getMasterKeyRevisionFromKeyGeneration(byte_t key_generation);
static void getNcaPartitionAesCtr(const nn::hac::sNcaFsHeader* hdr, byte_t* ctr);
};

View file

@ -17,17 +17,24 @@ namespace hac
static const size_t kPartitionNum = 4;
static const size_t kHeaderSectorNum = 6;
static const size_t kHeaderSize = kSectorSize * kHeaderSectorNum;
static const size_t kAesKeyNum = 16;
static const size_t kRightsIdLen = 0x10;
static const size_t kKeyAreaSize = 0x100;
static const size_t kKeyAreaKeyNum = kKeyAreaSize / fnd::aes::kAes128KeySize;
static const size_t kKeyAreaEncryptionKeyNum = 3;
static const size_t kFsHeaderHashSuperblockLen = 0x138;
static const uint16_t kDefaultFsHeaderVersion = 2;
enum ProgramPartitionId
enum HeaderFormatVersion
{
PARTITION_CODE,
PARTITION_DATA,
PARTITION_LOGO,
FORMAT_NCA2 = 2,
FORMAT_NCA3 = 3
};
enum ProgramContentPartitionIndex
{
PARTITION_CODE = 0,
PARTITION_DATA = 1,
PARTITION_LOGO = 2,
};
enum DistributionType
@ -83,11 +90,11 @@ namespace hac
CRYPT_AESXTS,
CRYPT_AESCTR,
CRYPT_AESCTREX
};
};
}
#pragma pack(push,1)
struct sNcaHeader
struct sContentArchiveHeader
{
le_uint32_t st_magic;
byte_t distribution_type;
@ -101,15 +108,15 @@ namespace hac
byte_t key_generation_2;
byte_t reserved_2[0xf];
byte_t rights_id[nca::kRightsIdLen];
struct sNcaSection
struct sPartitionEntry
{
le_uint32_t start; // block units
le_uint32_t end; // block units
le_uint32_t start_blk; // block units
le_uint32_t end_blk; // block units
byte_t enabled;
byte_t reserved[7];
} partition[nca::kPartitionNum];
fnd::sha::sSha256Hash partition_hash[nca::kPartitionNum];
fnd::aes::sAes128Key enc_aes_key[nca::kAesKeyNum];
} partition_entry[nca::kPartitionNum];
fnd::sha::sSha256Hash fs_header_hash[nca::kPartitionNum];
byte_t key_area[nca::kKeyAreaSize];
};
struct sNcaFsHeader
@ -124,11 +131,11 @@ namespace hac
byte_t reserved_1[0xB8];
};
struct sNcaHeaderBlock
struct sContentArchiveHeaderBlock
{
byte_t signature_main[fnd::rsa::kRsa2048Size];
byte_t signature_acid[fnd::rsa::kRsa2048Size];
sNcaHeader header;
sContentArchiveHeader header;
sNcaFsHeader fs_header[nn::hac::nca::kPartitionNum];
};

View file

@ -64,7 +64,7 @@
<ClInclude Include="include\nn\hac\MiscParamsHandler.h" />
<ClInclude Include="include\nn\hac\nacp.h" />
<ClInclude Include="include\nn\hac\nca.h" />
<ClInclude Include="include\nn\hac\NcaHeader.h" />
<ClInclude Include="include\nn\hac\ContentArchiveHeader.h" />
<ClInclude Include="include\nn\hac\NcaUtils.h" />
<ClInclude Include="include\nn\hac\nro.h" />
<ClInclude Include="include\nn\hac\NroHeader.h" />
@ -117,7 +117,7 @@
<ClCompile Include="source\MiscFlagsHandler.cpp" />
<ClCompile Include="source\MiscParamsEntry.cpp" />
<ClCompile Include="source\MiscParamsHandler.cpp" />
<ClCompile Include="source\NcaHeader.cpp" />
<ClCompile Include="source\ContentArchiveHeader.cpp" />
<ClCompile Include="source\NcaUtils.cpp" />
<ClCompile Include="source\NroHeader.cpp" />
<ClCompile Include="source\NsoHeader.cpp" />

View file

@ -144,7 +144,7 @@
<ClInclude Include="include\nn\hac\nca.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\nn\hac\NcaHeader.h">
<ClInclude Include="include\nn\hac\ContentArchiveHeader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\nn\hac\NcaUtils.h">
@ -293,7 +293,7 @@
<ClCompile Include="source\MiscParamsHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\NcaHeader.cpp">
<ClCompile Include="source\ContentArchiveHeader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\NcaUtils.cpp">

View file

@ -13,7 +13,7 @@ nn::hac::AccessControlInfoDesc::AccessControlInfoDesc(const AccessControlInfoDes
void nn::hac::AccessControlInfoDesc::operator=(const AccessControlInfoDesc & other)
{
mRawBinary = other.mRawBinary;
mNcaHeaderSignature2Key = other.mNcaHeaderSignature2Key;
mContentArchiveHeaderSignature2Key = other.mContentArchiveHeaderSignature2Key;
mFlags = other.mFlags;
mProgramIdRestrict = other.mProgramIdRestrict;
mFileSystemAccessControl = other.mFileSystemAccessControl;
@ -23,7 +23,7 @@ void nn::hac::AccessControlInfoDesc::operator=(const AccessControlInfoDesc & oth
bool nn::hac::AccessControlInfoDesc::operator==(const AccessControlInfoDesc & other) const
{
return (mNcaHeaderSignature2Key == other.mNcaHeaderSignature2Key) \
return (mContentArchiveHeaderSignature2Key == other.mContentArchiveHeaderSignature2Key) \
&& (mFlags == other.mFlags) \
&& (mProgramIdRestrict == other.mProgramIdRestrict) \
&& (mFileSystemAccessControl == other.mFileSystemAccessControl) \
@ -62,7 +62,7 @@ void nn::hac::AccessControlInfoDesc::toBytes()
sAciDescHeader* hdr = (sAciDescHeader*)mRawBinary.data();
// set rsa modulus
memcpy(hdr->nca_rsa_signature2_modulus, mNcaHeaderSignature2Key.modulus, fnd::rsa::kRsa2048Size);
memcpy(hdr->nca_rsa_signature2_modulus, mContentArchiveHeaderSignature2Key.modulus, fnd::rsa::kRsa2048Size);
// set type
hdr->st_magic = aci::kAciDescStructMagic;
@ -129,7 +129,7 @@ void nn::hac::AccessControlInfoDesc::fromBytes(const byte_t* data, size_t len)
memcpy(mRawBinary.data(), data, mRawBinary.size());
// save variables
memcpy(mNcaHeaderSignature2Key.modulus, hdr.nca_rsa_signature2_modulus, fnd::rsa::kRsa2048Size);
memcpy(mContentArchiveHeaderSignature2Key.modulus, hdr.nca_rsa_signature2_modulus, fnd::rsa::kRsa2048Size);
for (size_t i = 0; i < 32; i++)
{
@ -181,7 +181,7 @@ void nn::hac::AccessControlInfoDesc::validateSignature(const fnd::rsa::sRsa2048K
void nn::hac::AccessControlInfoDesc::clear()
{
mRawBinary.clear();
memset((void*)&mNcaHeaderSignature2Key, 0, sizeof(mNcaHeaderSignature2Key));
memset((void*)&mContentArchiveHeaderSignature2Key, 0, sizeof(mContentArchiveHeaderSignature2Key));
mFlags.clear();
mProgramIdRestrict.min = 0;
mProgramIdRestrict.max = 0;
@ -190,14 +190,14 @@ void nn::hac::AccessControlInfoDesc::clear()
mKernelCapabilities.clear();
}
const fnd::rsa::sRsa2048Key& nn::hac::AccessControlInfoDesc::getNcaHeaderSignature2Key() const
const fnd::rsa::sRsa2048Key& nn::hac::AccessControlInfoDesc::getContentArchiveHeaderSignature2Key() const
{
return mNcaHeaderSignature2Key;
return mContentArchiveHeaderSignature2Key;
}
void nn::hac::AccessControlInfoDesc::setNcaHeaderSignature2Key(const fnd::rsa::sRsa2048Key& key)
void nn::hac::AccessControlInfoDesc::setContentArchiveHeaderSignature2Key(const fnd::rsa::sRsa2048Key& key)
{
mNcaHeaderSignature2Key = key;
mContentArchiveHeaderSignature2Key = key;
}
const fnd::List<nn::hac::aci::Flag>& nn::hac::AccessControlInfoDesc::getFlagList() const

View file

@ -0,0 +1,334 @@
#include <nn/hac/ContentArchiveHeader.h>
nn::hac::ContentArchiveHeader::ContentArchiveHeader()
{
mRightsId.alloc(nca::kRightsIdLen);
mKeyArea.alloc(nca::kKeyAreaSize);
clear();
}
nn::hac::ContentArchiveHeader::ContentArchiveHeader(const ContentArchiveHeader & other) :
ContentArchiveHeader()
{
*this = other;
}
bool nn::hac::ContentArchiveHeader::operator==(const ContentArchiveHeader & other) const
{
return (mFormatVersion == other.mFormatVersion) \
&& (mDistributionType == other.mDistributionType) \
&& (mContentType == other.mContentType) \
&& (mKeyGeneration == other.mKeyGeneration) \
&& (mKaekIndex == other.mKaekIndex) \
&& (mContentSize == other.mContentSize) \
&& (mProgramId == other.mProgramId) \
&& (mContentIndex == other.mContentIndex) \
&& (mSdkAddonVersion == other.mSdkAddonVersion) \
&& (mRightsId == other.mRightsId) \
&& (mPartitionEntryList == other.mPartitionEntryList) \
&& (mKeyArea == other.mKeyArea);
}
bool nn::hac::ContentArchiveHeader::operator!=(const ContentArchiveHeader & other) const
{
return !(*this == other);
}
void nn::hac::ContentArchiveHeader::operator=(const ContentArchiveHeader & other)
{
mRawBinary = other.mRawBinary;
mDistributionType = other.mDistributionType;
mContentType = other.mContentType;
mKeyGeneration = other.mKeyGeneration;
mKaekIndex = other.mKaekIndex;
mContentSize = other.mContentSize;
mProgramId = other.mProgramId;
mContentIndex = other.mContentIndex;
mSdkAddonVersion = other.mSdkAddonVersion;
mRightsId = other.mRightsId;
mPartitionEntryList = other.mPartitionEntryList;
mKeyArea = other.mKeyArea;
}
void nn::hac::ContentArchiveHeader::toBytes()
{
mRawBinary.alloc(sizeof(sContentArchiveHeader));
sContentArchiveHeader* hdr = (sContentArchiveHeader*)mRawBinary.data();
// set header magic
switch(mFormatVersion)
{
case (nca::FORMAT_NCA2):
hdr->st_magic = nca::kNca2StructMagic;
break;
case (nca::FORMAT_NCA3):
hdr->st_magic = nca::kNca3StructMagic;
break;
default:
throw fnd::Exception(kModuleName, "Unsupported format version");
}
// set variables
hdr->distribution_type = mDistributionType;
hdr->content_type = mContentType;
if (mKeyGeneration > 2)
{
hdr->key_generation = 2;
hdr->key_generation_2 = mKeyGeneration;
}
else
{
hdr->key_generation = mKeyGeneration;
hdr->key_generation_2 = 0;
}
hdr->key_area_encryption_key_index = mKaekIndex;
hdr->content_size = mContentSize;
hdr->program_id = mProgramId;
hdr->content_index = mContentIndex;
hdr->sdk_addon_version = mSdkAddonVersion;
memcpy(hdr->rights_id, mRightsId.data(), nca::kRightsIdLen);
memcpy(hdr->key_area, mKeyArea.data(), nca::kKeyAreaSize);
for (size_t i = 0; i < mPartitionEntryList.size(); i++)
{
byte_t index = mPartitionEntryList[i].header_index;
if (index >= nca::kPartitionNum) continue;
hdr->partition_entry[index].start_blk = sizeToBlockNum(mPartitionEntryList[index].offset);
hdr->partition_entry[index].end_blk = (sizeToBlockNum(mPartitionEntryList[index].offset) + sizeToBlockNum(mPartitionEntryList[index].size));
hdr->partition_entry[index].enabled = true;
hdr->fs_header_hash[index] = mPartitionEntryList[i].fs_header_hash;
}
}
void nn::hac::ContentArchiveHeader::fromBytes(const byte_t * data, size_t len)
{
if (len < sizeof(sContentArchiveHeader))
{
throw fnd::Exception(kModuleName, "ContentArchive header size is too small");
}
clear();
mRawBinary.alloc(sizeof(sContentArchiveHeader));
memcpy(mRawBinary.data(), data, sizeof(sContentArchiveHeader));
sContentArchiveHeader* hdr = (sContentArchiveHeader*)mRawBinary.data();
// check magic
switch(hdr->st_magic.get())
{
case (nca::kNca2StructMagic) :
mFormatVersion = nca::FORMAT_NCA2;
break;
case (nca::kNca3StructMagic) :
mFormatVersion = nca::FORMAT_NCA3;
break;
throw fnd::Exception(kModuleName, "ContentArchive header corrupt (unrecognised header magic).");
}
// variables
mDistributionType = (nca::DistributionType)hdr->distribution_type;
mContentType = (nca::ContentType)hdr->content_type;
mKeyGeneration = _MAX(hdr->key_generation, hdr->key_generation_2);
mKaekIndex = hdr->key_area_encryption_key_index;
mContentSize = *hdr->content_size;
mProgramId = *hdr->program_id;
mContentIndex = *hdr->content_index;
mSdkAddonVersion = *hdr->sdk_addon_version;
memcpy(mRightsId.data(), hdr->rights_id, nca::kRightsIdLen);
memcpy(mKeyArea.data(), hdr->key_area, nca::kKeyAreaSize);
for (size_t i = 0; i < nca::kPartitionNum; i++)
{
if (hdr->partition_entry[i].enabled)
{
mPartitionEntryList.addElement({(byte_t)i, blockNumToSize(hdr->partition_entry[i].start_blk.get()), blockNumToSize(hdr->partition_entry[i].end_blk.get() - hdr->partition_entry[i].start_blk.get()), hdr->fs_header_hash[i] });
}
}
}
const fnd::Vec<byte_t>& nn::hac::ContentArchiveHeader::getBytes() const
{
return mRawBinary;
}
void nn::hac::ContentArchiveHeader::clear()
{
mFormatVersion = nca::FORMAT_NCA3;
mDistributionType = nca::DIST_DOWNLOAD;
mContentType = nca::TYPE_PROGRAM;
mKeyGeneration = 0;
mKaekIndex = 0;
mContentSize = 0;
mProgramId = 0;
mContentIndex = 0;
mSdkAddonVersion = 0;
memset(mRightsId.data(), 0, mRightsId.size());
mPartitionEntryList.clear();
memset(mKeyArea.data(), 0, mKeyArea.size());
}
byte_t nn::hac::ContentArchiveHeader::getFormatVersion() const
{
return mFormatVersion;
}
void nn::hac::ContentArchiveHeader::setFormatVersion(byte_t version)
{
mFormatVersion = version;
}
nn::hac::nca::DistributionType nn::hac::ContentArchiveHeader::getDistributionType() const
{
return mDistributionType;
}
void nn::hac::ContentArchiveHeader::setDistributionType(nca::DistributionType type)
{
mDistributionType = type;
}
nn::hac::nca::ContentType nn::hac::ContentArchiveHeader::getContentType() const
{
return mContentType;
}
void nn::hac::ContentArchiveHeader::setContentType(nca::ContentType type)
{
mContentType = type;
}
byte_t nn::hac::ContentArchiveHeader::getKeyGeneration() const
{
return mKeyGeneration;
}
void nn::hac::ContentArchiveHeader::setKeyGeneration(byte_t gen)
{
mKeyGeneration = gen;
}
byte_t nn::hac::ContentArchiveHeader::getKeyAreaEncryptionKeyIndex() const
{
return mKaekIndex;
}
void nn::hac::ContentArchiveHeader::setKeyAreaEncryptionKeyIndex(byte_t index)
{
mKaekIndex = index;
}
uint64_t nn::hac::ContentArchiveHeader::getContentSize() const
{
return mContentSize;
}
void nn::hac::ContentArchiveHeader::setContentSize(uint64_t size)
{
mContentSize = size;
}
uint64_t nn::hac::ContentArchiveHeader::getProgramId() const
{
return mProgramId;
}
void nn::hac::ContentArchiveHeader::setProgramId(uint64_t program_id)
{
mProgramId = program_id;
}
uint32_t nn::hac::ContentArchiveHeader::getContentIndex() const
{
return mContentIndex;
}
void nn::hac::ContentArchiveHeader::setContentIndex(uint32_t index)
{
mContentIndex = index;
}
uint32_t nn::hac::ContentArchiveHeader::getSdkAddonVersion() const
{
return mSdkAddonVersion;
}
void nn::hac::ContentArchiveHeader::setSdkAddonVersion(uint32_t version)
{
mSdkAddonVersion = version;
}
bool nn::hac::ContentArchiveHeader::hasRightsId() const
{
bool rightsIdIsSet = false;
for (size_t i = 0; i < nca::kRightsIdLen; i++)
{
if (mRightsId[i] != 0)
rightsIdIsSet = true;
}
return rightsIdIsSet;
}
const byte_t* nn::hac::ContentArchiveHeader::getRightsId() const
{
return mRightsId.data();
}
void nn::hac::ContentArchiveHeader::setRightsId(const byte_t* rights_id)
{
memcpy(mRightsId.data(), rights_id, nca::kRightsIdLen);
}
const fnd::List<nn::hac::ContentArchiveHeader::sPartitionEntry>& nn::hac::ContentArchiveHeader::getPartitionEntryList() const
{
return mPartitionEntryList;
}
void nn::hac::ContentArchiveHeader::setPartitionEntryList(const fnd::List<nn::hac::ContentArchiveHeader::sPartitionEntry>& partition_entry_list)
{
mPartitionEntryList = partition_entry_list;
// sanity check the list
if (mPartitionEntryList.size() >= nca::kPartitionNum)
{
throw fnd::Exception(kModuleName, "Too many partitions");
}
for (size_t i = 0; i < mPartitionEntryList.size(); i++)
{
if (mPartitionEntryList[i].header_index >= nca::kPartitionNum)
{
throw fnd::Exception(kModuleName, "Illegal partition index");
}
for (size_t j = i+1; j < mPartitionEntryList.size(); j++)
{
if (mPartitionEntryList[i].header_index == mPartitionEntryList[j].header_index)
{
throw fnd::Exception(kModuleName, "Duplicated partition index");
}
}
}
}
const byte_t* nn::hac::ContentArchiveHeader::getKeyArea() const
{
return mKeyArea.data();
}
void nn::hac::ContentArchiveHeader::setKeyArea(const byte_t* key_area)
{
memcpy(mKeyArea.data(), key_area, nca::kKeyAreaSize);
}
uint64_t nn::hac::ContentArchiveHeader::blockNumToSize(uint32_t block_num) const
{
return block_num * nca::kSectorSize;
}
uint32_t nn::hac::ContentArchiveHeader::sizeToBlockNum(uint64_t real_size) const
{
return (uint32_t)(align(real_size, nca::kSectorSize) / nca::kSectorSize);
}

View file

@ -1,327 +0,0 @@
#include <nn/hac/NcaHeader.h>
nn::hac::NcaHeader::NcaHeader()
{
clear();
}
nn::hac::NcaHeader::NcaHeader(const NcaHeader & other)
{
*this = other;
}
bool nn::hac::NcaHeader::operator==(const NcaHeader & other) const
{
return (mDistributionType == other.mDistributionType) \
&& (mContentType == other.mContentType) \
&& (mKeyGeneration == other.mKeyGeneration) \
&& (mKaekIndex == other.mKaekIndex) \
&& (mContentSize == other.mContentSize) \
&& (mProgramId == other.mProgramId) \
&& (mContentIndex == other.mContentIndex) \
&& (mSdkAddonVersion == other.mSdkAddonVersion) \
&& (mPartitions == other.mPartitions) \
&& (mEncAesKeys == other.mEncAesKeys);
}
bool nn::hac::NcaHeader::operator!=(const NcaHeader & other) const
{
return !(*this == other);
}
void nn::hac::NcaHeader::operator=(const NcaHeader & other)
{
if (other.getBytes().size())
{
fromBytes(other.getBytes().data(), other.getBytes().size());
}
else
{
mRawBinary.clear();
mDistributionType = other.mDistributionType;
mContentType = other.mContentType;
mKeyGeneration = other.mKeyGeneration;
mKaekIndex = other.mKaekIndex;
mContentSize = other.mContentSize;
mProgramId = other.mProgramId;
mContentIndex = other.mContentIndex;
mSdkAddonVersion = other.mSdkAddonVersion;
mPartitions = other.mPartitions;
mEncAesKeys = other.mEncAesKeys;
}
}
void nn::hac::NcaHeader::toBytes()
{
mRawBinary.alloc(sizeof(sNcaHeader));
sNcaHeader* hdr = (sNcaHeader*)mRawBinary.data();
switch(mFormatVersion)
{
case (NCA2_FORMAT):
hdr->st_magic = nca::kNca2StructMagic;
break;
case (NCA3_FORMAT):
hdr->st_magic = nca::kNca3StructMagic;
break;
default:
throw fnd::Exception(kModuleName, "Unsupported format version");
}
hdr->distribution_type = mDistributionType;
hdr->content_type = mContentType;
if (mKeyGeneration > 2)
{
hdr->key_generation = 2;
hdr->key_generation_2 = mKeyGeneration;
}
else
{
hdr->key_generation = mKeyGeneration;
hdr->key_generation_2 = 0;
}
hdr->key_area_encryption_key_index = mKaekIndex;
hdr->content_size = mContentSize;
hdr->program_id = mProgramId;
hdr->content_index = mContentIndex;
hdr->sdk_addon_version = mSdkAddonVersion;
memcpy(hdr->rights_id, mRightsId, nca::kRightsIdLen);
// TODO: properly reconstruct NCA layout? atm in hands of user
for (size_t i = 0; i < mPartitions.size(); i++)
{
// determine partition index
byte_t idx = mPartitions[i].index;
if (mPartitions[i].index >= nca::kPartitionNum || hdr->partition[idx].enabled) continue;
hdr->partition[idx].start = sizeToBlockNum(mPartitions[i].offset);
hdr->partition[idx].end = (sizeToBlockNum(mPartitions[i].offset) + sizeToBlockNum(mPartitions[i].size));
hdr->partition[idx].enabled = true;
hdr->partition_hash[idx] = mPartitions[i].hash;
}
for (size_t i = 0; i < nca::kAesKeyNum; i++)
{
hdr->enc_aes_key[i] = mEncAesKeys[i];
}
}
void nn::hac::NcaHeader::fromBytes(const byte_t * data, size_t len)
{
if (len < sizeof(sNcaHeader))
{
throw fnd::Exception(kModuleName, "NCA header size is too small");
}
clear();
mRawBinary.alloc(sizeof(sNcaHeader));
memcpy(mRawBinary.data(), data, sizeof(sNcaHeader));
sNcaHeader* hdr = (sNcaHeader*)mRawBinary.data();
switch(hdr->st_magic.get())
{
case (nca::kNca2StructMagic) :
mFormatVersion = NCA2_FORMAT;
break;
case (nca::kNca3StructMagic) :
mFormatVersion = NCA3_FORMAT;
break;
throw fnd::Exception(kModuleName, "NCA header corrupt");
}
mDistributionType = (nca::DistributionType)hdr->distribution_type;
mContentType = (nca::ContentType)hdr->content_type;
mKeyGeneration = _MAX(hdr->key_generation, hdr->key_generation_2);
mKaekIndex = hdr->key_area_encryption_key_index;
mContentSize = *hdr->content_size;
mProgramId = *hdr->program_id;
mContentIndex = *hdr->content_index;
mSdkAddonVersion = *hdr->sdk_addon_version;
memcpy(mRightsId, hdr->rights_id, nca::kRightsIdLen);
for (size_t i = 0; i < nca::kPartitionNum; i++)
{
// skip sections that don't exist
if (hdr->partition[i].enabled == 0) continue;
// add high level struct
mPartitions.addElement({(byte_t)i, blockNumToSize(hdr->partition[i].start.get()), blockNumToSize(hdr->partition[i].end.get() - hdr->partition[i].start.get()), hdr->partition_hash[i] });
}
for (size_t i = 0; i < nca::kAesKeyNum; i++)
{
mEncAesKeys.addElement(hdr->enc_aes_key[i]);
}
}
const fnd::Vec<byte_t>& nn::hac::NcaHeader::getBytes() const
{
return mRawBinary;
}
void nn::hac::NcaHeader::clear()
{
mFormatVersion = NCA3_FORMAT;
mDistributionType = nca::DIST_DOWNLOAD;
mContentType = nca::TYPE_PROGRAM;
mKeyGeneration = 0;
mKaekIndex = 0;
mContentSize = 0;
mProgramId = 0;
mContentIndex = 0;
mSdkAddonVersion = 0;
mPartitions.clear();
mEncAesKeys.clear();
}
nn::hac::NcaHeader::FormatVersion nn::hac::NcaHeader::getFormatVersion() const
{
return mFormatVersion;
}
void nn::hac::NcaHeader::setFormatVersion(FormatVersion version)
{
mFormatVersion = version;
}
nn::hac::nca::DistributionType nn::hac::NcaHeader::getDistributionType() const
{
return mDistributionType;
}
void nn::hac::NcaHeader::setDistributionType(nca::DistributionType type)
{
mDistributionType = type;
}
nn::hac::nca::ContentType nn::hac::NcaHeader::getContentType() const
{
return mContentType;
}
void nn::hac::NcaHeader::setContentType(nca::ContentType type)
{
mContentType = type;
}
byte_t nn::hac::NcaHeader::getKeyGeneration() const
{
return mKeyGeneration;
}
void nn::hac::NcaHeader::setKeyGeneration(byte_t gen)
{
mKeyGeneration = gen;
}
byte_t nn::hac::NcaHeader::getKaekIndex() const
{
return mKaekIndex;
}
void nn::hac::NcaHeader::setKaekIndex(byte_t index)
{
mKaekIndex = index;
}
uint64_t nn::hac::NcaHeader::getContentSize() const
{
return mContentSize;
}
void nn::hac::NcaHeader::setContentSize(uint64_t size)
{
mContentSize = size;
}
uint64_t nn::hac::NcaHeader::getProgramId() const
{
return mProgramId;
}
void nn::hac::NcaHeader::setProgramId(uint64_t program_id)
{
mProgramId = program_id;
}
uint32_t nn::hac::NcaHeader::getContentIndex() const
{
return mContentIndex;
}
void nn::hac::NcaHeader::setContentIndex(uint32_t index)
{
mContentIndex = index;
}
uint32_t nn::hac::NcaHeader::getSdkAddonVersion() const
{
return mSdkAddonVersion;
}
void nn::hac::NcaHeader::setSdkAddonVersion(uint32_t version)
{
mSdkAddonVersion = version;
}
bool nn::hac::NcaHeader::hasRightsId() const
{
bool rightsIdIsSet = false;
for (size_t i = 0; i < nca::kRightsIdLen; i++)
{
if (mRightsId[i] != 0)
rightsIdIsSet = true;
}
return rightsIdIsSet;
}
const byte_t* nn::hac::NcaHeader::getRightsId() const
{
return mRightsId;
}
void nn::hac::NcaHeader::setRightsId(const byte_t* rights_id)
{
memcpy(mRightsId, rights_id, nca::kRightsIdLen);
}
const fnd::List<nn::hac::NcaHeader::sPartition>& nn::hac::NcaHeader::getPartitions() const
{
return mPartitions;
}
void nn::hac::NcaHeader::setPartitions(const fnd::List<nn::hac::NcaHeader::sPartition>& partitions)
{
mPartitions = partitions;
if (mPartitions.size() >= nca::kPartitionNum)
{
throw fnd::Exception(kModuleName, "Too many NCA partitions");
}
}
const fnd::List<fnd::aes::sAes128Key>& nn::hac::NcaHeader::getEncAesKeys() const
{
return mEncAesKeys;
}
void nn::hac::NcaHeader::setEncAesKeys(const fnd::List<fnd::aes::sAes128Key>& keys)
{
mEncAesKeys = keys;
}
uint64_t nn::hac::NcaHeader::blockNumToSize(uint32_t block_num) const
{
return block_num*nca::kSectorSize;
}
uint32_t nn::hac::NcaHeader::sizeToBlockNum(uint64_t real_size) const
{
return (uint32_t)(align(real_size, nca::kSectorSize) / nca::kSectorSize);
}

View file

@ -1,6 +1,6 @@
#include <nn/hac/NcaUtils.h>
void nn::hac::NcaUtils::decryptNcaHeader(const byte_t* src, byte_t* dst, const fnd::aes::sAesXts128Key& key)
void nn::hac::NcaUtils::decryptContentArchiveHeader(const byte_t* src, byte_t* dst, const fnd::aes::sAesXts128Key& key)
{
byte_t tweak[fnd::aes::kAesBlockSize];
@ -9,7 +9,7 @@ void nn::hac::NcaUtils::decryptNcaHeader(const byte_t* src, byte_t* dst, const f
fnd::aes::AesXtsMakeTweak(tweak, 1);
fnd::aes::AesXtsDecryptSector(src + sectorToOffset(1), nn::hac::nca::kSectorSize, key.key[0], key.key[1], tweak, raw_hdr);
bool useNca2SectorIndex = ((nn::hac::sNcaHeader*)(raw_hdr))->st_magic.get() == nn::hac::nca::kNca2StructMagic;
bool useNca2SectorIndex = ((nn::hac::sContentArchiveHeader*)(raw_hdr))->st_magic.get() == nn::hac::nca::kNca2StructMagic;
// decrypt whole header
for (size_t i = 0; i < nn::hac::nca::kHeaderSectorNum; i++)

View file

@ -19,10 +19,10 @@ void KeyConfiguration::operator=(const KeyConfiguration& other)
{
mAcidSignKey = other.mAcidSignKey;
mPkg2SignKey = other.mPkg2SignKey;
mNcaHeader0SignKey = other.mNcaHeader0SignKey;
mContentArchiveHeader0SignKey = other.mContentArchiveHeader0SignKey;
mXciHeaderSignKey = other.mXciHeaderSignKey;
mNcaHeaderKey = other.mNcaHeaderKey;
mContentArchiveHeaderKey = other.mContentArchiveHeaderKey;
mXciHeaderKey = other.mXciHeaderKey;
for (size_t i = 0; i < kMasterKeyNum; i++)
@ -114,14 +114,14 @@ void KeyConfiguration::importHactoolGenericKeyfile(const std::string& path)
}
// store nca header key
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[nameidx], kKeyStr), mNcaHeaderKey.key[0], 0x20);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kContentArchiveHeaderBase[nameidx], kKeyStr), mContentArchiveHeaderKey.key[0], 0x20);
// store xci header key
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kKeyStr), mXciHeaderKey.key, 0x10);
// store rsa keys
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[nameidx], kRsaKeyPrivate), mNcaHeader0SignKey.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[nameidx], kRsaKeyModulus), mNcaHeader0SignKey.modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kContentArchiveHeaderBase[nameidx], kRsaKeyPrivate), mContentArchiveHeader0SignKey.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kContentArchiveHeaderBase[nameidx], kRsaKeyModulus), mContentArchiveHeader0SignKey.modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kRsaKeyPrivate), mXciHeaderSignKey.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kRsaKeyModulus), mXciHeaderSignKey.modulus, fnd::rsa::kRsa2048Size);
@ -149,12 +149,12 @@ void KeyConfiguration::importHactoolGenericKeyfile(const std::string& path)
{
if (i == 0 && nca_header_kek_source != kNullAesKey && nca_header_key_source != kNullAesXtsKey)
{
if (mNcaHeaderKey == kNullAesXtsKey)
if (mContentArchiveHeaderKey == kNullAesXtsKey)
{
fnd::aes::sAes128Key nca_header_kek;
nn::hac::AesKeygen::generateKey(nca_header_kek.key, aes_kek_generation_source.key, nca_header_kek_source.key, aes_key_generation_source.key, master_key[i].key);
nn::hac::AesKeygen::generateKey(mNcaHeaderKey.key[0], nca_header_key_source.key[0], nca_header_kek.key);
nn::hac::AesKeygen::generateKey(mNcaHeaderKey.key[1], nca_header_key_source.key[1], nca_header_kek.key);
nn::hac::AesKeygen::generateKey(mContentArchiveHeaderKey.key[0], nca_header_key_source.key[0], nca_header_kek.key);
nn::hac::AesKeygen::generateKey(mContentArchiveHeaderKey.key[1], nca_header_key_source.key[1], nca_header_kek.key);
}
}
@ -196,11 +196,11 @@ void KeyConfiguration::clearGeneralKeyConfiguration()
{
mAcidSignKey = kNullRsa2048Key;
mPkg2SignKey = kNullRsa2048Key;
mNcaHeader0SignKey = kNullRsa2048Key;
mContentArchiveHeader0SignKey = kNullRsa2048Key;
mXciHeaderSignKey = kNullRsa2048Key;
mPkiRootKeyList.clear();
mNcaHeaderKey = kNullAesXtsKey;
mContentArchiveHeaderKey = kNullAesXtsKey;
mXciHeaderKey = kNullAesKey;
for (size_t i = 0; i < kMasterKeyNum; i++)
@ -221,14 +221,14 @@ void KeyConfiguration::clearNcaExternalKeys()
mNcaExternalContentKeyList.clear();
}
bool KeyConfiguration::getNcaHeaderKey(fnd::aes::sAesXts128Key& key) const
bool KeyConfiguration::getContentArchiveHeaderKey(fnd::aes::sAesXts128Key& key) const
{
return copyOutKeyResourceIfExists(mNcaHeaderKey, key, kNullAesXtsKey);
return copyOutKeyResourceIfExists(mContentArchiveHeaderKey, key, kNullAesXtsKey);
}
bool KeyConfiguration::getNcaHeader0SignKey(fnd::rsa::sRsa2048Key& key) const
bool KeyConfiguration::getContentArchiveHeader0SignKey(fnd::rsa::sRsa2048Key& key) const
{
return copyOutKeyResourceIfExists(mNcaHeader0SignKey, key, kNullRsa2048Key);
return copyOutKeyResourceIfExists(mContentArchiveHeader0SignKey, key, kNullRsa2048Key);
}
bool KeyConfiguration::getAcidSignKey(fnd::rsa::sRsa2048Key& key) const

View file

@ -24,8 +24,8 @@ public:
void clearNcaExternalKeys();
// nca keys
bool getNcaHeaderKey(fnd::aes::sAesXts128Key& key) const;
bool getNcaHeader0SignKey(fnd::rsa::sRsa2048Key& key) const;
bool getContentArchiveHeaderKey(fnd::aes::sAesXts128Key& key) const;
bool getContentArchiveHeader0SignKey(fnd::rsa::sRsa2048Key& key) const;
bool getAcidSignKey(fnd::rsa::sRsa2048Key& key) const;
bool getNcaKeyAreaEncryptionKey(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const;
bool getNcaKeyAreaEncryptionKeyHw(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const;
@ -71,7 +71,7 @@ private:
const std::string kPkg1Base[kNameVariantNum] = { "package1", "package1", "package1" };
const std::string kPkg2Base[kNameVariantNum] = { "package2", "package2", "package2" };
const std::string kXciHeaderBase[kNameVariantNum] = { "xci_header", "xci_header", "xci_header" };
const std::string kNcaHeaderBase[kNameVariantNum] = { "nca_header", "header", "nca_header" };
const std::string kContentArchiveHeaderBase[kNameVariantNum] = { "nca_header", "header", "nca_header" };
const std::string kAcidBase[kNameVariantNum] = { "acid", "acid", "acid" };
const std::string kPkiRootBase[kNameVariantNum] = { "pki_root", "pki_root", "pki_root" };
const std::string kTicketCommonKeyBase[kNameVariantNum] = { "ticket_commonkey", "titlekek", "ticket_commonkey" };
@ -175,8 +175,8 @@ private:
fnd::aes::sAes128Key mPkg2Key[kMasterKeyNum];
// nca
fnd::rsa::sRsa2048Key mNcaHeader0SignKey;
fnd::aes::sAesXts128Key mNcaHeaderKey;
fnd::rsa::sRsa2048Key mContentArchiveHeader0SignKey;
fnd::aes::sAesXts128Key mContentArchiveHeaderKey;
fnd::aes::sAes128Key mNcaKeyAreaEncryptionKey[kNcaKeakNum][kMasterKeyNum];
fnd::aes::sAes128Key mNcaKeyAreaEncryptionKeyHw[kNcaKeakNum][kMasterKeyNum];

View file

@ -107,18 +107,18 @@ void NcaProcess::importHeader()
}
// read header block
(*mFile)->read((byte_t*)&mHdrBlock, 0, sizeof(nn::hac::sNcaHeaderBlock));
(*mFile)->read((byte_t*)&mHdrBlock, 0, sizeof(nn::hac::sContentArchiveHeaderBlock));
// decrypt header block
fnd::aes::sAesXts128Key header_key;
mKeyCfg.getNcaHeaderKey(header_key);
nn::hac::NcaUtils::decryptNcaHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, header_key);
mKeyCfg.getContentArchiveHeaderKey(header_key);
nn::hac::NcaUtils::decryptContentArchiveHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, header_key);
// generate header hash
fnd::sha::Sha256((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sNcaHeader), mHdrHash.bytes);
fnd::sha::Sha256((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sContentArchiveHeader), mHdrHash.bytes);
// proccess main header
mHdr.fromBytes((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sNcaHeader));
mHdr.fromBytes((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sContentArchiveHeader));
}
void NcaProcess::generateNcaBodyEncryptionKeys()
@ -129,25 +129,27 @@ void NcaProcess::generateNcaBodyEncryptionKeys()
// get key data from header
byte_t masterkey_rev = nn::hac::NcaUtils::getMasterKeyRevisionFromKeyGeneration(mHdr.getKeyGeneration());
byte_t keak_index = mHdr.getKaekIndex();
byte_t keak_index = mHdr.getKeyAreaEncryptionKeyIndex();
// process key area
sKeys::sKeyAreaKey kak;
fnd::aes::sAes128Key key_area_enc_key;
for (size_t i = 0; i < nn::hac::nca::kAesKeyNum; i++)
const fnd::aes::sAes128Key* key_area = (const fnd::aes::sAes128Key*) mHdr.getKeyArea();
for (size_t i = 0; i < nn::hac::nca::kKeyAreaKeyNum; i++)
{
if (mHdr.getEncAesKeys()[i] != zero_aesctr_key)
if (key_area[i] != zero_aesctr_key)
{
kak.index = (byte_t)i;
kak.enc = mHdr.getEncAesKeys()[i];
kak.enc = key_area[i];
// key[0-3]
if (i < 4 && mKeyCfg.getNcaKeyAreaEncryptionKey(masterkey_rev, keak_index, key_area_enc_key) == true)
{
kak.decrypted = true;
nn::hac::AesKeygen::generateKey(kak.dec.key, kak.enc.key, key_area_enc_key.key);
}
// key[4]
else if (i == 4 && mKeyCfg.getNcaKeyAreaEncryptionKeyHw(masterkey_rev, keak_index, key_area_enc_key) == true)
// key[KEY_AESCTR_HW]
else if (i == nn::hac::nca::KEY_AESCTR_HW && mKeyCfg.getNcaKeyAreaEncryptionKeyHw(masterkey_rev, keak_index, key_area_enc_key) == true)
{
kak.decrypted = true;
nn::hac::AesKeygen::generateKey(kak.dec.key, kak.enc.key, key_area_enc_key.key);
@ -223,22 +225,22 @@ void NcaProcess::generatePartitionConfiguration()
{
std::stringstream error;
for (size_t i = 0; i < mHdr.getPartitions().size(); i++)
for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++)
{
// get reference to relevant structures
const nn::hac::NcaHeader::sPartition& partition = mHdr.getPartitions()[i];
nn::hac::sNcaFsHeader& fs_header = mHdrBlock.fs_header[partition.index];
const nn::hac::ContentArchiveHeader::sPartitionEntry& partition = mHdr.getPartitionEntryList()[i];
nn::hac::sNcaFsHeader& fs_header = mHdrBlock.fs_header[partition.header_index];
// output structure
sPartitionInfo& info = mPartitions[partition.index];
sPartitionInfo& info = mPartitions[partition.header_index];
// validate header hash
fnd::sha::sSha256Hash calc_hash;
fnd::sha::Sha256((const byte_t*)&mHdrBlock.fs_header[partition.index], sizeof(nn::hac::sNcaFsHeader), calc_hash.bytes);
if (calc_hash.compare(partition.hash) == false)
fnd::sha::sSha256Hash fs_header_hash;
fnd::sha::Sha256((const byte_t*)&mHdrBlock.fs_header[partition.header_index], sizeof(nn::hac::sNcaFsHeader), fs_header_hash.bytes);
if (fs_header_hash.compare(partition.fs_header_hash) == false)
{
error.clear();
error << "NCA FS Header [" << partition.index << "] Hash: FAIL \n";
error << "NCA FS Header [" << partition.header_index << "] Hash: FAIL \n";
throw fnd::Exception(kModuleName, error.str());
}
@ -246,7 +248,7 @@ void NcaProcess::generatePartitionConfiguration()
if (fs_header.version.get() != nn::hac::nca::kDefaultFsHeaderVersion)
{
error.clear();
error << "NCA FS Header [" << partition.index << "] Version(" << fs_header.version.get() << "): UNSUPPORTED";
error << "NCA FS Header [" << partition.header_index << "] Version(" << fs_header.version.get() << "): UNSUPPORTED";
throw fnd::Exception(kModuleName, error.str());
}
@ -387,7 +389,7 @@ void NcaProcess::validateNcaSignatures()
{
// validate signature[0]
fnd::rsa::sRsa2048Key sign0_key;
mKeyCfg.getNcaHeader0SignKey(sign0_key);
mKeyCfg.getContentArchiveHeader0SignKey(sign0_key);
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;
@ -415,7 +417,7 @@ void NcaProcess::validateNcaSignatures()
npdm.setCliOutputMode(0);
npdm.process();
if (fnd::rsa::pss::rsaVerify(npdm.getMeta().getAcid().getNcaHeaderSignature2Key(), fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrBlock.signature_acid) != 0)
if (fnd::rsa::pss::rsaVerify(npdm.getMeta().getAcid().getContentArchiveHeaderSignature2Key(), fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrBlock.signature_acid) != 0)
{
std::cout << "[WARNING] NCA Header ACID Signature: FAIL" << std::endl;
}
@ -445,7 +447,7 @@ void NcaProcess::displayHeader()
std::cout << " Dist. Type: " << getDistributionTypeStr(mHdr.getDistributionType()) << std::endl;
std::cout << " Content Type: " << getContentTypeStr(mHdr.getContentType()) << std::endl;
std::cout << " Key Generation: " << std::dec << (uint32_t)mHdr.getKeyGeneration() << std::endl;
std::cout << " Kaek Index: " << getKaekIndexStr((nn::hac::nca::KeyAreaEncryptionKeyIndex)mHdr.getKaekIndex()) << " (" << std::dec << (uint32_t)mHdr.getKaekIndex() << ")" << std::endl;
std::cout << " Kaek Index: " << getKaekIndexStr((nn::hac::nca::KeyAreaEncryptionKeyIndex)mHdr.getKeyAreaEncryptionKeyIndex()) << " (" << std::dec << (uint32_t)mHdr.getKeyAreaEncryptionKeyIndex() << ")" << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getContentSize() << std::endl;
std::cout << " ProgID: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getProgramId() << std::endl;
std::cout << " Content Index: " << std::dec << mHdr.getContentIndex() << std::endl;
@ -483,10 +485,11 @@ void NcaProcess::displayHeader()
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
{
std::cout << " Partitions:" << std::endl;
for (size_t i = 0; i < mHdr.getPartitions().size(); i++)
for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++)
{
size_t index = mHdr.getPartitions()[i].index;
uint32_t index = mHdr.getPartitionEntryList()[i].header_index;
sPartitionInfo& info = mPartitions[index];
if (info.size == 0) continue;
std::cout << " " << std::dec << index << ":" << std::endl;
std::cout << " Offset: 0x" << std::hex << (uint64_t)info.offset << std::endl;
@ -546,9 +549,9 @@ void NcaProcess::displayHeader()
void NcaProcess::processPartitions()
{
for (size_t i = 0; i < mHdr.getPartitions().size(); i++)
for (size_t i = 0; i < mHdr.getPartitionEntryList().size(); i++)
{
size_t index = mHdr.getPartitions()[i].index;
uint32_t index = mHdr.getPartitionEntryList()[i].header_index;
struct sPartitionInfo& partition = mPartitions[index];
// if the reader is null, skip
@ -604,16 +607,16 @@ void NcaProcess::processPartitions()
}
}
const char* NcaProcess::getFormatVersionStr(nn::hac::NcaHeader::FormatVersion format_ver) const
const char* NcaProcess::getFormatVersionStr(byte_t format_ver) const
{
const char* str = nullptr;
switch (format_ver)
{
case (nn::hac::NcaHeader::NCA2_FORMAT):
case (nn::hac::nca::FORMAT_NCA2):
str = "NCA2";
break;
case (nn::hac::NcaHeader::NCA3_FORMAT):
case (nn::hac::nca::FORMAT_NCA3):
str = "NCA3";
break;
default:
@ -797,7 +800,7 @@ const char* NcaProcess::getContentTypeForMountStr(nn::hac::nca::ContentType cont
str = "data";
break;
case (nn::hac::nca::TYPE_PUBLIC_DATA):
str = "publicData";
str = "publicdata";
break;
default:
str = "";

View file

@ -4,7 +4,7 @@
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/LayeredIntegrityMetadata.h>
#include <nn/hac/NcaHeader.h>
#include <nn/hac/ContentArchiveHeader.h>
#include "KeyConfiguration.h"
@ -49,9 +49,9 @@ private:
bool mListFs;
// data
nn::hac::sNcaHeaderBlock mHdrBlock;
nn::hac::sContentArchiveHeaderBlock mHdrBlock;
fnd::sha::sSha256Hash mHdrHash;
nn::hac::NcaHeader mHdr;
nn::hac::ContentArchiveHeader mHdr;
// crypto
struct sKeys
@ -111,7 +111,7 @@ private:
void displayHeader();
void processPartitions();
const char* getFormatVersionStr(nn::hac::NcaHeader::FormatVersion format_ver) const;
const char* getFormatVersionStr(byte_t format_ver) const;
const char* getDistributionTypeStr(nn::hac::nca::DistributionType dist_type) const;
const char* getContentTypeStr(nn::hac::nca::ContentType cont_type) const;
const char* getEncryptionTypeStr(nn::hac::nca::EncryptionType enc_type) const;

View file

@ -4,6 +4,8 @@
#include <fnd/io.h>
#include "PfsProcess.h"
#include <fnd/SimpleTextOutput.h>
PfsProcess::PfsProcess() :
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
@ -80,6 +82,8 @@ void PfsProcess::importHeader()
// open minimum header to get full header size
scratch.alloc(sizeof(nn::hac::sPfsHeader));
(*mFile)->read(scratch.data(), 0, scratch.size());
std::cout << "raw pfs header: " << std::endl;
fnd::SimpleTextOutput::hxdStyleDump(scratch.data(), scratch.size());
if (validateHeaderMagic(((nn::hac::sPfsHeader*)scratch.data())) == false)
{
throw fnd::Exception(kModuleName, "Corrupt Header");

View file

@ -701,14 +701,14 @@ bool UserSettings::determineValidNcaFromSample(const fnd::Vec<byte_t>& sample) c
{
// prepare decrypted NCA data
byte_t nca_raw[nn::hac::nca::kHeaderSize];
nn::hac::sNcaHeader* nca_header = (nn::hac::sNcaHeader*)(nca_raw + nn::hac::NcaUtils::sectorToOffset(1));
nn::hac::sContentArchiveHeader* nca_header = (nn::hac::sContentArchiveHeader*)(nca_raw + nn::hac::NcaUtils::sectorToOffset(1));
if (sample.size() < nn::hac::nca::kHeaderSize)
return false;
fnd::aes::sAesXts128Key header_key;
mKeyCfg.getNcaHeaderKey(header_key);
nn::hac::NcaUtils::decryptNcaHeader(sample.data(), nca_raw, header_key);
mKeyCfg.getContentArchiveHeaderKey(header_key);
nn::hac::NcaUtils::decryptContentArchiveHeader(sample.data(), nca_raw, header_key);
if (nca_header->st_magic.get() != nn::hac::nca::kNca2StructMagic && nca_header->st_magic.get() != nn::hac::nca::kNca3StructMagic)
return false;
@ -882,9 +882,9 @@ void UserSettings::dumpKeyConfig() const
std::cout << "[KeyConfiguration]" << std::endl;
std::cout << " NCA Keys:" << std::endl;
if (mKeyCfg.getNcaHeader0SignKey(rsa2048_key) == true)
if (mKeyCfg.getContentArchiveHeader0SignKey(rsa2048_key) == true)
dumpRsa2048Key(rsa2048_key, "Header Signature[0] Key", 2);
if (mKeyCfg.getNcaHeaderKey(aesxts_key) == true)
if (mKeyCfg.getContentArchiveHeaderKey(aesxts_key) == true)
dumpAesXtsKey(aesxts_key, "Header Encryption Key", 2);
for (size_t i = 0; i < kMasterKeyNum; i++)