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

View file

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

View file

@ -9,7 +9,7 @@ namespace hac
{ {
public: public:
static inline size_t sectorToOffset(size_t sector_index) { return sector_index * nn::hac::nca::kSectorSize; } 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 byte_t getMasterKeyRevisionFromKeyGeneration(byte_t key_generation);
static void getNcaPartitionAesCtr(const nn::hac::sNcaFsHeader* hdr, byte_t* ctr); 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 kPartitionNum = 4;
static const size_t kHeaderSectorNum = 6; static const size_t kHeaderSectorNum = 6;
static const size_t kHeaderSize = kSectorSize * kHeaderSectorNum; static const size_t kHeaderSize = kSectorSize * kHeaderSectorNum;
static const size_t kAesKeyNum = 16;
static const size_t kRightsIdLen = 0x10; 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 kKeyAreaEncryptionKeyNum = 3;
static const size_t kFsHeaderHashSuperblockLen = 0x138; static const size_t kFsHeaderHashSuperblockLen = 0x138;
static const uint16_t kDefaultFsHeaderVersion = 2; static const uint16_t kDefaultFsHeaderVersion = 2;
enum ProgramPartitionId enum HeaderFormatVersion
{ {
PARTITION_CODE, FORMAT_NCA2 = 2,
PARTITION_DATA, FORMAT_NCA3 = 3
PARTITION_LOGO, };
enum ProgramContentPartitionIndex
{
PARTITION_CODE = 0,
PARTITION_DATA = 1,
PARTITION_LOGO = 2,
}; };
enum DistributionType enum DistributionType
@ -83,11 +90,11 @@ namespace hac
CRYPT_AESXTS, CRYPT_AESXTS,
CRYPT_AESCTR, CRYPT_AESCTR,
CRYPT_AESCTREX CRYPT_AESCTREX
}; };
} }
#pragma pack(push,1) #pragma pack(push,1)
struct sNcaHeader struct sContentArchiveHeader
{ {
le_uint32_t st_magic; le_uint32_t st_magic;
byte_t distribution_type; byte_t distribution_type;
@ -101,15 +108,15 @@ namespace hac
byte_t key_generation_2; byte_t key_generation_2;
byte_t reserved_2[0xf]; byte_t reserved_2[0xf];
byte_t rights_id[nca::kRightsIdLen]; byte_t rights_id[nca::kRightsIdLen];
struct sNcaSection struct sPartitionEntry
{ {
le_uint32_t start; // block units le_uint32_t start_blk; // block units
le_uint32_t end; // block units le_uint32_t end_blk; // block units
byte_t enabled; byte_t enabled;
byte_t reserved[7]; byte_t reserved[7];
} partition[nca::kPartitionNum]; } partition_entry[nca::kPartitionNum];
fnd::sha::sSha256Hash partition_hash[nca::kPartitionNum]; fnd::sha::sSha256Hash fs_header_hash[nca::kPartitionNum];
fnd::aes::sAes128Key enc_aes_key[nca::kAesKeyNum]; byte_t key_area[nca::kKeyAreaSize];
}; };
struct sNcaFsHeader struct sNcaFsHeader
@ -124,11 +131,11 @@ namespace hac
byte_t reserved_1[0xB8]; byte_t reserved_1[0xB8];
}; };
struct sNcaHeaderBlock struct sContentArchiveHeaderBlock
{ {
byte_t signature_main[fnd::rsa::kRsa2048Size]; byte_t signature_main[fnd::rsa::kRsa2048Size];
byte_t signature_acid[fnd::rsa::kRsa2048Size]; byte_t signature_acid[fnd::rsa::kRsa2048Size];
sNcaHeader header; sContentArchiveHeader header;
sNcaFsHeader fs_header[nn::hac::nca::kPartitionNum]; 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\MiscParamsHandler.h" />
<ClInclude Include="include\nn\hac\nacp.h" /> <ClInclude Include="include\nn\hac\nacp.h" />
<ClInclude Include="include\nn\hac\nca.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\NcaUtils.h" />
<ClInclude Include="include\nn\hac\nro.h" /> <ClInclude Include="include\nn\hac\nro.h" />
<ClInclude Include="include\nn\hac\NroHeader.h" /> <ClInclude Include="include\nn\hac\NroHeader.h" />
@ -117,7 +117,7 @@
<ClCompile Include="source\MiscFlagsHandler.cpp" /> <ClCompile Include="source\MiscFlagsHandler.cpp" />
<ClCompile Include="source\MiscParamsEntry.cpp" /> <ClCompile Include="source\MiscParamsEntry.cpp" />
<ClCompile Include="source\MiscParamsHandler.cpp" /> <ClCompile Include="source\MiscParamsHandler.cpp" />
<ClCompile Include="source\NcaHeader.cpp" /> <ClCompile Include="source\ContentArchiveHeader.cpp" />
<ClCompile Include="source\NcaUtils.cpp" /> <ClCompile Include="source\NcaUtils.cpp" />
<ClCompile Include="source\NroHeader.cpp" /> <ClCompile Include="source\NroHeader.cpp" />
<ClCompile Include="source\NsoHeader.cpp" /> <ClCompile Include="source\NsoHeader.cpp" />

View file

@ -144,7 +144,7 @@
<ClInclude Include="include\nn\hac\nca.h"> <ClInclude Include="include\nn\hac\nca.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\nn\hac\NcaHeader.h"> <ClInclude Include="include\nn\hac\ContentArchiveHeader.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\nn\hac\NcaUtils.h"> <ClInclude Include="include\nn\hac\NcaUtils.h">
@ -293,7 +293,7 @@
<ClCompile Include="source\MiscParamsHandler.cpp"> <ClCompile Include="source\MiscParamsHandler.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\NcaHeader.cpp"> <ClCompile Include="source\ContentArchiveHeader.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\NcaUtils.cpp"> <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) void nn::hac::AccessControlInfoDesc::operator=(const AccessControlInfoDesc & other)
{ {
mRawBinary = other.mRawBinary; mRawBinary = other.mRawBinary;
mNcaHeaderSignature2Key = other.mNcaHeaderSignature2Key; mContentArchiveHeaderSignature2Key = other.mContentArchiveHeaderSignature2Key;
mFlags = other.mFlags; mFlags = other.mFlags;
mProgramIdRestrict = other.mProgramIdRestrict; mProgramIdRestrict = other.mProgramIdRestrict;
mFileSystemAccessControl = other.mFileSystemAccessControl; mFileSystemAccessControl = other.mFileSystemAccessControl;
@ -23,7 +23,7 @@ void nn::hac::AccessControlInfoDesc::operator=(const AccessControlInfoDesc & oth
bool nn::hac::AccessControlInfoDesc::operator==(const AccessControlInfoDesc & other) const bool nn::hac::AccessControlInfoDesc::operator==(const AccessControlInfoDesc & other) const
{ {
return (mNcaHeaderSignature2Key == other.mNcaHeaderSignature2Key) \ return (mContentArchiveHeaderSignature2Key == other.mContentArchiveHeaderSignature2Key) \
&& (mFlags == other.mFlags) \ && (mFlags == other.mFlags) \
&& (mProgramIdRestrict == other.mProgramIdRestrict) \ && (mProgramIdRestrict == other.mProgramIdRestrict) \
&& (mFileSystemAccessControl == other.mFileSystemAccessControl) \ && (mFileSystemAccessControl == other.mFileSystemAccessControl) \
@ -62,7 +62,7 @@ void nn::hac::AccessControlInfoDesc::toBytes()
sAciDescHeader* hdr = (sAciDescHeader*)mRawBinary.data(); sAciDescHeader* hdr = (sAciDescHeader*)mRawBinary.data();
// set rsa modulus // 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 // set type
hdr->st_magic = aci::kAciDescStructMagic; 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()); memcpy(mRawBinary.data(), data, mRawBinary.size());
// save variables // 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++) 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() void nn::hac::AccessControlInfoDesc::clear()
{ {
mRawBinary.clear(); mRawBinary.clear();
memset((void*)&mNcaHeaderSignature2Key, 0, sizeof(mNcaHeaderSignature2Key)); memset((void*)&mContentArchiveHeaderSignature2Key, 0, sizeof(mContentArchiveHeaderSignature2Key));
mFlags.clear(); mFlags.clear();
mProgramIdRestrict.min = 0; mProgramIdRestrict.min = 0;
mProgramIdRestrict.max = 0; mProgramIdRestrict.max = 0;
@ -190,14 +190,14 @@ void nn::hac::AccessControlInfoDesc::clear()
mKernelCapabilities.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 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> #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]; 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::AesXtsMakeTweak(tweak, 1);
fnd::aes::AesXtsDecryptSector(src + sectorToOffset(1), nn::hac::nca::kSectorSize, key.key[0], key.key[1], tweak, raw_hdr); 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 // decrypt whole header
for (size_t i = 0; i < nn::hac::nca::kHeaderSectorNum; i++) 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; mAcidSignKey = other.mAcidSignKey;
mPkg2SignKey = other.mPkg2SignKey; mPkg2SignKey = other.mPkg2SignKey;
mNcaHeader0SignKey = other.mNcaHeader0SignKey; mContentArchiveHeader0SignKey = other.mContentArchiveHeader0SignKey;
mXciHeaderSignKey = other.mXciHeaderSignKey; mXciHeaderSignKey = other.mXciHeaderSignKey;
mNcaHeaderKey = other.mNcaHeaderKey; mContentArchiveHeaderKey = other.mContentArchiveHeaderKey;
mXciHeaderKey = other.mXciHeaderKey; mXciHeaderKey = other.mXciHeaderKey;
for (size_t i = 0; i < kMasterKeyNum; i++) for (size_t i = 0; i < kMasterKeyNum; i++)
@ -114,14 +114,14 @@ void KeyConfiguration::importHactoolGenericKeyfile(const std::string& path)
} }
// store nca header key // 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 // store xci header key
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kKeyStr), mXciHeaderKey.key, 0x10); _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kKeyStr), mXciHeaderKey.key, 0x10);
// store rsa keys // store rsa keys
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[nameidx], kRsaKeyPrivate), mNcaHeader0SignKey.priv_exponent, fnd::rsa::kRsa2048Size); _SAVE_KEYDATA(_CONCAT_2_STRINGS(kContentArchiveHeaderBase[nameidx], kRsaKeyPrivate), mContentArchiveHeader0SignKey.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], 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], kRsaKeyPrivate), mXciHeaderSignKey.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kRsaKeyModulus), mXciHeaderSignKey.modulus, 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 (i == 0 && nca_header_kek_source != kNullAesKey && nca_header_key_source != kNullAesXtsKey)
{ {
if (mNcaHeaderKey == kNullAesXtsKey) if (mContentArchiveHeaderKey == kNullAesXtsKey)
{ {
fnd::aes::sAes128Key nca_header_kek; 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(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(mContentArchiveHeaderKey.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[1], nca_header_key_source.key[1], nca_header_kek.key);
} }
} }
@ -196,11 +196,11 @@ void KeyConfiguration::clearGeneralKeyConfiguration()
{ {
mAcidSignKey = kNullRsa2048Key; mAcidSignKey = kNullRsa2048Key;
mPkg2SignKey = kNullRsa2048Key; mPkg2SignKey = kNullRsa2048Key;
mNcaHeader0SignKey = kNullRsa2048Key; mContentArchiveHeader0SignKey = kNullRsa2048Key;
mXciHeaderSignKey = kNullRsa2048Key; mXciHeaderSignKey = kNullRsa2048Key;
mPkiRootKeyList.clear(); mPkiRootKeyList.clear();
mNcaHeaderKey = kNullAesXtsKey; mContentArchiveHeaderKey = kNullAesXtsKey;
mXciHeaderKey = kNullAesKey; mXciHeaderKey = kNullAesKey;
for (size_t i = 0; i < kMasterKeyNum; i++) for (size_t i = 0; i < kMasterKeyNum; i++)
@ -221,14 +221,14 @@ void KeyConfiguration::clearNcaExternalKeys()
mNcaExternalContentKeyList.clear(); 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 bool KeyConfiguration::getAcidSignKey(fnd::rsa::sRsa2048Key& key) const

View file

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

View file

@ -107,18 +107,18 @@ void NcaProcess::importHeader()
} }
// read header block // 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 // decrypt header block
fnd::aes::sAesXts128Key header_key; fnd::aes::sAesXts128Key header_key;
mKeyCfg.getNcaHeaderKey(header_key); mKeyCfg.getContentArchiveHeaderKey(header_key);
nn::hac::NcaUtils::decryptNcaHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, header_key); nn::hac::NcaUtils::decryptContentArchiveHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, header_key);
// generate header hash // 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 // 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() void NcaProcess::generateNcaBodyEncryptionKeys()
@ -129,25 +129,27 @@ void NcaProcess::generateNcaBodyEncryptionKeys()
// get key data from header // get key data from header
byte_t masterkey_rev = nn::hac::NcaUtils::getMasterKeyRevisionFromKeyGeneration(mHdr.getKeyGeneration()); 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 // process key area
sKeys::sKeyAreaKey kak; sKeys::sKeyAreaKey kak;
fnd::aes::sAes128Key key_area_enc_key; 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.index = (byte_t)i;
kak.enc = mHdr.getEncAesKeys()[i]; kak.enc = key_area[i];
// key[0-3] // key[0-3]
if (i < 4 && mKeyCfg.getNcaKeyAreaEncryptionKey(masterkey_rev, keak_index, key_area_enc_key) == true) if (i < 4 && mKeyCfg.getNcaKeyAreaEncryptionKey(masterkey_rev, keak_index, key_area_enc_key) == true)
{ {
kak.decrypted = true; kak.decrypted = true;
nn::hac::AesKeygen::generateKey(kak.dec.key, kak.enc.key, key_area_enc_key.key); nn::hac::AesKeygen::generateKey(kak.dec.key, kak.enc.key, key_area_enc_key.key);
} }
// key[4] // key[KEY_AESCTR_HW]
else if (i == 4 && mKeyCfg.getNcaKeyAreaEncryptionKeyHw(masterkey_rev, keak_index, key_area_enc_key) == true) else if (i == nn::hac::nca::KEY_AESCTR_HW && mKeyCfg.getNcaKeyAreaEncryptionKeyHw(masterkey_rev, keak_index, key_area_enc_key) == true)
{ {
kak.decrypted = true; kak.decrypted = true;
nn::hac::AesKeygen::generateKey(kak.dec.key, kak.enc.key, key_area_enc_key.key); 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; 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 // get reference to relevant structures
const nn::hac::NcaHeader::sPartition& partition = mHdr.getPartitions()[i]; const nn::hac::ContentArchiveHeader::sPartitionEntry& partition = mHdr.getPartitionEntryList()[i];
nn::hac::sNcaFsHeader& fs_header = mHdrBlock.fs_header[partition.index]; nn::hac::sNcaFsHeader& fs_header = mHdrBlock.fs_header[partition.header_index];
// output structure // output structure
sPartitionInfo& info = mPartitions[partition.index]; sPartitionInfo& info = mPartitions[partition.header_index];
// validate header hash // validate header hash
fnd::sha::sSha256Hash calc_hash; fnd::sha::sSha256Hash fs_header_hash;
fnd::sha::Sha256((const byte_t*)&mHdrBlock.fs_header[partition.index], sizeof(nn::hac::sNcaFsHeader), calc_hash.bytes); fnd::sha::Sha256((const byte_t*)&mHdrBlock.fs_header[partition.header_index], sizeof(nn::hac::sNcaFsHeader), fs_header_hash.bytes);
if (calc_hash.compare(partition.hash) == false) if (fs_header_hash.compare(partition.fs_header_hash) == false)
{ {
error.clear(); 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()); throw fnd::Exception(kModuleName, error.str());
} }
@ -246,7 +248,7 @@ void NcaProcess::generatePartitionConfiguration()
if (fs_header.version.get() != nn::hac::nca::kDefaultFsHeaderVersion) if (fs_header.version.get() != nn::hac::nca::kDefaultFsHeaderVersion)
{ {
error.clear(); 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()); throw fnd::Exception(kModuleName, error.str());
} }
@ -387,7 +389,7 @@ void NcaProcess::validateNcaSignatures()
{ {
// validate signature[0] // validate signature[0]
fnd::rsa::sRsa2048Key sign0_key; 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) 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; std::cout << "[WARNING] NCA Header Main Signature: FAIL" << std::endl;
@ -415,7 +417,7 @@ void NcaProcess::validateNcaSignatures()
npdm.setCliOutputMode(0); npdm.setCliOutputMode(0);
npdm.process(); 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; 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 << " Dist. Type: " << getDistributionTypeStr(mHdr.getDistributionType()) << std::endl;
std::cout << " Content Type: " << getContentTypeStr(mHdr.getContentType()) << 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 << " 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 << " 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 << " ProgID: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getProgramId() << std::endl;
std::cout << " Content Index: " << std::dec << mHdr.getContentIndex() << 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)) if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
{ {
std::cout << " Partitions:" << std::endl; 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]; sPartitionInfo& info = mPartitions[index];
if (info.size == 0) continue;
std::cout << " " << std::dec << index << ":" << std::endl; std::cout << " " << std::dec << index << ":" << std::endl;
std::cout << " Offset: 0x" << std::hex << (uint64_t)info.offset << std::endl; std::cout << " Offset: 0x" << std::hex << (uint64_t)info.offset << std::endl;
@ -546,9 +549,9 @@ void NcaProcess::displayHeader()
void NcaProcess::processPartitions() 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]; struct sPartitionInfo& partition = mPartitions[index];
// if the reader is null, skip // 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; const char* str = nullptr;
switch (format_ver) switch (format_ver)
{ {
case (nn::hac::NcaHeader::NCA2_FORMAT): case (nn::hac::nca::FORMAT_NCA2):
str = "NCA2"; str = "NCA2";
break; break;
case (nn::hac::NcaHeader::NCA3_FORMAT): case (nn::hac::nca::FORMAT_NCA3):
str = "NCA3"; str = "NCA3";
break; break;
default: default:
@ -797,7 +800,7 @@ const char* NcaProcess::getContentTypeForMountStr(nn::hac::nca::ContentType cont
str = "data"; str = "data";
break; break;
case (nn::hac::nca::TYPE_PUBLIC_DATA): case (nn::hac::nca::TYPE_PUBLIC_DATA):
str = "publicData"; str = "publicdata";
break; break;
default: default:
str = ""; str = "";

View file

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

View file

@ -4,6 +4,8 @@
#include <fnd/io.h> #include <fnd/io.h>
#include "PfsProcess.h" #include "PfsProcess.h"
#include <fnd/SimpleTextOutput.h>
PfsProcess::PfsProcess() : PfsProcess::PfsProcess() :
mFile(), mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)), mCliOutputMode(_BIT(OUTPUT_BASIC)),
@ -80,6 +82,8 @@ void PfsProcess::importHeader()
// open minimum header to get full header size // open minimum header to get full header size
scratch.alloc(sizeof(nn::hac::sPfsHeader)); scratch.alloc(sizeof(nn::hac::sPfsHeader));
(*mFile)->read(scratch.data(), 0, scratch.size()); (*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) if (validateHeaderMagic(((nn::hac::sPfsHeader*)scratch.data())) == false)
{ {
throw fnd::Exception(kModuleName, "Corrupt Header"); 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 // prepare decrypted NCA data
byte_t nca_raw[nn::hac::nca::kHeaderSize]; 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) if (sample.size() < nn::hac::nca::kHeaderSize)
return false; return false;
fnd::aes::sAesXts128Key header_key; fnd::aes::sAesXts128Key header_key;
mKeyCfg.getNcaHeaderKey(header_key); mKeyCfg.getContentArchiveHeaderKey(header_key);
nn::hac::NcaUtils::decryptNcaHeader(sample.data(), nca_raw, 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) if (nca_header->st_magic.get() != nn::hac::nca::kNca2StructMagic && nca_header->st_magic.get() != nn::hac::nca::kNca3StructMagic)
return false; return false;
@ -882,9 +882,9 @@ void UserSettings::dumpKeyConfig() const
std::cout << "[KeyConfiguration]" << std::endl; std::cout << "[KeyConfiguration]" << std::endl;
std::cout << " NCA Keys:" << 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); 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); dumpAesXtsKey(aesxts_key, "Header Encryption Key", 2);
for (size_t i = 0; i < kMasterKeyNum; i++) for (size_t i = 0; i < kMasterKeyNum; i++)