mirror of
https://github.com/jakcron/nstool.git
synced 2024-12-22 18:55:29 +00:00
[nx|ncatool] Take out NCA constants and structures to separate file. Add support for NCA key area index 5.
This commit is contained in:
parent
dec7c50cc4
commit
fd9261b789
|
@ -1,10 +1,7 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <fnd/types.h>
|
||||
#include <nx/nca.h>
|
||||
#include <fnd/MemoryBlob.h>
|
||||
#include <fnd/List.h>
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <fnd/ISerialiseableBinary.h>
|
||||
|
||||
namespace nx
|
||||
|
@ -19,37 +16,16 @@ namespace nx
|
|||
NCA3_FORMAT
|
||||
};
|
||||
|
||||
enum DistributionType
|
||||
{
|
||||
DIST_DOWNLOAD,
|
||||
DIST_GAME_CARD
|
||||
};
|
||||
|
||||
enum ContentType
|
||||
{
|
||||
TYPE_PROGRAM,
|
||||
TYPE_META,
|
||||
TYPE_CONTROL,
|
||||
TYPE_MANUAL,
|
||||
TYPE_DATA,
|
||||
};
|
||||
|
||||
enum KeyBankIndex
|
||||
{
|
||||
KEY_AESXTS_0,
|
||||
KEY_AESXTS_1,
|
||||
KEY_AESCTR,
|
||||
KEY_UNUSED_3,
|
||||
};
|
||||
|
||||
struct sSection
|
||||
struct sPartition
|
||||
{
|
||||
byte_t index;
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
crypto::sha::sSha256Hash hash;
|
||||
|
||||
const sSection& operator=(const sSection& other)
|
||||
const sPartition& operator=(const sPartition& other)
|
||||
{
|
||||
index = other.index;
|
||||
offset = other.offset;
|
||||
size = other.size;
|
||||
hash = other.hash;
|
||||
|
@ -57,21 +33,20 @@ namespace nx
|
|||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const sSection& other) const
|
||||
bool operator==(const sPartition& other) const
|
||||
{
|
||||
return (offset == other.offset) \
|
||||
return (index == other.index) \
|
||||
&& (offset == other.offset) \
|
||||
&& (size == other.size) \
|
||||
&& (hash == other.hash);
|
||||
}
|
||||
|
||||
bool operator!=(const sSection& other) const
|
||||
bool operator!=(const sPartition& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
};
|
||||
|
||||
static const size_t kBlockSize = 0x200;
|
||||
|
||||
NcaHeader();
|
||||
NcaHeader(const NcaHeader& other);
|
||||
NcaHeader(const byte_t* bytes, size_t len);
|
||||
|
@ -92,85 +67,56 @@ namespace nx
|
|||
void clear();
|
||||
FormatVersion getFormatVersion() const;
|
||||
void setFormatVersion(FormatVersion ver);
|
||||
DistributionType getDistributionType() const;
|
||||
void setDistributionType(DistributionType type);
|
||||
ContentType getContentType() const;
|
||||
void setContentType(ContentType type);
|
||||
byte_t getCryptoType() const;
|
||||
void setCryptoType(byte_t type);
|
||||
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);
|
||||
uint64_t getNcaSize() const;
|
||||
void setNcaSize(uint64_t size);
|
||||
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);
|
||||
const fnd::List<sSection>& getSections() const;
|
||||
void addSection(const sSection& section);
|
||||
const byte_t* getRightsId() const;
|
||||
void setRightsId(const byte_t* rights_id);
|
||||
const fnd::List<sPartition>& getPartitions() const;
|
||||
void setPartitions(const fnd::List<sPartition>& partitions);
|
||||
const fnd::List<crypto::aes::sAes128Key>& getEncAesKeys() const;
|
||||
void addEncAesKey(const crypto::aes::sAes128Key& key);
|
||||
void setEncAesKeys(const fnd::List<crypto::aes::sAes128Key>& keys);
|
||||
|
||||
private:
|
||||
const std::string kModuleName = "NCA_HEADER";
|
||||
const std::string kNca2Sig = "NCA2";
|
||||
const std::string kNca3Sig = "NCA3";
|
||||
static const size_t kSectionNum = 4;
|
||||
static const size_t kAesKeyNum = 4;
|
||||
static const uint32_t kDefaultSdkAddonVersion = 721920;
|
||||
|
||||
//static const uint32_t kDefaultSdkAddonVersion = 721920;
|
||||
|
||||
enum ProgramPartitionId
|
||||
{
|
||||
SECTION_CODE,
|
||||
SECTION_DATA,
|
||||
SECTION_LOGO,
|
||||
PARTITION_CODE,
|
||||
PARTITION_DATA,
|
||||
PARTITION_LOGO,
|
||||
};
|
||||
|
||||
#pragma pack (push, 1)
|
||||
|
||||
struct sNcaHeader
|
||||
{
|
||||
char signature[4];
|
||||
byte_t distribution_type;
|
||||
byte_t content_type;
|
||||
byte_t crypto_type; // KeyGeneration
|
||||
byte_t key_area_encryption_key_index;
|
||||
le_uint64_t nca_size;
|
||||
le_uint64_t program_id;
|
||||
le_uint32_t content_index;
|
||||
le_uint32_t sdk_addon_version;
|
||||
byte_t crypto_type_2;
|
||||
byte_t reserved_2[0xf];
|
||||
byte_t rights_id[0x10];
|
||||
struct sNcaSection
|
||||
{
|
||||
le_uint32_t start; // block units
|
||||
le_uint32_t end; // block units
|
||||
byte_t enabled;
|
||||
byte_t reserved[7];
|
||||
} section[kSectionNum];
|
||||
crypto::sha::sSha256Hash section_hash[kSectionNum];
|
||||
crypto::aes::sAes128Key enc_aes_key[kAesKeyNum];
|
||||
|
||||
};
|
||||
#pragma pack (pop)
|
||||
|
||||
// binary
|
||||
fnd::MemoryBlob mBinaryBlob;
|
||||
|
||||
// data
|
||||
FormatVersion mFormatVersion;
|
||||
DistributionType mDistributionType;
|
||||
ContentType mContentType;
|
||||
byte_t mCryptoType;
|
||||
nca::DistributionType mDistributionType;
|
||||
nca::ContentType mContentType;
|
||||
byte_t mKeyGeneration;
|
||||
byte_t mKaekIndex;
|
||||
uint64_t mNcaSize;
|
||||
uint64_t mContentSize;
|
||||
uint64_t mProgramId;
|
||||
uint32_t mContentIndex;
|
||||
uint32_t mSdkAddonVersion;
|
||||
fnd::List<sSection> mSections;
|
||||
byte_t mRightsId[nca::kRightsIdLen];
|
||||
fnd::List<sPartition> mPartitions;
|
||||
fnd::List<crypto::aes::sAes128Key> mEncAesKeys;
|
||||
|
||||
uint64_t blockNumToSize(uint32_t block_num) const;
|
||||
|
|
141
lib/libnx/include/nx/nca.h
Normal file
141
lib/libnx/include/nx/nca.h
Normal file
|
@ -0,0 +1,141 @@
|
|||
#include <string>
|
||||
#include <fnd/types.h>
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <fnd/ISerialiseableBinary.h>
|
||||
|
||||
namespace nx
|
||||
{
|
||||
namespace nca
|
||||
{
|
||||
const std::string kNca2Sig = "NCA2";
|
||||
const std::string kNca3Sig = "NCA3";
|
||||
static const size_t kSectorSize = 0x200;
|
||||
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;
|
||||
|
||||
enum DistributionType
|
||||
{
|
||||
DIST_DOWNLOAD,
|
||||
DIST_GAME_CARD
|
||||
};
|
||||
|
||||
enum ContentType
|
||||
{
|
||||
TYPE_PROGRAM,
|
||||
TYPE_META,
|
||||
TYPE_CONTROL,
|
||||
TYPE_MANUAL,
|
||||
TYPE_DATA,
|
||||
};
|
||||
|
||||
enum KeyBankIndex
|
||||
{
|
||||
KEY_AESXTS_0,
|
||||
KEY_AESXTS_1,
|
||||
KEY_AESCTR,
|
||||
KEY_UNUSED_3,
|
||||
KEY_AESCTR_HW
|
||||
};
|
||||
|
||||
enum KeyAreaEncryptionKeyIndex
|
||||
{
|
||||
KAEK_IDX_APPLICATION,
|
||||
KAEK_IDX_OCEAN,
|
||||
KAEK_IDX_SYSTEM
|
||||
};
|
||||
|
||||
enum FormatType
|
||||
{
|
||||
FORMAT_ROMFS,
|
||||
FORMAT_PFS0
|
||||
};
|
||||
|
||||
enum HashType
|
||||
{
|
||||
HASH_AUTO,
|
||||
HASH_UNK1,
|
||||
HASH_HIERARCHICAL_SHA256,
|
||||
HASH_HIERARCHICAL_INTERGRITY
|
||||
};
|
||||
|
||||
enum EncryptionType
|
||||
{
|
||||
CRYPT_AUTO,
|
||||
CRYPT_NONE,
|
||||
CRYPT_AESXTS,
|
||||
CRYPT_AESCTR,
|
||||
CRYPT_BKTR
|
||||
};
|
||||
}
|
||||
|
||||
#pragma pack(push,1)
|
||||
struct sNcaHeader
|
||||
{
|
||||
char signature[4];
|
||||
byte_t distribution_type;
|
||||
byte_t content_type;
|
||||
byte_t key_generation; // KeyGeneration
|
||||
byte_t key_area_encryption_key_index;
|
||||
le_uint64_t content_size;
|
||||
le_uint64_t program_id;
|
||||
le_uint32_t content_index;
|
||||
le_uint32_t sdk_addon_version;
|
||||
byte_t key_generation_2;
|
||||
byte_t reserved_2[0xf];
|
||||
byte_t rights_id[nca::kRightsIdLen];
|
||||
struct sNcaSection
|
||||
{
|
||||
le_uint32_t start; // block units
|
||||
le_uint32_t end; // block units
|
||||
byte_t enabled;
|
||||
byte_t reserved[7];
|
||||
} partition[nca::kPartitionNum];
|
||||
crypto::sha::sSha256Hash partition_hash[nca::kPartitionNum];
|
||||
crypto::aes::sAes128Key enc_aes_key[nca::kAesKeyNum];
|
||||
};
|
||||
|
||||
struct sNcaFsHeader
|
||||
{
|
||||
le_uint16_t version; // usually 0x0002
|
||||
byte_t format_type; // RomFs(0x00), PartitionFs(0x01)
|
||||
byte_t hash_type; // HashTypeAuto(0x00), HashTypeHierarchicalSha256(0x02), HashTypeHierarchicalIntegrity(0x03).RomFs uses (0x03) this is forced, PartitionFs uses (0x02).
|
||||
byte_t encryption_type; // EncryptionTypeAuto(0x00), EncryptionTypeNone(0x01), EncryptionTypeAesCtr(0x03)
|
||||
byte_t reserved[3];
|
||||
};
|
||||
|
||||
struct sPfsSuperBlock
|
||||
{
|
||||
byte_t master_hash[0x20];
|
||||
le_uint32_t hash_block_size;
|
||||
le_uint32_t unk_0x02;
|
||||
struct sLayout
|
||||
{
|
||||
le_uint64_t offset;
|
||||
le_uint64_t size;
|
||||
} hash_data, hash_target;
|
||||
};
|
||||
|
||||
static const size_t kMaxIvfcLevel = 4;
|
||||
|
||||
struct sIvfcHeader
|
||||
{
|
||||
le_uint32_t magic;
|
||||
le_uint32_t id;
|
||||
le_uint32_t master_hash_size;
|
||||
le_uint32_t level_num;
|
||||
struct sIvfcLevelHeader
|
||||
{
|
||||
uint64_t logical_offset;
|
||||
uint64_t hash_data_size;
|
||||
uint32_t block_size;
|
||||
byte_t reserved[4];
|
||||
} level_header[kMaxIvfcLevel];
|
||||
byte_t unk_0xA0[0x20];
|
||||
byte_t master_hash[0x20];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
}
|
|
@ -12,38 +12,49 @@ void NcaHeader::exportBinary()
|
|||
switch(mFormatVersion)
|
||||
{
|
||||
case (NCA2_FORMAT):
|
||||
strncpy(hdr->signature, kNca2Sig.c_str(), 4);
|
||||
strncpy(hdr->signature, nca::kNca2Sig.c_str(), 4);
|
||||
break;
|
||||
case (NCA3_FORMAT):
|
||||
strncpy(hdr->signature, kNca3Sig.c_str(), 4);
|
||||
strncpy(hdr->signature, nca::kNca3Sig.c_str(), 4);
|
||||
break;
|
||||
default:
|
||||
throw fnd::Exception(kModuleName, "Unsupported format version");
|
||||
}
|
||||
hdr->distribution_type = mDistributionType;
|
||||
hdr->content_type = mContentType;
|
||||
hdr->crypto_type = mCryptoType;
|
||||
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->nca_size = mNcaSize;
|
||||
hdr->content_size = mContentSize;
|
||||
hdr->program_id = mProgramId;
|
||||
hdr->content_index = mContentIndex;
|
||||
hdr->sdk_addon_version = mSdkAddonVersion;
|
||||
hdr->crypto_type_2 = 0;
|
||||
memcpy(hdr->rights_id, mRightsId, nca::kRightsIdLen);
|
||||
|
||||
// TODO: properly reconstruct NCA layout? atm in hands of user
|
||||
|
||||
for (size_t i = 0; i < mSections.getSize(); i++)
|
||||
for (size_t i = 0; i < mPartitions.getSize(); i++)
|
||||
{
|
||||
// determine section index
|
||||
byte_t section = mSections.getSize() - 1 - i;
|
||||
// determine partition index
|
||||
byte_t idx = mPartitions[i].index;
|
||||
|
||||
hdr->section[section].start = sizeToBlockNum(mSections[i].offset);
|
||||
hdr->section[section].end = (sizeToBlockNum(mSections[i].offset) + sizeToBlockNum(mSections[i].size));
|
||||
hdr->section[section].enabled = true;
|
||||
hdr->section_hash[section] = mSections[i].hash;
|
||||
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 < kAesKeyNum; i++)
|
||||
for (size_t i = 0; i < nca::kAesKeyNum; i++)
|
||||
{
|
||||
hdr->enc_aes_key[i] = mEncAesKeys[i];
|
||||
}
|
||||
|
@ -63,11 +74,11 @@ void NcaHeader::importBinary(const byte_t * bytes, size_t len)
|
|||
|
||||
sNcaHeader* hdr = (sNcaHeader*)mBinaryBlob.getBytes();
|
||||
|
||||
if (memcmp(hdr->signature, kNca2Sig.c_str(), 4) == 0)
|
||||
if (memcmp(hdr->signature, nca::kNca2Sig.c_str(), 4) == 0)
|
||||
{
|
||||
mFormatVersion = NCA2_FORMAT;
|
||||
}
|
||||
else if (memcmp(hdr->signature, kNca3Sig.c_str(), 4) == 0)
|
||||
else if (memcmp(hdr->signature, nca::kNca3Sig.c_str(), 4) == 0)
|
||||
{
|
||||
mFormatVersion = NCA3_FORMAT;
|
||||
}
|
||||
|
@ -76,28 +87,26 @@ void NcaHeader::importBinary(const byte_t * bytes, size_t len)
|
|||
throw fnd::Exception(kModuleName, "NCA header corrupt");
|
||||
}
|
||||
|
||||
mDistributionType = (DistributionType)hdr->distribution_type;
|
||||
mContentType = (ContentType)hdr->content_type;
|
||||
mCryptoType = MAX(hdr->crypto_type, hdr->crypto_type_2);
|
||||
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;
|
||||
mNcaSize = *hdr->nca_size;
|
||||
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 < kSectionNum; i++)
|
||||
for (size_t i = 0; i < nca::kPartitionNum; i++)
|
||||
{
|
||||
// determine section index
|
||||
byte_t section = kSectionNum - 1 - i;
|
||||
|
||||
// skip sections that don't exist
|
||||
if (*hdr->section[section].start == 0 && *hdr->section[section].end == 0) continue;
|
||||
if (hdr->partition[i].enabled == 0) continue;
|
||||
|
||||
// add high level struct
|
||||
mSections.addElement({ blockNumToSize(*hdr->section[section].start), blockNumToSize(hdr->section[section].end.get() - hdr->section[section].start.get()), hdr->section_hash[section] });
|
||||
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 < kAesKeyNum; i++)
|
||||
for (size_t i = 0; i < nca::kAesKeyNum; i++)
|
||||
{
|
||||
mEncAesKeys.addElement(hdr->enc_aes_key[i]);
|
||||
}
|
||||
|
@ -106,15 +115,16 @@ void NcaHeader::importBinary(const byte_t * bytes, size_t len)
|
|||
void nx::NcaHeader::clear()
|
||||
{
|
||||
mFormatVersion = NCA3_FORMAT;
|
||||
mDistributionType = DIST_DOWNLOAD;
|
||||
mContentType = TYPE_PROGRAM;
|
||||
mCryptoType = 0;
|
||||
mDistributionType = nca::DIST_DOWNLOAD;
|
||||
mContentType = nca::TYPE_PROGRAM;
|
||||
mKeyGeneration = 0;
|
||||
mKaekIndex = 0;
|
||||
mNcaSize = 0;
|
||||
mContentSize = 0;
|
||||
mProgramId = 0;
|
||||
mContentIndex = 0;
|
||||
mSdkAddonVersion = 0;
|
||||
mSections.clear();
|
||||
|
||||
mPartitions.clear();
|
||||
mEncAesKeys.clear();
|
||||
}
|
||||
|
||||
|
@ -128,34 +138,34 @@ void nx::NcaHeader::setFormatVersion(FormatVersion version)
|
|||
mFormatVersion = version;
|
||||
}
|
||||
|
||||
nx::NcaHeader::DistributionType nx::NcaHeader::getDistributionType() const
|
||||
nx::nca::DistributionType nx::NcaHeader::getDistributionType() const
|
||||
{
|
||||
return mDistributionType;
|
||||
}
|
||||
|
||||
void nx::NcaHeader::setDistributionType(DistributionType type)
|
||||
void nx::NcaHeader::setDistributionType(nca::DistributionType type)
|
||||
{
|
||||
mDistributionType = type;
|
||||
}
|
||||
|
||||
nx::NcaHeader::ContentType nx::NcaHeader::getContentType() const
|
||||
nx::nca::ContentType nx::NcaHeader::getContentType() const
|
||||
{
|
||||
return mContentType;
|
||||
}
|
||||
|
||||
void nx::NcaHeader::setContentType(ContentType type)
|
||||
void nx::NcaHeader::setContentType(nca::ContentType type)
|
||||
{
|
||||
mContentType = type;
|
||||
}
|
||||
|
||||
byte_t nx::NcaHeader::getCryptoType() const
|
||||
byte_t nx::NcaHeader::getKeyGeneration() const
|
||||
{
|
||||
return mCryptoType;
|
||||
return mKeyGeneration;
|
||||
}
|
||||
|
||||
void nx::NcaHeader::setCryptoType(byte_t type)
|
||||
void nx::NcaHeader::setKeyGeneration(byte_t gen)
|
||||
{
|
||||
mCryptoType = type;
|
||||
mKeyGeneration = gen;
|
||||
}
|
||||
|
||||
byte_t nx::NcaHeader::getKaekIndex() const
|
||||
|
@ -168,14 +178,14 @@ void nx::NcaHeader::setKaekIndex(byte_t index)
|
|||
mKaekIndex = index;
|
||||
}
|
||||
|
||||
uint64_t NcaHeader::getNcaSize() const
|
||||
uint64_t NcaHeader::getContentSize() const
|
||||
{
|
||||
return mNcaSize;
|
||||
return mContentSize;
|
||||
}
|
||||
|
||||
void NcaHeader::setNcaSize(uint64_t size)
|
||||
void NcaHeader::setContentSize(uint64_t size)
|
||||
{
|
||||
mNcaSize = size;
|
||||
mContentSize = size;
|
||||
}
|
||||
|
||||
uint64_t NcaHeader::getProgramId() const
|
||||
|
@ -208,18 +218,28 @@ void nx::NcaHeader::setSdkAddonVersion(uint32_t version)
|
|||
mSdkAddonVersion = version;
|
||||
}
|
||||
|
||||
const fnd::List<NcaHeader::sSection>& NcaHeader::getSections() const
|
||||
const byte_t* nx::NcaHeader::getRightsId() const
|
||||
{
|
||||
return mSections;
|
||||
return mRightsId;
|
||||
}
|
||||
|
||||
void NcaHeader::addSection(const sSection & section)
|
||||
void nx::NcaHeader::setRightsId(const byte_t* rights_id)
|
||||
{
|
||||
if (mSections.getSize() >= kSectionNum)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "Too many NCA sections");
|
||||
memcpy(mRightsId, rights_id, nca::kRightsIdLen);
|
||||
}
|
||||
|
||||
const fnd::List<NcaHeader::sPartition>& NcaHeader::getPartitions() const
|
||||
{
|
||||
return mPartitions;
|
||||
}
|
||||
|
||||
void NcaHeader::setPartitions(const fnd::List<NcaHeader::sPartition>& partitions)
|
||||
{
|
||||
mPartitions = partitions;
|
||||
if (mPartitions.getSize() >= nca::kPartitionNum)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "Too many NCA partitions");
|
||||
}
|
||||
mSections.addElement(section);
|
||||
}
|
||||
|
||||
const fnd::List<crypto::aes::sAes128Key>& NcaHeader::getEncAesKeys() const
|
||||
|
@ -227,37 +247,32 @@ const fnd::List<crypto::aes::sAes128Key>& NcaHeader::getEncAesKeys() const
|
|||
return mEncAesKeys;
|
||||
}
|
||||
|
||||
void NcaHeader::addEncAesKey(const crypto::aes::sAes128Key & key)
|
||||
void NcaHeader::setEncAesKeys(const fnd::List<crypto::aes::sAes128Key>& keys)
|
||||
{
|
||||
if (mEncAesKeys.getSize() >= kAesKeyNum)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "Too many NCA aes keys");
|
||||
}
|
||||
|
||||
mEncAesKeys.addElement(key);
|
||||
mEncAesKeys = keys;
|
||||
}
|
||||
|
||||
uint64_t NcaHeader::blockNumToSize(uint32_t block_num) const
|
||||
{
|
||||
return block_num*kBlockSize;
|
||||
return block_num*nca::kSectorSize;
|
||||
}
|
||||
|
||||
uint32_t NcaHeader::sizeToBlockNum(uint64_t real_size) const
|
||||
{
|
||||
return align(real_size, kBlockSize)/kBlockSize;
|
||||
return align(real_size, nca::kSectorSize) / nca::kSectorSize;
|
||||
}
|
||||
|
||||
bool NcaHeader::isEqual(const NcaHeader & other) const
|
||||
{
|
||||
return (mDistributionType == other.mDistributionType) \
|
||||
&& (mContentType == other.mContentType) \
|
||||
&& (mCryptoType == other.mCryptoType) \
|
||||
&& (mKeyGeneration == other.mKeyGeneration) \
|
||||
&& (mKaekIndex == other.mKaekIndex) \
|
||||
&& (mNcaSize == other.mNcaSize) \
|
||||
&& (mContentSize == other.mContentSize) \
|
||||
&& (mProgramId == other.mProgramId) \
|
||||
&& (mContentIndex == other.mContentIndex) \
|
||||
&& (mSdkAddonVersion == other.mSdkAddonVersion) \
|
||||
&& (mSections == other.mSections) \
|
||||
&& (mPartitions == other.mPartitions) \
|
||||
&& (mEncAesKeys == other.mEncAesKeys);
|
||||
}
|
||||
|
||||
|
@ -272,13 +287,13 @@ void NcaHeader::copyFrom(const NcaHeader & other)
|
|||
mBinaryBlob.clear();
|
||||
mDistributionType = other.mDistributionType;
|
||||
mContentType = other.mContentType;
|
||||
mCryptoType = other.mCryptoType;
|
||||
mKeyGeneration = other.mKeyGeneration;
|
||||
mKaekIndex = other.mKaekIndex;
|
||||
mNcaSize = other.mNcaSize;
|
||||
mContentSize = other.mContentSize;
|
||||
mProgramId = other.mProgramId;
|
||||
mContentIndex = other.mContentIndex;
|
||||
mSdkAddonVersion = other.mSdkAddonVersion;
|
||||
mSections = other.mSections;
|
||||
mPartitions = other.mPartitions;
|
||||
mEncAesKeys = other.mEncAesKeys;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
#include <crypto/aes.h>
|
||||
#include <fnd/io.h>
|
||||
#include <fnd/MemoryBlob.h>
|
||||
#include <fnd/SimpleTextOutput.h>
|
||||
#include <nx/NXCrypto.h>
|
||||
#include <nx/NcaHeader.h>
|
||||
#include <nx/PfsHeader.h>
|
||||
#include <inttypes.h>
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
|
@ -11,8 +13,6 @@
|
|||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
const size_t kNcaSectorSize = nx::NcaHeader::kBlockSize;
|
||||
|
||||
std::string kFormatVersionStr[]
|
||||
{
|
||||
"NCA2",
|
||||
|
@ -40,7 +40,7 @@ std::string kEncryptionTypeStr[]
|
|||
"None",
|
||||
"AesXts",
|
||||
"AesCtr",
|
||||
"BKTR"
|
||||
"AesCtrEx"
|
||||
};
|
||||
|
||||
std::string kHashTypeStr[]
|
||||
|
@ -64,16 +64,6 @@ std::string kKaekIndexStr[]
|
|||
"System"
|
||||
};
|
||||
|
||||
enum EncryptionType
|
||||
{
|
||||
CRYPT_AUTO,
|
||||
CRYPT_NONE,
|
||||
CRYPT_AESXTS,
|
||||
CRYPT_AESCTR,
|
||||
CRYPT_BKTR
|
||||
};
|
||||
|
||||
static const byte_t kNcaMagic[2][4] = {{'N','C','A','2'}, {'N','C','A','3'}};
|
||||
|
||||
enum KeysetType
|
||||
{
|
||||
|
@ -87,18 +77,7 @@ static const byte_t* kNcaHeaderKey[2][2] =
|
|||
{ crypto::aes::nx::prod::nca_header_key[0], crypto::aes::nx::prod::nca_header_key[1] }
|
||||
};
|
||||
|
||||
#pragma pack(push,1)
|
||||
struct sNcaFsHeader
|
||||
{
|
||||
le_uint16_t version; // usually 0x0002
|
||||
byte_t format_type; // RomFs(0x00), PartitionFs(0x01)
|
||||
byte_t hash_type; // HashTypeAuto(0x00), HashTypeHierarchicalSha256(0x02), HashTypeHierarchicalIntegrity(0x03).RomFs uses (0x03) this is forced, PartitionFs uses (0x02).
|
||||
byte_t encryption_type; // EncryptionTypeAuto(0x00), EncryptionTypeNone(0x01), EncryptionTypeAesCtr(0x03)
|
||||
byte_t reserved[3];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
inline size_t sectorToOffset(size_t sector_index) { return sector_index * kNcaSectorSize; }
|
||||
inline size_t sectorToOffset(size_t sector_index) { return sector_index * nx::nca::kSectorSize; }
|
||||
|
||||
void initNcaCtr(byte_t ctr[crypto::aes::kAesBlockSize], uint32_t generation)
|
||||
{
|
||||
|
@ -109,110 +88,37 @@ void initNcaCtr(byte_t ctr[crypto::aes::kAesBlockSize], uint32_t generation)
|
|||
}
|
||||
}
|
||||
|
||||
void hexDump(const byte_t* data, size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
printf("%02X", data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void xorData(const byte_t* a, const byte_t* b, byte_t* out, size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
out[i] = a[i] ^ b[i];
|
||||
}
|
||||
}
|
||||
|
||||
void decryptNcaHeader(byte_t header[0xc00], const byte_t* key[2])
|
||||
void decryptNcaHeader(byte_t header[nx::nca::kHeaderSize], const byte_t* key[2])
|
||||
{
|
||||
byte_t tweak[crypto::aes::kAesBlockSize];
|
||||
|
||||
// decrypt main header
|
||||
byte_t raw_hdr[kNcaSectorSize];
|
||||
byte_t raw_hdr[nx::nca::kSectorSize];
|
||||
nx::NcaHeader hdr;
|
||||
crypto::aes::AesXtsMakeTweak(tweak, 1);
|
||||
crypto::aes::AesXtsDecryptSector(header + sectorToOffset(1), kNcaSectorSize, key[0], key[1], tweak, raw_hdr);
|
||||
hdr.importBinary(raw_hdr, kNcaSectorSize);
|
||||
crypto::aes::AesXtsDecryptSector(header + sectorToOffset(1), nx::nca::kSectorSize, key[0], key[1], tweak, raw_hdr);
|
||||
hdr.importBinary(raw_hdr, nx::nca::kSectorSize);
|
||||
|
||||
bool useNca2SectorIndex = hdr.getFormatVersion() == nx::NcaHeader::NCA2_FORMAT;
|
||||
|
||||
// decrypt whole header
|
||||
for (size_t i = 0; i < 6; i++)
|
||||
for (size_t i = 0; i < nx::nca::kHeaderSectorNum; i++)
|
||||
{
|
||||
crypto::aes::AesXtsMakeTweak(tweak, (i > 1 && hdr.getFormatVersion() == nx::NcaHeader::NCA2_FORMAT)? 0 : i);
|
||||
crypto::aes::AesXtsDecryptSector(header + sectorToOffset(i), kNcaSectorSize, key[0], key[1], tweak, header + sectorToOffset(i));
|
||||
crypto::aes::AesXtsMakeTweak(tweak, (i > 1 && useNca2SectorIndex)? 0 : i);
|
||||
crypto::aes::AesXtsDecryptSector(header + sectorToOffset(i), nx::nca::kSectorSize, key[0], key[1], tweak, header + sectorToOffset(i));
|
||||
}
|
||||
}
|
||||
|
||||
void decryptNcaSectorXts(const fnd::MemoryBlob& nca, byte_t out[kNcaSectorSize], size_t sector, const byte_t* key[2])
|
||||
{
|
||||
byte_t tweak[crypto::aes::kAesBlockSize];
|
||||
crypto::aes::AesXtsMakeTweak(tweak, sector);
|
||||
crypto::aes::AesXtsDecryptSector(nca.getBytes() + sectorToOffset(sector), kNcaSectorSize, key[0], key[1], tweak, out);
|
||||
}
|
||||
|
||||
void decryptNcaSectorCtr(const fnd::MemoryBlob& nca, byte_t out[kNcaSectorSize], size_t sector, const byte_t* key)
|
||||
{
|
||||
byte_t ctr[crypto::aes::kAesBlockSize];
|
||||
initNcaCtr(ctr, 0);
|
||||
crypto::aes::AesIncrementCounter(ctr, (sector*kNcaSectorSize)/crypto::aes::kAesBlockSize, ctr);
|
||||
crypto::aes::AesCtr(nca.getBytes() + sector*kNcaSectorSize, kNcaSectorSize, key, ctr, out);
|
||||
}
|
||||
|
||||
void dumpNcaSector(byte_t out[kNcaSectorSize])
|
||||
{
|
||||
for (size_t j = 0; j < kNcaSectorSize / crypto::aes::kAesBlockSize; j++)
|
||||
{
|
||||
hexDump(out + j * crypto::aes::kAesBlockSize, crypto::aes::kAesBlockSize);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void dumpHxdStyleSector(byte_t* out, size_t len)
|
||||
{
|
||||
// iterate over 0x10 blocks
|
||||
for (size_t i = 0; i < (len / crypto::aes::kAesBlockSize); i++)
|
||||
{
|
||||
// for block i print each byte
|
||||
for (size_t j = 0; j < crypto::aes::kAesBlockSize; j++)
|
||||
{
|
||||
printf("%02X ", out[i*crypto::aes::kAesBlockSize + j]);
|
||||
}
|
||||
printf(" ");
|
||||
for (size_t j = 0; j < crypto::aes::kAesBlockSize; j++)
|
||||
{
|
||||
printf("%c", isalnum(out[i*crypto::aes::kAesBlockSize + j]) ? out[i*crypto::aes::kAesBlockSize + j] : '.');
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
for (size_t i = 0; i < len % crypto::aes::kAesBlockSize; i++)
|
||||
{
|
||||
printf("%02X ", out[(len / crypto::aes::kAesBlockSize)*crypto::aes::kAesBlockSize + i]);
|
||||
}
|
||||
for (size_t i = 0; i < crypto::aes::kAesBlockSize - (len % crypto::aes::kAesBlockSize); i++)
|
||||
{
|
||||
printf(" ");
|
||||
}
|
||||
for (size_t i = 0; i < len % crypto::aes::kAesBlockSize; i++)
|
||||
{
|
||||
printf("%c", out[(len / crypto::aes::kAesBlockSize)*crypto::aes::kAesBlockSize + i]);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
bool testNcaHeaderKey(const byte_t* header_src, const byte_t* key[2])
|
||||
{
|
||||
bool validKey = false;
|
||||
byte_t header_dec[kNcaSectorSize];
|
||||
byte_t header_dec[nx::nca::kSectorSize];
|
||||
byte_t tweak[crypto::aes::kAesBlockSize];
|
||||
|
||||
// try key
|
||||
crypto::aes::AesXtsMakeTweak(tweak, 1);
|
||||
crypto::aes::AesXtsDecryptSector(header_src + sectorToOffset(1), kNcaSectorSize, key[0], key[1], tweak, header_dec);
|
||||
if (memcmp(header_dec, kNcaMagic[0], 4) == 0 || memcmp(header_dec, kNcaMagic[1], 4) == 0)
|
||||
crypto::aes::AesXtsDecryptSector(header_src + sectorToOffset(1), nx::nca::kSectorSize, key[0], key[1], tweak, header_dec);
|
||||
if (memcmp(header_dec, nx::nca::kNca2Sig.c_str(), 4) == 0 || memcmp(header_dec, nx::nca::kNca3Sig.c_str(), 4) == 0)
|
||||
{
|
||||
validKey = true;
|
||||
}
|
||||
|
@ -233,6 +139,86 @@ KeysetType getKeysetFromNcaHeader(const byte_t* header_src)
|
|||
throw fnd::Exception("Failed to determine NCA header key");
|
||||
}
|
||||
|
||||
void printHeader(const byte_t* header)
|
||||
{
|
||||
nx::NcaHeader hdr;
|
||||
hdr.importBinary(header + sectorToOffset(1), nx::nca::kSectorSize);
|
||||
|
||||
printf("[NCA Header]\n");
|
||||
printf(" Format Type: %s\n", kFormatVersionStr[hdr.getFormatVersion()].c_str());
|
||||
printf(" Dist. Type: %s\n", kDistributionTypeStr[hdr.getDistributionType()].c_str());
|
||||
printf(" Content Type: %s\n", kContentTypeStr[hdr.getContentType()].c_str());
|
||||
printf(" Key Generation: %d\n", hdr.getKeyGeneration());
|
||||
printf(" Kaek Index: %s (%d)\n", kKaekIndexStr[hdr.getKaekIndex()].c_str(), hdr.getKaekIndex());
|
||||
printf(" Size: 0x%" PRIx64 "\n", hdr.getContentSize());
|
||||
printf(" ProgID: 0x%016" PRIx64 "\n", hdr.getProgramId());
|
||||
printf(" Content Index: %" PRIu32 "\n", hdr.getContentIndex());
|
||||
uint32_t ver = hdr.getSdkAddonVersion();
|
||||
printf(" SdkAddon Ver.: v%d.%d.%d (v%" PRIu32 ")\n", (ver>>24 & 0xff),(ver>>16 & 0xff),(ver>>8 & 0xff), ver);
|
||||
printf(" RightsId: ");
|
||||
fnd::SimpleTextOutput::hexDump(hdr.getRightsId(), 0x10);
|
||||
printf("\n");
|
||||
printf(" Encrypted Key Area:\n");
|
||||
crypto::aes::sAes128Key zero_key;
|
||||
memset(zero_key.key, 0, sizeof(zero_key));
|
||||
for (size_t i = 0; i < hdr.getEncAesKeys().getSize(); i++)
|
||||
{
|
||||
if (hdr.getEncAesKeys()[i] != zero_key)
|
||||
{
|
||||
printf(" %2lu: ", i);
|
||||
fnd::SimpleTextOutput::hexDump(hdr.getEncAesKeys()[i].key, crypto::aes::kAes128KeySize);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf(" Sections:\n");
|
||||
for (size_t i = 0; i < hdr.getPartitions().getSize(); i++)
|
||||
{
|
||||
const nx::NcaHeader::sPartition& partition = hdr.getPartitions()[i];
|
||||
printf(" %lu:\n", i);
|
||||
//printf(" Start Blk: %" PRId32 "\n", partition.start_blk);
|
||||
//printf(" End Blk: %" PRId32 "\n", partition.end_blk);
|
||||
printf(" Index: %d\n", partition.index);
|
||||
printf(" Offset: 0x%" PRIx64 "\n", partition.offset);
|
||||
printf(" Size: 0x%" PRIx64 "\n", partition.size);
|
||||
|
||||
|
||||
size_t sector_index = 2 + partition.index;
|
||||
|
||||
crypto::sha::sSha256Hash ncaFsHeaderHash;
|
||||
crypto::sha::Sha256(header + sectorToOffset(sector_index), nx::nca::kSectorSize, ncaFsHeaderHash.bytes);
|
||||
if (partition.hash.compare(ncaFsHeaderHash) == false)
|
||||
{
|
||||
throw fnd::Exception("ncatool", "NcaFsHeader has bad sha256 hash");
|
||||
}
|
||||
|
||||
const nx::sNcaFsHeader* fsHdr = (const nx::sNcaFsHeader*)(header + sectorToOffset(sector_index));
|
||||
printf(" FsHeader:\n");
|
||||
printf(" Version: 0x%d\n", fsHdr->version.get());
|
||||
printf(" Format Type: %s\n", kFormatTypeStr[fsHdr->format_type].c_str());
|
||||
printf(" Hash Type: %s\n", kHashTypeStr[fsHdr->hash_type].c_str());
|
||||
printf(" Enc. Type: %s\n", kEncryptionTypeStr[fsHdr->encryption_type].c_str());
|
||||
if (fsHdr->format_type == nx::nca::FORMAT_ROMFS)
|
||||
{
|
||||
|
||||
}
|
||||
else if (fsHdr->format_type == nx::nca::FORMAT_PFS0)
|
||||
{
|
||||
const nx::sPfsSuperBlock* pfs0 = (const nx::sPfsSuperBlock*)(header + sectorToOffset(sector_index) + sizeof(nx::sNcaFsHeader));
|
||||
printf(" PFS0 SuperBlock:\n");
|
||||
printf(" Master Hash: \n");
|
||||
printf(" HashBlockSize: 0x%x\n", pfs0->hash_block_size.get());
|
||||
printf(" Unknown: 0x%x\n", pfs0->unk_0x02.get());
|
||||
printf(" HashDataOffset: 0x%" PRIx64 "\n", pfs0->hash_data.offset.get());
|
||||
printf(" HashDataSize: 0x%" PRIx64 "\n", pfs0->hash_data.size.get());
|
||||
printf(" HashTargetOffset: 0x%" PRIx64 "\n", pfs0->hash_target.offset.get());
|
||||
printf(" HashTargetSize: 0x%" PRIx64 "\n", pfs0->hash_target.size.get());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 2)
|
||||
|
@ -244,122 +230,13 @@ int main(int argc, char** argv)
|
|||
try
|
||||
{
|
||||
fnd::MemoryBlob nca;
|
||||
fnd::io::readFile(argv[1], nca);
|
||||
fnd::io::readFile(argv[1], 0x0, nx::nca::kHeaderSize, nca);
|
||||
|
||||
KeysetType keyset = getKeysetFromNcaHeader(nca.getBytes());
|
||||
|
||||
decryptNcaHeader(nca.getBytes(), kNcaHeaderKey[keyset]);
|
||||
//dumpHxdStyleSector(nca.getBytes(), 0xc00);
|
||||
|
||||
// nca test
|
||||
if (argc == 2 || argc == 3)
|
||||
{
|
||||
//decryptNcaSectorXts(nca, sector, 1, crypto::aes::nx::dev::nca_header_key[0], crypto::aes::nx::dev::nca_header_key[1]);
|
||||
|
||||
nx::NcaHeader hdr;
|
||||
hdr.importBinary(nca.getBytes() + sectorToOffset(1), kNcaSectorSize);
|
||||
|
||||
printf("[NCA Header]\n");
|
||||
printf(" Format Type: %s\n", kFormatVersionStr[hdr.getFormatVersion()].c_str());
|
||||
printf(" Dist. Type: %s\n", kDistributionTypeStr[hdr.getDistributionType()].c_str());
|
||||
printf(" Type: %s\n", kContentTypeStr[hdr.getContentType()].c_str());
|
||||
printf(" Crypto Type: %d\n", hdr.getCryptoType());
|
||||
printf(" Kaek Index: %s (%d)\n", kKaekIndexStr[hdr.getKaekIndex()].c_str(), hdr.getKaekIndex());
|
||||
printf(" Size: 0x%" PRIx64 "\n", hdr.getNcaSize());
|
||||
printf(" ProgID: 0x%016" PRIx64 "\n", hdr.getProgramId());
|
||||
printf(" Content. Idx: %" PRIu32 "\n", hdr.getContentIndex());
|
||||
uint32_t ver = hdr.getSdkAddonVersion();
|
||||
printf(" SdkAddon Ver.: v%d.%d.%d.%d (v%" PRIu32 ")\n", (ver>>24 & 0xff),(ver>>16 & 0xff),(ver>>8 & 0xff),(ver>>0 & 0xff), ver);
|
||||
printf(" Encrypted Key Area:\n");
|
||||
for (size_t i = 0; i < hdr.getEncAesKeys().getSize(); i++)
|
||||
{
|
||||
printf(" %lu: ", i);
|
||||
hexDump(hdr.getEncAesKeys()[i].key, crypto::aes::kAes128KeySize);
|
||||
printf("\n");
|
||||
/*
|
||||
byte_t key[crypto::aes::kAes128KeySize];
|
||||
crypto::aes::AesEcbDecrypt(hdr.getEncAesKeys()[i].key, crypto::aes::kAes128KeySize, crypto::aes::nx::dev::key_area_encryption_key_0, key);
|
||||
printf(" dec: ", i);
|
||||
hexDump(key, crypto::aes::kAes128KeySize);
|
||||
printf("\n");
|
||||
*/
|
||||
}
|
||||
|
||||
printf(" Sections:\n");
|
||||
for (size_t i = 0; i < hdr.getSections().getSize(); i++)
|
||||
{
|
||||
const nx::NcaHeader::sSection& section = hdr.getSections()[i];
|
||||
printf(" %lu:\n", i);
|
||||
//printf(" Start Blk: %" PRId32 "\n", section.start_blk);
|
||||
//printf(" End Blk: %" PRId32 "\n", section.end_blk);
|
||||
printf(" Offset: 0x%" PRIx64 "\n", section.offset);
|
||||
printf(" Size: 0x%" PRIx64 "\n", section.size);
|
||||
|
||||
|
||||
size_t sector_index = 1 + (hdr.getSections().getSize() - i);
|
||||
|
||||
byte_t hash[crypto::sha::kSha256HashLen];
|
||||
crypto::sha::Sha256(nca.getBytes() + sectorToOffset(sector_index), kNcaSectorSize, hash);
|
||||
if (section.hash.compare(hash) == false)
|
||||
{
|
||||
throw fnd::Exception("ncatool", "NcaFsHeader has bad sha256 hash");
|
||||
}
|
||||
|
||||
const sNcaFsHeader* fsHdr = (const sNcaFsHeader*)(nca.getBytes() + sectorToOffset(sector_index));
|
||||
printf(" FsHeader:\n");
|
||||
printf(" Version: 0x%d\n", fsHdr->version.get());
|
||||
printf(" Format Type: %s\n", kFormatTypeStr[fsHdr->format_type].c_str());
|
||||
printf(" Hash Type: %s\n", kHashTypeStr[fsHdr->hash_type].c_str());
|
||||
printf(" Enc. Type: %s\n", kEncryptionTypeStr[fsHdr->encryption_type].c_str());
|
||||
/*
|
||||
printf(" Hash: ");
|
||||
hexDump(section.hash.bytes, crypto::sha::kSha256HashLen);
|
||||
printf("\n");
|
||||
byte_t hash[crypto::sha::kSha256HashLen];
|
||||
crypto::sha::Sha256(nca.getBytes() + sectorToOffset(sector_index), kNcaSectorSize, hash);
|
||||
printf(" Hash: ");
|
||||
hexDump(hash, crypto::sha::kSha256HashLen);
|
||||
printf("\n");
|
||||
*/
|
||||
//dumpHxdStyleSector(nca.getBytes() + sectorToOffset(sector_index), 0x10);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef USE_OLD_CODE
|
||||
if (argc == 3)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
_mkdir(argv[2]);
|
||||
#else
|
||||
mkdir(argv[2], S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < hdr.getSections().getSize(); i++)
|
||||
{
|
||||
const nx::NcaHeader::sSection& section = hdr.getSections()[i];
|
||||
#ifdef _WIN32
|
||||
fnd::io::writeFile(std::string(argv[2]) + "\\" + std::to_string(i) + ".bin" , nca.getBytes() + section.offset, section.size);
|
||||
#else
|
||||
fnd::io::writeFile(std::string(argv[2]) + "/" + std::to_string(i) + ".bin", nca.getBytes() + section.offset, section.size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
if (argc == 4)
|
||||
{
|
||||
printf("decrypt test\n");
|
||||
byte_t sect[kNcaSectorSize];;
|
||||
for (size_t i = 0; i < 6; i++)
|
||||
{
|
||||
decryptNcaSectorXts(nca, sect, i, crypto::aes::nx::dev::nca_header_key[0], crypto::aes::nx::dev::nca_header_key[1]);
|
||||
dumpNcaSector(sect);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
printHeader(nca.getBytes());
|
||||
} catch (const fnd::Exception& e)
|
||||
{
|
||||
printf("%s\n",e.what());
|
||||
|
|
Loading…
Reference in a new issue