2017-08-05 13:09:50 +00:00
|
|
|
#include <nx/NcaHeader.h>
|
2017-07-05 08:57:14 +00:00
|
|
|
|
2017-07-06 11:17:21 +00:00
|
|
|
using namespace nx;
|
|
|
|
|
|
|
|
|
2017-07-05 08:57:14 +00:00
|
|
|
void NcaHeader::exportBinary()
|
|
|
|
{
|
2017-07-05 15:38:14 +00:00
|
|
|
mBinaryBlob.alloc(sizeof(sNcaHeader));
|
2017-07-06 03:30:27 +00:00
|
|
|
sNcaHeader* hdr = (sNcaHeader*)mBinaryBlob.getBytes();
|
2017-07-05 15:38:14 +00:00
|
|
|
|
2018-03-17 12:01:19 +00:00
|
|
|
strncpy(hdr->signature, kNcaSig.c_str(), 4);
|
|
|
|
hdr->distribution_type = mDistributionType;
|
|
|
|
hdr->content_type = mContentType;
|
|
|
|
hdr->key_generation = mEncryptionType;
|
|
|
|
hdr->key_area_encryption_key_index = mKeyIndex;
|
|
|
|
hdr->nca_size = mNcaSize;
|
|
|
|
hdr->program_id = mProgramId;
|
|
|
|
hdr->content_index = mContentIndex;
|
|
|
|
hdr->sdk_addon_version = mSdkAddonVersion;
|
2017-07-05 15:38:14 +00:00
|
|
|
|
|
|
|
// TODO: properly reconstruct NCA layout? atm in hands of user
|
|
|
|
|
2017-07-06 10:55:58 +00:00
|
|
|
for (size_t i = 0; i < mSections.getSize(); i++)
|
2017-07-05 15:38:14 +00:00
|
|
|
{
|
|
|
|
// determine section index
|
2017-07-06 10:55:58 +00:00
|
|
|
u8 section = mSections.getSize() - 1 - i;
|
2017-07-05 15:38:14 +00:00
|
|
|
|
2018-03-17 12:01:19 +00:00
|
|
|
hdr->section[section].start = sizeToBlockNum(mSections[i].offset);
|
|
|
|
hdr->section[section].end = (sizeToBlockNum(mSections[i].offset) + sizeToBlockNum(mSections[i].size));
|
|
|
|
hdr->section[section].enabled = true;
|
|
|
|
hdr->section_hash[section] = mSections[i].hash;
|
2017-07-05 15:38:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < kAesKeyNum; i++)
|
|
|
|
{
|
2018-03-17 12:01:19 +00:00
|
|
|
hdr->enc_aes_key[i] = mEncAesKeys[i];
|
2017-07-05 15:38:14 +00:00
|
|
|
}
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2017-07-15 08:28:01 +00:00
|
|
|
void NcaHeader::importBinary(const u8 * bytes, size_t len)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-15 08:28:01 +00:00
|
|
|
if (len < sizeof(sNcaHeader))
|
|
|
|
{
|
|
|
|
throw fnd::Exception(kModuleName, "NCA header size is too small");
|
|
|
|
}
|
|
|
|
|
2017-07-17 08:21:39 +00:00
|
|
|
clear();
|
2017-07-15 08:28:01 +00:00
|
|
|
|
2017-07-05 15:38:14 +00:00
|
|
|
mBinaryBlob.alloc(sizeof(sNcaHeader));
|
2017-07-06 03:30:27 +00:00
|
|
|
memcpy(mBinaryBlob.getBytes(), bytes, sizeof(sNcaHeader));
|
2017-07-05 08:57:14 +00:00
|
|
|
|
2017-07-06 03:30:27 +00:00
|
|
|
sNcaHeader* hdr = (sNcaHeader*)mBinaryBlob.getBytes();
|
2017-07-05 08:57:14 +00:00
|
|
|
|
2018-03-17 12:01:19 +00:00
|
|
|
if (memcmp(hdr->signature, kNcaSig.c_str(), 4) != 0)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
|
|
|
throw fnd::Exception(kModuleName, "NCA header corrupt");
|
|
|
|
}
|
2017-07-15 08:28:01 +00:00
|
|
|
|
2018-03-17 12:01:19 +00:00
|
|
|
mDistributionType = (DistributionType)hdr->distribution_type;
|
|
|
|
mContentType = (ContentType)hdr->content_type;
|
|
|
|
mEncryptionType = (EncryptionType)hdr->key_generation;
|
|
|
|
mKeyIndex = (EncryptionKeyIndex)hdr->key_area_encryption_key_index;
|
|
|
|
mNcaSize = *hdr->nca_size;
|
|
|
|
mProgramId = *hdr->program_id;
|
|
|
|
mContentIndex = *hdr->content_index;
|
|
|
|
mSdkAddonVersion = *hdr->sdk_addon_version;
|
2017-07-05 08:57:14 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < kSectionNum; i++)
|
|
|
|
{
|
|
|
|
// determine section index
|
|
|
|
u8 section = kSectionNum - 1 - i;
|
2017-07-15 08:28:01 +00:00
|
|
|
|
2017-07-05 08:57:14 +00:00
|
|
|
// skip sections that don't exist
|
2018-03-17 12:01:19 +00:00
|
|
|
if (*hdr->section[section].start == 0 && *hdr->section[section].end == 0) continue;
|
2017-07-15 08:28:01 +00:00
|
|
|
|
2017-07-18 14:17:32 +00:00
|
|
|
EncryptionType encType = mEncryptionType;
|
|
|
|
if (encType == CRYPT_AUTO)
|
|
|
|
{
|
|
|
|
if (mContentType == TYPE_PROGRAM && section == SECTION_LOGO)
|
|
|
|
{
|
|
|
|
encType = CRYPT_NONE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
encType = CRYPT_AESCTR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-05 08:57:14 +00:00
|
|
|
// add high level struct
|
2018-03-17 12:01:19 +00:00
|
|
|
mSections.addElement({ blockNumToSize(*hdr->section[section].start), blockNumToSize(hdr->section[section].end.get() - hdr->section[section].start.get()), encType, hdr->section_hash[section] });
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < kAesKeyNum; i++)
|
|
|
|
{
|
2018-03-17 12:01:19 +00:00
|
|
|
mEncAesKeys.addElement(hdr->enc_aes_key[i]);
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-15 08:28:01 +00:00
|
|
|
void nx::NcaHeader::clear()
|
2017-07-06 03:30:27 +00:00
|
|
|
{
|
2017-07-18 14:17:32 +00:00
|
|
|
mDistributionType = DIST_DOWNLOAD;
|
|
|
|
mContentType = TYPE_PROGRAM;
|
|
|
|
mEncryptionType = CRYPT_AUTO;
|
|
|
|
mKeyIndex = KEY_DEFAULT;
|
2017-07-17 08:21:39 +00:00
|
|
|
mNcaSize = 0;
|
|
|
|
mProgramId = 0;
|
2017-07-18 14:17:32 +00:00
|
|
|
mContentIndex = 0;
|
|
|
|
mSdkAddonVersion = 0;
|
2017-07-17 08:21:39 +00:00
|
|
|
mSections.clear();
|
2017-07-18 14:17:32 +00:00
|
|
|
mEncAesKeys.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
nx::NcaHeader::DistributionType nx::NcaHeader::getDistributionType() const
|
|
|
|
{
|
|
|
|
return mDistributionType;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nx::NcaHeader::setDistributionType(DistributionType type)
|
|
|
|
{
|
|
|
|
mDistributionType = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
nx::NcaHeader::ContentType nx::NcaHeader::getContentType() const
|
|
|
|
{
|
|
|
|
return mContentType;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nx::NcaHeader::setContentType(ContentType type)
|
|
|
|
{
|
|
|
|
mContentType = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
nx::NcaHeader::EncryptionType nx::NcaHeader::getEncryptionType() const
|
|
|
|
{
|
|
|
|
return mEncryptionType;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nx::NcaHeader::setEncryptionType(EncryptionType type)
|
|
|
|
{
|
|
|
|
mEncryptionType = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
nx::NcaHeader::EncryptionKeyIndex nx::NcaHeader::getKeyIndex() const
|
|
|
|
{
|
|
|
|
return mKeyIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nx::NcaHeader::setKeyIndex(EncryptionKeyIndex index)
|
|
|
|
{
|
|
|
|
mKeyIndex = index;
|
2017-07-06 03:30:27 +00:00
|
|
|
}
|
|
|
|
|
2017-07-05 08:57:14 +00:00
|
|
|
u64 NcaHeader::getNcaSize() const
|
|
|
|
{
|
2017-07-05 15:38:14 +00:00
|
|
|
return mNcaSize;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NcaHeader::setNcaSize(u64 size)
|
|
|
|
{
|
2017-07-05 15:38:14 +00:00
|
|
|
mNcaSize = size;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
u64 NcaHeader::getProgramId() const
|
|
|
|
{
|
2017-07-05 15:38:14 +00:00
|
|
|
return mProgramId;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NcaHeader::setProgramId(u64 program_id)
|
|
|
|
{
|
2017-07-05 15:38:14 +00:00
|
|
|
mProgramId = program_id;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2017-07-18 14:17:32 +00:00
|
|
|
u32 nx::NcaHeader::getContentIndex() const
|
|
|
|
{
|
|
|
|
return mContentIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nx::NcaHeader::setContentIndex(u32 index)
|
|
|
|
{
|
|
|
|
mContentIndex = index;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 nx::NcaHeader::getSdkAddonVersion() const
|
|
|
|
{
|
|
|
|
return mSdkAddonVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nx::NcaHeader::setSdkAddonVersion(u32 version)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-18 14:17:32 +00:00
|
|
|
mSdkAddonVersion = version;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2017-07-06 10:55:58 +00:00
|
|
|
const fnd::List<NcaHeader::sSection>& NcaHeader::getSections() const
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-05 15:38:14 +00:00
|
|
|
return mSections;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NcaHeader::addSection(const sSection & section)
|
|
|
|
{
|
2017-07-06 10:55:58 +00:00
|
|
|
if (mSections.getSize() >= kSectionNum)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
|
|
|
throw fnd::Exception(kModuleName, "Too many NCA sections");
|
|
|
|
}
|
2017-07-06 10:55:58 +00:00
|
|
|
mSections.addElement(section);
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2017-07-18 14:17:32 +00:00
|
|
|
const fnd::List<crypto::aes::sAes128Key>& NcaHeader::getEncAesKeys() const
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-18 14:17:32 +00:00
|
|
|
return mEncAesKeys;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2017-07-18 14:17:32 +00:00
|
|
|
void NcaHeader::addEncAesKey(const crypto::aes::sAes128Key & key)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-18 14:17:32 +00:00
|
|
|
if (mEncAesKeys.getSize() >= kAesKeyNum)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
|
|
|
throw fnd::Exception(kModuleName, "Too many NCA aes keys");
|
|
|
|
}
|
|
|
|
|
2017-07-18 14:17:32 +00:00
|
|
|
mEncAesKeys.addElement(key);
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
u64 NcaHeader::blockNumToSize(u32 block_num) const
|
|
|
|
{
|
2017-07-18 14:17:32 +00:00
|
|
|
return block_num*kBlockSize;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
u32 NcaHeader::sizeToBlockNum(u64 real_size) const
|
|
|
|
{
|
2017-07-18 14:17:32 +00:00
|
|
|
return align(real_size, kBlockSize)/kBlockSize;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2017-07-06 10:55:58 +00:00
|
|
|
bool NcaHeader::isEqual(const NcaHeader & other) const
|
|
|
|
{
|
2017-07-18 14:17:32 +00:00
|
|
|
return (mDistributionType == other.mDistributionType) \
|
|
|
|
&& (mContentType == other.mContentType) \
|
|
|
|
&& (mEncryptionType == other.mEncryptionType) \
|
|
|
|
&& (mKeyIndex == other.mKeyIndex) \
|
2017-07-06 10:55:58 +00:00
|
|
|
&& (mNcaSize == other.mNcaSize) \
|
|
|
|
&& (mProgramId == other.mProgramId) \
|
2017-07-18 14:17:32 +00:00
|
|
|
&& (mContentIndex == other.mContentIndex) \
|
|
|
|
&& (mSdkAddonVersion == other.mSdkAddonVersion) \
|
2017-07-06 10:55:58 +00:00
|
|
|
&& (mSections == other.mSections) \
|
2017-07-18 14:17:32 +00:00
|
|
|
&& (mEncAesKeys == other.mEncAesKeys);
|
2017-07-06 10:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NcaHeader::copyFrom(const NcaHeader & other)
|
|
|
|
{
|
|
|
|
if (other.getSize())
|
|
|
|
{
|
|
|
|
importBinary(other.getBytes(), other.getSize());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-07-18 14:17:32 +00:00
|
|
|
mBinaryBlob.clear();
|
|
|
|
mDistributionType = other.mDistributionType;
|
|
|
|
mContentType = other.mContentType;
|
|
|
|
mEncryptionType = other.mEncryptionType;
|
|
|
|
mKeyIndex = other.mKeyIndex;
|
2017-07-06 10:55:58 +00:00
|
|
|
mNcaSize = other.mNcaSize;
|
|
|
|
mProgramId = other.mProgramId;
|
2017-07-18 14:17:32 +00:00
|
|
|
mContentIndex = other.mContentIndex;
|
|
|
|
mSdkAddonVersion = other.mSdkAddonVersion;
|
2017-07-06 10:55:58 +00:00
|
|
|
mSections = other.mSections;
|
2017-07-18 14:17:32 +00:00
|
|
|
mEncAesKeys = other.mEncAesKeys;
|
2017-07-06 10:55:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-05 08:57:14 +00:00
|
|
|
NcaHeader::NcaHeader()
|
|
|
|
{
|
2017-07-17 08:21:39 +00:00
|
|
|
clear();
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NcaHeader::NcaHeader(const NcaHeader & other)
|
|
|
|
{
|
2017-07-06 10:55:58 +00:00
|
|
|
copyFrom(other);
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2017-07-15 08:28:01 +00:00
|
|
|
NcaHeader::NcaHeader(const u8 * bytes, size_t len)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-15 08:28:01 +00:00
|
|
|
importBinary(bytes, len);
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2017-07-06 10:55:58 +00:00
|
|
|
bool NcaHeader::operator==(const NcaHeader & other) const
|
|
|
|
{
|
|
|
|
return isEqual(other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool NcaHeader::operator!=(const NcaHeader & other) const
|
2017-07-06 03:30:27 +00:00
|
|
|
{
|
2017-07-06 10:55:58 +00:00
|
|
|
return !isEqual(other);
|
2017-07-06 03:30:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NcaHeader::operator=(const NcaHeader & other)
|
|
|
|
{
|
|
|
|
this->importBinary(other.getBytes(), other.getSize());
|
|
|
|
}
|
|
|
|
|
2017-07-05 08:57:14 +00:00
|
|
|
const u8 * NcaHeader::getBytes() const
|
|
|
|
{
|
2017-07-06 03:30:27 +00:00
|
|
|
return mBinaryBlob.getBytes();
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t NcaHeader::getSize() const
|
|
|
|
{
|
2017-07-06 03:30:27 +00:00
|
|
|
return mBinaryBlob.getSize();
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|