From e8c39ace04abee298971b8c886b6337ff57fc095 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 22 Jul 2017 16:59:06 +0800 Subject: [PATCH] [es/tiktool] start eticket. --- NXTools.sln | 26 ++ lib/es/ETicketBody_V2.cpp | 356 +++++++++++++++++++++++ lib/es/ETicketBody_V2.h | 219 ++++++++++++++ lib/es/ETicketContentRecord_V1.cpp | 6 + lib/es/ETicketContentRecord_V1.h | 46 +++ lib/es/ETicketSectionHeader_V2.cpp | 161 ++++++++++ lib/es/ETicketSectionHeader_V2.h | 101 +++++++ lib/es/es.vcxproj | 133 +++++++++ lib/es/es.vcxproj.filters | 42 +++ lib/es/makefile | 33 +++ lib/nx/nx.vcxproj.filters | 3 + programs/makefile | 2 +- programs/tiktool/main.cpp | 33 +++ programs/tiktool/makefile | 39 +++ programs/tiktool/tiktool.vcxproj | 125 ++++++++ programs/tiktool/tiktool.vcxproj.filters | 25 ++ 16 files changed, 1349 insertions(+), 1 deletion(-) create mode 100644 lib/es/ETicketBody_V2.cpp create mode 100644 lib/es/ETicketBody_V2.h create mode 100644 lib/es/ETicketContentRecord_V1.cpp create mode 100644 lib/es/ETicketContentRecord_V1.h create mode 100644 lib/es/ETicketSectionHeader_V2.cpp create mode 100644 lib/es/ETicketSectionHeader_V2.h create mode 100644 lib/es/es.vcxproj create mode 100644 lib/es/es.vcxproj.filters create mode 100644 lib/es/makefile create mode 100644 programs/tiktool/main.cpp create mode 100644 programs/tiktool/makefile create mode 100644 programs/tiktool/tiktool.vcxproj create mode 100644 programs/tiktool/tiktool.vcxproj.filters diff --git a/NXTools.sln b/NXTools.sln index fdb13e0..e7d9026 100644 --- a/NXTools.sln +++ b/NXTools.sln @@ -12,6 +12,9 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ncatool", "programs\ncatool\ncatool.vcxproj", "{7DA88C6F-4470-495D-995A-4F633F3370C1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{170B4A09-1B67-4A62-93AB-116EBCFF4A8C}" + ProjectSection(SolutionItems) = preProject + lib\makefile = lib\makefile + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "npdmtool", "programs\npdmtool\npdmtool.vcxproj", "{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C}" EndProject @@ -24,10 +27,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .gitignore = .gitignore makefile = makefile + README.md = README.md EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfstool", "programs\pfstool\pfstool.vcxproj", "{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libes", "lib\es\es.vcxproj", "{7BE99936-0D40-410D-944B-4513C2EFF8DC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tiktool", "programs\tiktool\tiktool.vcxproj", "{2200B834-F15A-4C6E-9DDB-6012B9A5C246}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -84,6 +92,22 @@ Global {BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}.Release|x64.Build.0 = Release|x64 {BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}.Release|x86.ActiveCfg = Release|Win32 {BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}.Release|x86.Build.0 = Release|Win32 + {7BE99936-0D40-410D-944B-4513C2EFF8DC}.Debug|x64.ActiveCfg = Debug|x64 + {7BE99936-0D40-410D-944B-4513C2EFF8DC}.Debug|x64.Build.0 = Debug|x64 + {7BE99936-0D40-410D-944B-4513C2EFF8DC}.Debug|x86.ActiveCfg = Debug|Win32 + {7BE99936-0D40-410D-944B-4513C2EFF8DC}.Debug|x86.Build.0 = Debug|Win32 + {7BE99936-0D40-410D-944B-4513C2EFF8DC}.Release|x64.ActiveCfg = Release|x64 + {7BE99936-0D40-410D-944B-4513C2EFF8DC}.Release|x64.Build.0 = Release|x64 + {7BE99936-0D40-410D-944B-4513C2EFF8DC}.Release|x86.ActiveCfg = Release|Win32 + {7BE99936-0D40-410D-944B-4513C2EFF8DC}.Release|x86.Build.0 = Release|Win32 + {2200B834-F15A-4C6E-9DDB-6012B9A5C246}.Debug|x64.ActiveCfg = Debug|x64 + {2200B834-F15A-4C6E-9DDB-6012B9A5C246}.Debug|x64.Build.0 = Debug|x64 + {2200B834-F15A-4C6E-9DDB-6012B9A5C246}.Debug|x86.ActiveCfg = Debug|Win32 + {2200B834-F15A-4C6E-9DDB-6012B9A5C246}.Debug|x86.Build.0 = Debug|Win32 + {2200B834-F15A-4C6E-9DDB-6012B9A5C246}.Release|x64.ActiveCfg = Release|x64 + {2200B834-F15A-4C6E-9DDB-6012B9A5C246}.Release|x64.Build.0 = Release|x64 + {2200B834-F15A-4C6E-9DDB-6012B9A5C246}.Release|x86.ActiveCfg = Release|Win32 + {2200B834-F15A-4C6E-9DDB-6012B9A5C246}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -95,5 +119,7 @@ Global {7DA88C6F-4470-495D-995A-4F633F3370C1} = {E0863FCC-8E72-490D-BE1B-458F12CA8298} {550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C} = {E0863FCC-8E72-490D-BE1B-458F12CA8298} {BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB} = {E0863FCC-8E72-490D-BE1B-458F12CA8298} + {7BE99936-0D40-410D-944B-4513C2EFF8DC} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C} + {2200B834-F15A-4C6E-9DDB-6012B9A5C246} = {E0863FCC-8E72-490D-BE1B-458F12CA8298} EndGlobalSection EndGlobal diff --git a/lib/es/ETicketBody_V2.cpp b/lib/es/ETicketBody_V2.cpp new file mode 100644 index 0000000..e14f8e1 --- /dev/null +++ b/lib/es/ETicketBody_V2.cpp @@ -0,0 +1,356 @@ +#include "ETicketBody_V2.h" + + + +es::ETicketBody_V2::ETicketBody_V2() +{ + clear(); +} + +es::ETicketBody_V2::ETicketBody_V2(const ETicketBody_V2 & other) +{ + copyFrom(other); +} + +es::ETicketBody_V2::ETicketBody_V2(const u8 * bytes, size_t len) +{ + importBinary(bytes, len); +} + +bool es::ETicketBody_V2::operator==(const ETicketBody_V2 & other) const +{ + return isEqual(other); +} + +bool es::ETicketBody_V2::operator!=(const ETicketBody_V2 & other) const +{ + return !isEqual(other); +} + +void es::ETicketBody_V2::operator=(const ETicketBody_V2 & other) +{ + copyFrom(other); +} + +const u8 * es::ETicketBody_V2::getBytes() const +{ + return mBinaryBlob.getBytes(); +} + +size_t es::ETicketBody_V2::getSize() const +{ + return mBinaryBlob.getSize(); +} + +bool es::ETicketBody_V2::isEqual(const ETicketBody_V2 & other) const +{ + return (mIssuer == other.mIssuer) \ + && (memcmp(mEncTitleKey, other.mEncTitleKey, kEncTitleKeyLen) == 0) \ + && (mEncType == other.mEncType) \ + && (mTicketVersion == other.mTicketVersion) \ + && (mLicenseType == other.mLicenseType) \ + && (mPreInstall == other.mPreInstall) \ + && (mSharedTitle == other.mSharedTitle) \ + && (mAllowAllContent == other.mAllowAllContent) \ + && (memcmp(mReservedRegion, other.mReservedRegion, kReservedRegionLen) == 0) \ + && (mTicketId == other.mTicketId) \ + && (mDeviceId == other.mDeviceId) \ + && (memcmp(mRightsId, other.mRightsId, kRightsIdLen) == 0) \ + && (mAccountId == other.mAccountId) \ + && (mSectTotalSize == other.mSectTotalSize) \ + && (mSectHeaderOffset == other.mSectHeaderOffset) \ + && (mSectNum == other.mSectNum) \ + && (mSectEntrySize == other.mSectEntrySize); +} + +void es::ETicketBody_V2::copyFrom(const ETicketBody_V2 & other) +{ + if (other.getSize()) + { + importBinary(other.getBytes(), other.getSize()); + } + else + { + clear(); + mIssuer = other.mIssuer; + memcpy(mEncTitleKey, other.mEncTitleKey, kEncTitleKeyLen); + mEncType = other.mEncType; + mTicketVersion = other.mTicketVersion; + mLicenseType = other.mLicenseType; + mPreInstall = other.mPreInstall; + mSharedTitle = other.mSharedTitle; + mAllowAllContent = other.mAllowAllContent; + memcpy(mReservedRegion, other.mReservedRegion, kReservedRegionLen); + mTicketId = other.mTicketId; + mDeviceId = other.mDeviceId; + memcpy(mRightsId, other.mRightsId, kRightsIdLen); + mAccountId = other.mAccountId; + mSectTotalSize = other.mSectTotalSize; + mSectHeaderOffset = other.mSectHeaderOffset; + mSectNum = other.mSectNum; + mSectEntrySize = other.mSectEntrySize; + } +} + +void es::ETicketBody_V2::exportBinary() +{ + mBinaryBlob.alloc(sizeof(sTicketBody_v2)); + sTicketBody_v2* body = (sTicketBody_v2*)mBinaryBlob.getBytes(); + + body->set_format_version(kFormatVersion); + + body->set_issuer(mIssuer.c_str()); + body->set_enc_title_key(mEncTitleKey, kEncTitleKeyLen); + body->set_title_key_enc_type(mEncType); + body->set_ticket_version(mTicketVersion); + u8 property_mask = 0; + property_mask |= mPreInstall ? BIT(FLAG_PRE_INSTALL) : 0; + property_mask |= mSharedTitle ? BIT(FLAG_SHARED_TITLE) : 0; + property_mask |= mAllowAllContent ? BIT(FLAG_ALLOW_ALL_CONTENT) : 0; + body->set_property_mask(property_mask); + body->set_reserved_region(mReservedRegion, kReservedRegionLen); + body->set_ticket_id(mTicketId); + body->set_device_id(mDeviceId); + body->set_rights_id(mRightsId); + body->set_account_id(mAccountId); + body->set_sect_total_size(mSectTotalSize); + body->set_sect_header_offset(mSectHeaderOffset); + body->set_sect_num(mSectNum); + body->set_sect_entry_size(mSectEntrySize); +} + +void es::ETicketBody_V2::importBinary(const u8 * bytes, size_t len) +{ + if (len < sizeof(sTicketBody_v2)) + { + throw fnd::Exception(kModuleName, "Header size too small"); + } + + clear(); + + mBinaryBlob.alloc(sizeof(sTicketBody_v2)); + memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize()); + sTicketBody_v2* body = (sTicketBody_v2*)mBinaryBlob.getBytes(); + + if (body->format_version() != kFormatVersion) + { + throw fnd::Exception(kModuleName, "Unsupported format version"); + } + + mIssuer.append(body->issuer(), kIssuerLen); + memcpy(mEncTitleKey, body->enc_title_key(), kEncTitleKeyLen); + mEncType = (TitleKeyEncType)body->title_key_enc_type(); + mTicketVersion = body->ticket_version(); + mLicenseType = (LicenseType)body->license_type(); + mPreInstall = (body->property_mask() & BIT(FLAG_PRE_INSTALL)) == BIT(FLAG_PRE_INSTALL); + mSharedTitle = (body->property_mask() & BIT(FLAG_SHARED_TITLE)) == BIT(FLAG_SHARED_TITLE); + mAllowAllContent = (body->property_mask() & BIT(FLAG_ALLOW_ALL_CONTENT)) == BIT(FLAG_ALLOW_ALL_CONTENT); + memcpy(mReservedRegion, body->reserved_region(), kReservedRegionLen); + mTicketId = body->ticket_id(); + mDeviceId = body->device_id(); + memcpy(mRightsId, body->rights_id(), kRightsIdLen); + mAccountId = body->account_id(); + mSectTotalSize = body->sect_total_size(); + mSectHeaderOffset = body->sect_header_offset(); + mSectNum = body->sect_num(); + mSectEntrySize = body->sect_entry_size(); +} + +void es::ETicketBody_V2::clear() +{ + mBinaryBlob.clear(); + mIssuer.clear(); + memset(mEncTitleKey, 0, kEncTitleKeyLen); + mEncType = AES128_CBC; + mTicketVersion = 0; + mLicenseType = ES_LICENSE_PERMANENT; + mPreInstall = false; + mSharedTitle = false; + mAllowAllContent = false; + memset(mReservedRegion, 0, kReservedRegionLen); + mTicketId = 0; + mDeviceId = 0; + memset(mRightsId, 0, kRightsIdLen); + mAccountId = 0; + mSectTotalSize = 0; + mSectHeaderOffset = 0; + mSectNum = 0; + mSectEntrySize = 0; +} + +const std::string & es::ETicketBody_V2::getIssuer() const +{ + return mIssuer; +} + +void es::ETicketBody_V2::setIssuer(const std::string & issuer) +{ + if (issuer.length() > kIssuerLen) + { + throw fnd::Exception(kModuleName, "Issuer is too long"); + } + + mIssuer = issuer; +} + +const u8 * es::ETicketBody_V2::getEncTitleKey() const +{ + return mEncTitleKey; +} + +void es::ETicketBody_V2::setEncTitleKey(const u8 * data, size_t len) +{ + memset(mEncTitleKey, 0, kEncTitleKeyLen); + memcpy(mEncTitleKey, data, MIN(len, kEncTitleKeyLen)); +} + +es::ETicketBody_V2::TitleKeyEncType es::ETicketBody_V2::getTitleKeyEncType() const +{ + return mEncType; +} + +void es::ETicketBody_V2::setTitleKeyEncType(TitleKeyEncType type) +{ + mEncType = type; +} + +u16 es::ETicketBody_V2::getTicketVersion() const +{ + return mTicketVersion; +} + +void es::ETicketBody_V2::setTicketVersion(u16 version) +{ + mTicketVersion = version; +} + +es::ETicketBody_V2::LicenseType es::ETicketBody_V2::getLicenseType() const +{ + return mLicenseType; +} + +void es::ETicketBody_V2::setLicenseType(LicenseType type) +{ + mLicenseType = type; +} + +bool es::ETicketBody_V2::isPreInstall() const +{ + return mPreInstall; +} + +void es::ETicketBody_V2::setIsPreInstall(bool isPreInstall) +{ + mPreInstall = isPreInstall; +} + +bool es::ETicketBody_V2::isSharedTitle() const +{ + return mSharedTitle; +} + +void es::ETicketBody_V2::setIsSharedTitle(bool isSharedTitle) +{ + mSharedTitle = isSharedTitle; +} + +bool es::ETicketBody_V2::allowAllContent() const +{ + return mAllowAllContent; +} + +void es::ETicketBody_V2::setAllowAllContent(bool allowAllContent) +{ + mAllowAllContent = allowAllContent; +} + +const u8 * es::ETicketBody_V2::getReservedRegion() const +{ + return mReservedRegion; +} + +void es::ETicketBody_V2::setReservedRegion(const u8 * data, size_t len) +{ + memset(mReservedRegion, 0, kReservedRegionLen); + memcpy(mReservedRegion, data, MIN(len, kReservedRegionLen)); +} + +u64 es::ETicketBody_V2::getTicketId() const +{ + return mTicketId; +} + +void es::ETicketBody_V2::setTicketId(u64 id) +{ + mTicketId = id; +} + +u64 es::ETicketBody_V2::getDeviceId() const +{ + return mDeviceId; +} + +void es::ETicketBody_V2::setDeviceId(u64 id) +{ + mDeviceId = id; +} + +const u8 * es::ETicketBody_V2::getRightsId() const +{ + return mRightsId; +} + +void es::ETicketBody_V2::setRightsId(const u8 * id) +{ + memcpy(mRightsId, id, kRightsIdLen); +} + +u32 es::ETicketBody_V2::getAccountId() const +{ + return mAccountId; +} + +void es::ETicketBody_V2::setAccountId(u32 id) +{ + mAccountId = id; +} + +u32 es::ETicketBody_V2::getSectionTotalSize() const +{ + return mSectTotalSize; +} + +void es::ETicketBody_V2::setSectionTotalSize(u32 size) +{ + mSectTotalSize = size; +} + +u32 es::ETicketBody_V2::getSectionHeaderOffset() const +{ + return mSectHeaderOffset; +} + +void es::ETicketBody_V2::setSectionHeaderOffset(u32 offset) +{ + mSectHeaderOffset = offset; +} + +u16 es::ETicketBody_V2::getSectionNum() const +{ + return mSectNum; +} + +void es::ETicketBody_V2::setSectionNum(u16 num) +{ + mSectNum = num; +} + +u16 es::ETicketBody_V2::getSectionEntrySize() const +{ + return mSectEntrySize; +} + +void es::ETicketBody_V2::setSectionEntrySize(u16 size) +{ + mSectEntrySize = size; +} diff --git a/lib/es/ETicketBody_V2.h b/lib/es/ETicketBody_V2.h new file mode 100644 index 0000000..0b5d4ff --- /dev/null +++ b/lib/es/ETicketBody_V2.h @@ -0,0 +1,219 @@ +#pragma once +#include +#include +#include +#include + +namespace es +{ + class ETicketBody_V2 : + public fnd::ISerialiseableBinary + { + public: + enum TitleKeyEncType + { + AES128_CBC, + RSA2048 + }; + + enum LicenseType + { + ES_LICENSE_PERMANENT = 0, + ES_LICENSE_DEMO = 1, + ES_LICENSE_TRIAL = 2, + ES_LICENSE_RENTAL = 3, + ES_LICENSE_SUBSCRIPTION = 4, + ES_LICENSE_SERVICE = 5, + }; + + ETicketBody_V2(); + ETicketBody_V2(const ETicketBody_V2& other); + ETicketBody_V2(const u8* bytes, size_t len); + + bool operator==(const ETicketBody_V2& other) const; + bool operator!=(const ETicketBody_V2& other) const; + void operator=(const ETicketBody_V2& other); + + // to be used after export + const u8* getBytes() const; + size_t getSize() const; + + // export/import binary + virtual void exportBinary(); + virtual void importBinary(const u8* bytes, size_t len); + + // variables + virtual void clear(); + + const std::string& getIssuer() const; + void setIssuer(const std::string& issuer); + + const u8* getEncTitleKey() const; + void setEncTitleKey(const u8* data, size_t len); + + TitleKeyEncType getTitleKeyEncType() const; + void setTitleKeyEncType(TitleKeyEncType type); + + u16 getTicketVersion() const; + void setTicketVersion(u16 version); + + LicenseType getLicenseType() const; + void setLicenseType(LicenseType type); + + bool isPreInstall() const; + void setIsPreInstall(bool isPreInstall); + + bool isSharedTitle() const; + void setIsSharedTitle(bool isSharedTitle); + + bool allowAllContent() const; + void setAllowAllContent(bool allowAllContent); + + const u8* getReservedRegion() const; + void setReservedRegion(const u8* data, size_t len); + + u64 getTicketId() const; + void setTicketId(u64 id); + + u64 getDeviceId() const; + void setDeviceId(u64 id); + + const u8* getRightsId() const; + void setRightsId(const u8* id); + + u32 getAccountId() const; + void setAccountId(u32 id); + + u32 getSectionTotalSize() const; + void setSectionTotalSize(u32 size); + + u32 getSectionHeaderOffset() const; + void setSectionHeaderOffset(u32 offset); + + u16 getSectionNum() const; + void setSectionNum(u16 num); + + u16 getSectionEntrySize() const; + void setSectionEntrySize(u16 size); + + private: + const std::string kModuleName = "ES_ETICKET_BODY_V2"; + static const size_t kIssuerLen = 0x40; + static const u8 kFormatVersion = 2; + static const size_t kEncTitleKeyLen = crypto::rsa::kRsa2048Size; + static const size_t kReservedRegionLen = 8; + static const size_t kRightsIdLen = 16; + + enum PropertyMaskFlags + { + FLAG_PRE_INSTALL, + FLAG_SHARED_TITLE, + FLAG_ALLOW_ALL_CONTENT + }; + +#pragma pack (push, 1) + struct sTicketBody_v2 + { + private: + char issuer_[kIssuerLen]; + u8 enc_title_key_[kEncTitleKeyLen]; + u8 format_version_; + u8 title_key_enc_type_; + u16 ticket_version_; + u8 license_type_; + u8 common_key_id_; + u8 property_mask_; + u8 reserved_0_; + u8 reserved_region_[kReservedRegionLen]; // explicitly reserved + u64 ticket_id_; + u64 device_id_; + u8 rights_id_[kRightsIdLen]; + u32 account_id_; + u32 sect_total_size_; + u32 sect_header_offset_; + u16 sect_num_; + u16 sect_entry_size_; + public: + const char* issuer() const { return issuer_; } + void set_issuer(const char issuer[kIssuerLen]) { strncpy(issuer_, issuer, kIssuerLen); } + + const u8* enc_title_key() const { return enc_title_key_; } + void set_enc_title_key(const u8* enc_title_key, size_t len) { memset(enc_title_key_, 0, kEncTitleKeyLen); memcpy(enc_title_key_, enc_title_key, MIN(len, kEncTitleKeyLen)); } + + u8 format_version() const { return format_version_; } + void set_format_version(u8 version) { format_version_ = version; } + + u8 title_key_enc_type() const { return title_key_enc_type_; } + void set_title_key_enc_type(u8 type) { title_key_enc_type_ = type; } + + u16 ticket_version() const { return le_hword(ticket_version_); } + void set_ticket_version(u16 version) { ticket_version_ = le_hword(version); } + + u8 license_type() const { return license_type_; } + void set_license_type(u8 license_type) { license_type_ = license_type; } + + u8 common_key_id() const { return common_key_id_; } + void set_common_key_id(u8 common_key_id) { common_key_id_ = common_key_id; } + + u8 property_mask() const { return property_mask_; } + void set_property_mask(u8 mask) { property_mask_ = mask; } + + const u8* reserved_region() const { return reserved_region_; } + void set_reserved_region(const u8* reserved_region, size_t len) { memcpy(reserved_region_, reserved_region, MIN(len, kReservedRegionLen)); } + + u64 ticket_id() const { return le_dword(ticket_id_); } + void set_ticket_id(u64 ticket_id) { ticket_id_ = le_dword(ticket_id); } + + u64 device_id() const { return le_dword(device_id_); } + void set_device_id(u64 device_id) { device_id_ = le_dword(device_id); } + + const u8* rights_id() const { return rights_id_; } + void set_rights_id(const u8 rights_id[kRightsIdLen]) { memcpy(rights_id_, rights_id, kRightsIdLen); } + + u32 account_id() const { return le_word(account_id_); } + void set_account_id(u32 id) { account_id_ = le_word(id); } + + u32 sect_total_size() const { return le_word(sect_total_size_); } + void set_sect_total_size(u32 size) { sect_total_size_ = le_word(size); } + + u32 sect_header_offset() const { return le_word(sect_header_offset_); } + void set_sect_header_offset(u32 offset) { sect_header_offset_ = le_word(offset); } + + u16 sect_num() const { return le_hword(sect_num_); } + void set_sect_num(u16 num) { sect_num_ = num; } + + u16 sect_entry_size() const { return le_hword(sect_entry_size_); } + void set_sect_entry_size(u16 size) { sect_entry_size_ = le_hword(size); } + }; +#pragma pack (pop) + + // raw binary + fnd::MemoryBlob mBinaryBlob; + + // variables + std::string mIssuer; + u8 mEncTitleKey[kEncTitleKeyLen]; + TitleKeyEncType mEncType; // 0 = aes-cbc, 1 = rsa2048 + u16 mTicketVersion; + LicenseType mLicenseType; + u8 kCommonKeyId; + bool mPreInstall; + bool mSharedTitle; + bool mAllowAllContent; + u8 mReservedRegion[kReservedRegionLen]; // explicitly reserved + u64 mTicketId; + u64 mDeviceId; + u8 mRightsId[kRightsIdLen]; + u32 mAccountId; + u32 mSectTotalSize; + u32 mSectHeaderOffset; + u16 mSectNum; + u16 mSectEntrySize; + + // helpers + bool isEqual(const ETicketBody_V2& other) const; + void copyFrom(const ETicketBody_V2& other); + }; +} + + diff --git a/lib/es/ETicketContentRecord_V1.cpp b/lib/es/ETicketContentRecord_V1.cpp new file mode 100644 index 0000000..bb4dcfe --- /dev/null +++ b/lib/es/ETicketContentRecord_V1.cpp @@ -0,0 +1,6 @@ +#include "ETicketContentRecord_V1.h" + + + +es::ETicketContentRecord_V1::ETicketContentRecord_V1() +{} \ No newline at end of file diff --git a/lib/es/ETicketContentRecord_V1.h b/lib/es/ETicketContentRecord_V1.h new file mode 100644 index 0000000..c1a9533 --- /dev/null +++ b/lib/es/ETicketContentRecord_V1.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include + +namespace es +{ + class ETicketContentRecord_V1 : + public fnd::ISerialiseableBinary + { + public: + ETicketContentRecord_V1(); + + private: + +#pragma pack (push, 1) + struct sContentRecord_v1 + { + private: + static const size_t kAccessMaskSize = 0x80; + static const u16 kGroupMask = 0xFC00; + static const u16 kAccessMaskMask = 0x3FF; + + u32 group_; + u8 access_mask_[kAccessMaskSize]; + public: + u32 index_group() const { return be_word(group_); } + bool is_index_enabled(u16 index) const + { + return (index_group() == get_group(index)) \ + && ((access_mask_[get_access_mask(index) / 8] & BIT(get_access_mask(index) % 8)) != 0); + } + + void clear() { memset(this, 0, sizeof(sContentRecord_v1)); } + + void set_index_group(u16 index) { group_ = be_hword(get_group(index)); } + void enable_index(u16 index) { access_mask_[get_access_mask(index) / 8] |= BIT(get_access_mask(index) % 8); } + void disable_index(u16 index) { access_mask_[get_access_mask(index) / 8] &= ~BIT(get_access_mask(index) % 8); } + + inline u16 get_access_mask(u16 index) const { return index & kAccessMaskMask; } + inline u16 get_group(u16 index) const { return index & kGroupMask; } + }; +#pragma pack (pop) + }; +} \ No newline at end of file diff --git a/lib/es/ETicketSectionHeader_V2.cpp b/lib/es/ETicketSectionHeader_V2.cpp new file mode 100644 index 0000000..3ca5246 --- /dev/null +++ b/lib/es/ETicketSectionHeader_V2.cpp @@ -0,0 +1,161 @@ +#include "ETicketSectionHeader_V2.h" + + + +es::ETicketSectionHeader_V2::ETicketSectionHeader_V2() +{} + +es::ETicketSectionHeader_V2::ETicketSectionHeader_V2(const ETicketSectionHeader_V2 & other) +{ + copyFrom(other); +} + +es::ETicketSectionHeader_V2::ETicketSectionHeader_V2(const u8 * bytes, size_t len) +{ + importBinary(bytes, len); +} + +bool es::ETicketSectionHeader_V2::operator==(const ETicketSectionHeader_V2 & other) const +{ + return isEqual(other); +} + +bool es::ETicketSectionHeader_V2::operator!=(const ETicketSectionHeader_V2 & other) const +{ + return !isEqual(other); +} + +void es::ETicketSectionHeader_V2::operator=(const ETicketSectionHeader_V2 & other) +{ + copyFrom(other); +} + +const u8 * es::ETicketSectionHeader_V2::getBytes() const +{ + return mBinaryBlob.getBytes(); +} + +size_t es::ETicketSectionHeader_V2::getSize() const +{ + return mBinaryBlob.getSize(); +} + +void es::ETicketSectionHeader_V2::exportBinary() +{ + mBinaryBlob.alloc(sizeof(sSectionHeader_v2)); + sSectionHeader_v2* hdr = (sSectionHeader_v2*)mBinaryBlob.getBytes(); + + hdr->set_section_offset(mSectionOffset); + hdr->set_record_size(mRecordSize); + hdr->set_section_size(mSectionSize); + hdr->set_record_num(mRecordNum); + hdr->set_section_type(mSectionType); +} + +void es::ETicketSectionHeader_V2::importBinary(const u8 * bytes, size_t len) +{ + if (len < sizeof(sSectionHeader_v2)) + { + throw fnd::Exception(kModuleName, "Binary too small"); + } + + clear(); + + mBinaryBlob.alloc(sizeof(sSectionHeader_v2)); + memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize()); + sSectionHeader_v2* hdr = (sSectionHeader_v2*)mBinaryBlob.getBytes(); + + mSectionOffset = hdr->section_offset(); + mRecordSize = hdr->record_size(); + mSectionSize = hdr->section_size(); + mRecordNum = hdr->record_num(); + mSectionType = (SectionType)hdr->section_type(); +} + +bool es::ETicketSectionHeader_V2::isEqual(const ETicketSectionHeader_V2 & other) const +{ + return (mSectionOffset == other.mSectionOffset) \ + && (mRecordSize == other.mRecordSize) \ + && (mSectionSize == other.mSectionSize) \ + && (mRecordNum == other.mRecordNum) \ + && (mSectionType == other.mSectionType); +} + +void es::ETicketSectionHeader_V2::copyFrom(const ETicketSectionHeader_V2 & other) +{ + if (other.getSize()) + { + importBinary(other.getBytes(), other.getSize()); + } + else + { + mBinaryBlob.clear(); + mSectionOffset = other.mSectionOffset; + mRecordSize = other.mRecordSize; + mSectionSize = other.mSectionSize; + mRecordNum = other.mRecordNum; + mSectionType = other.mSectionType; + } +} + +void es::ETicketSectionHeader_V2::clear() +{ + mBinaryBlob.clear(); + mSectionOffset = 0; + mRecordSize = 0; + mSectionSize = 0; + mRecordNum = 0; + mSectionType = PERMANENT; +} + +u32 es::ETicketSectionHeader_V2::getSectionOffset() const +{ + return mSectionOffset; +} + +void es::ETicketSectionHeader_V2::setSectionOffset(u32 offset) +{ + mSectionOffset = offset; +} + +u32 es::ETicketSectionHeader_V2::getRecordSize() const +{ + return mRecordSize; +} + +void es::ETicketSectionHeader_V2::setRecordSize(u32 size) +{ + mRecordSize = size; +} + +u32 es::ETicketSectionHeader_V2::getSectionSize() const +{ + return mSectionSize; +} + +void es::ETicketSectionHeader_V2::getSectionSize(u32 size) +{ + mSectionSize = size; +} + +u16 es::ETicketSectionHeader_V2::getRecordNum() const +{ + return mRecordNum; +} + +void es::ETicketSectionHeader_V2::setRecordNum(u16 record_num) +{ + mRecordNum = record_num; +} + +es::ETicketSectionHeader_V2::SectionType es::ETicketSectionHeader_V2::getSectionType() const +{ + return mSectionType; +} + +void es::ETicketSectionHeader_V2::setSectionType(SectionType type) +{ + mSectionType = type; +} + + diff --git a/lib/es/ETicketSectionHeader_V2.h b/lib/es/ETicketSectionHeader_V2.h new file mode 100644 index 0000000..9447185 --- /dev/null +++ b/lib/es/ETicketSectionHeader_V2.h @@ -0,0 +1,101 @@ +#pragma once +#include +#include +#include + +namespace es +{ + class ETicketSectionHeader_V2 : + public fnd::ISerialiseableBinary + { + public: + enum SectionType + { + PERMANENT = 1, + SUBSCRIPTION = 2, + CONTENT = 3, + CONTENT_CONSUMPTION = 4, + ACCESS_TITLE = 5, + LIMITED_RESOURCE = 6, + }; + + ETicketSectionHeader_V2(); + ETicketSectionHeader_V2(const ETicketSectionHeader_V2& other); + ETicketSectionHeader_V2(const u8* bytes, size_t len); + + bool operator==(const ETicketSectionHeader_V2& other) const; + bool operator!=(const ETicketSectionHeader_V2& other) const; + void operator=(const ETicketSectionHeader_V2& other); + + // to be used after export + const u8* getBytes() const; + size_t getSize() const; + + // export/import binary + virtual void exportBinary(); + virtual void importBinary(const u8* bytes, size_t len); + + // variables + virtual void clear(); + + u32 getSectionOffset() const; + void setSectionOffset(u32 offset); + + u32 getRecordSize() const; + void setRecordSize(u32 size); + + u32 getSectionSize() const; + void getSectionSize(u32 size); + + u16 getRecordNum() const; + void setRecordNum(u16 record_num); + + SectionType getSectionType() const; + void setSectionType(SectionType type); + + private: + const std::string kModuleName = "ETICKET_SECTION_HEADER_V2"; +#pragma pack (push, 1) + struct sSectionHeader_v2 + { + private: + u32 section_offset_; + u32 record_size_; + u32 section_size_; + u16 record_num_; + u16 section_type_; + public: + u32 section_offset() const { return le_word(section_offset_); } + void set_section_offset(u32 offset) { section_offset_ = le_word(offset); } + + u32 record_size() const { return le_word(record_size_); } + void set_record_size(u32 size) { record_size_ = le_word(size); } + + u32 section_size() const { return le_word(section_size_); } + void set_section_size(u32 size) { section_size_ = le_word(size); } + + u16 record_num() const { return le_hword(record_num_); } + void set_record_num(u16 num) { record_num_ = le_hword(num); } + + u16 section_type() const { return le_hword(section_type_); } + void set_section_type(u16 type) { section_type_ = le_hword(type); } + }; +#pragma pack (pop) + + // raw binary + fnd::MemoryBlob mBinaryBlob; + + // variables + u32 mSectionOffset; + u32 mRecordSize; + u32 mSectionSize; + u16 mRecordNum; + SectionType mSectionType; + + // helpers + bool isEqual(const ETicketSectionHeader_V2& other) const; + void copyFrom(const ETicketSectionHeader_V2& other); + + }; + +} diff --git a/lib/es/es.vcxproj b/lib/es/es.vcxproj new file mode 100644 index 0000000..84be88b --- /dev/null +++ b/lib/es/es.vcxproj @@ -0,0 +1,133 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {7BE99936-0D40-410D-944B-4513C2EFF8DC} + es + 8.1 + libes + + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + ..\ + + + + + Level3 + Disabled + true + ..\ + + + + + Level3 + MaxSpeed + true + true + true + ..\ + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + ..\ + + + true + true + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/es/es.vcxproj.filters b/lib/es/es.vcxproj.filters new file mode 100644 index 0000000..73aad3f --- /dev/null +++ b/lib/es/es.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/lib/es/makefile b/lib/es/makefile new file mode 100644 index 0000000..cea813e --- /dev/null +++ b/lib/es/makefile @@ -0,0 +1,33 @@ +# Sources +SRC_DIR = . +OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) + +INC_DIR = .. +INCS = $(foreach dir,$(INC_DIR), -I"$(dir)/") + +# Compiler Settings +CXXFLAGS = -std=c++11 $(INCS) -D__STDC_FORMAT_MACROS -Wall -Wno-unused-but-set-variable -Wno-unused-value +ifeq ($(OS),Windows_NT) + # Windows Only Flags/Libs + CC = x86_64-w64-mingw32-gcc + CXX = x86_64-w64-mingw32-g++ + CFLAGS += + CXXFLAGS += +else + # *nix Only Flags/Libs + CFLAGS += + CXXFLAGS += +endif + +# Output +OUTPUT = ../lib$(shell basename $(CURDIR)).a + +main: build + +rebuild: clean build + +build: $(OBJS) + ar cr -o $(OUTPUT) $(OBJS) + +clean: + rm -rf $(OUTPUT) $(OBJS) \ No newline at end of file diff --git a/lib/nx/nx.vcxproj.filters b/lib/nx/nx.vcxproj.filters index db662da..594b901 100644 --- a/lib/nx/nx.vcxproj.filters +++ b/lib/nx/nx.vcxproj.filters @@ -204,4 +204,7 @@ Source Files + + + \ No newline at end of file diff --git a/programs/makefile b/programs/makefile index 564d602..14bea3d 100644 --- a/programs/makefile +++ b/programs/makefile @@ -1,4 +1,4 @@ -PROGS = ncatool npdmtool pfstool +PROGS = ncatool npdmtool pfstool tiktool main: build diff --git a/programs/tiktool/main.cpp b/programs/tiktool/main.cpp new file mode 100644 index 0000000..708e40a --- /dev/null +++ b/programs/tiktool/main.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc < 2) + { + printf("usage: tiktool \n"); + return 1; + } + + try + { + fnd::MemoryBlob file; + fnd::io::readFile(argv[1], file); + + // import + es::ETicketBody_V2 body; + body.importBinary(file.getBytes() + 0x140, file.getSize() - 0x140); + + printf("Issuer: %s\n", body.getIssuer().c_str()); + + + } catch (const fnd::Exception& e) + { + printf("%s\n", e.what()); + } + + return 0; +} \ No newline at end of file diff --git a/programs/tiktool/makefile b/programs/tiktool/makefile new file mode 100644 index 0000000..38dc36b --- /dev/null +++ b/programs/tiktool/makefile @@ -0,0 +1,39 @@ +# Sources +SRC_DIR = . +OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) + +#local dependencies +DEPENDS = es crypto fnd + +LIB_DIR = ../../lib + +LIBS = -L"$(LIB_DIR)" $(foreach dep,$(DEPENDS), -l"$(dep)") +INCS = -I"$(LIB_DIR)/" + +OUTPUT = ../../bin/$(shell basename $(CURDIR)) + +# Compiler Settings +CXXFLAGS = -std=c++11 $(INCS) -D__STDC_FORMAT_MACROS -Wall -Wno-unused-but-set-variable -Wno-unused-value +ifeq ($(OS),Windows_NT) + # Windows Only Flags/Libs + CC = x86_64-w64-mingw32-gcc + CXX = x86_64-w64-mingw32-g++ + CFLAGS += + CXXFLAGS += + LIBS += -static +else + # *nix Only Flags/Libs + CFLAGS += + CXXFLAGS += + LIBS += +endif + +all: build + +rebuild: clean build + +build: $(OBJS) + $(CXX) $(OBJS) $(LIBS) -o $(OUTPUT) + +clean: + rm -rf $(OBJS) $(OUTPUT) \ No newline at end of file diff --git a/programs/tiktool/tiktool.vcxproj b/programs/tiktool/tiktool.vcxproj new file mode 100644 index 0000000..29fba34 --- /dev/null +++ b/programs/tiktool/tiktool.vcxproj @@ -0,0 +1,125 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {2200B834-F15A-4C6E-9DDB-6012B9A5C246} + tiktool + 8.1 + + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + ..\..\lib + + + + + Level3 + Disabled + true + ..\..\lib + + + + + Level3 + MaxSpeed + true + true + true + ..\..\lib + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + ..\..\lib + + + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/programs/tiktool/tiktool.vcxproj.filters b/programs/tiktool/tiktool.vcxproj.filters new file mode 100644 index 0000000..61ac64b --- /dev/null +++ b/programs/tiktool/tiktool.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + + \ No newline at end of file