[nx] Add ContentMetaBinary

This commit is contained in:
jakcron 2018-05-12 17:36:50 +08:00
parent 480a6225cc
commit a02ba43292
5 changed files with 600 additions and 0 deletions

View file

@ -0,0 +1,149 @@
#pragma once
#include <string>
#include <fnd/MemoryBlob.h>
#include <fnd/List.h>
#include <nx/cmnt.h>
namespace nx
{
class ContentMetaBinary :
public fnd::ISerialiseableBinary
{
public:
struct ContentInfo
{
crypto::sha::sSha256Hash hash;
byte_t nca_id[cmnt::kContentIdLen];
size_t size;
cmnt::ContentType type;
ContentInfo& operator=(const ContentInfo& other)
{
hash = other.hash;
memcpy(nca_id, other.nca_id, cmnt::kContentIdLen);
size = other.size;
type = other.type;
return *this;
}
bool operator==(const ContentInfo& other) const
{
return (hash == other.hash) \
&& (memcmp(nca_id, other.nca_id, cmnt::kContentIdLen) == 0) \
&& (size == other.size) \
&& (type == other.type);
}
bool operator!=(const ContentInfo& other) const
{
return !operator==(other);
}
};
struct ContentMetaInfo
{
uint64_t id;
uint32_t version;
cmnt::ContentMetaType type;
byte_t attributes;
ContentMetaInfo& operator=(const ContentMetaInfo& other)
{
id = other.id;
version = other.version;
type = other.type;
attributes = other.attributes;
return *this;
}
bool operator==(const ContentMetaInfo& other) const
{
return (id == other.id) \
&& (version == other.version) \
&& (type == other.type) \
&& (attributes == other.attributes);
}
bool operator!=(const ContentMetaInfo& other) const
{
return !operator==(other);
}
};
ContentMetaBinary();
ContentMetaBinary(const ContentMetaBinary& other);
ContentMetaBinary(const byte_t* bytes, size_t len);
// to be used after export
const byte_t* getBytes() const;
size_t getSize() const;
// export/import binary
void exportBinary();
void importBinary(const byte_t* bytes, size_t len);
// variables
void clear();
uint64_t getTitleId() const;
void setTitleId(uint64_t title_id);
uint32_t getTitleVersion() const;
void setTitleVersion(uint32_t version);
cmnt::ContentMetaType getType() const;
void setType(cmnt::ContentMetaType type);
byte_t getAttributes() const;
void setAttributes(byte_t attributes);
uint32_t getRequiredSystemVersion() const;
void setRequiredSystemVersion(uint32_t version);
const fnd::List<nx::ContentMetaBinary::ContentInfo>& getContentInfo() const;
void setContentInfo(const fnd::List<nx::ContentMetaBinary::ContentInfo>& info);
const fnd::List<nx::ContentMetaBinary::ContentMetaInfo>& getContentMetaInfo() const;
void setContentMetaInfo(const fnd::List<nx::ContentMetaBinary::ContentMetaInfo>& info);
const fnd::MemoryBlob& getExtendedData() const;
void setExtendedData(const fnd::MemoryBlob& data);
const nx::sDigest& getDigest() const;
void setDigest(const nx::sDigest& digest);
private:
const std::string kModuleName = "CONTENT_META_BINARY";
// binary blob
fnd::MemoryBlob mBinaryBlob;
// variables
uint64_t mTitleId;
uint32_t mTitleVersion;
cmnt::ContentMetaType mType;
byte_t mAttributes;
uint32_t mRequiredSystemVersion;
fnd::MemoryBlob mExtendedHeader;
fnd::List<nx::ContentMetaBinary::ContentInfo> mContentInfo;
fnd::List<nx::ContentMetaBinary::ContentMetaInfo> mContentMetaInfo;
fnd::MemoryBlob mExtendedData;
nx::sDigest mDigest;
inline size_t getExtendedHeaderOffset() const { return sizeof(sContentMetaHeader); }
inline size_t getContentInfoOffset(size_t exhdrSize) const { return getExtendedHeaderOffset() + exhdrSize; }
inline size_t getContentMetaInfoOffset(size_t exhdrSize, size_t contentInfoNum) const { return getContentInfoOffset(exhdrSize) + contentInfoNum * sizeof(sContentInfo); }
inline size_t getExtendedDataOffset(size_t exhdrSize, size_t contentInfoNum, size_t contentMetaNum) const { return getContentMetaInfoOffset(exhdrSize, contentInfoNum) + contentMetaNum * sizeof(sContentMetaInfo); }
inline size_t getDigestOffset(size_t exhdrSize, size_t contentInfoNum, size_t contentMetaNum, size_t exdataSize) const { return getExtendedDataOffset(exhdrSize, contentInfoNum, contentMetaNum) + exdataSize; }
inline size_t getTotalSize(size_t exhdrSize, size_t contentInfoNum, size_t contentMetaNum, size_t exdataSize) const { return getDigestOffset(exhdrSize, contentInfoNum, contentMetaNum, exdataSize) + cmnt::kDigestLen; }
bool validateExtendedHeaderSize(cmnt::ContentMetaType type, size_t exhdrSize);
size_t getExtendedDataSize(cmnt::ContentMetaType type, const byte_t* data);
void validateBinary(const byte_t* bytes, size_t len);
bool isEqual(const ContentMetaBinary& other) const;
void copyFrom(const ContentMetaBinary& other);
};
}

139
lib/libnx/include/nx/cmnt.h Normal file
View file

@ -0,0 +1,139 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <crypto/aes.h>
#include <crypto/sha.h>
#include <fnd/ISerialiseableBinary.h>
namespace nx
{
namespace cmnt
{
enum ContentType
{
TYPE_META = 0,
TYPE_PROGRAM,
TYPE_DATA,
TYPE_CONTROL,
TYPE_HTML_DOCUMENT,
TYPE_LEGAL_INFORMATION,
TYPE_DELTA_FRAGMENT
};
enum ContentMetaType
{
METATYPE_SYSTEM_PROGRAM = 1,
METATYPE_SYSTEM_DATA,
METATYPE_SYSTEM_UPDATE,
METATYPE_BOOT_IMAGE_PACKAGE,
METATYPE_BOOT_IMAGE_PACKAGE_SAFE,
METATYPE_APPLICATION = 0x80,
METATYPE_PATCH, // can have extended data
METATYPE_ADD_ON_CONTENT,
METATYPE_DELTA // can have extended data
};
enum UpdateType
{
UPDATETYPE_APPLY_AS_DELTA,
UPDATETYPE_OVERWRITE,
UPDATETYPE_CREATE
};
enum ContentMetaAttribute
{
ATTRIBUTE_NONE,
ATTRIBUTE_INCLUDES_EX_FAT_DRIVER,
ATTRIBUTE_REBOOTLESS
};
static const uint32_t kRequiredSystemVersion = 335544320;
static const uint32_t kDefaultVersion = 335545344;
static const size_t kContentIdLen = 0x10;
static const size_t kDigestLen = 0x20;
}
#pragma pack(push,1)
/*
struct sContentMeta
{
sContentMetaHeader hdr;
byte_t exhdr[]; // optional
sContentInfo info[];
sContentMetaInfo meta[];
byte_t extdata[];
byte_t digest[32]
};
*/
struct sContentMetaHeader
{
le_uint64_t id;
le_uint32_t version;
byte_t type;
byte_t reserved_0;
le_uint16_t exhdr_size;
le_uint16_t content_count;
le_uint16_t content_meta_count;
byte_t attributes;
byte_t reserved_1[3];
le_uint32_t required_system_version;
byte_t reserved_2[4];
};
struct sContentInfo
{
crypto::sha::sSha256Hash content_hash;
byte_t content_id[cmnt::kContentIdLen];
le_uint32_t size_lower;
le_uint16_t size_higher;
byte_t content_type;
byte_t id_offset;
};
struct sContentMetaInfo
{
le_uint64_t id;
le_uint32_t version;
byte_t type;
byte_t attributes;
byte_t reserved[2];
};
struct sApplicationMetaExtendedHeader
{
le_uint64_t id;
le_uint32_t required_system_version;
byte_t reserved[4];
};
struct sPatchMetaExtendedHeader
{
le_uint64_t id;
le_uint32_t required_system_version;
le_uint32_t extended_data_size;
byte_t reserved[8];
};
struct sAddOnContentMetaExtendedHeader
{
le_uint64_t id;
le_uint32_t required_system_version;
byte_t reserved[4];
};
struct sDeltaMetaExtendedHeader
{
le_uint64_t id;
le_uint32_t extended_data_size;
byte_t reserved[4];
};
struct sDigest
{
byte_t data[cmnt::kDigestLen];
};
#pragma pack(pop)
}

View file

@ -24,6 +24,8 @@
<ClInclude Include="include\nx\AcidBinary.h" /> <ClInclude Include="include\nx\AcidBinary.h" />
<ClInclude Include="include\nx\AciHeader.h" /> <ClInclude Include="include\nx\AciHeader.h" />
<ClInclude Include="include\nx\AesKeygen.h" /> <ClInclude Include="include\nx\AesKeygen.h" />
<ClInclude Include="include\nx\cmnt.h" />
<ClInclude Include="include\nx\ContentMetaBinary.h" />
<ClInclude Include="include\nx\FacBinary.h" /> <ClInclude Include="include\nx\FacBinary.h" />
<ClInclude Include="include\nx\FacHeader.h" /> <ClInclude Include="include\nx\FacHeader.h" />
<ClInclude Include="include\nx\HandleTableSizeEntry.h" /> <ClInclude Include="include\nx\HandleTableSizeEntry.h" />
@ -68,6 +70,7 @@
<ClCompile Include="source\AcidBinary.cpp" /> <ClCompile Include="source\AcidBinary.cpp" />
<ClCompile Include="source\AciHeader.cpp" /> <ClCompile Include="source\AciHeader.cpp" />
<ClCompile Include="source\AesKeygen.cpp" /> <ClCompile Include="source\AesKeygen.cpp" />
<ClCompile Include="source\ContentMetaBinary.cpp" />
<ClCompile Include="source\FacBinary.cpp" /> <ClCompile Include="source\FacBinary.cpp" />
<ClCompile Include="source\FacHeader.cpp" /> <ClCompile Include="source\FacHeader.cpp" />
<ClCompile Include="source\HandleTableSizeEntry.cpp" /> <ClCompile Include="source\HandleTableSizeEntry.cpp" />

View file

@ -144,6 +144,12 @@
<ClInclude Include="include\nx\hierarchicalsha256.h"> <ClInclude Include="include\nx\hierarchicalsha256.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\nx\ContentMetaBinary.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\nx\cmnt.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="source\AciBinary.cpp"> <ClCompile Include="source\AciBinary.cpp">
@ -245,6 +251,9 @@
<ClCompile Include="source\XciUtils.cpp"> <ClCompile Include="source\XciUtils.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\ContentMetaBinary.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="makefile" /> <None Include="makefile" />

View file

@ -0,0 +1,300 @@
#include <nx/ContentMetaBinary.h>
nx::ContentMetaBinary::ContentMetaBinary()
{
clear();
}
nx::ContentMetaBinary::ContentMetaBinary(const ContentMetaBinary & other)
{
copyFrom(other);
}
nx::ContentMetaBinary::ContentMetaBinary(const byte_t * bytes, size_t len)
{
importBinary(bytes, len);
}
const byte_t * nx::ContentMetaBinary::getBytes() const
{
return mBinaryBlob.getBytes();
}
size_t nx::ContentMetaBinary::getSize() const
{
return mBinaryBlob.getSize();
}
void nx::ContentMetaBinary::exportBinary()
{
throw fnd::Exception(kModuleName, "exportBinary() not implemented");
}
void nx::ContentMetaBinary::importBinary(const byte_t * bytes, size_t len)
{
// clear member variables
clear();
// validate layout
validateBinary(bytes, len);
// get pointer to header structure
const sContentMetaHeader* hdr = (const sContentMetaHeader*)bytes;
mTitleId = hdr->id.get();
mTitleVersion = hdr->version.get();
mType = (cmnt::ContentMetaType)hdr->type;
mAttributes = hdr->attributes;
mRequiredSystemVersion = hdr->required_system_version.get();
size_t exdata_size = 0;
// save exheader
if (hdr->exhdr_size.get() > 0)
{
mExtendedHeader.alloc(hdr->exhdr_size.get());
memcpy(mExtendedHeader.getBytes(), bytes + getExtendedHeaderOffset(), hdr->exhdr_size.get());
exdata_size = getExtendedDataSize(mType, mExtendedHeader.getBytes());
}
// save content info
if (hdr->content_count.get() > 0)
{
const sContentInfo* info = (const sContentInfo*)(bytes + getContentInfoOffset(hdr->exhdr_size.get()));
for (size_t i = 0; i < hdr->content_count.get(); i++)
{
mContentInfo[i].hash = info[i].content_hash;
memcpy(mContentInfo[i].nca_id, info[i].content_id, cmnt::kContentIdLen);
mContentInfo[i].size = (uint64_t)(info[i].size_lower.get()) | (uint64_t)(info[i].size_higher.get()) << 32;
mContentInfo[i].type = (cmnt::ContentType)info[i].content_type;
}
}
// save content meta info
if (hdr->content_meta_count.get() > 0)
{
const sContentMetaInfo* info = (const sContentMetaInfo*)(bytes + getContentMetaInfoOffset(hdr->exhdr_size.get(), hdr->content_count.get()));
for (size_t i = 0; i < hdr->content_meta_count.get(); i++)
{
mContentMetaInfo[i].id = info[i].id.get();
mContentMetaInfo[i].version = info[i].version.get();
mContentMetaInfo[i].type = (cmnt::ContentMetaType)info[i].type;
mContentMetaInfo[i].attributes = info[i].attributes;
}
}
// save exdata
if (exdata_size > 0)
{
mExtendedData.alloc(exdata_size);
memcpy(mExtendedData.getBytes(), bytes + getExtendedDataOffset(hdr->exhdr_size.get(), hdr->content_count.get(), hdr->content_meta_count.get()), exdata_size);
}
// save digest
memcpy(mDigest.data, bytes + getDigestOffset(hdr->exhdr_size.get(), hdr->content_count.get(), hdr->content_meta_count.get(), exdata_size), cmnt::kDigestLen);
}
void nx::ContentMetaBinary::clear()
{
mBinaryBlob.clear();
mTitleId = 0;
mTitleVersion = 0;
mType = cmnt::METATYPE_SYSTEM_PROGRAM;
mAttributes = cmnt::ATTRIBUTE_NONE;
mRequiredSystemVersion = 0;
mExtendedHeader.clear();
mContentInfo.clear();
mContentMetaInfo.clear();
mExtendedData.clear();
memset(mDigest.data, 0, cmnt::kDigestLen);
}
uint64_t nx::ContentMetaBinary::getTitleId() const
{
return mTitleId;
}
void nx::ContentMetaBinary::setTitleId(uint64_t title_id)
{
mTitleId = title_id;
}
uint32_t nx::ContentMetaBinary::getTitleVersion() const
{
return mTitleVersion;
}
void nx::ContentMetaBinary::setTitleVersion(uint32_t version)
{
mTitleVersion = version;
}
nx::cmnt::ContentMetaType nx::ContentMetaBinary::getType() const
{
return mType;
}
void nx::ContentMetaBinary::setType(cmnt::ContentMetaType type)
{
mType = type;
}
byte_t nx::ContentMetaBinary::getAttributes() const
{
return mAttributes;
}
void nx::ContentMetaBinary::setAttributes(byte_t attributes)
{
mAttributes = attributes;
}
uint32_t nx::ContentMetaBinary::getRequiredSystemVersion() const
{
return mRequiredSystemVersion;
}
void nx::ContentMetaBinary::setRequiredSystemVersion(uint32_t version)
{
mRequiredSystemVersion = version;
}
const fnd::List<nx::ContentMetaBinary::ContentInfo>& nx::ContentMetaBinary::getContentInfo() const
{
return mContentInfo;
}
void nx::ContentMetaBinary::setContentInfo(const fnd::List<nx::ContentMetaBinary::ContentInfo>& info)
{
mContentInfo = info;
}
const fnd::List<nx::ContentMetaBinary::ContentMetaInfo>& nx::ContentMetaBinary::getContentMetaInfo() const
{
return mContentMetaInfo;
}
void nx::ContentMetaBinary::setContentMetaInfo(const fnd::List<nx::ContentMetaBinary::ContentMetaInfo>& info)
{
mContentMetaInfo = info;
}
const fnd::MemoryBlob & nx::ContentMetaBinary::getExtendedData() const
{
return mExtendedData;
}
void nx::ContentMetaBinary::setExtendedData(const fnd::MemoryBlob & data)
{
mExtendedData = data;
}
const nx::sDigest & nx::ContentMetaBinary::getDigest() const
{
return mDigest;
}
void nx::ContentMetaBinary::setDigest(const nx::sDigest & digest)
{
memcpy(mDigest.data, digest.data, cmnt::kDigestLen);
}
bool nx::ContentMetaBinary::validateExtendedHeaderSize(cmnt::ContentMetaType type, size_t exhdrSize)
{
bool validSize = false;
if (type == cmnt::METATYPE_APPLICATION && exhdrSize == sizeof(sApplicationMetaExtendedHeader))
validSize = true;
else if (type == cmnt::METATYPE_PATCH && exhdrSize == sizeof(sPatchMetaExtendedHeader))
validSize = true;
else if (type == cmnt::METATYPE_ADD_ON_CONTENT && exhdrSize == sizeof(sAddOnContentMetaExtendedHeader))
validSize = true;
else if (type == cmnt::METATYPE_DELTA && exhdrSize == sizeof(sDeltaMetaExtendedHeader))
validSize = true;
return validSize;
}
size_t nx::ContentMetaBinary::getExtendedDataSize(cmnt::ContentMetaType type, const byte_t * data)
{
size_t exdata_len = 0;
if (type == cmnt::METATYPE_PATCH)
{
const sPatchMetaExtendedHeader* exhdr = (const sPatchMetaExtendedHeader*)(data);
exdata_len = exhdr->extended_data_size.get();
}
else if (type == cmnt::METATYPE_DELTA)
{
const sDeltaMetaExtendedHeader* exhdr = (const sDeltaMetaExtendedHeader*)(data);
exdata_len = exhdr->extended_data_size.get();
}
return exdata_len;
}
void nx::ContentMetaBinary::validateBinary(const byte_t * bytes, size_t len)
{
// check if it is large enough to read the header
if (len < sizeof(sContentMetaHeader))
{
throw fnd::Exception(kModuleName, "Binary too small");
}
// get pointer to header structure
const sContentMetaHeader* hdr = (const sContentMetaHeader*)bytes;
// validate extended header size
if (validateExtendedHeaderSize((cmnt::ContentMetaType)hdr->type, hdr->exhdr_size.get()) == false)
{
throw fnd::Exception(kModuleName, "Invalid extended header size");
}
// check binary size again for new minimum size
if (len < getTotalSize(hdr->exhdr_size.get(), hdr->content_count.get(), hdr->content_meta_count.get(), 0))
{
throw fnd::Exception(kModuleName, "Binary too small");
}
// check binary size again with extended data size
if (len < getTotalSize(hdr->exhdr_size.get(), hdr->content_count.get(), hdr->content_meta_count.get(), getExtendedDataSize((cmnt::ContentMetaType)hdr->type, bytes + getExtendedHeaderOffset())))
{
throw fnd::Exception(kModuleName, "Binary too small");
}
}
bool nx::ContentMetaBinary::isEqual(const ContentMetaBinary & other) const
{
return (mTitleId == other.mTitleId) \
&& (mTitleVersion == other.mTitleVersion) \
&& (mType == other.mType) \
&& (mAttributes == other.mAttributes) \
&& (mRequiredSystemVersion == other.mRequiredSystemVersion) \
&& (mExtendedHeader == other.mExtendedHeader) \
&& (mContentInfo == other.mContentInfo) \
&& (mContentMetaInfo == other.mContentMetaInfo) \
&& (mExtendedData == other.mExtendedData) \
&& (memcmp(mDigest.data, other.mDigest.data, cmnt::kDigestLen) == 0);
}
void nx::ContentMetaBinary::copyFrom(const ContentMetaBinary & other)
{
if (other.getSize() > 0)
{
importBinary(other.getBytes(), other.getSize());
}
else
{
clear();
mTitleId = other.mTitleId;
mTitleVersion = other.mTitleVersion;
mType = other.mType;
mAttributes = other.mAttributes;
mRequiredSystemVersion = other.mRequiredSystemVersion;
mExtendedHeader = other.mExtendedHeader;
mContentInfo = other.mContentInfo;
mContentMetaInfo = other.mContentMetaInfo;
mExtendedData = other.mExtendedData;
memcpy(mDigest.data, other.mDigest.data, cmnt::kDigestLen);
}
}