mirror of
https://github.com/jakcron/nstool
synced 2024-11-22 21:49:30 +00:00
[hac|nstool] Rename NcaHeader to ContentArchiveHeader.
This commit is contained in:
parent
4e6f7fed80
commit
dfe877b959
16 changed files with 482 additions and 455 deletions
|
@ -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;
|
||||||
|
|
|
@ -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;
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
@ -87,7 +94,7 @@ namespace hac
|
||||||
}
|
}
|
||||||
|
|
||||||
#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];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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
|
||||||
|
|
334
lib/libhac/source/ContentArchiveHeader.cpp
Normal file
334
lib/libhac/source/ContentArchiveHeader.cpp
Normal 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);
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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++)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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];
|
||||||
|
|
||||||
|
|
|
@ -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 = "";
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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++)
|
||||||
|
|
Loading…
Reference in a new issue