diff --git a/lib/libhac/include/nn/hac/AccessControlInfoDesc.h b/lib/libhac/include/nn/hac/AccessControlInfoDesc.h index 18c9c85..5b8d34e 100644 --- a/lib/libhac/include/nn/hac/AccessControlInfoDesc.h +++ b/lib/libhac/include/nn/hac/AccessControlInfoDesc.h @@ -57,8 +57,8 @@ namespace hac // variables void clear(); - const fnd::rsa::sRsa2048Key& getNcaHeaderSignature2Key() const; - void setNcaHeaderSignature2Key(const fnd::rsa::sRsa2048Key& key); + const fnd::rsa::sRsa2048Key& getContentArchiveHeaderSignature2Key() const; + void setContentArchiveHeaderSignature2Key(const fnd::rsa::sRsa2048Key& key); const fnd::List& getFlagList() const; void setFlagList(const fnd::List& flags); @@ -81,7 +81,7 @@ namespace hac fnd::Vec mRawBinary; // variables - fnd::rsa::sRsa2048Key mNcaHeaderSignature2Key; + fnd::rsa::sRsa2048Key mContentArchiveHeaderSignature2Key; fnd::List mFlags; sProgramIdRestrict mProgramIdRestrict; nn::hac::FileSystemAccessControl mFileSystemAccessControl; diff --git a/lib/libhac/include/nn/hac/NcaHeader.h b/lib/libhac/include/nn/hac/ContentArchiveHeader.h similarity index 55% rename from lib/libhac/include/nn/hac/NcaHeader.h rename to lib/libhac/include/nn/hac/ContentArchiveHeader.h index bc066ef..e071b99 100644 --- a/lib/libhac/include/nn/hac/NcaHeader.h +++ b/lib/libhac/include/nn/hac/ContentArchiveHeader.h @@ -7,53 +7,47 @@ namespace nn { namespace hac { - class NcaHeader : + class ContentArchiveHeader : public fnd::IByteModel { public: - enum FormatVersion + struct sPartitionEntry { - NCA2_FORMAT, - NCA3_FORMAT - }; - - struct sPartition - { - byte_t index; + byte_t header_index; uint64_t offset; 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; size = other.size; - hash = other.hash; + fs_header_hash = other.fs_header_hash; 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) \ && (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); } }; - NcaHeader(); - NcaHeader(const NcaHeader& other); + ContentArchiveHeader(); + ContentArchiveHeader(const ContentArchiveHeader& other); - void operator=(const NcaHeader& other); - bool operator==(const NcaHeader& other) const; - bool operator!=(const NcaHeader& other) const; + void operator=(const ContentArchiveHeader& other); + bool operator==(const ContentArchiveHeader& other) const; + bool operator!=(const ContentArchiveHeader& other) const; // IByteModel void toBytes(); @@ -62,40 +56,52 @@ namespace hac // variables void clear(); - FormatVersion getFormatVersion() const; - void setFormatVersion(FormatVersion ver); + + byte_t getFormatVersion() const; + void setFormatVersion(byte_t ver); + nca::DistributionType getDistributionType() const; void setDistributionType(nca::DistributionType type); + nca::ContentType getContentType() const; void setContentType(nca::ContentType type); + byte_t getKeyGeneration() const; 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; void setContentSize(uint64_t size); + uint64_t getProgramId() const; void setProgramId(uint64_t program_id); + uint32_t getContentIndex() const; void setContentIndex(uint32_t index); + uint32_t getSdkAddonVersion() const; void setSdkAddonVersion(uint32_t version); + bool hasRightsId() const; const byte_t* getRightsId() const; void setRightsId(const byte_t* rights_id); - const fnd::List& getPartitions() const; - void setPartitions(const fnd::List& partitions); - const fnd::List& getEncAesKeys() const; - void setEncAesKeys(const fnd::List& keys); + + const fnd::List& getPartitionEntryList() const; + void setPartitionEntryList(const fnd::List& partition_entry_list); + + const byte_t* getKeyArea() const; + void setKeyArea(const byte_t* key_area); private: - const std::string kModuleName = "NCA_HEADER"; + const std::string kModuleName = "CONTENT_ARCHIVE_HEADER"; // binary fnd::Vec mRawBinary; // data - FormatVersion mFormatVersion; + byte_t mFormatVersion; nca::DistributionType mDistributionType; nca::ContentType mContentType; byte_t mKeyGeneration; @@ -104,9 +110,9 @@ namespace hac uint64_t mProgramId; uint32_t mContentIndex; uint32_t mSdkAddonVersion; - byte_t mRightsId[nca::kRightsIdLen]; - fnd::List mPartitions; - fnd::List mEncAesKeys; + fnd::Vec mRightsId; + fnd::List mPartitionEntryList; + fnd::Vec mKeyArea; uint64_t blockNumToSize(uint32_t block_num) const; uint32_t sizeToBlockNum(uint64_t real_size) const; diff --git a/lib/libhac/include/nn/hac/NcaUtils.h b/lib/libhac/include/nn/hac/NcaUtils.h index 44eb6bb..b5ebacc 100644 --- a/lib/libhac/include/nn/hac/NcaUtils.h +++ b/lib/libhac/include/nn/hac/NcaUtils.h @@ -9,7 +9,7 @@ namespace hac { public: 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 void getNcaPartitionAesCtr(const nn::hac::sNcaFsHeader* hdr, byte_t* ctr); }; diff --git a/lib/libhac/include/nn/hac/define/nca.h b/lib/libhac/include/nn/hac/define/nca.h index a27a46c..3f20cca 100644 --- a/lib/libhac/include/nn/hac/define/nca.h +++ b/lib/libhac/include/nn/hac/define/nca.h @@ -17,17 +17,24 @@ namespace hac static const size_t kPartitionNum = 4; static const size_t kHeaderSectorNum = 6; static const size_t kHeaderSize = kSectorSize * kHeaderSectorNum; - static const size_t kAesKeyNum = 16; 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 kFsHeaderHashSuperblockLen = 0x138; static const uint16_t kDefaultFsHeaderVersion = 2; - enum ProgramPartitionId + enum HeaderFormatVersion { - PARTITION_CODE, - PARTITION_DATA, - PARTITION_LOGO, + FORMAT_NCA2 = 2, + FORMAT_NCA3 = 3 + }; + + enum ProgramContentPartitionIndex + { + PARTITION_CODE = 0, + PARTITION_DATA = 1, + PARTITION_LOGO = 2, }; enum DistributionType @@ -83,11 +90,11 @@ namespace hac CRYPT_AESXTS, CRYPT_AESCTR, CRYPT_AESCTREX - }; + }; } #pragma pack(push,1) - struct sNcaHeader + struct sContentArchiveHeader { le_uint32_t st_magic; byte_t distribution_type; @@ -101,15 +108,15 @@ namespace hac byte_t key_generation_2; byte_t reserved_2[0xf]; byte_t rights_id[nca::kRightsIdLen]; - struct sNcaSection + struct sPartitionEntry { - le_uint32_t start; // block units - le_uint32_t end; // block units + le_uint32_t start_blk; // block units + le_uint32_t end_blk; // block units byte_t enabled; byte_t reserved[7]; - } partition[nca::kPartitionNum]; - fnd::sha::sSha256Hash partition_hash[nca::kPartitionNum]; - fnd::aes::sAes128Key enc_aes_key[nca::kAesKeyNum]; + } partition_entry[nca::kPartitionNum]; + fnd::sha::sSha256Hash fs_header_hash[nca::kPartitionNum]; + byte_t key_area[nca::kKeyAreaSize]; }; struct sNcaFsHeader @@ -124,11 +131,11 @@ namespace hac byte_t reserved_1[0xB8]; }; - struct sNcaHeaderBlock + struct sContentArchiveHeaderBlock { byte_t signature_main[fnd::rsa::kRsa2048Size]; byte_t signature_acid[fnd::rsa::kRsa2048Size]; - sNcaHeader header; + sContentArchiveHeader header; sNcaFsHeader fs_header[nn::hac::nca::kPartitionNum]; }; diff --git a/lib/libhac/libhac.vcxproj b/lib/libhac/libhac.vcxproj index 1c69043..f867187 100644 --- a/lib/libhac/libhac.vcxproj +++ b/lib/libhac/libhac.vcxproj @@ -64,7 +64,7 @@ - + @@ -117,7 +117,7 @@ - + diff --git a/lib/libhac/libhac.vcxproj.filters b/lib/libhac/libhac.vcxproj.filters index 1aca3c6..116821c 100644 --- a/lib/libhac/libhac.vcxproj.filters +++ b/lib/libhac/libhac.vcxproj.filters @@ -144,7 +144,7 @@ Header Files - + Header Files @@ -293,7 +293,7 @@ Source Files - + Source Files diff --git a/lib/libhac/source/AccessControlInfoDesc.cpp b/lib/libhac/source/AccessControlInfoDesc.cpp index cfa5f7e..56031e1 100644 --- a/lib/libhac/source/AccessControlInfoDesc.cpp +++ b/lib/libhac/source/AccessControlInfoDesc.cpp @@ -13,7 +13,7 @@ nn::hac::AccessControlInfoDesc::AccessControlInfoDesc(const AccessControlInfoDes void nn::hac::AccessControlInfoDesc::operator=(const AccessControlInfoDesc & other) { mRawBinary = other.mRawBinary; - mNcaHeaderSignature2Key = other.mNcaHeaderSignature2Key; + mContentArchiveHeaderSignature2Key = other.mContentArchiveHeaderSignature2Key; mFlags = other.mFlags; mProgramIdRestrict = other.mProgramIdRestrict; mFileSystemAccessControl = other.mFileSystemAccessControl; @@ -23,7 +23,7 @@ void nn::hac::AccessControlInfoDesc::operator=(const AccessControlInfoDesc & oth bool nn::hac::AccessControlInfoDesc::operator==(const AccessControlInfoDesc & other) const { - return (mNcaHeaderSignature2Key == other.mNcaHeaderSignature2Key) \ + return (mContentArchiveHeaderSignature2Key == other.mContentArchiveHeaderSignature2Key) \ && (mFlags == other.mFlags) \ && (mProgramIdRestrict == other.mProgramIdRestrict) \ && (mFileSystemAccessControl == other.mFileSystemAccessControl) \ @@ -62,7 +62,7 @@ void nn::hac::AccessControlInfoDesc::toBytes() sAciDescHeader* hdr = (sAciDescHeader*)mRawBinary.data(); // 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 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()); // 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++) { @@ -181,7 +181,7 @@ void nn::hac::AccessControlInfoDesc::validateSignature(const fnd::rsa::sRsa2048K void nn::hac::AccessControlInfoDesc::clear() { mRawBinary.clear(); - memset((void*)&mNcaHeaderSignature2Key, 0, sizeof(mNcaHeaderSignature2Key)); + memset((void*)&mContentArchiveHeaderSignature2Key, 0, sizeof(mContentArchiveHeaderSignature2Key)); mFlags.clear(); mProgramIdRestrict.min = 0; mProgramIdRestrict.max = 0; @@ -190,14 +190,14 @@ void nn::hac::AccessControlInfoDesc::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::AccessControlInfoDesc::getFlagList() const diff --git a/lib/libhac/source/ContentArchiveHeader.cpp b/lib/libhac/source/ContentArchiveHeader.cpp new file mode 100644 index 0000000..0660773 --- /dev/null +++ b/lib/libhac/source/ContentArchiveHeader.cpp @@ -0,0 +1,334 @@ +#include + +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& 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::getPartitionEntryList() const +{ + return mPartitionEntryList; +} + +void nn::hac::ContentArchiveHeader::setPartitionEntryList(const fnd::List& 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); +} \ No newline at end of file diff --git a/lib/libhac/source/NcaHeader.cpp b/lib/libhac/source/NcaHeader.cpp deleted file mode 100644 index 12edd1d..0000000 --- a/lib/libhac/source/NcaHeader.cpp +++ /dev/null @@ -1,327 +0,0 @@ -#include - -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& 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::getPartitions() const -{ - return mPartitions; -} - -void nn::hac::NcaHeader::setPartitions(const fnd::List& partitions) -{ - mPartitions = partitions; - if (mPartitions.size() >= nca::kPartitionNum) - { - throw fnd::Exception(kModuleName, "Too many NCA partitions"); - } -} - -const fnd::List& nn::hac::NcaHeader::getEncAesKeys() const -{ - return mEncAesKeys; -} - -void nn::hac::NcaHeader::setEncAesKeys(const fnd::List& 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); -} \ No newline at end of file diff --git a/lib/libhac/source/NcaUtils.cpp b/lib/libhac/source/NcaUtils.cpp index 9b71878..9ad1576 100644 --- a/lib/libhac/source/NcaUtils.cpp +++ b/lib/libhac/source/NcaUtils.cpp @@ -1,6 +1,6 @@ #include -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]; @@ -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::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 for (size_t i = 0; i < nn::hac::nca::kHeaderSectorNum; i++) diff --git a/programs/nstool/source/KeyConfiguration.cpp b/programs/nstool/source/KeyConfiguration.cpp index 91c1122..7a02116 100644 --- a/programs/nstool/source/KeyConfiguration.cpp +++ b/programs/nstool/source/KeyConfiguration.cpp @@ -19,10 +19,10 @@ void KeyConfiguration::operator=(const KeyConfiguration& other) { mAcidSignKey = other.mAcidSignKey; mPkg2SignKey = other.mPkg2SignKey; - mNcaHeader0SignKey = other.mNcaHeader0SignKey; + mContentArchiveHeader0SignKey = other.mContentArchiveHeader0SignKey; mXciHeaderSignKey = other.mXciHeaderSignKey; - mNcaHeaderKey = other.mNcaHeaderKey; + mContentArchiveHeaderKey = other.mContentArchiveHeaderKey; mXciHeaderKey = other.mXciHeaderKey; for (size_t i = 0; i < kMasterKeyNum; i++) @@ -114,14 +114,14 @@ void KeyConfiguration::importHactoolGenericKeyfile(const std::string& path) } // 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 _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kKeyStr), mXciHeaderKey.key, 0x10); // store rsa keys - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[nameidx], kRsaKeyPrivate), mNcaHeader0SignKey.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], kRsaKeyPrivate), mContentArchiveHeader0SignKey.priv_exponent, 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], 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 (mNcaHeaderKey == kNullAesXtsKey) + if (mContentArchiveHeaderKey == kNullAesXtsKey) { 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(mNcaHeaderKey.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[0], nca_header_key_source.key[0], 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; mPkg2SignKey = kNullRsa2048Key; - mNcaHeader0SignKey = kNullRsa2048Key; + mContentArchiveHeader0SignKey = kNullRsa2048Key; mXciHeaderSignKey = kNullRsa2048Key; mPkiRootKeyList.clear(); - mNcaHeaderKey = kNullAesXtsKey; + mContentArchiveHeaderKey = kNullAesXtsKey; mXciHeaderKey = kNullAesKey; for (size_t i = 0; i < kMasterKeyNum; i++) @@ -221,14 +221,14 @@ void KeyConfiguration::clearNcaExternalKeys() 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 diff --git a/programs/nstool/source/KeyConfiguration.h b/programs/nstool/source/KeyConfiguration.h index 258dfbb..cbf2060 100644 --- a/programs/nstool/source/KeyConfiguration.h +++ b/programs/nstool/source/KeyConfiguration.h @@ -24,8 +24,8 @@ public: void clearNcaExternalKeys(); // nca keys - bool getNcaHeaderKey(fnd::aes::sAesXts128Key& key) const; - bool getNcaHeader0SignKey(fnd::rsa::sRsa2048Key& key) const; + bool getContentArchiveHeaderKey(fnd::aes::sAesXts128Key& key) const; + bool getContentArchiveHeader0SignKey(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 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 kPkg2Base[kNameVariantNum] = { "package2", "package2", "package2" }; 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 kPkiRootBase[kNameVariantNum] = { "pki_root", "pki_root", "pki_root" }; const std::string kTicketCommonKeyBase[kNameVariantNum] = { "ticket_commonkey", "titlekek", "ticket_commonkey" }; @@ -175,8 +175,8 @@ private: fnd::aes::sAes128Key mPkg2Key[kMasterKeyNum]; // nca - fnd::rsa::sRsa2048Key mNcaHeader0SignKey; - fnd::aes::sAesXts128Key mNcaHeaderKey; + fnd::rsa::sRsa2048Key mContentArchiveHeader0SignKey; + fnd::aes::sAesXts128Key mContentArchiveHeaderKey; fnd::aes::sAes128Key mNcaKeyAreaEncryptionKey[kNcaKeakNum][kMasterKeyNum]; fnd::aes::sAes128Key mNcaKeyAreaEncryptionKeyHw[kNcaKeakNum][kMasterKeyNum]; diff --git a/programs/nstool/source/NcaProcess.cpp b/programs/nstool/source/NcaProcess.cpp index 465c965..e5112f9 100644 --- a/programs/nstool/source/NcaProcess.cpp +++ b/programs/nstool/source/NcaProcess.cpp @@ -107,18 +107,18 @@ void NcaProcess::importHeader() } // 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 fnd::aes::sAesXts128Key header_key; - mKeyCfg.getNcaHeaderKey(header_key); - nn::hac::NcaUtils::decryptNcaHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, header_key); + mKeyCfg.getContentArchiveHeaderKey(header_key); + nn::hac::NcaUtils::decryptContentArchiveHeader((byte_t*)&mHdrBlock, (byte_t*)&mHdrBlock, header_key); // 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 - mHdr.fromBytes((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sNcaHeader)); + mHdr.fromBytes((byte_t*)&mHdrBlock.header, sizeof(nn::hac::sContentArchiveHeader)); } void NcaProcess::generateNcaBodyEncryptionKeys() @@ -129,25 +129,27 @@ void NcaProcess::generateNcaBodyEncryptionKeys() // get key data from header 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 sKeys::sKeyAreaKey kak; 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.enc = mHdr.getEncAesKeys()[i]; + kak.enc = key_area[i]; // key[0-3] if (i < 4 && mKeyCfg.getNcaKeyAreaEncryptionKey(masterkey_rev, keak_index, key_area_enc_key) == true) { kak.decrypted = true; nn::hac::AesKeygen::generateKey(kak.dec.key, kak.enc.key, key_area_enc_key.key); } - // key[4] - else if (i == 4 && mKeyCfg.getNcaKeyAreaEncryptionKeyHw(masterkey_rev, keak_index, key_area_enc_key) == true) + // key[KEY_AESCTR_HW] + else if (i == nn::hac::nca::KEY_AESCTR_HW && mKeyCfg.getNcaKeyAreaEncryptionKeyHw(masterkey_rev, keak_index, key_area_enc_key) == true) { kak.decrypted = true; 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; - 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 - const nn::hac::NcaHeader::sPartition& partition = mHdr.getPartitions()[i]; - nn::hac::sNcaFsHeader& fs_header = mHdrBlock.fs_header[partition.index]; + const nn::hac::ContentArchiveHeader::sPartitionEntry& partition = mHdr.getPartitionEntryList()[i]; + nn::hac::sNcaFsHeader& fs_header = mHdrBlock.fs_header[partition.header_index]; // output structure - sPartitionInfo& info = mPartitions[partition.index]; + sPartitionInfo& info = mPartitions[partition.header_index]; // validate header hash - fnd::sha::sSha256Hash calc_hash; - fnd::sha::Sha256((const byte_t*)&mHdrBlock.fs_header[partition.index], sizeof(nn::hac::sNcaFsHeader), calc_hash.bytes); - if (calc_hash.compare(partition.hash) == false) + fnd::sha::sSha256Hash fs_header_hash; + fnd::sha::Sha256((const byte_t*)&mHdrBlock.fs_header[partition.header_index], sizeof(nn::hac::sNcaFsHeader), fs_header_hash.bytes); + if (fs_header_hash.compare(partition.fs_header_hash) == false) { 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()); } @@ -246,7 +248,7 @@ void NcaProcess::generatePartitionConfiguration() if (fs_header.version.get() != nn::hac::nca::kDefaultFsHeaderVersion) { 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()); } @@ -387,7 +389,7 @@ void NcaProcess::validateNcaSignatures() { // validate signature[0] 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) { std::cout << "[WARNING] NCA Header Main Signature: FAIL" << std::endl; @@ -415,7 +417,7 @@ void NcaProcess::validateNcaSignatures() npdm.setCliOutputMode(0); 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; } @@ -445,7 +447,7 @@ void NcaProcess::displayHeader() std::cout << " Dist. Type: " << getDistributionTypeStr(mHdr.getDistributionType()) << 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 << " 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 << " ProgID: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getProgramId() << 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)) { 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]; + if (info.size == 0) continue; std::cout << " " << std::dec << index << ":" << std::endl; std::cout << " Offset: 0x" << std::hex << (uint64_t)info.offset << std::endl; @@ -546,9 +549,9 @@ void NcaProcess::displayHeader() 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]; // 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; switch (format_ver) { - case (nn::hac::NcaHeader::NCA2_FORMAT): + case (nn::hac::nca::FORMAT_NCA2): str = "NCA2"; break; - case (nn::hac::NcaHeader::NCA3_FORMAT): + case (nn::hac::nca::FORMAT_NCA3): str = "NCA3"; break; default: @@ -797,7 +800,7 @@ const char* NcaProcess::getContentTypeForMountStr(nn::hac::nca::ContentType cont str = "data"; break; case (nn::hac::nca::TYPE_PUBLIC_DATA): - str = "publicData"; + str = "publicdata"; break; default: str = ""; diff --git a/programs/nstool/source/NcaProcess.h b/programs/nstool/source/NcaProcess.h index e87a408..2b9ba2e 100644 --- a/programs/nstool/source/NcaProcess.h +++ b/programs/nstool/source/NcaProcess.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "KeyConfiguration.h" @@ -49,9 +49,9 @@ private: bool mListFs; // data - nn::hac::sNcaHeaderBlock mHdrBlock; + nn::hac::sContentArchiveHeaderBlock mHdrBlock; fnd::sha::sSha256Hash mHdrHash; - nn::hac::NcaHeader mHdr; + nn::hac::ContentArchiveHeader mHdr; // crypto struct sKeys @@ -111,7 +111,7 @@ private: void displayHeader(); 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* getContentTypeStr(nn::hac::nca::ContentType cont_type) const; const char* getEncryptionTypeStr(nn::hac::nca::EncryptionType enc_type) const; diff --git a/programs/nstool/source/PfsProcess.cpp b/programs/nstool/source/PfsProcess.cpp index 999b487..1a9776c 100644 --- a/programs/nstool/source/PfsProcess.cpp +++ b/programs/nstool/source/PfsProcess.cpp @@ -4,6 +4,8 @@ #include #include "PfsProcess.h" +#include + PfsProcess::PfsProcess() : mFile(), mCliOutputMode(_BIT(OUTPUT_BASIC)), @@ -80,6 +82,8 @@ void PfsProcess::importHeader() // open minimum header to get full header size scratch.alloc(sizeof(nn::hac::sPfsHeader)); (*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) { throw fnd::Exception(kModuleName, "Corrupt Header"); diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index 8e7ae78..13e932b 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -701,14 +701,14 @@ bool UserSettings::determineValidNcaFromSample(const fnd::Vec& sample) c { // prepare decrypted NCA data 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) return false; fnd::aes::sAesXts128Key header_key; - mKeyCfg.getNcaHeaderKey(header_key); - nn::hac::NcaUtils::decryptNcaHeader(sample.data(), nca_raw, header_key); + mKeyCfg.getContentArchiveHeaderKey(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) return false; @@ -882,9 +882,9 @@ void UserSettings::dumpKeyConfig() const std::cout << "[KeyConfiguration]" << 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); - if (mKeyCfg.getNcaHeaderKey(aesxts_key) == true) + if (mKeyCfg.getContentArchiveHeaderKey(aesxts_key) == true) dumpAesXtsKey(aesxts_key, "Header Encryption Key", 2); for (size_t i = 0; i < kMasterKeyNum; i++)